pygtkで遊ぼう(13) メニューバーで階層メニュー 編

最近作っているファイルコピーツールのメニューをいじっていて、「そういえば、サブメニューのあるメニューバーって作ったことないな。。」ということに気づきました。

以前、pygtkで遊ぼう(6) メニュー編 でメニューバーについて書きました。
その時のメニューバーは単純なメニューバーで、サブメニューのあるメニュー項目のないものでした。

今回は、その時作ったサンプルを拡張して、サブメニューのあるメニューバーにしてみました。


ソースコードはこんな感じです。

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

import sys
import pygtk

if sys.platform != 'win32':
    pygtk.require('2.0')
import gtk

#
#
class simpleWindow:
    '''
    テスト用ウインドウ
    '''

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_border_width(2)
        self.window.set_size_request(400, 300)

        self.window.connect('delete_event', self.on_quit)

        vbox = gtk.VBox()

        menubar = self.get_main_menu()
        vbox.pack_start(menubar, False, False, 0)

        layout = gtk.Layout()
        vbox.pack_start(layout)
        
        self.window.add(vbox)
        self.window.show_all()
        
        gtk.main()

    def get_main_menu(self):
        '''
        メインメニュー構築処理
        '''
        ## メニューバーの定義
        mainMenu = '''
        
            
                
                    
                    
                

                
                    
                    
                

                
                    
                    
                


                
                    
                        
                            
                            
                        
                    

                    
                        
                            
                            
                        
                    
                    
                    
                        
                            
                            
                        
                    
                
            
        
        '''

        # UIManagerインスタンスの再生
        uimgr = gtk.UIManager()
        
        # アクセラレータグループをウインドウに追加
        acclgrp = uimgr.get_accel_group()
        self.window.add_accel_group(acclgrp)

        # アクショングループの作成
        actgrpBase = gtk.ActionGroup('MenuActionBase')
        actgrpBase.add_actions([('Normal Menu', None, '_Normal Menu'),
                                ('Toggle Menu', None, '_Toggle Menu'),
                                ('Radio Menu', None, '_Radio Menu'),
                                ('PlaceHolders', None, '_PlaceHolders'),
                                ('Normal setting', None, '_Normal setting'),
                                ('Toggle setting', None, '_Toggle setting'),
                                ('Radio setting', None, '_Radio setting')])

        uimgr.insert_action_group(actgrpBase, 0)

        # ノーマルメニューアクションの登録
        actgrpBase.add_actions([('normal item 1', None,
                                 'item _1', None,
                                 'sample item for normal menu action',
                                 self.on_normal_item1),

                                ('normal item 2', None,
                                 'item _2', None,
                                 'sample item for normal menu action',
                                 self.on_normal_item2)])
                                
        # トグルメニューアクションの登録
        actgrpBase.add_toggle_actions([('toggle item 1', None,
                                        'item _1', None,
                                        'sample item for toggle menu action',
                                        self.on_toggle_item1,
                                        True),

                                       ('toggle item 2', None,
                                        'item _2', None,
                                        'sample item for toggle menu action',
                                        self.on_toggle_item2,
                                        True)])

        # Modeメニューアクションの登録
        actgrpBase.add_radio_actions([('radio item 1', None,
                                       'item _1', None,
                                       'sample item for radio menu action',
                                       0),

                                      ('radio item 2', None,
                                       'item _2', None,
                                       'sample item for radio menu action',
                                       1)],
                                     0,
                                     self.on_radio_menu_changed)

        # PlaceHoldersメニューアクションの登録
        actgrpBase.add_actions([('normal holder item 1', None,
                                 'holder item _1', None,
                                 'sample item for normal sub menu action',
                                 self.on_normal_holder_item1),

                                ('normal holder item 2', None,
                                 'holder item _2', None,
                                 'sample item for normal sub menu action',
                                 self.on_normal_holder_item2)])

        actgrpBase.add_toggle_actions([('toggle holder item 1', None,
                                        'holder item _1', None,
                                        'sample item for toggle menu action',
                                        self.on_toggle_holder_item1,
                                        True),

                                       ('toggle holder item 2', None,
                                        'holder item _2', None,
                                        'sample item for toggle menu action',
                                        self.on_toggle_holder_item2,
                                        True)])

        actgrpBase.add_radio_actions([('radio holder item 1', None,
                                       'holder item _1', None,
                                       'sample item for radio menu action',
                                       0),

                                      ('radio holder item 2', None,
                                       'holder item _2', None,
                                       'sample item for radio menu action',
                                       1)],
                                     0,
                                     self.on_radio_holder_menu_changed)
        
        uimgr.add_ui_from_string(mainMenu)
        
        return uimgr.get_widget('/MenuBar')        

    def on_normal_item1(self, widget, event=None, data=None):
        '''
        normal item 1が選択された場合の処理
        '''
        print 'normal item 1'

    def on_normal_item2(self, widget, event=None, data=None):
        '''
        normal item 2が選択された場合の処理
        '''
        print 'normal item 2'

    def on_toggle_item1(self, action, data=None):
        '''
        toggle item 1が選択された場合の処理
        '''
        print 'toggle item 1 is %s' % action.get_active()
        print data

    def on_toggle_item2(self, action, data=None):
        '''
        toggle item 2が選択された場合の処理
        '''
        print 'toggle item 2 is %s' % action.get_active()
        print data

    def on_radio_menu_changed(self, action, current, data=None):
        '''
        Radioメニューの選択が変更された場合の処理
        '''
        print 'radio item %d selected' % action.get_current_value()

    def on_normal_holder_item1(self, widget, event=None, data=None):
        '''
        normal item 1が選択された場合の処理
        '''
        print 'normal holder item 1'

    def on_normal_holder_item2(self, widget, event=None, data=None):
        '''
        normal item 2が選択された場合の処理
        '''
        print 'normal holder item 2'

    def on_toggle_holder_item1(self, action, data=None):
        '''
        toggle item 1が選択された場合の処理
        '''
        print 'toggle holder item 1 is %s' % action.get_active()
        print data

    def on_toggle_holder_item2(self, action, data=None):
        '''
        toggle item 2が選択された場合の処理
        '''
        print 'toggle holder item 2 is %s' % action.get_active()
        print data

    def on_radio_holder_menu_changed(self, action, current, data=None):
        '''
        Radioメニューの選択が変更された場合の処理
        '''
        print 'radio holder item %d selected' % action.get_current_value()
    
    def on_quit(self, widget, event=None, data=None):
        print '-- quit --'
        gtk.main_quit()
        return False

#
#
def main():
    wind = simpleWindow()
    
#
#
if __name__ == '__main__':
    main()

メニュー項目にサブメニューを作る場合のポイントは、

・UIManagerに渡すXML文字列中でタグをを利用する。
のnameは画面上には表示されない。
の子供の階層に

を記述すると、それがサブメニューとなる。
・サブメニュー用のアクション追加方法は、通常のメニューの場合と全く同じ。

と言った感じです。

思ったよりも簡単でよかったです。

pygtkで遊ぼうシリーズでは、今のところGladeなどのUI作成ツールを全く使っていません。
やっぱり、仕組みを理解するには、ゴリゴリと書くのが一番だと思うので(笑
それに、ゴリゴリと書いたとしても、あまりストレスなく作れてしまうので、小さいアプリを作るぶんにはそのほうが簡単かもしれません。

一通りのウィジェットで遊び終わったら、今度はGladeを使って遊ぼうと思っています。

さて、コピーツールの続きをやろうかな。(笑