Qt图形视图体系结构示例解析(视图、拖拽、动画)

本博的示例来自与QTExample:C:\Qt\Qt5.9.3\Examples\Qt-5.9.3\widgets\graphicsview\dragdroprobot将通过分析示例完成主要功能:

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

  本博的示例来自与QT Example:C:\Qt\Qt5.9.3\Examples\Qt-5.9.3\widgets\graphicsview\dragdroprobot

  将通过分析示例完成主要功能:

  (1)颜色图元绘制

  (2)机器人图元绘制

  (3)颜色图元的鼠标事件

  (4)机器人图元的DragDrop事件

  (5)图元动画效果

一、颜色图元类实现

  QGraphicsItem作为所有图元类的基类,自定义图元类需继承QGraohicsItem类,实现其基类的纯虚函数

virtual QRectF boundingRect() const = 0;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = Q_NULLPTR) = 0;

  boundingRect()设置图元的边界矩形范围,QGraphicsView使用此来确定图元是否需要重绘

  paint()实现图元的绘制操作,一种方法是直接在paint中对图元进行绘制。另一种方法可以通过shape返回QPainterPath,然后在paint中依据QPainterPath进行绘制

  该示例实现了随机的10中颜色图元,boundRect()为QRectF(-15,-15,30,30),图元的中心坐标为(0,0)

(1)自定义随机颜色

m_pColor(qrand() % 256, qrand() % 256, qrand() % 256)

(2)图元边界矩形设置

QRectF ColorItem::boundingRect() const
{
    return QRectF(-15,-15,30,30);
}

(3)图元绘制

void ColorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setBrush(m_pColor);
    painter->drawEllipse(boundingRect());
}

(4)光标设置

  当鼠标进入图元或是拖动图元时设置光标形状,光标形状查看枚举类型:CursorShape

setCursor(Qt::OpenHandCursor);
setAcceptedMouseButtons(Qt::LeftButton);

(5)设置ToolTip  

  当鼠标进入图元时显示提示内容:

  Qt图形视图体系结构示例解析(视图、拖拽、动画)

setToolTip(QString("QColor(%1,%2,%3)\n%4").arg(m_pColor.red())
               .arg(m_pColor.green()).arg(m_pColor.blue())
               .arg("Click and drag this color onto the robot!"));

二、机器人头像图元类实现

  颜色图元的实现中已经了解了基本实现方法,机器人图元的实现也不例外,由于机器人包括很多图元部分(头、身体等),我们可以采用面对对象继承的方式来实现。

  定义所有机器人图元的基类Robot

class Robot : public QGraphicsObject
{
public:
    Robot(QGraphicsItem *parent = Q_NULLPTR);

protected:
    virtual void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
    virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
    //virtual void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
    virtual void dropEvent(QGraphicsSceneDragDropEvent *event);

    QColor m_Color;   // 颜色
    bool m_bDragOver; // 鼠标是否拖放完毕
};

  机器人头部图元:

class QPixmap;
class RobotHand : public Robot
{
public:
    RobotHand(QGraphicsItem *parent = Q_NULLPTR);

    QRectF boundingRect() const override;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override;

protected:
    void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override;
    void dropEvent(QGraphicsSceneDragDropEvent *event) override;

private:
    QPixmap m_pixmap;
};

(1)边界矩形设置

QRectF RobotHand::boundingRect() const
{
    return QRectF(-15, -15, 30,30);
}

(2)机器人头部绘制

  当m_pixmap.isNull()为真时,使用默认颜色或拖放后的颜色m_Color进行填充,否则使用pixmap绘制

void RobotHand::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    if (m_pixmap.isNull())
    {
        painter->setPen(Qt::black);
        painter->setBrush(m_bDragOver ? m_Color.light(130) : m_Color);
        //painter->drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt::RelativeSize);
        painter->drawRoundedRect(-15, -15, 30, 30, 25, 25, Qt::RelativeSize);
        painter->setBrush(Qt::white);
        painter->drawEllipse(-7, -12, 7,7);
        painter->drawEllipse(1, -12, 7,7);
        painter->setBrush(Qt::black);
        painter->drawEllipse(-5, -11, 2, 2);
        painter->drawEllipse(2, -11, 2, 2);
        painter->setPen(QPen(Qt::black, 2));
        painter->setBrush(Qt::NoBrush);
        painter->drawArc(-6, -9, 12, 15, 190 * 16, 160 * 16);
    }
    else
    {
        painter->scale(.15, .15);
        painter->drawPixmap(QPointF(-15 * 4.4, -30 * 3.54), m_pixmap);
    }
}

三、视图、场景类实现

(1)场景设置

QGraphicsScene* m_pScene;
m_pScene = new QGraphicsScene(QRectF(-150,-150,300,300));

  添加图元:

for (int i = 0; i < 10; i ++)
    {
        ColorItem *item = new ColorItem;

        item->setPos(qCos((i / 10.0) *6.28) * 100,qSin((i / 10.0) *6.28) * 100);
        if(i == 0)
        {
            item->setData(ColorItem::COLOR_TYPE,"pixmap");
        }
        m_pScene->addItem(item);
    }
    
    Robot* pRobot = new RobotHand;
    pRobot->setPos(-10,-30);
    m_pScene->addItem(pRobot);

(2)视图设置

  自定义视图:

class GraphicsView : public QGraphicsView
{
public:
    GraphicsView(QGraphicsScene *scene, QWidget *parent = Q_NULLPTR)
        :QGraphicsView(scene, parent)
    {

    }

    void resizeEvent(QResizeEvent *event)
    {
        fitInView(sceneRect(), Qt::KeepAspectRatio);
    }
};

这里重点提下resizeEvent虚函数,设置场景虽视图的变化情况,以下来自QT官方文档:

Qt图形视图体系结构示例解析(视图、拖拽、动画)

  视图设置和添加场景:

GraphicsView* m_pView;
m_pView = new GraphicsView(m_pScene);
    m_pView->setBackgroundBrush(QColor(230, 200, 167));
    m_pView->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);

    setCentralWidget(m_pView);

四、颜色图元鼠标事件实现

  颜色图元的鼠标事件包括鼠标按下,鼠标移动和鼠标释放,要了解更详细的事件机制可阅读前面的博客:Qt之事件处理机制

  重载事件虚函数:

virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void ColorItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    setCursor(Qt::OpenHandCursor);
}

void ColorItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "drag instance:" << QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton))
              .length();
    qDebug() << "startDragDistance:" << QApplication::startDragDistance();
    if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton))
            .length() < QApplication::startDragDistance())
    {
        return;
    }

    QDrag *drag = new QDrag(event->widget());
    QMimeData *mime = new QMimeData;
    drag->setMimeData(mime);
    if (data(COLOR_TYPE) == "pixmap")
    {
        mime->setImageData(QPixmap(":/images/head.png"));
    }
    else
    {
        mime->setColorData(m_pColor);
    }

    QPixmap pixMap(30,30);
    pixMap.fill(Qt::white);
    QPainter painter(&pixMap);
    painter.translate(15, 15);
    paint(&painter, 0, 0);
    painter.end();

    drag->setPixmap(pixMap);
    drag->setHotSpot(QPoint(15, 15));

    drag->exec();
    setCursor(Qt::OpenHandCursor);
}

void ColorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    setCursor(Qt::OpenHandCursor);
}

五、拖拽事件实现

  在介绍如何实现拖拽事件之前先来了解两个类QDrag和QMimeData

(1)QMimeData类

  QMimeData类为数据提供一个容器,用来记录关于MIME类型数据的信息

  QMimeData常用来描述保存在剪切板里信息,或者拖拽原理

  QMimeData对象把它所保存的信息和正确的MIME类型连接起来来保证信息可以被安全的在应用程序之间转移,或者在同一个应用程序之间拷贝

  QMimeData对象通产雇佣new来创建,并且支持QDrag和QClipboard对象,这可以使QT管理他们所使用的内存

  单一的QMimeData对象可以同时用好几种不同的格式来存储同一个数据,formats()函数返回可以用的数据格式的list,data()函数可以返回和MIME类型相连的数据类型,setData()用来为MIME类型设置数据

  对于大多数MIME类型,QMimeData提供方便的函数来获取数据

Qt图形视图体系结构示例解析(视图、拖拽、动画)

  QMiMeData数据的设置:

    QMimeData *mime = new QMimeData;
    if (data(COLOR_TYPE) == "pixmap")
    {
        mime->setImageData(QPixmap(":/images/head.png"));
    }
    else
    {
        mime->setColorData(m_pColor);
    }

  QMiMeData数据的获取:

    const QMimeData* mime = event->mimeData();
    if (mime->hasImage())
    {
        m_pixmap = qvariant_cast<QPixmap>(mime->imageData());
        update();
    }

(2)QDrag类

  QDrag类提供了MIME基础数据类型的拖动和释放,拖放是用户在应用程序中复制和移动数据的一种直观方式,在许多桌面环境中被用作在应用程序之间复制数据的机制,qt中的拖放支持以处理拖放操作的大部分细节的QDrag类为中心。

  QDrag类常用函数:

    void setMimeData(QMimeData *data);
    QMimeData *mimeData() const;

    void setPixmap(const QPixmap &);
    QPixmap pixmap() const;

    void setHotSpot(const QPoint &hotspot);  // 设置热点
    QPoint hotSpot() const;

    QObject *source() const;
    QObject *target() const;

    Qt::DropAction start(Qt::DropActions supportedActions = Qt::CopyAction);
    Qt::DropAction exec(Qt::DropActions supportedActions = Qt::MoveAction);
    Qt::DropAction exec(Qt::DropActions supportedActions, Qt::DropAction defaultAction);

    void setDragCursor(const QPixmap &cursor, Qt::DropAction action);
    QPixmap dragCursor(Qt::DropAction action) const;

    Qt::DropActions supportedActions() const;
    Qt::DropAction defaultAction() const;

    static void cancel();
  void setMimeData(QMimeData *data);  // 设置MimeData
  
