На днях вышло так называемое «технологическое превью» (technological preview) Qt 4.6, которое позволяет уже сейчас попробовать новые фичи, которые войдут в релиз 4.6 этого замечательного фреймворка. Перечислять новшества я не буду — они были достаточно хорошо освещены в этом топике, а подробнее остановлюсь на двух из них: State Machine и Qt Animation Framevork.
Итак, что же они из себя представляют?
State Machine — это ничто иное, как конечный автомат: некий объект с заранее заданным конечным числом состояний и переходами между ними.
В Qt 4.6 реализовано именно так: мы можем создать нужное нам число состояний, и для каждого состояния указать, какие свойства имеет каждый объект в данном состоянии, после чего задать переходы между состояниями, указать стартовую точку и запустить автомат на исполнение.
Но для начала нам потребуется сам Qt 4.6. Я расскажу, как собрать его из исходников для Linux (применимо для любого дистрибутива). Сборка под Windows, думаю, мало отличается, если предварительно установить MinGW. Возможно, собрать можно и с помощью MSVS, но сам я не пробовал.
Сборка Qt 4.6
Я расскажу о своём способе сборки Qt 4.6 без какого-либо «негативного влияния» на текущую версию, установленную из репозитория. Возможно, мой способ не самый оптимальный, но по крайней мере он работает :)
Сразу предупреждаю: для сборки потребуется ~3 ГБ места на диске, а потом ещё ~900 МБ для установки (всё, что останется от сборки, потом можно будет удалить). Занимает так много, потому что во-первых содержит дебаг-информацию (т.к. это ещё не релиз), плюс это не просто библиотеки, но также примеры, документация и т.д.
Для начала скачиваем исходный код отсюда (tar.gz) или отсюда (zip). Распаковываем, переходим в каталог с исходниками, запускаем конфигурирование
$ ./configure --prefix=/opt/Qt4.6
Указывая подобный префикс, мы убиваем двух зайцев: 1) никак не влияем на уже установленную версию; и 2) можем впоследствии удалить всё «лёгким движением руки».
В процессе конфигурирования зададут вопрос о лицензии: отвечаем «о» (open source версия, включающая GPL и LGPL), соглашаемся с ней (говорим «yes»), ждём несколько минут. После окончания нам ещё раз напомнят, с каким префиксом было произведено конфигурирование.
Ну и, наконец, сборка! Запускаем
$ make
И ждём. Ждём. Ждём…
Пока идёт сборка, можно не просто сходить заварить себе чаю/кофе, но и заняться уборкой в комнате: на моём четырёхъядернике сборка в 4 потока заняла 40 минут. Количество потоков (по числу ядер процессора) можно указать при помощи опции "-j":
$ make -j 4
После окончания сборки устанавливаем с помощью
$ sudo make install
В общем случае такая установка крайне нежелательна, но в данном конкретном случае мы можем себе это позволить, т.к. обезопасили себя указанием нужного префикса, и стройность нашей системы от этого не особо пострадает.
Настройка
Теперь нам нужно получить возможность использовать новую версию вместо той, что установлена в системе. Всё, что нам для этого нужно — это использовать правильный qmake, а он уже сам подхватит нужные инклюды и библиотеки и сгенерирует Makefile с их использованием.
Тут у нас есть 3 различных варианта:
- каждый раз вызывать qmake с полным указанием пути:
$ /opt/Qt4.6/bin/qmake
- установить путь для qmake на время терминального сеанса:
$ export PATH=/opt/Qt4.6/bin:$PATH
- прописать строку выше в ~/.bashrc, и тогда этот qmake будет использоваться всегда
Я использую второй вариант — для экспериментов это более, чем достаточно.
Пробуем
Для проверки того, что мы всё сделали правильно, скомпилируем небольшую программку:
$ mkdir qt_anim $ cd qt_anim $ (vim|emacs|nano|kate|gedit|juffed|...) anim.cpp
anim.cpp:
Copy Source | Copy HTML<br/><br/>#include <QApplication><br/>#include <QWidget><br/> <br/>int main(int argc, char* argv[]) {<br/> QApplication app(argc, argv);<br/> QWidget w;<br/> w.resize(200, 100);<br/> w.show();<br/> return app.exec();<br/>}<br/> <br/>
$ qmake -project $ qmake $ make $ ldd qt_anim | grep Qt
После этого нам должны отрапортовать, что используются те библиотеки, на которые мы рассчитываем. Если нет — убедитесь, что используется нужная версия qmake:
$ which qmake
и если версия не та — проверьте, что вы не ошиблись с настройкой PATH.
Начинаем!
Для начала поймём, что такое State Machine и с чем её едят. Ставим задачу: создать окно, в углу которого будет отображаться маленькая картинка, которая при клике по окну будет перемещаться и увеличиваться в размерах.
anim.cpp:
Copy Source | Copy HTML<br/>#include <QApplication><br/>#include "AnimWidget.h" <br/> <br/>int main(int argc, char* argv[]) {<br/> QApplication app(argc, argv);<br/> AnimWidget w;<br/> w.resize(300, 300);<br/> w.show();<br/> return app.exec();<br/>}<br/> <br/> <br/>
AnimWidget.h:
Copy Source | Copy HTML<br/>#ifndef __ANIM_WIDGET_H__<br/>#define __ANIM_WIDGET_H__<br/> <br/>#include <QLabel><br/>#include <QStateMachine><br/>#include <QWidget><br/> <br/>class AnimWidget : public QWidget {<br/>Q_OBJECT<br/>public:<br/> AnimWidget();<br/> <br/>signals:<br/> /* signal for triggering state machine changes */<br/> void clicked();<br/> <br/>protected:<br/> /* here we will be catching clicks */<br/> virtual void mouseReleaseEvent(QMouseEvent*);<br/> <br/>private:<br/> QStateMachine machine_;<br/> QLabel* photo_;<br/>};<br/> <br/>#endif // __ANIM_WIDGET_H__<br/> <br/>
AnimWidget.cpp:
Copy Source | Copy HTML<br/>#include "AnimWidget.h"<br/> <br/>#include <QState><br/>#include <QVariant><br/> <br/>AnimWidget::AnimWidget() {<br/> photo_ = new QLabel("", this);<br/> photo_->setGeometry( 0, 0, 40, 40);<br/> photo_->setScaledContents(true);<br/> photo_->setPixmap(QPixmap("ottawa.png"));<br/> <br/> /* creating 2 states */<br/> QState* st1 = new QState();<br/> QState* st2 = new QState();<br/> <br/> /* defining photo's properties for each of them */<br/> st1->assignProperty(photo_, "geometry", QRect( 0, 0, 40, 40));<br/> st2->assignProperty(photo_, "geometry", QRect(50, 50, 200, 200));<br/> <br/> /* define transitions between states by clicking on main window*/<br/> st1->addTransition(this, SIGNAL(clicked()), st2);<br/> st2->addTransition(this, SIGNAL(clicked()), st1);<br/> <br/> /* adding states to state machine */<br/> machine_.addState(st1);<br/> machine_.addState(st2);<br/> machine_.setInitialState(st1);<br/> <br/> /* starting machine */<br/> machine_.start();<br/>}<br/> <br/>void AnimWidget::mouseReleaseEvent(QMouseEvent*) {<br/> emit clicked();<br/>}<br/> <br/>
В строках
st1->assignProperty(photo_, "geometry", QRect(0, 0, 40, 40)); st2->assignProperty(photo_, "geometry", QRect(50, 50, 200, 200));
задаются свойства объектов, присущие им в том или ином состоянии. Стандартные Qt-шные классы содержат определённый набор свойств, а для задания свойств для ваших собственных классов используйте Q_PROPERTY (см. документацию).
Строки
st1->addTransition(this, SIGNAL(clicked()), st2); st2->addTransition(this, SIGNAL(clicked()), st1);
определяют переходы между состояниями и условия для этих переходов. В нашем случае условием перехода будет являться сигнал «clicked()», брошенный главным окном.
Переход между состояниями может быть и безусловный — тогда не указывается объект и сигнал, а просто указывается оконечное состояние (см. документацию).
И не забудьте указать другую картинку вместо «ottawa.png» и положить рядом с исходниками (или же скачайте исходники по ссылке ниже).
Собираем:
$ qmake -project $ qmake $ make $ ./qt_anim
Кликаем — прыгает!
Но прыгает моментально. Да, переход между состояниями машины происходит мгновенно — но это ведь не то, что мы хотим, верно? Давайте добавим немного анимации для перехода между состояниями. Для этого добавим следующий код перед строкой «machine_.start();»:
Copy Source | Copy HTML<br/>...<br/>/* adding animation */<br/>QPropertyAnimation* an1 = new QPropertyAnimation(photo_, "geometry");<br/>machine_.addDefaultAnimation(an1);<br/>...<br/> <br/>
и не забудем добавить
#include <QPropertyAnimation>в начало файла.
Тут, в принципе, всё понятно: создаём анимацию для изменения атрибута «geometry» объекта «photo_» и добавляем анимацию в машину.
Собираем, запускаем…
Так намного лучше, правда? Картинка не прыгает, а плавно едет на новое место, изменяясь в размерах.
Можно менять различные параметры анимации, в частности, задавать длительность и «кривую ускорения». Можно сделать, чтобы картинка начинала движение быстро, а потом замедлялась; можно и наоборот, чтобы начинала медленно, а потом разгонялась; а можно и так, чтобы начинала медленно, разгонялась, а потом плавно тормозила. Возможных вариантов — несколько десятков (см. документацию). Я выбрал вариант «разгон — торможение» с кубическим изменением скорости. Добавим следующие строки после создания анимации:
Copy Source | Copy HTML<br/>...<br/>/* customizing the animation */<br/>an1->setEasingCurve(QEasingCurve::InOutCubic);<br/>an1->setDuration(700);<br/>... <br/>
Ну вот, теперь вместо скучного движения с постоянной скоростью, картинка двигается плавно и величаво :)
Собственно говоря, на этом моя статья подходит к концу, а дальше дело остаётся только за вашей фантазией. Применяя этот простой механизм «определяем множество состояний — назначаем свойства объектов для каждого состояния — определяем переходы между состояниями — задаём анимации для переходов», можно добиться впечатляющих результатов.
И небольшо пожелания напоследок: помните, что всё хорошо в меру. Программа, перегруженная анимацией, куда менее приятна в использовании, чем программа, в которой анимация отсутствует вообще, поэтому я надеюсь, вы используете полученные знания во благо :)
Исходники проекта можно взять тут (66 КБ).
Update: добавил ещё один небольшой проект, сделанный на базе первого (368 КБ, в основном за счёт картинок, кода там не намного больше).