WxPython FAQ Menus and Toolbars

Материал из Wiki.crossplatform.ru

Версия от 09:14, 19 февраля 2009; ViGOur (Обсуждение | вклад)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Содержание

Creating a MenuBar

A menubar is one of the most visible parts of the GUI application. It is a group of commands located in various menus. While in console applications you had to remember all those arcane commands, here we have most of the commands grouped into logical parts. There are accepted standards that further reduce the amount of time spending to learn a new application. To implement a menubar in wxPython we need to have three things. A wx.MenuBar, a wx.Menu and a wx.MenuItem.

center

A Simple menu example

Creating a menubar in wxPython is very simple. Just a few lines of code.

#!/usr/bin/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, 'simple menu example')
app.MainLoop()
 menubar = wx.MenuBar()

First we create a menubar object.

 file = wx.Menu()

Next we create a menu object.

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

We append a menu item into the menu object. The first parameter is the id of the menu item. The second parameter is the name of the menu item. The last parameter defines the short helpstring that is displayed on the statusbar, when the menu item is selected. Here we did not create a wx.MenuItem explicitely. It was created by the Append() method behind the scenes. Later on, we will create a wx.MenuItem manually.

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

After that, we append a menu into the menubar. The &amp character creates an accelerator key. The character that follows the &amp is underlined. This way the menu is accessible via the alt + F shortcut. In the end, we call the SetMenuBar() method. This method belongs to the wx.Frame widget. It sets up the menubar.

center

A dockable menubar

Under Linux, we can create a dockable menubar. This feature is not commonly seen in applications. But similar thing can be seen on Mac OS. Mac users do not have a menubar in the toplevet application window. The menubar is implemented outside the main window.

#!/usr/bin/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.Centre()
        self.Show(True)
 
app = wx.App()
Dockable(None, -1, 'Dockable menubar')
app.MainLoop()
 menubar = wx.MenuBar(wx.MB_DOCKABLE)

We create a dockable menubar by providing a wx.MB_DOCKABLE flag to the constructor.

center

Icons, shortcuts, events

In the next section we will further enhance our menu example. We will see, how we can add icons to our menus. Icons make our applications more visually attractive. Further, they help us understand the menu commands. We will see, how we can add shortcuts to our menus. Shortcuts are not a relict from the past. They enable us to work more quickly with our applications. One of the most widely used shortcut is the Ctrl + S one. There are not many people, that would not know the meaning of this shortcut. It is more handy to press this shortcut, than to move a mouse pointer to the menubar, click a File menu and select the Save commnand. Shortcuts are a productivity boost to most users.

We will also briely touch events.

#!/usr/bin/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.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, '')
app.MainLoop()
 quit = wx.MenuItem(file, 1, '&Quit\tCtrl+Q')
 quit.SetBitmap(wx.Bitmap('icons/exit.png'))
 file.AppendItem(quit)

If we want to add shortcuts and icons to our menus, we have to manually create a wx.MenuItem. So far we have created menuitems indirectly. The &amp character specifies an accelerator key. The following character is underlined. The actual shortcut is defined by the combination of characters. We have specified Ctrl + Q characters. So if we press Ctrl + Q, we close the application. We put a tab character between the &amp character and the shortcut. This way, we manage to put some space between them. To provide an icon for a menuitem, we call a SetBitmap() method. A manually created menuitem is appended to the menu by calling the AppendItem() method.

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

If we select a quit menu item or press a keyboard shortcut, a wx.EVT_MENU event is generated. We bind an event handler to the event. The event handler is a method, that is being called. In our example, the OnQuit() method closes the application. There can be several menuitems, so we have to give a unique id to each of them. Working with events is very easy and straightforward in wxPython. We will talk about events in a separate chapter.

center

Submenus

Each menu can also have a submenu. This way we can group similar commands into groups. For example we can place commands that hide/show various toolbars like personal bar, address bar, status bar or navigation bar into a submenu called toolbars. Within a menu, we can seperate commands with a separator. It is a simple line. It is common practice to separate commands like new, open, save from commands like print, print preview with a single separator. In our example we will see, how we can create submenus and menu separators.

#!/usr/bin/python
# submenu.py
 
import wx
 
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')
app.MainLoop()
 file.AppendSeparator()

A menu separator is appended with the AppendSeparator() method.

 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)

Creating a submenu is trivial. First, we create a menu. Then we append menu items. A submenu is created by calling the AppenMenu() on the menu object.

