Hlavní navigace

Grafické uživatelské rozhraní v Pythonu: kouzla s kreslicí plochou (dokončení)

12. 9. 2017
Doba čtení: 21 minut

Sdílet

Popis kreslicí plochy (canvasu) v knihovně Tkinter dnes dokončíme. Popíšeme si práci s rastrovými obrázky i s jednořádkovými a víceřádkovými řetězci. Pak se budeme zabývat nejsložitějším widgetem knihovny Tkinter, který se jmenuje Text.

Obsah

1. Rastrové obrázky typu BitmapImage

2. Rastrové obrázky typu PhotoImage

3. Bitmapy (BitmapImage) – dvoubarevné obrázky

4. Změna barvy popředí a pozadí u bitmap

5. Pixmapy (PhotoImage) – vícebarevné obrázky

6. Text na canvasu

7. Vertikální výpis textu na canvas

8. Rotace textu

9. Maximální šířka textu

10. Widget Text

11. Příklad použití widgetu Text

12. Nastavení stylů widgetu Text

13. Použití tagů ve widgetu Text pro selektivní nastavení stylu vybraného textu

14. Složitější příklady se styly textu

15. Repositář s demonstračními příklady

16. Odkazy na Internetu

1. Rastrové obrázky typu BitmapImage

Objekty typu image slouží k vytváření a manipulaci s rastrovými obrázky a to jak bitmapami (v kontextu knihovny Tkinter jednobarevnými či dvoubarevnými obrázky), tak i takzvanými pixmapami (obecně vícebarevnými obrázky, ať již s barvovou paletou či bez palety).

Poznámka: rozlišení mezi bitmapami a obrázky s větším množstvím barev do určité míry souvisí s historickým vývojem programovacího jazyka TCL a knihovny Tk. Dnes je již použití klasických bitmap do značné míry omezeno na specifikaci výběrových masek či pro specifický pixel art [1] [2] [3].

Objekt typu BitmapImage slouží k manipulaci s dvoubarevnými rastrovými obrázky, u kterých je každý pixel reprezentován pouze jedním bitem – může tedy reprezentovat výběr ze dvou hodnot. Výhodou bitmap je jejich takřka absolutní přenositelnost mezi různými platformami s grafickým uživatelským rozhraním, protože alespoň dvě barvy dokáže zobrazit každé grafické zařízení (dívat se na jednobarevnou plochu je trošku nudné :-). V pojetí knihovny Tkinter však mohou být bitmapy komplikovanější, protože je podporována i transparentnost. Ta je řešena pomocí dvojice bitmap, přičemž první bitmapa obsahuje vlastní obrázek a druhá bitmapa bitovou masku, kterou se specifikuje, které pixely mají být vykresleny a u kterých se má ponechat původní hodnota.

Obrázek 1: BitmapImage, tj. dvoubarevný rastrový obrázek.

2. Rastrové obrázky typu PhotoImage

Objekt typu PhotoImage slouží k manipulaci s vícebarevnými obrázky. Způsob zobrazení těchto objektů se může na různých systémech lišit, zejména je nutné brát do úvahy barevnou hloubku zobrazení (tj. počet bitů na jeden pixel); dnes již však prakticky každé moderní zařízení podporuje zobrazení plnobarevných obrázků. Implicitně se objekt typu PhotoImage vytváří při běhu aplikace tak, aby co nejvíce odpovídal nastavení konkrétního systému, toto chování je však možné změnit a například vždy pracovat s osmibitovou barevnou hloubkou (vhodné pro obrázky s paletou). Mimo nastavování a čtení barev jednotlivých pixelů je umožněno, aby se obrázek načetl či uložil do externího souboru.

Obrázek 2: PhotoImage, tj. rastrový obrázek s barvovou paletou nebo plnobarevný obrázek.

Spolu s knihovnou Tkinter jsou dodávány filtry pro práci s formáty PPM (Portable PixelMap), PGM (Portable GrayMap) a GIF (Graphics Interchange Format). Pokud budete vyžadovat použití jiného formátu (což asi budete, minimálně PNG a JPEG), lze pro načtení použít knihovnu PIL neboli Python Imaging Library nebo ještě lépe Pillow, což je fork dnes již pravděpodobně nevyvíjeného PILu.

3. Bitmapy (BitmapImage) – dvoubarevné obrázky

Bitmapu, tedy dvoubarevný obrázek, je možné vytvořit dvěma způsoby. Buď máme přímo v programu data bitmapy:

bitmap = tkinter.BitmapImage(data=data_bitmapy)

Nebo máme k dispozici externí soubor s bitmapou:

bitmap = tkinter.BitmapImage(file="soubor_s_bitmapou.xbm")

Knihovna Tkinter akceptuje formát XBM neboli X-Window Bitmap popř. pouze X BitMap. Tento formát je vlastně tvořen formalizovaně zapsaným fragmentem céčkovského kódu, který může být uložen v externím obrázku nebo přímo v řetězci v programu:

#define test_width 336
#define test_height 240
static unsigned char test_bits[] = {
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...
...
...
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

Bitmapa se vytvoří voláním:

V dalším demonstračním příkladu je načten a následně zobrazen externí dvoubarevný obrázek uložený ve formátu XBM:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
import sys
 
 
WIDTH = 400
HEIGHT = 400
GRID_SIZE = 100
 
 
def exit():
    sys.exit(0)
 
 
def basic_canvas(root, width, height, grid_size):
    canvas = tkinter.Canvas(root, width=width, height=height,
                            background='#ccffcc')
    canvas.pack()
 
    draw_grid(canvas, width, height, grid_size)
    return canvas
 
 
def draw_grid(canvas, width, height, grid_size):
    for x in range(0, width, grid_size):
        canvas.create_line(x, 0, x, height, dash=7, fill="gray")
    for y in range(0, height, grid_size):
        canvas.create_line(0, y, width, y, dash=7, fill="gray")
 
 
root = tkinter.Tk()
 
bitmap_image = tkinter.BitmapImage(file="test.xbm")
 
canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE)
 
canvas.create_image((200, 200), image=bitmap_image)
 
root.mainloop()

Obrázek 3: Zobrazení bitmapy na canvasu.

4. Změna barvy popředí a pozadí u bitmap

Bitmapy nejsou striktně černobílé, protože je u nich možné nastavit barvu popředí a pozadí, jak je to patrné z následujícího úryvku kódu:

bitmap_image = tkinter.BitmapImage(file="test.xbm",
                                   background="black",
                                   foreground="yellow")

Opět se podívejme na zdrojový kód demonstračního příkladu, kde je tato možnost použita:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
import sys
 
 
WIDTH = 400
HEIGHT = 400
GRID_SIZE = 100
 
 
def exit():
    sys.exit(0)
 
 
def basic_canvas(root, width, height, grid_size):
    canvas = tkinter.Canvas(root, width=width, height=height,
                            background='#ccffcc')
    canvas.pack()
 
    draw_grid(canvas, width, height, grid_size)
    return canvas
 
 
def draw_grid(canvas, width, height, grid_size):
    for x in range(0, width, grid_size):
        canvas.create_line(x, 0, x, height, dash=7, fill="gray")
    for y in range(0, height, grid_size):
        canvas.create_line(0, y, width, y, dash=7, fill="gray")
 
 
root = tkinter.Tk()
 
bitmap_image = tkinter.BitmapImage(file="test.xbm",
                                   background="black",
                                   foreground="yellow")
 
canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE)
 
canvas.create_image((200, 200), image=bitmap_image)
 
root.mainloop()

Obrázek 4: Bitmapa na canvasu se specifikovanou barvou popředí a pozadí.

5. Pixmapy (PhotoImage) – vícebarevné obrázky

Vytvoření objektu typu PhotoImage, tj. rastrového obrázku s barvovou paletou popř. plnobarevného obrázku, je snadné:

photo_image = tkinter.PhotoImage(file="demo.gif")

Podívejme se nyní na zdrojový kód příkladu, v němž se načte a zobrazí několik ikon (uložených do souborů typu GIF), které jsme již využili minule při tvorbě menu:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
import sys
 
 
WIDTH = 400
HEIGHT = 400
GRID_SIZE = 100
 
 
def exit():
    sys.exit(0)
 
 
def basic_canvas(root, width, height, grid_size):
    canvas = tkinter.Canvas(root, width=width, height=height,
                            background='#ccffcc')
    canvas.pack()
 
    draw_grid(canvas, width, height, grid_size)
    return canvas
 
 
def draw_grid(canvas, width, height, grid_size):
    for x in range(0, width, grid_size):
        canvas.create_line(x, 0, x, height, dash=7, fill="gray")
    for y in range(0, height, grid_size):
        canvas.create_line(0, y, width, y, dash=7, fill="gray")
 
 
image_names = [
    "document-open",
    "document-save",
    "application-exit",
    "edit-undo",
    "edit-cut",
    "edit-copy",
    "edit-paste",
    "edit-delete",
    "edit-select-all"]
 
root = tkinter.Tk()
 
images = {}
for image_name in image_names:
    images[image_name] = tkinter.PhotoImage(file="icons/%s.gif" % image_name)
 
canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE)
 
canvas.create_image((50, 50), image=images['document-open'])
canvas.create_image((150, 50), image=images['document-save'])
canvas.create_image((250, 50), image=images['application-exit'])
canvas.create_image((50, 150), image=images['edit-undo'])
canvas.create_image((150, 150), image=images['edit-cut'])
canvas.create_image((250, 150), image=images['edit-copy'])
canvas.create_image((50, 250), image=images['edit-paste'])
canvas.create_image((150, 250), image=images['edit-delete'])
canvas.create_image((250, 250), image=images['edit-select-all'])
 
root.mainloop()

Obrázek 5: Pixmapy (plnobarevné obrázky).

6. Text na canvasu

Na plátno lze samozřejmě vkládat i text. Pro tento účel se používá objekt nazvaný jak jinak než text. Způsob vytvoření tohoto objektu je velmi jednoduchý:

canvas.create_text(x, y, další volby)

kde se pomocí hodnot [x, y] zadává souřadnice počátku textu (resp. přesněji řečeno referenční bod). Mezi nejpoužívanější volby patří text (vlastní řetězec, který má být zobrazen), fill (barva textu), font (specifikace fontu) a anchor (způsob umístění textu vůči vkládacímu bodu). Řetězec i další parametry textu lze samozřejmě při běhu aplikace měnit, ale pro uživatelem prováděnou editaci je mnohem výhodnější používat widget text. Ukažme si tedy poněkud složitější příklad:

canvas.create_text(x, y,
                   text="Test",
                   fill="gray",
                   anchor="ne",
                   font="Helvetica 16")

Hodnotu anchor je možné zadat řetězcem nebo předdefinovanou konstantou:

Řetězec Konstanta Význam
„center“ tkinter.CENTER uprostřed textu (horizontálně i vertikálně)
„n“ tkinter.N horní okraj, horizontálně vycentrováno
„s“ tkinter.S spodní okraj, horizontálně vycentrováno
"w"" tkinter.W vertikálně vycentrováno, horizontálně na levém konci textu
„e“ tkinter.E vertikálně vycentrováno, horizontálně na pravém konci textu
„nw“ tkinter.NW horní okraj, horizontálně na levém konci textu
„sw“ tkinter.SW spodní okraj, horizontálně na levém konci textu
„ne“ tkinter.NE horní okraj, horizontálně na pravém konci textu
„se“ tkinter.SE spodní okraj, horizontálně na pravém konci textu

V dalším demonstračním příkladu je ukázáno, jakým způsobem se používá volba anchor pro specifikaci referenčního bodu textu:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
import sys
 
 
WIDTH = 400
HEIGHT = 400
GRID_SIZE = 100
 
 
def exit():
    sys.exit(0)
 
 
