試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

PyGTKでツリービューにリストのデータ(ListStore)を表示(ボタンからデータの順番を入れ替える・コード例)

「PyGTKでツリービューにリストのデータ(ListStore)を表示(ボタンからデータの順番を入れ替える・メモ)」の続き。

コード例

「上へ」の処理を行うハンドラは「PyGTKでツリービューにリストのデータ(ListStore)を表示(ボタンからデータの順番を入れ替える・メモ)」で2種類の方法を扱ったのでそれぞれの方法で2つ用意した。シグナル接続部分で片方コメントにしているので、その部分を入れ替えればもう1つのほうが使用される。

単独選択モード時
#! /usr/bin/python
# -*- encoding: utf-8 -*-

import sys
try:
  import pygtk
  pygtk.require('2.0')
except:
  pass
try:
  import gtk
except:
  print >> sys.stderr, 'Error: PyGTK is not installed'
  sys.exit(1)


class TreeViewWithColumn(gtk.TreeView):
  """
  コラムを含んだツリービュー
  """
  (
    COLUMN_STR,
  ) = range(1)
  def __init__(self, *args, **kwargs):
    gtk.TreeView.__init__(self, *args, **kwargs)
    # コラムの設定
    self.col_str = gtk.TreeViewColumn('Data',
                                      gtk.CellRendererText(),
                                      text=self.COLUMN_STR)
    # コラムを追加
    self.append_column(self.col_str)

class MainWindow(gtk.Window):
  """
  メインウィンドウ
  """
  # 直接ウィンドウとは関係ないが、データは便宜上ここに用意しておくことにする
  data = \
  [
    ('AAA',),
    ('BBB',),
    ('CCC',),
    ('DDD',),
    ('EEE',),
  ]
  def __init__(self, *args, **kwargs):
    gtk.Window.__init__(self, *args, **kwargs)
    # ショートカットキー(アクセラレータ)
    self.accelgroup = gtk.AccelGroup()
    self.add_accel_group(self.accelgroup)
    # メニュー項目
    self.item_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accelgroup)
    self.menu_file = gtk.Menu()
    self.menu_file.add(self.item_quit)
    self.item_file = gtk.MenuItem('_File')
    self.item_file.set_submenu(self.menu_file)
    self.menubar = gtk.MenuBar()
    self.menubar.append(self.item_file)
    # ツリービュー
    self.treeview = TreeViewWithColumn(model=gtk.ListStore(str))
    self.treeview.set_rules_hint(True)
    # ツリービュー向けスクロールウィンドウ
    self.sw = gtk.ScrolledWindow()
    self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    self.sw.add(self.treeview)
    # ボタン
    self.button_top = gtk.Button(stock=gtk.STOCK_GOTO_TOP)
    self.button_up = gtk.Button(stock=gtk.STOCK_GO_UP)
    self.button_down = gtk.Button(stock=gtk.STOCK_GO_DOWN)
    self.button_bottom = gtk.Button(stock=gtk.STOCK_GOTO_BOTTOM)
    # レイアウト用コンテナ
    self.vbox_btn = gtk.VBox()  # ボタンを縦に並べる
    self.vbox_btn.pack_start(self.button_top)
    self.vbox_btn.pack_start(self.button_up)
    self.vbox_btn.pack_start(self.button_down)
    self.vbox_btn.pack_start(self.button_bottom)
    self.hbox = gtk.HBox()  # ツリービューとボタン群
    self.hbox.pack_start(self.sw)
    self.hbox.pack_start(self.vbox_btn, expand=False, fill=False)
    self.vbox = gtk.VBox()  # 全体
    self.vbox.pack_start(self.menubar, expand=False, fill=False)
    self.vbox.pack_start(self.hbox)
    # シグナル
    self.connect('delete_event', gtk.main_quit)
    self.item_quit.connect('activate', gtk.main_quit)
    self.button_top.connect('clicked', self.on_button_top_clicked)
    self.button_up.connect('clicked', self.on_button_up_clicked)
    #self.button_up.connect('clicked', self.on_button_up_clicked2)
    self.button_down.connect('clicked', self.on_button_down_clicked)
    self.button_bottom.connect('clicked', self.on_button_bottom_clicked)
    # データ追加
    for rec in self.data:
      self.treeview.get_model().append(rec)
    # ウィンドウ
    self.add(self.vbox)
    self.set_size_request(350, 300)
  def on_button_top_clicked(self, widget):
    """
    選択項目を一番上に移動
    """
    # 現在選択されている項目を取り出す
    (model, iter_current) = self.treeview.get_selection().get_selected()
    # move_after()の2番目の引数をNoneにすると一番上に移動する
    model.move_after(iter_current, None)
  def on_button_up_clicked(self, widget):
    """
    選択項目を1つ上に移動
    """
    (model, iter_current) = self.treeview.get_selection().get_selected()
    # 先頭の項目を示しているか選択項目がない場合は何もしない
    if iter_current \
    and model.get_path(iter_current) != model.get_path(model.get_iter_first()):
      # 選択項目の1つ上(手前)の項目を探す
      # 逆方向にはたどれないので先頭から順にたどる
      iter = model.get_iter_first()
      while True:
        iter_next = model.iter_next(iter)
        # 1つ手前が選択項目と同じ位置であればそこで止まる
        if model.get_path(iter_next) == model.get_path(iter_current):
          break
        iter = iter_next.copy()
      model.move_before(iter_current, iter)  # このときのiterが1つ手前
  def on_button_up_clicked2(self, widget):
    """
    選択項目を1つ上に移動・その2
    """
    (model, iter_current) = self.treeview.get_selection().get_selected()
    if iter_current \
    and model.get_path(iter_current) != model.get_path(model.get_iter_first()):
      # ツリーパスのタプルの要素(ListStoreでは1階層だけなので0番のみ)を
      # 1引くことで手前の項目のツリーパスを得てTreeIterに変換する方法もある
      iter = model.get_iter(model.get_path(iter_current)[0] - 1)
      model.move_before(iter_current, iter)
  def on_button_down_clicked(self, widget):
    """
    選択項目を1つ下に移動
    """
    (model, iter_current) = self.treeview.get_selection().get_selected()
    # 最後の項目を示しているか選択項目がない場合は何もしない
    if iter_current:
      iter_next = model.iter_next(iter_current)
      if iter_next:
        model.move_after(iter_current, iter_next)
  def on_button_bottom_clicked(self, widget):
    """
    選択項目を一番下に移動
    """
    (model, iter_current) = self.treeview.get_selection().get_selected()
    # move_before()の2番目の引数をNoneにすると一番下に移動する
    model.move_before(iter_current, None)

