Qt Animation Framework overview
"Accidenti - grideranno alcuni - un altro post serio... Cosa starà succedendo?". Ebbene, sembra che in questo momento io abbia decisamente poco da fare, qua a lavoro. E le direttive sono:
Apriti una birra.
Apri un sacchetto di patatine.
Mentre mangi e bevi documentati su qualcosa e approfondisci argomenti che possano esserti di aiuto in ambito lavorativo.
Ovviamente l'unica direttiva seria è la terza. E io l'ho seguita alla lettera.
Ultimamente mi sono trovato spesso a lavorare con il C++, linguaggio che conosco per sentito dire e che è notoriamente abbastanza complesso. Per colmare queste lacune, senza però trascurare il lato divertente della cosa, ho deciso di sperimentare con il C++ e il Qt Animation Framework. Ah, questo sconosciuto!
Per la vostra gioia condividerò con voi le mie prove, per darvi eventuali spunti, nonché per ricevere eventuali critiche e suggerimenti su come migliorare il tutto.
Comunicazione di servizio. Il tag codeblock di b2evolution non funziona a modo, ma dato che volevo pubblicare il post prima dell'uscita di Qt5 ho deciso di infischiarmene, dei problemi di formattazione, e di postarlo lo stesso. Spero che i contenuti risultino abbastanza interessanti da far dimenticare le enormi praterie che vengono fuori chissà per quale motivo, sotto gli ultimi codeblock.
EDIT: Grazie al nostro amico Slash ora tutto funziona come dovrebbe (a parte lo skin, che ancora proprio non va bene)...
Follow up:
Partiamo dicendo che non ho una grande conoscenza di qmake e dei suoi files di progetto, quindi mi sono affidato al caro Qt Creator offertoci in dono da QtSoftware (ex Trolltech, ora parte di Nokia). Consiglio a chiunque mi segue di fare altrettanto, o di documentarsi bene sul funzionamento di qmake. Se poi oltretutto, dopo essersi documentato, questo qualcuno, riporta eventuali problemi o migliorie da effettuare al progetto giuro che sarò molto contento.
L'obiettivo di questo tutorial è di arrivare a scrivere un'applicazione del tutto inutile, che consiste in un bottone che si muove casualmente sullo schermo/sugli schermi quando ci si clicka sopra (potevo per caso scegliere un esempio serio??), ovviamente con un'animazione gradevole alla vista. Insomma, la tipica inutility fashion.
-
Creiamo quindi un progetto con il nostro amato Qt Creator, aggiungiamo un bel main.cpp, e una classe RandomMovingButton (con il suo .h e il suo .cpp). Il risultante file .pro dovrebbe assomigliare vagamente a questo:
Text:
SOURCES += main.cpp \ randommovingbutton.cpp HEADERS += randommovingbutton.h Se così è possiamo passare alla fase successiva dello sviluppo dell'applicazione di prova, altrimenti vi suggerisco vivamente di abbandonare la programmazione per dedicarvi a qualcosa di più adatto a voi, come l'uncinetto.
-
Partiamo quindi da una panoramica dell'interfaccia della nostra classe: RandomMovingButton (randommovingbutton.h, leggibile, chiaro semplice).
C++:
#ifndef RANDOMMOVINGBUTTON_H #define RANDOMMOVINGBUTTON_H #include <QPushButton> #include <QPropertyAnimation> class RandomMovingButton : public QPushButton { Q_OBJECT public: RandomMovingButton(); private: QPropertyAnimation animation; private slots: void moveIt(); }; #endif // RANDOMMOVINGBUTTON_H Questo file si spiega da solo. Ma io farò lo sforzo di spiegarlo più nel dettaglio, per quanto possibile. Il nostro RandomMovingButton deriva direttamente dal QPushButton di Qt. Questo ci permette di avere già un pulsante totalmente funzionante, pronto per essere esteso!
Quello che facciamo è aggiungergli un attributo privato, contenente un QPropertyAnimation (l'animazione), e uno slot, sempre privato, che si occuperà di impostare le informazioni necessarie all'animazione e di farla partire.
Ecco fatto. Semplice, conciso, minimale ma totalmente funzionale per il nostro scopo.
-
Diamo un'occhiata adesso all'implementazione di tale classe (randommovingbutton.cpp):
C++:
#include <QApplication> #include <QRect> #include <QDesktopWidget> #include <QEasingCurve> #include "randommovingbutton.h" RandomMovingButton::RandomMovingButton(): QPushButton("Push Me"), animation(this, "geometry") { connect(this, SIGNAL(clicked()), this, SLOT(moveIt())); animation.setDuration(1000); animation.setEasingCurve(QEasingCurve::InOutBack); } void RandomMovingButton::moveIt() { animation.stop(); QRect button_geometry = this->geometry(); animation.setStartValue(button_geometry); QDesktopWidget *desktop = qApp->desktop(); int screen_count = desktop->screenCount(); int sel_screen = qrand() % screen_count; QRect screen_geometry = desktop->screenGeometry(sel_screen); int x = qrand() % ( screen_geometry.x() + screen_geometry.width() - button_geometry.width() ); int y = qrand() % ( screen_geometry.y() + screen_geometry.height() - button_geometry.height() ); animation.setEndValue( QRect(x, y, button_geometry.width(), button_geometry.height()) ); animation.start(); } Andiamo a vedere blocco per blocco, il significato di queste istruzioni.
C++:
RandomMovingButton::RandomMovingButton(): QPushButton("Push Me"), animation(this, "geometry") { connect(this, SIGNAL(clicked()), this, SLOT(moveIt())); animation.setDuration(1000); animation.setEasingCurve(QEasingCurve::InOutBack); } Il costruttore di RandomMovingbutton funziona in maniera abbastanza semplice: inizializza in cascata: QPushButton, dandogli come etichetta "Push Me", e l'animazione definita come attributo nell'interfaccia vista precedentemente, dicendogli che dovrà avere a che fare con l'oggetto stesso, mettendo le mani sulla Q_PROPERTY "geometry".
EDIT: come il buon Rugginoso suggerisce, se si intende modificare solamente la posizione del widget si può collegare l'animazione semplicemente alla Q_PROPERTY "pos", che essendo un semplice QPoint è abbastanza più facile da gestire.
Per una documentazione più precisa sul QPropertyAnimation consiglio la lettura di questa pagina, mentre per quanto riguarda le property consiglio quest'altra. Dopo aver inizializzato in cascata i vari componenti necessari si provvede a connettere il signal "clicked()" di QPushButton con il nostro slot custom, "moveIt". Le altre due righe servono rispettivamente a impostare la durata dell'animazione (in msecs) e la curva di accelerazione. Una lista comprensiva di tutte le curve preimpostate può essere trovata in questa pagina.
C++:
animation.stop(); Qui fermo qualsiasi animazione in corso. In questo modo, clickando sul pulsante quando ancora l'animazione non è ancora terminata, questa ripartirà dal punto corrente, e non dal punto destinazione.
C++:
QRect button_geometry = this->geometry(); animation.setStartValue(button_geometry); Qua salvo in una variabile temporanea il geometry corrente del button. Così da poterlo poi utilizzare come punto di partenza per l'animazione.
C++:
QDesktopWidget *desktop = qApp->desktop(); Recupero quindi con questa riga di codice il widget che mappa lo schermo (o gli schermi). Maggiori informazioni su questo utilissimo widget possono essere trovate in questa pagina.
C++:
int screen_count = desktop->screenCount(); int sel_screen = qrand() % screen_count; A questo punto seleziono casualmente uno degli schermi attualmente collegati
C++:
QRect screen_geometry = desktop->screenGeometry(sel_screen); int x = qrand() % ( screen_geometry.x() + screen_geometry.width() - button_geometry.width() ); int y = qrand() % ( screen_geometry.y() + screen_geometry.height() - button_geometry.height() ); Con questi complessi calcoli faccio in modo di ottenere delle coordinate casuali in cui posizionare il mio widget alla fine dell'animazione. In pratica quello che faccio è selezionare un punto (x,y) compreso nello schermo selezionato, escludendo però le coordinate che porterebbero il button a disegnarsi, anche solo parzialmente, fuori dallo schermo selezionato.
C++:
animation.setEndValue( QRect(x, y, button_geometry.width(), button_geometry.height()) ); animation.start(); Imposto tali coordinate come punto di arrivo del widget e
avvio l'animazione! Semplice, ma allo stesso tempo accattivante! -
Infine il main. Breve, contenente solamente lo stretto
indispensabile, come siamo abituati a vedere, lavorando con Qt.C++:
#include <QApplication>; #include <QObject> #include <QRect> #include "randommovingbutton.h" int main(int argc, char** argv) { QApplication app(argc, argv); RandomMovingButton *button = new RandomMovingButton(); button->show(); return app.exec(); } Qua non faccio altro che istanziare il QApplication, il RandomMovingButton, a rendere visibile quest'ultimo e a far partire il loop degli eventi Qt. Tutto chiaro, no?
Ovviamente, come per il restante codice presente all'interno del mio blog, questo può esser utilizzato, smembrato, ricomposto, incluso in progetti ecc... Fate tranquillamente di questo codice la vostra prostituta. Utilizzatelo senza ritegno. Non me ne avrò a male.
Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/skins/_item_feedback.inc.php on line 156
Trackback address for this post
Trackback URL (right click and copy shortcut/link location)
5 comments
Puoi usare la proprietà pos http://qt.nokia.com/doc/4.6/qwidget.html#pos-prop che è un comodo QPoint invece di geometry che è un QRect.
Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/skins/_item_comment_form.inc.php on line 71
Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/skins/_item_comment_form.inc.php on line 115
Comments are not allowed from anonymous visitors.