Glib の g_io_add_watch を使う

GUI プログラムであっても、パイプやソケットからコマンドやデータを受け取りたいことがある。コンソール・アプリケーションでは getline() 等でループを回せばいいのだが、GUI アプリケーションではそうはいかない。GUI のためのメッセージループが無限ループになっており、その内部で getline() というブロッキングIO を利用するわけにはいかないからだ。そのため、ノン・ブロッキングIO を使ったりといろいろ面倒が生じるのだが、GTK の場合は Glib の IOChannel 機能で簡単に実装できる。

以下のコードでは、コンソールから文字列を入力するたびにラベルが書き換わる。

import gtk, glib
import sys

class TestWindow:
    def __init__(self):
        self.window = gtk.Window()
        self.window.connect("destroy", gtk.main_quit)
        self.label = gtk.Label()
        self.label.set_label("received text will be shown here")
        self.window.add(self.label)
        self.window.show_all()
        self.i = 0
        glib.io_add_watch(sys.stdin, glib.IO_IN, self.update_label)

    def update_label(self, fd, condition):
        if condition == glib.IO_IN: # これ、必要なの?
            self.i += 1
            str = fd.readline().rstrip()
            print "called %d: %s" % (self.i, str)
            self.label.set_label(str)
        return True # False を返すと、この割り込みハンドラは破棄される

TestWindow()
gtk.main()

glib.io_add_watch に、ファイル・ディスクリプタを指定して監視対象にしておく。ここでは glib.IO_IN を監視して、イベントが発生するとハンドラ update_label を呼び出すようにした。ハンドラの内部では、引数として渡されるファイル・ディスクリプタを使って、好きなことができる。ハンドラの返り値は真偽値と定められており、False を返すと、そのハンドラは破棄される。