本博的示例来自与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 = ;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = Q_NULLPTR) = ;

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

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

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

(1)自定义随机颜色

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

(2)图元边界矩形设置

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

(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  

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

  

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 = ) override; protected:
void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override;
void dropEvent(QGraphicsSceneDragDropEvent *event) override; private:
QPixmap m_pixmap;
};

(1)边界矩形设置

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

(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() : m_Color);
//painter->drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt::RelativeSize);
painter->drawRoundedRect(-, -, , , , , Qt::RelativeSize);
painter->setBrush(Qt::white);
painter->drawEllipse(-, -, ,);
painter->drawEllipse(, -, ,);
painter->setBrush(Qt::black);
painter->drawEllipse(-, -, , );
painter->drawEllipse(, -, , );
painter->setPen(QPen(Qt::black, ));
painter->setBrush(Qt::NoBrush);
painter->drawArc(-, -, , , * , * );
}
else
{
painter->scale(., .);
painter->drawPixmap(QPointF(- * 4.4, - * 3.54), m_pixmap);
}
}

三、视图、场景类实现

(1)场景设置

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

  添加图元:

for (int i = ; i < ; i ++)
{
ColorItem *item = new ColorItem; item->setPos(qCos((i / 10.0) *6.28) * ,qSin((i / 10.0) *6.28) * );
if(i == )
{
item->setData(ColorItem::COLOR_TYPE,"pixmap");
}
m_pScene->addItem(item);
} Robot* pRobot = new RobotHand;
pRobot->setPos(-,-);
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官方文档:

  视图设置和添加场景:

GraphicsView* m_pView;
m_pView = new GraphicsView(m_pScene);
m_pView->setBackgroundBrush(QColor(, , ));
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(,);
pixMap.fill(Qt::white);
QPainter painter(&pixMap);
painter.translate(, );
paint(&painter, , );
painter.end(); drag->setPixmap(pixMap);
drag->setHotSpot(QPoint(, )); 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提供方便的函数来获取数据

  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(,);
pixMap.fill(Qt::white);
QPainter painter(&pixMap);
painter.translate(, );
paint(&painter, , );
painter.end(); drag->setPixmap(pixMap);
drag->setHotSpot(QPoint(, )); 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(,);
pixMap.fill(Qt::white);
QPainter painter(&pixMap);
painter.translate(, );
paint(&painter, , );
painter.end(); drag->setPixmap(pixMap);
drag->setHotSpot(QPoint(, )); 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();
headAnimation->setEndValue(-);
   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 = ; i < animation->animationCount(); ++i) {
QPropertyAnimation *anim = qobject_cast<QPropertyAnimation *>(animation->animationAt(i));
anim->setEasingCurve(QEasingCurve::SineCurve);
anim->setDuration();
} headAnimation->setLoopCount(-); // 无限循环
headAnimation->start();

七、程序效果