void setHotSpot(const QPoint &hotspot); // 设置热点,即鼠标在拖动图片的显示位置
  void setPixmap(const QPixmap &); // 设置跟随鼠标拖动的位图
  exec()开始drag事件循环
  QDrag对象的初始化在源窗口的mouseMoveEvent中进行:
    QMimeData *mime = new QMimeData;
    drag->setMimeData(mime);
    if (data(COLOR_TYPE) == "pixmap")
    {
        mime->setImageData(QPixmap(":/images/head.png"));
    }
    else
    {
        mime->setColorData(m_pColor);
    }

    QPixmap pixMap(30,30);
    pixMap.fill(Qt::white);
    QPainter painter(&pixMap);
    painter.translate(15, 15);
    paint(&painter, 0, 0);
    painter.end();

    drag->setPixmap(pixMap);
    drag->setHotSpot(QPoint(15, 15));

    drag->exec();
    setCursor(Qt::OpenHandCursor);
}

(2)拖拽事件实现

  在源窗口中的事件响应:

void ColorItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    setCursor(Qt::OpenHandCursor);
}

void ColorItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "drag instance:" << QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton))
              .length();
    qDebug() << "startDragDistance:" << QApplication::startDragDistance();
    if (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton))
            .length() < QApplication::startDragDistance())
    {
        return;
    }

    QDrag *drag = new QDrag(event->widget());
    QMimeData *mime = new QMimeData;
    drag->setMimeData(mime);
    if (data(COLOR_TYPE) == "pixmap")
    {
        mime->setImageData(QPixmap(":/images/head.png"));
    }
    else
    {
        mime->setColorData(m_pColor);
    }

    QPixmap pixMap(30,30);
    pixMap.fill(Qt::white);
    QPainter painter(&pixMap);
    painter.translate(15, 15);
    paint(&painter, 0, 0);
    painter.end();

    drag->setPixmap(pixMap);
    drag->setHotSpot(QPoint(15, 15));

    drag->exec();
    setCursor(Qt::OpenHandCursor);
}

void ColorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    setCursor(Qt::OpenHandCursor);
}

  目标窗口中的事件响应:

  setAcceptDrops(true); 设置窗口的接收事件

void RobotHand::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    if (event->mimeData()->hasImage())
    {
        event->setAccepted(true);
        m_bDragOver = true;
        update();
    }
    else
    {
        Robot::dragEnterEvent(event);
    }
}

void RobotHand::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    m_bDragOver = false;

    const QMimeData* mime = event->mimeData();
    if (mime->hasImage())
    {
        m_pixmap = qvariant_cast<QPixmap>(mime->imageData());
        update();
    }
    else
    {
        Robot::dropEvent(event);
    }
}

六、图元动画实现

(1)QPropertyAnimation

  QPropertyAnimation类定义了Qt的属性动画,QPropertyAnimation以Qt属性做差值,作为属性值存储在QVariants中,该类继承自QVariantAnimation,并支持基类相同的元类型动画。声明属性的类必须是一个QObject,为了能够让属性可以用做动画效果,必须提供一个setter(这样,QPropertyAnimation才可以设置属性的值)。注意:这能够使它让许多Qt控件产生动画效果。

  QPropertyAnimation类介绍:

class QPropertyAnimationPrivate;
class Q_CORE_EXPORT QPropertyAnimation : public QVariantAnimation
{
    Q_OBJECT
    Q_PROPERTY(QByteArray propertyName READ propertyName WRITE setPropertyName)
    Q_PROPERTY(QObject* targetObject READ targetObject WRITE setTargetObject)

public:
    QPropertyAnimation(QObject *parent = Q_NULLPTR);
    QPropertyAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = Q_NULLPTR);  // 对象指针、属性名
    ~QPropertyAnimation();

    QObject *targetObject() const;
    void setTargetObject(QObject *target);

    QByteArray propertyName() const;
    void setPropertyName(const QByteArray &propertyName);

protected:
    bool event(QEvent *event) Q_DECL_OVERRIDE;
    void updateCurrentValue(const QVariant &value) Q_DECL_OVERRIDE;
    void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) Q_DECL_OVERRIDE;

