Hlavní navigace

Programovací jazyk TCL (7)

30. 8. 2005
Doba čtení: 12 minut

Sdílet

V dnešním pokračování seriálu o programovacím jazyku Tcl a jeho knihovně Tk si ukážeme práci s dalšími typy widgetů, zejména textovými poli, listboxy, posuvníky a vstupními prvky specializovanými na zadávání číselných hodnot.

Obsah

1. Rozšiřující widgety poskytované knihovnou Tk
2. Widget entry
3. Widget listbox
4. Widget text
5. Widget labelframe
6. Widget scrollbar
7. Widget spinbox
8. Obsah dalšího pokračování tohoto seriálu

1. Rozšiřující widgety poskytované knihovnou Tk

předchozí části tohoto seriálu jsme si popsali základní widgety, které v knihovně Tk slouží ke komunikaci mezi aplikací a uživatelem. Jednalo se o widgety label (jednořádkový i víceřádkový text), button (příkazové tlačítko), checkbutton (přepínací tlačítko se zobrazením stavu) a radiobutton (tlačítko sdružované do skupin za účelem uživatelského výběru pouze jednoho tlačítka). Přitom se jednalo veskrze o widgety, které uživateli v grafické podobě nabídly jeden či několik příkazů a uživatel posléze některý příkaz vybral a tím spustil v aplikaci některou z předprogramo­vaných akcí. V dnešním pokračování se budeme věnovat popisu dalších widgetů, které umožňují, aby uživatel mohl do aplikace zadat nebo modifikovat komplikovanější data. Popsány budou následující widgety:

  • entry (druhá kapitola) – widget, do kterého je možné zapisovat text, k tomu má přidruženo mnoho klávesových zkratek (jde o kombinaci staršího a novějšího standardu)
  • listbox (třetí kapitola) – widget, jež nabízí na výběr prakticky neomezené množství řádků s textem
  • text (čtvrtá kapitola) – výkonný widget pro práci s editovatelným víceřádkovým textem
  • labelframe (pátá kapitola) – kontejner s možností zobrazení popisku
  • scrollbar (šestá kapitola) – podobné widgetu scale s tím rozdílem, že zobrazuje posuvné šipky a naopak nezobrazuje přidruženou číselnou hodnotu
  • spinbox (sedmá kapitola) – widget určený pro zadávání číselných hodnot kombinací klávesnice a myši (i s kontrolou­ mezí)

2. Widget entry

Pomocí widgetu typu entry je možné v okně či dialogu zobrazit jeden řádek textu. Od minule popsaného widgetu label se tento widget liší především v tom, že zobrazený řádek textu je možné editovat. Při editaci jsou k dispozici základní klávesy pro pohyb kurzoru (šipka doleva, šipka doprava, klávesa [Home] a klávesa [End]) a mimo jiné také další klávesové zkratky, které jsou známé například z shellu či editoru Emacs, Joe a Pico: [Ctrl+A] (posun na začátek textu), [Ctrl+E] (posun na konec textu). K tomu připočítejme dnes již standardní klávesy pro práci se schránkou: [Ctrl+C], [Ctrl+V] a [Ctrl+X] a na některých systémech i kombinace kláves [Ctrl+Insert], [Shift+Insert] a [Shift+Delete]. Použití tohoto widgetu si ukážeme na příkladu, ve kterém zobrazíme dialog pro přístup do nějaké hypotetické aplikace. Tento dialog budeme později upravovat tak, aby vyhovoval dalším požadavkům. První verze dialogu může vypadat například následovně:

#!/usr/bin/wish

# vytvoření popisků
label .name -text "Name"
label .password -text "Password"
label .role -text "Role"

# textová vstupní pole
entry .nameEntry -textvariable name
entry .passwordEntry -textvariable password
entry .roleEntry -textvariable role

# použití manažera geometrie
grid .name -row 0 -column 0 -sticky w
grid .password -row 1 -column 0 -sticky w
grid .role -row 2 -column 0 -sticky w
grid .nameEntry -row 0 -column 1 -sticky w
grid .passwordEntry -row 1 -column 1 -sticky w
grid .roleEntry -row 2 -column 1 -sticky w 
TCL 7a

Obrázek 1: první verze přihlašovacího dialogu

Na výše uvedeném příkladu je zajímavé zejména použití proměnných name, password a role, do kterých se průběžně ukládají hodnoty zapsané uživatelem do widgetů nazvaných .nameEntry, .passwordEntry a .roleEntry. Malou úpravou předchozího příkladu si ukážeme, že proměnné jsou opravdu korektně naplněny:

#!/usr/bin/wish

