From DCSwiki

Uživatelská rozhraní (URO)

Contents

Základní info

Pro splnění předmětu je třeba:

  1. 20 bodů (min. 10): vytvoření všech úloh ze cvičení
  2. 40 bodů (min. 20): projekt v jazyku Tcl/Tk
  3. 40 bodů (min. 20): projekt v jazyku Java (Swing) nebo C++ (Qt)

Celkový získaný počet bodů určuje známku. Za každou část je třeba získat alespoň uvedený minimální počet bodů. V případě jakýchkoli nejasností kontaktujte emailem nebo osobně svého cvičícího nebo Martina Němce (kancelář A1047).

Studijní materiály

Historie

Znalost vzniku a vývoje uživatelských rozhraní není jen zajímavostí, ale dovoluje nám také pochopit mnohé vlastnosti, které nám z dnešního pohledu mohou připadat velmi "podivné" a těžko pochopitelné.

  • As We May Think
    Vannevar Bush
    Velmi známá esej Vannevara Bushe, který již ve 30. letech navrhl stroj Memex, který obsahoval pracoviště se dvěma dotykovými monitory, klávesnici a scannerem.
  • A History of the GUI
    Jeremy Reimer
    Pěkný článek mapující historii grafických uživatelských rozhraní od samého počátku až do roku 2000.

Tcl/Tk

Interpretovaný jazyk podobný C a Lisp. Tcl/Tk čti tickle [tykl].

Nepoužívejte dokumentaci, která je vydána společností ActiveState, neboť není úplná a některé důležité věci tam chybí.

Java

Jednou z nejčastěji využívanou technologií pro tvorbu grafických uživatelských rozhraní je Java. Její výhodou není jenom nezávislost na platformě, ale také možnost vytváření vlastních grafických komponent.

  • Creating a GUI with JFC/Swing
    Tutoriál pocházející přímo z dílny tvůrců prostředí Java -- společnosti Sun. Předpokládá pouze základní znalosti programovacího jazyka Java a umožní vám vstoupit do světa GUI krok po kroku.
  • Visual Editor Project
    Další z nástrojů pro tvorbu uživatelských rozhraní. Tentokrát pro prostředí Eclipse. Velmi pěknou prezentaci práce v prostředí Visual Editoru můžete najít zde.

Cvičení

2008

25.2.-30.5.

1 (25.2.2008)

Jazyk Tcl je stručně popsán na české a anglické Wikipedii. Také vám může pomoci tento popis základních částí jazyka. Stěžejní nejspíš bude oficiální stránka s kompletní dokumentací. Jazyk Tcl je objektový a interpretovaný. Narozdíl od kompilovaných jazyků zde stroj - interpret načítá příkaz po příkazu a provádí jej. Program se tedy nemusí kompilovat do strojového jazyka, tím ale zase běží o něco pomaleji. Je kompatibilní s C, ale má v něčem odlišnou syntaxi (vliv Lispu), takže se to může plést. Hlavně někde jsou mezery vyžadovány, takže jejich nepřítomnost vyvolá chybu.

V Tcl je co řádek to příkaz pro interpret. Pokud chcete na řádek více příkazů, tak je oddělte středníkem. Pokud chcete jeden příkaz na více řádků, tak před konec řádku napište zpětné lomítko \. Následující příkazy si rovnou zkoušejte v programu Wish.

puts a
puts "b b"

Na začátku řádku je příkaz a za ním následují parametry oddělené mezerou. Jediný jednoduchý typ je řetězec a pokud neobsahuje mezery, pak jej nemusíme ohraničovat uvozovkami. Příkaz puts vypisuje to, co je mu předáno 1. parametrem.

puts [expr 0.9 *sin(2.5)]

Hranaté závorky umožňují provedení příkazu (expr) a předání jeho výsledku jako parametr jinému příkazu (puts). expr vyhodnocuje matematické výrazy. Všimněte si, že na rozložení částí výrazu do jednotlivých parametrů je benevolentní.

set a 5
puts $a

I v tomto jazyku můžeme používat proměnné, které vytvoříme pomocí příkazu set. Zde jsme tedy proměnné a přiřadili hodnotu 5 a pak jsme vypsali její obsah. Při použití proměnné je vždy třeba použití dolaru $.

