pygtkで遊ぼう(5) gtk.EventBox編

今日はgtk.EventBoxについてです。
gtk.EventBoxはとても地味なウィジェットです。ウインドウ上に配置しても何も描画されません。
かといって、HBoxやVBoxなどの配置用のウィジェットとも少し違っています。

なんといっていいのか難しいですが、ウィジェットにウインドウの機能を付加するためのもの。。
といった感じでしょうか。リファレンスによると、「ウインドウを持たないウィジェットのイベントをキャッチするために使用される」とあります。

最初は意味がよくわかりませんでしたが、今日ちょっとしたスクリプトを試していて実感しました。

以前、ここでも紹介したgtk.Frame。これをウインドウ上に配置するスクリプトを書いたとします。
そしてgtk.Frameがクリックされたら何かさせたい。。と思ったとします。
でも、gtk.Frameがクリックされたとしても、何のシグナルも発生しません。
gtk.Frameなどはそれ自信はウインドウをもたないことがその理由のようです)

そこでgtk.EventBoxの登場です。
gtk.Frameなどそれ自信ではイベントをハンドリングできないウィジェットgtk.EventBoxの上に作成することで、イベントをハンドリングできるようになります。

ということで、今回のちょっとしたスクリプトです。
このスクリプトが実際に何かの役に立つかどうかは。。。(笑

このスクリプトを実行すると、ウインドウ上に2つのgtk.Frameが作られます。
そして、gtk.Frame()をクリックしたままドラッグすると。。。移動できます。

# -*- coding: utf-8 -*-

import pygtk
pygtk.require('2.0')
import gtk

from threading import Timer

#
#
class CustomFrame(gtk.EventBox):
    ##
    def __init__(self, width, height, label=None):
        gtk.EventBox.__init__(self)

        self.dragTimer = None
        
        self.frame = gtk.Frame(label)
        self.add(self.frame)

        self.frame.set_size_request(width, height)

        self.connect('button_press_event', self.on_mouse_down)
        self.connect('button_release_event', self.on_mouse_up)
        
        self.show_all()

    ##
    def on_mouse_down(self, event, data):
        '''
        マウスボタンが押された時にタイマを作成します。
        '''
        parent = self.get_parent()
        if parent == None:
            return
        
        self.dragTimer = Timer(0.2, self.on_timer, [parent])
        self.dragTimer.start()

    ##
    def on_mouse_up(self, widget, signal_id):
        '''
        マウスボタンが離された時に、タイマをキャンセルします。
        '''
        if self.dragTimer:
            self.dragTimer.cancel()
            self.dragTimer = None
 
    ##
    def on_timer(self, *args):
        '''
        ドラッグ中にフレームの位置を動かします。
        '''
        parent = args[0]
        if parent == None:
            return
        if type(parent) != gtk.Layout:
            return
        
        (x, y) = parent.get_pointer()
        parent.move(self, x, y)

        self.dragTimer = Timer(0.2, self.on_timer, [parent])
        self.dragTimer.start()
        

#
#
class TestWindow:
    ##
    def __init__(self):
        self.wind = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.wind.set_size_request(400, 300)
        self.wind.connect('destroy', gtk.main_quit)
        
        # ウィジェット配置用
        self.layout = gtk.Layout()
        self.wind.add(self.layout)

        # ドラッグ可能なフレームを配置
        fr = CustomFrame(100, 40, '#1')
        self.layout.put(fr, 10, 10)

        fr = CustomFrame(100, 40, '#1')
        self.layout.put(fr, 10, 60)

        self.wind.show_all()

#
#
def main():
    win = TestWindow()

    gtk.gdk.threads_init()
    
    gtk.gdk.threads_enter()
    gtk.main()
    gtk.gdk.threads_leave()

#
#
if __name__ == '__main__':
    main()

ドラッグ中のタイマ処理の部分はあまりカッコよくないのですが。。それはそれとしてください。(笑

gtk.Layoutや、Timerなどについての説明は別の機会にまた。

つづく。