# vytvoření popisků
label .name -text "Name"
label .password -text "Password"
label .role -text "Role"

# vytvoření tlačítek
button .login -text "Login" -command {login}
button .exit -text "Exit" -command {exit}

# textová vstupní pole
entry .nameEntry -textvariable name
entry .passwordEntry -textvariable password
entry .roleEntry -textvariable role

# použití manažera geometrie
grid .name -row 0 -column 0 -sticky w
grid .password -row 1 -column 0 -sticky w
grid .role -row 2 -column 0 -sticky w
grid .nameEntry -row 0 -column 1 -sticky w
grid .passwordEntry -row 1 -column 1 -sticky w
grid .roleEntry -row 2 -column 1 -sticky w
grid .login -row 3 -column 0 -sticky w
grid .exit -row 3 -column 1 -sticky w

# procedura pro výpis zadaných informací
proc login {} {
    global name password role
    puts "Name: $name"
    puts "Password: $password"
    puts "Role: $role"
} 
TCL 7b

Obrázek 2: druhá verze přihlašovacího dialogu

Po stisknutí tlačítka Login se na konzoli vypíšou zadané údaje, tlačítko Exit slouží k uzavření dialogu. Widget entry může také při psaní zobrazovat místo skutečně zapsaných znaků pouze zástupné symboly. To se hodí právě při zadávání hesla, kde je již po delší dobu zvykem místo znaků psát hvězdičky či podobné symboly. Na dalším příkladu si povšimněte, že se pomocí volby -show specifikuje, který znak se bude ve widgetu zobrazovat. Místo hvězdičky je samozřejmě možné zvolit libovolný znak (ne však jejich kombinaci).

#!/usr/bin/wish

# vytvoření popisků
label .name -text "Name"
label .password -text "Password"
label .role -text "Role"

# vytvoření tlačítek
button .login -text "Login" -command {login}
button .exit -text "Exit" -command {exit}

# textová vstupní pole
entry .nameEntry -textvariable name
entry .passwordEntry -textvariable password -show *
entry .roleEntry -textvariable role

# použití manažera geometrie
grid .name -row 0 -column 0 -sticky w
grid .password -row 1 -column 0 -sticky w
grid .role -row 2 -column 0 -sticky w
grid .nameEntry -row 0 -column 1 -sticky w
grid .passwordEntry -row 1 -column 1 -sticky w
grid .roleEntry -row 2 -column 1 -sticky w
grid .login -row 3 -column 0 -sticky w
grid .exit -row 3 -column 1 -sticky w

# procedura pro výpis zadaných informací
proc login {} {
    global name password role
    puts "Name: $name"
    puts "Password: $password"
    puts "Role: $role"
} 

Widget entry má velmi mnoho voleb, z nichž zajímavá je především volba -validate, pomocí které je možné automaticky kontrolovat, zda zadávaný text odpovídá specifikovaným požadavkům.

3. Widget listbox

Předchozí příklad s přihlašovacím dialogem nebyl vyřešen k naší úplné spokojenosti, protože v reálných aplikacích si může uživatel vybírat pouze z několika předem známých rolí. Proto buď můžeme po stisknutí tlačítka Login vyvolat proceduru, která porovná zadaný text se seznamem předem známých řetězců, a v případě chyby znovu zobrazit přihlašovací dialog, nebo můžeme využít widget typu listbox, pomocí kterého se uživateli zobrazí pouze nabídka předem daných rolí, ze kterých si může vybírat. Je zřejmé, že v tomto případě bude jak pro programátora, tak i pro uživatele výhodnější použít právě listbox – programátor si ušetří práci s kontrolou zadaných údajů a uživatel si nemusí pamatovat všechny povolené řetězce, protože je před sebou vidí na obrazovce. Nejprve si ukažme, jakým způsobem se listbox vytváří a jak se do něj „vkládají“ jednotlivé řetězce (položky):

#!/usr/bin/wish

# vytvoření listboxu
listbox .mujList

# zobrazení listboxu
pack .mujList

# přidání položek do listboxu
.mujList insert end "Položka na konci"
.mujList insert end "Další položka na konci"
.mujList insert 0 "Položka na začátku" 

Z předchozího příkladu je patrné, jakým způsobem se pomocí příkazu insert do listboxu vkládají jednotlivé položky – zadá se pozice vkládané položky a potom text, který se má zobrazit. Pozice může být zadána buď absolutně indexem (číslo), nebo je možné použít slovo end, které zabezpečí vložení položky na konec listboxu. Kromě příkazu insert je možné použít i další příkazy pro manipulaci s položkami, například get (získání textové podoby), delete (vymazání položky), index, scan atd. Také stojí za povšimnutí, že položky se do lisboxu mohou vkládat až po jeho zobrazení bez nutnosti volat nějakou překreslovací rutinu.

Při práci s listboxem poprvé narazíme na menší problém – jakým způsobem je možné reagovat na změnu výběru? K dispozici totiž nemáme ani volbu -variable, ani volbu -command. Řešení spočívá v programovém navázání (bind) procedury na nějaký widget. Procedura je zavolána v případě, že na widgetu došlo k nějaké události, typicky při interakci uživatele s widgetem. Pro navázání procedury se používá příkaz bind. Ihned za tímto příkazem se objevuje jméno widgetu, které je tvořeno, jak již víme z předchozího dílu, cestou k widgetu s jeho jednoznačným identifikátorem. Po jméně widgetu následuje typ události, který je, podobně jako v konfiguračních souborech Motifu, uzavřen do lomených závorek. Za specifikací typu události se nachází blok příkazů, které se provedou v případě, že daná událost nastane. Předchozí příklad je možné upravit tak, že po každé změně výběru z listboxu (pomocí myši) se zavolá příkaz put, který vypíše text právě vybrané položky. K tomu využijeme podfunkce pro získání pozice aktivní položky (curselection) a pro přečtení textu na nějaké pozici v listboxu (get):

#!/usr/bin/wish

# vytvoření listboxu
listbox .mujList

# navázání příkazů, které se provedou po
# puštění levého tlačítka myši
bind .mujList <ButtonRelease-1> {puts [.mujList get [.mujList curselection]]}

# zobrazení listboxu
pack .mujList

# přidání položek do listboxu
.mujList insert end "Položka na konci"
.mujList insert end "Další položka na konci"
.mujList insert 0 "Položka na začátku" 

Pokud bychom potřebovali, aby se po stisknutí pravého tlačítka myši dialog zavřel, není nic jednoduššího než navázat další proceduru, která je zavolána při stisknutí pravého tlačítka (to má v Tk číslo tři, prostřední tlačítko má číslo 2):

#!/usr/bin/wish

# vytvoření listboxu
listbox .mujList

# navázání příkazů, které se provedou po
# puštění levého tlačítka myši
bind .mujList <ButtonRelease-1> {puts [.mujList get [.mujList curselection]]}

# navázání příkazu, který se provede při
# stisknutí pravého tlačítka myši
bind .mujList <ButtonPress-3> {exit}

# zobrazení listboxu
pack .mujList

# přidání položek do listboxu
.mujList insert end "Položka na konci"
.mujList insert end "Další položka na konci"
.mujList insert 0 "Položka na začátku" 

Verze přihlašovacího dialogu s listboxem by mohla vypadat například následovně:

#!/usr/bin/wish

# vytvoření popisků
label .name -text "Name"
label .password -text "Password"
label .role -text "Role"

# vytvoření tlačítek
button .login -text "Login" -command {login}
button .exit -text "Exit" -command {exit}

# textová vstupní pole
entry .nameEntry -textvariable name
entry .passwordEntry -textvariable password -show *

# vytvoření listboxu
listbox .roleListBox -height 0
bind .roleListBox <ButtonRelease-1> {set role [.roleListBox get [.roleListBox curselection]]}

# přidání položek do listboxu
.roleListBox insert end "Guest"
.roleListBox insert end "Editor"
.roleListBox insert end "Gestor"
.roleListBox insert end "Administrator"

# použití manažera geometrie
grid .name -row 0 -column 0 -sticky w
grid .password -row 1 -column 0 -sticky w
grid .role -row 2 -column 0 -sticky w
grid .nameEntry -row 0 -column 1 -sticky w
grid .passwordEntry -row 1 -column 1 -sticky w
grid .roleListBox -row 2 -column 1 -sticky w
grid .login -row 3 -column 0 -sticky w
grid .exit -row 3 -column 1 -sticky w

# procedura pro výpis zadaných informací
proc login {} {
    global name password role
    puts "Name: $name"
    puts "Password: $password"
    puts "Role: $role"
} 
TCL 7c

Obrázek 3: třetí verze přihlašovacího dialogu s listboxem

Pokud by bylo zapotřebí místo listboxu použít combobox (zde by možná nebyl tak vhodný), je možné použít widget ComboBox z knihovny BWidgets, která rozšiřuje množinu základních widgetů z knihovny Tk.

4. Widget text

Widget text patří vedle později popsaného widgetu canvas k prakticky nejsložitějším objektům, se kterými je možné v knihovně Tk 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 – 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 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 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). Není divu, že na bázi Tcl/Tk vznikl webovský prohlížeč, prohlížeč manuálových stránek a další užitečné aplikace – velkou část funkcionality 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.

Použitím widgetu text se ještě budeme podrobněji zabývat v dalších pokračováních tohoto seriálu.

5. Widget labelframe

Widget labelframe slouží ve většině případů jako kontejner, do něhož se ukládají další widgety. Rozdíl oproti podobnému widgetu frame spočívá v tom, že zde může být specifikovaný i nápis, který se zobrazí v levém horním rohu widgetu. Podobně jako u tlačítek, i u těchto widgetů je možné zvolit jejich velikost i to, jakým způsobem se zvýrazní jejich „trojrozměrný“ okraj – viz následující příklad:

#!/usr/bin/wish
foreach lf {sunken flat ridge groove raised} {
    labelframe .$lf -width 50px -height 50px -relief $lf -text $lf
    pack .$lf -side left -padx 10 -pady 10
} 

Na předchozím příkladu je (opět) vidět, jak efektivní může být práce se seznamy – položka v seznamu vystupuje jak v roli názvu jednotlivých objektů, tak i jako jejich nadpis a hodnota vlastnosti -relief.

6. Widget scrollbar

Widget scrollbar umožňuje zobrazení klasického posuvníku, jehož vizuální podoba se však může na různých operačních systémech (resp. v závislosti na jejich GUI) podstatným způsobem odlišovat. Většinou jsou scrollbary spojeny s dalšími widgety, čímž je dosaženo možnosti posunu pohledu po větší ploše widgetu, než je možné na obrazovce zobrazit. Orientace scrollbaru se řídí volbou -orient. Na dalším příkladu si ukážeme spojení listboxu a scrollbaru:

#!/usr/bin/wish

# vytvoření popisků
label .name -text "Name"
label .password -text "Password"
label .role -text "Role"

# vytvoření tlačítek
button .login -text "Login" -command {login}
button .exit -text "Exit" -command {exit}

# textová vstupní pole
entry .nameEntry -textvariable name
entry .passwordEntry -textvariable password -show *

# vytvoření listboxu
listbox .roleListBox -height 3
bind .roleListBox <ButtonRelease-1> {set role [.roleListBox get [.roleListBox curselection]]}

# vytvoření scrollbaru a navázání na listbox
scrollbar .vertical -command ".roleListBox yview"

# přidání položek do listboxu
.roleListBox insert end "Guest"
.roleListBox insert end "Editor"
.roleListBox insert end "Gestor"
.roleListBox insert end "Administrator"

# použití manažera geometrie
grid .name -row 0 -column 0 -sticky w
grid .password -row 1 -column 0 -sticky w
grid .role -row 2 -column 0 -sticky w
grid .nameEntry -row 0 -column 1 -sticky w
grid .passwordEntry -row 1 -column 1 -sticky w
grid .roleListBox -row 2 -column 1 -sticky w
grid .login -row 3 -column 0 -sticky w
grid .exit -row 3 -column 1 -sticky w
grid .vertical -row 0 -column 2 -sticky w

# procedura pro výpis zadaných informací
proc login {} {
    global name password role
    puts "Name: $name"
    puts "Password: $password"
    puts "Role: $role"
} 

Pro zcela korektní použití scrollbaru by bylo vhodné nastavit zpětnou vazbu při změně vybrané položky v listboxu tak, aby se upravila i poloha posuvníku na scrollbaru. To zajistí řádek:

listbox .roleListBox -height 3 -yscroll ".vertical set" 
TCL 7d

Obrázek 4: čtvrtá verze přihlašovacího dialogu s listboxem

7. Widget spinbox

Widget spinbox je jakousi směsicí widgetuentry a scrollbaru – uživatel může do tohoto widgetu zadávat čísla z předem známého intervalu, zvyšování a snižování číselné hodnoty zabezpečí zobrazené šipky (můžeme se přít o to, zda je takový způsob zadávání hodnot efektivní, uživatelé ho však – zdá se – preferují). Tento interval se většinou zadává již při vytváření widgetu pomocí voleb -from a -to. Kromě toho je ještě možné volbou -textvariable specifikovat název proměnné, která bude sledovat právě zadanou hodnotu. Použití spinboxu je ukázáno na následujícím jednoduchém příkladu:

CS24_early

#!/usr/bin/wish
spinbox .spin -from 100 -to 120 -textvariable number
button .confirm -text Confirm -command {puts $number}
pack .spin
pack .button 

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

V navazujícím pokračování tohoto seriálu se budeme zabývat zejména způsobem práce s grafikou pomocí knihovny Tk. Ukážeme si práci s velmi flexibilním widgetem canvas a dále widgetybitmap, image a photo.

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.