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)


相关推荐

  • javaweb-spring-52

    javaweb-spring-52

  • java中什么是过滤器_JAVAweb过滤器

    java中什么是过滤器_JAVAweb过滤器【扩展】过滤器:Filter概念:对目标资源的请求和响应进行过滤截取。在请求到达servlet之前,进行逻辑判断,判断是否放行到servlet;也可以在一个响应response到达客户端之前进行过滤,判断是否允许返回客户端。场景:(用户授权的过滤器:判断用户是否有权限请求界面)(日志信息的过滤器:过滤用户在网站的所有请求,记录轨迹 )(负责解码的过滤器:规定请求的解码方式)备注:过滤…

  • 服务器基础知识全解(汇总版)[通俗易懂]

    服务器基础知识全解(汇总版)[通俗易懂]服务器基础知识全解(汇总版)https://www.zack.cn/archives/729本文对服务器知识进行了汇总,并添加了服务器基准测试和认证章节,内容包括9大章节,从服务器的概念、服务器重要部件技术和架构组成,并且对磁盘、RAID知识,网卡等知识做了深度详细介绍。说明:部分内容首发“智能计算芯世界”微信公众号。简单来说,服务器就是在网络中为其他客户机提供服务的计算机;具有高性能、高可靠、高IO数据传输能力等特点,企业从基础的邮件、打印到核心应用如ERP、数据库等业务,再到我们所熟

  • linux中iostat命令_ios命令行怎么打开

    linux中iostat命令_ios命令行怎么打开简介iostat主要用于监控系统设备的IO负载情况,iostat首次运行时显示自系统启动开始的各项统计信息,之后运行iostat将显示自上次运行该命令以后的统计信息。用户可以通过指定统计的次数和时间来获得所需的统计信息。 语法iostat[-c][-d][-h][-N][-k|-m][-t][-V][-x][-

  • 闫学灿acwing_用标号法求网络最大流

    闫学灿acwing_用标号法求网络最大流给定一个包含 n 个点 m 条边的有向图,并给定每条边的容量,边的容量非负。图中可能存在重边和自环。求从点 S 到点 T 的最大流。输入格式第一行包含四个整数 n,m,S,T。接下来 m 行,每行三个整数 u,v,c,表示从点 u 到点 v 存在一条有向边,容量为 c。点的编号从 1 到 n。输出格式输出点 S 到点 T 的最大流。如果从点 S 无法到达点 T 则输出 0。数据范围2≤n≤1000,1≤m≤10000,0≤c≤10000,S≠T输入样例:7 14 1 71 2

  • 共勉:作为一名程序员你应该怎么提一个高质量的问题?

    做一个积极的人编码、改bug、提升自己我有一个乐园,面向编程,春暖花开!又是一个周五,今天依旧不分享技术,这几天不知道怎么,感觉有点累,昨天十点就睡觉了,很久没有这么早睡觉了。现在已经是晚上10点了,我还在码字中,今天争取早点睡觉。今天整理一点关于如何提问的内容,因为最近一段时间有一些技术朋友加我好友,一起探讨一些技术问题。但是一些伙伴加我之后,提问的姿势可能有点不太正确,导致聊了很一会…

发表回复

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

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