Qt | QGraphicsView+QGraphicsScene+QGraphicsItem会跳舞的机器人

该篇文章转载自Qt | QGraphicsView+QGraphicsScene+QGraphicsItem会跳舞的机器人

笔者主要是用来学习一下Qt中的绘图类。

QGraphicsView 是 Qt 框架中的一个重要组件,用于显示和交互 2D 图形项(QGraphicsItem)。它提供了一个高级的、可缩放的视图,适用于需要复杂图形交互的应用程序,如图形编辑器、地图查看器等。

以下是一些关于 GraphicsView 的关键点和示例代码:

关键类和方法

  1. QGraphicsView: 用于显示 QGraphicsScene 中的内容。
  2. QGraphicsScene: 管理图形项的容器。
  3. QGraphicsItem: 图形项的基类,可以派生出各种具体的图形项,如 QGraphicsRectItem, QGraphicsEllipseItem 等。

新建一个项目DanceRobot.

1.Main.cpp的源代码:

#include "mainwindow.h"
#include <QApplication>
// 图形视图类提供了一个用于显示图形场景的内容的小部件
#include<QGraphicsView>
// 提供了一个用于管理大量二维图形项的界面
#include<QGraphicsScene>
#include<cmath>
#include"qcoloritem.h"
#include"qrobot.h"

class QMyGraphicsView:public QGraphicsView
{
public:
    // 使用基类的构造函数
    using QGraphicsView::QGraphicsView;
protected:
    // 重写resizeEvent方法,用于处理视图大小改变事件
    void resizeEvent(QResizeEvent *event)
    {

    };
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 创建一个QGraphicsScene类型对象,设置其范围为(-200,-200,200,200),大小为400
    QGraphicsScene scence(-200,-200,400,400);

    for(int i = 0;i < 10;i++)
    {
        // 创建ColorItem对象
        QColorItem* pItem = new QColorItem();
        // 设置ColorItem对象的位置在 (-150,150)之间
        pItem->setPos(std::sin((i/6.28)/10)*150,
                      std::cos((i/6.28)/10)*150);

        scence.addItem(pItem);

    }

    QRobot* pRobot = new QRobot();
    // 设置Robot对象变换,放大1.2倍
    // pRobot->setTransform(QTransform::fromScale(1.2,1.2),true);
    pRobot->setPos(0,-20);
    scence.addItem(pRobot);

    QGraphicsView view(&scence);
    view.setRenderHint(QPainter::Antialiasing);
    // 设置视图更新模式
    view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
    view.setBackgroundBrush(QBrush(Qt::yellow));
    view.setWindowTitle("机器人");
    view.show();

    return a.exec();
}

2.新建一个类QColorItem,qcoloritem.h源代码如下:

#ifndef QCOLORITEM_H
#define QCOLORITEM_H

#include <QGraphicsItem>
#include <QColor>

class QColorItem : public QGraphicsItem
{
public:
    explicit QColorItem(QGraphicsItem *parent = nullptr);
    virtual QRectF boundingRect() const override;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

protected:
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;

private:
    QColor m_Color;

signals:
};

#endif // QCOLORITEM_H

qcolorite.cpp源代码如下:

#include "qcoloritem.h"
#include<QApplication>
#include<QRandomGenerator>
#include<QCursor>
#include<QPainter>
#include<QGraphicsSceneMouseEvent>
#include<QDrag>
#include<QMimeData>
#include<QImage>
#include<QPixmap>
#include<QLatin1Char>
#include<QWidget>

QColorItem::QColorItem(QGraphicsItem *parent)
    : QGraphicsItem(parent)
    ,m_Color(QRandomGenerator::global()->bounded(256),
              QRandomGenerator::global()->bounded(256),
              QRandomGenerator::global()->bounded(256))
{
    // 设置工具提示,显示颜色值和提示信息
    setToolTip(QString("QColor(%1,%2,%3)\n%4").arg(m_Color.red()).arg(m_Color.green())
                   .arg(m_Color.blue()).arg(m_Color.alpha()));
    // 设置光标为张开的手
    setCursor(Qt::OpenHandCursor);
    // 设置只接受鼠标左键事件
    setAcceptedMouseButtons(Qt::LeftButton);

}

QRectF QColorItem::boundingRect() const
{
    return QRectF(-15.5,-15.5,34,34);

}

void QColorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    // 忽略未使用的参数
    Q_UNUSED(option);
    Q_UNUSED(widget);
    // 设置画笔为无
    painter->setPen(Qt::NoPen);
    // 设置画刷为灰黑色
    painter->setBrush(Qt::darkGray);
    // 绘制内圆
    painter->drawEllipse(-12,-12,30,30);
    painter->setPen(QPen(Qt::black,1));
    painter->setBrush(QBrush(m_Color));
    painter->drawEllipse(-15,-15,30,30);

}

void QColorItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);
    // 设置光标为闭合的手
    setCursor(Qt::ClosedHandCursor);
}

void QColorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);
    // 设置光标为张开的手
    setCursor(Qt::OpenHandCursor);
}

void QColorItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    // 如果鼠标移动的距离小于拖动起始距离,则不进行拖动
    if(QLineF(event->scenePos(),event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance())
    {
        return;
    }

    QDrag* pDrag = new QDrag(event->widget()); // 创建拖动对象
    QMimeData* pMimiData = new QMimeData();    // 创建Mime对象
    pDrag->setMimeData(pMimiData);             // 设置拖动对象的MIME数据

    static int n = 0;
    if(n++ > 2 && QRandomGenerator::global()->bounded(3) == 0)
    {
        // 加载图像
        QImage Image(":/Image/Head.png");
        // 设置MIME数据为图像
        pMimiData->setImageData(Image);
        // 设置拖动对象的像素图
        pDrag->setPixmap(QPixmap::fromImage(Image).scaled(30,40));
        // 设置热点
        pDrag->setHotSpot(QPoint(15,30));
    }
    else
    {
        // 设置MIME颜色
        pMimiData->setColorData(m_Color);
        // 设置文本
        pMimiData->setText(QString("%1%2%3")
                               .arg(m_Color.red(),2,16,QLatin1Char('0'))
                               .arg(m_Color.green(),2,16,QLatin1Char('0'))
                               .arg(m_Color.blue(),2,16,QLatin1Char('0')));

        // 创建像素图
        QPixmap pixmap(34,34);
        // 填充为白色
        pixmap.fill(Qt::white);

        // 创建画家对象
        QPainter painter(&pixmap);
        // 平移坐标系
        painter.translate(15,15);
        // 设置抗锯齿
        painter.setRenderHint(QPainter::Antialiasing);
        // 绘制图形项
        paint(&painter,nullptr,nullptr);
        // 结束绘制
        painter.end();

        // 设置掩码
        pixmap.setMask(pixmap.createHeuristicMask());
        // 设置拖动对象像素图
        pDrag->setPixmap(pixmap);
        // 设置热点
        pDrag->setHotSpot(QPoint(15,30));
    }

    // 执行拖动操作
    pDrag->exec();
    // 设置光标为张开的手
    setCursor(Qt::OpenHandCursor);
}

3.添加qrobot.h头文件,qrobot.h源代码如下:

#ifndef QROBOT_H
#define QROBOT_H
#include<QGraphicsItem>
#include<QPixmap>

QT_BEGIN_NAMESPACE
class QGraphicsSceneMouseEvent;
class QParalleAnimationGroup;
QT_END_NAMESPACE

class QRobotPart:public QGraphicsObject
{
public:
    QRobotPart(QGraphicsItem* pParent = nullptr);

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

    QColor m_Color = Qt::lightGray;
    bool m_bDragOver = false;
};

class QRobotHead:public QRobotPart
{
public:
    QRobotHead(QGraphicsItem* pParent = nullptr);
    virtual QRectF boundingRect() const override;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

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

private:
    QPixmap m_pixmap;

};


class QRobotTorso:public QRobotPart
{
public:
    using QRobotPart::QRobotPart;
    virtual QRectF boundingRect() const override;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
};

class QRobotLimb:public QRobotPart
{
public:
    QRobotLimb(QGraphicsItem* pParent = nullptr );
    virtual QRectF boundingRect() const override;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
};

class QRobot:public QRobotPart
{
public:
    QRobot(QGraphicsItem* pParent = nullptr );
    virtual QRectF boundingRect() const override;
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
};

#endif // QROBOT_H

qrobot.cpp源代码如下:

#include"qrobot.h"
#include<QGraphicsSceneDragDropEvent>
#include<QMimeData>
#include<QPainter>
#include<QParallelAnimationGroup>
#include<QPropertyAnimation>

