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

Ahoj Světe! Podruhé a objektově.

Když se píše rozsáhlejší program, je vhodné umístit kód do jedné nebo více tříd. Následující příklad "Hello World" je převzat z knihy Matta Conwaye A Tkinter Life Preserver

# -*- coding: utf-8 -*-
from Tkinter import *

class App:

    def __init__(self, master):

        frame = Frame(master)
        frame.pack()

        self.button = Button(frame, text="KONEC", fg="red", command=master.destroy)
        self.button.pack(side=LEFT)

        self.hi_there = Button(frame, text="Pozdrav!", command=self.rekni_ahoj)
        self.hi_there.pack(side=LEFT)

    def rekni_ahoj(self):

        print u'Ahoj všici!'


if __name__=="__main__":

    root = Tk()
    app = App(root)
    root.mainloop()

Spuštění příkladu.

Pokud spuštění proběhne v pořádku, uvidíte následující okno.

Ukázková aplikace.

Když nyní kliknete na pravé tlačítko, vypíše se do konzole text 'Ahoj všici!'. Po kliknutí na levé se program ukončí.

Poznámka : Některá vývojová prostředí mají problém spustit programy, jako je tento. Je to zapříčiněno tím, že používají Tkinter pro vykreslování sama sebe a pokud v programu použijete mainloop a pak zavoláte destroy(), ukončí se jak Váš program tak vlastní vývojové prostředí. Proto si pročtěte dokumentaci k Vašemu editoru a následně upravte svůj program. (Nebo vývojové prostředí ;-)).

Detaily.

Tento ukázkový program je napsaný jako třída. Konstruktor (metoda __init__) je volaný s parametrem obsahujícím rodičovský widget (master), do kterého sepřidává množství widgetů - potomků. Vlastní metoda začíná vytvořením widgetu Frame, který je jednoduchým kontejnerem (prvkem, který se používá pouze k udržení ostatních widgetů pohromadě).

class App:

    def __init__(self, master):

        frame = Frame(master)
        frame.pack()

Instance frame je uložena v lokální proměnné frame. Ihned poté, co jsme vytvořili widget, zavoláme metodu pack(), abychom učinili frame viditelným.

Dále vytvoříme dvě tlačítka (widget Button) jako potomky frame.

self.button = Button(frame, text="KONEC", fg="red", command=master.destroy)
self.button.pack(side=LEFT)

self.hi_there = Button(frame, text="Pozdrav!", command=self.rekni_ahoj)
self.hi_there.pack(side=LEFT)

Tentokrát jsme použili několik argumentů. První tlačítko je pojmenováno 'KONEC' a je červené (fg je zkratkou pro foreground). Druhé se jmenuje 'Pozdrav!'. Obě dvě tlačítka mají také argument 'command', který specifikuje funkci (v tomto případě metodu, ale to je jen slovíčkaření ;-) ) která se volá po kliknutí na tlačítko.

Poznámka ke command: Všimněte si prosím, že zde *není* napsáno command=self.rekni_ahoj(). Za názvem funkce se nepíší uvozovky. Pokud je tam napíšete, funkce se zavolá pouze jednou, při spuštění programu a na samotné tlačítko pak již nebude reagovat.

Obě dvě tlačítka jsou zobrazena pomocí metody pack(), ale tentokrát s argumentem side=LEFT. Znamená to, že se budou přitiskávat k předchozím zapakovaným widgetům vždy jejich levé hrany. Standardně jsou widgety vykreslovány uvnitř jejich rodičovských prvků (které jsou je momentálně master pro frame a frame pro tlačítka). Jestliže argument side není zadán, použije se TOP.

Odezva kliknutí na tlačítko 'Pozdrav!' je zajištěna následující funkcí. Jednoduše vytiskne na konzoli zprávu pokaždé, když je kliknuto na tlačítko.

def rekni_ahoj(self):

    print u"Ahoj všici!"

Nakonec vytvoříme kód, který vytvoří widget root (třídy Tk) a jednu instanci třídy App (použitím root jako rodiče).

if __name__=="__main__":

    root = Tk()
    app = App(root)
    root.mainloop()

Poslední volání je metoda mainloop() widgetu root. Spustí smyčku Tkinteru, ve které zůstane, dokud nebude zavolána metoda destroy() nebo dokud nebude okno prostě zavřeno.

Více o odkazech

V druhém ukázkovém programu je widget frame uložen v lokální proměnné frame, zatímco widgety tlačítek jsou uloženy v atributech instance třídy App. Rýsuje se zde vážný problém? Co se stane, když se metoda __init__ ukončí, tím pádem také proměnná frame?

V tomto případě můžete být v klidu, není potřeba uchovávat odkaz na widget frame. Tkinter automaticky upravuje strom widgetů (přes rodič/potomek vazby každého atributu), takže widget 'nezmizí' když je zničen poslední odkaz. Pokud bychom tak chtěli učinit, musíme widget ručně zničit (použitím metody destroy()), ale pokud chcete s widdgetem ještě něco dělat po jeho vytvoření, je lepší jeho odkaz uchovat ručně.

Jestli nechcete uchovat odkaz na widget, můžete použít jedno řádkový zápis.

Button(frame, text="Pozdrav!", command=self.hello).pack(side=LEFT)

Neukládejte ale návratovou hodnotu této operace. Dostali byste stejně jenom None, generované metodou pack(). Tak jako tak, je bezpečnější a lepší oddělit inicializaci od vlastního vykreslení.

w = Button(frame, text="Pozdrav§", command=self.hello) 
w.pack(side=LEFT)

Více o jménech

Dalším zdrojem zmatků, hlavně pro ty programátory kteří ovládají Tcl, je způsob zápisu jmen widgetů v Tkinteru. V Tcl, musíte specifikovat název každého widgetu. Například, následující Tcl kód vytvoří tlačítko pojmenované 'ok', potomek widgety 'dialog' (který je potomkem root -> ".").

button .dialog.ok

Shodný program v Pythonu.

ok = Button(dialog)

Nicméně, v příkladu Tkinteru jsou ok a dialog odkazy na instance widgetů, ne přímo jejich názvy. Protože Tk samo o sobě potřebuje jména, Tkinter automaticky přidělí každému widgetu unikátní název. V příkladu tedy jméno instance dialog může vypadat třeba takto '.1428748' a tlačítko třeba takhle '.1428748.142920'. Pokud se chcete dozvědět tato plná jména, stačí použít funkci str()

>>> print str(ok)
.1428748.1432920

(Pokud něco tisknete, Python automaticky použije str() k nalezení tisknutého textu. Ale operace třeba jako 'jmeno = ok' toto neprovádí, takže pozor na to.)

Pokud opravdu potřebujete definovat název widgetu, můžete použít parametr name. Jeden z důvodu (možná i jediný) proč dělat takovouto věc, je potřeba spolupracovat s kódem psaným v Tcl.

V následujícím případě je výsledný název widgetu '.dialog.ok' (nebo, pokud zapomenete pojmenovat dialog, tak nějak takhle '.1428748.ok')

ok = Button(dialog, name="ok")

Aby se nestalo, že vaše nové jméno bude kolidovat se jmény automaticky přidělovanými Pythonem, nepoužívejte názvy, které obsahují pouze číslice. Také se nesnažte měnit názvy již vytvořených widgetů!


Přeložil a upravil - tommz - Copyright © 2006 TkinterCZ - GNU Free Documentation License 1.2