Ve druhém dílu seriálu o wxPythonu se naučíme, jak vytvářet menu a nástrojovou lištu (toolbar).
Vytváření MenuBaru
Menu je jedno z nejviditelnějších částí celé GUI aplikace. Zatímco v
konzolových aplikacích si musíte pamatovat všechny příkazy, v menu
naleznete všechny příkazy roztříděné do logických částí. K vytvoření
menu potřebujeme tři komponenty: wx.MenuBar
,
wx.Menu
a wx.MenuItem
.
Vytvoření jednoduchého menu ve wxPythonu je opravdu jednoduché. Zabere to pouze několik řádek kódu.
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()
Jako obvykle si kód rozebereme řádku po řádce.
menubar = wx.MenuBar()
Nejprve vytvoříme objekt wx.MenuBar
.
file = wx.Menu()
Pak vytvoříme objekt wx.Menu
.
file.Append(-1, 'Quit', 'Quit application')
Přidáme položku do menu. První parametr je id
položky.
Druhý parametr je popisek položky a třetí parametr je pomocný popisek,
který se zobrazí ve statusbaru, pokud je položka vybrána. V tomto
příkladě jsme nevyužili objekt wx.MenuItem
. Tento objekt
byl za nás vytvořen metodou Append()
. Později se naučíme
wx.MenuItem
používat.
menubar.Append(file, '&File') self.SetMenuBar(menubar)
Poté přidáme menu do menubaru. Znak &
představuje
klávesovou zkratku. Znak, který následuje po &
, bude
podtržený. Tímto způsobem je menu přístupné pod klávesovou zkratkou
alt + F
. Nakonec zavoláme metodu SetMenuBar()
.
Tato metoda vykreslí menubar do okna.
Ikony, zkratky a události
Na několika následujících řádkách vylepšíme náš předchozí příklad. Ukážeme si, jak jednotlivým položkám v menu přidat ikonu. Ikony dělají naši aplikaci atraktivnější pro oči uživatele a také jim pomáhají pochopit, co přesně daná položka dělá. Také si ukážeme, jak přiřazovat naší aplikaci klávesové zkratky. Klávesové zkratky mohou velmi urychlit uživatelům práci s aplikací. Jednou z nejpoužívanějších klávesových zkratek je Ctrl + s
. Je daleko rychlejší zmáčknout kombinaci těchto kláves, než najet myší na menu, otevřít ho a kliknou na Uložit. Také se velmi stručně řekneme něco o událostech a o tom, jak je zachytit a zpracovat.
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, '&QuittCtrl+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()
Analýza kódu:
quit = wx.MenuItem(file, 1, '&QuittCtrl+Q') quit.SetBitmap(wx.Bitmap('icons/exit.png')) file.AppendItem(quit)
Pokud chceme přidat ikonku nějaké položce v menu, musíme už použít objekt wx.MenuItem
(nevystačíme si tedy s metodou Apend()
). Znak následující po &
je klávesová zkratka. V tomto případě se tedy aplikace zavře, pokud uživatel stiskne kombinaci Ctrl + q
. Ikonu definujeme pomocí metody SetBitmap()
.
Jelikož jsme položku menu vytvářeli pomocí objektu wx.MenuItem
a ne pomocí metody Append()
, musíme danou položku ještě do menu přidat. Děje se tak pomocí metody AppendItem()
.
self.Bind(wx.EVT_MENU, self.OnQuit, id=1)
Pokud klikneme na položku quit nebo stiskneme kombinaci kláves Ctrl + q
, wxPython vygeneruje událost wx.EVT_MENU
. My jsme této události přiřadili metodu self.OnQuit
. Třetím parametrem metody Bind
je id
. Menubar bude mít totiž obvykle více než jednu položku a proto musí mít každá položka unikátní id. O událostech si řekneme více později.
Submenu
Každé menu může mít v sobě submenu (menu vnořené v menu). Tímto způsobem můžeme rozdělit podobné příkazy do jednotlivých skupin. Například můžeme položky Projekt, Editace a Vzhled vložit do submenu nazvaného Panely nástrojů. V rámci menu můžeme jednotlivé položky od sebe oddělit separátorem. Je běžná záležitost, že položky Nový soubor, Otevřít a Uložit jsou odděleny separátorem od položek Tisk nebo Zavřít. V následujícím příkladu si ukážeme, jak vytvořit submenu a jak vložit do menu separátor.
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, '&QuittCtrl+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()
Následuje analýza kódu:
file.AppendSeparator()
Separátor do menu vložíme pomocí metody 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)
Vytváření submenu je opravdu triviální záležitost. Nejprve vytvoříme objekt wx.Menu
, do kterého vložíme požadované položky. Nakonec vložíme submenu do hlavního menu zavoláním metody AppendMenu()
.
Různé druhy menu
Existují tři druhy menu:
- Normální menu
- Zaškrtávací (check) menu
- Přepínací (radio) menu
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()
Následuje analýza kódu:
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)
Pokud chceme do menu přidat položku typu „zaškrtávátka“, musíme specifikovat parametr kind = wx.ITEM_CHECK
(standardní hodnota je wx.ITEM_NORMAL
). Metoda Append
vrací wx.MenuItem
.
view.Check(ID_STAT, True) view.Check(ID_TOOL, True)
Když se aplikace spustí, je vidět nástrojová lišta (toolbar) i statusbar. Proto musíme obě položky zaškrtnout zavoláním metody Check()
.
def ToggleStatusBar(self, event): if self.shst.IsChecked(): self.statusbar.Show() else: self.statusbar.Hide()
Schováme nebo zobrazíme statusbar na základě toho, jestli je daná položka menu zaškrtnuta nebo ne. To zjistíme zavoláním metody IsChecked()
.
Kontextové menu
Kontextové menu je vlastně menu bez menubaru. Objeví se vždy tam, kde je potřeba. Znáte to například z webového prohlížeče Firefox. Když kliknete pravým tlačítkem na stránku, objeví se kontextové menu s nabídkami jako „Ukázat zdrojový kód“ apod. Následující kód ukazuje, jak takové menu vytvořit.
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()
Následuje analýza kódu:
class MyPopupMenu(wx.Menu): def __init__(self, parent): wx.Menu.__init__(self)
Vytváříme vlastní třídu, která dědí z wx.Menu
. V této třídě definujeme dvě metody: OnClose
a OnMinimize
.
self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
Pokud v okně klikneme pravým tlačítkem myši, zavoláme tím metodu OnRightDown()
. K tomu používáme ovladač wx.EVT_RIGHT_DOWN
.
def OnRightDown(self, event): self.PopupMenu(MyPopupMenu(self), event.GetPosition())
V metodě OnRightDown()
voláme metodu PopupMenu
. Tato metoda zobrazí kontextové menu. První parametr je menu, které se zobrazí, a druhý parametr je pozice, kde se kontextové menu objeví. Správně by se mělo objevit na aktuální pozici myši a tu získáme, pokud zavoláme metodu GetPosition()
.
Nástrojová lišta
Do menu se vkládá většina funkcí, které daná aplikace zvládne. Nástrojová lišta (Toolbar) poskytuje přístup k těm nejpoužívanějším.
CreateToolBar(long style=-1, int winid=-1, String name=ToolBarNameStr)
Abychom vytvořili nástrojovou lištu, musíme zavolat metodu CreateToolBar
.
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()
Analýza kódu:
toolbar.AddLabelTool(wx.ID_EXIT, '', wx.Bitmap('../icons/exit.png'))
Tlačítka do nástrojové lišty přidáváme pomocí metody AddLabelTool
.
toolbar.Realize()
Poté, co do nástrojové lišty přidáme všechny tlačítka, musíme zavolat metodu Realize
. Tím vykreslíme nástrojovou lištu do okna.
self.Bind(wx.EVT_TOOL, self.OnExit, id=wx.ID_EXIT)
Ke zpracování události kliknutí na nástrojovou lištu se používá ovladač wx.EVT_TOOL
.
Pokud chceme, aby naše aplikace měla dvě nástrojové lišty, musíme to udělat jinak:
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()
Vytvořili jsme dvě instance třídy wx.ToolBar
a umístili jsme je do vertikálního boxu.
toolbar1 = wx.ToolBar(self, -1) ... toolbar2 = wx.ToolBar(self, -1)
Doposud jsme vytvářeli horizontální nástrojové lišty, ale občas může nastat situace, kdy budeme chtít vytvořit vertikální lištu.
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('new.bmp')) toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('open.bmp')) toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('save.bmp')) toolbar.AddLabelTool(wx.ID_ANY, '', wx.Bitmap('quit.bmp')) toolbar.Realize() self.Centre() self.Show(True) def OnExit(self, event): self.Close() app = wx.App() VerticalToolbar(None, -1, 'vertical toolbar') app.MainLoop()
Analýza kódu:
toolbar = self.CreateToolBar(wx.TB_VERTICAL)
Vertikální lištu vytváříme pomocí stylu wx.TB_VERTICAL
.
V následujícím příkladu si ukážeme jak zablokovat a odblokovat jednotlivá tlačítka nástrojové lišty. Také se naučíme jak do nástrojové lišty vložit separátor.
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()
V našem příkladu obsahuje lišta tři tlačítka. Jedno pro ukončení aplikace, zbylá dvě tlačítka se snaží simulovat funkci Zpět a Vpřed.
self.toolbar.EnableTool(wx.ID_REDO, False) self.toolbar.AddSeparator()
Na začátku předchozího kódu je tlačítko Zpět zablokováno (disabled). Udělali jsme to zavoláním metody EnableTool()
. Na druhém řádku vytváříme separátor. Ten má podobnou funkci jako v menu - odděluje skupiny tlačítka. Separátor se vytváří pomocí metody AddSeparator()
.