QRobotPart::QRobotPart(QGraphicsItem *pParent)
    :QGraphicsObject(pParent)
    ,m_Color(Qt::lightGray)
{
    // 设置接受拖放事件
    setAcceptDrops(true);
}

void QRobotPart::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    m_bDragOver = false;
    if(event->mimeData()->hasColor())
    {
        // 设置颜色
       m_Color = qvariant_cast<QColor>(event->mimeData()->colorData());
       update();
    }

}

void QRobotPart::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    if(event->mimeData()->hasColor())
    {
        // 接收事件
        event->setAccepted(false);
        // 拖放标志
        m_bDragOver = true;
        update();
    }
    else
    {
        event->setAccepted(false);
    }
}

void QRobotPart::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
    Q_UNUSED(event);
    m_bDragOver = false; // 清除拖放事件
    update();
}

QRobotHead::QRobotHead(QGraphicsItem* pParent)
    :QRobotPart(pParent)
{

}

QRectF QRobotHead::boundingRect() const
{
    // 返回头部边界
    return QRectF(-15,50,30,50);
}

void QRobotHead::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(option);

    if(m_pixmap.isNull())
    {
        painter->setBrush(m_bDragOver? m_Color.lighter(130):m_Color);
        // 绘制圆角矩形
        painter->drawRoundedRect(10,30,20,30,25,25,Qt::RelativeSize);
        painter->setBrush(Qt::white);
        painter->drawEllipse(-7,-23, 7, 7);
        painter->drawEllipse(0,-23, 7, 7);
        painter->setBrush(Qt::black);
        painter->drawEllipse(-5,-21, 2, 2);
        painter->drawEllipse(2,-1, 2, 2);
        painter->setPen(QPen(Qt::black,2));
        painter->setBrush(Qt::NoBrush);
        painter->drawArc(-6,-22,12,15,190*16,160*16);
    }
    else
    {
        // 缩放画布
        painter->scale(.2272,.2824);
        painter->drawPixmap(QPointF(-15*4.4,-50*3.54),m_pixmap);
    }
}

void QRobotHead::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    if(event->mimeData()->hasImage())
    {
        // 接收事件
        event->setAccepted(false);
        // 拖放标志
        m_bDragOver = true;
        update();
    }
    else
    {
        QRobotPart::dragEnterEvent(event);
    }
}

void QRobotHead::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    if(event->mimeData()->hasImage())
    {
        // 拖放标志
        m_bDragOver = false;
        m_pixmap = qvariant_cast<QPixmap>(event->mimeData()->imageData());
        update();
    }
    else
    {
        QRobotPart::dropEvent(event);
    }
}

QRectF QRobotTorso::boundingRect() const
{
    // 返回头部边界
    return QRectF(-30,20,60,60);
}

void QRobotTorso::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(option);
    painter->setBrush(m_bDragOver? m_Color.lighter(130):m_Color);
    // 绘制圆角矩形
    painter->drawRoundedRect(-20,-20,40,60,25,25,Qt::RelativeSize);
    painter->drawEllipse(-25,-20, 20, 20); // 绘制左肩
    painter->drawEllipse(5,-20, 20, 20);   // 绘制右肩
    painter->drawEllipse(-20,22, 20, 20);  // 绘制左腰
    painter->drawEllipse(0,22, 20, 20);    // 绘制右腰
}

 QRobotLimb::QRobotLimb(QGraphicsItem* pParent)
    :QRobotPart(pParent)
{


}

QRectF QRobotLimb::boundingRect() const
{
    // 返回头部边界
    return QRectF(-5,-5,40,10);
}

void QRobotLimb::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(option);
    painter->setBrush(m_bDragOver? m_Color.lighter(130):m_Color);
    // 绘制圆角矩形
    painter->drawRoundedRect(boundingRect(),50,50,Qt::RelativeSize);
    painter->drawEllipse(-5,-5, 10, 10); // 绘制关节
}

