UP | HOME

メニューとツールバー

Table of Contents

メニューバーを作る

メニューバーはGUIアプリケーションの中で最も視覚的なパーツで、 いろんなメニューの中にあるコマンドをグループ化してまとめたものです。 コンソールアプリケーションでは、全ての難解なコマンドを覚えなければなりませんでした。 GUIアプリケーションでは、大半のコマンドが理路整然とグループ分けされています。 新しいアプリケーションを習得するための時間を減らすべきだ、という考え方が広く受け入れられてきています。 メニューバーをwxPythonで実装するためには、wx.MenuBar, wx.Menu, wx.MenuItem の3つが必要です。

./img/menu.jpg

簡単なメニューの例

wxPythonでは数行コードを書くだけで、簡単にメニューバーを作ることができます。

#! /usr/bin/env python

# simplemenu.py

import wx

class SimpleMenu(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(250, 150))
        
        menubar = wx.MenuBar()
        file = wx.Menu()
        file.Append(-1, 'Quit', 'Quit application')
        menubar.Append(file, '&File')
        self.SetMenuBar(menubar)
        
        self.Centre()
        self.Show(True)

app = wx.App()
SimpleMenu(None, -1, 'simplemenu.py')
app.MainLoop()
menubar  = wx.Menubar()   

初めにメニューバーオブジェクトを生成します。

file = wx.Menu()   

次にメニューオブジェクトを生成します。

file.Append(-1, 'Quit', 'Quit application')

メニューアイテムをメニューオブジェクトに追加します。 最初の引数はメニューアイテムのIDです。 2番目の引数はメニューアイテムの名前です。 最後の引数では、アイテムが選択されたときにステータスバーに表示される、短いヘルプ文字列を定義します。 この時点では、まだ wx.MenuItem を作成していません。 Append() メソッドを呼び出すことで、バックグラウンドで作成されます。 あとで wx.MenuItem を手動で作成しましょう。

menubar.Append(file, '&File')
self.SetMenubar(menubar)

次にメニューをメニューバーに追加します。 &はショートカットキーを作成します。 &に続く文字がアンダーライン付けされて、 alt + F のショートカットでアクセスしやすくなります。 最後にwx.Frameウィジェットから SetMenuBar() メソッドを呼び出して、メニューバーをセットしましょう。

./img/simplemenu.png

くっ付くメニューバー

Linux環境では、ドッキング可能なメニューバーを作ることができます。 この機能はどのアプリケーションでも使えるわけではありませんが、 似たようなことはMac OSでも可能です。 Macユーザーはメニューバーをトップレベルのアプリケーションウィンドウに配置しません。 メニューバーはメインウィンドウの外に置かれます。

#! /usr/bin/env python

# dockable.py

import wx

class Dockable(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title)
        menubar = wx.MenuBar(wx.MB_DOCKABLE)
        file = wx.Menu()
        edit = wx.Menu()
        view = wx.Menu()
        insr = wx.Menu()
        form = wx.Menu()
        tool = wx.Menu()
        help = wx.Menu()

        menubar.Append(file, '&File')
        menubar.Append(edit, '&Edit')
        menubar.Append(view, '&View')
        menubar.Append(insr, '&Insert')
        menubar.Append(form, '&Format')
        menubar.Append(tool, '&Tools')
        menubar.Append(help, '&Help')

        self.SetMenuBar(menubar)
        self.Center()
        self.Show(True)

app = wx.App()
Dockable(None, -1, 'dockable.py')
app.MainLoop()
menubar = wx.Menubar(wx.MB_DOCKABLE)

wx.MB_DOCKABLEフラグをコンストラクタに渡すことで、ドッキング可能なメニューバーを作ることができます。

./img/dockable.jpg

アイコン、ショートカット、イベント

次の章ではメニュー例をもっと発展させていきます。

始めに、アイコンをメニューに追加する方法を見ていきましょう。 アイコンがあると、私たちのアプリケーションの外見は魅力的になります。 そして、メニューのコマンドがどんなものであるか理解しやすくなります。

次に、メニューにショートカットを追加する方法も見ていきます。 ショートカットは、過去の遺物ではありません。 ショートカットのおかげで、私たちはアプリケーションをテキパキと使うことができるのです。 最もよく使われるショートカットのひとつに、 Ctrl + S があります。 このショートカットの意味を知らない人は少ないと思います。 マウスポインタをメニューバーに持って行き、 [ファイル]メニューをクリックし[保存]コマンドを選ぶよりも、 ショートカットコマンドを押すほうがはるかにお手軽です。 ショートカットはユーザーを生産的にする後押しをしてくれます。

