Hlavní navigace

Programovací jazyk TCL (7)

Pavel Tišnovský 30. 8. 2005

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:

#!/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.

Našli jste v článku chybu?

21. 2. 2007 16:18

To puts() vypisuje na standardni vystup, tj. pri spusteni ze shellu na konzoli (terminal). Nebo je mozne vystup presmerovat napriklad do souboru zpusobem:

moje_aplikace > vystup.txt

21. 2. 2007 16:01

Jan (neregistrovaný)
Ono vlastne jakkoliv pouziju to "puts", tak to nic nevypisuje...
Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu

Vitalia.cz: To není kašel! Správná diagnóza zachrání život

To není kašel! Správná diagnóza zachrání život

Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

Podnikatel.cz: Nejenom EET, začaly platit další zákony

Nejenom EET, začaly platit další zákony

Root.cz: Certifikáty zadarmo jsou horší než za peníze?

Certifikáty zadarmo jsou horší než za peníze?

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

Podnikatel.cz: EET: Totálně nezvládli metodologii projektu

EET: Totálně nezvládli metodologii projektu

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Root.cz: Vypadl Google a rozbilo se toho hodně

Vypadl Google a rozbilo se toho hodně

Podnikatel.cz: Babiše přesvědčila 89letá podnikatelka?!

Babiše přesvědčila 89letá podnikatelka?!

120na80.cz: Rakovina oka. Jak ji poznáte?

Rakovina oka. Jak ji poznáte?

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Podnikatel.cz: Babiš: E-shopy z EET možná vyjmeme

Babiš: E-shopy z EET možná vyjmeme

Podnikatel.cz: Víme první výsledky doby odezvy #EET

Víme první výsledky doby odezvy #EET

Vitalia.cz: Co pomáhá dítěti při zácpě?

Co pomáhá dítěti při zácpě?

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka

DigiZone.cz: Sony KD-55XD8005 s Android 6.0

Sony KD-55XD8005 s Android 6.0