zoukankan      html  css  js  c++  java
  • 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(
    050);
        gradient.setFinalStop(
    20050);
        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.0100.0),
            QPointF(
    20.010.0),
            QPointF(
    200.050.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.010.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.0100.0),
            QPointF(
    20.010.0),
            QPointF(
    200.050.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.010.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"10"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
         
    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 {
                                 
    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
             
    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
             
    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++中的鼠标响应事件就不再响应。

  • 相关阅读:
    [转]给C++初学者的50个忠告
    [转]代理模式——代理模式的核心思想
    [转]单例模式——C++实现自动释放单例类的实例
    [转]代理模式——何时使用代理模式
    观察者模式——生动的气象信息订阅服务图示
    [转]单例模式——Java中互斥访问单一实例
    [转]C++ const变量使用技巧总结
    [转]用C++设计一个不能被继承的类
    ExtJs2.0学习系列(14)Ext.TreePanel之第三式(可增删改的树)
    ExtJs2.0学习系列(7)Ext.FormPanel之第四式(其他组件示例篇)
  • 原文地址:https://www.cnblogs.com/MakeView660/p/11238422.html
Copyright © 2011-2022 走看看