Hlavní navigace

Programovací jazyk TCL (6)

23. 8. 2005
Doba čtení: 11 minut

Sdílet

V šestém pokračování seriálu o programovacím jazyku Tcl a jeho knihovně Tk se budeme podrobněji zabývat nejpoužívanějšími widgety, které jsou knihovnou Tk uživatelům i programátorům nabízeny. Také si ukážeme způsob rozvržení widgetů v oknech a dialozích pomocí takzvaných správců geometrie.

Obsah

1. Popis vybraných widgetů poskytovaných knihovnou Tk
2. Widget label
3. Widget button
4. Widget checkbutton
5. Widget radiobutton
6. Popis správců geometrie
7. Správce geometrie pack
8. Správce geometrie grid
9. Obsah dalšího pokračování tohoto seriálu

1. Popis vybraných widgetů poskytovaných knihovnou Tk

Widgety jsme si již představili v předchozí části tohoto seriálu. Proto si pouze stručně zopakujeme základní fakta: z pohledu uživatele aplikací s grafickým uživatelským rozhraním se jedná o grafické prvky zobrazené na obrazovce, které mají předem známé chování a předvídatelnou funkci, z pohledu programátora je naproti tomu widget objektem, kterému lze nastavit určitý stav a který reaguje na události generované uživatelem při jeho práci. Widgety se do jisté míry chovají jako objekty známé z OOP – podporují zapouzdření, jsou umístěny v hierarchii objektů, obsahují metody pro nastavování a čtení jejich vlastností atd.

V knihovně Tk je velmi zajímavým způsobem řešeno pojmenování widgetů a jejich umístění do hierarchie tvořené okny a v nich umístěnými kontejnery. Každému widgetu přísluší jedinečný řetězec, který obsahuje jak samotný název widgetu (ten volí uživatel), tak i umístění widgetu v hierarchii. Zápis řetězce začíná znakem tečka (.) – tento znak představuje kořen celé hierarchie a svou funkcí odpovídá kořenu v unixové hierarchii souborů, tj. lomítku (/). Za tečkou následuje jméno objektu na kořenové úrovni; tímto objektem bývá často okno, dialogový box či nějaký kontejner. Za tečkou následuje jméno kontejneru na druhé úrovni hierarchie, další tečka atd. Celý řetězec končí jménem widgetu.

2. Widget label

Jedním z nejjednodušších widgetů je takzvaný label. Jedná se o widget zobrazující v nejjednodušším případě jednořádkový text. V mnoha toolkitech bychom tímto konstatováním mohli popis widgetu ukončit. Ne tak v případě Tk, protože zde může být widget label poněkud složitější. Labely zde totiž mohou zobrazovat víceřádkový text, což je výhodné zejména při výpisu delších zpráv – tyto výpisy se v jiných toolkitech musí provádět například do textových polí. V případě, že se zobrazuje jednořádkový text, je použití widgetu label velmi jednoduché, jak to ostatně ilustruje následující příklad:

#!/usr/bin/wish
label .label1 -text "Hello world!"
pack .label1 

Ve víceřádkovém textu se musí nějakým způsobem specifikovat konce řádků. To se provede tak, že se do řetězce vloží speciální zástupný znak „\n“. Zde je typický příklad použití:

#!/usr/bin/wish
label .label2 -text "Tady se\nnachází\nvíceřádkový\ntext"
pack .label2 

Kromě nastavení textu je možné specifikovat i font, kterým je text vypsán, stejně jako barvu popředí a pozadí:

#!/usr/bin/wish
label .label3 -text "Tady se\nnachází\nvíceřádkový\ntext" -font "Helvetica +14"
label .label4 -text "Hello world" -font "Lucida +36"
label .label5 -text "Výstraha" -background green -foreground red -font "Times +72"
pack .label3
pack .label4
pack .label5 
TCL 6a

Obrázek 1: možnosti nastavení widgetů label

3. Widget button

Dalším základním widgetem je takzvaný button. Jak již název tohoto widgetu napovídá, jedná se o tlačítko, které může uživatel vizuálně zamáčknout, typicky pomocí levého tlačítka myši. K tlačítku lze přímo při jeho vytváření přiřadit implicitní akci, která se provede po jeho stisknutí. Základ práce s tímto widgetem je patrný z následujícího příkladu:

#!/usr/bin/wish
button .button1 -text "Press me"
pack .button1 

Implicitní akce se naprogramuje velmi jednoduše pomocí volby -command:

#!/usr/bin/wish
button .button2 -text "Click to quit" -command {exit}
pack .button2 

Resp. ve zkráceném (a používanějším) tvaru:

#!/usr/bin/wish
pack [button .button2 -text "Click to quit" -command {exit}] 

I u tlačítka lze specifikovat, jak má v okně vypadat. Kromě zadání jména fontu a barvy popředí i pozadí je možné zadat tvar okraje tlačítka. Vyzkoušejte si následující příklad, který ukazuje všechny možnosti tvarů okraje tlačítek:

#!/usr/bin/wish
button .button1 -text "Flat button" -relief flat
button .button2 -text "Groove button" -relief groove
button .button3 -text "Raised button" -relief raised
button .button4 -text "Ridge button" -relief ridge
button .button5 -text "Solid button" -relief solid
button .button6 -text "Sunken button" -relief sunken
pack .button1
pack .button2
pack .button3
pack .button4
pack .button5
pack .button6 
TCL 6b

Obrázek 2: nastavení okrajů tlačítek

K tlačítkům lze přiřadit i reakce na další události (například stlačení druhého či třetího tlačítka myši), ale touto problematikou se budeme zabývat až v další části tohoto seriálu. Všechny vlastnosti tlačítka lze vypsat příkazem:

.button1 config 

A nastavit příkazem:

.button1 configure -vlastnost hodnota 

4. Widget checkbutton

Dalším typem tlačítek je takzvaný checkbutton, dnes nepřesně nazývaný checkbox. Od obyčejného tlačítka se liší tím, že je vizuálně patrný jeho stav – nastaveno/nenas­taveno. Tento typ tlačítek je zobrazován různým způsobem, typicky se však jedná o čtvereček, který je buď zatržený, nebo prázdný; v některých GUI prostředích se stav tlačítka reprezentuje pouze jeho barvou. V nejjednodušším případě se přepínací tlačítko vytvoří následovně:

#!/usr/bin/wish
checkbutton .ch1 -text "Přepínací tlačítko"
pack .ch1 

Praktičtější však bývá navázání nějaké proměnné na tlačítko – to se provádí pomocí volby -variable. Pokud je tlačítko nastaveno, bude do proměnné implicitně vložena hodnota 1, pokud je naopak nenastaveno, nastaví se nulová hodnota. Vše je patrné z následujícího příkladu:

#!/usr/bin/wish
checkbutton .ch2 -text "My check button" -variable state -command {puts $state}
pack .ch2 

Kromě toho je možné pomocí voleb -onvalue a -offvalue zvolit, jakých hodnot bude daná proměnná nabývat v případě, že je tlačítko nastaveno i nenastaveno:

#!/usr/bin/wish
checkbutton .ch3 -text "Next button" -variable state -onvalue Stlačeno -offvalue Nestlačeno -command {puts $state}
pack .ch3 

5. Widget radiobutton

Dalším často používaným tlačítkem je takzvaný radiobutton. Tento typ widgetu se od předchozích dvou typů tlačítek odlišuje především tím, že je používán ve větších skupinách. Z každé skupiny přitom může být vybráno (nastaveno) pouze jedno tlačítko. Jak se však pozná, která tlačítka patří k sobě, tj. do jedné skupiny? Je to dáno jménem globální proměnné, která musí být pro tlačítka v jedné skupině stejná. Přitom je vhodné nastavit pro každé tlačítko jinou hodnotu, která se do proměnné nastaví v případě jeho stisknutí (v kódu je také navíc provedeno zarovnání všech tlačítek doleva):

#!/usr/bin/wish
radiobutton .r1 -text "First radio button" -variable radio -value 1 -justify left
radiobutton .r2 -text "Second radio button" -variable radio -value 2 -justify left
radiobutton .r3 -text "Third radio button" -variable radio -value 3 -justify left
radiobutton .r4 -text "Fourth radio button" -variable radio -value 4 -justify left
grid .r1 -column 1 -row 1 -sticky w
grid .r2 -column 1 -row 2 -sticky w
grid .r3 -column 1 -row 3 -sticky w
grid .r4 -column 1 -row 4 -sticky w 