def basic_canvas(root, width, height, grid_size):
    canvas = tkinter.Canvas(root, width=width, height=height,
                            background='#ccffcc')
    canvas.pack()
 
    draw_grid(canvas, width, height, grid_size)
    return canvas
 
 
def draw_grid(canvas, width, height, grid_size):
    for x in range(0, width, grid_size):
        canvas.create_line(x, 0, x, height, dash=7, fill="gray")
    for y in range(0, height, grid_size):
        canvas.create_line(0, y, width, y, dash=7, fill="gray")
 
 
def draw_text(canvas, x, y, anchor):
    canvas.create_text(x, y, text="Test", fill="gray", anchor=anchor,
                       font="Helvetica 16")
    canvas.create_line(x-5, y, x+5, y, fill="red")
    canvas.create_line(x, y-5, x, y+5, fill="red")
 
 
root = tkinter.Tk()
 
canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE)
 
draw_text(canvas, 50, 50, "ne")
draw_text(canvas, 150, 50, "n")
draw_text(canvas, 250, 50, "nw")
 
draw_text(canvas, 50, 150, "e")
draw_text(canvas, 150, 150, "center")
draw_text(canvas, 250, 150, "w")
 
draw_text(canvas, 50, 250, "se")
draw_text(canvas, 150, 250, "s")
draw_text(canvas, 250, 250, "sw")
 
root.mainloop()

Obrázek 6: Texty s různě nastavenými kotvicími body.

7. Vertikální výpis textu na canvas

V dalším demonstračním příkladu je ukázán trik pro tvorbu vertikálního textu (což je něco jiného než pouhá rotace, která je ovšem taktéž podporována). Text totiž může obsahovat konce řádků, které jsou správně interpretovány. Pokud tedy vložíme znak pro konec řádku za každé písmeno, získáme vertikální text:

canvas.create_text(x, y,
                   text="\n".join("Test"),
                   fill="gray",
                   anchor=anchor,
                   font="Helvetica 12")

Úplný zdrojový kód takto upraveného demonstračního příkladu vypadá následovně:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
import sys
 
 
WIDTH = 400
HEIGHT = 400
GRID_SIZE = 100
 
 
def exit():
    sys.exit(0)
 
 
def basic_canvas(root, width, height, grid_size):
    canvas = tkinter.Canvas(root, width=width, height=height,
                            background='#ccffcc')
    canvas.pack()
 
    draw_grid(canvas, width, height, grid_size)
    return canvas
 
 
def draw_grid(canvas, width, height, grid_size):
    for x in range(0, width, grid_size):
        canvas.create_line(x, 0, x, height, dash=7, fill="gray")
    for y in range(0, height, grid_size):
        canvas.create_line(0, y, width, y, dash=7, fill="gray")
 
 
def draw_text(canvas, x, y, anchor):
    canvas.create_text(x, y, text="\n".join("Test"),
                       fill="gray", anchor=anchor, font="Helvetica 12")
    canvas.create_line(x-5, y, x+5, y, fill="red")
    canvas.create_line(x, y-5, x, y+5, fill="red")
 
 
root = tkinter.Tk()
 
canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE)
 
draw_text(canvas, 50, 50, "ne")
draw_text(canvas, 150, 50, "n")
draw_text(canvas, 250, 50, "nw")
 
draw_text(canvas, 50, 200, "e")
draw_text(canvas, 150, 200, "center")
draw_text(canvas, 250, 200, "w")
 
draw_text(canvas, 50, 350, "se")
draw_text(canvas, 150, 350, "s")
draw_text(canvas, 250, 350, "sw")
 
root.mainloop()

Obrázek 7: Vertikální texty s různě nastavenými kotvicími body.

8. Rotace textu

Text může být na kreslicí plochu vložen pod různým úhlem, přičemž rotace se specifikuje nepovinným (pojmenovaným) parametrem angle. Rotace textu je zadána ve stupních, nikoli tedy v radiánech, jak je tomu v jiných knihovnách (například i standardní funkce sin akceptuje úhel zadaný v radiánech):

canvas.create_text(x, y,
                   text="Test test test",
                   fill="gray",
                   anchor=anchor,
                   font="Helvetica 12",
                   angle=30)

Podívejme se nyní na příklad, v němž je text vykreslen pod úhly, které jsou celočíselnými násobky 30°:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
import sys
 
 
WIDTH = 400
HEIGHT = 400
GRID_SIZE = 100
 
 
def exit():
    sys.exit(0)
 
 
def basic_canvas(root, width, height, grid_size):
    canvas = tkinter.Canvas(root, width=width, height=height,
                            background='#ccffcc')
    canvas.pack()
 
    draw_grid(canvas, width, height, grid_size)
    return canvas
 
 
def draw_grid(canvas, width, height, grid_size):
    for x in range(0, width, grid_size):
        canvas.create_line(x, 0, x, height, dash=7, fill="gray")
    for y in range(0, height, grid_size):
        canvas.create_line(0, y, width, y, dash=7, fill="gray")
 
 
def draw_text(canvas, x, y, angle):
    canvas.create_text(x, y, text="Test", fill="gray", anchor="center",
                       font="Helvetica 16", angle=angle)
    canvas.create_line(x-5, y, x+5, y, fill="red")
    canvas.create_line(x, y-5, x, y+5, fill="red")
 
 
root = tkinter.Tk()
 
canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE)
 
draw_text(canvas, 50, 50, 0)
draw_text(canvas, 150, 50, 30)
draw_text(canvas, 250, 50, 60)
draw_text(canvas, 350, 50, 90)
 
draw_text(canvas, 50, 150, 120)
draw_text(canvas, 150, 150, 150)
draw_text(canvas, 250, 150, 180)
draw_text(canvas, 350, 150, 210)
 
draw_text(canvas, 50, 250, 240)
draw_text(canvas, 150, 250, 270)
draw_text(canvas, 250, 250, 300)
draw_text(canvas, 350, 250, 330)
 
root.mainloop()

Obrázek 8: Text, který je otočen o 0°, 30°, 60° atd.

9. Maximální šířka textu

V některých případech může být nutné omezit maximální šířku textu. To zajistí nepovinný parametr width. V případě, že je text širší než uvedená hodnota, bude zalomen, a to samozřejmě v místě mezi jednotlivými znaky (nikoli v půlce písmen atd.):

canvas.create_text(x, y,
                   text="Příliš dlouhý text",
                   fill="gray",
                   anchor="center",
                   font="Helvetica 16",
                   width=50)

Poznámka: v případě, že preferujete zalomení na koncích slov (což je většinou logický požadavek), může být výhodnější použít widget Text, který je popsán ve druhé části dnešního článku.

Podívejme se nyní na příklad, v němž je šířka textu omezena, a to i ve chvíli, kdy je text vykreslen pod jiným úhlem:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
import sys
 
 
WIDTH = 400
HEIGHT = 400
GRID_SIZE = 100
 
 
def exit():
    sys.exit(0)
 
 
def basic_canvas(root, width, height, grid_size):
    canvas = tkinter.Canvas(root, width=width, height=height,
                            background='#ccffcc')
    canvas.pack()
 
    draw_grid(canvas, width, height, grid_size)
    return canvas
 
 
def draw_grid(canvas, width, height, grid_size):
    for x in range(0, width, grid_size):
        canvas.create_line(x, 0, x, height, dash=7, fill="gray")
    for y in range(0, height, grid_size):
        canvas.create_line(0, y, width, y, dash=7, fill="gray")
 
 
def draw_text(canvas, x, y, angle):
    canvas.create_text(x, y, text="*Python*", fill="gray", anchor="center",
                       font="Helvetica 16", angle=angle, width=50)
    canvas.create_line(x-5, y, x+5, y, fill="red")
    canvas.create_line(x, y-5, x, y+5, fill="red")
 
 
root = tkinter.Tk()
 
canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE)
 
draw_text(canvas, 50, 50, 0)
draw_text(canvas, 150, 50, 30)
draw_text(canvas, 250, 50, 60)
draw_text(canvas, 350, 50, 90)
 
draw_text(canvas, 50, 150, 120)
draw_text(canvas, 150, 150, 150)
draw_text(canvas, 250, 150, 180)
draw_text(canvas, 350, 150, 210)
 
draw_text(canvas, 50, 250, 240)
draw_text(canvas, 150, 250, 270)
draw_text(canvas, 250, 250, 300)
draw_text(canvas, 350, 250, 330)
 
root.mainloop()

Obrázek 9: Text, který je otočen o 0°, 30°, 60° atd. a současně je omezena jeho šířka na 50 pixelů. Povšimněte si, že šířka je vždy chápána ve směru rotace textu a nikoli ve směru horizontální osy.

10. Widget Text

Widget nazvaný Text patří vedle již výše popsaného widgetu canvas k prakticky nejsložitě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.

V tomto ohledu se tedy widget text chová podobně jako například komponenta Rich Edit Control z Windows API, resp. z rozšiřující knihovny RICHED32.DLL (textový editor WordPad není nic jiného než zabalení této komponenty do GUI). Widget Text však při správné konfiguraci dokáže i další věci – do oblasti s textem je možné vkládat další widgety, zejména tlačítka a obrázky (bitmapy i pixmapy), používat hypertextové odkazy apod. Podobný widget byl součástí prohlížeče/editoru Amaya. Na druhou stranu například komponenta Scintilla sice má některé podobné vlastnosti jako widget Text, ale není zde podporována změna stylů ani použití obrázků.

Není divu, že na bázi Tcl/Tk (Tkinter volá funkce Tk) vznikl webový prohlížeč, prohlížeč manuálových stránek a další někdy více a někdy méně užitečné aplikace. Velkou část funkcionality přitom zajistil právě widget Text, na který byly navěšeny procedury zpracovávající jednotlivé události, například kliknutí na některý obrázek atd.

11. Příklad použití widgetu Text

V dalším demonstračním příkladu se widget Text (pojmenovaný pro jednoduchost text) nachází jako jedna ze dvou komponent v okně aplikace (druhou komponentou je tlačítko pro zavření aplikace). Do widgetu Text je s využitím metody insert vložen řetězec. Povšimněte si, že v řetězci je možné používat řídicí znak „\n“ pro ukončení řádku. Zdrojový kód prvního demonstračního příkladu vypadá následovně:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
import sys
 
 
WIDTH = 400
HEIGHT = 400
GRID_SIZE = 100
 
 
def exit():
    sys.exit(0)
 
 
root = tkinter.Tk()
 
text = tkinter.Text(root)
 
text.insert(tkinter.END, "Test widgetu\n'text'")
 
button = tkinter.Button(root, text="Close window", command=exit)
 
text.pack()
button.pack()
 
root.mainloop()

Obrázek 10: Widget Text, do kterého byl nakopírován jeden odstavec z dnešního článku.

12. Nastavení stylů widgetu Text

U widgetu Text je možné nastavit prakticky všechny základní vlastnosti (snad kromě specifikace horizontálního prokládání), zejména pak:

Vlastnost Význam
width šířka widgetu zadaná v počtu znaků (což není u proporcionálního fontu přesné)
height výška widgetu zadaná v počtu řádků (záleží tedy na velikosti fontu)
font použitý font specifikovaný většinou jménem a velikostí (v bodech)
   
foreground barva vlastního textu
background barva pozadí widgetu
selectforeground barva pozadí výběru (bloku)
selectbackground barva pozadí výběru (bloku)
   
insertwidth šířka kurzoru (caretu)
insertbackground barva kurzoru
insertborderwidth šířka okraje kurzoru (nefunguje dobře ve chvíli, kdy systém emuluje kurzor s reliéfem)
   
wrap povolení (True) či zákaz (False) zalamování delších textových řádků

Podívejme se nyní na příklad, v němž jsou výše popsané vlastnosti použity pro vytvoření primitivního textového editoru:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
import sys
 
 
WIDTH = 400
HEIGHT = 400
GRID_SIZE = 100
 
 
def exit():
    sys.exit(0)
 
 
root = tkinter.Tk()
 
text = tkinter.Text(root,
                    font="Helvetica 20",
                    foreground="#0000c0",
                    background="#c0ffc0",
                    selectforeground="white",
                    selectbackground="red",
                    insertwidth=4,
                    insertbackground="red",
                    insertborderwidth=1,
                    wrap=tkinter.WORD,
                    width=40, height=16)
 
text.insert(tkinter.END, "Test widgetu\n'text'")
 
button = tkinter.Button(root, text="Close window", command=exit)
 
text.pack()
button.pack()
 
root.mainloop()

Obrázek 11: Widget Text s upraveným stylem, do kterého byl nakopírován jeden odstavec z dnešního článku.

13. Použití tagů ve widgetu Text pro selektivní nastavení stylu vybraného textu

Pokud potřebujeme u textových řetězců vkládaných do widgetu Text měnit jejich vlastnosti, například barvu či typ a velikost písma, můžeme použít tagy, s nimiž jsme se již setkali u canvasu. Pomocí tagů se, zjednodušeně řečeno, pojmenuje určitá vlastnost nebo skupina vlastností a při vkládání řetězců do widgetu Text pomocí metody insert se tyto vlastnosti řetězcům přiřadí. Velká výhoda tagů spočívá v tom, že jim přidružené vlastnosti je možné za běhu aplikace měnit, přičemž se změny ihned projeví na vizuálním vzhledu textu. Ukažme si další příklad, v němž se pomocí tagů vypíše několik řádků textu, každý řádek je přitom vykreslen jinou barvou (při vytváření tagů se do metody tag_configure vkládá název tagu, nenechte se tedy zmást zdánlivě dvojím zápisem barvy):

text.tag_configure("red", foreground="red")
 
text.insert(tkinter.END, "Red\n", "red")

V příkladu si také všimněte, že se celému widgetu text pomocí parametru background přiřadila černá barva pozadí, aby bylo barevné písmo na obrazovce lépe čitelné:

#!/usr/bin/env python
 
import tkinter
from tkinter import ttk
import sys
 
 
WIDTH = 400
HEIGHT = 400
GRID_SIZE = 100
 
 
def exit():
    sys.exit(0)
 
 
root = tkinter.Tk()
 
text = tkinter.Text(root,
                    font="Helvetica 20",
                    wrap=tkinter.WORD,
                    background="#202020",
                    width=40, height=16)
 
text.tag_configure("red", foreground="red")
text.tag_configure("green", foreground="green")
text.tag_configure("blue", foreground="blue")
text.tag_configure("magenta", foreground="magenta")
text.tag_configure("cyan", foreground="cyan")
text.tag_configure("yellow", foreground="yellow")
text.tag_configure("brown", foreground="brown")
text.tag_configure("pink", foreground="pink")
text.tag_configure("white", foreground="white")
 