Větší projekty je lépe vytvářet v nějakém zvýrazňovacím textovém editoru. Jazyk Tcl/Tk podporuje např. PsPad. Stáhněte si šablonu programu pro řešení kvadratické rovnice.

#----------------------------------------------------------------------------#
# URO 2008                                              eduard.sojka@vsb.cz  #
#----------------------------------------------------------------------------#
#----------------   Definice prvků a struktury rozhraní   -------------------#
wm title . "Q rovnice"
wm resizable . 1 1
label  .labA -text "a ="
entry  .entA -width 16
label  .labB -text "b ="
button .butVymaz -text "Vymazat" -command vymazat
     
#----------------   Formátování přenecháme příkazu pack   -------------------#
pack .labA 
pack .entA
pack .labB
pack .butVymaz -side bottom

#--------------------   Předvolíme několik detailů   ------------------------#
.entA insert 0 "+1.0"
focus .entA

#----------------------   Funkce pro výmaz zadání   -------------------------#
proc vymazat { } {
}

#-----------------------   Vlastní řešení rovnice   -------------------------#
proc vyresit { } {
}
#----------------------------------------------------------------------------#

Symbolem #, který musí být na začátku řádku, se uvozují komentáře, které jsou interpretem ignorovány. První příkaz label vytvoří v paměti objekt, který uloží pod .labA, a vlastnost text nastaví na hodnotu "a =". Vlastnost se vždy zapisuje s pomlčkou před názvem a mezi tím nesmí být mezera. Objekt .labA pak můžeme vykreslit pomocí předání parametrem příkazu pack. Objekty mají i své metody. Ty voláme tak, že napíšeme nejprve název objektu (.entA) a za něj napíšeme název metody (insert), za kterou napíšeme parametry. V příkladu se tímto na pozici 0 vloží řetězec +1.0 do textu, který je pak viditelný uživatelem. Můžeme také tvořit procedury, jejichž zápis uvedeme příkazem proc a následuje její název. Za názvem následují složené závorky, ve kterých uvedeme seznam parametrů. Za uvozokami musí být mezera a za ní následuje levá složená závorka. Zde pak je tělo procedury, které obsahuje potřebné příkazy a celé je to uzavřeno pravou složenou závorkou. Hodnotu, kterou má procedura vracet, předáme jako parametr příkazu return. Všimněte si vlastnosti command objektu button, kde je uložen příkaz, který se provede, pokud je toto tlačítko zmáčknuto uživatelem. Pokud chcete provést více příkazů, pak je uzavřete do složených závorek.

Za použití příkazů pack, grid a frame přidejte do programu všechny potřebné prvky (tlačítka, editační pole, popisky). Můžete se inspirovat následujícím obrázkem.

Image:Tk_řešení_kvadratické_rovnice.png

Rámce (frame) používáme k rozdělení prostoru na nezávislé oblasti. Tyto oblasti samozřejmě můžeme dále podobně dělit na podoblasti. V našem příkladě stačí pouze dvě nebo tři oblasti.

frame .levy
label .levy.labA -text popis
pack .levy
pack .levy.labA

Takto vytvoříme objekt rámec a v něm vytvoříme objekt nápis. Nesmíme zapomenout vykreslit rámec (.levy) pomocí pack nebo grid, neboť v opačném případě by se vnořené objekty (.levy.labA) nevykreslily. Příkaz pack vykreslí objekty na libovolné místo, aby se nepřekrývaly. Příkaz grid umožňuje vykreslit objekty do neviditelné mřížky.

label .labA -text vlevo
label .labB -text vpravo
grid .labA -row 0 -column 0
grid .labB -row 0 -column 1

První řádek i sloupec je označen nulou. Počet řádků a sloupců není třeba určovat, stejně tak se automaticky zvolí velikost buňek, aby se tam všechny objekty vešly. Není vhodné v rámci daného rámce používat pack i grid zároveň.

2 (3.3.2008)

Nejprve se podíváme na jednoduchou logickou hru Invertor. Pro spuštění si příponu souboru přejmenujte na tcl. Prohlédněte si zdrojový kód. Zvláště si všimněte vnitřku vnořeného cyklu v první proceduře NewGame.

button .game.but${i}${j}

