Hlavní navigace

Grafické uživatelské rozhraní v Pythonu: menu v knihovně Tkinter

15. 8. 2017
Doba čtení: 22 minut

Sdílet

V dnešním článku tvorbě GUI v Pythonu budeme pokračovat v popisu knihovny Tkinter. Zaměříme se na problematiku tvorby menu, která tvoří součást většiny složitějších aplikací využívajících grafické uživatelské rozhraní.

Obsah

1. Použití nabídek (menu) v knihovně Tkinter

2. Typy nabídek/menu

3. Toplevel menu (menubar)

4. Demonstrační příklad – vytvoření menubaru

5. Menubar v kombinaci s dalšími widgety

6. Popup menu (kontextové menu)

7. Demonstrační příklad – vytvoření kontextového menu a navázání jeho zobrazení na tlačítko myši

8. Roletové menu (pull down)

9. Demonstrační příklad – základní roletové menu

10. Zákaz „odtrhovacího“ menu

11. Změna barev jednotlivých položek menu

12. Podtržení znaků v položkách menu pro jejich rychlý výběr

13. Přidání obrázků (ikon) k položkám menu

14. Repositář s demonstračními příklady

15. Odkazy na Internetu

1. Použití nabídek (menu) v knihovně Tkinter

Nabídky (menu) patří mezi nedílnou součást prakticky každého složitějšího programu s grafickým uživatelským rozhraním. Systém menu zobrazuje uživateli GUI aplikace jednu nebo více zobrazitelných nabídek, které je možné vybrat, nastavit nebo přepnout; na rozdíl od dialogů však menu na ploše obrazovky zabírá jen minimální místo či dokonce žádné místo v případě kontextových menu. Bývá dobrým zvykem, že menu ve své struktuře obsahuje všechny příkazy a parametry provozované aplikace (ne všechny příkazy musí být samozřejmě po celou dobu běhu aplikace dostupné, někdy mohou být „zašedlé“).

x

Obrázek 1: Velmi jednoduchý textový procesor nazvaný Typewriter, který byl naprogramován pro mikropočítače ZX Spectrum, používá asi nejjednodušší systém menu vůbec (ostatně podle takových nabídek vlastně menu dostala svůj název).

Dnes se bohužel i u profesionálních aplikací stává, že je toto pravidlo často porušováno a některé příkazy zůstávají skryty pouze v dialozích nebo konfiguračních souborech. Mezi velké výhody menu patří to, že si uživatel nemusí pamatovat způsob zadávání parametrů (například pomocí klíčových slov nebo formulářů) – vše je snadno dosažitelné právě ze struktury menu. Také pro programátory je tvorba menu mnohdy jednodušší než návrh dialogových boxů (což ostatně uvidíme v dalších kapitolách). Na druhou stranu může systém menu zdržovat v práci zkušenější uživatele. Z tohoto důvodu je vhodné menu vhodným způsobem spojit s dalšími prvky grafického uživatelského rozhraní (tlačítky, výběrovými boxy a především tzv. horkými klávesami).

Obrázek 2: Klasická roletová menu se používala už v počítačovém dávnověku, například i v Microsoft Windows 1.0 spuštěné na emulátoru počítače PC XT s grafickou kartou CGA.

2. Typy nabídek/menu

V některých systémech GUI je menu složeno z jednodušších objektů (widgetů). Z toho také vyplývá, že se zde menu chová jako kontejner schopný pojmout další widgety. Existují však i GUI systémy, v nichž je menu považováno za jeden dynamicky se měnící widget, který je rozdělen do jednotlivých objektů – položek menu (to má výhodu v tom, že se pro menu alokuje méně systémových zdrojů; to však dnes již nebývá kritickým parametrem jako v dobách, kdy se například každému widgetu přiřazovalo jen šestnáctibitové číslo, které navíc muselo být pro celý systém unikátní).

Obrázek 3: Grafické uživatelské rozhraní grafického editoru DEGAS Elite s toplevel menu i s takzvaným screen menu.

Příkazy je možné z menu vyvolat pomocí tlačítek (push-button/button). S využitím tlačítek se přímo spouští nějaká naprogramovaná akce. Dále je možné v menu používat zaškrtávací/výběrová tlačítka (check-button/checkbutton) a přepínací tlačítka (radio-button/radiobutton). V případě, že je menu hierarchicky strukturováno, je k dispozici ještě tlačítko, které slouží k přechodu na nižší úroveň. Některé programy, například původní vývojová prostředí firmy Borland, používaly pouze jednoúrovňové menu, v dalších programech jsou menu mnohdy složitě strukturovaná a uživatelsky konfigurovatelná (GIMP apod.).

