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

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

PyGObject/PyGTKにおけるGObjectプロパティの操作について

ここではgobject.GObjectクラスから派生した定義済みクラスにおけるGObjectプロパティの操作のみを扱い、自分でgobject.GObjectクラスを継承してプロパティを作り設定する手順については別記事にて扱う。

PyGObjectのプロパティ

PyGObjectPyGTKのリファレンスを見ると

[モジュール名].[クラス名] Properties

という一覧がある場合がある(例:gtk.Windowクラスの場合・一覧が存在しないクラスもある)。これがGObjectの世界におけるプロパティとなっており、扱いはPythonのプロパティとは異なる。

値の読み書きを行うメンバ関数

PyGObjectのgobject.GObjectクラスに

  • get_property(単数読み込み・値で受け取る)
  • get_properties(複数読み込み・タプルで受け取る)
  • set_property(単数書き込み)
  • set_properties(複数書き込み)

といったメンバ関数があり、PyGTKのほとんどのクラスを含め、ここから派生しているクラスではこれらのメンバ関数を用いて値の出し入れができる。ただし、リファレンスのプロパティ一覧においてプロパティ名の右に

  • 「Read-Write」もしくは「Read」と書かれているものにしかget_property()メンバ関数は使えない
  • 「Read-Write」もしくは「Write」と書かれているものにしかset_property()メンバ関数は使えない

という制限があり、各プロパティごとのアクセス許可の設定となっている。不正な操作をするとTypeError例外が発生し「TypeError: property [プロパティ名] is not [readable もしくは writable]」となる。

propsメンバ

PyGObjectのgobject.GObjectクラスとその派生クラス(PyGTKの各種クラス含む)にある「props」というメンバを用いると、更にその下のメンバとしてそのオブジェクトのGObjectプロパティにアクセスできる。読み書きのアクセスの制限は上のメンバ関数と同様に存在し、リファレンス内のアクセス許可が「Read」を含めば値の取り出しが可能となり、「Write」を含めば代入ができる。「props」を挟まずに直接プロパティ名のメンバにアクセスしてもダメなので注意。
例えば、PyGTKにおけるウィンドウのgtk.Windowクラスには「title」というプロパティがあり、ウィンドウのタイトルとして読み書きができるようになっていて

[gtk.Windowオブジェクト].props.title = 'window title'

とするとそのウィンドウのタイトルが「window title」になり

print [gtk.Windowオブジェクト].props.title

とするとウィンドウタイトルが取り出されて端末に表示される。
この形を用いることで、別に用意されている「set_[文字列]」といった名前のメンバ関数を呼ぶ代わりに値の代入で処理ができる場合がある。ただ、「set_[文字列]」といった名前のメンバ関数全てがGObjectプロパティで置き換えられるわけではない。値の取り出しについても同様。*1

プロパティ名にハイフンを含むと文法エラー?

プロパティにハイフン(「-」)文字が含まれている場合、get_properties(),set_properties()やpropsメンバを用いると「SyntaxError: can't assign to operator」となってうまくいかない。これについてはget_property()set_property()を用いてもよいが、ハイフンをアンダースコア(「_」)文字に置き換えるとうまくいくようだ。

下は「PythonでUNIXシグナルのハンドラを扱う(後半)」のコードをもとに、一部メンバ関数の呼び出しをpropsメンバを用いたGObjectプロパティの代入に置き換えたものとなる。

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

import signal
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.props.title = 'test'
    self.set_size_request(320, 200)
    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.props.submenu = self.__menu_file
    self.__menubar = gtk.MenuBar()
    self.__menubar.append(self.__item_file)
    self.__button = gtk.Button()
    self.__button.props.label = 'Test'
    self.__statusbar = gtk.Statusbar()
    self.__vbox = gtk.VBox(False, 0)
    self.__vbox.pack_start(self.__menubar, expand=False, fill=False)
    self.__vbox.pack_start(self.__button)
    self.__vbox.pack_start(self.__statusbar, expand=False, fill=False)
    self.add(self.__vbox)
    self.__button.connect('clicked', self.__on_button_clicked)
    self.__item_quit.connect('activate', gtk.main_quit)
    self.connect('delete_event', gtk.main_quit)
  def __on_button_clicked(self, widget):
    """
    ボタンが押されたときの処理
    """
    print '__on_button_clicked()'

class PyGTKUnixSigHandlerTest2Prop:
  """
  UNIXシグナルハンドラのテスト2 PyGObjectプロパティ使用版
  """
  def main(self):
    """
    ウィンドウを作成してGTK+のメインループを呼ぶ
    """
    # シグナルSIGINTを受け取った場合に
    # 例外KeyboardInterruptを出さずに終了する
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    win = MainWindow()
    win.show_all()
    gtk.main()


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

関連記事:

使用したバージョン:

*1:幾つかの「get_[文字列]」の名前のメンバ関数を用いる代わりに単なる値のように取り出せる場合がある