Uvedený kód vytvoří tlačítko s názvem podle obsahu proměnných i a j. Pokud tedy i je 1 a j je 2, pak tento kód vytvoří objekt tlačítko s názvem .game.but12. Takovou fintu nelze použít v běžných imperativních jazycích. V jazyku C nebo Java bychom běžně vytvořili dvourozměrné pole tlačítek. V uvedeném příkladu jsou však indexy tlačítek rovnou součástí názvu.

Nyní dopracujte minule zadaný příklad včetně očekávané funkčnosti. K dokončení vizuální stránky se vám budou hodit následující příkazy a jejich vlastnosti:

  • pack
    • padx (přidá vodorovně prázdné místo kolem objektu v pixelech)
    • pady (přidá svisle prázdné místo kolem objektu v pixelech)
    • fill (vyplní prostor objektem horizontálně (parametr x), vertikálně (y), všude (both) nebo zachování původní velikosti (none))
  • grid
    • padx (přidá vodorovně prázdné místo kolem objektu v pixelech)
    • pady (přidá svisle prázdné místo kolem objektu v pixelech)
    • columnspan (roztáhne objekt přes několik sloupců virtuální mřížky)
    • rowspan (roztáhne objekt přes několik řádků virtuální mřížky)
  • frame
    • bd (určuje šířku ohraničení v pixelech, výchozí 0)
    • relief (určuje druh ohraničení; pro viditelnost nutno nastavit nenulovou šířku, nejlépe šířku 2 px)
  • button
    • width (nastaví šířku tlačítka)
    • height (nastaví výšku tlačítka)

K získání hodnoty napsané uživatelem v objektu entry použijte metodu get. Jednotlivé hodnoty si můžete pro přehlednost uložit do proměnných. Ve výpočtu je vhodné zkontrolovat, zda determinant je nenulový.

if {$d != 0} {
#vypočti výsledek a zobraz
} else {
#informuj uživatele, že výsledek nelze spočítat
}

Kromě nerovnítka != lze použít běžná porovnání: ==, <, >= apod. a vzájemně je lze kombinovat pomocí logických operátorů nebo ||, a &&. Dále se vám také může hodit cyklus.

while {$a != ""} {
#tělo cyklu
}

Pro změnu textu objektu label použijte jeho metodu configure, kde jako 1. parametr zadáte -text a druhý parametr je pak nový text popisku. Pro spojení několika řetězců do jednoho použijte příkaz concat, kterému jako parametry předáte jednotlivé řetězce.

entry .e
.e insert 0 "tohle stejně nebude vidět"
.e delete 0 end

Nyní už byste měli být schopni sestavit výsledný program. Další poznámky k programu jsou doplňkové a není tedy nutné je implementovat. Doporučuji si před výpočty výsledků zkontrolovat, zda se nedělí nulou. Pokud totiž uživatel zadal správně čísla, pak by měl program vybrat jednu z 5 výpočetních větví a vypsat příslušné výsledky či hlášení. Ideálně by program měl kontrolovat správný tvar zadaných čísel třeba pomocí regulárních výrazů (regexp). Uživatel uvidí výsledky, ale nemůže si je zkopírovat do jiného programu, proto by bylo nanejvýš vhodné použít pro zobrazení výsledků objekt entry.

bind .
 vymazat
Příkaz '''bind''' provede v hlavním okně příkaz (vymazat), jakmile je stisknuto tlačítko uvedené ve špičatých závorkách. Lze samozřejmě provést více příkazů a to tak, že je uzavřeme do složených závorek. Stejně se to dá provést u vlastnosti '''command''' objektu '''button'''. Kromě kláves lze také zachytávat různé události jako třeba '''Destroy'''.
 
 if {[catch {expr 4/0} err]} {
 #proveď při dělení nulou, můžeš použít $err
 }
Tento kód zachytí chybu při vykonávání skriptu (expr) a případně provede vnitřek podmínky. Do proměnné '''err''' se uloží text chyby.
 error "Text chyby"
Příkazem '''error''' se vyvolává chyba.
 
=== 3 (10.3.2008) ===
Dnes byste měli mít grafický návrh vašeho prvního projektu na papíře a po vypracování druhého úkolu ze cvičení byste už měli na něm začít pracovat.
 
Nyní se pustíme do tvorby jednoduché kalkulačky, která by měla obsahovat kromě základních numerických operací i možnost změny velikosti písma. Můžete se inspirovat nejen následujícími obrázky, ale i kalkulačkou obsaženou v systému Windows.
 
[[Image:Kalkulačka.jpg]]   [[Image:Kalkulačka_velká.jpg]] 
 
Pro začátek použijte následující šablonu.
 
 wm title . "Calculator"
 wm resizable . 0 0
 set myfont [font create -family {Helvetica} -size 10 -weight normal -slant roman]
 
 label .en -font $myfont -width 10 -anchor e -relief sunken -bd 4 -background white -textvariable visNum
 frame .opts -relief groove -bd 2
   radiobutton .opts.normal -text Normal -font $myfont -var appar -val "normal" -command normalform
 frame .opbts
   button .opbts.add  -text + -font $myfont -width 3 -foreground #0050d0 -command bAdd
 frame .numbts
   button .numbts.b0  -text 0 -font $myfont -width 3 -command "insKey 0"
 
 #-------------   Formátování přenecháme příkazům pack a grid   ----------------#
 pack .en -side top -fill x
 pack .opts -fill x -pady 2
   pack .opts.normal -side left -padx 2 -pady 2
 pack .opbts -side right -padx 2 -pady 2
   pack .opbts.add -padx 2 -pady 2
 pack .numbts -padx 4 -pady 2
   grid .numbts.b0 -row 3 -column 0 -padx 2 -pady 2
 
 #------------------------------------------------------------------------------#
 .opts.normal select
 
 proc normalform {} {
 }
 
 proc bAdd {} {
 }
 
 proc insKey {key} {
 }
 
S objektem '''.en''' je svázána proměnná '''visNum'''. Pro změnu textu objetu '''.en''' nám tedy stačí změnit obsah proměnné '''visNum'''. Tato proměnná je však globální, proto pro její použití v proceduře je třeba použít příkaz '''global'''. V opačném případě se vytvoří lokální proměnná stejného jména, což nezmění text objektu '''.en'''.
 
 proc insKey {key} {
   global visNum
   set visNum $key
 }
 
Pro spojení dvou řetězců můžete použít příkaz '''concat''' nebo příkaz '''append'''. Druhý jmenovaný však jako první parametr bere název proměnné a druhý parametr je text, který se připojuje k němu. Všimněte si implementace těchto příkazů.
 
 proc append {var str} {
   set $var [puts ${$var}$str]
 }
 proc concat {a b}{
   puts "$a $b"
 }
 
Pomocí metody '''create''' příkazu '''font''' už umíme vytvořit vlastní font a uložit si jej do své proměnné (viz šablona výše). Pokud však příkazu '''font''' předáme jako 1. parametr '''configure''' a jako druhý prametr název fontu (neplést s názvem proměnné, která uchovává název fontu), pak můžeme měnit vlastnosti fontu stejně, jako jsme je definovali při vytváření fontu.
 
 set myfont [font create]
 font configure $myfont -size 10
 
Vytvoření nového okna sice možná nebudete potřebovat pro vaši kalkulačku, ale bude se vám určitě hodit pro 1. projekt.
 
 toplevel .mojeOkno
 wm title .mojeOkno "Název mojeho okna"
 button .mojeOkno.b1
 destroy .mojeOkno
 
Zde jsem vytvořil nové okno, které jsem označil '''MojeOkno''', nastavil jsem mu titulek, vložil (nezobrazil) do něj tlačítko a nakonec je okno zrušeno. Pokud chceme něco provést těsně před zrušením okna, pak to můžeme provést pomocí svázání události '''Destroy''' daného okna s konkrétním skriptem.
 
 bind .mojeOkno <Destroy> {UlozStav; DejSiKafe}
 
Hlavní (výchozí) okno je označeno prázdným slovem ('''""'''), takže pokud se na něj odkazujeme, tak použijeme jen tečku. Pokud do hlavního okna vkládáme objekty, pak neuvádíme název okna.
 
 button ..b1
 button .b2
 pack ..b1
 pack .b2
 
Kupodivu bude fungovat první řádek, přičemž se vytvoří tlačítko, ale s adresou '''.b1'''. Třetí řádek však nahlásí chybu. Nakonec se podíváme na možnosti výběru barvy.
 
 tk_ChooseColor -title "Výběr barvy" -initialcolor "#07F85A"
 tk_setPalette "#F0F000"
 
První příkaz spustí okno, kde si uživatel může vybrat barvu. Pokud stiskne '''OK''', tak výsledkem příkazu je vybraná barva. Pokud stiskne '''Storno''', pak výsledkem je prázdné slovo. Druhý příkaz nastavuje barvu oken.
 
=== 4 (17.3.2008) ===
Dnes se naučíme tvořit nabídku (menu), zkusíme jednoduchou práci se soubory a využijeme plochu okna pro zobrazení obrázku. Vše to použijeme v dalším příkladu, jehož šablonu si stáhněte.
 
 #------------------------------------------------------------------------------#
 # URO 2008, 4. cvičení                                                         #
 #------------------------------------------------------------------------------#
 wm title . "Popis fotografií"
 wm resizable . 0 0
 #----------------------   Definice vzhledu rozhraní   -------------------------#
 frame .m -relief raised -borderwidth 2
 menubutton .m.file -text "Soubory" -menu .m.file.menu
 menu .m.file.menu -tearoff 0
   .m.file.menu add command -label "Otevřít obrázek" -underline 0 -command openimage
   .m.file.menu add separator
 labelframe .f1 -text " Co je na obrázku: " 
   text .f1.tx -width 30 -height 4
 labelframe .f2 -text " Chyby: " -relief groove
   checkbutton .f2.c1 -text "Kompozice" -variable co
 #-------------------   Vykreslení a formátování rozhraní   --------------------#
 pack .m -fill x
   pack .m.file -side left -padx 1m
 pack .f1 -padx 4 -pady 4
   grid .f1.tx -row 0 -column 0 -padx 2 -pady 2
 pack .f2 -padx 4 -pady 4 -side left
   pack .f2.c1 -anchor w -padx 2 -pady 0
 #--------------------------------   Příkazy   ---------------------------------#
 proc openimage { } {
   global fileName
   set types {{"Image files" {. gif .jpg .bmp}} {"All files" *}}
   set fileName [tk_getOpenFile -filetypes $types -parent .]
   image create photo img1 -file $fileName
   toplevel .w1
   wm resizable .w1 0 0
   wm title .w1 "Foto"
   # canvas .w1.fr.ca -width [image width img1] -heigh [image height img1]
 }
 proc closeimage { } { 
   destroy .w1
 }
 proc savecomments { } {
 }
 #------------------------------------------------------------------------------#
 
Můžete se inspirovat následujícím řešením.
 
[[Image:Popis_obrazku_Tcl.png]]
 
Při pohledu na šablonu si jistě všimnete nového objektu '''menu'''. Nejprve se vytvoří rámec ('''frame'''), který opticky oddělí nabídku od zbytku okna. Do tohoto rámce se vloží položka nabídky '''Soubor''' ('''menubutton''') a do rozbalovací nabídky se vloží položka '''Otevřít obrázek'''. Podobně se tam dá vložit oddělovač pro přehlednost. Důležité je u tlačítka nabídky uvést odpovídající vysunovací nabídku - vlastnost '''menu'''. Samozřejmě nesmíme zapomenout na vykreslení nadefinovaných objektů. Vlastnost '''tearoff''' určuje, zda má být na prvním místě vysouvací nabídky přerušovaná čára. Kliknutím na tuto čáru se pak daná nabídka objeví ve vlastním okně. 
 
 checkbutton .c -variable c
 proc konec {} {
   global c
   if {$c} {
     exit
   }
 }
 
Za zmínku rozhodně stojí proměnná přiřazená zaškrtávátku ('''checkbutton''') pomocí vlastnosti '''variable'''. Obsah proměnné je '''0''' nebo '''1''', podle zaškrtnutí. V příkladě výše je tedy program ukončen, pokud je spuštěna procedura '''konec''' a zároveň musí být zaškrtávátko zaškrtnuto.
 
 proc about{ } { tk_messageBox-title About -message "URO 2008" -type ok -icon info -parent . }
 
Takto jednoduše lze zobrazit okno s informativním textem a jedním tlačítkem.
 
 set file [open "text1.txt" w]
 puts $file "Zapiš do souboru"
 close $file
 
Podle příkazů je zřejmé, že tento malý příklad pracuje se souborem. '''open''' otevře soubor pro zápis ('''w'''). Pokud není uvedena celá cesta, pak je výchozím adresářem zvolen váš domovský adresář. Příkaz '''puts''' díky prvnímu parametru zapisuje do souboru. Z důvodu konzistence souboru je vhodné jej také uzavřít pomocí '''close'''.
 
 canvas .ca -width 30 -heigh 20
 pack .ca
 image create photo "image1" -file "image.gif"
 .ca create image 1 2 -anchor nw -image "image1"
 
Uvedený kód vytvoří objekt plátno ('''canvas'''), nastaví mu zadané rozměry a zobrazí ho. Dále se načte obrázek ze souboru, který se uloží do proměnné '''image1'''. Nakonec se na plátno vloží načtený obrázek s horizontálním posunem '''1''' a vertikálním '''2''' a na tento bod se napasuje levý horní roh obrázku ('''nw''' vlastnosti '''anchor'''). Plátnu není nutné nastavovat velikost, protože ta se automaticky přizpůsobí načtenému obrázku.
 
=== 5 (24.3.2008) ===
Java Swing
...
 
=== 6 (31.3.2008) ===
'''Poslední termín možný pro předvedení a odevzdání 1. projektu!'''
=== 7 (7.4.2008) ===
=== 8 (14.4.2008) ===
=== 9 (21.4.2008) ===
 
=== 10 (28.4.2008) ===
=== 11 (5.5.2008) ===
=== 12 (12.5.2008) ===
=== 13 (19.5.2008) ===
'''Odevzdání projektu a příkladů z Javy!'''
 
=== 14 (26.5.2008) ===
 
= Úlohy ze cvičení =
Úlohy, které jsou zadány na cvičení, vypracujte a budou kontrolovány v libovolném cvičení, nejpozději však s projektem stejného jazyka.
== 2008 ==
=== Tcl/Tk ===
 
*Výpočet kvadratické rovnice (2 body)
*Kalkulačka (3)
*Popis obrázků (3)
 
=== Java ===
*Převodník teploty (2)
*Kalkulačka (2)
*Malování (2)
*Seznam lidí (2)
 
=== C++ ===
*Převodník teploty (2)
*Kalkulačka (2)
 
= Projekty =
Za každý projekt a úkol jsou body, jejich součet pak tvoří známku. Projekty budou prezentovány autorem na cvičení a bude vyžadována orientace ve zdrojovém kódu při předvádění.
 
Projekt '''musí''' splňovat:
*projekt má smysluplný účel
*skládá se z minimálně 3 oken (okno "o autorovi" apod. se nepočítá)
*vhodné a intuitivní rozložení objektů v oknech
*vhodné textové popisky
*zdrojový kód:
**bude mít hlavičku (autor, login, rok, předmět) a bude okomentován
**bude přeložitelný a spustitelný při předvádění
 
Projekt '''nemusí''' splňovat:
*komunikace s internetem nebo jinými programy
*použití složitých datových struktur
*(efektivní) implementace (otevírání a zavírání oken musí fungovat; další implementace je kladně hodnocena)
*použití funkční databáze pro data
 
== Tcl/Tk ==
Poslední termín odevzdání je 6. cvičení. Příklady ze cvičení v Tcl/Tk odevzdejte zároveň s projektem nebo dříve.
 
== Java ==
Předběžný poslední termín odevzdání je poslední cvičení v semestru (zápočtový týden). Příklady ze cvičení v jazyku Java odevzdejte zároveň s projektem nebo dříve.
 
= Časté dotazy (FAQ) =
Zde budou zveřejněny otázky a odpovědi týkající se tohoto předmětu. Dotazy zasílejte na email sru048 na serveru vsb.cz.
== Mohou být uznány projekty z loňska? ==
Ano.
 
== Mohu v obou jazycích zpracovat stejný projekt? ==
Ano.
 
== Může se pracovat ve skupině? ==
Projekty lze vypracovat buď individuálně nebo ve dvojici. Přičemž při práci ve dvojici je třeba, aby projekt byl většího rozsahu, a oba autoři se musí orientovat v celém zdrojovém kódu.