最後は、イベントについて少し触れておきます。

#! /usr/bin/env python

# menuexample.py

import wx

class MenuExample(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(250, 150))

        menubar = wx.MenuBar()
        file = wx.Menu()
        quit = wx.Menu()
        quit = wx.MenuItem(file, 1, '&Quit\tCtrl+Q')
        quit.SetBitmap(wx.Bitmap('icons/exit.png'))
        file.AppendItem(quit)

        self.Bind(wx.EVT_MENU, self.OnQuit, id=1)

        menubar.Append(file, '&File')
        self.SetMenuBar(menubar)

        self.Centre()
        self.Show(True)
        
    def OnQuit(self, event):
        self.Close()

app = wx.App()
MenuExample(None, -1, 'menuexample.py')
app.MainLoop()

quit = wx.MenuItem(file, 1, '&Quit\tCtrl+Q')
quit.SetBitmap(wx.Bitmap('icons/exit.png'))
file.AppendItem(quit)

ショートカットをメニューに追加したいときは、手動で wx.MenuItem を作る必要があります。 メニューアイテムを直接生成しないときは、 & 文字によってショートカットキーを設定します。 &に続く文字にアンダーラインが引かれます。 実際のショートカットは、文字の組み合わせで定義されます。 私たちは"Ctrl+Q"という文字列を設定したので、Ctrl + Q を押せばアプリケーションを終了することができます。 & に続く文字列とショートカットの間にはタブ文字を入力しておきます。 こうすることで、これら文字列の間に空白を入れて調整することができます。 メニューアイテムにアイコンを追加するには、 SetBitmap() メソッドを呼び出します。 手動で生成したメニューアイテムをメニューに追加するには、 AppendItem() メソッドを呼び出します。

self.Bind(wx.EVT_MENU, self.OnQuit, id=1)

終了メニューアイテムを選択したりショートカットを押すと、 wx.EVT_MENU イベントが生成されます。 イベントハンドラとイベントを関連付けていきます。 イベントハンドラは呼び出されるメソッドです。 私たちの例では、 OnQuit() メソッドがアプリケーションを終了させます。 メニューアイテムは複数になるでしょうから、それぞれに固有のIDを与えておく必要があります。 wxPythonでのイベント処理は非常に簡単で単純です。 イベントについては、後ほど別の章でお話します。

./img/menuexample.png

サブメニュー

メニューはそれぞれにサブメニューを持つことができます。 こうすることで、用途の似たコマンドをグループ化出来るのです。 たとえば、パーソナルバー・アドレスバー・ステータスバー・ナビゲーションバーといった、様々なツールバーの表示/非表示を切り替えるコマンドを、[ツールバー]というサブメニューに配置することができます。 メニューの中ではセパレータを使ってコマンドを分けて配置することができます。 [新規作成]・[ファイルを開く]・[保存]といったコマンドと、[印刷]・[印刷プレビュー]のようなコマンドを分けておくのが慣例となっています。 以下の例を通じて、サブメニューとメニューセパレータの作り方を勉強していきましょう。

#! /usr/bin/env python

# submenu.py

ID_QUIT = 1

class SubMenuExample(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(350, 250))

        menubar = wx.Menubar()

        file = wx.Menu()
        file.Append(-1, '&New')
        file.Append(-1, '&Open')
        file.Append(-1, '&Save')
        file.AppendSeparator()

        imp = wx.Menu()
        imp.Append(-1, 'Import newsfeed list...')
        imp.Append(-1, 'Import bookmarks...')
        imp.Append(-1, 'Import mail...')

        file.AppendMenu(-1, 'I&mport', imp)

        quit = wx.MenuItem(file, ID_QUIT, '&Quit\tCtrl+W')
        quit.SetBitmap(wx.Bitmap('./icons/exit.png'))
        file.AppendItem(quit)

        self.Bind(wx.EVT_MENU, self.OnQuit, id=ID_QUIT)

        menubar.Append(file, '&File')
        self.SetMenuBar(menubar)

        self.Centre()
        self.Show(True)

    def OnQuit(self, event):
        self.Close()

app = wx.App()
SubMenuExample(None, -1, 'submenu.py')
app.MainLoop()
file.AppendSeparator()

メニューセパレータは AppendSeparator() メソッドによって追加されます。

imp = wx.Menu()
imp.Append(-1, 'Import newsfeed list...')
imp.Append(-1, 'Import bookmarks...')
imp.Append(-1, 'Import mail...')