Qt图形视图体系结构示例解析(视图、拖拽、动画)的更多相关文章

  1. Qt之股票组件-自选股--列表可以拖拽、右键常用菜单

    目录 一.开头嘴一嘴 二.效果展示 三.自选股列表 1.列表初始化 2.添加Item 3.右键菜单 4.拖拽Item 5.刷新数据 四.相关文章 原文链接:Qt之股票组件-自选股--列表可以拖拽.右键 ...

  2. Qt无边框窗体-最大化时支持拖拽还原

    目录 一.概述 二.效果展示 三.demo制作 1.设计窗体 2.双击放大 四.拖拽 五.相关文章 原文链接:Markdown模板 一.概述 用Qt进行开发界面时,既想要实现友好的用户交互又想界面漂亮 ...

  3. ios-将代码创建的视图控件放入拖拽控件的下面

    如图所示 图片是拖拽上去的imageView,橘黄色控件是在代码中创建的添加上去的,此时黄色view在imageView 上方 调用方法bringSubviewToFront:试图将imageView ...

  4. Qt::QWidget 无默认标题栏边框的拖拽修改大小方式

    开发环境:win10+vs2015+qt5.9.1 背景:开发过程中,一般很少会使用系统提供的标题栏和边框:往往都是自定义一个自己设计的方案.这时候在QWidget中需要加上flag:Qt::Fram ...

  5. React Editor 应用编辑器(1) - 拖拽功能剖析

    这是可视化编辑器 Gaea-Editor 的第一篇连载分析文章,希望我能在有限的篇幅讲清楚制作这个网页编辑器的动机,以及可能带来的美好使用前景(画大饼).它会具有如下几个特征: 运行在网页 文档流布局 ...

  6. Vue拖拽组件列表实现动态页面配置

    需求描述 最近在做一个后台系统,有一个功能产品需求是页面分为左右两部分,通过右边的组件列表来动态配置左边的页面视图,并且左边由组件拼装起来的视图,可以实现上下拖拽改变顺序,也可以删除. 根据这个需求我 ...

  7. JS—实现拖拽

    JS中的拖拽示例:    1)实现拖拽思路:当鼠标按下和拖拽过程中,鼠标与拖拽物体之间的相对距离保持不变    2)实现拖拽遇到的问题:        问题1:当鼠标按下移动过快时,离开了拖拽的物体时 ...

  8. Android Launcher拖拽事件详解【android4.0--Launcher系列二】

    AndroidICS4.0版本的launcher拖 拽的流程,基本和2.3的相似.就是比2.3写的封装的接口多了一些,比如删除类的写法就多了个类.等等.4.0的改变有一些,但是不是特别大.这个月一 直 ...

  9. 开源自己写的一个拖拽库,兼容到IE8+

    github地址:https://github.com/qiangzi7723/draggable 目前这个库的兼容做到了兼容IE8,所以如果需要兼容IE8的朋友不妨试试.库里面写了很多的注释,对于想 ...

随机推荐

  1. Mybatis之基础应用小结以及IntelliJ IDEA目录结构的一些小问题

    IntelliJ IDEA 目录结构的一些小问题 [Mybatis 之基础应用小结] 1.不管怎么样,先建立一个简单的MySQL数据表,如下所示 2.接下来要做的事情就是通过Mybatis对数据表进行 ...

  2. systemverilog interface杂记

    随着IC设计复杂度的提高,模块间互联变得复杂,SV引入接口,代表一捆连线的结构. Systemverilog语法标准,新引入一个重要的数据类型:interface. interface主要作用有两个: ...

  3. 20145201《Java程序设计》第五次实验报告

    实验五 Java网络编程及安全 实验内容 1.掌握Socket程序的编写: 2.掌握密码技术的使用: 3.设计安全传输系统. 我负责客户端 组队队员:鄢曼君20145227负责服务器 博客地址:htt ...

  4. 20145235李涛《网络对抗》Exp5 MSF基础应用

    基础问答 用自己的话解释什么是exploit,payload,encode? exploit:相当于搬运工,把攻击代码传送到靶机中. payload:相当于shellcode. encode:相当于包 ...

  5. 线程的sleep()方法和yield()方法区别

    1.sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会 2.yield()方法只会给相同优先级或更高优先级的线程以运行的机会 3.线程执行sleep()方法后 ...

  6. Sharding-Jdbc实现分表分库

    Sharding-Jdbc分表分库LogicTable数据分片的逻辑表,对于水平拆分的数据库(表),同一类表的总称.订单信息表拆分为2张表,分别是t_order_0.t_order_1,他们的逻辑表名 ...

  7. 智能穿戴设备移动APP端与外设数据传输协议功能模块CMD&ACK表

    Notification Module Function CMD ACK Notification History Count [0x0301] [0x0000] [0x01] [0x0301] [0 ...

  8. java中Vector类的常用方法

    Vector类是实现List接口,所以继承的方法就不在这里讲了 https://www.cnblogs.com/xiaostudy/p/9503199.html public void add(int ...

  9. mybatis报错 Error instantiating interface com.atguigu.mybatis.dao.DepartmentMapper with invalid types () or values ()

    mybatis报错 Error instantiating interface com.atguigu.mybatis.dao.DepartmentMapper with invalid types ...

  10. 报错cannot be cast to javassist.util.proxy.Proxy

    出现这种错误还有一个原因:hibernate和struts中都有javassit的jar包,把其中的低版本删除就行了.