QML\Qt Quick на практике

1,315 views

Published on

QML\Qt Quick это превосходный декларативный язык программирования, призванный сильно упростить создание и дальнейшую поддержку пользовательских интерфейсов.В докладе я расскажу что из себя представляет QML, попробуем разобраться в вопросе “Где и как уместно использовать QML\Qt Quick” и приведу краткий обзор полезных инструментов для разработки QML\Qt Quick приложений.

Published in: Software
  • Be the first to comment

QML\Qt Quick на практике

  1. 1. QMLQt Quick на практике Хомяков Сергей
  2. 2. Какой UI требует рынок?
  3. 3. Требуется тесное взаимодействие между разработчиком и дизайнером
  4. 4. horizontalLayoutWidget->setGeometry(QRect(10, 0, 501, 51)); horizontalLayout = new QHBoxLayout(horizontalLayoutWidget); horizontalLayout->setSpacing(6); horizontalLayout->setContentsMargins(11, 11, 11, 11); horizontalLayout->setContentsMargins(0, 0, 0, 0); pushButton_2 = new QPushButton(horizontalLayoutWidget); horizontalLayout->addWidget(pushButton_2); pushButton_4 = new QPushButton(horizontalLayoutWidget); horizontalLayout->addWidget(pushButton_4); pushButton_3 = new QPushButton(horizontalLayoutWidget); horizontalLayout->addWidget(pushButton_3); pushButton = new QPushButton(horizontalLayoutWidget); horizontalLayout->addWidget(pushButton); horizontalSlider = new QSlider(centralWidget); horizontalSlider->setGeometry(QRect(10, 60, 501, 16)); horizontalSlider->setOrientation(Qt::Horizontal); textEdit = new QTextEdit(centralWidget); textEdit->setGeometry(QRect(20, 90, 481, 201)); MainWindow->setCentralWidget(centralWidget);
  5. 5. RowLayout { anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 18 Button { text: qsTr("Button 1") } Button { text: qsTr("Button 2") } Button { text: qsTr("Button 3") } Button { text: qsTr("Button 4") } } Slider { x: 143; y: 57 width: 355; height: 28 } TextArea { x: 142; y: 105 width: 355; height: 150 }
  6. 6. Что такое QML?
  7. 7. QML это JSON-подобный декларативный язык программирования, основанный на JavaScript, использующий С++ API для интеграции с Qt Qt Quick это scenegraph-based UI framework, использующий в качестве языка программирования QML и позиционирующий себя как инструмент для быстрой разработки и прототипирования
  8. 8. Где уместно использовать Qt Quick? Там где вы уже используете Qt Там где требуется не стандартный (вычурный) UI Там где необходим кросплатформенный “look and feel” Там где есть постоянно меняющиеся требования к дизайну и бизнес- логике
  9. 9. Hello World
  10. 10. Hello World
  11. 11. Hello World
  12. 12. Hello World import QtQuick 2.0 Rectangle { id: root width: 120; height: 240 color: "#D8D8D8" Image { id: world x: (parent.width - width)/2 y: 40 source: 'assets/world.png' } Text { y: world.y + world.height + 20 width: root.width horizontalAlignment: Text.AlignHCenter text: 'Hello World' } }
  13. 13. import QtQuick 2.2 Rectangle { id: photo objectName: "photo" property bool thumbnail: false property alias image: photoImage.source signal clicked(); color: "gray" x: 20; y: 20 height: 150 width: { if(photoImage.width > 200) { photoImage.width; } else { 200; } } function doSomething(dx) { return dx + photoImage.width; } onHeightChanged: { var tmp = doSomething(photo.height); console.log( tmp ); } QtObject { id: p_attr readonly property color borderSelectedColor: "red" readonly property color borderUnSelectedColor: "black" property int animationDuration: 200 } Rectangle { id: border anchors.centerIn: parent Image { id: photoImage; anchors.centerIn: parent } } MouseArea { anchors.fill: parent onClicked: photo.clicked(); } states:[ State { name: "Selected" PropertyChanges { target: border; color: p_attr.borderSelectedColor } }, State { name: "UnSelected" PropertyChanges { target: border; color: p_attr.borderUnSelectedColor } } ] transitions: Transition { to: "Selected" ColorAnimation { target: border; duration: p_attr.animationDuration } } }
  14. 14. import QtQuick 2.2 Rectangle { id: photo objectName: "photo" property bool thumbnail: false property alias image: photoImage.source signal clicked(); color: "gray" x: 20; y: 20 height: 150 width: { if(photoImage.width > 200) { photoImage.width; } else { 200; } } function doSomething(dx) { return dx + photoImage.width; } onHeightChanged: { var tmp = doSomething(photo.height); console.log( tmp ); } QtObject { id: p_attr readonly property color borderSelectedColor: "red" readonly property color borderUnSelectedColor: "black" property int animationDuration: 200 } Rectangle { id: border anchors.centerIn: parent Image { id: photoImage; anchors.centerIn: parent } } MouseArea { anchors.fill: parent onClicked: photo.clicked(); } states:[ State { name: "Selected" PropertyChanges { target: border; color: p_attr.borderSelectedColor } }, State { name: "UnSelected" PropertyChanges { target: border; color: p_attr.borderUnSelectedColor } } ] transitions: Transition { to: "Selected" ColorAnimation { target: border; duration: p_attr.animationDuration } } }
  15. 15. import QtQuick 2.2 Rectangle { id: photo objectName: "photo" property bool thumbnail: false property alias image: photoImage.source signal clicked(); color: "gray" x: 20; y: 20 height: 150 width: { if(photoImage.width > 200) { photoImage.width; } else { 200; } } function doSomething(dx) { return dx + photoImage.width; } onHeightChanged: { var tmp = doSomething(photo.height); console.log( tmp ); } QtObject { id: p_attr readonly property color borderSelectedColor: "red" readonly property color borderUnSelectedColor: "black" property int animationDuration: 200 } Rectangle { id: border anchors.centerIn: parent Image { id: photoImage; anchors.centerIn: parent } } MouseArea { anchors.fill: parent onClicked: photo.clicked(); } states:[ State { name: "Selected" PropertyChanges { target: border; color: p_attr.borderSelectedColor } }, State { name: "UnSelected" PropertyChanges { target: border; color: p_attr.borderUnSelectedColor } } ] transitions: Transition { to: "Selected" ColorAnimation { target: border; duration: p_attr.animationDuration } } }
  16. 16. import QtQuick 2.2 Rectangle { id: photo objectName: "photo" property bool thumbnail: false property alias image: photoImage.source signal clicked(); color: "gray" x: 20; y: 20 height: 150 width: { if(photoImage.width > 200) { photoImage.width; } else { 200; } } function doSomething(dx) { return dx + photoImage.width; } onHeightChanged: { var tmp = doSomething(photo.height); console.log( tmp ); } QtObject { id: p_attr readonly property color borderSelectedColor: "red" readonly property color borderUnSelectedColor: "black" property int animationDuration: 200 } Rectangle { id: border anchors.centerIn: parent Image { id: photoImage; anchors.centerIn: parent } } MouseArea { anchors.fill: parent onClicked: photo.clicked(); } states:[ State { name: "Selected" PropertyChanges { target: border; color: p_attr.borderSelectedColor } }, State { name: "UnSelected" PropertyChanges { target: border; color: p_attr.borderUnSelectedColor } } ] transitions: Transition { to: "Selected" ColorAnimation { target: border; duration: p_attr.animationDuration } } }
  17. 17. import QtQuick 2.2 Rectangle { id: photo objectName: "photo" property bool thumbnail: false property alias image: photoImage.source signal clicked(); color: "gray" x: 20; y: 20 height: 150 width: { if(photoImage.width > 200) { photoImage.width; } else { 200; } } function doSomething(dx) { return dx + photoImage.width; } onHeightChanged: { var tmp = doSomething(photo.height); console.log( tmp ); } QtObject { id: p_attr readonly property color borderSelectedColor: "red" readonly property color borderUnSelectedColor: "black" property int animationDuration: 200 } Rectangle { id: border anchors.centerIn: parent Image { id: photoImage; anchors.centerIn: parent } } MouseArea { anchors.fill: parent onClicked: photo.clicked(); } states:[ State { name: "Selected" PropertyChanges { target: border; color: p_attr.borderSelectedColor } }, State { name: "UnSelected" PropertyChanges { target: border; color: p_attr.borderUnSelectedColor } } ] transitions: Transition { to: "Selected" ColorAnimation { target: border; duration: p_attr.animationDuration } } }
  18. 18. import QtQuick 2.2 Rectangle { id: photo objectName: "photo" property bool thumbnail: false property alias image: photoImage.source signal clicked(); color: "gray" x: 20; y: 20 height: 150 width: { if(photoImage.width > 200) { photoImage.width; } else { 200; } } function doSomething(dx) { return dx + photoImage.width; } onHeightChanged: { var tmp = doSomething(photo.height); console.log( tmp ); } QtObject { id: p_attr readonly property color borderSelectedColor: "red" readonly property color borderUnSelectedColor: "black" property int animationDuration: 200 } Rectangle { id: border anchors.centerIn: parent Image { id: photoImage; anchors.centerIn: parent } } MouseArea { anchors.fill: parent onClicked: photo.clicked(); } states:[ State { name: "Selected" PropertyChanges { target: border; color: p_attr.borderSelectedColor } }, State { name: "UnSelected" PropertyChanges { target: border; color: p_attr.borderUnSelectedColor } } ] transitions: Transition { to: "Selected" ColorAnimation { target: border; duration: p_attr.animationDuration } } }
  19. 19. import QtQuick 2.2 Rectangle { id: photo objectName: "photo" property bool thumbnail: false property alias image: photoImage.source signal clicked(); color: "gray" x: 20; y: 20 height: 150 width: { if(photoImage.width > 200) { photoImage.width; } else { 200; } } function doSomething(dx) { return dx + photoImage.width; } onHeightChanged: { var tmp = doSomething(photo.height); console.log( tmp ); } QtObject { id: p_attr readonly property color borderSelectedColor: "red" readonly property color borderUnSelectedColor: "black" property int animationDuration: 200 } Rectangle { id: border anchors.centerIn: parent Image { id: photoImage; anchors.centerIn: parent } } MouseArea { anchors.fill: parent onClicked: photo.clicked(); } states:[ State { name: "Selected" PropertyChanges { target: border; color: p_attr.borderSelectedColor } }, State { name: "UnSelected" PropertyChanges { target: border; color: p_attr.borderUnSelectedColor } } ] transitions: Transition { to: "Selected" ColorAnimation { target: border; duration: p_attr.animationDuration } } }
  20. 20. import QtQuick 2.2 Rectangle { id: photo objectName: "photo" property bool thumbnail: false property alias image: photoImage.source signal clicked(); color: "gray" x: 20; y: 20 height: 150 width: { if(photoImage.width > 200) { photoImage.width; } else { 200; } } function doSomething(dx) { return dx + photoImage.width; } onHeightChanged: { var tmp = doSomething(photo.height); console.log( tmp ); } QtObject { id: p_attr readonly property color borderSelectedColor: "red" readonly property color borderUnSelectedColor: "black" property int animationDuration: 200 } Rectangle { id: border anchors.centerIn: parent Image { id: photoImage; anchors.centerIn: parent } } MouseArea { anchors.fill: parent onClicked: photo.clicked(); } states:[ State { name: "Selected" PropertyChanges { target: border; color: p_attr.borderSelectedColor } }, State { name: "UnSelected" PropertyChanges { target: border; color: p_attr.borderUnSelectedColor } } ] transitions: Transition { to: "Selected" ColorAnimation { target: border; duration: p_attr.animationDuration } } }
  21. 21. import QtQuick 2.2 Rectangle { id: photo objectName: "photo" property bool thumbnail: false property alias image: photoImage.source signal clicked(); color: "gray" x: 20; y: 20 height: 150 width: { if(photoImage.width > 200) { photoImage.width; } else { 200; } } function doSomething(dx) { return dx + photoImage.width; } onHeightChanged: { var tmp = doSomething(photo.height); console.log( tmp ); } QtObject { id: p_attr readonly property color borderSelectedColor: "red" readonly property color borderUnSelectedColor: "black" property int animationDuration: 200 } Rectangle { id: border anchors.centerIn: parent Image { id: photoImage; anchors.centerIn: parent } } MouseArea { anchors.fill: parent onClicked: photo.clicked(); } states:[ State { name: "Selected" PropertyChanges { target: border; color: p_attr.borderSelectedColor } }, State { name: "UnSelected" PropertyChanges { target: border; color: p_attr.borderUnSelectedColor } } ] transitions: Transition { to: "Selected" ColorAnimation { target: border; duration: p_attr.animationDuration } } }
  22. 22. // Circle.qml import QtQuick 2.0 Rectangle { property real diameter : 30 width: diameter height: diameter radius: diameter } import QtQuick 2.0 Rectangle { width: 120; height: 240 Column { anchors.fill: parent Repeater { model: 3 Circle { color: "blue" diameter: 90 } } } }
  23. 23. Позиционирование и выравнивание
  24. 24. Якоря бывают двух видов: -ссылающиеся на элемент (centerIn, fill) -ссылающиеся на другой якорь ( left, right, top, bottom, …. )
  25. 25. import QtQuick 2.0 Rectangle { id: root width: 120; height: 240 color: "#D8D8D8" Image { id: world anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 40 source: 'assets/world.png' } Text { anchors.top: world.bottom anchors.topMargin: 20 anchors.horizontalCenter: world.horizontalCenter text: 'Hello World' } } Hello World
  26. 26. Причём тут С++?
  27. 27. С++ API позволяет: – экспортировать в QML C++ обьекты наследованные от QObject – экспортировать в QML не визуальные типы – классы на основе QQuickPaintedItem для визуальных элементов с поддержкой QPainter – классы на основе QQuickItem для визуальных элементов сцены
  28. 28. Экспорт С++ объекта
  29. 29. class User : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged) public: User(const QString &name, int age, QObject *parent = 0); ... }
  30. 30. void main( int argc, char* argv[] ) { ... User *currentUser = new User("Alice", 29); QQuickView *view = new QQuickView; QQmlContext *context = view->engine()->rootContext(); context->setContextProperty("currentUser", currentUser); ... }
  31. 31. void main( int argc, char* argv[] ) { ... User *currentUser = new User("Alice", 29); QQuickView *view = new QQuickView; QQmlContext *context = view->engine()->rootContext(); context->setContextProperty("currentUser", currentUser); ... } Text { text : currentUser.name }
  32. 32. Экспорт С++ типа
  33. 33. #include <QObject> class CustomTimer : public QObject { Q_OBJECT Q_PROPERTY( int interval READ interval WRITE setInterval NOTIFY intervalChanged ) public: CustomTimer(QObject *parent = 0); int interval() const; public slots: void start(); void stop(); void setInterval(int arg); signals: void intervalChanged(int arg); void timeout(); private: QTimer* m_timer; int m_interval; };
  34. 34. #include <QGuiApplication> #include <QQuickView> #include "CustomTimer.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterType<CustomTimer>("CustomComponents", 1, 0, "CustomTimer"); QQuickView view; view.setSource(QUrl("qrc:///main.qml")); view.show(); return app.exec(); }
  35. 35. import CustomComponents 1.0 Rectangle { id: root Component.onCompleted: { timer.start(); } …….. CustomTimer { id: timer interval: 3000 onTimeout: { console.log( "Timer timeout!" ); root.color = "red"; } } }
  36. 36. Экспорт QQuickPaintedltem
  37. 37. #include <QQuickPaintedItem> class EllipseItem : public QQuickPaintedItem { Q_OBJECT public: EllipseItem(QQuickItem *parent = 0); void paint(QPainter *painter); }; void EllipseItem::paint(QPainter *painter) { const qreal halfPenWidth = qMax(painter->pen().width() / 2.0, 1.0); QRectF rect = boundingRect(); rect.adjust(halfPenWidth, halfPenWidth, -halfPenWidth, -halfPenWidth); painter->drawEllipse(rect); }
  38. 38. #include <QGuiApplication> #include <QQuickView> #include "EllipseItem.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterType<EllipseItem>("CustomComponents", 1, 0, "Ellipse"); QQuickView view; view.setSource(QUrl("qrc:///main.qml")); view.show(); return app.exec(); }
  39. 39. import CustomComponents 1.0 Rectangle { id: root EllipseItem { anchors.centerIn: parent width: 64 height: 48 } }
  40. 40. Экспорт QQuickltem
  41. 41. #include <QQuickItem> #include <QSGGeometry> #include <QSGFlatColorMaterial> class TriangleItem : public QQuickItem { Q_OBJECT public: TriangleItem(QQuickItem *parent = 0); protected: QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data); private: QSGGeometry m_geometry; QSGFlatColorMaterial m_material; };
  42. 42. QSGNode *TriangleItem::updatePaintNode(QSGNode *n, UpdatePaintNodeData *) { QSGGeometryNode *node = static_cast<QSGGeometryNode *>(n); if (!node) node = new QSGGeometryNode(); QSGGeometry::Point2D *v = m_geometry.vertexDataAsPoint2D(); const QRectF rect = boundingRect(); v[0].x = rect.left(); v[0].y = rect.bottom(); v[1].x = rect.left() + rect.width()/2; v[1].y = rect.top(); v[2].x = rect.right(); v[2].y = rect.bottom(); node->setGeometry(&m_geometry); node->setMaterial(&m_material); return node; }
  43. 43. QSGNode * QQuickCustomItem::updatePaintNode(QSGNode * node, UpdatePaintNodeData * nodedata) { TexureHolderNode * texture_node = static_cast<TexureHolderNode *>(node); if (!texture_node) texture_node = new TexureHolderNode(); if (texture_node->fbo_ && (texture_node->fbo_->width() != width() || texture_node->fbo_->height() != height())) { texture_node->fbo_.reset(); } if (texture_node->fbo_.isNull()) { QSize fboSize(qMax<int>(1, int(width())), qMax<int>(1, int(height()))); QOpenGLFramebufferObjectFormat format; texture_node->fbo_.reset(new QOpenGLFramebufferObject(fboSize, format)); texture_node->setTexture( window()->createTextureFromId( texture_node->fbo_->texture(), texture_node->fbo_->size(), 0) ); texture_node->setRect(0, 0, width(), height()); redraw_texture_needed_ = true; } if (redraw_texture_needed_) { redraw_texture_needed_ = false; texture_node->fbo_->bind(); { paintGL(); } texture_node->fbo_->bindDefault(); texture_node->markDirty(QSGNode::DirtyMaterial); } return texture_node; } class TexureHolderNode : public QSGSimpleTextureNode { public: TexureHolderNode() {} QScopedPointer<QOpenGLFramebufferObject> fbo_; };
  44. 44. https://github.com/2gis/qtandroidextensions
  45. 45. Что нужно оставлять в плюсах, что лучше перенести в QML?
  46. 46. Инструменты разработки и отладки
  47. 47. Autopilot-Qt5QtCreator GammaRay Squish – qt.io/ru/download-open-source – kdab.com/gammaray – froglogic.com/squish – wiki.ubuntu.com/Touch/Testing/Autopilot
  48. 48. Вопросы? Хомяков Сергей s.homyakov@2gis.ru

×