Praktikum 7
Graafika. Graafiline kasutajaliides.
Teemad
JavaFX. Stseenigraaf. Elementide struktuur ja paigutamine.
Pärast selle praktikumi läbimist oskab üliõpilane
- JavaFX abil luua lihtsamaid graafilisi programme;
- koostada stseenigraafe;
- tuua näiteid geomeetrilise abstraktsionismi teostest;
- kirjeldada mõnda lippu ja liiklusmärki.
Sissejuhatus ja JavaFX paigaldamine
Seni on sellel kursusel põhiliselt käsitletud programme, kus tulemused ilmuvad tekstilisel kujul ja suhtlemine kasutajaga on olnud suhteliselt askeetlik. Selles ja järgmises praktikumis käsitletakse erinevaid graafilisi võimalusi, mille abil saab programme oluliselt värvilisemaks ja interaktiivsemaks teha. Põhimõtteliselt on selles vallas muidugi väga palju ja erinevaid võimalusi, siin piirdutakse paljus vaid sissejuhatusega, mille abil aga siiski midagi on võimalik juba ära teha.
Ajalooliselt on Javas graafika (graafiliste kasutajaliideste) programmeerimiseks olnud mitmeid võimalusi. Põhiliselt on need koondatud pakettidesse AWT
ja Swing
. Paraku on nende võimaluste struktuur ja loogika kohati suhteliselt raskesti hõlmatavad ja nii on püütud luua mugavamaid vahendeid. Käesolevas kursuses on selle temaatika aluseks teek (õieti teekide komplekt) JavaFX
, mis tundub saavat üldisemaks standardiks. Võimalusi on JavaFX puhul ka muidugi palju, aga siin vaadeldakse vaid osasid - lootuses, et huvilised ise saavad edasi minna. Ilmselt on mitmetes kohtades erinevaid võimalikke lahendusi ja praktikumimaterjalides toodu ei pruugi kõige optimaalsem olla.
JavaFX kasutamiseks soovitame kasutada Java versiooni 10 või vanemat. Uuemad Java versioonid ei paigalda enam automaatselt JavaFX ja see tuleb eraldi seadistada. Java11+JavaFX kasutamiseks vajalikud lisasammud on kirjeldatud siin: https://openjfx.github.io/openjfx-docs/
JavaFX-ga mugavamaks töötamiseks on IDEdes mitmed vahendid sisse ehitatud. IntelliJ-ga tuleb JavaFX plugin automaatselt kaasa (kontrolli, et see oleks sisse lülitatud: Settings -> Plugins
). Eclipse jaoks on olemas pistikprogramm E(fx)clipe
, mille paigaldamiseks on siin installeerimisjuhend
. Pärast E(fx)clipe
paigaldamist saad Eclipse-s JavaFX projekti luua järgnevate valikutega: File - New - Other - JavaFX - JavaFX Project
. Uue rakenduse (application) loomist saab nüüd alustada valikuga File - New - Other - JavaFX - Classes - JavaFX Main Class
.
IntelliJ-s JavaFX projekti loomiseks hakka tavalist uut projekti looma (New -> Project
), aga vali avanenud dialoogis vasakul servas loetelust Java asemel Java FX. Uue JavaFX peaklassi loomiseks saab menüüst valida File -> New -> JavaFXApplication
.
Tegelikult on projekti loomisega juba klass nimega Main
olemas. JavaFX peaklass on tavaline klass, mille ülemklass on javafx.application.Application
. IDE menüüvalikud iseenesest midagi maagilist ei tee. Käesolevas juhendis aga alustatakse uuest klassist, mille nimeks on MustRuut
, sest see on alguses peaaegu tühi.
Esimesed graafilised programmid
Ilmunud klassi alge on üldise loogika ja struktuuri mõttes küllaltki kõnekas.
import javafx.application.Application; import javafx.stage.Stage; public class MustRuut extends Application { @Override public void start(Stage primaryStage) { } public static void main(String[] args) { launch(args); } }
Programmi täitmine algab (nagu ikka on alanud) meetodist main
. Selles käivitatud meetod launch
algatabki rakenduse töö, milles on väga olulisel kohal meetod start
, millest nö. kogu tegevus stardibki. See meetod on abstraktsena klassis Application
, mille alamklass iga JavaFX rakendus ongi. Igal konkreetsel juhul tuleb meetod start
üle katta. Meetodi argumendiks on Stage
-tüüpi objekt. Käesolevas materjalis nimetatakse Stage
-tüüpi objekti "lavaks", kuna teatriteemalise terminoloogia kasutamine tundub küllaltki tabav. Edasi on vastavalt muudetud ka meetodi parameetri nime.
public void start(Stage peaLava)
Meetodi start
sees luuakse üks Group
-tüüpi isend, mis toimib juurena. Loodav ristkülik (Rectangle
-tüüpi isend) on see, mida tegelikult näha tahetakse. Ristkülik lisatakse juure alluvaks. Seejärel luuakse Scene
-tüüpi isend (stseen). Lava tiitelribale pannakse tekst (meetodiga setTitle
), stseen paigutatakse lavale (meetodiga setScene
) ning lõpuks tehakse lava nähtavaks (meetodiga show
).
public void start(Stage peaLava) { Group juur = new Group(); // luuakse juur Rectangle ristkülik1 = new Rectangle(50, 50, 435, 435); juur.getChildren().add(ristkülik1); // ristkülik lisatakse juure alluvaks Scene stseen1 = new Scene(juur, 535, 535, Color.SNOW); // luuakse stseen peaLava.setTitle("Must ruut"); // lava tiitelribale pannakse tekst peaLava.setScene(stseen1); // lavale lisatakse stseen peaLava.show(); // lava tehakse nähtavaks }
JavaFX programmide tegemisel tuleb üsna tihti klasse importida. Seejuures võib IDE anda võimaluse valida mitme sama nimega klassi vahel. Tõenäoliselt on õige see valik, mille juures on paketi nimes javafx mainitud, näiteks ristküliku puhul import javafx.scene.shape.Rectangle;
.
Ülesanne 1.
Proovige see programm tööle saada.
Küllaltki sarnaseid pilte tehti ka sadakond aastat tagasi. http://www.wikiart.org/en/kazimir-malevich/black-square-1915
Stseenigraaf ja elementide struktuur
Käeolevas materjalis nimetatakse Scene
-tüüpi objekti "stseeniks". (Siin võiks ka variandid "lavapilt" või "kuliss" kõne alla tulla, aga tõenäoliselt on "stseen" sobivam.) Stseen ongi konteineriks, kuhu siis erinevad nähtavad (ja nähtamatud) elemendid paigutatakse. Stseen ise aga pannakse lavale (meetodi setScene
abil) ja sellega üldse ongi võimalik, et midagi rakenduse sees paistma hakkaks. Stseeni sisemine loogika määratakse stseenigraafi (scene graph) abil. Kasutusel ongi graafide (just puude) terminoloogia: tipp (node), juurtipp e. juur (root node), vahetipp (branch node), lehttipp e. leht (leaf node), ülemus (parent), alluv (child). Üldine skeem võib olla näiteks selline.
Stseeni loomisel antakse argumendina juur, mis on Parent
-tüüpi (või tegelikult Group
-, Region
- või WebView
-tüüpi või mingi nende alamklassi) isend. Hiljem lisatakse sellele alluvaid. Näiteks oli eelnevas näites juurele lisatud alluvaks ristkülik. Konkreetsele tipule alluvate tippude loendi saab meetodi getChildren
abil. Sellesse saab elemente lisada meetodi add
abil. (Nii oli ka klassi ArrayList
puhul ja on üldse listide puhul, on ju ObservableList
liidese List
alamliides.)
Stseenigraaf on sel juhul väga lakooniline. Enam väiksem ei saakski olla, kuna klassi Rectangle
isend ise juureks ei sobi, sest see klass pole klassi Parent
alamklass.
Kui tulla tagasi ermitaaži ja maalimise juurde, siis aastaid tagasi ja praegugi maalivad kunstnikud maale lõuendile. Lõuend (canvas) on olemas ka JavaFX-s - klassina Canvas
.
Näiteks musta ruudu joonistamine lõuendile:
public void start(Stage peaLava) { Group juur = new Group(); // luuakse juur Canvas lõuend = new Canvas(535, 535); // luuakse lõuend GraphicsContext gc = lõuend.getGraphicsContext2D(); // graafiline sisu gc.fillRect(50, 50, 435, 435); // ruut juur.getChildren().add(lõuend); // lõuend lisatakse juure alluvaks Scene stseen1 = new Scene(juur); // luuakse stseen peaLava.setTitle("Must ruut"); // lava tiitelribale pannakse tekst peaLava.setScene(stseen1); // lavale lisatakse stseen peaLava.show(); // lava tehakse nähtavaks }
Ülesanne 2. (Kontroll)
Tehke klassi Canvas
abil üks (vähemalt 3 värviga või mingi keerulisema kujundiga) riigilipp (http://www.flags.net/). Vajalikud näpunäited saab http://docs.oracle.com/javase/8/javafx/graphics-tutorial/canvas.htm.
Järgnevad täiendused on mõeldud musta ruudu programmi muutmiseks. Muidugi saab ekraanile lisada mitmesuguseid teisi elemente
. Näiteks nupu saab lisada start
-meetodit vastavalt täiendades.
Button nupp1 = new Button("Olen nõus"); // luuakse nupp nupp1.setLayoutX(100); // nupu paigutamine x-koordinaadi mõttes nupp1.setLayoutY(60); // nupu paigutamine y-koordinaadi mõttes juur.getChildren().add(nupp1); // nupp lisatakse juure alluvaks
Ülesanne 3.
Lisage see lõik sobivasse kohta programmis. Kas nupp ilmus? Kui mitte, siis püüdke see ikkagi välja paistma saada.
Efektid ja animatsioon
JavaFX pakub suure hulga meetodeid, mis erinevate graafikaelementidega manipuleerida aitavad. Päris palju neist on juba klassis Node
toodud, aga spetsiifilisemad meetodid on alamklassides. Edasi on nüüd proovitud meetodit setEffect
, mis võimaldab erinevaid efekte esile tuua. Antud juhul on nupule pandud roheline vari.
DropShadow vari = new DropShadow(20, Color.GREEN); vari.setOffsetX(20); nupp1.setEffect(vari);
Ülesanne 4. (Kontroll)
Proovige ise mõnda teist efekti. Vaadake mõningaid näiteid.
Tulles tagasi stseenigraafi juurde, siis on praegu meetodeid rakendatud pigem lehttippude puhul. Juure korral on seni rakendatud ainult alluvate lisamist. Järgmistes näidetes püütakse just juurega manipuleerida. Näiteks juur.setVisible(false)
muudab nähtamatuks juure koos alluvatega, antud juhul siis ristküliku ja nupu. Edasi on üks animatsiooni näide.
FadeTransition ft = new FadeTransition(Duration.millis(10000), juur); // luuakse uus haihtumine ft.setFromValue(1.0); // määratakse algväärtus (1.0 - täiesti selge) ft.setToValue(0.0); // määratakse lõppväärtus (0 - täiesti haihtunud) ft.setCycleCount(Timeline.INDEFINITE); // lõpmatu tsüklite arv ft.setAutoReverse(true); // lõppu jõudes tagasi, algusest jälle edasi ft.play(); // animatsioon mängima
Ülesanne 5. (Kontroll)
Lisage mõni uus kujund (mõnest klassi Shape
alamklassist) ja proovige ise klassi PathTransition
abil kujundite liigutamist. Samuti püüdke mitut animatsiooni korraga (Parallel Transition
) ja üksteise järel (Sequential Transition
) rakendada.
Abiks võiks olla http://docs.oracle.com/javase/8/javafx/visual-effects-tutorial/animations.htm.
Elementide paigutamine
Graafilise kasutajaliidese puhul on oluline elementide paigutus. Kui eespool kasutati stseenigraafi juurena klassi Group
isendit, siis paigutuse korraldamiseks on mõistlik juure või vahetipuna kasutada klassi Region
(või selle mingi alamklassi) isendit. Näiteks klassi BorderPane
abil saame määrata, kas konkreetne element lehttipuna (või vahetipuga määratud elementide rühm) asub ülaservas, alaservas, vasakul, paremal või keskel. Järgnevas näites on salasõnaväli, liugur ja silt paigutatud vastavalt ülaserva, keskele ja alaserva.
public void start(Stage peaLava) { BorderPane piiriPaan = new BorderPane(); PasswordField salasõnaVäli = new PasswordField(); Slider liugur = new Slider(0, 4, 0.5); Label silt = new Label("Silt"); piiriPaan.setTop(salasõnaVäli); // ülaserva piiriPaan.setCenter(liugur); // keskele piiriPaan.setBottom(silt); // alaserva Scene stseen1 = new Scene(piiriPaan, 400, 400, Color.SNOW); peaLava.setTitle("Paigutus"); peaLava.setScene(stseen1); peaLava.show(); }
Veel üks paigutamiste näide:
public void start(Stage peaLava) { FlowPane flow = new FlowPane(); TextField tekst = new TextField("mingi tekst"); flow.getChildren().add(tekst); ListView<String> list = new ListView<String>(); ObservableList<String> items =FXCollections.observableArrayList ( "Esimene", "Teine", "Kolmas", "Neljas"); list.setMaxHeight(100); list.setItems(items); flow.getChildren().add(list); BorderPane border = new BorderPane(); border.setMinWidth(250); Button nupp1 = new Button("1"); border.setLeft(nupp1); Button nupp2 = new Button("2"); border.setRight(nupp2); HBox hbox = new HBox(); Label silt1 = new Label("silt1"); Label silt2 = new Label("silt2"); hbox.getChildren().addAll(silt1, silt2); border.setCenter(hbox); flow.getChildren().add(border); Scene stseen1 = new Scene(flow, 250, 150, Color.SNOW); peaLava.setScene(stseen1); peaLava.show(); }
Sellele aknale vastav stseenigraaf on järgmine:
Ülesanne 6.
Lisage veel erinevaid elemente ja paigutus teha nii, et ühe klassi BorderPane
isendiga määratud piirkonna (nt. Center
) sees on elemendid paigutatud klassi GridPane
isendi abil. Abiks võivad olla http://docs.oracle.com/javase/8/javafx/layout-tutorial/builtin_layouts.htm ja http://docs.oracle.com/javase/8/javafx/user-interface-tutorial/ui_controls.htm.
Ülesanne 7. (Kontroll)
Koostage programm, mis kuvab mingi (eba)reaalse seadme, skeemi, kunstiteose vms. Näiteks bussipeatuse või spordivõistluse tabloo, ristmiku skeem, geomeetrilise abstraktsionismi stiilis maali reproduktsioon ...
Ebareaalsuse võib sisse tuua näiteks animatsiooniga - korvpallitablool isikliku vea märgis lendab vahepeal ringi vms.
- Stseenigraaf peab olema vähemalt nelja tasemega.
- Paigutust tuleb reguleerida klassi
Pane
mingi alamklassi isendi abil. - Midagi peab olema animeeritud.
Ise ülesande väljamõtlemine ei pruugi olla lihtne, aga püüdke siiski. Ühiskond vajab loovat lähenemist! Kui tõesti ühtegi enda mõtet pole, tehke üks liiklusmärk (liiklusseaduses määratud liiklusmärke saab vaadata https://www.riigiteataja.ee/akt/103032011006 lisadest), millel näiteks nool liigub või mingid teised osad muutuvad vms.