private:
    Q_DISABLE_COPY(QPropertyAnimation)
    Q_DECLARE_PRIVATE(QPropertyAnimation)
};

  QVariantAnimation类属性:起始值、结束值、当前值、时间间隔

  Q_PROPERTY(QVariant startValue READ startValue WRITE setStartValue)
    Q_PROPERTY(QVariant endValue READ endValue WRITE setEndValue)
    Q_PROPERTY(QVariant currentValue READ currentValue NOTIFY valueChanged)
    Q_PROPERTY(int duration READ duration WRITE setDuration)
    Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve)

  示例:实现图元的放大、缩小和旋转

    QPropertyAnimation *headAnimation = new QPropertyAnimation(this, "rotation");  // 旋转属性
    headAnimation->setStartValue(30);
    headAnimation->setEndValue(-30);
   headAnimation->setDuration(2000);
    QPropertyAnimation *headScaleAnimation = new QPropertyAnimation(this, "scale"); // 比例属性
    headScaleAnimation->setEndValue(0.5);
   headAnimation->setDuration(2000);

(2)QParallelAnimationGroup

  QParallelAnimationGroup类提供动画的并行组。

  QParallelAnimationGroup – 一个动画容器,当它启动的时候它里面的所有动画也启动,即:并行运行所有动画,当持续时间最长的动画完成时动画组也随之完成。

    QParallelAnimationGroup *animation = new QParallelAnimationGroup(this);
   animation->addAnimation(headAnimation);
    animation->addAnimation(headScaleAnimation);

    for (int i = 0; i < animation->animationCount(); ++i) {
        QPropertyAnimation *anim = qobject_cast<QPropertyAnimation *>(animation->animationAt(i));
        anim->setEasingCurve(QEasingCurve::SineCurve);
        anim->setDuration(2000);
    }

    headAnimation->setLoopCount(-1);   // 无限循环
    headAnimation->start();

七、程序效果

Qt图形视图体系结构示例解析(视图、拖拽、动画)

Qt图形视图体系结构示例解析(视图、拖拽、动画)

Qt图形视图体系结构示例解析(视图、拖拽、动画)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/120082.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 如何利用matlab做BP神经网络分析(包括利用matlab神经网络工具箱)「建议收藏」

    利用MATLAB进行BP神经网络的预测(含有神经网络工具箱)最近一段时间在研究如何利用预测其销量个数,在网上搜索了一下,发现了很多模型来预测,比如利用回归模型、时间序列模型,GM(1,1)模型,可是自己在结合实际的工作内容,发现这几种模型预测的精度不是很高,于是再在网上进行搜索,发现神经网络模型可以来预测,并且有很多是结合时间序列或者SVM(支持向量机)等组合模型来进…

  • setproperty java_Java中System.setProperty()的用法

    setproperty java_Java中System.setProperty()的用法该方法的作用是:设置指定键指示的系统属性在实际项目中发现,如果一个服务器下放置了多个web项目的话,在一个项目中通过调用System.setProperty设置了某个值,再另一个项目中也能获取到该值,所以如果想在一个项目中设置全局变量时尽量避免使用这个方法,应该调用相应的appContext。/**设置指定键对值的系统属性*setProperty(Stringprop,Stringva…

  • 对于Redis中设置了过期时间的Key,你需要知道这些内容「建议收藏」

    对于Redis中设置了过期时间的Key,你需要知道这些内容

  • 如何对图像进行卷积操作[通俗易懂]

    如何对图像进行卷积操作[通俗易懂]1、首先先了解下什么是卷积呢?2、卷积操作:卷积核与原图对应位置相乘再求和;然后将所求和放在被卷积操作的图中心位置。上图表示一个8×8的原图,每个方格代表一个像素点;其中一个包含X的方格是一个5×5的卷积核,核半径等于5/2=2;进行卷积操作后,生成图像为上图中包含Y的方格,可以看出是一个4×4的生成图;通过比较观察可以发现,生成图比原图尺寸要小,为了保证生成…

  • statement和prepareStatement 的区别

    statement和prepareStatement 的区别????Java基础教程系列:Java基础教程系列????Java学习路线总结:搬砖工逆袭Java架构师????Java经典面试题大全:10万字208道Java经典面试题总结(附答案)????简介:Java领域优质创作者????、CSDN哪吒公众号作者✌、Java架构师奋斗者????????扫描主页左侧二维码,加入群聊,一起学习、一起进步????欢迎点赞????收藏⭐留言????一、【粉丝福利】送书啦,大家期待已久的Vue!本次活动送书规则:【送

  • 百度分享按钮代码

    百度分享按钮代码百度分享官网已经获取不了了,但是这些按钮功能还在.下面直接把代码放出,有需要的同学可以直接复制第一种:按钮式:<divclass=”bdsharebuttonbox”><ahref=”#”class=”bds_more”data-cmd=”more”></a><ahref=”#”class=”bds_qzone”…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号