class PyGTKTreeViewListStoreTest5:
  """
  リストを用いたツリービューのテスト5
  """
  def main(self):
    """
    アプリケーションのメイン処理
    """
    win = MainWindow()
    win.show_all()
    gtk.main()


if __name__ == '__main__':
  app = PyGTKTreeViewListStoreTest5()
  app.main()
複数選択モードで1つの項目が選択されたときに移動できるようにする場合

複数個の項目が選択されたときには何もせず、1つの項目が選択されたときに限り上のように移動できるようになっている。

#! /usr/bin/python
# -*- encoding: utf-8 -*-

import sys
try:
  import pygtk
  pygtk.require('2.0')
except:
  pass
try:
  import gtk
except:
  print >> sys.stderr, 'Error: PyGTK is not installed'
  sys.exit(1)


class TreeViewWithColumn(gtk.TreeView):
  """
  コラムを含んだツリービュー
  """
  (
    COLUMN_STR,
  ) = range(1)
  def __init__(self, *args, **kwargs):
    gtk.TreeView.__init__(self, *args, **kwargs)
    # コラムの設定
    self.col_str = gtk.TreeViewColumn('Data',
                                      gtk.CellRendererText(),
                                      text=self.COLUMN_STR)
    # コラムを追加
    self.append_column(self.col_str)
    # 複数行選択を可能にする
    self.get_selection().set_mode(gtk.SELECTION_MULTIPLE)