Výše uvedený příklad je však velmi složitý a dlouhý, proto se v praxi používá zkrácený zápis, například podle následujícího schématu, kde se nápaditým a elegantním způsobem využívají seznamy. Jedinou podmínkou, kterou musíme splnit, je název widgetu, jenž musí začínat malým písmenem – viz použití pomocné proměnné lower:

#!/usr/bin/wish
foreach button {Assembler Basic "Brainfuck :-)" C C++ Java Lisp Perl Python Ruby Scheme Tcl} {
    set lower [string tolower $button]
    radiobutton .$lower -text $button -variable language -value $button -anchor w
    pack .$lower -side top -pady 5 -fill x
} 
TCL 6c

Obrázek 3: příklad použití radiobuttonů

6. Popis správců geometrie

V zásadě existují dva způsoby, jakými lze vkládat widgety do okna či dialogu. První možností, která je použita zejména v aplikacích tvořených pomocí WinAPI (Windows API) a MFC (Microsoft Foundation Classes), je absolutní umisťování, například specifikací souřadnic levého horního rohu widgetu a jeho šířky a výšky. Tato možnost je dobře použitelná pro aplikace, které mají běžet na jedné platformě s jedním správcem oken. Pro multiplatformní aplikace, u nichž není dopředu známá přesná velikost oken ani rozlišení obrazovky, se často používá druhá možnost, při které jsou widgety umisťovány buď relativně vůči sobě, nebo do flexibilních mřížek či dalších tvarů – tento způsob využívají známé knihovny widgetů, jakými jsou Qt, GTK, AWTSwing.

Oba dva výše zmíněné způsoby jsou v knihovně Tk podporovány, používat by se však měl především způsob druhý, tj. relativní umisťování widgetů. Pro umisťování jednotlivých widgetů do okna jsou v Tk používáni takzvaní správci (manažeři) geometrie. V knihovně Tk jsou k dispozici tři typy těchto správců, my si zde popíšeme správce pack a grid (poslední správce bude popsán v některém z navazujících dílů tohoto seriálu).

7. Správce geometrie pack

Nejprve si ukážeme použití správce geometrie nazvaného pack, který, jak již jeho jméno naznačuje, zarovnává jednotlivé widgety vedle sebe, a to buď ve směru horizontálním, či vertikálním. V prvních verzích knihovny Tk se jednalo o jediného dostupného správce, ale od verze 4.x se objevují i další dva typy správců. Na následujícím příkladu je vidět nejjednodušší použití správce pack:

#!/usr/bin/wish
button .prvni -text "First button" -command {exit}
button .druhy -text "Second button" -command {exit}
button .treti -text "Third button" -command {exit}
button .ctvrty -text "Fourth button" -command {exit}
pack .prvni
pack .druhy
pack .treti
pack .ctvrty 

Po spuštění tohoto příkladu se nejprve vytvoří čtveřice tlačítek, ke každému tlačítku je již při jeho vytvoření přiřazen příkaz, který se provede při stisknutí levého tlačítka myši (toto je implicitní nastavení, které je možné několika způsoby změnit). Tlačítka jsou po svém vytvoření neviditelná, protože knihovna Tk prozatím neví, na jaký kontejner je má umístit. Proto je po vytvoření tlačítek použit správce geometrie pack, který tlačítka umístí do hlavního (a současně i jediného) okna aplikace. Toto okno nemusíme vytvářet ručně, protože to za nás provede vizuální shell wish – viz první řádek programu, který tento shell zavolá.

Výše uvedený příklad je možné modifikovat tak, že se pomocí manažeru geometrie přikáže jiné rozmístění tlačítek v okně aplikace. To je ukázáno na dalším příkladu, ze kterého je patrné, že při zavolání správce pack je možné kromě jména widgetu, který se má na okno umístit, zadat i další příkazy, v tomto případě rozmístění tlačítek.

#!/usr/bin/wish
button .prvni -text "First button" -command {exit}
button .druhy -text "Second button" -command {exit}
button .treti -text "Third button" -command {exit}
button .ctvrty -text "Fourth button" -command {exit}
pack .prvni -side left
pack .druhy -side right
pack .treti -side top
pack .ctvrty -side bottom 

V praxi se příkazy pro vytvoření widgetu a jeho umístění slučují do jednoho řádku, takže předchozí příklad by mohl být napsán i následovně:

#!/usr/bin/wish

pack [button .prvni -text "First button" -command {exit}] -side left
pack [button .druhy -text "Second button" -command {exit}] -side right
pack [button .treti -text "Third button" -command {exit}] -side top
pack [button .ctvrty -text "Fourth button" -command {exit}] -side buttom 

Předchozí zápis je korektní z toho důvodu, že příkaz button vrací celý název vytvořeného widgetu.

8. Správce geometrie grid

Správce geometrie pack je sice velmi flexibilní (zejména při hierarchickém vkládání jednotlivých manažerů na sebe), v některých případech je však rozmisťování widgetů pomocí tohoto manažeru problematické nebo přinejmenším zdlouhavé. Z tohoto důvodu byl později vytvořen další správce geometrie, který je celkem trefně nazvaný grid. Pomocí tohoto správce se widgety umisťují do pomyslné mřížky, přičemž rozměr mřížky se, spolu s počtem sloupců a řádků, flexibilně mění podle tvarových charakteristik vkládaných widgetů. Příklad použití správce grid je podobné předchozí dvojici příkladů, u každého vkládaného widgetu se však musí specifikovat jeho umístění v mřížce. Sloupce a řádky jsou číslovány od nuly, pokud však začneme číslování od jedničky, bude mít vynechaný sloupec či řádek nulovou velikost, a umístění widgetů v okně tak žádným viditelným způsobem neovlivní:

#!/usr/bin/wish
button .prvni -text "First button" -command {exit}
button .druhy -text "Second button" -command {exit}
button .treti -text "Third button" -command {exit}
button .ctvrty -text "Fourth button" -command {exit}
grid .prvni -column 1 -row 1
grid .druhy -column 2 -row 1
grid .treti -column 1 -row 2
grid .ctvrty -column 2 -row 2 

Umístění widgetů v okně nemusí být pravidelné, jak je patrné z následujícího příkladu, kde jsou některé buňky mřížky prázdné. Manažer geometrie nemůže v tomto případě buňkám nastavit nulovou velikost, protože v daném řádku či sloupci jsou umístěny widgety, proto je volné místo viditelné i na obrazovce:

#!/usr/bin/wish
button .prvni -text "First button" -command {exit}
button .druhy -text "Second button" -command {exit}
button .treti -text "Third button" -command {exit}
button .ctvrty -text "Fourth button" -command {exit}
grid .prvni -column 2 -row 4
grid .druhy -column 3 -row 1
grid .treti -column 1 -row 3
grid .ctvrty -column 4 -row 2 

Kromě specifikace sloupce a řádku pro daný widget je možné nastavit i další vlastnosti. Často jsou používány volby -rowspan ? a -columnspan ?, které umožňují daný widget roztáhnout přes více řádků či sloupců. Místo otazníků se zadává počet řádků či sloupců (prakticky úplně stejným způsobem je řešeno slučování buněk v HTML). Můžete si zkusit změnit předchozí příklad například následovně:

grid .prvni -column 2 -row 4 -columnspan=3 

Zarovnání widgetu do buňky řeší vlastnost -sticky ?, kde se za otazník musí přidat jedno z písmen n (north), s (south), e (east) nebo w (west). Widget je po zadání této vlastnosti zarovnán specifikovaným směrem.

Další modifikací předchozích příkladů je specifikace mezer mezi jednotlivými widgety (resp. mezi widgetem a hranicí mřížky). Mezeru lze udávat v různých jednotkách, implicitně jsou použity pixely:

#!/usr/bin/wish
button .prvni -text "First button" -command {exit}
button .druhy -text "Second button" -command {exit}
button .treti -text "Third button" -command {exit}
button .ctvrty -text "Fourth button" -command {exit}
grid .prvni -column 1 -row 1 -padx 10 -pady 10
grid .druhy -column 2 -row 1 -padx 10 -pady 10
grid .treti -column 1 -row 2 -padx 10 -pady 10
grid .ctvrty -column 2 -row 2 -padx 10 -pady 10 
TCL 6d

Obrázek 4: rozmístění prvků do mřížky

root_podpora

9. Obsah dalšího pokračování tohoto seriálu

V dalším pokračování tohoto seriálu si ukážeme práci s dalšími typy widgetů. Kromě toho se zaměříme i na vytváření základních i složitěji strukturovaných me­nu.

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

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.