QQuickPaintedItem鼠標精准拾取(pick/select)研究


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++中的鼠標響應事件就不再響應。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM