Obsah
1. Grafické uživatelské rozhraní v Pythonu: další možnosti nabízené widgety Text a ScrolledText
2. Složitější příklad se styly textu
3. Obrázky BitmapImage a PhotoImage ve widgetu Text
4. Konfigurace tabulačních zarážek
5. Zarovnání textu na tabulačních zarážkách
6. Další vlastnosti řetězců vkládaných do widgetu Text
7. Automatické zvýraznění textu, nad nímž se nachází kurzor myši
8. Reakce na stisk vybraného tlačítka (tlačítek) myši nad částí textu
9. Další widgety vložené do widgetu Text
10. Přidání dalších widgetů (tlačítka, checkboxu…) do textu
11. Modul ScrolledText/tkinter.scrolledtext
12. Příklad použití modulu ScrolledText – varianta pro Python 2.x
13. Příklad použití modulu tkinter.scrolledtext – varianta pro Python 3.x
14. Repositář s demonstračními příklady
1. Grafické uživatelské rozhraní v Pythonu: další možnosti – nabízené widgety Text a ScrolledText
V předchozí části seriálu o tvorbě grafického uživatelského rozhraní v Pythonu jsme se kromě jiného seznámili i se základními vlastnostmi widgetu nazvaného Text. Připomeňme si jen, že Text patří vedle již popsaného widgetu canvas k prakticky nejsložitějším a současně i nejflexibilnějším objektům, se kterými je možné v knihovně Tkinter pracovat. Ve své nejjednodušší podobě slouží tento widget k zobrazení víceřádkového textu. To však není zdaleka vše. Text je totiž možné sdružovat do bloků a každému bloku nastavit nějaké atributy, typicky font, velikost textu, barvu textu, styl znaků (tučně, kurzíva, podtrženě atd.). Kromě toho se každý blok může chovat jako hypertextový odkaz. Dále je možné text různě zarovnávat, přidávat do něj obrázky či další widgety atd.
Obrázek 1: Nejjednodušší forma widgetu Text.
Dnes se seznámíme s dalšími možnostmi, které jsou tímto widgetem nabízeny. Nejprve si ukážeme složitější formátování textu, dále vkládání obrázků (bitmap i plnobarevných obrázků) do textu, konfiguraci tabulačních zarážek, které lze v případě potřeby použít pro zarovnání textu, a nezapomeneme ani na možnost zavolání zvoleného uživatelského kódu (většinou funkce či anonymní funkce) ve chvíli, kdy dojde k nějaké události. Na závěr se seznámíme s widgetem nazvaným ScrollText či scrolltext (podle použité verze Pythonu). Tento widget vlastně spojuje možnosti klasického scrollbaru s widgetem Text, a to velmi jednoduchým způsobem – celá konfigurace scrollbaru je totiž záležitostí jediného parametru.
2. Složitější příklad se styly textu
Kromě barev vykreslovaných řetězců je možné ve widgetu Text měnit i typ písma, což je ukázáno na dnešním prvním demonstračním příkladu. Zde si můžete všimnout, že vlastnosti underline (podtržení) a overstrike (přeškrtnutí, v příkladu není použito, sami si ho samozřejmě můžete vyzkoušet) se povolují a zakazují přiřazením logické hodnoty True/False. Přesná specifikace fontu je popsána na stránce http://www.tcl.tk/man/tcl8.5/TkCmd/font.htm#M13, v naprosté většině případů si však vystačíme s řetězcem obsahujícím jméno fontu a jeho velikost, které mohou být alternativně doplněny o slovo „bold“ a/nebo „italic“ (obě slova lze zkombinovat, tedy například „„Helvetica 12 bold italic““). Kromě toho je možné font specifikovat n-ticí. Do textového widgetu je vložena i bitmapa, což je problematika, které se budeme podrobněji věnovat v navazující kapitole:
Obrázek 2: Text s různými styly i s vloženou bitmapou.
Následuje výpis zdrojového kódu prvního demonstračního příkladu:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys def exit(): sys.exit(0) root = tkinter.Tk() image = tkinter.BitmapImage(file="test.xbm", foreground="white") text = tkinter.Text(root, font="Helvetica 20", wrap=tkinter.WORD, background="#202020", width=40, height=16) text.tag_configure("underlined_red", foreground="red", underline=True) text.tag_configure("big_green", foreground="green", font="Helvetica 40") text.tag_configure("blue", foreground="blue") text.tag_configure("magenta", foreground="magenta") text.tag_configure("cyan", foreground="cyan") text.tag_configure("small_yellow", foreground="yellow", font="Helvetica 10") text.tag_configure("brown", foreground="brown") text.tag_configure("pink", foreground="pink") text.tag_configure("white", foreground="white", font="Courier 20") # práce s widgetem text.insert(tkinter.END, "Underlined Red\n", "underlined_red") text.insert(tkinter.END, "Magenta\n", "magenta") text.insert(tkinter.END, "Blue\n", "blue") text.insert(tkinter.END, "Cyan\n", "cyan") text.insert(tkinter.END, "Big Green\n", "big_green") text.insert(tkinter.END, "Small Yellow\n", "small_yellow") text.image_create(tkinter.END, image=image) text.insert(tkinter.END, "Brown\n", "brown") text.insert(tkinter.END, "Pink\n", "pink") text.insert(tkinter.END, "White\n", "white") button = tkinter.Button(root, text="Close window", command=exit) text.pack() button.pack() root.mainloop()
Poznámka: ještě bych rád připomenul, že u všech dnes popsaných demonstračních příkladů je zachována editovatelnost textu, nejedná se tedy o neživou část GUI, ale část aktivně komunikující s uživatelem.
3. Obrázky BitmapImage a PhotoImage ve widgetu Text
Mezi text umístěný na widgetu Text je možné vkládat obrázky, a to jak dvoubarevné obrázky, tak i obrázky vícebarevné. Postup je ve skutečnosti velmi jednoduchý – nejdříve obrázek načteme z externího datového souboru nebo z dat uložených přímo ve skriptu a následně obrázek vložíme do textu metodou text.image_create(), v níž specifikujeme jak bod vložení (například na konec textu), tak i vlastní obrázek. Samotný text přitom zůstává editovatelný; i samotné obrázky je možné mazat, posouvat atd.
Příklad vložení bitmapy, tedy dvoubarevného obrázku:
image = tkinter.BitmapImage(file="test.xbm", foreground="white") text.image_create(tkinter.END, image=image)
Příklad vložení vícebarevného obrázku (buď s barvovou paletou či obrázku plnobarevného):
photo_image = tkinter.PhotoImage(file="icons/application-exit.gif") text.image_create(tkinter.END, image=photo_image)
Následuje výpis zdrojového kódu demonstračního příkladu, v němž se používá jak objekt typu BitmapImage, tak i objekt typu PhotoImage:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys def exit(): sys.exit(0) root = tkinter.Tk() image = tkinter.BitmapImage(file="test.xbm", foreground="white") photo_image = tkinter.PhotoImage(file="icons/application-exit.gif") text = tkinter.Text(root, font="Helvetica 20", wrap=tkinter.WORD, background="#202020", width=40, height=16) text.tag_configure("underlined_red", foreground="red", underline=True) text.tag_configure("big_green", foreground="green", font="Helvetica 40") text.tag_configure("blue", foreground="blue") text.tag_configure("magenta", foreground="magenta") text.tag_configure("cyan", foreground="cyan") text.tag_configure("small_yellow", foreground="yellow", font="Helvetica 10") text.tag_configure("brown", foreground="brown") text.tag_configure("pink", foreground="pink") text.tag_configure("white", foreground="white", font="Courier 20") # práce s widgetem text.insert(tkinter.END, "Underlined Red\n", "underlined_red") text.insert(tkinter.END, "Magenta\n", "magenta") text.insert(tkinter.END, "Blue\n", "blue") text.insert(tkinter.END, "Cyan\n", "cyan") text.insert(tkinter.END, "Big Green\n", "big_green") text.insert(tkinter.END, "Small Yellow\n", "small_yellow") text.image_create(tkinter.END, image=image) text.insert(tkinter.END, "Brown\n", "brown") text.insert(tkinter.END, "Pink ", "pink") text.image_create(tkinter.END, image=photo_image) text.insert(tkinter.END, "White", "white") button = tkinter.Button(root, text="Close window", command=exit) text.pack() button.pack() root.mainloop()
Obrázek 3: Text s různými styly i s vloženou bitmapou i barevným obrázkem. Povšimněte si, že obrázek může být umístěn na stejném řádku s textem („x“).
4. Konfigurace tabulačních zarážek
Minule jsme si ukázali, jak se nastavují rozměry widgetu Text. Výška je specifikována v počtu textových řádků a šířka v počtu normoznaků odvozených od metriky fontu:
text = tkinter.Text(root, width=40, height=16)
Kromě rozměrů widgetu je možné specifikovat i pozice tabulačních zarážek. Ty se zadávají pomocí pojmenované volby tabs, za níž následuje řetězec obsahující seznam značek. Seznam obsahuje pozice tabulačních zarážek; tyto pozice jsou chápány jako vzdálenosti zarážek od levého okraje widgetu. Pokud je za číslem udávajícím vzdálenost zapsán znak „c“, znamená to, že se vzdálenost bude počítat v šířkách normalizovaných znaků (což je většinou lepší, než se snažit o přepočet na pixely či na milimetry, i to je ale možné):
text = tkinter.Text(root, font="Helvetica 20", wrap=tkinter.WORD, tabs="5c 11c 18c", width=40, height=16)
Pozor na to, že použitý font může obsahovat (a většinou i obsahuje) proporcionální znaky a v některých případech nemusí výpočet šířky normalizovaných znaků proběhnout korektně – to je případ zejména různých sharewarových TrueType fontů, které někdy mají špatně vyplněnou tabulku s metrikami (tyto fonty se však nebudou chovat korektně ani v dalších aplikacích, navíc dnes pro Linux existují dostatečně kvalitní open source fonty).
V dalším demonstračním příkladu je vytvořen widget text, který bude mít rozměry 40×16 normoznaků. Přitom jsou nastaveny dvě tabulační zarážky. Povšimněte si, že znak tabulátoru se do řetězce zadává naprosto stejným způsobem jako v mnoha dalších programovacích jazycích (C, C++, Java, Perl):
text.insert(tkinter.END, "Mesic\tObrat\n", "nadpis") text.insert(tkinter.END, "leden\t100\n") ... ... ... text.insert(tkinter.END, "suma\t3900\n", "suma")
Některé řádky textu jsou zobrazeny jinou barvou a první řádek je podtržen. Toho bylo dosaženo vytvořením tagů nadpis a suma:
text.tag_configure("nadpis", foreground="red", underline=True) text.tag_configure("suma", foreground="blue")
Obrázek 4: Text s tabulačními zarážkami použitými pro vytvoření jednoduché tabulky.
Následuje výpis zdrojového kódu demonstračního příkladu:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys def exit(): sys.exit(0) root = tkinter.Tk() text = tkinter.Text(root, font="Helvetica 20", wrap=tkinter.WORD, tabs="5c 11c", width=40, height=16) text.tag_configure("nadpis", foreground="red", underline=True) text.tag_configure("suma", foreground="blue") text.insert(tkinter.END, "Mesic\tObrat\n", "nadpis") text.insert(tkinter.END, "leden\t100\n") text.insert(tkinter.END, "unor\t200\n") text.insert(tkinter.END, "brezen\t0\n") text.insert(tkinter.END, "duben\t1000\n") text.insert(tkinter.END, "kveten\t100\n") text.insert(tkinter.END, "cerven\t200\n") text.insert(tkinter.END, "cervenec\t0\tdovolene\n") text.insert(tkinter.END, "srpen\t1000\n") text.insert(tkinter.END, "zari\t100\n") text.insert(tkinter.END, "rijen\t200\n") text.insert(tkinter.END, "listopad\t0\n") text.insert(tkinter.END, "prosinec\t1100\tvanoce\n\n") text.insert(tkinter.END, "suma\t3900\n", "suma") button = tkinter.Button(root, text="Close window", command=exit) text.pack() button.pack() root.mainloop()
5. Zarovnání textu na tabulačních zarážkách
Pokud se v seznamu s tabulačními zarážkami uvede jedno ze slov left, right, center nebo numeric, značí to způsob zarovnání textu vůči zarážce – zarovnání doleva, doprava, na střed a na desetinnou tečku/čárku (zarovnání na čárku bude funkční jen se správně nastavenou lokalizací). Tato funkce se chová stejně jako v textových procesorech. Například se podívejme na specifikaci zarovnání druhého sloupce doprava a třetího sloupce doleva (první sloupec samozřejmě začíná na levém okraji widgetu):
text = tkinter.Text(root, font="Helvetica 20", wrap=tkinter.WORD, tabs="7c right 11c left", width=40, height=16)
Obrázek 5: Text s tabulačními zarážkami se specifikovaným způsobem zarovnání jednotlivých sloupců „tabulky“.
Opět se podívejme na to, jak vypadá celý demonstrační příklad s widgetem Text a s nakonfigurovanými tabulačními zarážkami:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys def exit(): sys.exit(0) root = tkinter.Tk() text = tkinter.Text(root, font="Helvetica 20", wrap=tkinter.WORD, tabs="7c right 11c left", width=40, height=16) text.tag_configure("nadpis", foreground="red", underline=True) text.tag_configure("suma", foreground="blue") text.insert(tkinter.END, "Mesic\tObrat\n", "nadpis") text.insert(tkinter.END, "leden\t100\n") text.insert(tkinter.END, "unor\t200\n") text.insert(tkinter.END, "brezen\t0\n") text.insert(tkinter.END, "duben\t1000\n") text.insert(tkinter.END, "kveten\t100\n") text.insert(tkinter.END, "cerven\t200\n") text.insert(tkinter.END, "cervenec\t0\tdovolene\n") text.insert(tkinter.END, "srpen\t1000\n") text.insert(tkinter.END, "zari\t100\n") text.insert(tkinter.END, "rijen\t200\n") text.insert(tkinter.END, "listopad\t0\n") text.insert(tkinter.END, "prosinec\t1100\tvanoce\n\n") text.insert(tkinter.END, "suma\t3900\n", "suma") button = tkinter.Button(root, text="Close window", command=exit) text.pack() button.pack() root.mainloop()
Obrázek 6: Widget Text nemá problém ani se znaky s nabodeníčky.
6. Další vlastnosti řetězců vkládaných do widgetu Text
S využitím tagů je možné nastavit i další vlastnosti textových řetězců, které se vkládají do widgetu Text. Pro mnoho aplikací je důležité nastavení zarovnávání textů. To je možné obstarat buď pomocí výše zmíněných tabulačních zarážek (což je mnohdy zbytečně komplikované), nebo využitím vlastnosti justify. Této vlastnosti lze přiřadit více hodnot: tkinter.LEFT, tkinter.RIGHT a tkinter.CENTER (prozatím však není k dispozici možnost zarovnat text do bloku).
Dále je možné zvýraznit okraj okolo textu (funguje korektně i pro víceřádkový text, teoreticky se tak dají vytvořit jednodušší tabulky). Tento okraj může být opticky buď vysunutý nebo zasunutý (tkinter.RAISED, tkinter.SUNKEN).
Horní a dolní indexy se tvoří pomocí změny hodnoty vlastnosti offset, pomocí níž je možné text vertikálně posunout vůči textové osnově (kromě toho je vhodné text zmenšit o jeden až dva body, což je však již ponecháno na programátoru). Nastavení všech těchto vlastností je patrné z dalšího demonstračního příkladu, který rozšiřuje příklad předchozí:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys def exit(): sys.exit(0) root = tkinter.Tk() text = tkinter.Text(root, font="Helvetica 14", wrap=tkinter.WORD, width=40, height=20) text.tag_configure("underlined_red", foreground="red", underline=True) text.tag_configure("big_green", foreground="green", font="Helvetica 40") text.tag_configure("blue", foreground="blue") text.tag_configure("magenta", foreground="magenta") text.tag_configure("cyan", foreground="cyan") text.tag_configure("small_yellow", foreground="yellow", font="Helvetica 10") text.tag_configure("brown", foreground="brown") text.tag_configure("pink", foreground="pink") text.tag_configure("white", foreground="white", font="Courier 20") text.tag_configure("raised", relief=tkinter.RAISED, borderwidth=2) text.tag_configure("sunken", relief=tkinter.SUNKEN, borderwidth=2) text.tag_configure("center", justify=tkinter.CENTER) text.tag_configure("left", justify=tkinter.LEFT) text.tag_configure("right", justify=tkinter.RIGHT) text.tag_configure("sup", offset="3p") text.tag_configure("sub", offset="-3p") # práce s widgetem text.insert(tkinter.END, "Underlined Red\n", "underlined_red") text.insert(tkinter.END, "Magenta\n", "magenta") text.insert(tkinter.END, "Blue\n", "blue") text.insert(tkinter.END, "Cyan\n", "cyan") text.insert(tkinter.END, "Big Green\n", "big_green") text.insert(tkinter.END, "Small Yellow\n", "small_yellow") text.insert(tkinter.END, "Brown\n", "brown") text.insert(tkinter.END, "Pink\n", "pink") text.insert(tkinter.END, "White\n", "white") text.insert(tkinter.END, "Raised box\n", "raised") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Sunken box\n", "sunken") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Centered text\n", "center") text.insert(tkinter.END, "Left justificiation\n", "left") text.insert(tkinter.END, "Right justificiation\n", "right") text.insert(tkinter.END, "H") text.insert(tkinter.END, "2", "sub") text.insert(tkinter.END, "O\n") text.insert(tkinter.END, "E=mc") text.insert(tkinter.END, "2", "sup") button = tkinter.Button(root, text="Close window", command=exit) text.pack() button.pack() root.mainloop()
Poznámka: stále se jedná o editovatelný text!
Obrázek 7: Různé styly textu, včetně horních a dolních indexů.
7. Automatické zvýraznění textu, nad nímž se nachází kurzor myši
Každý objekt vkládaný do widgetu Text (ať se jedná o textový řetězec, obrázek či o jiný widget), může reagovat na různé události. Vazba objektu na události se vytvoří pomocí příkazu tag_bind při konfiguraci tagu, který je či může být objektům přiřazen:
text.tag_bind(jméno_tagu, jméno_události, volaný_kód)
Mezi podporované události patří stisk či puštění libovolného tlačítka myši, posun kurzoru myši, přesun kurzoru myši nad objekt či opuštění hranic objektu atd. V dnešním šestém demonstračním příkazu je ukázáno, jak lze nakonfigurovat tag pojmenovaný colorOnEnter pomocí vazeb na dvě události. První událost nastane v případě, že je nad hranice objektu přesunut kurzor myši, druhá událost nastane ve chvíli opuštění hranic objektu kurzorem myši. Tyto události se nazývají Any-Enter a Any-Leave:
text.tag_bind("colorOnEnter", "<Any-Enter>", lambda e: text.tag_configure("colorOnEnter", background="red")) text.tag_bind("colorOnEnter", "<Any-Leave>", lambda e: text.tag_configure("colorOnEnter", background="")) text.insert(tkinter.END, "active-text", "colorOnEnter")
Obrázek 8: Aktivní text není zvýrazněn.
Obrázek 9: Zvýraznění pozadí „aktivního“ řetězce ve widgetu Text.
Následuje výpis zdrojového kódu šestého demonstračního příkladu:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys def exit(): sys.exit(0) root = tkinter.Tk() text = tkinter.Text(root, font="Helvetica 14", wrap=tkinter.WORD, width=40, height=24) text.tag_configure("underlined_red", foreground="red", underline=True) text.tag_configure("big_green", foreground="green", font="Helvetica 40") text.tag_configure("blue", foreground="blue") text.tag_configure("magenta", foreground="magenta") text.tag_configure("cyan", foreground="cyan") text.tag_configure("small_yellow", foreground="yellow", font="Helvetica 10") text.tag_configure("brown", foreground="brown") text.tag_configure("pink", foreground="pink") text.tag_configure("white", foreground="white", font="Courier 20") text.tag_configure("raised", relief=tkinter.RAISED, borderwidth=2) text.tag_configure("sunken", relief=tkinter.SUNKEN, borderwidth=2) text.tag_configure("center", justify=tkinter.CENTER) text.tag_configure("left", justify=tkinter.LEFT) text.tag_configure("right", justify=tkinter.RIGHT) text.tag_configure("sup", offset="3p") text.tag_configure("sub", offset="-3p") # chovani pri prejeti kurzorem mysi pres text text.tag_bind("colorOnEnter", "<Any-Enter>", lambda e: text.tag_configure("colorOnEnter", background="red")) text.tag_bind("colorOnEnter", "<Any-Leave>", lambda e: text.tag_configure("colorOnEnter", background="")) # práce s widgetem text.insert(tkinter.END, "Underlined Red\n", "underlined_red") text.insert(tkinter.END, "Magenta\n", "magenta") text.insert(tkinter.END, "Blue\n", "blue") text.insert(tkinter.END, "Cyan\n", "cyan") text.insert(tkinter.END, "Big Green\n", "big_green") text.insert(tkinter.END, "active-text", "colorOnEnter") text.insert(tkinter.END, "Small Yellow\n", "small_yellow") text.insert(tkinter.END, "Brown\n", "brown") text.insert(tkinter.END, "Pink\n", "pink") text.insert(tkinter.END, "White\n", "white") text.insert(tkinter.END, "Raised box\n", "raised") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Sunken box\n", "sunken") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Centered text\n", "center") text.insert(tkinter.END, "Left justificiation\n", "left") text.insert(tkinter.END, "Right justificiation\n", "right") text.insert(tkinter.END, "H") text.insert(tkinter.END, "2", "sub") text.insert(tkinter.END, "O\n") text.insert(tkinter.END, "E=mc") text.insert(tkinter.END, "2\n", "sup") text.insert(tkinter.END, "active-text", "colorOnEnter") button = tkinter.Button(root, text="Close window", command=exit) text.pack() button.pack() root.mainloop()
8. Reakce na stisk vybraného tlačítka (tlačítek) myši nad částí textu
Nepatrným rozšířením zdrojového kódu popsaného v předchozí kapitole dosáhneme toho, že vybraný text či texty (s přiřazeným tagem colorOnEnter) budou reagovat na stisk tlačítek myši. Připomeňme si, že tlačítka myši jsou v knihovně Tkinter pojmenována takto:
Tlačítko | Pojmenování události |
---|---|
levé | <Button-1> |
prostřední | <Button-2> |
pravé | <Button-3> |
Registrace handlerů událostí pro stisk levého a pravého tlačítka tedy může vypadat následovně:
text.tag_bind("colorOnEnter", "<Button-1>", lambda e: text.tag_configure("colorOnEnter", foreground="blue")) text.tag_bind("colorOnEnter", "<Button-3>", lambda e: text.tag_configure("colorOnEnter", foreground="black"))
Obrázek 10: Stisk levého tlačítka myši změní barvu textu na modrou.
Obrázek 11: Stisk pravého tlačítka myši změní barvu textu zpět na černou.
Opět si ukažme celý příklad ve spustitelné formě:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys def exit(): sys.exit(0) root = tkinter.Tk() text = tkinter.Text(root, font="Helvetica 14", wrap=tkinter.WORD, width=40, height=24) text.tag_configure("underlined_red", foreground="red", underline=True) text.tag_configure("big_green", foreground="green", font="Helvetica 40") text.tag_configure("blue", foreground="blue") text.tag_configure("magenta", foreground="magenta") text.tag_configure("cyan", foreground="cyan") text.tag_configure("small_yellow", foreground="yellow", font="Helvetica 10") text.tag_configure("brown", foreground="brown") text.tag_configure("pink", foreground="pink") text.tag_configure("white", foreground="white", font="Courier 20") text.tag_configure("raised", relief=tkinter.RAISED, borderwidth=2) text.tag_configure("sunken", relief=tkinter.SUNKEN, borderwidth=2) text.tag_configure("center", justify=tkinter.CENTER) text.tag_configure("left", justify=tkinter.LEFT) text.tag_configure("right", justify=tkinter.RIGHT) text.tag_configure("sup", offset="3p") text.tag_configure("sub", offset="-3p") # chovani pri prejeti kurzorem mysi text.tag_bind("colorOnEnter", "<Any-Enter>", lambda e: text.tag_configure("colorOnEnter", background="red")) text.tag_bind("colorOnEnter", "<Any-Leave>", lambda e: text.tag_configure("colorOnEnter", background="")) # chovani pri stisku leveho a praveho tlacitka text.tag_bind("colorOnEnter", "<Button-1>", lambda e: text.tag_configure("colorOnEnter", foreground="blue")) text.tag_bind("colorOnEnter", "<Button-3>", lambda e: text.tag_configure("colorOnEnter", foreground="black")) # práce s widgetem text.insert(tkinter.END, "Underlined Red\n", "underlined_red") text.insert(tkinter.END, "Magenta\n", "magenta") text.insert(tkinter.END, "Blue\n", "blue") text.insert(tkinter.END, "Cyan\n", "cyan") text.insert(tkinter.END, "Big Green\n", "big_green") text.insert(tkinter.END, "active-text", "colorOnEnter") text.insert(tkinter.END, "Small Yellow\n", "small_yellow") text.insert(tkinter.END, "Brown\n", "brown") text.insert(tkinter.END, "Pink\n", "pink") text.insert(tkinter.END, "White\n", "white") text.insert(tkinter.END, "Raised box\n", "raised") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Sunken box\n", "sunken") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Centered text\n", "center") text.insert(tkinter.END, "Left justificiation\n", "left") text.insert(tkinter.END, "Right justificiation\n", "right") text.insert(tkinter.END, "H") text.insert(tkinter.END, "2", "sub") text.insert(tkinter.END, "O\n") text.insert(tkinter.END, "E=mc") text.insert(tkinter.END, "2\n", "sup") text.insert(tkinter.END, "active-text", "colorOnEnter") button = tkinter.Button(root, text="Close window", command=exit) text.pack() button.pack() root.mainloop()
Obrázek 12: Zvýraznění textu po najetí kurzorem myši.
9. Další widgety vložené do widgetu Text
Do widgetu Text je možné kromě textových řetězců vkládat i další objekty a widgety. Všechny tyto objekty se vytváří pomocí oken vkládaných do textu. Na dalších dvou programových řádcích je ukázáno, jak se do textu vkládá widget typu button:
text = tkinter.Text(root, font="Helvetica 14", wrap=tkinter.WORD, width=40, height=24) text.insert(tkinter.END, "Brown\n", "brown") button = tkinter.Button(root, text="Exit", command=exit) text.window_create(tkinter.END, window=button) text.insert(tkinter.END, "Pink\n", "pink")
Velmi flexibilní (ale v současné verzi Tkinteru stále problematické) je vložení plátna (canvasu) do textu nebo práce s obrázky – touto problematikou, která se často používá například při tvorbě prohlížečů dokumentace nebo nápovědy, se budeme zabývat v navazujících kapitolách.
10. Přidání dalších widgetů (tlačítka, checkboxu…) do textu
Pro zajímavost si ukažme, jakým způsobem se do widgetu Text vloží další ovládací prvky, zejména tlačítka, checkboxy a přepínače (radio buttony):
Obrázek 13: Další widgety vložené do textu.
Zdrojový kód příkladu:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys def exit(): sys.exit(0) root = tkinter.Tk() text = tkinter.Text(root, font="Helvetica 14", wrap=tkinter.WORD, width=40, height=24) text.tag_configure("underlined_red", foreground="red", underline=True) text.tag_configure("big_green", foreground="green", font="Helvetica 40") text.tag_configure("blue", foreground="blue") text.tag_configure("magenta", foreground="magenta") text.tag_configure("cyan", foreground="cyan") text.tag_configure("small_yellow", foreground="yellow", font="Helvetica 10") text.tag_configure("brown", foreground="brown") text.tag_configure("pink", foreground="pink") text.tag_configure("white", foreground="white", font="Courier 20") text.tag_configure("raised", relief=tkinter.RAISED, borderwidth=2) text.tag_configure("sunken", relief=tkinter.SUNKEN, borderwidth=2) text.tag_configure("center", justify=tkinter.CENTER) text.tag_configure("left", justify=tkinter.LEFT) text.tag_configure("right", justify=tkinter.RIGHT) text.tag_configure("sup", offset="3p") text.tag_configure("sub", offset="-3p") # chovani pri prejeti kurzorem mysi text.tag_bind("colorOnEnter", "<Any-Enter>", lambda e: text.tag_configure("colorOnEnter", background="red")) text.tag_bind("colorOnEnter", "<Any-Leave>", lambda e: text.tag_configure("colorOnEnter", background="")) # chovani pri stisku leveho a praveho tlacitka text.tag_bind("colorOnEnter", "<Button-1>", lambda e: text.tag_configure("colorOnEnter", foreground="blue")) text.tag_bind("colorOnEnter", "<Button-3>", lambda e: text.tag_configure("colorOnEnter", foreground="black")) checkbutton = tkinter.Checkbutton(root, text="Delete Internet?", command=lambda: print("changed")) quitButton = tkinter.Button(root, text="Exit", command=exit) # práce s widgetem text.insert(tkinter.END, "Underlined Red\n", "underlined_red") text.insert(tkinter.END, "Magenta\n", "magenta") text.insert(tkinter.END, "Blue\n", "blue") text.window_create(tkinter.END, window=checkbutton) text.insert(tkinter.END, "\nCyan\n", "cyan") text.insert(tkinter.END, "Big Green\n", "big_green") text.insert(tkinter.END, "active-text", "colorOnEnter") text.insert(tkinter.END, "Small Yellow\n", "small_yellow") text.insert(tkinter.END, "Brown\n", "brown") text.window_create(tkinter.END, window=quitButton) text.insert(tkinter.END, "Pink\n", "pink") text.insert(tkinter.END, "White\n", "white") text.insert(tkinter.END, "Raised box\n", "raised") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Sunken box\n", "sunken") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Centered text\n", "center") text.insert(tkinter.END, "Left justificiation\n", "left") text.insert(tkinter.END, "Right justificiation\n", "right") text.insert(tkinter.END, "H") text.insert(tkinter.END, "2", "sub") text.insert(tkinter.END, "O\n") text.insert(tkinter.END, "E=mc") text.insert(tkinter.END, "2\n", "sup") text.insert(tkinter.END, "active-text", "colorOnEnter") radio_var = tkinter.StringVar() radio1 = tkinter.Radiobutton(root, variable=radio_var, value="Assembler", text="Assembler") radio2 = tkinter.Radiobutton(root, variable=radio_var, value="Basic", text="Basic") radio3 = tkinter.Radiobutton(root, variable=radio_var, value="Brainfuck", text="Brainfuck") radio4 = tkinter.Radiobutton(root, variable=radio_var, value="C", text="C") radio5 = tkinter.Radiobutton(root, variable=radio_var, value="Python", text="Python") text.insert(tkinter.END, "\nMaly vyber:\n") text.window_create(tkinter.END, window=radio1) text.window_create(tkinter.END, window=radio2) text.window_create(tkinter.END, window=radio3) text.window_create(tkinter.END, window=radio4) text.window_create(tkinter.END, window=radio5) button = tkinter.Button(root, text="Close window", command=exit) text.pack() button.pack() root.mainloop()
11. Modul ScrolledText/tkinter.scrolledtext
K widgetu Text je samozřejmě možné připojit další widget představující scrollbar, ovšem konfigurace není v tomto případě příliš příjemná záležitost, neboť je nutné oba widgety správně umístit (stejná výška, variabilní šířka textu) a navíc i navázat scrollbar na widget Text (reakce na posun scrollbaru) a zajistit i zpětnou vazbu (posun kurzoru v textu nesmí vést k tomu, aby kurzor z okna zmizel). Aby se těmto zbytečným komplikacím zabránilo, byl vytvořen nový (doplňkový) „superwidget“ nazvaný ScrollText či scrolltext, podle toho, jakou verzi Pythonu aplikace bude používat. Už z odlišného pojmenování je patrné, že se nejedná o součást původní standardní sady widgetů knihovny Tkinter:
Obrázek 14: Doplňkový widget ScrollText.
12. Příklad použití modulu ScrolledText – varianta pro Python 2.x
Ve variantě aplikace používající widget ScrolledText, která je psaná pro Python 2.x, je zapotřebí provést následující kroky:
Import modulu (liší se od Pythonu 3.x!):
from ScrolledText import *
Konstrukce widgetu (jeho jméno se opět od Pythonu 3.x odlišuje):
text = ScrolledText(root, font="Helvetica 14", wrap=tkinter.WORD, width=40, height=24)
Úplný zdrojový text příkladu:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys from ScrolledText import * def exit(): sys.exit(0) root = tkinter.Tk() text = ScrolledText(root, font="Helvetica 14", wrap=tkinter.WORD, width=40, height=24) text.tag_configure("underlined_red", foreground="red", underline=True) text.tag_configure("big_green", foreground="green", font="Helvetica 40") text.tag_configure("blue", foreground="blue") text.tag_configure("magenta", foreground="magenta") text.tag_configure("cyan", foreground="cyan") text.tag_configure("small_yellow", foreground="yellow", font="Helvetica 10") text.tag_configure("brown", foreground="brown") text.tag_configure("pink", foreground="pink") text.tag_configure("white", foreground="white", font="Courier 20") text.tag_configure("raised", relief=tkinter.RAISED, borderwidth=2) text.tag_configure("sunken", relief=tkinter.SUNKEN, borderwidth=2) text.tag_configure("center", justify=tkinter.CENTER) text.tag_configure("left", justify=tkinter.LEFT) text.tag_configure("right", justify=tkinter.RIGHT) text.tag_configure("sup", offset="3p") text.tag_configure("sub", offset="-3p") # chovani pri prejeti kurzorem mysi text.tag_bind("colorOnEnter", "<Any-Enter>", lambda e: text.tag_configure("colorOnEnter", background="red")) text.tag_bind("colorOnEnter", "<Any-Leave>", lambda e: text.tag_configure("colorOnEnter", background="")) # chovani pri stisku leveho a praveho tlacitka text.tag_bind("colorOnEnter", "<Button-1>", lambda e: text.tag_configure("colorOnEnter", foreground="blue")) text.tag_bind("colorOnEnter", "<Button-3>", lambda e: text.tag_configure("colorOnEnter", foreground="black")) def on_checkbutton(): print "Changed" checkbutton = tkinter.Checkbutton(root, text="Delete Internet?", command=on_checkbutton) quitButton = tkinter.Button(root, text="Exit", command=exit) # prace s widgetem text.insert(tkinter.END, "Underlined Red\n", "underlined_red") text.insert(tkinter.END, "Magenta\n", "magenta") text.insert(tkinter.END, "Blue\n", "blue") text.window_create(tkinter.END, window=checkbutton) text.insert(tkinter.END, "\nCyan\n", "cyan") text.insert(tkinter.END, "Big Green\n", "big_green") text.insert(tkinter.END, "active-text", "colorOnEnter") text.insert(tkinter.END, "Small Yellow\n", "small_yellow") text.insert(tkinter.END, "Brown\n", "brown") text.window_create(tkinter.END, window=quitButton) text.insert(tkinter.END, "Pink\n", "pink") text.insert(tkinter.END, "White\n", "white") text.insert(tkinter.END, "Raised box\n", "raised") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Sunken box\n", "sunken") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Centered text\n", "center") text.insert(tkinter.END, "Left justificiation\n", "left") text.insert(tkinter.END, "Right justificiation\n", "right") text.insert(tkinter.END, "H") text.insert(tkinter.END, "2", "sub") text.insert(tkinter.END, "O\n") text.insert(tkinter.END, "E=mc") text.insert(tkinter.END, "2\n", "sup") text.insert(tkinter.END, "active-text", "colorOnEnter") radio_var = tkinter.StringVar() radio1 = tkinter.Radiobutton(root, variable=radio_var, value="Assembler", text="Assembler") radio2 = tkinter.Radiobutton(root, variable=radio_var, value="Basic", text="Basic") radio3 = tkinter.Radiobutton(root, variable=radio_var, value="Brainfuck", text="Brainfuck") radio4 = tkinter.Radiobutton(root, variable=radio_var, value="C", text="C") radio5 = tkinter.Radiobutton(root, variable=radio_var, value="Python", text="Python") text.insert(tkinter.END, "\nMaly vyber:\n") text.window_create(tkinter.END, window=radio1) text.window_create(tkinter.END, window=radio2) text.window_create(tkinter.END, window=radio3) text.window_create(tkinter.END, window=radio4) text.window_create(tkinter.END, window=radio5) button = tkinter.Button(root, text="Close window", command=exit) text.pack() button.pack() root.mainloop()
13. Příklad použití modulu tkinter.scrolledtext – varianta pro Python 3.x
Verze příkladu s widgetem tkinter.scrolledtext pro Python 3 má oproti předchozí variantě tyto rozdíly.
Odlišný modul pro import:
from tkinter.scrolledtext import *
Jiný konstruktor:
text = ScrolledText(root, font="Helvetica 14", wrap=tkinter.WORD, width=40, height=24)
Zjednodušenou reakci (handler) na událost:
checkbutton = tkinter.Checkbutton(root, text="Delete Internet?", command=lambda: print("changed"))
Celý příklad vypadá takto:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys from tkinter.scrolledtext import * def exit(): sys.exit(0) root = tkinter.Tk() text = ScrolledText(root, font="Helvetica 14", wrap=tkinter.WORD, width=40, height=24) text.tag_configure("underlined_red", foreground="red", underline=True) text.tag_configure("big_green", foreground="green", font="Helvetica 40") text.tag_configure("blue", foreground="blue") text.tag_configure("magenta", foreground="magenta") text.tag_configure("cyan", foreground="cyan") text.tag_configure("small_yellow", foreground="yellow", font="Helvetica 10") text.tag_configure("brown", foreground="brown") text.tag_configure("pink", foreground="pink") text.tag_configure("white", foreground="white", font="Courier 20") text.tag_configure("raised", relief=tkinter.RAISED, borderwidth=2) text.tag_configure("sunken", relief=tkinter.SUNKEN, borderwidth=2) text.tag_configure("center", justify=tkinter.CENTER) text.tag_configure("left", justify=tkinter.LEFT) text.tag_configure("right", justify=tkinter.RIGHT) text.tag_configure("sup", offset="3p") text.tag_configure("sub", offset="-3p") # chovani pri prejeti kurzorem mysi text.tag_bind("colorOnEnter", "<Any-Enter>", lambda e: text.tag_configure("colorOnEnter", background="red")) text.tag_bind("colorOnEnter", "<Any-Leave>", lambda e: text.tag_configure("colorOnEnter", background="")) # chovani pri stisku leveho a praveho tlacitka text.tag_bind("colorOnEnter", "<Button-1>", lambda e: text.tag_configure("colorOnEnter", foreground="blue")) text.tag_bind("colorOnEnter", "<Button-3>", lambda e: text.tag_configure("colorOnEnter", foreground="black")) checkbutton = tkinter.Checkbutton(root, text="Delete Internet?", command=lambda: print("changed")) quitButton = tkinter.Button(root, text="Exit", command=exit) # práce s widgetem text.insert(tkinter.END, "Underlined Red\n", "underlined_red") text.insert(tkinter.END, "Magenta\n", "magenta") text.insert(tkinter.END, "Blue\n", "blue") text.window_create(tkinter.END, window=checkbutton) text.insert(tkinter.END, "\nCyan\n", "cyan") text.insert(tkinter.END, "Big Green\n", "big_green") text.insert(tkinter.END, "active-text", "colorOnEnter") text.insert(tkinter.END, "Small Yellow\n", "small_yellow") text.insert(tkinter.END, "Brown\n", "brown") text.window_create(tkinter.END, window=quitButton) text.insert(tkinter.END, "Pink\n", "pink") text.insert(tkinter.END, "White\n", "white") text.insert(tkinter.END, "Raised box\n", "raised") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Sunken box\n", "sunken") text.insert(tkinter.END, "\n") text.insert(tkinter.END, "Centered text\n", "center") text.insert(tkinter.END, "Left justificiation\n", "left") text.insert(tkinter.END, "Right justificiation\n", "right") text.insert(tkinter.END, "H") text.insert(tkinter.END, "2", "sub") text.insert(tkinter.END, "O\n") text.insert(tkinter.END, "E=mc") text.insert(tkinter.END, "2\n", "sup") text.insert(tkinter.END, "active-text", "colorOnEnter") radio_var = tkinter.StringVar() radio1 = tkinter.Radiobutton(root, variable=radio_var, value="Assembler", text="Assembler") radio2 = tkinter.Radiobutton(root, variable=radio_var, value="Basic", text="Basic") radio3 = tkinter.Radiobutton(root, variable=radio_var, value="Brainfuck", text="Brainfuck") radio4 = tkinter.Radiobutton(root, variable=radio_var, value="C", text="C") radio5 = tkinter.Radiobutton(root, variable=radio_var, value="Python", text="Python") text.insert(tkinter.END, "\nMaly vyber:\n") text.window_create(tkinter.END, window=radio1) text.window_create(tkinter.END, window=radio2) text.window_create(tkinter.END, window=radio3) text.window_create(tkinter.END, window=radio4) text.window_create(tkinter.END, window=radio5) button = tkinter.Button(root, text="Close window", command=exit) text.pack() button.pack() root.mainloop()
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:
Poznámka: poslední soubor test.xbm je bitmapou použitou v některých demonstračních příkladech. Tato bitmapa musí být umístěna ve stejném adresáři jako všechny příklady.
Obrázek 15: V poslední části článku o knihovně Tkinter se budeme zabývat mj. i problematikou tvorby dialogů.
15. Odkazy na Internetu
- Hra Breakout napísaná v Tkinteri
https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/ - Hra Snake naprogramovaná v Pythone s pomocou Tkinter
https://www.root.cz/clanky/hra-snake-naprogramovana-v-pythone-s-pomocou-tkinter/ - Python Tkinter Fonts
https://www.tutorialspoint.com/python/tk_fonts.htm - The Tkinter Canvas Widget
http://effbot.org/tkinterbook/canvas.htm - Ovládací prvek (Wikipedia)
https://cs.wikipedia.org/wiki/Ovl%C3%A1dac%C3%AD_prvek_%28po%C4%8D%C3%ADta%C4%8D%29 - Rezervovaná klíčová slova v Pythonu
https://docs.python.org/3/reference/lexical_analysis.html#keywords - TkDocs: Styles and Themes
http://www.tkdocs.com/tutorial/styles.html - Drawing in Tkinter
http://zetcode.com/gui/tkinter/drawing/ - Changing ttk widget text color (StackOverflow)
https://stackoverflow.com/questions/16240477/changing-ttk-widget-text-color - The Hitchhiker's Guide to Pyhton: GUI Applications
http://docs.python-guide.org/en/latest/scenarios/gui/ - 7 Top Python GUI Frameworks for 2017
http://insights.dice.com/2014/11/26/5-top-python-guis-for-2015/ - GUI Programming in Python
https://wiki.python.org/moin/GuiProgramming - Cameron Laird's personal notes on Python GUIs
http://phaseit.net/claird/comp.lang.python/python_GUI.html - Python GUI development
http://pythoncentral.io/introduction-python-gui-development/ - Graphic User Interface FAQ
https://docs.python.org/2/faq/gui.html#graphic-user-interface-faq - TkInter
https://wiki.python.org/moin/TkInter - Tkinter 8.5 reference: a GUI for Python
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/index.html - TkInter (Wikipedia)
https://en.wikipedia.org/wiki/Tkinter - appJar
http://appjar.info/ - appJar (Wikipedia)
https://en.wikipedia.org/wiki/AppJar - appJar na Pythonhosted
http://pythonhosted.org/appJar/ - Stránky projektu PyGTK
http://www.pygtk.org/ - PyGTK (Wikipedia)
https://cs.wikipedia.org/wiki/PyGTK - Stránky projektu PyGObject
https://wiki.gnome.org/Projects/PyGObject - Stránky projektu Kivy
https://kivy.org/#home - Stránky projektu PyQt
https://riverbankcomputing.com/software/pyqt/intro - PyQt (Wikipedia)
https://cs.wikipedia.org/wiki/PyGTK - Stránky projektu PySide
https://wiki.qt.io/PySide - PySide (Wikipedia)
https://en.wikipedia.org/wiki/PySide - Stránky projektu Kivy
https://kivy.org/#home - Kivy (framework, Wikipedia)
https://en.wikipedia.org/wiki/Kivy_(framework) - QML Applications
http://doc.qt.io/qt-5/qmlapplications.html - KDE
https://www.kde.org/ - Qt
https://www.qt.io/ - GNOME
https://en.wikipedia.org/wiki/GNOME - Category:Software that uses PyGTK
https://en.wikipedia.org/wiki/Category:Software_that_uses_PyGTK - Category:Software that uses PyGObject
https://en.wikipedia.org/wiki/Category:Software_that_uses_PyGObject - Category:Software that uses wxWidgets
https://en.wikipedia.org/wiki/Category:Software_that_uses_wxWidgets - GIO
https://developer.gnome.org/gio/stable/ - GStreamer
https://gstreamer.freedesktop.org/ - GStreamer (Wikipedia)
https://en.wikipedia.org/wiki/GStreamer - Wax Gui Toolkit
https://wiki.python.org/moin/Wax - Python Imaging Library (PIL)
http://infohost.nmt.edu/tcc/help/pubs/pil/ - 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/