file.AppendMenu(-1, 'I&mport', imp)

サブメニューを作るのも非常に簡単です。 まずメニューを作り、次にそのメニューに対してメニューアイテムを追加していきます。 サブメニューは、メニューオブジェクトから AppendMenu() を呼び出すことで作ることができます。

./img/submenu.png

さまざまなメニューアイテム

メニューの項目は3つに分類されます。

  • 通常の項目
  • チェック項目
  • ラジオ項目
#! /usr/bin/env python

# checkmenuitem.py

import wx

ID_STAT = 1
ID_TOOL = 2

class CheckMenuItem(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(350, 250))

        menubar = wx.MenuBar()
        file = wx.Menu()
        view = wx.Menu()
        self.shst = view.Append(ID_STAT, 'Show statusbar', 'Show Statusbar', kind=wx.ITEM_CHECK)
        self.shtl = view.Append(ID_TOOL, 'Show toolbar', 'Show Toolbar', kind=wx.ITEM_CHECK)
        view.Check(ID_STAT, True)
        view.Check(ID_TOOL, True)

        self.Bind(wx.EVT_MENU, self.ToggleStatusBar, id=ID_STAT)
        self.Bind(wx.EVT_MENU, self.ToggleToolBar, id=ID_TOOL)

        menubar.Append(file, '&File')
        menubar.Append(view, '&View')
        self.SetMenuBar(menubar)

        self.toolbar = self.CreateToolBar()
        self.toolbar.AddLabelTool(3, '', wx.Bitmap('icons/exit.png'))
        self.toolbar.Realize()

        self.statusbar = self.CreateStatusBar()
        self.Centre()
        self.Show(True)

    def ToggleStatusBar(self, event):
        if self.shst.IsChecked():
            self.statusbar.Show()
        else:
            self.statusbar.Hide()

    def ToggleToolBar(self, event):
        if self.shtl.IsChecked():
            self.toolbar.Show()
        else:
            self.toolbar.Hide()

app = wx.App()
CheckMenuItem(None, -1, 'checkmenuitem.py')
app.MainLoop()
self.shst = view.Append(ID_STAT, 'Show statubar', 'Show Statusbar', kind=wx.ITEM_CHECK)
self.shtl = view.Append(ID_TOOL, 'Show toolbar', 'Show Toolbar', kind=wx.ITEM_CHECK)

チェック項目を追加したいときは、kind変数に wx.ITEM_CHECK を設定します。 デフォルトの値は wx.ITEM_NORMAL です。 Append() メソッドは wx.MenuItem 型の値を返します。

view.Check(ID_STAT, True)
view.Check(ID_TOOL, True)

アプリケーションが開始されると、ステータスバーとツールバーが見えるようになります。 これは両方のメニュー項目を Check() メソッドで表示状態にしているからです。

def ToggleStatusBar(self, event):
    if self.shst.IsChecked():
        self.statusbar.Show()
    else:
        self.statusbar.Hide()

チェック項目の状態によって、ステータスバーを表示したり隠したりできます。 項目がチェックされているか調べるには、 IsChecked() メソッドを呼び出します。 ツールバーについても同じです。

./img/checkmenuitem.png

コンテキストメニュー

コンテキストメニューとは、ある状況下で現われるコマンドのリストのことです。 ポップアップメニューとも呼ばれます。 例えばwebブラウザのFirefoxで、webページ上を右クリックすると、コンテキストメニューが表示されます。 コンテキストメニューでは、ページを再読み込みやソースコードを閲覧することができます。 ツールバー上で右クリックすると、ツールバーを管理するための別のコンテキストメニューが出てきます。

#! /usr/bin/env python

# contextmenu.py

import wx

class MyPopoupMenu(wx.Menu):
    def __init__(self, parent):
        wx.Menu.__init__(self)

        self.parent = parent

        minimize = wx.MenuItem(self, wx.NewId(), 'Minimize')
        self.AppendItem(minimize)
        self.Bind(wx.EVT_MENU, self.OnMinimize, id=minimize.GetId())

        close = wx.MenuItem(self, wx.NewId(), 'Close')
        self.AppendItem(close)
        self.Bind(wx.EVT_MENU, self.OnClose, id=close.GetId())

    def OnMinimize(self, event):
        self.parent.Iconize()
    
    def OnClose(self, event):
        self.parent.Close()

class ContextMenu(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(250, 150))

        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)

        self.Centre()
        self.Show(True)

    def OnRightDown(self, event):
        self.PopupMenu(MyPopoupMenu(self), event.GetPosition())