# práce s widgetem
text.insert(tkinter.END, "Red\n", "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, "Green\n", "green")
text.insert(tkinter.END, "Yellow\n", "yellow")
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()

Obrázek 12: Použití tagů pro změnu barvy vybrané části textu.

14. Složitější příklady se styly textu

Widget Text umožňuje provádění i mnohých dalších zajímavých a užitečných operací, například:

  1. Vkládání obrázků typu BitmapImage a PhotoImage
  2. Specifikace zarovnání textu, selektivně pro vybrané řádky
  3. Specifikace tabelačních zarážek
  4. Zvýraznění okraje okolo textu s výběrem stylu okraje
  5. Tvorbu horních a dolních indexů pomocí vlastnosti offset
  6. Vkládání hyperlinků
  7. Vložení libovolného dalšího widgetu, například tlačítka

Tyto vlastnosti si podrobněji popíšeme příště.

Obrázek 13: Text s různými styly i s vloženou bitmapou.

Obrázek 14: 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.

root_podpora

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

16. Odkazy na Internetu

  1. Hra Breakout napísaná v Tkinteri
    https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/
  2. Hra Snake naprogramovaná v Pythone s pomocou Tkinter
    https://www.root.cz/clanky/hra-snake-naprogramovana-v-pythone-s-pomocou-tkinter/
  3. The Tkinter Canvas Widget
    http://effbot.org/tkinter­book/canvas.htm
  4. Ovládací prvek (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Ovl%C3%A1dac%C3%AD_prvek_­%28po%C4%8D%C3%ADta%C4%8D%29
  5. Rezervovaná klíčová slova v Pythonu
    https://docs.python.org/3/re­ference/lexical_analysis.html#ke­ywords
  6. TkDocs: Styles and Themes
    http://www.tkdocs.com/tuto­rial/styles.html
  7. Drawing in Tkinter
    http://zetcode.com/gui/tkin­ter/drawing/
  8. Changing ttk widget text color (StackOverflow)
    https://stackoverflow.com/qu­estions/16240477/changing-ttk-widget-text-color
  9. The Hitchhiker's Guide to Pyhton: GUI Applications
    http://docs.python-guide.org/en/latest/scenarios/gui/
  10. 7 Top Python GUI Frameworks for 2017
    http://insights.dice.com/2014/11/26/5-top-python-guis-for-2015/
  11. GUI Programming in Python
    https://wiki.python.org/mo­in/GuiProgramming
  12. Cameron Laird's personal notes on Python GUIs
    http://phaseit.net/claird/com­p.lang.python/python_GUI.html
  13. Python GUI development
    http://pythoncentral.io/introduction-python-gui-development/
  14. Graphic User Interface FAQ
    https://docs.python.org/2/faq/gu­i.html#graphic-user-interface-faq
  15. TkInter
    https://wiki.python.org/moin/TkInter
  16. Tkinter 8.5 reference: a GUI for Python
    http://infohost.nmt.edu/tcc/hel­p/pubs/tkinter/web/index.html
  17. TkInter (Wikipedia)
    https://en.wikipedia.org/wiki/Tkinter
  18. appJar
    http://appjar.info/
  19. appJar (Wikipedia)
    https://en.wikipedia.org/wiki/AppJar
  20. appJar na Pythonhosted
    http://pythonhosted.org/appJar/
  21. Stránky projektu PyGTK
    http://www.pygtk.org/
  22. PyGTK (Wikipedia)
    https://cs.wikipedia.org/wiki/PyGTK
  23. Stránky projektu PyGObject
    https://wiki.gnome.org/Pro­jects/PyGObject
  24. Stránky projektu Kivy
    https://kivy.org/#home
  25. Stránky projektu PyQt
    https://riverbankcomputin­g.com/software/pyqt/intro
  26. PyQt (Wikipedia)
    https://cs.wikipedia.org/wiki/PyGTK
  27. Stránky projektu PySide
    https://wiki.qt.io/PySide
  28. PySide (Wikipedia)
    https://en.wikipedia.org/wiki/PySide
  29. Stránky projektu Kivy
    https://kivy.org/#home
  30. Kivy (framework, Wikipedia)
    https://en.wikipedia.org/wi­ki/Kivy_(framework)
  31. QML Applications
    http://doc.qt.io/qt-5/qmlapplications.html
  32. KDE
    https://www.kde.org/
  33. Qt
    https://www.qt.io/
  34. GNOME
    https://en.wikipedia.org/wiki/GNOME
  35. Category:Software that uses PyGTK
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_PyGTK
  36. Category:Software that uses PyGObject
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_PyGObject
  37. Category:Software that uses wxWidgets
    https://en.wikipedia.org/wi­ki/Category:Software_that_u­ses_wxWidgets
  38. GIO
    https://developer.gnome.or­g/gio/stable/
  39. GStreamer
    https://gstreamer.freedesktop.org/
  40. GStreamer (Wikipedia)
    https://en.wikipedia.org/wi­ki/GStreamer
  41. Wax Gui Toolkit
    https://wiki.python.org/moin/Wax
  42. Python Imaging Library (PIL)
    http://infohost.nmt.edu/tcc/hel­p/pubs/pil/
  43. 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/

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