QT C++在2D圖形方面已經做的很完善了,在PC端(Windows、Linux和MaC)上都有很好的表現。
QT中的QML特別適合於移動端應用的開發,QML中的一些基本形狀類型並不是一一地與Qt C++相對應,但是通過C++可以來擴展QML。
QQuickPaintedItem繼承自QQuickItem,提供了使用QPainter API的方法來擴展QML中的2D圖形項。
QQuickPaintedItem沒有像QGraphicsItem那樣提供shape()方法來獲取圖形項的具體實際形狀,但是其包含contains()方法,我們可以間接地結合鼠標操作點來判斷是否包含在實際形狀范圍內。
廢話不多說,直接上測試代碼:
CustomItem.h
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#ifndef
CUSTOMITEM_H #define CUSTOMITEM_H #include <QQuickPaintedItem> #include <QQuickItem> #include <QMouseEvent> #include <QPainter> class CustomItem : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(QColor color READ color WRITE setColor) public : CustomItem(QQuickItem *parent = nullptr); QString name() const ; void setName( const QString &name); QColor color() const ; void setColor( const QColor &color); // override paint() for actual painting. virtual void paint(QPainter *painter); // override contans() for shape mask, Q_INVOKABLE for QML use. Q_INVOKABLE virtual bool contains( const QPointF &point) const ; virtual void mousePressEvent(QMouseEvent *event); virtual void mouseMoveEvent(QMouseEvent *event); virtual void mouseReleaseEvent(QMouseEvent *event); private : QString m_name; QColor m_color; }; #endif // CUSTOMITEM_H |
CustomItem.cpp
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
#include
"CustomItem.h"
#include <QPainter> #include <QPainterPathStroker> #include <QDebug> CustomItem::CustomItem(QQuickItem *parent) : QQuickPaintedItem(parent) { setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton); setFlag(QQuickItem::ItemHasContents); } QString CustomItem::name() const { return m_name; } void CustomItem::setName( const QString &name) { m_name = name; } QColor CustomItem::color() const { return m_color; } void CustomItem::setColor( const QColor &color) { m_color = color; } void CustomItem::paint(QPainter *painter) { // pen & brush QPen pen(m_color, 10 ); QLinearGradient gradient; gradient.setStart( 0 , 50 ); gradient.setFinalStop( 200 , 50 ); gradient.setColorAt( 0 , Qt::blue); gradient.setColorAt( 0 . 2 , Qt::green); gradient.setColorAt( 0 . 4 , Qt::red); gradient.setColorAt( 0 . 6 , Qt::yellow); gradient.setColorAt( 1 , Qt::cyan); painter->setPen(pen); painter->setBrush(gradient); painter->setRenderHints(QPainter::Antialiasing, true ); // Unclosed shape static const QPointF points[ 3 ] = { QPointF( 10 . 0 , 100 . 0 ), QPointF( 20 . 0 , 10 . 0 ), QPointF( 200 . 0 , 50 . 0 ), }; painter->save(); pen.setJoinStyle(Qt::MiterJoin); // MiterJoin, BevelJoin, RoundJoin pen.setCapStyle(Qt::RoundCap); // FlatCap, SquareCap, RoundCap pen.setStyle(Qt::DashLine); painter->drawPolyline(points, 3 ); painter->restore(); // Closed shape QPainterPath path; path.addEllipse(QRectF( 10 . 0 , 10 . 0 , width() - 10 . 0 , height() - 10 . 0 )); painter->drawPath(path); /* 三角形 QPainterPath path; path.moveTo(width() / 2, 0); path.lineTo(width(), height()); path.lineTo(0, height()); path.lineTo(width() / 2, 0); painter->fillPath(path, m_color); */ } bool CustomItem::contains( const QPointF &point) const { // Unclosed shape static const QPointF points[ 3 ] = { QPointF( 10 . 0 , 100 . 0 ), QPointF( 20 . 0 , 10 . 0 ), QPointF( 200 . 0 , 50 . 0 ), }; QPainterPath path; path.moveTo(points[ 0 ]); path.lineTo(points[ 1 ]); path.lineTo(points[ 2 ]); QPainterPathStroker stroker; stroker.setWidth( 10 ); stroker.setJoinStyle(Qt::MiterJoin); stroker.setCapStyle(Qt::RoundCap); stroker.setDashPattern(Qt::SolidLine); return stroker.createStroke(path).contains(point); // Close shape QPainterPath path; path.addEllipse(QRectF( 10 . 0 , 10 . 0 , width() - 10 . 0 , height() - 10 . 0 )); QPainterPathStroker stroker; stroker.setWidth( 10 ); return path.contains(point) || stroker.createStroke(path).contains(point); /* 三角形 QPainterPath path; path.moveTo(width() / 2, 0); path.lineTo(width(), height()); path.lineTo(0, height()); path.lineTo(width() / 2, 0); QPainterPathStroker stroker; stroker.setWidth(10); return return path.contains(point) || stroker.createStroke(path).contains(point); */ } void CustomItem::mousePressEvent(QMouseEvent *event) { qDebug() << "CustomItem::mousePressEvent" ; QQuickPaintedItem::mousePressEvent(event); } void CustomItem::mouseMoveEvent(QMouseEvent *event) { qDebug() << "CustomItem::mouseMoveEvent" ; QQuickPaintedItem::mouseMoveEvent(event); } void CustomItem::mouseReleaseEvent(QMouseEvent *event) { qDebug() << "CustomItem::mouseReleaseEvent" ; QQuickPaintedItem::mouseReleaseEvent(event); } |
在main.cpp中注冊CustomItem類型:qmlRegisterType<CustomItem>("CustomItem", 1, 0, "CustomItem");
main.cpp
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include
<QGuiApplication> #include <QQmlApplicationEngine> #include "CustomItem.h" int main( int argc, char *argv[]) { qputenv( "QT_IM_MODULE" , QByteArray( "qtvirtualkeyboard" )); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); qmlRegisterType<CustomItem>( "CustomItem" , 1 , 0 , "CustomItem" ); QQmlApplicationEngine engine; const QUrl url(QStringLiteral( "qrc:/main.qml" )); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject * obj, const QUrl & objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(- 1 ); }, Qt::QueuedConnection); engine.load(url); return app.exec(); } |
在qml文件中使用:
main.qml
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
import
QtQuick
2
.
12
import QtQuick.Window 2 . 12 import QtQuick.Controls 2 . 5 import CustomItem 1 . 0 Window { id: window visible: true width: 640 height: 480 title: qsTr( "qml-2d-viewer" ) Button { id: btn anchors.right: parent.right anchors.bottom: parent.bottom text: "Populate" onClicked: { var object = Qt.createQmlObject( 'import QtQuick 2.12; import CustomItem 1 . 0 ; CustomItem { width: 100 height: 50 x: window.width * Math.random() y: window.height * Math.random() name: "A simple Rectangle Item" color: Qt.rgba(Math.random(), Math.random(), Math.random(), 1 ) MouseArea { anchors.fill: parent onPressed: { if (parent.contains(Qt.point(mouse.x, mouse.y))){ drag.target = parent parent.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1 ); parent.update(); } else { drag.target = null } } drag.axis: Drag.XAndYAxis drag.minimumX: 0 drag.maximumX: window.width - parent.width drag.minimumY: 0 drag.maximumY: window.height - parent.width } } ', parent, "dynamicSnippet" ); } } // QML Rectangle Type Rectangle { id: blueSquare width: 120 ; height: 120 x: window.width - width - 10 ; y: 10 // making this item draggable, so don't use anchors color: "blue" visible: false Text { text: "Drag" ; font.pixelSize: 16 ; color: "white" ; anchors.centerIn: parent } MouseArea { anchors.fill: parent //! [drag] drag.target: blueSquare drag.axis: Drag.XAndYAxis drag.minimumX: 0 drag.maximumX: window.width - parent.width drag.minimumY: 0 drag.maximumY: window.height - parent.width //! [drag] } } // CustomItem Type Inherited from QQuickPaintedItem CustomItem { id: aRectangle width: 200 height: 100 name: "A simple Rectangle Item" color: "red" MouseArea { anchors.fill: parent onPressed: { if (parent.contains(Qt.point(mouse.x, mouse.y))){ drag.target = parent parent.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1 ); parent.update(); } else { drag.target = null } } drag.axis: Drag.XAndYAxis drag.minimumX: 0 drag.maximumX: window.width - parent.width drag.minimumY: 0 drag.maximumY: window.height - parent.width } } } |
Q_INVOKABLE宏將contains()方法注冊到元對象系統中,這樣QML就可以調用該方法來判斷鼠標指針點是否在圖形項形狀區域,從而實現精准拾取。
在QML中MouseArea為簡單的鼠標交互提供了方便,比較容易使用,如果有了MouseArea,則C++中的鼠標響應事件就不再響應。