app = wx.App()
ContextMenu(None, -1, 'contextmenu.py')
app.MainLoop()
class MyPopupMenu(wx.Menu):
    def __init__(self, parent):
         wx.Menu.__init__(self)

wx.Menuという独立したクラスを定義しました。 このクラスでは、 CloseMinimize という2つのコマンドが使用可能です。

self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)

フレーム上で右クリックすると、 OnRightDown() メソッドを呼び出します。 このためには、 wx.EVT_RIGHT_DOWN イベントバインダを使用します。

def OnRightDown(self, event):
    self.PopupMenu(MyPopupMenu(self), event.GetPosition())

OnRightDown() メソッドの中で PopupMenu() メソッドを呼び出します。 このメソッドがコンテキストメニューを表示します。 最初の引数は表示するメニューです。 二番目の引数は、コンテキストメニューを表示する場所です。 ここでは、コンテキストメニューをマウスカーソルの位置に表示することにしましょう。 マウスカーソルの位置を取得するには、 GetPosition() メソッドを呼び出します。

ツールバー

メニューはアプリケーションで使用できるコマンド全てをまとめたものです。 一方ツールバーは、よく使われるコマンドに素早くアクセスできるようにするものです。

シンプルなツールバー

CreateToolBar(long style=-1, int winid=-1, String name=ToolBarNameStr)

ツールバーを作るには、 wx.Frameウィジェットの CreateToolBar() メソッドを呼び出します。

#! /usr/bin/env python

# simpletoolbar.py

import wx

class SimpleToolbar(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(200,300))

        toolbar = self.CreateToolBar()
        toolbar.AddLabelTool(wx.ID_EXIT, '', wx.Bitmap('./icons/exit.png'))
        toolbar.Realize()

        self.Bind(wx.EVT_TOOL, self.OnExit, id=wx.ID_EXIT)

        self.Centre()
        self.Show(True)

    def OnExit(self, event):
        self.Close()

app = wx.App()
SimpleToolbar(None, -1, 'simpletoolbar.py')
app.MainLoop()
toolbar.AddLabelTool(wx.ID_EXIT, '', wx.Bitmap('./icons/exit.png'))

ツールバーボタンを作るには、 AddLabelTool() メソッドを呼び出します。

toolbar.Realize()

ツールバーにアイテムを配置したら、 Resize() メソッドを呼び出します。 Linuxでは必ずしもこのメソッドを呼び出す必要はありませんが、Windowsでは必須です。

self.Bind(wx.EVT_TOOL, self.OnExit, id=wx.ID_EXIT)

ツールバーイベントを処理するために、 wx.EVT_TOOL イベントバインダを使用します。

./img/simpletoolbar.jpg

複数のツールバー

2つ以上のツールバーを作りたいときは、違うやり方をする必要があります。

#! /usr/bin/env python

# toolbars.py

import wx

class Toolbars(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(300, 200))

        vbox = wx.BoxSizer(wx.VERTICAL)

        toolbar1 = wx.ToolBar(self, -1)
        toolbar1.AddLabelTool(wx.ID_ANY, 'a', wx.Bitmap('./icons/exit.png'))
        toolbar1.AddLabelTool(wx.ID_ANY, 'b', wx.Bitmap('./icons/open.png'))
        toolbar1.AddLabelTool(wx.ID_ANY, 'c', wx.Bitmap('./icons/save.png'))
        toolbar1.Realize()

        toolbar2 = wx.ToolBar(self, -1)
        toolbar2.AddLabelTool(wx.ID_EXIT, 'q', wx.Bitmap('./icons/exit.png'))
        toolbar2.Realize()

        vbox.Add(toolbar1, 0, wx.EXPAND)
        vbox.Add(toolbar2, 0, wx.EXPAND)

        self.Bind(wx.EVT_TOOL, self.OnExit, id=wx.ID_EXIT)

        self.SetSizer(vbox)
        self.Centre()
        self.Show(True)

    def OnExit(self, event):
        self.Close()

app = wx.App()
Toolbars(None, -1, 'toolbars.py')
app.MainLoop()

toolbar1 = wx.ToolBar(self, -1)
...
toolbar2 = wx.ToolBar(self, -1)

2つのツールバーオブジェクトを作成して、それらを垂直のボックスサイザーに配置します。

./img/toolbars.jpg

垂直なツールバー

垂直なツールーバーを作る必要性に迫られる時があります。 これはInsscapeやXara Xtremeなどの、グラフィクスアプリケーションにしばしば見られます。

#! /usr/bin/env python

# verticaltoolbar.py

import wx