V průběhu vývoje grafických uživatelských rozhraní se ustálily čtyři hlavní typy menu:

  1. roletové menu (pull down)
  2. vynořující se menu (pop up/popup) – také nazývané kontextové menu
  3. výběrové menu (option)
  4. kaskádové menu (cascading)

V navazujících kapitolách si jednotlivé typy menu popíšeme, spolu s ukázkami jejich podpory v knihovně Tkinter.

3. Toplevel menu (menubar)

Některé aplikace ze systému menu používají pouze jednoduchou lištu, na které jsou statická tlačítka popř. se položky menu dynamicky mění. Tento způsob však ztratil svůj půvab ve chvíli, kdy množství funkcí dostupných z menu rapidně vzrostlo na desítky a někdy i stovky příkazů. Příkladem aplikací využívajících pouze lištu menu je například Lotus 1–2–3 nebo z ještě starších aplikací textový editor Cheops Writer dostupný pro osmibitové domácí mikropočítače Atari. Opakem menu zobrazovaného pouze na liště je takzvané screen menu používané například u starších verzí AutoCADu – toto menu se později jako velká „novinka“ objevilo i u Microsoft Office. V knihovně Tkinter se menu s jedinou lištou (toplevel menu) vytváří velmi snadno, jak je ostatně patrné z prvního demonstračního příkladu popsaného v navazující kapitole.

xxx

Obrázek 4: Textový procesor Cheops'Writer: dvouřádkové hlavní menu (toplevel menu) textového procesoru navržené ve stylu Lotus 1–2–3.

4. Demonstrační příklad – vytvoření menubaru

Dnešní první demonstrační příklad je velmi jednoduchý, protože je v něm ukázán způsob vytvoření „toplevel“ menu (neboli menubaru) obsahujícího pouze dvě příkazová tlačítka. Po stisku prvního tlačítka se zavolá uživatelsky definovaná funkce test, po stisku tlačítka druhého pak metoda root.quit(), která ukončí běh aplikace:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
 
 
def test():
    print("Test!")
 
 
root = tkinter.Tk()
 
menubar = tkinter.Menu(root)
menubar.add_command(label="Test", command=test)
menubar.add_command(label="Quit", command=root.quit)
 
root.config(menu=menubar)
 
root.mainloop()

Obrázek 5: První demonstrační příklad.

5. Menubar v kombinaci s dalšími widgety

V dalším příkladu je ukázáno, že menubar je možné bez problému kombinovat s dalšími widgety. Samotný menubar je přitom jakoby vyjmut z mřížky, do níž se vkládají ostatní widgety tvořící GUI aplikace, takže widgety se mohou vkládat i do prvního řádku mřížky, který ve skutečnosti leží až pod menubarem:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
 
 
def test():
    print("Test!")
 
 
root = tkinter.Tk()
 
menubar = tkinter.Menu(root)
menubar.add_command(label="Test", command=test)
menubar.add_command(label="Quit", command=root.quit)
 
root.config(menu=menubar)
 
style = ttk.Style()
style.theme_use("alt")
style.configure('Red.TButton', background='#ff8080')
 
radio_var = tkinter.StringVar()
radio_var.set("Python")
 
langs = ("Assembler", "Basic", "Brainfuck", "C", "Python")
 
f1 = ttk.LabelFrame(root, text="Languages")
f2 = ttk.LabelFrame(root, text="Commands")
 
radio_buttons = (ttk.Radiobutton(f1, text=lang, value=lang,
                                 variable=radio_var)
                 for lang in langs)
 
showButton = ttk.Button(f2, text="Show var",
                        command=lambda: print(radio_var.get()))
 
quitButton = ttk.Button(f2, text="Exit", style='Red.TButton',
                        command=root.quit)
 
for i, radio_button in enumerate(radio_buttons):
    radio_button.grid(column=1, row=i, sticky="w")
 
showButton.grid(column=1, row=1, sticky="we", padx=6, pady=6)
quitButton.grid(column=1, row=2, sticky="we", padx=6, pady=6)
 
f1.grid(column=1, row=1, sticky="ne", padx=6, pady=6)
f2.grid(column=2, row=1, sticky="ne", padx=6, pady=6)
 
root.mainloop()

Obrázek 6: Druhý demonstrační příklad.

6. Popup menu (kontextové menu)

