久しぶりにPyGTK。(gtk.NotebookのタブにCloseボタンを)


今日は、日頃からチクチクと作っているエディタで使っているgtk.NotebookにCloseボタンをつけて
見ました。
結構簡単なんですが、思わぬところでハマってしまいましたので、メモしておきます。


gkt.NotebookのタブにCloseボタンをつけるサンプルは、Googleで検索すると
結構簡単に見つかります。

こことか:http://coding.debuntu.org/python-gtk-how-customize-size-button-notebook-tab-label


でもですね、上記のサイトのやり方を試してみたのですがタブの縦方向の幅が高すぎてかなり不格好
な気がしました。
そこで、もう少し引き締まった感じにならないかなとためしてみました。


gtk.Notebookのタブには、文字列が表示されていることが多いのですが、
gtk.Widgetのサブクラスであれば何でもタブ上に置くことができます。


ということで、以下の方法では、タブに表示するためのカスタムウィジェット(gtk.HBoxのサブクラス)
を作ってみました。

■■ その1

この方法は、上記の参考サイトなどで紹介されている方法に近いです。


スクリプトはこんな感じです。

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

import sys

# GTK関連のインポート
import pygtk
if sys.platform != 'win32':
    pygtk.require('2.0')
import gtk
import gobject


class LabeledCloseBox(gtk.HBox):
    '''
    クローズボタン付きのラベル
    '''
    # gobjectのシグナル
    __gsignals__ = {
        'closed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        }

    
    def __init__(self, label, width=-1, height=-1):
        '''
        初期化処理。
        '''
        gtk.HBox.__init__(self)

        lb = gtk.Label(label)
        self.pack_start(lb, False, False, 2)

        img = gtk.image_new_from_stock(gtk.STOCK_CLOSE,
                                            gtk.ICON_SIZE_MENU)        
        bt = gtk.Button()
        bt.set_image(img)

        # ボタンの状態がわかりやすいようにコメントアウトしました。
        # bt.set_relief(gtk.RELIEF_NONE)

        w, h = gtk.icon_size_lookup_for_settings(bt.get_settings(),
                                                 gtk.ICON_SIZE_MENU)
        bt.set_size_request(w+2, h+2)
        self.pack_start(bt, False, False, 2)
        
        bt.connect('clicked', self.on_close_clicked)

        self.set_size_request(width, height)
        self.show_all()

gobject.type_register(LabeledCloseBox)

上記のウィジェットをNotebookのタブに配置してみると、


なんだかずれてますね。。
上記の

        w, h = gtk.icon_size_lookup_for_settings(bt.get_settings(),
                                                 gtk.ICON_SIZE_MENU)
        bt.set_size_request(w+2, h+2)

で行っているset_size_request()を行うとよくないみたいです。
理屈としては、きちんと動くはずなんですけど。。。。
ちなみに、その部分をコメントアウトして動かすと、

となり、無事に配置できました。が、これではクローズボタンの余白が多すぎます。
ということで、別の方法も試してみました。

■■ その2

ほとんどは「その1」と同じなのですが、gtk.Buttonにアイコンを設定する方法
をset_imageではなく、addを使ってみました。


スクリプトはこんな感じです。

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

import sys

# GTK関連のインポート
import pygtk
if sys.platform != 'win32':
    pygtk.require('2.0')
import gtk
import gobject


class LabeledCloseBox(gtk.HBox):
    '''
    クローズボタン付きのラベル
    '''
    # gobjectのシグナル
    __gsignals__ = {
        'closed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        }

    
    def __init__(self, label, width=-1, height=-1):
        '''
        初期化処理。
        '''
        gtk.HBox.__init__(self)

        lb = gtk.Label(label)
        self.pack_start(lb, False, False, 2)

        img = gtk.image_new_from_stock(gtk.STOCK_CLOSE,
                                            gtk.ICON_SIZE_MENU)        
        bt = gtk.Button()
        bt.add(img)       # <---- ここが変わっただけです。

        # ボタンの状態がわかりやすいようにコメントアウトしました。
        # bt.set_relief(gtk.RELIEF_NONE)

        w, h = gtk.icon_size_lookup_for_settings(bt.get_settings(),
                                                 gtk.ICON_SIZE_MENU)
        bt.set_size_request(w+2, h+2)
        self.pack_start(bt, False, False, 2)
        
        bt.connect('clicked', self.on_close_clicked)

        self.set_size_request(width, height)
        self.show_all()

gobject.type_register(LabeledCloseBox)


さて、上記のスクリプトを実行させると、

あら。。アイコンが切れてしまってますね。。

■■ 最後の手段

で、最終的には、以下の方法でなんとかなりました。
あまりかっこいい方法ではないですけどね。(笑い

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

import sys

# GTK関連のインポート
import pygtk
if sys.platform != 'win32':
    pygtk.require('2.0')
import gtk
import gobject


class LabeledCloseBox(gtk.HBox):
    '''
    '''
    __gsignals__ = {
        'closed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        }

    
    def __init__(self, label, width=-1, height=-1):
       '''
       '''
       gtk.HBox.__init__(self)

       lb = gtk.Label(label)
       self.pack_start(lb, False, False, 2)

       img = gtk.image_new_from_stock(gtk.STOCK_CLOSE,
                                      gtk.ICON_SIZE_MENU)

       # この方法のミソはこの部分。
       # アイコンであるimgをHBox, VBoxで二重に囲んでやります。
       imgBoxH = gtk.HBox(False, 0)
       imgBoxH.pack_start(img, True, False, 0)
       imgBoxV = gtk.VBox(False, 0)
       imgBoxV.pack_start(imgBoxH, True, False, 0)
        
       bt = gtk.Button()
       #bt.set_relief(gtk.RELIEF_NONE)
       bt.add(imgBoxV)

       w, h = gtk.icon_size_lookup_for_settings(bt.get_settings(),
                                                gtk.ICON_SIZE_MENU)
       bt.set_size_request(w, h)
       self.pack_start(bt, False, False, 2)
        
       bt.connect('clicked', self.on_close_clicked)

       self.set_size_request(width, height)
       self.show_all()

    def on_close_clicked(self, widget, data=None):
        self.emit('closed')


gobject.type_register(LabeledCloseBox)

コメントにも書きましたが、HBox, VBoxを使って余分な空白部分を
なくしました。


実行すると、こんな感じです。

ずいぶん余白が減りました。(笑
縦方向に少し長いようですが、Notebookに複数のタブがある場合、現在選択されているタブ以外の
高さは少し低くなるので、アイコンが切れないためにもこのくらいの余白はあってもいいかと。

■■ おまけ

以下のようにしてNotebookのプロパティを設定すると少しは余白を少なくすることができます。

nb = gtk.Notebook()
nb.set_scrollable(True)
nb.set_property('tab-border', 0)
nb.set_property('tab-hborder', 0)
nb.set_property('tab-vborder', 0)

それぞれのプロパティの詳細については、PyGTKのリファレンスをご覧ください。


いやはや、「なんでやのん?」っていう動きにビックリすることも
ありますが、PyGTK、いいツールキットですね!