class VerticalToolbar(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(240, 300))

        toolbar = self.CreateToolBar(wx.TB_VERTICAL)
        toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('./icons/select.gif'))
        toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('./icons/freehand.gif'))
        toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('./icons/shapeed.gif'))
        toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('./icons/pen.gif'))
        toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('./icons/rectangle.gif'))
        toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('./icons/ellipse.gif'))
        toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('./icons/qs.gif'))
        toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('./icons/text.gif'))

        toolbar.Realize()

        self.Centre()
        self.Show(True)

    def OnExit(self, event):
        self.Close()

app = wx.App()
VerticalToolbar(None, -1, 'verticaltoolbar.py')
app.MainLoop()
toolbar = self.CreateToolBar(wx.TB_VERTICAL)

垂直なツールバーを作成します。

toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('../icons/select.gif'))
toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('../icons/freehand.gif'))
...

私はXara Xtreme グラフィクスアプリケーションのアイコンを借りていますが、 別のアイコンで代用してもらっても構いません。

./img/verticaltoolbar.jpg

ツールバーの有効/無効化

以下の例では、ツールバーボタンを有効/無効化する方法について見ていきましょう。 分割線を入れる方法も学びます。

#! /usr/bin/env python

# enabledisable.py

import wx

class EnableDisable(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(250, 150))

        self.count = 5

        self.toolbar = self.CreateToolBar()
        self.toolbar.AddLabelTool(wx.ID_UNDO, '', wx.Bitmap('./icons/undo.png'))
        self.toolbar.AddLabelTool(wx.ID_REDO, '', wx.Bitmap('./icons/redo.png'))
        self.toolbar.EnableTool(wx.ID_REDO, False)
        self.toolbar.AddSeparator()
        self.toolbar.AddLabelTool(wx.ID_EXIT, '', wx.Bitmap('./icons/exit.png'))
        self.toolbar.Realize()

        self.Bind(wx.EVT_TOOL, self.OnExit, id=wx.ID_EXIT)
        self.Bind(wx.EVT_TOOL, self.OnUndo, id=wx.ID_UNDO)
        self.Bind(wx.EVT_TOOL, self.OnRedo, id=wx.ID_REDO)

        self.Centre()
        self.Show(True)

    def OnUndo(self, event):
        if self.count > 1 and self.count <= 5:
            self.count = self.count - 1

        if self.count == 1:
            self.toolbar.EnableTool(wx.ID_UNDO, False)
        
        if self.count == 4:
            self.toolbar.EnableTool(wx.ID_REDO, True)

    def OnRedo(self, event):
        if self.count < 5 and self.count >= 1:
            self.count = self.count + 1
            
        if self.count == 5:
            self.toolbar.EnableTool(wx.ID_REDO, False)

        if self.count == 2:
            self.toolbar.EnableTool(wx.ID_UNDO, True)

    def OnExit(self, event):
        self.Close()

app = wx.App()
EnableDisable(None, -1, 'enabledisable.py')
app.MainLoop()

今回の例では、3つのツールバーボタンを使用しています。 1つはアプリケーションを終了するためのもので、 残り2つはアンドゥ・リドゥボタンです。 アプリケーションのアンドゥ・リドゥ機能を模して作ってあります。 (実用例としては、小ネタとテクニックをご覧ください)。 ボタンを続けて4回押すと、アンドゥ・リドゥボタンは使用できなくなります。

self.toolbar.EnableTool(wx.ID_REDO, False)
self.toolbar.AddSeparator()

最初にリドゥボタンが無効化されました、 EnableTool() メソッドを呼び出してこれを行います。 ツールバーの中で意味のあるまとまりを作ることができます。 小さなタテ線を引くことで、ボタンのグループを分割することができます。 これを行うには、 AddSeparator() メソッドを呼び出します。

def OnUndo(self, event):
    if self.count > 1 and self.count <= 5:
        self.count = self.count - 1

    if self.count == 1:
        self.toolbar.EnableTool(wx.ID_UNDO, False)

    if self.count == 4:
        self.toolbar.EnableTool(wx.ID_REDO, True)

アンドゥ・リドゥ機能をマネしてみました。 4回ボタンを押して、これ以上アンドゥできなくなるとアンドゥボタンが無効化されます。 1回アンドゥを行ったら、リドゥボタンを有効化しておきます。 同じ仕組みを OnRedo() メソッドにも実装しておきましょう。

./img/enabledisable.jpg

The original page is here.

Date: 2010-10-28 19:49:51 JST

HTML generated by org-mode 6.36c in emacs 23