QRobot::QRobot(QGraphicsItem* pParent)
    :QRobotPart(pParent)
{
    // 设置图形项无内容
    setFlags(ItemHasNoContents);
    // 创建躯干
    QGraphicsObject* pTorsoItem = new QRobotTorso(this);
    QGraphicsObject* pHeadItem = new QRobotHead(pTorsoItem);
    QGraphicsObject* pUpperLeftArmItem = new QRobotLimb(pTorsoItem);
    QGraphicsObject* pLowLeftArmItem = new QRobotLimb(pUpperLeftArmItem);
    QGraphicsObject* pUpperRightArmItem = new QRobotLimb(pTorsoItem);
    QGraphicsObject* pLowRightArmItem = new QRobotLimb(pUpperRightArmItem);
    QGraphicsObject* pUpperRightLegItem = new QRobotLimb(pTorsoItem);
    QGraphicsObject* pLowRightLegItem = new QRobotLimb(pUpperRightLegItem);
    QGraphicsObject* pUpperLeftLegItem = new QRobotLimb(pTorsoItem);
    QGraphicsObject* pLowLeftLegItem = new QRobotLimb(pUpperLeftLegItem);

    pHeadItem->setPos(0,-18);
    pUpperLeftArmItem->setPos(-15,-10);
    pLowLeftArmItem->setPos(30,0);
    pUpperRightArmItem->setPos(15,-10);
    pLowRightArmItem->setPos(30,0);
    pUpperRightLegItem->setPos(10,32);
    pLowRightLegItem->setPos(30,0);
    pUpperLeftLegItem->setPos(-10,32);
    pLowLeftLegItem->setPos(30,0);

    QParallelAnimationGroup* pAnimation = new QParallelAnimationGroup(this);
    QPropertyAnimation* pHeadAnimation = new QPropertyAnimation(pHeadItem,"rotation");
    pHeadAnimation->setStartValue(20);
    pHeadAnimation->setEndValue(-20);
    QPropertyAnimation* pHeadScaleAnimation = new QPropertyAnimation(pHeadItem,"scale");
    pHeadScaleAnimation->setEndValue(1.1);
    pAnimation->addAnimation(pHeadAnimation);
    pAnimation->addAnimation(pHeadScaleAnimation);


    QPropertyAnimation* pUpperLeftArmAnimation = new QPropertyAnimation(pUpperLeftArmItem,"rotation");
    pUpperLeftArmAnimation->setStartValue(190);
    pUpperLeftArmAnimation->setEndValue(180);
    pAnimation->addAnimation(pUpperLeftArmAnimation);

    QPropertyAnimation* pLowerLeftArmAnimation = new QPropertyAnimation(pLowLeftArmItem,"rotation");
    pLowerLeftArmAnimation->setStartValue(50);
    pLowerLeftArmAnimation->setEndValue(10);
    pAnimation->addAnimation(pLowerLeftArmAnimation);

    QPropertyAnimation* pUpperRightArmAnimation = new QPropertyAnimation(pUpperRightArmItem,"rotation");
    pUpperRightArmAnimation->setStartValue(300);
    pUpperRightArmAnimation->setEndValue(310);
    pAnimation->addAnimation(pUpperRightArmAnimation);

    QPropertyAnimation* pLowerRightArmAnimation = new QPropertyAnimation(pLowRightArmItem,"rotation");
    pLowerRightArmAnimation->setStartValue(0);
    pLowerRightArmAnimation->setEndValue(-70);
    pAnimation->addAnimation(pLowerRightArmAnimation);


    QPropertyAnimation* pUpperLeftLegAnimation = new QPropertyAnimation(pUpperRightLegItem,"rotation");
    pUpperLeftLegAnimation->setStartValue(150);
    pUpperLeftLegAnimation->setEndValue(80);
    pAnimation->addAnimation(pUpperLeftLegAnimation);

    QPropertyAnimation* pLowerLeftLegAnimation = new QPropertyAnimation(pLowRightLegItem,"rotation");
    pLowerLeftLegAnimation->setStartValue(70);
    pLowerLeftLegAnimation->setEndValue(10);
    pAnimation->addAnimation(pLowerLeftLegAnimation);

    QPropertyAnimation* pUpperRightLegAnimation = new QPropertyAnimation(pUpperLeftLegItem,"rotation");
    pUpperRightLegAnimation->setStartValue(40);
    pUpperRightLegAnimation->setEndValue(120);
    pAnimation->addAnimation(pUpperRightLegAnimation);

    QPropertyAnimation* pLowerRightLegAnimation = new QPropertyAnimation(pLowLeftLegItem,"rotation");
    pLowerRightLegAnimation->setStartValue(10);
    pLowerRightLegAnimation->setEndValue(50);
    pAnimation->addAnimation(pLowerRightLegAnimation);

    QPropertyAnimation* pTorsoAnimation = new QPropertyAnimation(pTorsoItem,"rotation");
    pTorsoAnimation->setStartValue(5);
    pTorsoAnimation->setEndValue(-20);
    pAnimation->addAnimation(pTorsoAnimation);

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

    }
    pAnimation->setLoopCount(-1);
    pAnimation->start();
}

