PyGTKの簡単な例をPythonのコードのみで書く
以前はgladeファイルやuiファイルを使用してユーザインターフェース部分をコードから分離して書いたのだが、分離させずにPythonのコードのみで記述することももちろんできる。ただし、書くのは色々と面倒。
コード
「PyGTK + Gladeの簡単な例」をもとにしている。
[任意]ファイル名: pygtktest-noglade.py
#! /usr/bin/python # -*- encoding: utf-8 -*- import sys import os try: import pygtk pygtk.require("2.0") except: pass try: import gtk except: sys.exit(1) class PyGTKTestNoGlade: """ テスト """ def __init__(self): """ 初期化処理 """ # ウィンドウ self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) # 種類をトップレベルに self.window.set_title("Test") # タイトル self.window.set_size_request(320, 200) # 最小サイズ #self.window.set_default_size(320, 200) # 既定のサイズを指定する場合 # ショートカットキー(アクセラレータ) self.accelgroup = gtk.AccelGroup() self.window.add_accel_group(self.accelgroup) # ウィンドウに関連付け # メニューバー項目 # 「ファイル」の項目 self.menuitem = gtk.MenuItem("_File", True) # 真偽値は下線の処理の有無 # 「終了」のストックアイコン/ショートカット付きメニュー項目 # ストックIDの部分は"gtk-quit"のような文字列でもよい # gtk.ImageMenuItemはgtk.MenuItemの子クラス self.imagemenuitem = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accelgroup) #self.imagemenuitem = gtk.ImageMenuItem("gtk-quit", self.accelgroup) # 以下、メニューを構成していく self.gtkmenu = gtk.Menu() # ファイルのサブメニュー self.gtkmenu.add(self.imagemenuitem) # サブメニューに終了を追加 self.menuitem.set_submenu(self.gtkmenu) # ファイルの下にサブメニュー self.menubar = gtk.MenuBar() # メニューバーウィジェット self.menubar.append(self.menuitem) # バーにファイルメニューを追加 # ボタン self.button = gtk.Button() self.button.set_label("Test") # ステータスバー self.statusbar = gtk.Statusbar() # 垂直ボックス(コンテナ=入れ物 の一種) # 2つの真偽値(2番目と3番目の引数)はそれぞれ # expand(場所を最大限広く確保するか)とfill(部品を最大限広く描画するか) # (厳密には親子ウィジェット間のスペースの使い方として定義されている) # pack_start()は手前から詰めていく形でpack_end()は後ろから詰めていく形 # http://www.pygtk.org/docs/pygtk/class-gtkbox.html self.vbox = gtk.VBox(False, 0) # 真偽値は子ウィジェットを均等に配置するか # 上からメニューバー/ボタン/ステータスバーの順で入れる self.vbox.pack_start(self.menubar, False, True, 0) self.vbox.pack_start(self.button, True, True, 0) self.vbox.pack_start(self.statusbar, False, True, 0) self.window.add(self.vbox) # 空のウィンドウの中に垂直ボックスを入れる # シグナルを手動で接続 self.button.connect("clicked", self.on_button_clicked) self.imagemenuitem.connect("select", self.on_imagemenuitem_select) self.imagemenuitem.connect("deselect", self.on_imagemenuitem_deselect) self.imagemenuitem.connect("activate", gtk.main_quit) self.window.connect("delete_event", gtk.main_quit) # 表示 self.window.show_all() def on_button_clicked(self, widget): """ ボタンが押されたときの処理 """ print "on_button1_clicked" def on_imagemenuitem_select(self, widget): """ メニュー項目が選択されたときの処理 """ print "on_imagemenuitem1_select" def on_imagemenuitem_deselect(self, widget): """ メニュー項目が選択解除されたときの処理 """ print "on_imagemenuitem1_deselect" def main(self): """ GTK+のメインループを呼ぶ """ gtk.main() if __name__ == "__main__": app = PyGTKTestNoGlade() app.main()
(2009/4/17)その後の色々なコード例ではメインウィンドウなどの部品のオブジェクトについてクラスの継承を用いている。これによりコードがより書きやすく分かりやすくなる。
GUIのプログラムにおいて部品などのクラスを継承することは色々なところで役に立つということが分かってきた。
上のコードは
#! /usr/bin/python # -*- encoding: utf-8 -*- import sys import os try: import pygtk pygtk.require("2.0") except: pass try: import gtk except: sys.exit(1) class MainWindow(gtk.Window): """ メインウィンドウ """ def __init__(self, *args, **kwargs): """ 初期化処理 """ gtk.Window.__init__(self, *args, **kwargs) # 親クラスの初期化が必須 self.set_title("Test") # タイトル self.set_size_request(320, 200) # 最小サイズ #self.set_default_size(320, 200) # 既定のサイズを指定する場合 # ショートカットキー(アクセラレータ) self.accelgroup = gtk.AccelGroup() self.add_accel_group(self.accelgroup) # ウィンドウに関連付け # メニューバー項目 # 「ファイル」の項目 self.menuitem = gtk.MenuItem("_File", True) # 真偽値は下線の処理の有無 # 「終了」のストックアイコン/ショートカット付きメニュー項目 # ストックIDの部分は"gtk-quit"のような文字列でもよい # gtk.ImageMenuItemはgtk.MenuItemの子クラス self.imagemenuitem = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.accelgroup) #self.imagemenuitem = gtk.ImageMenuItem("gtk-quit", self.accelgroup) # 以下、メニューを構成していく self.gtkmenu = gtk.Menu() # ファイルのサブメニュー self.gtkmenu.add(self.imagemenuitem) # サブメニューに終了を追加 self.menuitem.set_submenu(self.gtkmenu) # ファイルの下にサブメニュー self.menubar = gtk.MenuBar() # メニューバーウィジェット self.menubar.append(self.menuitem) # バーにファイルメニューを追加 # ボタン self.button = gtk.Button() self.button.set_label("Test") # ステータスバー self.statusbar = gtk.Statusbar() # 垂直ボックス(コンテナ=入れ物 の一種) # 2つの真偽値(2番目と3番目の引数)はそれぞれ # expand(場所を最大限広く確保するか)とfill(部品を最大限広く描画するか) # (厳密には親子ウィジェット間のスペースの使い方として定義されている) # pack_start()は手前から詰めていく形でpack_end()は後ろから詰めていく形 # http://www.pygtk.org/docs/pygtk/class-gtkbox.html self.vbox = gtk.VBox(False, 0) # 真偽値は子ウィジェットを均等に配置するか # 上からメニューバー/ボタン/ステータスバーの順で入れる self.vbox.pack_start(self.menubar, False, True, 0) self.vbox.pack_start(self.button, True, True, 0) self.vbox.pack_start(self.statusbar, False, True, 0) self.add(self.vbox) # 空のウィンドウの中に垂直ボックスを入れる # シグナルを手動で接続 self.button.connect("clicked", self.on_button_clicked) self.imagemenuitem.connect("select", self.on_imagemenuitem_select) self.imagemenuitem.connect("deselect", self.on_imagemenuitem_deselect) self.imagemenuitem.connect("activate", gtk.main_quit) self.connect("delete_event", gtk.main_quit) def on_button_clicked(self, widget): """ ボタンが押されたときの処理 """ print "on_button1_clicked" def on_imagemenuitem_select(self, widget): """ メニュー項目が選択されたときの処理 """ print "on_imagemenuitem1_select" def on_imagemenuitem_deselect(self, widget): """ メニュー項目が選択解除されたときの処理 """ print "on_imagemenuitem1_deselect" class PyGTKTestNoGladeWithInheritance: """ クラスの継承を用いた場合のGladeを用いないテスト """ def main(self): """ ウィンドウを作成してGTK+のメインループを呼ぶ """ win = MainWindow(gtk.WINDOW_TOPLEVEL) # gtk.WINDOW_TOPLEVELは省略可 win.show_all() gtk.main() if __name__ == "__main__": app = PyGTKTestNoGladeWithInheritance() app.main()
のようになる。MainWindowクラス内で「self.window.[メンバ]」と書いていたものが「self.[メンバ]」のように変わっている。
メニュー
メニューはgtk.UIManagerクラスを使用することで、XMLの記述により簡単に構成することができる(PyGTK 2.4以上が必要)のだが、今回はそれを使用せず、原始的な方法で書くことにした。
「終了」という1つの項目で構成される「ファイル」メニューを含むメニューバーを作るのには
- 「ファイル」のメニュー項目(gtk.MenuItem)
- 「終了」のメニュー項目(gtk.ImageMenuItem)*1
- 「ファイル」を展開したときに出るメニュー(gtk.Menu)
- ウィンドウ上に配置するメニューバー(gtk.MenuBar)
これだけのものが必要になる。
「終了」のメニュー項目に関しては、アイコンテーマに含まれる「終了」のアイコン*2とCtrl+Qというショートカットキーの設定を適用することができる。そのためには、コードのように「ストックID」と「ウィンドウに関連付け済みのアクセラレータのグループ(gtk.AccelGroupオブジェクト)」を指定する。
シグナル接続(ユーザのアクションを種類ごとに処理に関連付ける)
以前にも書いているように、手動でのシグナル接続としてgobject.GObjectオブジェクトのメンバ関数connect()(リファレンス)を使用する。
垂直ボックス
GTK+アプリケーションでは、場所を分割するために垂直ボックス(gtk.VBox)や水平ボックス(gtk.HBox)などのコンテナ(入れ物)ウィジェットを使用することが多い。
コードを書いてユーザインターフェースのレイアウトを構成していく場合でも、Gladeでグラフィカルに作業していくのと感覚としては同じで、一度Gladeで完成形を作っておき、その構造通りにコードを書いていけば、基本的には同じものが作れる。
今回はgladeファイルのときの例にならって垂直ボックスを3分割してメニュー/ボタン/ステータスバーを入れていった。
gtk.HBoxやgtk.VBoxのコンストラクタ*3では2つの引数を渡し、1つ目は真偽値で子ウィジェットを均等に配置するかどうか、2つ目は余白スペースとなる。
pack_start()は手前から順に子ウィジェットを中に入れていく。注意するのは2番目と3番目の引数で、これらはGladeでは「パッキング」タブにある「展張」と「フィル」にそれぞれ相当する。スペースの取り方が変わるのだが、Glade上でいじってみて確認するのが分かりやすい。
pack_end()は後ろから順に入れる点を除いてpack_start()と同じ。
ウィンドウの最小サイズ
Gladeでは「共通」タブの「要求する幅」「要求する高さ」で最小サイズを指定したのだが、コードではgtk.Widgetクラスのset_size_request()(リファレンス)で指定する。
関連記事:
- PyGTK + Gladeの簡単な例
- GtkBuilderとGladeについてと、PyGTK上での使用について(概要)
- GtkBuilderとGladeについてと、PyGTK上での使用について(実際のコード・前半)
- GtkBuilderとGladeについてと、PyGTK上での使用について(実際のコード・後半)
参考URL:
- PyGObject リファレンス: gobject.GObject
- PyGTK リファレンス: gtk.Box
- PyGTK リファレンス: gtk.HBox
- PyGTK リファレンス: gtk.VBox
- PyGTK リファレンス: gtk.Menu
- PyGTK リファレンス: gtk.MenuBar
- PyGTK リファレンス: gtk.MenuItem
- PyGTK リファレンス: gtk.ImageMenuItem
使用したバージョン:
- Glade 3.4.4