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

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

GtkBuilderとGladeについてと、PyGTK上での使用について(実際のコード・後半)

GtkBuilderとGladeについてと、PyGTK上での使用について(実際のコード・前半)」の続き。

メニュー項目の選択と選択解除のシグナル(select/deselect)は使えない?

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

import sys
import os
try:
  import pygtk
  pygtk.require("2.0")
except:
  pass
try:
  import gtk
except:
  print >> sys.stderr, "Error: PyGTK not installed"
  sys.exit(1)
if gtk.pygtk_version < (2,12,0):
  errtitle = "Error"
  errmsg = "PyGTK 2.12.0 or later required"
  if gtk.pygtk_version < (2,4,0):
    print >> sys.stderr, errtitle + ": " + errmsg
  else:
    errdlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_OK)
    errdlg.set_title(errtitle)
    errdlg.set_markup(errmsg)
    errdlg.run()
  sys.exit(1)


class PyGTKBuilderTest:
  """
  テスト
  """
  def __init__(self):
    """
    初期化処理
    """
    # uiファイル(同じディレクトリのpygtkbuildertest.ui)を読み込む
    uifile = os.path.dirname(__file__) + "/pygtkbuildertest.ui"
    xml = gtk.Builder()
    xml.add_from_file(uifile)
    # uiファイルで定義されているシグナルを自動で接続
    #xml.connect_signals(self)
    # ハンドラ「gtk_main_quit」は別途「gtk.main_quit()」に設定
    # インスタンス指定と辞書指定はどちらか一方しかできない?
    # 先に実行したほうだけが有効になっているような感じ
    signal_dic = { "on_button1_clicked" : self.on_button1_clicked,
                   "gtk_main_quit" : gtk.main_quit }
    xml.connect_signals(signal_dic)
    # 個別のウィジェットをXMLから取り出す
    self.window = xml.get_object("window1")
    self.imagemenuitem1 = xml.get_object("imagemenuitem1")
    # 手動でシグナル接続
    self.window.connect("delete_event", gtk.main_quit)
    # 下の2つは「unknown signal name」でダメ
    # (GtkActionになっていてGtkMenuItemではない)
    self.imagemenuitem1.connect("select", self.on_imagemenuitem1_select)
    self.imagemenuitem1.connect("deselect", self.on_imagemenuitem1_deselect)
    # 表示
    self.window.show_all()
  def on_button1_clicked(self, widget):
    """
    ボタンが押されたときの処理
    """
    print "on_button1_clicked"
  def on_imagemenuitem1_select(self, widget):
    """
    メニュー項目が選択されたときの処理(呼ばれない)
    """
    print "on_imagemenuitem1_select"
  def on_imagemenuitem1_deselect(self, widget):
    """
    メニュー項目が選択解除されたときの処理(呼ばれない)
    """
    print "on_imagemenuitem1_deselect"
  def main(self):
    """
    GTK+のメインループを呼ぶ
    """
    gtk.main()

if __name__ == "__main__":
  app = PyGTKBuilderTest()
  app.main()

今度はメニュー項目に相当するGUI部品(ウィジェット)を取り出した後、gobject.GObjectオブジェクトのメンバ関数connect()(リファレンス)を使用して、手動でシグナル接続を試みたのだが

Traceback (most recent call last):
  File "[スクリプトの場所]", line 81, in ?
    app = PyGTKBuilderTest()
  File "[スクリプトの場所]", line 55, in __init__
    self.imagemenuitem1.connect("select", self.on_imagemenuitem1_select)
TypeError: <gtk.Action object at 0x7fbc26cf2e60 (GtkAction at 0x839930)>: unknown signal name: select

のようになってダメだった。エラーが示すのは、gtk.Actionオブジェクトには「select」(「deselect」も同様)というシグナルはないということで、これは正しい。
gladeファイルを使用した場合、メニュー項目というウィジェットgtk.Itemから派生したクラスになるため、これらのシグナルが扱えるのだが、uiファイル/gtk.Builderを使用する場合、gtk-builder-convertでgladeファイルから変換するときにgtk.Actionにされてしまうため、使えなくなってしまうようだ(恐らくは仕様)。

動作する最小限サンプル

動作する形としては、下のような形に落ち着いた。gladeファイルを使用したときよりもできることが減ってしまった。

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

import sys
import os
try:
  import pygtk
  pygtk.require("2.0")
except:
  pass
try:
  import gtk
except:
  print >> sys.stderr, "Error: PyGTK not installed"
  sys.exit(1)
if gtk.pygtk_version < (2,12,0):
  errtitle = "Error"
  errmsg = "PyGTK 2.12.0 or later required"
  if gtk.pygtk_version < (2,4,0):
    print >> sys.stderr, errtitle + ": " + errmsg
  else:
    errdlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_OK)
    errdlg.set_title(errtitle)
    errdlg.set_markup(errmsg)
    errdlg.run()
  sys.exit(1)


class PyGTKBuilderTest:
  """
  テスト
  """
  def __init__(self):
    """
    初期化処理
    """
    # uiファイル(同じディレクトリのpygtkbuildertest.ui)を読み込む
    uifile = os.path.dirname(__file__) + "/pygtkbuildertest.ui"
    xml = gtk.Builder()
    xml.add_from_file(uifile)
    # uiファイルで定義されているシグナルを自動で接続
    #xml.connect_signals(self)
    # ハンドラ「gtk_main_quit」は別途「gtk.main_quit()」に設定
    # インスタンス指定と辞書指定はどちらか一方しかできない?
    # 先に実行したほうだけが有効になっているような感じ
    signal_dic = { "on_button1_clicked" : self.on_button1_clicked,
                   "gtk_main_quit" : gtk.main_quit }
    xml.connect_signals(signal_dic)
    # 個別のウィジェットをXMLから取り出す
    self.window = xml.get_object("window1")
    # 手動でシグナル接続
    self.window.connect("delete_event", gtk.main_quit)
    # 表示
    self.window.show_all()
  def on_button1_clicked(self, widget):
    """
    ボタンが押されたときの処理
    """
    print "on_button1_clicked"
  def main(self):
    """
    GTK+のメインループを呼ぶ
    """
    gtk.main()

if __name__ == "__main__":
  app = PyGTKBuilderTest()
  app.main()

関連記事:

参考URL:

使用したバージョン:

  • GTK+ 2.12.9(2.12.9-r2)
  • PyGTK 2.12.0
  • Glade 3.4.4