Hlavní navigace

Make up pro vaše aplikace: modul Tkinter

12. 5. 2003
Doba čtení: 9 minut

Sdílet

Po delší době se opět vracíme ke grafickým uživatelským rozhraním jazyka Python. Po dříve popisovaných vazbách na C nebo C++ knihovny si ukážeme použití modulu Tkinter. Zasvěceným není třeba nic vysvětlovat, jde o téměř standardní rozhraní k toolkitu Tk.

Opomíjené Tk

Tkinter je modul, který má mezi moduly pro práci s GUI téměř výsadní postavení, jako jediný je totiž zahrnut ve standardní knihovně Pythonu, což znamená, že je dostupný na téměř libovolné platformě (Linux, Mac, Windows). Na jeho vývoji pracuje mnoho vývojářů samotného Pythonu a používá ho dokonce i nativní IDE jazyka pojmenované IDLE.

Na začátek bych chtěl říci, že Tkinter osobně nepoužívám. Obdobně jsem na tom i s Tcl a Tk, ačkoli pár řádků kódu zde přece jen bude uvedeno. Jde však jen o „otrocký“ přepis kódu z Pythonu do Tcl za pomoci dokumentace. Pokud by cokoli šlo řešit lépe, rád se nechám poučit v diskusi.

Pro nedostatek prostoru nelze popsat programování úplně do hloubky, a proto se pokusím pouze o výtah toho nejdůležitějšího. Pro více informací vám doporučuji dokument An Introduction to Tkinter, dokumentaci Tcl/Tk a dokumentaci modulu Tkinter.

Na jednu stranu vám Tkinter přináší možnost vytvářet platformně nezávislé GUI aplikace (čehož důkazem je právě IDLE), na stranu druhou poněkud nezapadá do dnešního světa Linuxu, který je téměř polarizován na dvě části – vyznavače GNOME a KDE. Nepodporuje totiž nejmodernější grafické kudrlinky jako obě výše uvedená prostředí ani jejich knihovny GTK+ a Qt, nenaleznete v něm žádné skiny nebo, chcete-li, témata ani další funkce, které jsou spjaty spíše s prostředím desktopu než s GUI, jako jsou virtuální souborový systém apod.

Přesto má Tk několik předností – je dostupné (téměř) kdekoli, vyniká jednoduchou logikou, snaží se být v prostředí Unixů kompatibilní s Motifem a hlavně je postaveno kolem interpretovaného jazyka Tcl, což je poněkud sporná výhoda, ale výhoda to je.

Poněvadž je známo, že vrána k vráně sedá, i interpretovaný jazyk jako Python má daleko blíže k interpretovanému jazyku Tcl než k „revoluci znamének“ v podobě C++. Pádným argumentem proti Tkinteru by měla být právě ona nutnost, aby s každou GUI aplikací běžel i interpretr Tcl. Na jednu stranu se tím zvyšuje paměťová náročnost, na stranu druhou v dnešní době pamětí, jejichž velikost se blíží závratným hodnotám, a procesorů, jejichž rychlost roste snad ještě více, tento argument příliš neobstojí. Ke všemu obzvláště majitelé starších počítačů, na kterých jsou rozdíly v rychlosti startu daleko znatelnější, mohou potvrdit, že Tkinter se načte a běží daleko rychleji než kolos PyQt. Pokud by vás problematika GUI v Pythonu zajímala, mohu vás odkázat na tento thread v konferenci python@py.cz.

Vzhůru do toho…

Stejně, jako jsme začínali v předchozích dílech, i nyní si uvedeme jednoduchý Hello World:

from Tkinter import *

class Application(Frame):
    def print_hello(self):
        print 'Hello World'

    def createWidgets(self):
        self.QUIT = Button(self)
        self.QUIT['text'] = 'QUIT'
        self.QUIT['fg']   = 'red'
        self.QUIT['command'] =  self.quit

        self.QUIT.pack(side = 'left')

        self.hello = Button(self)
        self.hello['text'] = 'Hello World'
        self.hello['command'] = self.print_hello

        self.hello.pack(side = 'left')

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()
        self.createWidgets()

app = Application()
app.mainloop()

Na prvním řádku zavedeme modul Tkinter a do globálního prostoru jmen uložíme všechna jeho jména. To je doporučováno i dokumentací samotného Tkinteru, čili nemusíte se bát nějakých konfliktů jmen a dalších problémů. Kromě tohoto modulu jsou v Tkinteru i další, povětšinou obsahující třídy definující widgety, které jsou velice často používány, apod. Ti, kdo s nostalgií vzpomínají na své začátky v Turbo Pascalu, si možná vzpomenou na tzv. „želví grafiku“ a s překvapením zjistí, že ji lze používat i z Pythonu jako modul jménem turtle. Pro zájemce zkuste „import turtle; turtle.demo()“.