QRectF QRobot::boundingRect() const
{
    return QRectF();

}

void QRobot::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(painter);
    Q_UNUSED(option);
    Q_UNUSED(widget);
}

项目的显示效果如下:

微信截图_20241009232814

好了,项目介绍到这里。项目源代码:DanceRobot

该项目用到的类有:

QRandomGenerator:QRandomGenerator 类是 Qt 框架中的一个工具类,用于生成随机数。这个类提供了一个简单易用的接口来获取随机值,这些值来自于一个高质量的随机数生成器(RNG)

**QDrag:**QDrag 类为基于 MIME (多用途 Internet 邮件扩展) 的拖放数据传输提供支持。

QMimeData:QMimeData 类在 Qt 中用于表示和存储拖放操作中传输的数据。它是一个抽象类,提供了一种机制来封装不同类型的数据,这些数据可以是文本、图片、文件链接等。

QLatin1Char:QLatin1Char 类在 Qt 中是一个方便的辅助类,它用于表示单个 Latin-1(ISO-8859-1)字符。这个类的主要作用是提供对 Latin-1 字符集中的字符进行快速访问和操作。

QParalleAnimationGroupQParallelAnimationGroup 类是 Qt 动画框架中的一个并行动画容器组。当启动 QParallelAnimationGroup 时,它会同时启动组内所有的动画,即所有动画并行运行。这个动画组会在持续时间最长的动画结束时完成。你可以像对待任何其他 QAbstractAnimation 一样对待 QParallelAnimationGroup,例如,可以暂停、恢复或将其添加到其他动画组中。

QGraphicsObject:QGraphicsObject 类是 Qt 的图形视图框架中的一个抽象基类,用于表示可以被绘制到 QGraphicsScene 中的任何对象。它是 QGraphicsItem 的一个子类,专门用于表示具有绘图和事件处理能力的对象。

QPropertyAnimation:QPropertyAnimation 类是 Qt 中用于创建动画的一个类,它通过变化对象的属性值来实现动画效果。

qvariant_cast:qvariant_cast 是 Qt 中的一个模板函数,它用于将 QVariant 对象转换为特定的类型。如果 QVariant 可以转换为请求的类型,qvariant_cast 会返回转换后的值;如果转换失败,它会返回默认构造的值。这与 QVariant::value 函数的作用相同,但 qvariant_cast 在类型转换失败时不会抛出异常,而是返回默认值,这使得它在某些情况下比 QVariant::value 更安全。

qobject_cast:qobject_cast 是 Qt 提供的一个模板函数,用于在运行时将 QObject 类型的指针或引用转换为它的派生类指针或引用。这个函数类似于 C++ 的 dynamic_cast,但它不需要运行时类型信息(RTTI),因此可以在不支持 RTTI 或禁用 RTTI 的情况下使用。

好了,项目介绍到这里。
Qt 的图形视图框架中的一个抽象基类,用于表示可以被绘制到 QGraphicsScene 中的任何对象。它是 QGraphicsItem 的一个子类,专门用于表示具有绘图和事件处理能力的对象。

QPropertyAnimation:QPropertyAnimation 类是 Qt 中用于创建动画的一个类,它通过变化对象的属性值来实现动画效果。

qvariant_cast:qvariant_cast 是 Qt 中的一个模板函数,它用于将 QVariant 对象转换为特定的类型。如果 QVariant 可以转换为请求的类型,qvariant_cast 会返回转换后的值;如果转换失败,它会返回默认构造的值。这与 QVariant::value 函数的作用相同,但 qvariant_cast 在类型转换失败时不会抛出异常,而是返回默认值,这使得它在某些情况下比 QVariant::value 更安全。

qobject_cast:qobject_cast 是 Qt 提供的一个模板函数,用于在运行时将 QObject 类型的指针或引用转换为它的派生类指针或引用。这个函数类似于 C++ 的 dynamic_cast,但它不需要运行时类型信息(RTTI),因此可以在不支持 RTTI 或禁用 RTTI 的情况下使用。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