Úvod do Tkinter - Okna pro jazyk Python - Postavené na základech An Introduction to Tkinter

Události a vazby (Events and bindings)

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.

Zachytávání myši

# -*- 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".

Zachytávání klávesnice

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.

Události

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í:

Formáty událostí

<Button-1>

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.

Namísto Button můžete použít ButtonPress nebo to dokonce zcela vypustit: <Button-1>, <ButtonPress-1> a <1> jsou všechno synonyma. Kvůli čitelnosti dávám přednost syntaxi <Button-1>.
<B1-Motion>

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.

<ButtonRelease-1>

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.

<Double-Button-1>

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.

<Enter>

Ukazatel myši vstoupil na udělátko (tato událost neznamená, že uživatel stiskl Enter !).

<Leave>

Ukazatel myši opustil udělátko.

<FocusIn>

Tento widget (nebo jeho potomek) získal klávesnicový focus.

<FocusOut>

Focus byl přesunut z tohoto udělátka na jiný.

Klávesy na klávesnici - např. <Return>

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.

<Key>

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).

a

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š.

<Shift-Up>

Uživatel stiskl šipku nahoru současně s přidrženou klávesou Shift. Jako prefix můžete použít Alt, Shift a Control.

<Configure>

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.

<Control-Shift-KeyPress-H>

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".

<MouseWheel>

Uživatel roloval s kolečkem myši. Směr posunu je poskytnut v atributu delta události, předávané volané funkci.

Objekt události

Událost je obvyklá instance třídy s množstvím atributů, popisujícím událost.

Atributy události

widget

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í.

x, y

Současná pozice myši, v pixelech.

x_root, y_root

Současná pozice myši relativně k levému hornímu rohu obrazovky, v pixelech.

char

Kód znaku, jen u klávesnicových událostí, jako řetězec.

keysym

Symbol klávesy, jen u klávesnicových událostí.

keycode

Kód klávesy, jen u klávesnicových událostí.

num

Číslo tlačítka, , jen u myších událostí.

width, height

Nová velikost udělátka, v pixelech jen u Configure událostí.

type

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...

Vázání instancí a tříd

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:

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")

Protokoly

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 :

Zachytávání zavíracích událostí
# -*- 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()

Další protokoly

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.

Záložky, Oblíbené

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