Tk

Jak jsme si již řekli, Tkinter používá toolkit Tk. Poněvadž programování v Tk nelze popsat v jediném článku, odkážu vás na jeho dokumentaci, případně na knihu Linux začínáme programovat (Computer Press 2000), kde je i jedna kapitola věnována Tcl a další právě Tk. Jen pro ukázku si zde uvedeme, jak by se výše uvedený program přepsal do jazyka Tcl (poznamenejme, že Tcl není objektový jazyk):

frame .application
button .application.quit -text "QUIT" \
    -fg red -command exit
pack .application.quit -side left
button .application.hi_there \
    -text "Hello World" -command say_hi
pack .application.hi_there -side left
pack .application

proc say_hi {} {
    puts "Hello Wordl"
}

Tk programátorovi nabízí mnoho widgetů (tlačítka, kontejnery, editovací řádky a pole, seznamy a také velmi silný widget canvas). Každý widget vytvoříme pomocí příkazu odpovídajícího jménu widgetu, za kterým mohou následovat volitelné vlastnosti. Tak již při vytvoření widgetu můžeme určit jeho vlastnosti včetně callbacku pomocí vlastnosti -command. Jako callbacky se předávají procedury, vytvořené příkazem proc. Dále je možné callback pověsit i na libovolnou událost, jako je stisk klávesy či přesun myši pomocí příkazu bind.

Stromová struktura widgetů se v Tk vytváří pomocí jmen widgetů a tečkové notace. Například .application.quit určuje, že widget .application je podwidgetem hlavního okna a má dalšího potomka .quit, poznamenejme, že potomky mohou mít pouze kontejnery. Ty nabízejí další důležitou funkci, pomocí níž se v kontejneru umisťují widgety – správce geometrie. Tk nabízí tři: Placer, Packer a Grid a jim odpovídající příkazy. Tito správci se starají o rozmístění widgetů vzhledem k rodičovskému kontejneru. Každý widget má svého jediného rodiče (v terminologii Tk označovaný jako master) a ten zase svého rodiče atd. až po nejvyšší okno (označované jako tečka ‚.‘), který již žádného rodiče nemá.

Poslední vlastností, o níž se v souvislosti s Tk zmíníme, je možnost provázání widgetů a proměnných. Pokud vím, je Tk jediným toolkitem s takovou koncepcí. Umožňuje totiž, aby např. textová řádka byla propojena s jednou globální proměnnou a obsah tohoto widgetu bude okamžitě zpřístupněn jako obsah této proměnné:

set text "Python & Tk"
entry .entry -textvariable text
pack .entry
button .clear -text "Clear"
    -command {set text ""}
pack .clear
button .set -text "Ahoj" \
    -command {set text "Python!"}
pack .set

Python + Tk

Nyní se budeme zabývat tím, jak to celé „zpythonizovat“. Budeme se muset vyrovnat s několika fakty. Především, Python je objektový jazyk, čili vaše programy v Pythonu budou napsány nejspíše pomocí objektového programování na rozdíl od programování v Tcl, z kterého přímo čiší procedurální styl (pokud máte někdo zkušenosti s programováním v [incr Tcl], které by mělo být objektovým rozšířením Tcl, můžete se podělit v diskusi). Dále výše uvedená šaráda s proměnnými se v Pythonu realizuje taktéž jiným způsobem. Při programování v Tkinteru navíc nemusíte o Tcl ani zavadit (nic vám ovšem nebrání pomocí metody tk.eval() vykonávat libovolné příkazy jazyka Tcl).

Nejprve si ukážeme nastavování vlastností. V první řadě můžete vlastnosti předávat objektu při jeho vytváření pomocí keyword argumentů (např. Button(text=„QUIT“) apod.). Pokud již máte nějaký widget vytvořen, můžete libovolnou vlastnost nastavit pomocí „indexu“ jako: my_butt[‚fg‘] = ‚red‘. Dále lze použít i metodu config() ve spojení s keyword argumenty pro nastavení více hodnot najednou.

Při vytváření stromu widgetů budete ještě potřebovat určit rodiče widgetu. To se provede velice jednouše předáním rodičovského widgetu jako prvního argumentu při vytváření potomka, viz volání ‚self.QUIT = Button(self)‘. Takto vytvoříme widget typu tlačítko jako potomka widgetu reprezentujícího naši aplikaci. Po vytvoření widgetu ho nezapomeňte zobrazit pomocí správce geometrie (volání metody widgetu pack() nebo place() či grid())! Těmto metodám lze rovněž předávat pojmenované argumenty specifikující volby správce geometrie. Tyto volby mají stejné názvy jako jejich ekvivalenty v Tk.

