Úvod do Tkinter - Okna pro jazyk Python - Postavené na základech An Introduction to Tkinter
Jak již bylo zmíněno dříve, aplikace Tkinter stráví většinu času uvnitř smyčky událostí, kam se dostane metodou mainloop. Události přichází z nejrůznějších zdrojů včetně uživatelského stisku kláves a operace myši a dále pak události překreslování od okenního správce (zprostředkovaně vyvolané v mnoha případech také uživatelem).
Tkinter poskytuje mocný mechanismus, který vám umožňuje tyto události obsluhovat. U každého udělátka můžete svázat (bind) jeho události s pythonýrskými funkcemi a metodami.
udelatko.bind(udalost, ovladac)
Pokud na udělátku nastane příslušná událost, daný ovladač je zavolán s parametrem popisujícím událost.
# -*- coding: utf-8 -*- from Tkinter import * hlavni = Tk() def volanafunkce(udalost): print u"kliknuto na pozici:", udalost.x, udalost.y ramec = Frame(hlavni, width=100, height=100) ramec.bind("<Button-1>", volanafunkce) ramec.pack() hlavni.mainloop()
V tomto příkladě používáme metodu bind udělátka 'ramec' k tomu, abychom svázali funkci 'volanafunkce' s událostí jménem <Button-1>. Spusťte tento program a klikejte v okně, které se objeví. Pokaždé když kliknete, vytiskne se do konsolového okna zpráva něco jako "kliknuto na pozici: 44 63".
Události klávesnice jsou posílány udělátku, které aktuálně má focus. Pokud tedy chcete, aby klávesnice fungovala v celé aplikaci, svažte příslušnou událost s hlavním oknem nebo nejvyšším Framem. Pokud chcete mít u každého udělátka jiné klávesové vazby, musíte zajistit vhodné přepínaní focusu mezi udělátky (např. pomocí událostí <Enter> a <Leave>). Na nastavení focusu na určité udělátko můžete použít metodu focus_set :
# -*- coding: utf-8 -*- from Tkinter import * hlavni = Tk() def klavesa(udalost): print u"stisknuto", repr(udalost.char) ramec = Frame(hlavni, width=100, height=100) ramec.bind("<Key>", klavesa) ramec.pack() ramec.focus_set() hlavni.mainloop()
Pustíte-li tento script, zjistíte, že musíte nejprve kliknout do rámce, než tento začne přijímat události z klávesnice.
Popisovače událostí se zadávají jako řetězce, za použití speciální syntaxe:
<modifikace-typ-detail>
Nejdůležitější částí popisovače události je pole typ. Určuje druh události, který si přejeme vázat, a může být uživatelskými akcemi jako Button nebo Key, případně událostmi okenního správce jako Enter, Configure a další. Pole modifikace a detail se používá na upřesňující informace - v mnoha případech se mohou vynechat. Existuje také mnoho způsobů, jak zjednodušit popisovač události; např. při zachytávání klávesy můžete vynechat ostré závorky a použít název klávesy tak jak je. Samozřejmě pokud to není mezera nebo ostrá závorka.
Namísto několika stran ohledně diskuze nad všemi syntaktickými zkratkami se podívejme na nejpoužívanější formáty událostí:
Tlačítko na myši je stisknuto nad udělátkem. Button 1 je úplně vlevo, button 2 je uprostřed a button 3 je vpravo na myši. Když stlačíte tlačítko myši na udělátku, Tkinter automaticky zachytí ukazatel myši a událost myši je zaslána k aktuálnímu udělátku. Aktuální pozice ukazatele myši (relativně vzhledem k udělátku) je pak přístupna v atributech x a y události, zaslaného volanefunkci.
Myš se pohybuje se stisknutým tlačítkem 1 (použijte B pro střední a B3 pro pravé tlačítko). Aktuální pozice myši je poskytována v atributech x a y v události zaslaného volanefunkci.
Tlačítko 1 bylo uvolněno. Aktuální pozice myši je poskytována v atributech x a y v události zaslaného volanefunkci.
Na Tlačítko 1 bylo dvojkliknuto. Jako prefixy můžete použít Double nebo Triple. Pokud svážete udělátko jak s obyčejným klikem (< Button-1> ) tak s dvojitým klikem, tak obě dvě události budou zavolány.
Ukazatel myši vstoupil na udělátko (tato událost neznamená, že uživatel stiskl Enter !).
Ukazatel myši opustil udělátko.
Tento widget (nebo jeho potomek) získal klávesnicový focus.
Focus byl přesunut z tohoto udělátka na jiný.
Na svázání se dají použít všechny klávesy na klávesnici. Pro běžnou 102-klávesovou klávesnici existují specielní klávesy Cancel, BackSpace, Tab, Return (klávesa Enter), Shift_L (levá klávesa Shift), Control_L (levá klávesa Control), Alt_L (levá klávesa Alt), Pause, Caps_Lock, Escape, Prior (Page Up), Next (Page Down), End, Home, Left, Up, Right, Down, Print, Insert, Delete, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, Num_Lock a Scroll_Lock.
Uživatel stiskl jakoukoliv klávesu. Klávesa je poskytnuta v atributu char události, která se předává volané funkci. (u speciálních kláves se jedná o prázdný řetězec).
Uživatel napsal písmeno "a" . Většina tisknutelných znaků se dá použít tímto způsobem. Výjimkou jsou klávesy mezerník ( <space> ) a "je menší" ( <less> ). 1 značí vazbu na klávesu "1", zatímco <1> je vazba na myš.
Uživatel stiskl šipku nahoru současně s přidrženou klávesou Shift. Jako prefix můžete použít Alt, Shift a Control.
Udělátko změnilo velikost (na některých platformách umístnění). Nová velikost je poskytována v atributech width a height události, předávané volané funkci.
Uživatel stiskl CTRL+SHIFT+H. Záleží na velikosti písmen, tedy např. <Control-KeyPress-H> není stejné jako <Control-KeyPress-h>. U prvního se musí ještě navíc přidržet Shift, aby se dosáhlo velkého písmene "H".
Uživatel roloval s kolečkem myši. Směr posunu je poskytnut v atributu delta události, předávané volané funkci.
Událost je obvyklá instance třídy s množstvím atributů, popisujícím událost.
Udělátko, které vyvolalo tuto událost. Toto je platná instance tkinterovského udělátka. Při tisku ovšem získáte přes metodu __str__ pouze jeho jméno. Při porovnávání se však použije daná instance. Tento atribut se nastavuje u všech událostí.
Současná pozice myši, v pixelech.
Současná pozice myši relativně k levému hornímu rohu obrazovky, v pixelech.
Kód znaku, jen u klávesnicových událostí, jako řetězec.
Symbol klávesy, jen u klávesnicových událostí.
Kód klávesy, jen u klávesnicových událostí.
Číslo tlačítka, , jen u myších událostí.
Nová velikost udělátka, v pixelech jen u Configure událostí.
Typ události.
Z důvodu přenositelnosti byste se měli držet char, height, width, x, y, x_root, y_root a widget. Pokud samozřejmě si nejste jisti, tím co děláte...
Metodu bind, kterou jsme používali výše, vytvářela vazby na instance. To značí, že vazba se vztahuje na pouze konkrétní udělátko; pokud vytvoříte nová udělátka, vazby se dědit nebudou.
Nicméně Tkinter dovoluje vytvářet vazby i na úrovni tříd a aplikací; ve skutečnosti to je tak, že můžete vytvářet vazby na čtyřech úrovních:
instance udělátka - bind.
toplevel okno (Toplevel nebo root ) - bind.
třída udělátka - bind_class
celá aplikace - bind_all.
Můžete například použít bind_all, chcete-li svázat klávesu F1 kompletně v celé aplikaci. Ale co se stane, když vytvoříte opakované vazby pro stejnou klávesu nebo budete chtít mít vazby, které se budou překrývat?
Tak za prvé: na každé z těchto čtyř úrovní Tkinter vybírá "nejbližší shodu" z dostupných vazeb. Když například vytvoříte vazby na události <Key> a <Return>, pouze ta druhá vazba bude vyvolána, když stisknete klávesu Enter.
Pokud však přidáte vazbu na <Return> na toplevel úrovni, budou volány obě dvě vazby.Tkinter nejprve nejlepší vazbu na úrovni instance, pak na úrovni toplevel, pak nejlepší vazbu na úrovni třídy (což je často standardní) a nakonec nejlepší vazbu na úrovni aplikace. Takže v extrémním případě jediná událost volá 4 ovladače událostí.
Nejběžnější zmatky nastávají, když si chcete použít vazby tak, abyste přepsali chování standardního widgetu. Můžeme si za příklad vzít požadavek na změnu chování klávesy Enter v udělátku Text, tak, aby uživatel nemohl do textu vkládat řádky. Snad to dokáže tento trik?
def ignorace(udalost): pass text.bind("<Return>", ignorace)
nebo když dáváte přednost jednořádkovým programům:
text.bind("<Return>", lambda e: None)
(funkce lambda zde přijímá jeden argument a vrací None )
Bohužel nové řádky se dají stále ještě vložit, protože výše uvedená vazba se děje jen na úrovni instance a standardní chování je odvozeno od vazby ve třídě.
Můžete použít metodu bind_class na změnu vazby úrovně třídy, což však zapřičiní změnu chování všech textových udělátek v aplikaci. Snažší řešením je zabránit Tkinteru v šíření (propagating) události do dalších ovladačů; prostě vraťte "break" z vašeho ovladače události:
def ignorace(udalost): return "break" text.bind("<Return>", ignorace)
nebo
text.bind("<Return>", lambda e: "break")
Jen tak mimochodem - pokud byste opravdu chtěli změnit chování všech textových udělátek ve vaší aplikace, takto se dá použít metoda bind_class :
top.bind_class("Text", "<Return>", lambda e: None)
Je ale několik důvodů, proč byste to tak dělat neměli. Například dokonale vám to zamíchá hlavu toho dne, kdy se rozhodnete používat nějaké super malé UI komponenty z webu. Lepší bude, použijete-li své vlastní specializované udělátka Text a standardní vazby Tkinter zachováte netknuté:
class MyText(Text): def __init__(self, master, **kw): apply(Text.__init__, (self, master), kw) self.bind("<Return>", lambda e: "break")
Nad událostní vazby Tkinter také podporuje mechanismus nazývaný ovladače protokolu ( protocol handlers). Termín protokol se zde vztahuje na spolupráci aplikace a správce oken. Nejpoužívanější protokol se nazývá WM_DELETE_WINDOW a používání na definování toho, co se má stát, když uživatel explicitně uzavírá okno pomocí správce okna (např. Alt+F4, nebo klik na křížek v pravém horním rohu).
Můžete použít metodu protocol na instalaci ovladače pro tento protokol (udelatko musí být root nebo Toplevel):
udelatko.protocol("WM_DELETE_WINDOW", ovladac)
Jakmile toto uděláte, Tkinter přestane automaticky zavírat okno. Namísto toho můžete zobrazovat hlášku, že data nejsou uložena na disk nebo v některých případech, dokonce požadavek zavření ignorovat. Zavření okna z tohot ovladače provedete pomocí metody okna destroy :
# -*- coding: utf-8 -*- from Tkinter import * import tkMessageBox def volanafunkce(): if tkMessageBox.askokcancel(u"Ukončení", u"Skutečně chcete skončit?"): hlavni.destroy() hlavni = Tk() hlavni.protocol("WM_DELETE_WINDOW", volanafunkce) hlavni.mainloop()
Okenní manažeři byly původně součástí X windows systému (jsou definovány v dokumentu s názvem Inter-Client Communication Conventions Manual neboli ICCCM). Na této platformě můžete instalovat také další protokoly, jako například WM_TAKE_FOCUS a WM_SAVE_YOURSELF. Detaily najdete v dokumentaci ICCCM.
http://infohost.nmt.edu/tcc/help/pubs/tkinter/events.html
http://tmml.sourceforge.net/doc/tk/bind.html
Přeložil a upravil - Pavel Kosina - Copyright © 2006 TkinterCZ - GNU Free Documentation License 1.2