Vynořující se menu (pop up/popup), která jsou někdy nazývána také kontextová menu, jsou charakteristická tím, že se mohou zobrazit (vynořit) v kterémkoli místě okna aplikace (netypický zástupce vynořujícího se menu byl použit například ve známém textovém editoru T602, zde se však menu vynořovalo vždy na stejném místě). Často se podle místa také mění příkazy dostupné z menu. Toto menu je výhodné v tom, že spoří místo v okně a navíc se při ovládání aplikace myší nemusí při vyvolání menu dojíždět až na lištu, jak tomu je v případě roletových menu. Rovněž dynamická (kontextová) změna příkazů v menu je velmi výhodná, protože uživatel dostane k dispozici pouze ty příkazy, které mají pro vybraný objekt smysl. Jediná nevýhoda tohoto typu menu spočívá v tom, že uživatel musí o existenci menu předem vědět nebo experimentovat.

V knihovně Tkinter se s kontextovými menu pracuje poměrně jednoduše (význam parametru tearoff si vysvětlíme v dalším textu):

popup = tkinter.Menu(root, tearoff=0)
 
popup.add_command(label="Open")
popup.add_command(label="Save")
popup.add_separator()
popup.add_command(label="Exit", command=root.quit)

Nesmíme ovšem zapomenout na navázání nějaké události k vyvolání (zobrazení) tohoto typu menu. Typicky se pro vyvolání tohoto menu používá pravé tlačítko myši, jak je ostatně naznačeno v následujícím úryvku kódu (připomeňme si, že tlačítka myši jsou v knihovně Tkinter číslována trošku zmateně, takže pravé tlačítko má číslo 3):

root.bind("<Button-3>", on_popup)

Výše uvedený zápis zavolá funkci on_popup ve chvíli, kdy je na ploše hlavního okna stisknuto pravé tlačítko myši. Samotná funkce on_popup vypadá následovně:

def on_popup(event):
    popup.post(event.x_root-5, event.y_root-5)

Při umístění menu pomocí metody popup.post() je možné použít speciální atributy x_root a y_root objektu event, které udávají absolutní pozici kurzoru myši.

Poznámka: od pozice kurzoru odečítám hodnotu 5 pixelů, protože v takovém případě bude kurzor automaticky umístěn na první položku menu (resp. přesněji řečeno bude první položka menu posunuta tak, aby byla pod kurzorem myši).

7. Demonstrační příklad – vytvoření kontextového menu a navázání jeho zobrazení na tlačítko myši

Všechny důležité funkce nutné pro vytvoření kontextového menu jsme si již popsali v předchozí kapitole, takže se nyní podívejme na to, jak je možné kontextové menu přidat do aplikace s jediným (prozatím prázdným) oknem a jak se zobrazení tohoto menu naváže na pravé tlačítko myši:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
 
 
def test():
    print("Test!")
 
 
root = tkinter.Tk()
 
popup = tkinter.Menu(root, tearoff=0)
 
popup.add_command(label="Open")
popup.add_command(label="Save")
popup.add_separator()
popup.add_command(label="Exit", command=root.quit)
 
 
def on_popup(event):
    popup.post(event.x_root-5, event.y_root-5)
 
 
root.bind("<Button-3>", on_popup)
root.mainloop()

Obrázek 7: Kontextové menu ve třetím demonstračním příkladu.

8. Roletové menu (pull down)

Roletové menu (pull down) patří mezi základní typy menu a je proto dostupné prakticky ze všech aplikací využívajících grafické uživatelské rozhraní. Z celého menu je bez jeho aktivace viditelná pouze nejvyšší úroveň, která se nachází na takzvané liště. Standardně bývá lišta umístěna v horní části okna aplikace, i když samozřejmě existují různé výjimky. Mezi výhody tohoto typu menu patří skutečnost, že uživatel vždy vidí nejvyšší úroveň příkazů a nemusí tedy složitě pátrat nebo slepě zkoušet, ve kterém místě se menu aktivuje (to je případ mnohých multimediálních přehrávačů, u kterých autoři preferují „originalitu“ namísto použitelnosti :-). Další výhoda roletového menu spočívá ve stálé viditelnosti klíčů (horkých kláves) pro příkazy na základní úrovni (pokud tedy není viditelnost klíčů zakázána, což se kupodivu také poměrně často děje).

Obrázek 8: Roletové menu zobrazené čtvrtým příkladem.

V knihovně Tkinter je roletové menu vytvořeno jednoduše přidáním objektu typu Menu do toplevel menu (což již známe) metodou add_cascade:

menubar = tkinter.Menu(root)
 
filemenu = tkinter.Menu(menubar)
editmenu = tkinter.Menu(menubar)
helpmenu = tkinter.Menu(menubar)
 
menubar.add_cascade(label="File", menu=filemenu)
menubar.add_cascade(label="Edit", menu=editmenu)
menubar.add_cascade(label="Help", menu=helpmenu)

Obrázek 9: Roletové menu bez „odtrhovací“ části.

Do jednotlivých roletových menu se přidávají položky metodami add_command, add_cascade, add_checkbutton či add_radiobutton. Taktéž lze přidat oddělovač metodou add_cascade.

Obrázek 10: Roletové menu bez „odtrhovací“ části (klasický styl Windows XP).

9. Demonstrační příklad – základní roletové menu

V následujícím demonstračním příkladu jsou vytvořena tři roletová menu, která jsou pod jmény File, Edit a Help přidána do toplevel menu. Prozatím nepoužíváme žádnou speciální konfiguraci, takže jsou menu zobrazena s použitím výchozího stylu, bez horkých klíčů, ikon atd.:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
 
 
def test():
    print("Test!")
 
 
root = tkinter.Tk()
 
menubar = tkinter.Menu(root)
 
filemenu = tkinter.Menu(menubar)
filemenu.add_command(label="Open")
filemenu.add_command(label="Save")
filemenu.add_separator()
filemenu.add_command(label="Exit", command=root.quit)
menubar.add_cascade(label="File", menu=filemenu)
 
editmenu = tkinter.Menu(menubar)
editmenu.add_command(label="Undo")
editmenu.add_separator()
editmenu.add_command(label="Cut")
editmenu.add_command(label="Copy")
editmenu.add_command(label="Paste")
editmenu.add_command(label="Delete")
editmenu.add_separator()
editmenu.add_command(label="Select All")
menubar.add_cascade(label="Edit", menu=editmenu)
 
helpmenu = tkinter.Menu(menubar)
helpmenu.add_command(label="About", command=test)
menubar.add_cascade(label="Help", menu=helpmenu)
 
root.config(menu=menubar)
 
root.mainloop()

10. Zákaz „odtrhovacího“ menu

Pravděpodobně jste si v předchozích demonstračních příkladech povšimli, že roletová menu byla „odtrhovací“, tj. výběrem čárkované čáry zobrazené ve vrchní části menu se menu může odtrhnout od svého okna a vytvořit tak okno samostatné. To je velmi výhodné, protože velkou část grafického uživatelského rozhraní je možné nahradit právě těmito typy menu a podle preferencí uživatele mohou být příkazy buď stále viditelné, nebo skryté v menu. Ze známých aplikací využívá tuto možnost například Gvim (pouze na některých GUI systémech), rastrový grafický editor GIMP a taktéž další rastrový grafický editor mtPaint.

Na druhou stranu nejsou uživatelé na podobné chování zvyklí, takže mohou požadovat „obyčejná menu“. Toho lze snadno dosáhnout. Buď se globálně (pro dané okno či pro celou hierarchii oken) nastaví konfigurační volba *tearOff na False:

root.option_add('*tearOff', False)

Nebo se při vytváření každého menu explicitně nastaví parametr tearOff na nulu:

filemenu = tkinter.Menu(menubar, tearoff=0)
helpmenu = tkinter.Menu(menubar, tearoff=0)
...
...
...

Obrázek 11: Menu bez odtrhovací části.

V dalším příkladu je „odtrhování“ všech menu zakázáno globálně prvním zmíněným způsobem:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
 
 
def test():
    print("Test!")
 
 
root = tkinter.Tk()
 
menubar = tkinter.Menu(root)
 
root.option_add('*tearOff', False)
 
filemenu = tkinter.Menu(menubar)
filemenu.add_command(label="Open")
filemenu.add_command(label="Save")
filemenu.add_separator()
filemenu.add_command(label="Exit", command=root.quit)
menubar.add_cascade(label="File", menu=filemenu)
 
editmenu = tkinter.Menu(menubar)
editmenu.add_command(label="Undo")
editmenu.add_separator()
editmenu.add_command(label="Cut")
editmenu.add_command(label="Copy")
editmenu.add_command(label="Paste")
editmenu.add_command(label="Delete")
editmenu.add_separator()
editmenu.add_command(label="Select All")
menubar.add_cascade(label="Edit", menu=editmenu)
 
helpmenu = tkinter.Menu(menubar)
helpmenu.add_command(label="About", command=test)
menubar.add_cascade(label="Help", menu=helpmenu)
 
root.config(menu=menubar)
 
root.mainloop()

V příkladu druhém je menu File a Help „normální“, zatímco menu Edit je odtrhovací:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
 
 
def test():
    print("Test!")
 
 
root = tkinter.Tk()
 
menubar = tkinter.Menu(root)
 
filemenu = tkinter.Menu(menubar, tearoff=0)
filemenu.add_command(label="Open")
filemenu.add_command(label="Save")
filemenu.add_separator()
filemenu.add_command(label="Exit", command=root.quit)
menubar.add_cascade(label="File", menu=filemenu)
 
editmenu = tkinter.Menu(menubar)
editmenu.add_command(label="Undo")
editmenu.add_separator()
editmenu.add_command(label="Cut")
editmenu.add_command(label="Copy")
editmenu.add_command(label="Paste")
editmenu.add_command(label="Delete")
editmenu.add_separator()
editmenu.add_command(label="Select All")
menubar.add_cascade(label="Edit", menu=editmenu)
 
helpmenu = tkinter.Menu(menubar, tearoff=0)
helpmenu.add_command(label="About", command=test)
menubar.add_cascade(label="Help", menu=helpmenu)
 
root.config(menu=menubar)
 
root.mainloop()

Poznámka: povšimněte si použití metody add_separator pro vizuální oddělení jednotlivých položek menu.

11. Změna barev jednotlivých položek menu

Podobně jako u většiny standardních widgetů lze i u položek menu měnit barvy textu či barvy pozadí. Na dalším demonstračním příkladu je ukázáno, jakým způsobem je možné vytvořit různobarevné položky. Položky však nejsou vytvářeny otrocky jedna po druhé, ale je použita n-tice nazvaná colors, která se zpracovává s využitím generátorové notace seznamu:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
 
 
def test():
    print("Test!")
 
 
root = tkinter.Tk()
 
menubar = tkinter.Menu(root)
 
filemenu = tkinter.Menu(menubar, tearoff=0)
filemenu.add_command(label="Open")
filemenu.add_command(label="Save")
filemenu.add_separator()
filemenu.add_command(label="Exit", command=root.quit)
menubar.add_cascade(label="File", menu=filemenu)
 
editmenu = tkinter.Menu(menubar, tearoff=0)
editmenu.add_command(label="Undo")
editmenu.add_separator()
editmenu.add_command(label="Cut")
editmenu.add_command(label="Copy")
editmenu.add_command(label="Paste")
editmenu.add_command(label="Delete")
editmenu.add_separator()
editmenu.add_command(label="Select All")
menubar.add_cascade(label="Edit", menu=editmenu)
 
colors = ("white", "yellow", "orange", "red", "magenta",
          "blue", "cyan", "green")
colormenu = tkinter.Menu(menubar, tearoff=0)
 
for color in colors:
    colormenu.add_command(label=color, background=color)
 
menubar.add_cascade(label="Colors", menu=colormenu)
 
helpmenu = tkinter.Menu(menubar, tearoff=0)
helpmenu.add_command(label="About", command=test)
menubar.add_cascade(label="Help", menu=helpmenu)
 
root.config(menu=menubar)
 
root.mainloop()

Obrázek 12: Výběr barev v minoritním OS.

Obrázek 13: Výběr barev v Linuxu.

Obrázek 14: Opět výběr barev v Linuxu.

12. Podtržení znaků v položkách menu pro jejich rychlý výběr

Jednotlivé položky menu je možné modifikovat pomocí několika příkazů. Pravděpodobně nejpoužívanější modifikací je přiřazení horkých klíčů jednotlivým položkám. To se provádí pomocí volby underline, za kterou následuje index znaku, který má být podtržen. První znak má index rovný nule. V některých GUI systémech je horký klíč specifikován přímo v textu jednotlivých položek pomocí speciálního znaku (například ampersandu). Tuto funkcionalitu je do knihovny Tkinter také možné doplnit pomocí jednoduché procedury, která bude mít jako své parametry identifikátor menu a řetězec se speciálním znakem.

Poznámka: horké klíče (hotkeys) se odlišují od klávesových zkratek, protože je jejich použití pouze lokální v rámci jediného menu. Pro větší zmatky se však tento termín někdy používá i v kontextu globálně použitelné klávesové zkratky.

Obrázek 15: Menu obsahuje položky s horkými klíči.

Použití indexu je samozřejmě univerzálnější, takže se podívejme, jak je realizováno:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
 
 
def test():
    print("Test!")
 
 
root = tkinter.Tk()
 
menubar = tkinter.Menu(root)
 
filemenu = tkinter.Menu(menubar, tearoff=0)
filemenu.add_command(label="Open", underline=0)
filemenu.add_command(label="Save", underline=0)
filemenu.add_separator()
filemenu.add_command(label="Exit", underline=1, command=root.quit)
menubar.add_cascade(label="File", menu=filemenu, underline=0)
 
editmenu = tkinter.Menu(menubar, tearoff=0)
editmenu.add_command(label="Undo", underline=0)
editmenu.add_separator()
editmenu.add_command(label="Cut", underline=2)
editmenu.add_command(label="Copy", underline=0)
editmenu.add_command(label="Paste", underline=0)
editmenu.add_command(label="Delete", underline=2)
editmenu.add_separator()
editmenu.add_command(label="Select All", underline=7)
menubar.add_cascade(label="Edit", menu=editmenu, underline=0)
 
colors = ("white", "yellow", "orange", "red", "magenta",
          "blue", "cyan", "green")
colormenu = tkinter.Menu(menubar, tearoff=0)
 
for color in colors:
    colormenu.add_command(label=color, background=color)
 
menubar.add_cascade(label="Colors", menu=colormenu, underline=0)
 
helpmenu = tkinter.Menu(menubar, tearoff=0)
helpmenu.add_command(label="About", command=test, underline=0)
menubar.add_cascade(label="Help", menu=helpmenu, underline=0)
 
root.config(menu=menubar)
 
root.mainloop()

13. Přidání obrázků (ikon) k položkám menu

K jednotlivým položkám menu je možné přidat i ikony, tedy rastrové obrázky. Obrázky je možné vytvořit programově, mnohem častěji se však nahrávají z externích souborů. V následujícím demonstračním příkladu je ukázáno, jakým způsobem je možné rastrové obrázky nahrát ze souboru. Základem je použití konstruktoru:

image_object = tkinter.PhotoImage(file="jméno_souboru_s_ikonou")

Tento konstruktor dokáže načíst obrázek v některém z podporovaných formátů, mezi něž patří především GIF (nikoli však již PNG). U obrázků je nutné si dát pozor na to, aby objekt nebyl automaticky odstraněn správcem paměti, který nedokáže detekovat existenci reference v knihovně Tk. Z tohoto důvodu budou všechny obrázky (prozatím) načteny do globálních proměnných:

open_image = tkinter.PhotoImage(file="icons/document-open.gif")
save_image = tkinter.PhotoImage(file="icons/document-save.gif")
exit_image = tkinter.PhotoImage(file="icons/application-exit.gif")
undo_image = tkinter.PhotoImage(file="icons/edit-undo.gif")
cut_image = tkinter.PhotoImage(file="icons/edit-cut.gif")
copy_image = tkinter.PhotoImage(file="icons/edit-copy.gif")
paste_image = tkinter.PhotoImage(file="icons/edit-paste.gif")
delete_image = tkinter.PhotoImage(file="icons/edit-delete.gif")
select_all_image = tkinter.PhotoImage(file="icons/edit-select-all.gif")

Dále je možné přiřadit obrázek k položce menu. Parametr compound je důležitý, jinak bude obrázek zobrazen namísto nápisu, zatímco my potřebujeme mít zobrazen jak obrázek, tak i nápis současně:

filemenu.add_command(label="Open", underline=0, image=open_image,
                     compound="left")

Obrázek 16: Menu s ikonami.

Poznámka: pokud máte ikony uložené ve formátu PNG, použijte pro konverzi knihovnu PIL (Python Imaging Library).

Úplný zdrojový kód vypadá následovně:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
 
 
def test():
    print("Test!")
 
 
root = tkinter.Tk()
 
menubar = tkinter.Menu(root)
 
open_image = tkinter.PhotoImage(file="icons/document-open.gif")
save_image = tkinter.PhotoImage(file="icons/document-save.gif")
exit_image = tkinter.PhotoImage(file="icons/application-exit.gif")
undo_image = tkinter.PhotoImage(file="icons/edit-undo.gif")
cut_image = tkinter.PhotoImage(file="icons/edit-cut.gif")
copy_image = tkinter.PhotoImage(file="icons/edit-copy.gif")
paste_image = tkinter.PhotoImage(file="icons/edit-paste.gif")
delete_image = tkinter.PhotoImage(file="icons/edit-delete.gif")
select_all_image = tkinter.PhotoImage(file="icons/edit-select-all.gif")
 
filemenu = tkinter.Menu(menubar, tearoff=0)
 
filemenu.add_command(label="Open", underline=0, image=open_image,
                     compound="left")
 
filemenu.add_command(label="Save", underline=0, image=save_image,
                     compound="left")
 
filemenu.add_separator()
 