class MainWindow(gtk.Window):
  """
  メインウィンドウ
  """
  # 直接ウィンドウとは関係ないが、データは便宜上ここに用意しておくことにする
  data = \
  [
    ('AAA',),
    ('BBB',),
    ('CCC',),
    ('DDD',),
    ('EEE',),
  ]
  def __init__(self, *args, **kwargs):
    gtk.Window.__init__(self, *args, **kwargs)
    # ショートカットキー(アクセラレータ)
    self.accelgroup = gtk.AccelGroup()
    self.add_accel_group(self.accelgroup)
    # メニュー項目
    self.item_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accelgroup)
    self.menu_file = gtk.Menu()
    self.menu_file.add(self.item_quit)
    self.item_file = gtk.MenuItem('_File')
    self.item_file.set_submenu(self.menu_file)
    self.menubar = gtk.MenuBar()
    self.menubar.append(self.item_file)
    # ツリービュー
    self.treeview = TreeViewWithColumn(model=gtk.ListStore(str))
    self.treeview.set_rules_hint(True)
    # ツリービュー向けスクロールウィンドウ
    self.sw = gtk.ScrolledWindow()
    self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    self.sw.add(self.treeview)
    # ボタン
    self.button_top = gtk.Button(stock=gtk.STOCK_GOTO_TOP)
    self.button_up = gtk.Button(stock=gtk.STOCK_GO_UP)
    self.button_down = gtk.Button(stock=gtk.STOCK_GO_DOWN)
    self.button_bottom = gtk.Button(stock=gtk.STOCK_GOTO_BOTTOM)
    # レイアウト用コンテナ
    self.vbox_btn = gtk.VBox()  # ボタンを縦に並べる
    self.vbox_btn.pack_start(self.button_top)
    self.vbox_btn.pack_start(self.button_up)
    self.vbox_btn.pack_start(self.button_down)
    self.vbox_btn.pack_start(self.button_bottom)
    self.hbox = gtk.HBox()  # ツリービューとボタン群
    self.hbox.pack_start(self.sw)
    self.hbox.pack_start(self.vbox_btn, expand=False, fill=False)
    self.vbox = gtk.VBox()  # 全体
    self.vbox.pack_start(self.menubar, expand=False, fill=False)
    self.vbox.pack_start(self.hbox)
    # シグナル
    self.connect('delete_event', gtk.main_quit)
    self.item_quit.connect('activate', gtk.main_quit)
    self.button_top.connect('clicked', self.on_button_top_clicked)
    self.button_up.connect('clicked', self.on_button_up_clicked)
    #self.button_up.connect('clicked', self.on_button_up_clicked2)
    self.button_down.connect('clicked', self.on_button_down_clicked)
    self.button_bottom.connect('clicked', self.on_button_bottom_clicked)
    # データ追加
    for rec in self.data:
      self.treeview.get_model().append(rec)
    # ウィンドウ
    self.add(self.vbox)
    self.set_size_request(350, 300)
  def on_button_top_clicked(self, widget):
    """
    選択項目を一番上に移動
    """
    # 現在選択されている項目を取り出す
    (model, selected) = self.treeview.get_selection().get_selected_rows()
    iters = [model.get_iter(path) for path in selected]
    if len(iters) == 1:
      # move_after()の2番目の引数をNoneにすると一番上に移動する
      model.move_after(iters[0], None)
  def on_button_up_clicked(self, widget):
    """
    選択項目を1つ上に移動
    """
    # 現在選択されている項目を取り出す
    (model, selected) = self.treeview.get_selection().get_selected_rows()
    iters = [model.get_iter(path) for path in selected]
    if len(iters) == 1:
      # 先頭の項目を示しているか選択項目がない場合は何もしない
      if iters[0] \
      and model.get_path(iters[0]) != model.get_path(model.get_iter_first()):
        # 選択項目の1つ上(手前)の項目を探す
        # 逆方向にはたどれないので先頭から順にたどる
        iter = model.get_iter_first()
        while True:
          iter_next = model.iter_next(iter)
          # 1つ手前が選択項目と同じ位置であればそこで止まる
          if model.get_path(iter_next) == model.get_path(iters[0]):
            break
          iter = iter_next.copy()
        model.move_before(iters[0], iter)  # このときのiterが1つ手前
  def on_button_up_clicked2(self, widget):
    """
    選択項目を1つ上に移動・その2
    """
    # 現在選択されている項目を取り出す
    (model, selected) = self.treeview.get_selection().get_selected_rows()
    iters = [model.get_iter(path) for path in selected]
    if len(iters) == 1:
      if iters[0] \
      and model.get_path(iters[0]) != model.get_path(model.get_iter_first()):
        # ツリーパスのタプルの要素(ListStoreでは1階層だけなので0番のみ)を
        # 1引くことで手前の項目のツリーパスを得てTreeIterに変換する方法もある
        iter = model.get_iter(model.get_path(iters[0])[0] - 1)
        model.move_before(iters[0], iter)
  def on_button_down_clicked(self, widget):
    """
    選択項目を1つ下に移動
    """
    # 現在選択されている項目を取り出す
    (model, selected) = self.treeview.get_selection().get_selected_rows()
    iters = [model.get_iter(path) for path in selected]
    if len(iters) == 1:
      # 最後の項目を示しているか選択項目がない場合は何もしない
      if iters[0]:
        iter_next = model.iter_next(iters[0])
        if iter_next:
          model.move_after(iters[0], iter_next)
  def on_button_bottom_clicked(self, widget):
    """
    選択項目を一番下に移動
    """
    # 現在選択されている項目を取り出す
    (model, selected) = self.treeview.get_selection().get_selected_rows()
    iters = [model.get_iter(path) for path in selected]
    if len(iters) == 1:
      # move_before()の2番目の引数をNoneにすると一番下に移動する
      model.move_before(iters[0], None)

class PyGTKTreeViewListStoreTest5Multi:
  """
  リストを用いたツリービューのテスト5-2
  複数選択モードで単数選択時に移動できるようにしたもの
  """
  def main(self):
    """
    アプリケーションのメイン処理
    """
    win = MainWindow()
    win.show_all()
    gtk.main()


if __name__ == '__main__':
  app = PyGTKTreeViewListStoreTest5Multi()
  app.main()


右に並んでいるボタンから操作が行える。

メインウィンドウオブジェクトの書き方の微調整について

この記事の内容とは関係がないが、最近までgtk.main()を呼ぶ前のメインウィンドウオブジェクトを

win = MainWindow()

のように生成しながらそのオブジェクト(win)を使用していない書き方になっていたのでメンバ関数show_all()をここで呼ぶことにした。過去の記事の例についても修正を行った。

関連記事: