Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_core/__core.init.php on line 814

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/sessions/_sessions.init.php on line 268

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_main.inc.php on line 135

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_main.inc.php on line 139

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_main.inc.php on line 147

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_main.inc.php on line 154

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_main.inc.php on line 201

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_main.inc.php on line 207

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_main.inc.php on line 235

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_main.inc.php on line 250

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_main.inc.php on line 261

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_main.inc.php on line 273

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/files/model/_file.funcs.php on line 583

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_connect_db.inc.php on line 29

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_core/_param.funcs.php on line 1739

Warning: Cannot modify header information - headers already sent by (output started at /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_core/__core.init.php:814) in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/sessions/model/_session.class.php on line 222

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/items/model/_item.funcs.php on line 114

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_core/model/dataobjects/_dataobject.class.php on line 417

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_core/model/dataobjects/_dataobject.class.php on line 433

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_core/model/dataobjects/_dataobject.class.php on line 442

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_blog_main.inc.php on line 507

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/items/model/_item.class.php on line 1877

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/items/model/_item.class.php on line 3546

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/items/model/_itemlist.class.php on line 538

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/items/model/_itemlistlight.class.php on line 118

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/items/model/_itemlistlight.class.php on line 178

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/items/model/_itemlistlight.class.php on line 833

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/generic/model/_genericelement.class.php on line 109

Warning: Cannot modify header information - headers already sent by (output started at /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_core/__core.init.php:814) in /home/mhd-01/www.kojia.net/htdocs/uranio/inc/_core/_template.funcs.php on line 59

Deprecated: Assigning the return value of new by reference is deprecated in /home/mhd-01/www.kojia.net/htdocs/uranio/plugins/code_highlight_plugin/_code_highlight.plugin.php on line 344
Qt Animation Framework overview

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:

  1. Apriti una birra.

  2. Apri un sacchetto di patatine.

  3. 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.

  1. 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.

  2. 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.

  3. 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!

  4. 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

Comment from: Slash [Member] Email
Duplo, premesso che i veri programmatori si fixano da soli i bug... ti ho corretto i problemi con il codeblock, ora per piacere cambia tema e mettine uno che non mi faccia venir voglia di cancellarti l'account tutte le volte che apro il blog.
12.01.10 @ 01:16
Comment from: Duplo [Member] Email
Fantastico, cos'era che non andava? Comunque sì, questo tema non piace affatto nemmeno a me, ma non riesco a trovarne uno che mi piaccia un minimo. Se non fosse che sono un completo disastro come grafico mi farei i templates da solo. Oppure potrei prendere in considerazione l'idea di farmi il porting di qualche tema di wordpress.
12.01.10 @ 01:35
Comment from: Giovanni Bajo [Visitor]
@slash è per questo che esistono i feed rss :)
12.01.10 @ 12:20
Comment from: Il Rugginoso [Visitor] · http://rugginoso.homelinux.org/
Visto che ti interessa solo spostare il bottone e non modificare la sua dimensione, perchè non animi semplicmente la sua posizione?
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.
:fonzie:
12.01.10 @ 18:52
Comment from: Duplo [Member] Email
Giusto. In realtà l'idea iniziale era di modificare anche la dimensione, per rendere il tutto ancora più inutile. Poi ho cominciato a sperimentare e non ho aggiunto l'animazione per la dimensione. Grazie comunque per la segnalazione, che provvederò a pubblicare quanto prima ;)
12.01.10 @ 19:13

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.