KDE 4.4 ist die erste KDE-Version, die sich das aktuelle Qt 4.6 einverleibt. Dieser Artikel führt dem Anwendungsentwickler zwei neue Qt-Eigenschaften vor Augen, mit denen er in Zukunft zu tun haben wird: grafische Animation und gestische Bedienung .
Qt und KDE entstehen unabhängig voneinander, gehen jedoch eine symbiotische Beziehung ein. Was KDE-Anwender wollen, resultiert in neuen Qt-Funktionen. Und mit den Neuerungen der jüngsten Qt-Release [1] wird sich auch das KDE-Projekt neue Features ausdenken. Die meisten Verbesserungen in Qt betreffen das Anwender-Erlebnis. Ein Blick auf die technische Seite offenbart, was Entwickler mit zwei der wichtigsten Verbesserungen – Animationseffekte und Multitouch-Unterstützung – anstellen dürfen. Der Quellcode für beide Beispiele liegt auf dem FTP-Server [2] bereit.
Bewegtes Plasma
Mit dem brandneuen Animations-Framework des Qt-Kinetic-Projekts [3] wird KDE 4.4 aller Voraussicht nach eine ordentliche Oberflächenpolitur in Form animierter Objektbewegungen erfahren. Zum Beispiel weichen Fenster langsam in den Hintergrund, statt mit einem Sprung die Ebene zu wechseln. Beim Plasma-Desktop stehen fließende Bewegungen ganz oben auf der Prioritätenliste.
Der Idee nach führen Programmierer damit das Auge des Anwenders zu Gunsten intuitiver Benutzung. Damit sich Effekte leicht verwirklichen lassen, baut Qt 4.6 das Animations-Framework um die Klasse »QAbstractAnimation« herum auf. Sie folgt dem Konzept von Eigenschaften. Das bedeutet, dass Animationen sich nicht auf Bewegung, Drehung oder Skalierung von Objekten beschränken, sondern sich auch im Grad der Transparenz oder farblich ändern lassen.
Aus Sicht des KDE-Programmierers beginnt die Implementation bei den grafischen Ansichtsklassen, auf die der Plasma-Desktop aufbaut. Das erste Programmierbeispiel zeigt, wie sich grafische Objekte animieren lassen. Listing 1 verwirklicht beispielhaft die Animationen zweier Widgets.
|
Listing 1: Animiertes |
|---|
1 ViewWidget::ViewWidget(QWidget *parent)
2 : QGraphicsView(parent)
3 {
4 setRenderHints(QPainter::Antialiasing |
QPainter::SmoothPixmapTransform);
5
6 QGraphicsScene *scene =
new QGraphicsScene(-100, -100, 200, 200, this);
7 setScene(scene);
8
9 QPushButton *blurButton = new QPushButton("Blur");
10 QGraphicsProxyWidget *blurItem = scene->addWidget(blurButton);
11 blurItem->setPos(-blurButton->width()/2, -10-blurButton->height());
12 QGraphicsBlurEffect *blurEffect = new QGraphicsBlurEffect(this);
13 blurEffect->setBlurRadius(0);
14 blurItem->setGraphicsEffect(blurEffect);
15
16 QPushButton *rotateButton = new QPushButton("Rotation");
17 QGraphicsProxyWidget *rotateItem = scene->addWidget(rotateButton);
18 rotateItem->setPos(-rotateButton->width()/2, 10);
19 rotateItem->setTransformOriginPoint(rotateButton->width()/2, rotateButton->height()/2);
20
21 QPropertyAnimation *blurAnimation =
new QPropertyAnimation(blurEffect, "blurRadius", this);
22 blurAnimation->setStartValue(0.0);
23 blurAnimation->setKeyValueAt(0.5, 10.0);
24 blurAnimation->setEndValue(0.0);
25 blurAnimation->setDuration(1500);
26 connect(blurButton, SIGNAL(clicked()), blurAnimation, SLOT(start()));
27
28 QPropertyAnimation *rotateAnimation =
new QPropertyAnimation(rotateItem, "rotation", this);
29 rotateAnimation->setStartValue(0.0);
30 rotateAnimation->setEndValue(360.0);
31 rotateAnimation->setDuration(2000);
32 rotateAnimation->setEasingCurve(QEasingCurve::OutBounce);
33 connect(rotateButton, SIGNAL(clicked()),
rotateAnimation, SLOT(start()));
34 }
|
Zeile 4 bewirkt zunächst die Kantenglättung für eine bestmögliche Darstellung bei sparsamem Ressourcenverbrauch. Die beiden Zeilen 6 und 7 erstellen ein Ansichtsfeld »scene«, das die gesetzten Bedingungen benutzt. Das Ansichtsfeld erhält eine quadratische Fläche der Kantenlänge 200 Pixel.
Spezifiziert der Entwickler keine Größe, würde Qt das Ansichtsfeld zunächst so klein wie möglich zeichnen. Es wüchse anschließend nach Bedarf, wenn die Animationen die Größe des Feldes verändern. Eventuell entstehen dann Rollbalken oder die Positionen der Inhalte im Feld ändern sich, damit immer alles sichtbar ist. Solche Mätzchen vermeidet, wer Qt ein festes Viereck vorgibt.
Die Zeilen 9 bis 11 erstellen das erste Element der Fläche. Das kann alles Mögliche sein, beispielsweise eine Bitmap, eine SVG-Zeichnung oder eine geometrische Form. Das angegebene Beispiel bettet den Knopf »blurItem» ein. Der Code instanziert erst die Schaltfläche und fügt sie dann dem Ansichtsfeld hinzu. Damit erhält es automatisch einen Platz in der Klasse »QGraphicsProxyWidget«. Sie ist für das Weiterreichen von Events und Vorgängen zwischen Widget und Ansichtsfeld zuständig. Wenn die Schaltfläche Teil des Ansichtfelds ist, zentriert sie die Funktion »setPos()«.
Qt betritt die Bühne
Die neuen grafischen Effekte von Qt 4.6 kommen in den Zeilen 12 bis 14 ins Spiel. Die Effekte können seit der neuen Qt-Version Eigenschaft eines jeden Gegenstands sein. Mitgelieferte Standardeffekte sind das Verschwimmen und Färben, Schatten werfen und Deckkraft ändern. Wem das nicht genügt, der programmiert eigene Effekte oder kombiniert vorhandene. Im Beispiel verschwimmt das Widget.
Zunächst erhält der verschwimmende Bereich um das Objekt herum jedoch die Ausdehnung 0, sodass der Effekt nicht sichtbar ist – er kommt später zum Zuge. Eine zweite Schaltfläche entsteht in den Zeilen 16 bis 19. Der Programmtext ist fast gleich, nur liegt der neue Button unterhalb des ersten. Standardmäßig ist die linke obere Ecke der Anker. Da die Schaltfläche aber rotieren soll, wird mit »setTransformOriginPoint()« stattdessen der Mittelpunkt zum Anker – das sieht besser aus (Zeile 19).
Sportstunde
Nachdem die Buttons vorhanden sind, nimmt der Programmtext mit der Instanz »blurAnimation« ab Zeile 21 die Animation ins Visier: Er legt »blurRadius« als animierbare Eigenschaft von »blurEffect« fest. Nun fehlen noch Angaben, wie sich die Eigenschaft entlang der Zeitachse verändert. Der Zeitwert für Animationen des Qt-Animation-Framework liegt immer zwischen 0 und 1. Die Zeilen 22 bis 24 bedeuten, dass sich der Verschwimm-Radius nach der Hälfte der Zeit (0,5) auf fünf Pixel (0,5 mal 10) ausdehnt und dann wieder zurückgeht. Zeile 25 sagt, dass der Prozess eineinhalb Sekunden dauert. In Zeile 26 löst ein Klick auf den Button den Vorgang aus.
Eine weitere Animation kommt in den Zeilen 28 bis 31 hinzu, die die Rotationseigenschaft des zweiten Buttons beeinflusst. Diese Schaltfläche kreist einmal in 2 Sekunden um ihren eigenen Anker. Zusätzlich dämpft jedoch Zeile 32 die Bewegung. Die Methode »setEasingCurve()« beschreibt, wie der Zeitwert der Animation von 0 auf 1 geht. Im vorliegenden Fall sorgt die Auswahl »OutBounce« dafür, dass die Rotation des Buttons mit einem kleinen Hüpfer endet. Das lässt sie hübscher aussehen.
Bei der Zeile 33 des Beispiels angekommen, lohnt es sich zu betrachten, wie gut sich das Animations-Framework in die grafischen Klassen einfügt. Auch fällt auf, dass die Animation lediglich so viel Programmtext braucht wie das Ansichtfeld – Animationen sind normalerweise viel aufwändiger. Das Ergebnis ist in Abbildung 1 zu sehen.

Abbildung 1: Hier verschwimmen und rotieren zwei Elemente innerhalb eines Ansichtsfelds auf Mausklick, indem der Programmierer sie erst als Objekt anlegt und dann mit den entsprechenden Eigenschaften versieht.
Animationen sind jedoch nicht das einzige Feature, das im Qt-Universum zur Berühmtheit taugt. Das zweite Beispiel ist die Bedienung via Multitouch-Gesten.
Finger im Spiel
Mit der Verbreitung von Touchscreens hat die gestische Bedienung jetzt auch Tablet-PCs und den Desktop erreicht. Gleichzeitig bewegt sich KDE auf mobile Geräte wie Netbooks und Smartphones zu. Für Anwendungsentwickler sind zwei Probleme zu meistern: Erstens betätigt der Benutzer mehrere Stellen der grafischen Schnittstelle gleichzeitig, so als hätte er mehrere Mäuse zur Verfügung. Das schränkt die Vorannahmen über den Zustand von Widgets ein, wenn sie voneinander abhängen. Qt und KDE können dieses Problem nicht allein lösen. Der Entwickler muss sich darüber aber im Klaren sein, wenn er seine Anwendung für den Multitouch-Betrieb freigibt.
Das zweite Problem liegt darin, auszuwerten, was die verschiedenen gestischen Anweisungen ausdrücken. Dieses Problem geht Qt selbst an, indem es die Bewegung von Druckpunkten verfolgt und für den Programmierer interpretiert. Er kann das wie ein API auf höherer Ebene betrachten. Diese Dienstleistung von Qt ist viel einfacher zu benutzen, als den Zusammenhang mehrerer Druckpunkte manuell auszuwerten.
Pinching the Quad statt Punching the Ball
Das veranschaulicht die Klasse »PinchWidget«. Die Pinch-Geste ist die Zwei-Finger-Geste, die das I-Phone berühmt gemacht hat. Entfernen sich zwei Finger voneinander, zoomt der Bildschirm aus. Nähern sich zwei Finger, zoomt er ein. Ein Drehen des Fingers dreht das festgehaltene Bild. Diesen ganzen Zauber vollführt das Pinch-Widget beispielsweise mit einem Quadrat, wie es auszugsweise in Listing 2 beschrieben ist. Nicht in dem Listing enthalten ist der Konstruktor »grabGesture(Qt::PinchGesture)«. Ohne diesen Aufruf würde das Widget keine Pinch-Geste erkennen. Mit ihm hingegen kann es sie auffangen und wie ein Ereignis verarbeiten.
|
Listing 2: |
|---|
1 bool PinchWidget::event(QEvent *event)
2 {
3 if(event->type() == QEvent::Gesture)
4 return gestureEvent(static_cast <QGestureEvent*>(event));
5 return QWidget::event(event);
6 }
7
8 bool PinchWidget::gestureEvent
(QGestureEvent *event)
9 {
10 if(QGesture *pinch = event->gesture (Qt::PinchGesture))
11 {
12 pinchGesture(static_cast <QPinchGesture*>(pinch));
13 return true;
14 }
15 return false;
16 }
17
18 void PinchWidget::pinchGesture
(QPinchGesture *gesture)
19 {
20 QPinchGesture::ChangeFlags flags =
gesture->changeFlags();
21 if(flags & QPinchGesture::RotationAngleChanged)
22 {
23 qreal value = gesture->rotationAngle();
24 qreal lastValue = gesture->lastRotationAngle();
25 rotationAngle += value - lastValue;
26 }
27 if(flags & QPinchGesture::ScaleFactorChanged)
28 {
29 currentScaleFactor = gesture->scaleFactor();
30 }
31 if(gesture->state() == Qt::GestureFinished)
32 {
33 scaleFactor *= currentScaleFactor;
34 currentScaleFactor = 1;
35 }
36 update();
37 }
|
Für das Ereignis ist die »event()«-Methode in den Zeilen 1 bis 6 zuständig. Nach dem Abfangen gibt sie das Gesten-Ereignis weiter an die Methode »gestureEvent()« (Zeilen 8 bis 16). Diese entscheidet, ob das Ereignis ein Pinch-Ereignis ist, und gibt es gegebenenfalls an die »pinchGesture()«-Methode weiter (Zeilen 18 bis 37). Sie interpretiert die Geste und aktualisiert das Widget. Die Klasse »QPinchGesture« zeichnet das Delta zur letzten Veränderung auf.
Das wirkt sich zum Beispiel auf das erste If-Statement in Zeile 21 aus. Es testet, ob sich die Rotationsachse geändert hat. Ist das Ergebnis positiv, passt es die »rotationAngle«-Variable entsprechend an. Und falls das Objekt gewachsen oder geschrumpft ist, was Zeile 27 in Erfahrung bringt, ändert sich auch der Ausgangspunkt des nächsten Skalierungsvorgangs in »currentScaleFactor«.
Die Bedingung in Zeile 31 prüft schließlich, ob die Geste beendet ist, aktualisiert den Wert »scaleFactor« und setzt den »currentScaleFactor« zurück. Damit kann Letzterer die nächste Pinch-Geste zur Größenveränderung zwischenspeichern. Unabhängig von den Ergebnissen ordnet Zeile 36 an, das Widget neu zu malen. Abbildung 2 zeigt das Widget bei der Arbeit: Die Pinch-Geste steuert Größe und Drehwinkel des Quadrats.

Abbildung 2: Drehen und Skalieren mittels Pinch-Geste bedeutet, das Objekt mit zwei Fingern klein- oder groß zu ziehen beziehungsweise zu rotieren.
Heimvorteil
In KDE 4.4 – so hier die Prognose – werden die Anwender im Gefolge entsprechender Geräte sicher noch sehr viel mehr solcher Effekte zu Gesicht bekommen. Für Plasmoid-Entwickler [4] liegt ein ganzer Satz fertiger Animationen bereit. Um etwa ein Bildschirmelement pulsieren zu lassen, entsteht dank der Klasse »Animator« schnell eine entsprechende Bewegung, der nur noch ein Widget und ein Startsignal zuzuordnen sind:
Animation *pulseAnimation = Animator::create(Animator::PulseAnimation); pulseAnimation->setWidgetToAnimate(button); connect(button, SIGNAL(clicked()), pulseAnimation, SLOT(start()));
Alle Plasmoide kennen die Pinch-Bewegung zum Drehen und Skalieren out of the Box. In Qt stecken darüber hinaus weitere Kommandogesten. Version 4.6 erlaubt etwa das so genannte Panning, also Scrollen per Berührung, sowie Swiping, was beispielsweise von Bild zu Bild wischt. Den KDE-Entwicklern stehen diese Features zur Verfügung – und damit bald den Anwendern. (ake)
|
Infos |
|---|
|
[1] Qt 4.6: [http://doc.qt.nokia.com/4.6/qt4-6-intro.html] [2] Programmierbeispiele: [ftp://ftp.linux-magazin.de/pub/listings/magazin/2010/03/qt-kde] [3] Animations-Framework: [http://qt.nokia.com/doc/4.6/animation-overview.html] [4] Johan Thelin, “Plasmoide programmieren in C++”: Linux-Magazin 12/09, S. 110 |
|
Der Autor |
|---|
|
Johan Thelin arbeitet als Berater und freiberuflicher Autor. Seine Leidenschaft gilt eingebetteten Systemen und Qt, aber er spielt mit allem zwischen Elektronik und Webentwicklung herum. Gesten hat er Qt schon in Version 4.0 erklärt. |





