Programovací jazyk TCL (19)

22. 11. 2005
Doba čtení: 10 minut

Sdílet

V již předposlední části seriálu o programovacím jazyku Tcl dokončíme stručný popis objektů dostupných z knihovny IWidgets. Ukážeme si použití widgetu určeného pro přesuny položek mezi dvojicí seznamů (listboxů), widgetu pro výběr souboru, rozšířeného kreslicího plátna (canvasu) umožňujícího tisk a konečně widgetu poskytujícího prostředky pro zobrazení HTML stránky.

Obsah

1. Přesun položek mezi dvojicí listboxů
2. Widget pro výběr souboru
3. Kreslicí plátno určené pro tisk
4. Widget umožňující zobrazení HTML stránky
5. Obsah dalšího pokračování tohoto seriálu

1. Přesun položek mezi dvojicí listboxů

V mnoha aplikacích s grafickým uživatelským rozhraním (GUI) se dostáváme do situací, ve kterých je zapotřebí nabídnout uživateli seznam nějakých akcí či objektů, jež je možné vybírat, povolovat či zakazovat. Pokud je počet akcí či objektů (obecně položek) relativně malý a předem známý, je možné pro tuto činnost použít skupinu přepínacích tlačítek (radio buttons) či zaškrtávacích tlačítek (check boxes/check buttons), pokud však počet položek stoupá nad cca deset (což nastává například při práci se styly v aplikacích typu Office či při změně hladin v CAD systémech), je vhodnější použít jiný typ widgetů, které by měly umožňovat zobrazení libovolného množství položek, samozřejmě za přispění dalších ovládacích prvků: výběrových menu či skrolovacích lišt. V knihovně IWidgets je pro podobné případy nabízen widget nazvaný disjointlistbox.

Tento widget je ve své podstatě složen z několika jednodušších widgetů. Kromě popisek (labels) se zde nachází dvojice seznamů (listboxů) a dvojice příkazových tlačítek (buttons). První tlačítko slouží k přesunu vybraných položek z levého seznamu do seznamu pravého, druhé tlačítko provádí pravý opak – přesun z pravého seznamu do seznamu levého. Do pravého i levého seznamu je možné přidávat položky pomocí příkazu název_widgetu insertlhs, název_widgetu insertrhs, název_widgetu setlhs a název_widgetu setrhs. Získání aktuálních obsahů listboxů zajistí příkazy název_widgetu getlhs a název_widgetu getrhs. Na prvním demonstračním příkladu je ukázáno nejjednodušší použití widgetu disjointlistbox, kdy je levý seznam naplněn několika položkami a pravý seznam zůstává prázdný.

#!/usr/bin/wish

# Práce s knihovnou IWidgets
# První demonstrační příklad

# načtení knihovny IWidgets
package require Iwidgets 4.0

# vytvoření dvojice seznamů pomocí widgetu disjointlistbox
iwidgets::disjointlistbox .seznamy

# naplnění levého seznamu, pravý seznam zůstává prázdný
.seznamy insertlhs {černá červená zelená modrá žlutá oranžová azurová fialová bílá}

# vložení widgetu disjointlistbox do okna
pack .seznamy

# tlačítko pro ukončení aplikace
pack [button .q -command {exit} -text "Konec"]

# finito 
TCL 19 - 1

Obrázek 1: Screenshot prvního demonstračního příkladu

Widget disjointlistbox je možné konfigurovat pomocí poměrně velkého množství voleb, v tomto článku si však uvedeme pouze volby nejčastěji používané. Vzhledem k tomu, že tento typ widgetu je v originální podobě navržen pro anglicky mluvící uživatele, je vhodné v českých aplikacích přejmenovat popisky a tlačítka. To se provádí již při vytváření widgetu pomocí voleb -lhslabeltext text, -rhslabeltext text, -lhsbuttonlabel text a -rhsbuttonlabel text. Texty je však možné změnit i u vytvořeného widgetu příkazem option. „Překlad“ widgetu disjointlistbox je ukázán ve druhém demonstračním příkladu:

#!/usr/bin/wish

# Práce s knihovnou IWidgets
# Druhý demonstrační příklad

# načtení knihovny IWidgets
package require Iwidgets 4.0

# vytvoření dvojice seznamů pomocí widgetu disjointlistbox
iwidgets::disjointlistbox .seznamy \
        -lhslabeltext   "Dostupné barvy" \
        -rhslabeltext   "Výběr" \
        -lhsbuttonlabel "Přidej barvu" \
        -rhsbuttonlabel "Odeber barvu"

# naplnění levého seznamu, pravý seznam zůstává prázdný
.seznamy insertlhs {černá červená zelená modrá žlutá oranžová azurová fialová bílá}

# vložení widgetu disjointlistbox do okna
pack .seznamy

# tlačítko pro ukončení aplikace
pack [button .q -command {exit} -text "Konec"]

# finito 
TCL 19 - 2

Obrázek 2: Screenshot druhého demonstračního příkladu

Ve třetím demonstračním příkladu je ukázána další důležitá vlastnost seznamů použitých ve widgetu disjointlistbox. Položky v seznamech totiž mohou být automaticky seřazeny. Způsob řazení se pro levý seznam specifikuje volbou -lhssortoption a pro pravý seznam volbou -rhssortoption. K dispozici jsou tři možnosti řazení: vzestupné („increasing“), sestupné („decreasing“) a žádné („none“).

#!/usr/bin/wish

# Práce s knihovnou IWidgets
# Třetí demonstrační příklad

# načtení knihovny IWidgets
package require Iwidgets 4.0

# vytvoření dvojice seznamů pomocí widgetu disjointlistbox
iwidgets::disjointlistbox .seznamy \
        -lhslabeltext   "Dostupné barvy" \
        -rhslabeltext   "Výběr" \
        -lhsbuttonlabel "Přidej barvu" \
        -rhsbuttonlabel "Odeber barvu" \
        -rhssortoption  decreasing

# naplnění levého seznamu, pravý seznam zůstává prázdný
.seznamy insertlhs {černá červená zelená modrá žlutá oranžová azurová fialová bílá}

# vložení widgetu do okna
pack .seznamy

# tlačítko pro ukončení aplikace
pack [button .q -command {exit} -text "Konec"]

# finito 
TCL 19 - 3

Obrázek 3: Screenshot třetího demonstračního příkladu

Tento widget však má i některé nevýhody, z nichž nejzávažnější je nemožnost pohybu po jednotlivých položkách v levém i pravém seznamu pomocí klávesnice, tj. kurzorových šipek.

2. Widget pro výběr souboru

Dalším užitečným widgetem poskytovaným knihovnou IWidgets je grafický objekt, který uživateli umožňuje interaktivní výběr souboru s případným pohybem po adresářové struktuře. Při popisu toolkitu Tk v předchozích pokračováních tohoto seriálu (části 5 až 15) jsme se dozvěděli, že je k dispozici standardní dialogové okno pro výběr souboru, v některých případech však může být vhodnější, aby se soubor vybíral v dialogovém okně aplikace a nikoli v samostatném dialogu. Tento požadavek, který je vznesen například u aplikací řešících správu projektů, může být splněn pomocí widgetu fileselectionbox, popř. extfileselecti­onbox. Oba zmíněné widgety se skládají z jednodušších částí, zejména ze seznamu adresářů, seznamu souborů ve vybraném adresáři, editačního pole se zadanou maskou (filtrem) a editačního pole se jménem vybraného souboru. Použití widgetu fileselectionbox (popř. i extfileselec­tionbox) je velmi jednoduché, což je ukázáno na čtvrtém demonstračním příkladu:

#!/usr/bin/wish

# Práce s knihovnou IWidgets
# Čtvrtý demonstrační příklad

# načtení knihovny IWidgets
package require Iwidgets 4.0

# vytvoření widgetu pro výběr souboru
iwidgets::fileselectionbox .vyber -width 6i -height 4i

# vložení widgetu do okna
pack .vyber

# tlačítko pro ukončení aplikace
pack [button .q -command {exit} -text "Konec"]

# finito 
TCL 19 - 4

Obrázek 4: Screenshot čtvrtého demonstračního příkladu

Widget fileselectionbox je možné, podobně jako výše popisovaný widgetdisjoin­tlistbox, pomocí několika voleb „počeštit“. Konkrétně se jedná o volby -dirslabel text, -fileslabel text, -filterlabel text a -selectionlabel text. Vytvoření českého formuláře pro výběr souboru je ukázáno v pátém demonstračním příkladu. Poznámka: ve všech svých aplikacích se držím a budu držet termínu „adresář“ a nikoli „složka“ (mimo jiné proto, že dle dokumentace Microsoftu jsou možnosti složek omezené, nejdou do nich například umístit linky).

#!/usr/bin/wish

# Práce s knihovnou IWidgets
# Pátý demonstrační příklad

# načtení knihovny IWidgets
package require Iwidgets 4.0

# vytvoření widgetu pro výběr souboru
iwidgets::fileselectionbox .vyber -width 600 -height 400 \
        -dirslabel      "Seznam adresářů" \
        -fileslabel     "Seznam souborů" \
        -filterlabel    "Filtr (výběr/maska)" \
        -selectionlabel "Aktuálně vybraný soubor"

# vložení widgetu do okna
pack .vyber

# tlačítko pro ukončení aplikace
pack [button .q -command {exit} -text "Konec"]

# finito 
TCL 19 - 5

Obrázek 5: Screenshot pátého demonstračního příkladu

3. Kreslicí plátno určené pro tisk

Kreslicí plátno neboli canvas jsme si již popsali při vytváření jednoduchého vektorového grafického editoru pomocí toolkitu Tk. Knihovna IWidgets možnosti původního kreslicího plátna rozšiřuje o možnost interaktivního nastavení tisku. Pro ukázku vykreslení plátna si vezmeme právě náš jednoduchý grafický editor. Tento editor bude rozšířen o jedno tlačítko nazvané jednoduše „Tisk“. Po stisknutí tohoto tlačítka se zobrazí dialog s nastavením tisku. Samotný tisk se může provést dvěma způsoby: buď do souboru ve formátu PostScriptu, nebo na tiskárnu (resp. do filtru lpr, který tisk posléze provede). Dialog je vytvořen pomocí příkazu iwidgets::can­vasprintdialog .print, jeho navázání na kreslicí plátno zajistí příkaz .print setcanvas .editor.platno.

#!/usr/bin/wish

# Práce s knihovnou IWidgets
# Šestý demonstrační příklad

# Jednoduchý grafický editor založený na widgetu canvas.
# Úprava editoru tak, aby podporoval zobrazení "pružných úseček"
# během kreslení. "Pružné úsečky" jsou vykreslovány červenou
# barvou, již nakreslené entity potom barvou černou.

# načtení knihovny IWidgets
package require Iwidgets 4.0

# globální proměnné
set rezim usecka
set x1 0
set y1 0
set button 0
set vektor 0

# seznam tlačítek, kterými se editor bude ovládat
set tlacitka {usecka kruznice elipsa ctverec obdelnik vymazat tisk konec}
set nazvy {Úsečka Kružnice Elipsa Čtverec Obdélník "Vymazat entitu" Tisk Konec}

# kontejner pro vkládání dalších widgetů
frame .editor
frame .editor.tlacitka

# plátno, na které se bude kreslit
canvas .editor.platno -width 512 -height 384

# vytvoření tlačítek, kterými se mění režim editoru
foreach entita $tlacitka nazev $nazvy {
    button .editor.tlacitka.$entita -text $nazev -command " nastavRezim $entita "
}

# vložení všech widgetů do okna aplikace
pack .editor
pack .editor.platno
pack .editor.tlacitka -side top -fill both -expand true

# vložení všech tlačítek na kontejner
foreach entita $tlacitka {
    pack .editor.tlacitka.$entita -side left -fill both -expand true
}

# navázání událostí od myši
bind .editor.platno <ButtonPress-1>   { onStart %x %y }
bind .editor.platno <ButtonRelease-1> { onStop %x %y }
bind .editor.platno <Motion>          { onMotion %x %y }

# dialogový box pro tisk
iwidgets::canvasprintdialog .print -modality application \
    -printcmd "lpr" -pagesize "A4"

# navázání kreslicího plátna na dialogový box
.print setcanvas .editor.platno

# procedura pro nastavení režimu editoru
proc nastavRezim {rez} {
    global rezim
    set rezim $rez
    # bylo stlačeno tlačítko pro ukončení aplikace
    if { [string compare $rezim konec] == 0 } {
        exit
    }
    # bylo stlačeno tlačítko pro tisk plátna
    if { [string compare $rezim tisk] == 0 } {
        if {[.print activate]} {
            .print print
        }
    }
}

# procedura, která je zavolána v případě,
# že uživatel stiskne levé tlačítko myši
proc onStart { x y } {
    global rezim
    # zapamatování polohy kurzoru myši
    global x1
    global y1
    global button
    set x1 $x
    set y1 $y
    set button 1
    # v režimu mazání se vymaže nejbližší entita
    # ke kurzoru myši
    if { [string compare $rezim vymazat] == 0 } {
        .editor.platno delete [.editor.platno find closest $x $y]
    }
}

# procedura, která je zavolána v případě,
# že uživatel pustí levé tlačítko myši
proc onStop { x y } {
    global rezim
    global x1
    global y1
    global button
    set button 0

    # rozdíly mezi starou a novou polohou
    # kurzoru myši
    set dx [expr abs($x1-$x)]
    set dy [expr abs($y1-$y)]

    global vektor
    .editor.platno delete $vektor

    # vykreslení jednotlivých entit
    switch $rezim {
       usecka   {.editor.platno create line $x1 $y1 $x $y}
       kruznice {
            if { $dx>$dy} {
                .editor.platno create oval $x1 $y1 $x [expr $y1+$x-$x1]
            } else {
                .editor.platno create oval $x1 $y1 [expr $x1+$y-$y1] $y
            }
       }
       elipsa   {.editor.platno create oval $x1 $y1 $x $y}
       ctverec  {
            if { $dx>$dy} {
                .editor.platno create rect $x1 $y1 $x [expr $y1+$x-$x1]
            } else {
                .editor.platno create rect $x1 $y1 [expr $x1+$y-$y1] $y
            }
       }
       obdelnik {.editor.platno create rect $x1 $y1 $x $y}
       default  {}
    }
}

# procedura, která je zavolána v případě,
# že uživatel pohybuje korzorem myši
proc onMotion { x y } {
    global x1
    global y1
    global button
    global vektor
    global rezim

    # rozdíly mezi starou a novou polohou
    # kurzoru myši
    set dx [expr abs($x1-$x)]
    set dy [expr abs($y1-$y)]

    # pokud je tlačítko myši stisknuto, vykreslí se pružná úsečka
    if ($button) {
        .editor.platno delete $vektor
        switch $rezim {
            usecka   {set vektor [.editor.platno create line $x1 $y1 $x $y -fill red]}
            kruznice {
                if { $dx>$dy} {
                    set vektor [.editor.platno create oval $x1 $y1 $x [expr $y1+$x-$x1] -outline red]
                } else {
                    set vektor [.editor.platno create oval $x1 $y1 [expr $x1+$y-$y1] $y -outline red]
                }
            }
            elipsa   {set vektor [.editor.platno create oval $x1 $y1 $x $y -outline red]}
            ctverec  {
                if { $dx>$dy} {
                    set vektor [.editor.platno create rect $x1 $y1 $x [expr $y1+$x-$x1] -outline red]
                } else {
                    set vektor [.editor.platno create rect $x1 $y1 [expr $x1+$y-$y1] $y -outline red]
                }
            }
            obdelnik {set vektor [.editor.platno create rect $x1 $y1 $x $y -outline red]}
            default  {}
        }
    }
}

# finito 
TCL 19 - 6

Obrázek 6: Screenshot šestého demonstračního příkladu

4. Widget umožňující zobrazení HTML stránky

Velmi užitečným widgetem poskytovaným knihovnou IWidgets je widget, který na své ploše dokáže zobrazit HTML stránku. Tento widget je možné použít například při programování interaktivní nápovědy k aplikaci – vytvořené stránky je možné zobrazit jak přímo v aplikaci, tak i při prezentaci aplikace na Internetu. Na tomto místě je však vhodné podotknout, že možnosti zde popisovaného widgetu v žádném případě nedosahují kvalit moderních renderovacích jader (Gecko apod.), na druhou stranu jsou však možnosti pro zobrazení běžné nápovědy dostačující a samotný widget není příliš náročný ani na strojový čas, ani na operační paměť. K dispozici jsou značky zhruba na úrovni HTML verze 3.1 – nadpisy, číslované i nečíslované seznamy, nastavení písma (normální, kurzíva, tučné, podtržené, zvýrazněné, indexy, mocniny atd.), hypertextové odkazy, obrázky atd. Použití widgetu pro zobrazení HTML stránky ukazuje sedmý demonstrační příklad:

#!/usr/bin/wish

# Práce s knihovnou IWidgets
# Sedmý demonstrační příklad

# načtení knihovny IWidgets
package require Iwidgets 4.0

# widget pro zobrazení HTML stránky
iwidgets::scrolledhtml .html -labeltext "HTML stránka" \
      -width 512 -height 384

# načtení HTML stránky
.html import "test.html"

# vložení widgetu do okna
pack .html

# tlačítko pro ukončení aplikace
pack [button .q -command {exit} -text "Konec"]

# finito 
TCL 19 - 7

Obrázek 7: Screenshot sedmého demonstračního příkladu

Osmý demonstrační příklad ukazuje, jakým způsobem je možné nastavit další vlastnosti widgetu pro zobrazování HTML stránek. Nejdůležitější je zde volba -linkcommand, která po výběru hypertextového odkazu zajistí skok na tento odkaz.

#!/usr/bin/wish

# Práce s knihovnou IWidgets
# Osmý demonstrační příklad

# načtení knihovny IWidgets
package require Iwidgets 4.0

option add *textBackground seashell

# widget pro zobrazení HTML stránky
iwidgets::scrolledhtml .html -labeltext "HTML stránka" \
      -width 512 -height 384 \
      -wrap word -linkcommand ".html import -link"

# načtení HTML stránky
.html import "test.html"

# vložení widgetu do okna
pack .html

# tlačítko pro ukončení aplikace
pack [button .q -command {exit} -text "Konec"]

# finito 
TCL 19 - 8

Obrázek 8: Screenshot osmého demonstračního příkladu

ict ve školství 24

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

V závěrečném pokračování tohoto seriálu si řekneme základní informace o práci se zvuky v Tcl a také o rozsáhlé knihovně Tcllib, která obsahuje velké množství modulů usnadňujících každodenní programátorskou­ práci.

Autor článku

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