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图形视图体系结构示例解析(视图、拖拽、动画)

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 在事务中执行批量复制操作SqlBulkCopy,SqlTransaction「建议收藏」

    在事务中执行批量复制操作SqlBulkCopy,SqlTransaction「建议收藏」    MicrosoftSQLServer包含名为bcp的常用命令行应用程序,用于快速将大文件批量复制到SQLServer数据库的表或视图中。使用SqlBulkCopy类可以编写提供类似功能的托管代码解决方案。还可以通过其他方式将数据加载到SQLServer表中(例如INSERT语句),但是SqlBulkCopy提供的性能要明显优于这些方式。   

    2022年5月8日
    151
  • 简单网络管理协议SNMP(史上最全)

    简单网络管理协议SNMP(史上最全)简单网络管理协议(SNMP)是TCP/IP协议簇的一个应用层协议。在1988年被制定,并被Internet体系结构委员会(IAB)采纳作为一个短期的网络管理解决方案;由于SNMP的简单性,在Internet时代得到了蓬勃的发展,1992年发布了SNMPv2版本,以增强SNMPv1的安全性和功能。现在,已经有了SNMPv3版本。SNMP版本…

    2022年10月17日
    0
  • J1939TP「建议收藏」

    J1939TP「建议收藏」J1939TP给上层、下层提供的服务,和它本身内部的行为。1939协议定义了一些参数组,每个参数组包含确定的内容和信号。并提供以下PG:负载的长度类型:最大字节数、可变或固定大小参数组号:18位包含以下信息:2bit数据页信息8bitPDU格式8bitPDU细节PF小于240的为PDU1格式,用于点对点通信;大于等于240的为PDU2格式,用于广播通信。PDU细节仅与PDU2格式有关。在PDU1格式下的点对点通信,PS总为0。J1939使用29位CANid作为消…

    2022年5月5日
    39
  • scrapy安装步骤_scrapy官网

    scrapy安装步骤_scrapy官网安装scrapy过程中出现各种包安装错误,所以自己一直看教程知道scrapy安装需要准备好各种环境。这些包按照从下到上的顺序下载,lxml这个包按下文教程安装。不想看过多文字和图片的懒人们可看教程视频:http://www.iqiyi.com/w_19rz36pjft.html利用pipinstall命令安装pywin32,pyopenssl.这两个包可在cmd安装成功pip…

    2022年9月18日
    0
  • clion永久激活码_通用破解码

    clion永久激活码_通用破解码,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月16日
    310
  • visual studio code适合什么语言_将当前运行的配置备份成初始配置

    visual studio code适合什么语言_将当前运行的配置备份成初始配置VSCode是一款非常好用的编辑器(或者IDE),具有很好的可扩展性,功能比较强大,占用的系统资源也适中,启动速度较快,而且支持全平台,比较适合作为Python开发用的IDE。本文针对Linux(主要是Ubuntu,其他发行版类似),整合一些Python开发相关的配置,仅供刚入坑Linuxer参考。一、VSCode与其他编辑器(或IDE)的比较(1)VSCode与Atom的比较:Atom是一款由g…

    2022年8月25日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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