center

Various menu items

There are tree kinds of menu items.

  • normal item
  • check item
  • radio item
#!/usr/bin/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 statubar', '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/quit.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, 'check menu item')
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)

If we want to append a check menu item, we set a kind parameter to wx.ITEM_CHECK. The default parameter is wx.ITEM_NORMAL. The Append() method returns a wx.MenuItem.

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

When the application starts, both statusbar and toolbar are visible. So we check both menu items with the Check() method.

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

We show or hide the statusbar according to the state of the check menu item. We find out the state of the check menu item with the IsChecked() method. Same with toolbar.

center

Context menu

It is a list of commands that appears under some context. For example, in a Firefox web browser, when we right click on a web page, we get a context menu. Here we can reload a page, go back or view page source. If we right click on a toolbar, we get another context menu for managing toolbars. Context menus are sometimes called popup menus.

#!/usr/bin/python
# contextmenu.py
 
import wx
 
 
class MyPopupMenu(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.Center()
        self.Show()
 
    def OnRightDown(self, event):
        self.PopupMenu(MyPopupMenu(self), event.GetPosition())
 
 
app = wx.App()
frame = ContextMenu(None, -1, 'context menu')
app.MainLoop()
 class MyPopupMenu(wx.Menu):
     def __init__(self, parent):
         wx.Menu.__init__(self)

We create a separate wx.Menu class. Here we define two commands. Close and minimize window.

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

If we right click on the frame, we call the OnRightDown() method. For this, we use the wx.EVT_RIGHT_DOWN event binder.

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

In the OnRightDown() method, we call the PopupMenu() method. This method shows the context menu. The first parameter is the menu to be shown. The second parameter is the position, where the context menu appears. The context menus appear at the point of the mouse cursor. To get the actual mouse position, we call the GetPosition() menthod.

Toolbars

Menus group all commands that we can use in an application. Toolbars provide a quick access to the most frequently used commands.

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

To create a toolbar, we call the CreateToolBar() method of the frame widget.

#!/usr/bin/python
# simpletoolbar.py
 
import wx
 
class SimpleToolbar(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(300, 200))
 
        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, 'simple toolbar')
app.MainLoop()
 toolbar.AddLabelTool(wx.ID_EXIT, '', wx.Bitmap('../icons/exit.png'))

To create a toolbar button, we call the AddLabelTool() method.

 toolbar.Realize()

After we have put our items to the toolbar, we call the Realize() method. Calling this method is not obligatory on Linux. On windows it is.

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

To handle toolbar events, we use the wx.EVT_TOOL event binder.

center

If we want to create more than one toolbars, we must do it differently.

#!/usr/bin/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, '', wx.Bitmap('../icons/new.png'))
        toolbar1.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('../icons/open.png'))
        toolbar1.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('../icons/save.png'))
        toolbar1.Realize()
 
        toolbar2 = wx.ToolBar(self, -1)
        toolbar2.AddLabelTool(wx.ID_EXIT, '', 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')
app.MainLoop()
 toolbar1 = wx.ToolBar(self, -1)
 ...
 toolbar2 = wx.ToolBar(self, -1)

We create two toolbar objects. And put them into a vertical box.

center

Sometimes we need to create a vertical toolbar. Vertical toolbars are often seen in graphics applications like Inkscape or Xara Xtreme.

#!/usr/bin/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, 'vertical toolbar')
app.MainLoop()
 toolbar = self.CreateToolBar(wx.TB_VERTICAL)

Here we create a vertical toolbar.

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

I have borrowed icons from the Xara Xtreme graphics application.

center

In the following example, we will show, how we can enable and disable toolbar buttons. We will also see a separator line.

#!/usr/bin/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, 'enable disable')
app.MainLoop()

In our example, we have three toolbar buttons. One button is for exiting the application. The other two buttons are undo and redo buttons. They simulate undo/redo functionality in an application. (for a real example, see tips and tricks) We have 4 changes. The undo and redo butons are disabled accordingly.

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

In the beginning, the redo button is disabled. We do it by calling the EnableTool() method. We can create some logical groups within a toolbar. We can separate various groups of buttons by a small vertical line. To do this, we call the AddSeparator() method.

 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)

We simulate undo and redo functionality. We have 4 changes. If there is nothing left to undo, the undo button is disabled. After undoing the first change, we enable the redo button. Same logic applies for the OnRedo() method.

center