[Python,Gtk4] Gtk.ListViewの使用について

Python Gtk4プログラミング

目標

 Gtk.ListViewは、Gtk4.10より追加されたリストを表示をするためのものです。Gtk.ListViewでは、factoryを使用してGtk.ListViewに表示する項目ごとに1つのWigetを生成して、それらをGtk.ListView内に一列に並べて表示することで、ユーザーに項目が見えるようにしています。

 今回は、Gtk.ListViewを使用してリスト(Gtk.StringListで作成)を表示する方法を紹介します。

Gtk.ListViewの定義

 Gtk.ListViewの定義は、以下のようにおこないます。

        listview = Gtk.ListView()

Gio.ListStore(Gtk.ListView用のデータ)の作成

 Gtk.ListViewで使用するデータはGtk.SelectionModelなので、以下のようにGtk.SelectionModelを作成して、それをGtk.ListViewのモデルにします。
 1. データの内容をクラスで定義する。(class Data(GObject.Object)
   Gtk.ListViewのデータなので、1個のデータ(data)を受けて変数self.dataに
   その値を入れる作業をする。
 2. Gio.ListStoreを定義して、それに値をクラスDataを使用して入力する。
   入力にはメソッドappendを使用する。
 3. modelにGio.ListStoreを指定して、Gtk.SingleSelectionを定義する。
 4. Gtk.ListViewにメソッドset_modelを使用して、3.で定義したGtk.SingleSelectionを
  modelに指定する。

class Data(GObject.Object):

    def __init__(self, data):
        super().__init__()
        self.data = data


class Gtk4TestTest(Gtk.Window):

        # 省略

        model = Gio.ListStore()
        model.append(Data('項目1'))
        model.append(Data('項目2'))
        model.append(Data('項目3'))
        model.append(Data('項目4'))

        smodel = Gtk.SingleSelection(model=model)

        # 省略

        listview.set_model(smodel)

factoryについて

 Gtk.ListViewのfactoryには、Gtk.ListItemFactoryのサブクラスであるGtk.SignalListItemFactoryを使用します。Gtk.SingleListItemFactoryは、リスト項目を管理するためにシグナルを発行するためのものであり、Gtk.ListViewでは以下のような作業をおこないます。
 1. ビュー(今回はGtk.ListView)が必要とするとき(その項目が表示される場合)に
   モデルから取得した項目のウィジェットを作成する。
 2. ビューによって表示される項目が変更されるとウィジェットを更新するタスクを行う。

Gtk.SignalListItemFactoryの設定

 Gtk.SignalListitemFactoryには、’setup’、’bind’、’unbind’、’teardown’の4つのシグナルがあります。今回のサンプルプログラムでは、Gtk.ListViewに4個の項目を表示するだけなので、シグナル’setup’と’bind’を使用します。

内容
setupリスト項目を設定するために発行
 通常、行に使用されるWidgetを構築し構築したものを listitem に追加する。 
bind‘setup’で作成したWidgetにバインドするか、項目固有のウィジェットを
追加するために発行
unbind‘bind’で実行されたすべてを元に戻すために発行
teardown元に戻すことを許可するために発行
Gtk.SignalListitemFactoryのシグナル

 作成したGtk.SignalListitemFactoryは、メソッドset_factoryによりGtk.ListViewにセットします。

        factory = Gtk.SignalListItemFactory()
        factory.connect("setup", self.on_list_item_setup)
        factory.connect("bind", self.on_list_item_bind)

        smodel.connect("selection-changed", self.on_selected_items_changed)

        listview = Gtk.ListView()
        listview.set_model(smodel)
        listview.set_factory(factory)

factoryのシグナルの処理①: setup

 シグナル’setup’と紐付けた関数では、Gtk.InscriptionによりGtk.SingleSelectionの項目を表示するものを定義して、それをitem(Gtk.ListItem)にメソッドset_childを使用して、itemの子に定義したものを設定しています。

 今回はGtk.Inscriptionを’行に使用されるWidget’に使用しましたが、他のWidget(適切なものであれば)を指定することも可能です。

    def on_list_item_setup(self, factory, item):
        label = Gtk.Inscription()
        item.set_child(label)

factoryのシグナルの処理②: bind

 シグナル’bind’と紐付けた関数では、item(Gtk.Listitem)のメソッドget_itemによりitemに関連付けられたモデル項目を取得して、そのモデル項目の中のdataの値を得ています。

 そして、itemのメソッドget_childにより取得したWidget(今回はGtk.Inscription)に対して、メソッドset_textを用いてdataの値を表示するように指示しています。

    def on_list_item_bind(self, factory, item):
        item.get_child().set_text(item.get_item().data)

Gtk.ListViewで別の項目が選択された場合に処理をおこなわせる

 Gtk.ListViewで別の項目が選択された場合の処理をするには、Gtk.SingleSelectionのシグナルselection-changedと別の項目が選択された場合に実行したい関数とを紐付けて、紐付けた関数の中に記述します。

 また、紐付けた関数内で、選択された項目を取得するには、引数selection(Gtk.SingleSelection)のメソッドget_selected_itemを使用します。

        smodel.connect("selection-changed", self.on_selected_items_changed)

  # 省略

    def on_selected_items_changed(self, selection, position, n_items):
        selected_item = selection.get_selected_item()
        if selected_item is not None:
            print(f"アイテムが変更されました。: {selected_item.data}")

サンプルプログラム

 以下のプログラムを実行すると、下図のようなウィンドウが表示されます。ウィンドウ内のGtk.ListViewの項目を変更すると、変更した項目名などをターミナルに表示します。

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, Gio, GObject


APPID = 'com.github.taniyoshima.g4_fblog2_listview'


class Data(GObject.Object):

    def __init__(self, data):
        super().__init__()
        self.data = data


class Gtk4TestTest(Gtk.Window):

    def __init__(self, app):
        Gtk.Window.__init__(
            self, application=app, title='Listview Test',
            default_width=400, default_height=300,
        )

        model = Gio.ListStore()
        model.append(Data('項目1'))
        model.append(Data('項目2'))
        model.append(Data('項目3'))
        model.append(Data('項目4'))

        smodel = Gtk.SingleSelection(model=model)

        factory = Gtk.SignalListItemFactory()
        factory.connect("setup", self.on_list_item_setup)
        factory.connect("bind", self.on_list_item_bind)

        smodel.connect("selection-changed", self.on_selected_items_changed)

        listview = Gtk.ListView()
        listview.set_model(smodel)
        listview.set_factory(factory)

        self.set_child(listview)

    def on_selected_items_changed(self, selection, position, n_items):
        selected_item = selection.get_selected_item()
        if selected_item is not None:
            print(f"アイテムが変更されました。: {selected_item.data}")

    def on_list_item_setup(self, factory, item):
        label = Gtk.Inscription()
        item.set_child(label)

    def on_list_item_bind(self, factory, item):
        item.get_child().set_text(item.get_item().data)


class Gtk4TestApp(Gtk.Application):

    def __init__(self):
        Gtk.Application.__init__(self, application_id=APPID)

    def do_activate(self):
        window = Gtk4TestTest(self)
        window.present()


def main():
    app = Gtk4TestApp()
    app.run()


if __name__ == '__main__':
    main()
タイトルとURLをコピーしました