Události

Jak jsme si již řekli, v Tk se handlery navazují na události pomocí příkazu bind. V Pythonu k tomu slouží metoda widgetu bind(). Té jako parametr předáme jednak událost, na kterou chceme handler navázat, jednak funkci a parametr, který určuje, zda má tento handler přepsat původní handlery, nebo se k nim má připojit. Vše si ukážeme na příkladě:

from Tkinter import *

def print_key_1(e):
    print 'key down (1): %s' % e.char

def print_key_2(e):
    print 'key down (2): %s' % e.char

b = Button(text = 'Button 1')
b.pack()
b.bind('<KeyPress>', print_key_1)
b.bind('<KeyPress>', print_key_2, add = '+')
b.mainloop()

Povšimněte si, že v Tk se jako specifikace události používá řetězec. Například ‚<1>‘ specifikuje stisk prvního tlačítka myši, ‚<Enter>‘ signalizuje vstup myši do oblasti widgetu atd. Jako handler události pak vystupuje funkce přebírající jeden argument – instanci popisující událost. Její atributy pak přesně popisují událost (např. widget určuje, ve kterém widgetu událost vznikla atd.).

Kromě těchto událostí jsou zde ještě zjednodušené callbacky typu příkaz, které mají především widgety typu tlačítko. Jedná se o funkci bez argumentů, která se vykoná při určité akci (stisk tlačítka atd.). Tyto příkazy se nastavují pomocí vlastnosti ‚command‘ widgetu.

Proměnné

Jak jsme si ukázali na příkladu Tcl kódu, můžete používat speciální proměnné, které uchovávají vnitřní stav widgetu. V Pythonu však nelze spojit přímo libovolnou proměnnou s widgetem, k tomu zde slouží instance podtříd třídy Variable, například StringVar nebo IntVar:

from Tkinter import *

t = Entry()
t.pack()

text = StringVar()
t['textvariable'] = text

def clear_text():
    text.set('')
clear = Button(text = 'Clear', command = clear_text)
clear.pack()
t.mainloop()

Ke zpřístupnění hodnot proměnných se pak používá dvojice metod get() a set(). Dejte však pozor, při vytváření takovýchto proměnných je třeba mít existující kořenové okno (v Tk pojmenovávané jako tečka ‚.‘, v Tkinteru je reprezentováno jako instance třídy Tk), protože s každým takovýmto oknem je spojen jeden interpretr Tcl. Pokud vytváříte widgety, vytvoří se automaticky, na rozdíl od případu, kdy budete vytvářet pouhou proměnnou.

Unicode

Na závěr dnešního dílu se podíváme na podporu Unicode z Tkinteru. Jak jsme se již dozvěděli, v PyGTK se Unicode nedočkáte, zatímco v PyQt ano. Stejně tak dobře lze Unicode řetězce používat i z Tkinteru. Zkuste třeba následující ukázku:

from Tkinter import *

b = Button(text = u'\u592a\u9177')
b.pack()
b.mainloop()

Jak jsem se dozvěděl z knihy Linux začínáme programovat, mělo by se jednat o čínský přepis slova „Tcl“. Bohužel pravdivost této informace vám nemohu zaručit.

Závěr

Kromě výše uvedených vlastností ještě Tkinter dokáže spolupracovat s knihovnou Python Imaging Library (tedy spíše PIL spolupracuje s Tkinterem pomocí modulu ImageTk) a zobrazovat instance třídy Image rovnou ve widgetu PhotoImage. Jako ukázku použití vás odkážu např. na distribuci zdrojových kódů PIL, kde v adresáři Scripts naleznete několik ukázek použití ImageTk.

CS24_early

Jak vidíte, Tkinter (a potažmo Tk) není i přes svůj konzervativní vzhled vůbec špatný toolkit a dokáže mnoho věcí. Bude výhodný především pro ty, kdo vyvíjejí průmyslové aplikace (připomíná Motif) nebo pro programátory, kteří chtějí mít zaručenou platformní nezávislost toolkitu. Nezávislost ještě umocňuje podpora Unicode a výborná podpora ze stran vývojářů Pythonu.

Příště

V příštím dílu opustíme svět grafických rozhraní a řekneme si, jak se dají vyvíjet konzolové aplikace z jazyka Python. Podíváme se na zoubek modulu curses.

Byl pro vás článek přínosný?