filemenu.add_command(label="Exit", underline=1, image=exit_image,
                     compound="left", command=root.quit)
 
menubar.add_cascade(label="File", menu=filemenu, underline=0)
 
 
editmenu = tkinter.Menu(menubar, tearoff=0)
 
editmenu.add_command(label="Undo", underline=0, image=undo_image,
                     compound="left")
 
editmenu.add_separator()
 
editmenu.add_command(label="Cut", underline=2, image=cut_image,
                     compound="left")
 
editmenu.add_command(label="Copy", underline=0, image=copy_image,
                     compound="left")
 
editmenu.add_command(label="Paste", underline=0, image=paste_image,
                     compound="left")
 
editmenu.add_command(label="Delete", underline=2, image=delete_image,
                     compound="left")
 
editmenu.add_separator()
 
editmenu.add_command(label="Select All", underline=7, image=select_all_image,
                     compound="left")
 
menubar.add_cascade(label="Edit", menu=editmenu, underline=0)
 
 
colors = ("white", "yellow", "orange", "red", "magenta",
          "blue", "cyan", "green")
colormenu = tkinter.Menu(menubar, tearoff=0)
 
for color in colors:
    colormenu.add_command(label=color, background=color)
 
menubar.add_cascade(label="Colors", menu=colormenu, underline=0)
 
helpmenu = tkinter.Menu(menubar, tearoff=0)
helpmenu.add_command(label="About", command=test, underline=0)
menubar.add_cascade(label="Help", menu=helpmenu, underline=0)
 
root.config(menu=menubar)
 
root.mainloop()

Program si samozřejmě můžeme značně zjednodušit, například tak, že se obrázky načtou v jediné smyčce do slovníku:

image_names = [
    "document-open",
    "document-save",
    "application-exit",
    "edit-undo",
    "edit-cut",
    "edit-copy",
    "edit-paste",
    "edit-delete",
    "edit-select-all"]
 
images = {}
for image_name in image_names:
    images[image_name] = tkinter.PhotoImage(file="icons/%s.gif" % image_name)

Následně je možné si obrázky jednoduše ze slovníku „vytáhnout“:

filemenu.add_command(label="Open", underline=0, image=images["document-open"],
                     compound="left")

Úplný zdrojový kód vypadá takto:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
 
 
def test():
    print("Test!")
 
 
root = tkinter.Tk()
 
menubar = tkinter.Menu(root)
 
image_names = [
    "document-open",
    "document-save",
    "application-exit",
    "edit-undo",
    "edit-cut",
    "edit-copy",
    "edit-paste",
    "edit-delete",
    "edit-select-all"]
 
images = {}
for image_name in image_names:
    images[image_name] = tkinter.PhotoImage(file="icons/%s.gif" % image_name)
 
filemenu = tkinter.Menu(menubar, tearoff=0)
 
filemenu.add_command(label="Open", underline=0, image=images["document-open"],
                     compound="left")
 
filemenu.add_command(label="Save", underline=0, image=images["document-save"],
                     compound="left")
 
filemenu.add_separator()
 
filemenu.add_command(label="Exit", underline=1,
                     image=images["application-exit"], compound="left",
                     command=root.quit)
 
menubar.add_cascade(label="File", menu=filemenu, underline=0)
 
 
editmenu = tkinter.Menu(menubar, tearoff=0)
 
editmenu.add_command(label="Undo", underline=0, image=images["edit-undo"],
                     compound="left")
 
editmenu.add_separator()
 
editmenu.add_command(label="Cut", underline=2, image=images["edit-cut"],
                     compound="left")
 
editmenu.add_command(label="Copy", underline=0, image=images["edit-copy"],
                     compound="left")
 
editmenu.add_command(label="Paste", underline=0, image=images["edit-paste"],
                     compound="left")
 
editmenu.add_command(label="Delete", underline=2, image=images["edit-delete"],
                     compound="left")
 
editmenu.add_separator()
 
editmenu.add_command(label="Select All", underline=7,
                     image=images["edit-select-all"], compound="left")
 
menubar.add_cascade(label="Edit", menu=editmenu, underline=0)
 
 
colors = ("white", "yellow", "orange", "red", "magenta",
          "blue", "cyan", "green")
colormenu = tkinter.Menu(menubar, tearoff=0)
 
for color in colors:
    colormenu.add_command(label=color, background=color)
 
menubar.add_cascade(label="Colors", menu=colormenu, underline=0)
 
helpmenu = tkinter.Menu(menubar, tearoff=0)
helpmenu.add_command(label="About", command=test, underline=0)
menubar.add_cascade(label="Help", menu=helpmenu, underline=0)
 
