-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathkapitel_05.tex
2081 lines (1781 loc) · 88.4 KB
/
kapitel_05.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\ospchapter{Qt Quick und QML}{Qt Quick und QML}{chap:qtquick}
\index{Qt Quick}%
Mit Qt Quick hat Nokia 2009 ein Framework vorgestellt, das die
zentrale Rolle bei der Erstellung von Qt-GUIs einnehmen soll. Die
Programmierung erfolgt deklarativ: Statt wie bei der imperativen
Programmierung den Ablauf bzw. Algorithmus in Code zu implementieren,
beschreibt der Entwickler lediglich Zustände, Zustandsübergänge,
Ereignisse usw. unabhängig von einer bestimmten
Implementierungslogik.
Die deklarative Sprache, QML, stellt dem Entwickler Bausteine zur
Verfügung, die durch eigene Kombinationen und im Zusammenspiel mit C++
(oder Python) den GUI-Entwurf weitgehend von der Programmlogik
abkoppelt. Das ist auch die Idee von Qt Quick: GUI und Programm so
weit wie möglich voneinander zu trennen, um in größeren Projekten die
GUI-Designer unabhängig von den Code-Entwicklern zu machen oder auch
einfach nur den Code der gesamten Anwendung besser wartbar zu halten.
Zu Beginn der Entwicklung kam hinzu, dass die vorhandenen Qt Widgets
nicht zu den mobilen Nokia- und Intel-Geräten passten. Die
deklarative Erstellung der GUIs per QML erlaubt die Unterstützung von
Gesten, Multitouch und Ähnlichem zur Steuerung der Oberfläche.
Trotz der Einstellung von MeeGo und dem Rückschlag für die
Qt"=Entwicklung für mobile Geräte ist Qt Quick nach wie vor die Zukunft
von Qt. Auch wenn sich einige Anforderungen geändert haben,
Touch-Interfaces werden Tastatur und Maus weitgehend ablösen, auch auf
den schon bestehenden Qt-Plattformen. Während diese Zeilen geschrieben
wurden, ist schon mehr oder weniger beschlossen, dass die Qt Widgets
ab Qt Version 5 nicht mehr weiter entwickelt werden. Zwar werden
weiterhin Bugs mit bestimmten Prioritäten behoben, eine
Weiterentwicklung von GUI-Komponenten wird aber nur noch auf Basis von
Qt Quick stattfinden. Zwar gibt es derzeit noch einige Lücken bei der
Erstellung von Desktop-Anwendungen mit Qt Quick. Dennoch soll dieses
Kapitel schon einmal so weit in die Programmierung mit QML einführen,
dass Sie erste Schritte bei der Erstellung von Oberflächen der
nächsten Qt-Generation machen können. Zunächst geben wir einen groben
Überblick über die Architektur sowie fertige und noch in Entwicklung
befindliche Tools von Qt Quick. Anschließend gehen wir näher auf die
Programmierung mit QML ein, bevor es um die Anschlussmöglichkeiten von
QML-GUIs an Python-Code unter PyQt und PySide geht.
\ospsection{Überblick über Qt Quick}{Überblick über Qt Quick}{sec:qtquickoverview}
\index{Qt Quick!UI Runtime}%
\index{Qt Designer}%
\index{QML}%
\index{QML!QML-Viewer}%
Auch wenn das Qt-Quick-Framework noch häufigen Veränderungen
unterliegt, steht die Architektur, und es lassen sich schon jetzt mit
Qt Quick Anwendungen entwickeln. Das Framework besteht aus drei
Komponenten: der Programmiersprache QML für deklaratives
Programmieren, der Qt Quick UI Runtime zum Ausführen und Darstellen
von QML-Programmen und zur Interaktion mit anderen Programmiersprachen
wie C++ und Python sowie den IDEs und Gestaltungswerkzeugen Qt Creator
und Qt Designer. Da der Qt Creator derzeit vor allem für die
Entwicklung von C++-Anwendungen vorgesehen ist, soll er hier nicht
weiter besprochen werden. Als bisher einzige IDE bietet er zwar
Syntax-Highlighting und Code-Vervollständigung für QML, fügt sich aber
nur schlecht in die Abläufe der PyQt- oder PySide-Projektentwicklung
ein.
Wir werfen stattdessen zunächst einen Blick auf das deklarative
Programmieren mit QML. Anschließend stellen wir mit dem QML Viewer
eine wichtige Komponente der Runtime vor. Er erlaubt die Ausführung
reiner QML-Anwendungen ohne C++ oder Python. Damit lassen sich GUIs
schon einmal testen und schnell Prototypen entwickeln. Schließlich
wird mit dem Qt Quick Designer derzeit ein grafisches Werkzeug für die
Erstellung von QML-Oberflächen entwickelt und aktuell als Teil des
Qt-SDKs vertrieben. Gerade hierbei handelt es sich aber noch um eine
große Baustelle, so dass wir im Weiteren auf die Beschreibung und den
Einsatz des Qt Designers für die QML-Code-Erstellung verzichten.
\ospsubsection{Deklaratives Programmieren mit QML}{Deklaratives Programmieren mit QML}{sec:declarativeprogramming}
\index{QML}%
\index{Deklaratives Programmieren}%
\index{GUI}%
Deklaratives Programmieren lässt sich als Pendant zum imperativen
Programmieren auffassen. Beim imperativen Programmieren in einer
Programmiersprache wie Python wird die Aufgabe des Programms
algorithmisch beschrieben, d.\,h. der Entwickler stellt mit den
Mitteln der Programmiersprache die Logik des Programmablaufs dar und
nutzt dazu Schleifen, Variablen, Funktionen usw.
Im Gegensatz dazu \emph{deklariert} der Entwickler beim deklarativen
Programmieren einzelne Zustände oder Zustandsübergange eines
Programms. Die Übergänge werden nicht algorithmisch beschrieben, es
werden lediglich deren Eigenschaften definiert. Die eigentliche
\emph{Implementierung} zur Darstellung von Zuständen und Änderungen
ist vollkommen unabhängig von deren Deklaration und wird
beispielsweise von einer Runtime-Umgebung zur Verfügung gestellt.
Die Qt-Entwickler haben sich für die Zukunft von Qt zu einem
deklarativen Ansatz bei der Erstellung von GUIs entschieden, um deren
Entwicklung unabhängiger von der Implementierung der eigentlichen
Programmlogik zu machen. Das macht Qt-Quick-Projekte generell besser
wartbar und den Oberflächen-Design-Prozess unabhängiger vom
Entwickler. So kann ein Grafiker beispielsweise schon das GUI
inklusive Interaktion mit dem Anwender vollständig als Prototyp
entwerfen, ohne dass dazu auch nur eine Codezeile aus der
Entwicklungsabteilung notwendig ist. Ähnliche Ansätze verfolgen auch
Microsoft in der Silverlight-Entwicklung oder Adobe mit Flash und AIR.
Qt Quick hängt diesen Frameworks noch deutlich hinterher: Vor allem
die Design-Werkzeuge können es noch nicht mit der Konkurrenz
aufnehmen, auch wenn beispielsweise schon Export-Möglichkeiten aus
Photoshop zur Erzeugung von Qt-Quick-Komponenten bestehen.
Da die Möglichkeiten einer rein deklarativen Programmierung stark von
der Runtime abhängig sind, hat man sich bei Qt Quick zu einem hybriden
Ansatz entschieden: So lassen sich QML-Anwendungen mit Javascript
mischen, um dann doch auch Teile der Oberfläche mit einer imperativen
Logik zu versehen. Javascript war eine naheliegende Wahl, da sich
diese Programmiersprache durch den Einsatz in Webbrowsern massiv
verbreitet hat und sich rasant weiterentwickelt hat. Außerdem hat sich
diese Sprache gerade auch im Zusammenspiel mit HTML, das ja auch im
weitesten Sinne als \dqo{}deklarativ\dqc{} bezeichnet werden kann,
entwickelt, so dass den Javascript-Entwickler beim Ansprechen von
QML-Objekten per Javascript kein größerer Lernprozess erwartet. Im
Gegensatz zu HTML ist QML jedoch deutlich grafischer orientiert und
stellt eine Reihe von grafischen Primitiven bereit. Hinzu kommen
Zustände und Zustandsübergänge als grundlegende Sprachelemente.
Wie aber sieht nun das \dqo{}deklarative Programmieren\dqc{} in Qt
Quick genau aus? Da es sich um eine Sprache für Oberflächen handelt,
gibt es zunächst einmal eine Reihe grafischer Objekte, die
miteinander kombiniert und verschachtelt werden können. Die Definition
eines blauen Rechtecks sieht beispielsweise so aus:
\index{QML!Rectangle}%
\index{Hauptfenster}%
\index{QML!Hauptfenster}%
\begin{osplisting}{QML}{Ein blaues Rechteck}{code:qmlbluerectangle}
import Qt 4.7
Rectangle {
id: mainScreen
width: 200
height: 200
color: "blue"
}
\end{osplisting}
Zunächst bindet man per \ospcmd{import} die in der Datei benutzten
Module ein. Die grundlegenden Sprachelemente befinden sich in unserem
Beispiel im Modul \ospcmd{Qt}, das wir in Version 4.7 einbinden.
Modulname und Version haben sich in neueren Qt-Quick-Paketen geändert.
Falls die oben verwendete Zeile also bei Ihnen nicht funktioniert,
dann ersetzen Sie diese einfach durch:
\begin{ospsimplelisting}
import QtQuick 1.0
\end{ospsimplelisting}
Auch die Versionsnummer kann bei Ihnen schon höher sein, zum
Erscheinen dieses Buches steht die Veröffentlichung von
\ospcmd{QtQuick 2.0} bevor. Es gibt leider keine einfache automatische
oder auch nur manuelle Möglichkeit, die ausgelieferte Qt-Quick-Version
unter PyQt oder PySide zu erkennen. Am einfachsten ist es derzeit, die
verschiedenen Versionsnummern in einer einfachen QML-Datei
auszuprobieren bis das Modul erfolgreich eingebunden werden kann.
Darüber hinaus wurden im Beispiel lediglich Größe und Farbe des
Rechtecks deklariert. Außerdem kann jedem QML-Element eine ID
zugewiesen werden. Über diese ID werden die Elemente später aus
anderen QML-Elementen oder aus Javascript-Code heraus angesprochen.
Allgemein erinnert die QML-Syntax an die \emph{JavaScript Object
Notation} (JSON), die auch Pate bei der Entwicklung von QML stand.
Zeilen können, aber müssen nicht mit Semikolon abgeschlossen werden.
Wenn man aber mehrere Deklarationen in eine Zeile schreiben möchte, ist
das Semikolon obligatorisch:
\begin{ospsimplelisting}
width: 100; height: 100;
\end{ospsimplelisting}
\index{QML!Elemente verschachteln}%
In das blaue Rechtecke hinein lässt sich nun einfach ein zweites,
kleineres und grünes Rechteck zeichnen:
\begin{osplisting}{QML}{Ein grünes Rechteck im blauen}{code:qmlgreenrectangle}
import Qt 4.7
Rectangle {
id: mainScreen
width: 200
height: 200
color: "blue"
Rectangle {
anchors.fill: parent
anchors.margins: 50
color: "green"
}
}
\end{osplisting}
\index{QML!Anker}%
Dieses Verschachteln von Elementen und das Ausrichten an
Elternelementen wird uns in QML noch häufiger begegnen und auch noch
ausführlicher besprochen. Wichtigstes Gestaltungsmittel für Layouts
sind die sogenannten \emph{Anker} (\emph{Anchor}). Jede Seite eines
Elements sowie Mittelpunkt in der Vertikalen und Horizontalen stellen
Ankerpunkte dar, die sich an den Ankerpunkten anderer Elemente
ausrichten lassen. Für HTML-Profis ist das zumindest am Anfang eine
Herausforderung, da die Ausrichtung in QML oft ganz anders
funktioniert als in HTML. Nach einer Eingewöhnungszeit mit viel
Herumprobieren erweist sich die Ausrichtung in QML aber als deutlich
logischer und konsequenter definiert als in HTML, so dass man später
jedes gewünschte Layout schnell in QML abbildet. In unserem Fall
richten wir alle Ankerpunkte zunächst am Elternelement aus
(\ospcmd{anchors.fill: parent}), bevor wir für alle Seiten einen
Abstand von 50 Pixeln angeben (\ospcmd{anchors.margins: 50}). Damit
liegt das grüne Rechteck genau mittig im Blauen und ist 100 mal 100
Pixel groß.
\index{QML!MouseArea}%
\index{QML!Signale und Slots}%
Neben den Angaben zum Aussehen gibt es in QML auch Elemente, die eine
Interaktion mit dem Benutzer ermöglichen. Eines davon ist die
\ospcmd{MouseArea}, mit der Sie Eingaben mit der Maus abfragen. Um
beispielsweise das grüne Rechteck rot zu färben, sobald der Benutzer
darauf klickt, kann der bisherige Code einfach folgendermaßen
erweitert werden:
\begin{osplisting}{QML}{Farbe zuweisen nach Mausklick}{code:qmlredrectangle}
import Qt 4.7
Rectangle {
id: mainScreen
width: 200; height: 200;
color: "blue"
Rectangle {
anchors.fill: parent
anchors.margins: 50
color: "green"
MouseArea {
anchors.fill: parent
onClicked: { parent.color="red"; }
}
}
}
\end{osplisting}
Wieder werden die Elemente verschachtelt, wobei die \ospcmd{MouseArea}
jetzt ein Kindelement des grünen Rechtecks ist. Per
\ospcmd{anchors.fill: parent} wird ihm die gesamte Rechteckfläche
zugewiesen, so dass der Benutzer irgendwo ins grüne Rechteck klicken
kann. Der \ospcmd{onClicked}-Handler beschreibt schließlich, was beim
Klick passiert. Solche Event-Handler bestehen aus einer Art anonymer
Javascript-Funktion, die bei Ereignis aufgerufen wird. Im Hintergrund
arbeitet hier allerdings der Signal-Slot-Mechanismus in Qt, wobei das
\ospcmd{onClicked}-Element hier auf das Signal \ospcmd{clicked}
reagiert (Näheres zu Signalen und Slots dann in Kapitel
\ref{sec:createcustomqmlelements}). In unserem Fall setzen wir die
Farbe des Elternelements auf rot. Allgemein können diese Funktionen
beliebig komplex werden, so dass versierte Javascript-Entwickler
durchaus eine vollständige Anwendungslogik entwickeln können. Den
meisten Handlern werden außerdem die Daten zum Ereignis als Parameter
mitgegeben. In unserem Fall haben wir in der Javascript-Funktion
Zugriff auf eine Variable \ospcmd{mouse}, die Angaben zu Mausbutton
und Position des Mauszeigers enthält. Per Kommando
\ospcmd{console.log()} können wir beispielsweise die X-Position des
Mauszeigers ausgeben lassen:
\begin{ospsimplelisting}
onClicked: { parent.color="red"; console.log(mouse.x); }
\end{ospsimplelisting}
Diese Ausgabemöglichkeit ist übrigens derzeit die einzig mögliche
Debugging"=Funktion. Hier merkt man noch deutlich, dass Qt Quick ein
Framework in Entwicklung ist.
Starten wir nun aber die QML-Anwendung. Dazu bedarf es keines weiteren
Programm-Codes, wir können den von Qt ausgelieferten QML Viewer
verwenden.
\ospsubsection{Der QML Viewer}{Der QML Viewer}{sec:qmlviewer}
\index{QML!QML Viewer}%
Der QML Viewer ist eine kleine Anwendung zur Darstellung einer
QML-Datei. Dazu wird ein Hauptfenster erstellt, das einen
Anzeigebereich für die QML-Datei enthält. Der QML Viewer ist bisher
nicht Teil einer PyQt- oder PySide-Installation, sondern steht nach
der Installation des Qt-SDK bzw. des entsprechenden Pakets unter
Linux zur Verfügung (unter Ubuntu installieren Sie dazu das Paket
\ospfile{qt4-qmlviewer}). Erst dann steht Ihnen eine ausführbare
Datei \ospfile{qmlviewer(.exe)} zur Verfügung.
\osppagebreak
Um nun die QML-Beispiele des vorhergehenden Kapitels zu starten,
speichern Sie diese in einer Datei und rufen dann den QML Viewer mit
dem Dateinamen als Argument auf:
\begin{ospsimplelisting}
$ <bf>qmlviewer beispiel.qml</bf>
\end{ospsimplelisting}
Mit der Option \ospcmd{-h} erhalten Sie eine Liste aller Optionen für
den Viewer, z.\,B. können Sie die Anwendung im Vollbildmodus starten
oder bestimmen, dass sie immer im Vordergrund läuft. Wie wir noch
sehen werden, können QML-Dateien andere QML-Dateien einbinden,
beispielsweise um einzelne GUI-Elemente an mehreren Stellen einer
Anwendung wiederzuverwenden. Diese eingebetteten QML-Dateien werden
immer automatisch aus demselben Verzeichnis der einbettenden QML-Datei
geladen, so dass Sie dem QML Viewer nur die Haupt-QML-Datei übergeben.
Nachdem nun die grundlegenden Eigenschaften von Qt Quick und QML
vorgestellt wurden, wollen wir im nächsten Kapitel eine kleine
Beispielanwendung entwickeln, und zwar in reinem QML, also erst einmal
ohne Python-Code.
\ospsection{QML-Anwendungen}{QML-Anwendungen}{sec:pureqmlapplications}
\index{RSS}%
Die Anwendung beschränkt sich darauf, die Beiträge eines Blogs in
einer Liste anzuzeigen. Bei Klick auf einen Eintrag der Liste wird die
Webseite des Beitrags in einem Web-View geladen. In diesem Fall
verwenden wir eine feste Adresse für den Blog, zurückgegriffen wird
auf das RSS des Blog des
Autors.\ospfootnote{fn:dasskript}{\ospurl{http://www.dasskript.com}}
Abbildung \ospfigref{fig:qml_rssviewer1} zeigt die laufende Anwendung
unter Ubuntu.
\ospfigure{0.32}{images/qml_rssviewer1}{RSS-Viewer in QML}{fig:qml_rssviewer1}
\ospsubsection{Ein erster View in QML}{Ein erster View in QML}{sec:firstqmlview}
\index{WebKit}%
\index{QML!WebKit}%
Die Anwendung wird später aus nur zwei Views bestehen: die Liste der
Blog-Einträge und ein Webkit-View zum Anzeigen der Blog-Webseite.
Dazu definiert man zunächst das Hauptfenster als \ospcmd{Rectangle}
mit den Werten für die Größe des Fensters. Da in diesem Fall eine
Liste von Blogeinträgen dargestellt werden soll, bietet es sich an,
das Fenster in der Vertikalen größer zu machen als in der
Horizontalen. In diesem Fall wählen wir eine Größe von 360 mal 640
Pixeln\ospfootnote{fn:qmlsymbian}{Was mehr oder weniger zufällig der
Auflösung eines Symbian-Handys entspricht. Der RSS-Viewer lässt sich
damit auch wunderbar auf einem Mobiltelefon mit Symbian, Maemo oder
MeeGo starten.}:
\begin{osplisting}{QML}{Hauptfenster der QML-Anwendung}{code:qmlmainwindow}
import Qt 4.7
import QtWebKit 1.0
Rectangle {
id: mainScreen
property int currentIndex: 0
width: 360
height: 640
color: "white"
ListView {
id: listviewRss
anchors.fill: parent
focus: true
orientation: ListView.Vertical
}
}
\end{osplisting}
Das Hauptfenster enthält also ein Rechteck der Größe 360 mal 640
Pixeln und hat eine weiße Hintergrundfarbe (\ospcmd{white}). Wir
definieren außerdem gleich ein \ospcmd{property} namens
\ospcmd{currentIndex} als Integer, das später die ID des aktuell
ausgewählten Blogeintrags enthält. Ganz am Anfang binden wir neben dem
Qt-Modul per \ospcmd{import QtWebKit 1.0} außerdem das
QML-WebKit-Modul ein. Speichern Sie diesen Code beispielsweise in
einer Datei \ospfile{main.qml}, sie wird die Hauptdatei unserer
QML-Anwendung.
\index{Modelle und Views}%
\index{QML!Modelle und Views}%
\index{QML!ListView}%
Schließlich wird innerhalb des Rechtecks das erste GUI-Element
definiert: ein \ospcmd{ListView}. In QML schachtelt man zueinander
gehörende GUI-Elemente ineinander; wir stellen gleich noch eine Reihe
weiterer Elemente vor. Innerhalb des \ospcmd{ListView} definieren wir
per \ospcmd{anchors.fill}, dass die Liste alle Layout-Angaben von
seinem Elternelement (also vom Rechteck mit der ID
\ospcmd{mainScreen}) erben soll. Außerdem erhält die Liste sofort den
Fokus für Tastatureingaben und soll schließlich eine vertikale Liste
sein. Wo kommt aber nun der Inhalt der Liste her? QML greift hier auf
das unter Qt oft verwendete Model-View-Konzept zurück, für das Qt
schon seit längerer Zeit eine Reihe von Widget- und Modell-Klassen
bereitstellt. Während der View für die Darstellung der Liste zuständig
ist, liefert das Modell die eigentlichen Daten, in diesem Fall die
Einträge eines RSS-Streams.
\ospsubsection{Die Daten als Modell}{Die Daten als Modell}{sec:qmldatamodels}
\index{XML}%
\index{QML!XML}%
\index{QML!XmlListModel}%
QML stellt dem Entwickler drei Modelltypen zur Verfügung. Dazu besitzt
QML die drei folgenden Datentypen:
\begin{ospdeflist}
\ospdefitem{\ospcmd{ListModel}}{eine einfache Hierarchie von
QML-Elementen. Jeder Eintrag in diesem Modell wird durch ein
\ospcmd{ListElement} definiert. Die eigentlichen Daten werden also
direkt in QML kodiert.}
\ospdefitem{\ospcmd{XmlListModel}}{erzeugt ein Datenmodell aus einer
XML-Datei. Die Datei kann lokal oder aus dem Netzwerk geladen
werden, z.\,B. von einem Webserver.}
\ospdefitem{\ospcmd{VisualItemModel}}{funktioniert ähnlich wie
\ospcmd{ListMode}, allerdings können hier beliebige QML-Elemente
als Dateneinträge dienen, nicht nur \ospcmd{ListElement}.}
\end{ospdeflist}
Weitere Modelle können vom Entwickler in Python entwickelt und per
Python"=Datentypen an den QML-View übergeben werden, darauf werden wir
in Kapitel \ref{sec:pythondatainqmlviews} noch zurückkommen. Für
unsere Zwecke benötigen wir ein XML-Modell, da die Daten des Blog-RSS
in diesem Format gespeichert sind. Glücklicherweise stellt QML eben
mit \ospcmd{XmlListModel} genau einen solchen Typ bereit. Diesem
übergeben wir lediglich eine URL zur RSS-XML-Datei sowie einen
Query-String, der beschreibt, welche Daten aus dem XML wir abfragen
wollen. Dazu fügen wir einfach folgenden Code innerhalb des oben
implementierten \ospcmd{Rectangle}-Blocks ein:
\begin{osplisting}{QML}{XML-Listen-Modell}{code:qmlxmllistmodel}
XmlListModel {
id: xmlModel
source: "http://www.dasskript.com/blogposts.rss"
query: "/rss/channel/item"
XmlRole { name: "title"; query: "title/string()" }
XmlRole { name: "guid"; query: "guid/string()" }
XmlRole { name: "link"; query: "link/string()" }
XmlRole { name: "pubDate"; query: "pubDate/string()" }
}
\end{osplisting}
Die zusätzlichen \ospcmd{XmlRole}-Definitionen geben an, welches
Element wir auf welche Variable abbilden. Diese Rollen müssen auch bei
einem \ospcmd{ListModel} definiert werden, sie entsprechen in etwa dem
Spaltennamen in einer Datenbanktabelle. Jeder Modelleintrag entspräche
dann einer Datenbankzeile. In unserem Fall stellt der \ospcmd{query}
des \ospcmd{XmlListModel} die Daten zur Verfügung, in einem
\ospcmd{ListModel} definiert man eine Reihe von
\ospcmd{ListElement}-Unterelementen. Der String innerhalb der
\ospcmd{title}-Tags im XML wird also innerhalb des Views als Variable
\ospcmd{title} verfügbar sein usw. Zur Verdeutlichung hier ein
Beispieleintrag im RSS-XML:
\begin{osplisting}{XML}{Auszug aus dem RSS-Stream des Blogs}{code:qmlblogrss}
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
[...]
<item>
<title>Processing in Javascript in QML in Python @ dasskript.com</title>
<description>Es gibt gute [...]</description>
<pubDate>Mon, 05 Sep 2011 12:31:18 +0200</pubDate>
<guid isPermaLink="false">www.dasskript.com:95</guid>
<author>pbouda</author>
<link>http://www.dasskript.com/blogposts/95</link>
</item>
<item>
[...]
</item>
</channel>
</rss>
\end{osplisting}
Der \ospcmd{query} liest also alle \ospcmd{item}-Einträge in das
Modell, die Rollen verweisen auf den Inhalt der Unterelemente, die
später im View angezeigt werden sollen. Damit ist die Hauptarbeit
bereits erledigt. Wir müssen nun noch dem View mitteilen, dass er
genau dieses Modell für die Anzeige von Daten verwenden soll. Dazu
fügen wir dem \ospcmd{ListView}-Element folgende Zeile hinzu:
\begin{ospsimplelisting}
model: xmlModel
\end{ospsimplelisting}
\index{QML!Delegates}%
\index{QML!Text}%
Im Prinzip greift der View nun auf das Modell zu. Wenn man die
Anwendung jetzt startet, bleibt der Bildschirm dennoch leer. Wir
müssen dem View noch mitteilen, wie er die einzelnen Elemente des
Modells darzustellen hat. Dazu dienen in QML die sogenannten
\emph{Delegates}. Ein Delegate besteht aus einem oder mehreren
GUI-Elementen, die Zugriff auf die im Modell definierten Variablen
bzw. Rollen haben. Um eine Liste aller RSS-Elemente anzuzeigen,
genügt zunächst einmal ein einfaches Textelement. Dazu fügt man in den
\ospcmd{ListView} folgende Zeile eine:
\begin{ospsimplelisting}
delegate: Text { text: pubDate + ": " + title }
\end{ospsimplelisting}
Das QML-Element \ospcmd{Text} ist eines der Standard-QML-Elemente und
erhält hier genau ein Attribut \ospcmd{text}, das den anzuzeigenden
Text definiert. Hier können wir jetzt auf die Rollen des XML-Modells
zugreifen. Wenn man die Anwendung nun startet, sieht man schon einmal
die Elemente in einer Liste. Schön sieht das aber noch nicht aus.
Außerdem soll ja ein Klick auf einen der Einträge später auch eine
Aktion auslösen. Dazu implementieren wir im nächsten Schritt unser
eigenes, aus QML-Primitiven aufgebautes GUI-Element.
\ospsubsection{Eigene GUI-Elemente komponieren}{Eigene GUI-Elemente komponieren}{sec:createcustomqmlelements}
Um ein \ospcmd{item}-Element des RSS anzuzeigen, werden wir nun unser
eigenes Widget implementieren. Es soll einigermaßen ansehnlich sein
und bei einem Klick-Event (per Touch oder Maus) ein Signal aussenden,
das wir dann später weiterverarbeiten. Für die Gestaltung setzen wir
das Element einfach aus einigen Primitiven wie einem Rechteck und
Textelementen zusammen.
\index{QML!Externe Datei laden}%
Zunächst wollen wir aber das Element in einer eigenen QML-Datei
implementieren. Erzeugen Sie eine Datei im Ordner, in
dem auch die Datei \ospfile{main.qml} liegt (im Beispielprojekt ist es
das Hauptverzeichnis der Anwendung). Nur dann können wir das
neue GUI-Element ohne Umwege in \ospfile{main.qml} verwenden. Als
Dateiname verwenden wir \ospfile{RssListItem.qml}. In diesem Fall ist
der Dateiname wichtig, da er gleichzeitig den Namen des QML-Elements
festlegt. In \ospfile{main.qml} kann ein Element \ospcmd{RssListItem}
definiert werden, Qt lädt dann automatisch den gesamten Code aus
unserer neu erzeugten QML-Datei.
Zunächst definieren wir darin das Aussehen unseres Elements sowie
einige Felder für den Textinhalt, die wir später mit den Variablen des
Modells füllen. Der Code dazu sieht folgendermaßen aus:
\begin{osplisting}{QML}{Ein eigenes QML-Element für die RSS-Listeneinträge}{code:customqmlcomponent}
import Qt 4.7
Item {
id: rssItem
property alias text: itemText.text
property alias date: itemDate.text
property alias backgroundcolor: rectBackground.color
signal clicked
height: itemDate.height + itemText.height + 20
width: parent.width
Rectangle {
id: rectBackground
anchors.fill: parent
color: "lightgrey"
border.color: "darkgrey"
border.width: 2
Item {
Text {
id: itemDate
font.pixelSize: 16
}
Text {
id: itemText
anchors.margins: 10
anchors.top: itemDate.bottom
font.pixelSize: 20
width: 360
wrapMode: Text.WordWrap
}
}
}
}
\end{osplisting}
\index{QML!Item}%
Zu Beginn definieren wir, nach der üblichen \ospcmd{import}-Anweisung,
ein \ospcmd{Item}-Element. Items dienen in QML meist dazu, andere
GUI-Elemente zu gruppieren. So lassen sich mehreren Elementen
gemeinsame Layout-Angaben oder aktive Flächen für Mausklicks und
Gesten zuweisen. In unserem Fall benötigen wir beides. Im Gegensatz zu
\ospcmd{Rectangle}-Elementen haben Items aber \emph{kein} eigenes
Aussehen, d.\,h. es sind keine Angaben wie Hintergrundfarbe, Rahmen,
usw. möglich. Es handelt sich um quasi um die unsichtbare Variante eines
\ospcmd{Rectangle}. Nach der \ospcmd{id} definieren wir die
sogenannten \ospcmd{property} unseres Elements. Wir wollen später
für jeden einzelnen Eintrag die Texte für das Veröffentlichungsdatum,
den Titel des RSS-Eintrags sowie eine Hintergrundfarbe setzen können.
Damit können wir dem gerade aktiven Element eine andere
Hintergrundfarbe geben als den anderen Elementen der Liste.
Die \ospcmd{property} sind alle als \ospcmd{alias} definiert. Das
heißt, sie verweisen lediglich auf bestimmte Werte in Unterelementen,
in diesem Fall auf das Unterelement \ospcmd{Rectangle} sowie die
beiden \ospcmd{Text}-Elemente. Verweise auf Elemente erfolgen in QML,
ähnlich wie in HTML und Javascript, grundsätzlich über die \ospcmd{id}
der Elemente. Die Angabe \ospcmd{itemText.text} verweist also auf den
Wert des \ospcmd{text}-Felds innerhalb des Elements mit der ID
\ospcmd{itemText}. Da es sich dabei um ein Element des Typs
\ospcmd{Text} handelt, ist der Wert des \ospcmd{text}-Felds genau der
Text, der auf dem Bildschirm angezeigt wird.
\index{Signale und Slots}%
\index{Signale und Slots!QML}%
\index{QML!Signale und Slots}%
Nach den Properties definieren wir noch ein Signal \ospcmd{clicked},
das ausgesendet wird, sobald ein Mausklick auf das Element erfolgt.
Die Verarbeitung der Signale folgt dem Signal-Slot-Konzept, das in Qt
die zentrale Rolle bei der Ablaufsteuerung von Anwendungen einnimmt
und das in Kapitel \ref{sec:signalslot} beschrieben wurde. QML fügt
sich also an dieser Stelle nahtlos in das Qt-Framework ein.
\osppagebreak
\index{QML!Abstände}%
\index{QML!Anker}%
Anschließend definieren wir Höhe und Breite des Listen-Elements sowie
seine Unterelemente. Normalerweise verlangt jedes Element genaue
Angaben zum Layout; in QML ist das einmal die Größe, zum anderen die
sogenannten \emph{Anker} (\ospcmd{anchors}), die die Positionierung
des Elements im Layout bestimmen. Das Element lässt sich dazu an allen
anderen sichtbaren Elementen ausrichten; zudem gibt es die Möglichkeit
der absoluten Positionierung. Häufig muss man, gerade beim Einstieg in
QML, mit diesen Angaben herumspielen, bis die Anwendung so aussieht,
wie man es sich vorstellt. Ein erster Ansatzpunkt ist meist die Angabe
\ospcmd{anchors.fill: parent}. Damit erbt das Element alle Layout- und
Positionsangaben von seinem Elternelement. Anschließend kann man die
einzelnen Angaben, wie beispielsweise Abstand nach links oder rechts,
noch einmal speziell für dieses Element verfeinern. In unserem Fall
erbt das Rechteck alle Angaben von seinem Elternelement \ospcmd{Item},
wobei dessen Größe von uns definiert wurde und der \ospcmd{ListView}
daraus die Positionierung des \ospcmd{Item} innerhalb der Liste
ableitet. Die Textelemente ordnen wir innerhalb des Rechtecks
untereinander an, indem wir dem zweiten Textelement (der Überschrift)
die Angabe \ospcmd{anchors.top: itemDate.bottom} übergeben. Die
Oberseite der Überschrift beginnt damit an der Unterseite des Datums,
mit einem Abstand von 10 Pixeln (\ospcmd{anchors.margins: 10}).
Dem Textelement für die Überschrift müssen wir zudem eine feste
Breite zuweisen, damit die Angabe für den Zeilenumbruch greift
(\ospcmd{wrapMode: Text.WordWrap}).
Wer mit HTML und CSS gearbeitet hat, wird mit den meisten Angaben
schnell zurechtkommen. Genau wie bei Webseiten muss man aber ein
genaues Verständnis des Layout-Mechanismus haben, um auf Anhieb das
gewünschte Aussehen zu erreichen. Hier hilft eigentlich nur Erfahrung.
Im Qt Designer gibt es zwar schon einen rudimentären WYSIWYG-Editor
für QML, allerdings hat man eben wie auch bei HTML und CSS nur dann
die größtmögliche Kontrolle, wenn man direkt im Code arbeitet.
Nun wollen wir unser eigenes Element aber auch als Delegate im
\ospcmd{ListView} verwenden. Dazu wechselt man zunächst zurück in die
Datei \ospfile{main.qml} und passt den Code des
\ospcmd{delegate}-Attributs folgendermaßen an:
\begin{ospsimplelisting}
delegate: RssListItem {
text: title
date: pubDate
backgroundcolor: mainScreen.currentIndex == index ? "#9bf" : "lightgrey"
}
\end{ospsimplelisting}
\ospcmd{text}, \ospcmd{date} und \ospcmd{backgroundcolor} entsprechen
den \ospcmd{property} unseres\osplinebreak{}
\ospcmd{RssListItem}-Elements, die wiederum, wie oben beschrieben, auf
die entsprechenden Attribute der Unterelemente verweisen. Dem gerade
aktiven Element der Liste weisen wir eine andere Hintergrundfarbe zu,
indem wir den zentral gespeicherten Wert
\ospcmd{mainScreen.currentIndex} mit dem aktuellen \ospcmd{index}-Wert
des \ospcmd{ListView} vergleichen. Innerhalb des
\ospcmd{delegate}-Attributs steht uns in dieser Variable der Index des
gerade zu zeichnenden Listenelements zur Verfügung -- ein klassisches
Beispiel für deklaratives Programmieren.
Beachten Sie wieder: In einer QML-Datei stehen grundsätzlich alle
Elemente in QML-Dateien desselben Ordners zur Verfügung, man braucht
also keinen zusätzlichen Import für das \ospcmd{RssListItem}.
Wird die Anwendung jetzt gestartet, wird schon das selbst entworfene
GUI-Element für die Liste verwendet, so wie es Abbildung
\ospfigref{fig:qml_rssviewer1} zeigt. Was nun noch fehlt, ist die
Aktion: Bei Klick auf eines der Elemente soll die Webseite des
Eintrags in einem \ospcmd{WebView}-Element angezeigt werden.
\ospsubsection{Signale und Zustände}{Signale und Zustände}{sec:qmlsignalsandstates}
\index{QML!Zustände}%
\index{QML!Signale}%
Der erste Schritt für die Benutzereingabe ist es, diejenigen Flächen
zu deklarieren, in denen auf Eingaben reagiert werden soll. Dazu dient
beispielsweise das QML-Element \ospcmd{MouseArea}, das wir in unserem
Fall dem Rechteck unseres \ospcmd{RssListItem} hinzufügen. Dazu öffnet
man wieder die Datei \ospcmd{RssListIten.qml} im Editor und fügt dem
\ospcmd{Rectangle}-Element am Ende folgenden Code hinzu:
\begin{ospsimplelisting}
MouseArea {
id: mouseRegion
anchors.fill: parent
onPressed: rssItem.state = 'Pressed'
onReleased: rssItem.state = 'Default'
onClicked: { rssItem.clicked(); }
}
\end{ospsimplelisting}
Der Mausklick-Bereich erhält eine ID und übernimmt per
\ospcmd{anchors.fill} Größe und Position des Rechtecks. Die letzte
Zeile löst das von uns definierte Signal \ospcmd{clicked()} aus,
sobald die \ospcmd{MouseArea} angeklickt wurde. Die zusätzlichen
Angaben für \ospcmd{onPressed} und \ospcmd{onReleased} werden wir für
einen netten Effekt verwenden: Sobald das Element angeklickt wurde,
wird der Text-Stil des Elements geändert, solange die Maustaste
gedrückt wird. Auch dieses Verhalten definieren wir deklarativ, in
diesem Fall über Zustände. Wir können dazu für jedes Element beliebig
viele Zustände ad hoc definieren. In unserem Fall hat unser Element
schon einmal die beiden Zustände \ospcmd{Pressed} und
\ospcmd{Default}, die Namen der Zustände sind frei gewählt und können
beliebig geändert werden.
\index{QML!Zustandsübergänge}%
\index{QML!PropertyChanges}%
Wir müssen dem \ospcmd{RssListItem} nun nur noch mitteilen, welchen
Zustand eine Aktion auslöst. Hier soll ein Wechsel des Zustands nach
\ospcmd{Pressed} den Stil des Textelements \ospcmd{itemText} ändern.
Dazu definiert man in QML ein Array \ospcmd{states}, das die Angaben
für die einzelnen Zustände und Zustandsänderungen enthält. Um das
Textelement bei Mausklick zu ändern, fügt man folgenden Code
an das Ende des äußeren \ospcmd{Item}-Elements mit der ID
\ospcmd{rssItem} hinzu:
\begin{ospsimplelisting}
states:[
State {
name: "Pressed"
when: mouseRegion.pressed == true
PropertyChanges {
target: itemText
style: Text.Sunken
color: "white"
}
}
]
}
\end{ospsimplelisting}
Der Text wird bei Klick auf ein Listenelement nun weiß und erhält den
Stil \ospcmd{Text.Sunken}. Wenn die Anwendung jetzt per QML Viewer
gestartet wird, kann man dieses Verhalten gleich ausprobieren. Unser
Element kümmert sich also ganz allein um die Änderung des Text-Stils,
in der Datei \ospfile{main.qml} sind dazu keine Änderungen notwendig.
Nun soll aber auch das Hauptfenster auf den Klick reagieren. Das
Signal \ospcmd{clicked()} haben wir ja schon ausgelöst, jetzt müssen
wir nur noch in \ospfile{main.qml} darauf reagieren. Zunächst
beschränken wir uns darauf, nach einem Klick das aktuelle Element mit
der Hintergrundfarbe zu markieren. Dazu reicht es aus, in
\ospfile{main.qml} dem \ospcmd{delegate}-Attribut des
\ospcmd{RssListItem} noch ein Attribut \ospcmd{onClicked}
hinzuzufügen. Das gesamte Delegate sieht dann so aus:
\begin{ospsimplelisting}
delegate: RssListItem {
text: title
date: pubDate
backgroundcolor: mainScreen.currentIndex == index ? "#9bf" : "lightgrey"
onClicked: mainScreen.currentIndex = index
}
\end{ospsimplelisting}
Man reagiert also auf eigene Signale genauso wie oben auf die
eingebauten Signale der \ospcmd{MouseArea}. Dazu fügt man dem Element
ein Attribut mit dem Signal samt Präfix \ospcmd{on} hinzu. In unserem
Fall wechseln wir einfach den zentralen \ospcmd{currentIndex} auf den
neuen \ospcmd{index}, sobald wir das Signal empfangen. Besser gesagt:
Wir \dqo{}deklarieren\dqc{} die Zuweisung der Eigenschaft
\ospcmd{mainScreen.currentIndex} für dasjenige Attribut, das den
Empfang des Signals beschreibt.
Das reicht auch schon aus: Wenn man die Anwendung nun startet,
reagiert nicht nur der Text auf den Mausklick. Nach Loslassen der
Maustaste wird außerdem das gewählte Element mit einer anderen
Hintergrundfarbe gekennzeichnet.
\ospsubsection{Zustandsänderungen und View-Wechsel}{Zustandsänderungen und View-Wechsel}{sec:qmlchangestateandview}
\index{QML!WebView}%
\index{QML!WebKit}%
\index{WebKit}%
\index{QML!Zustandsübergänge}%
Grundsätzlich erfolgt die Anzeige der Webseite zu einem der
Listeneinträge auf dieselbe Weise wie der Wechsel des
Text-Stils im Listeneintrag: Ein Klick auf einen Eintrag weist dem
Hauptelement \ospcmd{mainScreen} zunächst einen neuen Zustand zu.
Dieser neue Zustand wiederum löst einen Wechsel des aktuellen Views
aus: Statt dem \ospcmd{ListView} zeigen wir einfach einen
\ospcmd{WebView} an. Dieser Wechsel lässt sich darüber hinaus
animieren, so dass schließlich zwischen Liste und Webseite eine
butterweiche Überblendung stattfindet.
Zunächst definieren wir dazu in \ospfile{main.qml} innerhalb des
\ospcmd{Recangle}"=Elements mit der ID \ospcmd{mainScreen} einen
weiteren View, dieses Mal ein \ospcmd{WebView}-Element:
\begin{osplisting}{QML}{Der Webview zur Anzeige der RSS-Inhalte}{code:qmlwebviewforrss}
WebView {
id: webview
opacity: 0
anchors.fill: parent
MouseArea {
anchors.fill: parent
onClicked: mainScreen.state = "Rsslist"
}
}
\end{osplisting}
Der \ospcmd{WebView} stellt Webseiten dar, gerendert wird der gesamte
Inhalt der Seite von einer WebKit-Komponente. Dazu muss am Anfang der
Datei das Qt-WebKit-Modul per \ospcmd{import QtWebKit 1.0} geladen
werden. Das hatten wir aber oben schon erledigt.
Der View bleibt zunächst unsichtbar, da wir das Attribut
\ospcmd{opacitity}, also die Deckkraft des Elements, auf \ospcmd{0}
(Null) gesetzt haben. Per \ospcmd{anchors.fill} weisen wir dem Element
seine Position zu, der \ospcmd{WebView} liegt damit an exakt derselben
Stelle wir der \ospcmd{ListView}, ist aber absolut durchsichtig. Bei
Klick auf den View wollen wir zunächst zurück in die Liste wechseln.
Die Webansicht ist also zunächst unbrauchbar, man gelangt von der
Liste zwar dorthin, aber dann bei Klick gleich wieder zurück. Das
liegt daran, dass der \ospcmd{WebView} in QML zunächst keine
Funktionalität außer der Anzeige der Webseite bereitstellt. Weder
kann man Scrollen noch Zoomen noch irgendetwas anderes. Für
Demonstrationszwecke ist das zunächst ausreichend. Wenn man die
Anwendung weiterentwickeln möchte, müssten zumindest einige Funktionen
für den Web-View bereitgestellt werden, damit der Benutzer auch etwas
mit der dargestellten Webseite anfangen kann.
Einen Zustand namens \ospcmd{RssList} haben wir also schon einmal
benannt. Er wird gesetzt, sobald auf den \ospcmd{WebView} geklickt
wird. Umgekehrt soll aber ein Klick auf ein RSS-Listenelement den
Zustand für die Anzeige der Webseite setzen und am besten auch gleich
die URL der Webseite im \ospcmd{WebView} laden. Dazu fügen wir dem
\ospcmd{onClicked}-Attribut des \ospcmd{delegate} im \ospcmd{ListView}
noch zwei Zeilen hinzu, so dass das Attribut folgendermaßen aussieht:
\begin{ospsimplelisting}
onClicked: mainScreen.currentIndex = index,
webview.url = xmlModel.get(index).link,
mainScreen.state = "Webview"
\end{ospsimplelisting}
Die zwei zusätzlichen Zeilen laden die Webseite, in dem die Variable
\ospcmd{link} für den aktuellen Eintrag aus dem Modell geladen und dem
\ospcmd{WebView} als URL zugewiesen wird. Außerdem setzen wir den
Zustand namens \ospcmd{Webview} im Hauptfenster.
Was noch fehlt, ist die Definition der Zustandsübergänge. Beim
Übergang zum Zustand \ospcmd{Webview} soll das
\ospcmd{WebView}-Element angezeigt, beim Übergang nach
\ospcmd{Rsslist} ausgeblendet werden. Wiederum definieren wir dazu ein
Array \ospcmd{states}, dieses Mal in \ospfile{main.qml} am Ende des
\ospcmd{Rectangle}-Elements:
\begin{osplisting}{QML}{Definition der Zustandsübergange zwischen List- und Web-View}{code:qmltransitiondef}
states: [
State{
name: "Webview"
PropertyChanges{
target: webview
opacity: 1
focus: true
}
PropertyChanges {
target: listviewRss
opacity: 0
}
},
State {
name: "Rsslist"
PropertyChanges{
target: webview
opacity: 0
focus: false
}
PropertyChanges {
target: listviewRss
opacity: 1
}
}
]
\end{osplisting}
Die Übergange zwischen den Zuständen setzen jeweils den Wert
\ospcmd{opacity} der beiden Elemente: Eines davon wird durchsichtig
gemacht, das andere angezeigt. Startet man die Anwendung nun, kann man
schon einmal die Listenelemente auswählen und bekommt die
entsprechende Webseite angezeigt. Klickt man dann auf die Webseite,
gelangt man zurück in den \ospcmd{ListView}.
\index{QML!Animationen}%
\index{QML!PropertyAnimation}%
\index{Animationen}%
Das Ganze lässt sich nun auf einfache Weise verschönern. Dazu
definiert man für den Übergang von einer \ospcmd{opacity}-Einstellung
zu einer anderen einfach eine Animation. Wiederum erfolgt die Angabe
deklarativ, und zwar innerhalb eines Array \ospcmd{transitions}.
Dieses Array definiert man parallel zu \ospcmd{states} am Ende des
\ospcmd{Rectangle}-Elements in \ospfile{main.qml}:
\begin{ospsimplelisting}
transitions: [
Transition {
PropertyAnimation{
properties: "opacity"
duration: 1000
easing.type: "OutCubic"
}
}
]
\end{ospsimplelisting}
Wenn sich also die Durchlässigkeit eines Elements unseres
Hauptfensters ändert, wird nicht einfach der Wert gesetzt.
Stattdessen wird innerhalb von 1000 Millisekunden
(\ospcmd{duration}-Attribut) der Wert kubisch angenähert
(\ospcmd{easing.type}-Attribut). Das war's auch schon -- die
Überblendung zwischen unseren Views erfolgt jetzt durch sanfte Ein-
und Ausblendung des Web-Views.
Der nächste logische Schritt wäre nun, die Anzeige der Webseite
brauchbar zu gestalten, also zumindest ein Zoomen und Scrollen zu
erlauben. Dies soll nicht mehr Teil dieses Kapitels sein, aber ein
Hinweis sei gegeben: Nokia stellt bei den deklarativen Qt-Demos einen
QML-Webbrowser zur
Verfügung,\ospfootnote{fn:declbrowser}{\ospurl{http://doc.qt.nokia.com/4.7/demos-declarative-webbrowser.html}}
dessen QML-Elemente sich in unserer Anwendung nutzen lassen. Der erste
Schritt wäre, die einzelnen QML-Dateien des Beispiels in das
Verzeichnis unserer Anwendung zu kopieren und dann statt dem
Standard-WebView den \ospcmd{FlickableWebView} des Demos zu verwenden.
\ospsection{Vordefinierte QML-Elemente}{Vordefinierte QML-Elemente}{sec:qtcomponents}
\index{QML!Elemente}%
\index{Qt Components}%
\index{QML!Qt Components}%
Bevor wir näher auf die Integration von QML-Views in Python eingehen,
widem wir uns zunächst den von Qt derzeit zur Verfügung gestellten
QML-Elementen. Die meisten stellen basale geometrische oder logische
Strukturen dar, wie die bereits vorgestellten \ospcmd{Rectangle} oder
\ospcmd{Item}. Wenn man diese mit den in Kapitel
\ref{chap:einfuehrung} besprochenen Qt-Widgets vergleicht, kann man
sich wohl nur schwer vorstellen, wie man in reinem QML eine
Desktop-GUI mit Menü, Buttons und den übrigen Standardelementen
aufbauen soll. Zumindest ist dazu viel Handarbeit notwendig.
Selbstverständlich soll der bisherige Stand auch nicht die Basis für
Desktop-Anwendungen sein. Bisher standen durch die Übernahme von
Trolltech durch Nokia eben Touch-Oberflächen für relativ kleine
Display-Oberflächen im Vordergrund, für die ein ausgereiftes
Widget-System zwar auch notwendig ist, das vom Umfang her aber
deutlich reduzierter ausfallen kann. Für Symbian und MeeGo wurde darum
zunächst das
\emph{Qt-Components-Projekt}\ospfootnote{fn:qtcomponents}{Sourcecode
unter \ospurl{http://qt.gitorious.org/qt-components}.} ins Leben
gerufen. Es entwickelt eine Reihe von Widgets speziell für
Touch-Interfaces. Selbst hier gibt es einige Unterschiede zwischen
den Elementen für Symbian und MeeGo, für Desktop-Anwendungen ist im
Prinzip eine Parallelentwicklung notwendig. Auch diese wurde gestartet
und soll letztlich das bisherige Widget-System in Qt
ablösen.\ospfootnote{fn:qtdesktopcomponents}{Den Sourcecode dieses
Projekts finden Sie ebenfalls unter der Web-Adresse des
Qt-Component-Projekts:
\ospurl{http://qt.gitorious.org/qt-components/desktop}.} Für alle Qt
Components existiert aber bisher weder eine vernünftige Dokumentation,
noch lässt sich das Desktop-Projekt ohne Umstände auf einem beliebigen
System kompilieren und ausprobieren. Wir verzichten hier darum auf
eine weitere Beschreibung der Qt Components. Schauen Sie jedoch ab und