root.config(menu=menubar)
 
root.mainloop()

Obrázek 17: Menu s ikonami.

ict ve školství 24

14. Repositář s demonstračními příklady

Zdrojové kódy všech dnes popsaných demonstračních příkladů naleznete pod následujícími odkazy:

Použité ikony naleznete na adrese https://github.com/tisnik/pre­sentations/tree/master/Pyt­hon_GUI/Tkinter/icons i s příslušnou licencí k použití.

15. Odkazy na Internetu

  1. Ovládací prvek (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Ovl%C3%A1dac%C3%AD_prvek_­%28po%C4%8D%C3%ADta%C4%8D%29
  2. Rezervovaná klíčová slova v Pythonu
    https://docs.python.org/3/re­ference/lexical_analysis.html#ke­ywords
  3. TkDocs: Styles and Themes
    http://www.tkdocs.com/tuto­rial/styles.html
  4. Drawing in Tkinter
    http://zetcode.com/gui/tkin­ter/drawing/
  5. Changing ttk widget text color (StackOverflow)
    https://stackoverflow.com/qu­estions/16240477/changing-ttk-widget-text-color
  6. Hra Breakout napísaná v Tkinteri
    https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/
  7. The Hitchhiker's Guide to Pyhton: GUI Applications
    http://docs.python-guide.org/en/latest/scenarios/gui/
  8. 7 Top Python GUI Frameworks for 2017
    http://insights.dice.com/2014/11/26/5-top-python-guis-for-2015/
  9. GUI Programming in Python
    https://wiki.python.org/mo­in/GuiProgramming
  10. Cameron Laird's personal notes on Python GUIs
    http://phaseit.net/claird/com­p.lang.python/python_GUI.html
  11. Python GUI development
    http://pythoncentral.io/introduction-python-gui-development/
  12. Graphic User Interface FAQ
    https://docs.python.org/2/faq/gu­i.html#graphic-user-interface-faq
  13. TkInter
    https://wiki.python.org/moin/TkInter
  14. Tkinter 8.5 reference: a GUI for Python
    http://infohost.nmt.edu/tcc/hel­p/pubs/tkinter/web/index.html
  15. TkInter (Wikipedia)
    https://en.wikipedia.org/wiki/Tkinter
  16. appJar
    http://appjar.info/
  17. appJar (Wikipedia)
    https://en.wikipedia.org/wiki/AppJar
  18. appJar na Pythonhosted
    http://pythonhosted.org/appJar/
  19. Stránky projektu PyGTK
    http://www.pygtk.org/
  20. PyGTK (Wikipedia)
    https://cs.wikipedia.org/wiki/PyGTK
  21. Stránky projektu PyGObject
    https://wiki.gnome.org/Pro­jects/PyGObject
  22. Stránky projektu Kivy
    https://kivy.org/#home
  23. Stránky projektu PyQt
    https://riverbankcomputin­g.com/software/pyqt/intro
  24. PyQt (Wikipedia)
    https://cs.wikipedia.org/wiki/PyGTK
  25. Stránky projektu PySide
    https://wiki.qt.io/PySide
  26. PySide (Wikipedia)
    https://en.wikipedia.org/wiki/PySide
  27. Stránky projektu Kivy
    https://kivy.org/#home
  28. Kivy (framework, Wikipedia)
    https://en.wikipedia.org/wi­ki/Kivy_(framework)
  29. QML Applications
    http://doc.qt.io/qt-5/qmlapplications.html
  30. KDE
    https://www.kde.org/
  31. Qt
    https://www.qt.io/
  32. GNOME
    https://en.wikipedia.org/wiki/GNOME
  33. Category:Software that uses PyGTK
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_PyGTK
  34. Category:Software that uses PyGObject
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_PyGObject
  35. Category:Software that uses wxWidgets
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_wxWidgets
  36. GIO
    https://developer.gnome.or­g/gio/stable/
  37. GStreamer
    https://gstreamer.freedesktop.org/
  38. GStreamer (Wikipedia)
    https://en.wikipedia.org/wi­ki/GStreamer
  39. Wax Gui Toolkit
    https://wiki.python.org/moin/Wax
  40. Python Imaging Library (PIL)
    http://infohost.nmt.edu/tcc/hel­p/pubs/pil/
  41. Why Pyjamas Isn’t a Good Framework for Web Apps (blogpost z roku 2012)
    http://blog.pyjeon.com/2012/07/29/why-pyjamas-isnt-a-good-framework-for-web-apps/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.