Qt之QGraphicsView实战篇

2023-11-15

作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

前言

前面的章节介绍了 Graphics View 绘图架构,终于到实战了,真的是千呼万唤始出来!这一章节就用 Graphics View 绘图架构来做一个绘图工具,实现一些基础图形的绘制,废话不多说先来看一下结果演示:

在这里插入图片描述

图形绘制介绍

我们知道简单的几何图形,只要确定两个点就可以完成绘制。比如要画一个圆,一个点可以定为圆心,由另一个点则可以计算出到圆心的距离,即圆的为半径,确定了圆心和半径那么这个圆就完成了。

同样的道理,椭圆、正方形、矩形、圆端矩形,也可以由一个中心点和一个可拖动来改变图形的形状和大小的点(这里我们管这个点叫边缘点)来确定,边缘点的坐标可以直接用来确定图形的宽度和高度。

而在饼、和弦中,边缘点不仅仅是用来决定图形的宽度和高度,还需要用来确定角度(与X轴正方向的夹角)。

最麻烦的是多边形,因为每点击一下就需要绘制一个点并且完成连线,所以需要把每个点的坐标从场景传递到图形,然后在图形中完成绘制。

自定义功能图元 - 点

点依附于图形之上,所以构造函数中需要传入一个图形,代表这个点在这个图形上,并且需要初始化点的坐标和类型;

class BPointItem : public QObject, public QAbstractGraphicsShapeItem
{
    Q_OBJECT

public:
    enum PointType {
        Center = 0, // 中心点
        Edge,       // 边缘点(可拖动改变图形的形状、大小)
        Special     // 特殊功能点
    };

    BPointItem(QAbstractGraphicsShapeItem* parent, QPointF p, PointType type);

    QPointF getPoint() { return m_point; }
    void setPoint(QPointF p) { m_point = p; }

protected:
    virtual QRectF boundingRect() const override;

    virtual void paint(QPainter *painter,
                       const QStyleOptionGraphicsItem *option,
                       QWidget *widget) override;

    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;

private:
    QPointF m_point;
    PointType m_type;
};

根据类型不同,绘制的形状也不同。中心点是一个圆,并且光标是 OpenHandCursor,而其他类型的点则是一个正方形,光标是 PointingHandCursor

当我们移动点的时候,如果是中心点则会整个图形一起移动,而移动边缘点的时候会根据边缘点的坐标来动态改变图形的形状,这里代码比较多就不贴了。

BPointItem::BPointItem(QAbstractGraphicsShapeItem* parent, QPointF p, PointType type)
    : QAbstractGraphicsShapeItem(parent)
    , m_point(p)
    , m_type(type)
{
    this->setPos(m_point);
    this->setFlags(QGraphicsItem::ItemIsSelectable |
                   QGraphicsItem::ItemIsMovable |
                   QGraphicsItem::ItemIsFocusable);

    switch (type) {
    case Center:
        this->setCursor(Qt::OpenHandCursor);
        break;
    case Edge:
        this->setCursor(Qt::PointingHandCursor);
        break;
    case Special:
        this->setCursor(Qt::PointingHandCursor);
        break;
    default: break;
    }
}

QRectF BPointItem::boundingRect() const
{
    return QRectF(-4, -4, 8, 8);
}

void BPointItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    painter->setPen(this->pen());
    painter->setBrush(this->brush());
    this->setPos(m_point);

    switch (m_type) {
    case Center:
        painter->drawEllipse(-4, -4, 8, 8);
        break;
    case Edge:
        painter->drawRect(QRectF(-4, -4, 8, 8));
        break;
    case Special:
        painter->drawRect(QRectF(-4, -4, 8, 8));
        break;
    default: break;
    }
}

void BPointItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if ( event->buttons() == Qt::LeftButton ) {
        qreal dx = event->scenePos().x() - event->lastScenePos().x();
        qreal dy = event->scenePos().y() - event->lastScenePos().y();

        BGraphicsItem* item = static_cast<BGraphicsItem *>(this->parentItem());
        BGraphicsItem::ItemType itemType = item->getType();

        switch (m_type) {
        case Center: {
            item->moveBy(dx, dy);
            this->scene()->update();
        } break;
        case Edge: {
            略...
        } break;
        case Special: {
            略...
        } break;
        default: break;
        }
    }
}

自定义图元基类

定义了一个图元基类,每一个图元都至少需要一个中心点、一个边缘点和图元类型。重写了焦点事件,当获得焦点时会改变颜色

class BGraphicsItem : public QObject, public QAbstractGraphicsShapeItem
{
    Q_OBJECT

public:
    enum ItemType {
        Circle = 0,         // 圆
        Ellipse,            // 椭圆
        Concentric_Circle,  // 同心圆
        Pie,                // 饼
        Chord,              // 和弦
        Rectangle,          // 矩形
        Square,             // 正方形
        Polygon,            // 多边形
        Round_End_Rectangle,// 圆端矩形
        Rounded_Rectangle   // 圆角矩形
    };

    QPointF getCenter() { return m_center; }
    void setCenter(QPointF p) { m_center = p; }

    QPointF getEdge() { return m_edge; }
    void setEdge(QPointF p) { m_edge = p; }

    ItemType getType() { return m_type; }

protected:
    BGraphicsItem(QPointF center, QPointF edge, ItemType type);

    virtual void focusInEvent(QFocusEvent *event) override;
    virtual void focusOutEvent(QFocusEvent *event) override;

protected:
    QPointF m_center;
    QPointF m_edge;
    ItemType m_type;
    BPointItemList m_pointList;

    QPen m_pen_isSelected;
    QPen m_pen_noSelected;
};
BGraphicsItem::BGraphicsItem(QPointF center, QPointF edge, ItemType type)
    : m_center(center), m_edge(edge), m_type(type)
{
    m_pen_noSelected.setColor(QColor(0, 160, 230));
    m_pen_noSelected.setWidth(2);
    m_pen_isSelected.setColor(QColor(255, 0, 255));
    m_pen_isSelected.setWidth(2);

    this->setPen(m_pen_noSelected);
    this->setFlags(QGraphicsItem::ItemIsSelectable |
                   QGraphicsItem::ItemIsMovable |
                   QGraphicsItem::ItemIsFocusable);
}

void BGraphicsItem::focusInEvent(QFocusEvent *event)
{
    Q_UNUSED(event);
    this->setPen(m_pen_isSelected);
}

void BGraphicsItem::focusOutEvent(QFocusEvent *event)
{
    Q_UNUSED(event);
    this->setPen(m_pen_noSelected);
}

矩形

以矩形为例来介绍一下图元,构造函数参数为中心点坐标和矩形的宽、高、类型。初始化中心点和边缘点之后即可绘制出矩形,重写 paint 函数把矩形的宽和高绑定到 m_edge 的坐标,矩形的大小就由边缘点的坐标决定,当我们拖到边缘点时只需更新 m_edge 的坐标,矩形的形状就能动态改变

class BRectangle : public BGraphicsItem
{
public:
    BRectangle(qreal x, qreal y, qreal width, qreal height, ItemType type);

protected:
    virtual QRectF boundingRect() const override;

    virtual void paint(QPainter *painter,
                       const QStyleOptionGraphicsItem *option,
                       QWidget *widget) override;
};
BRectangle::BRectangle(qreal x, qreal y, qreal width, qreal height, ItemType type)
    : BGraphicsItem(QPointF(x,y), QPointF(width/2,height/2), type)
{
    BPointItem *point = new BPointItem(this, m_edge, BPointItem::Edge);
    point->setParentItem(this);
    m_pointList.append(point);
    m_pointList.append(new BPointItem(this, m_center, BPointItem::Center));
    m_pointList.setRandColor();
}

QRectF BRectangle::boundingRect() const
{
    return QRectF(m_center.x() - m_edge.x(), m_center.y() - m_edge.y(), m_edge.x() * 2, m_edge.y() * 2);
}

void BRectangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    painter->setPen(this->pen());
    painter->setBrush(this->brush());

    QRectF ret(m_center.x() - m_edge.x(), m_center.y() - m_edge.y(), m_edge.x() * 2, m_edge.y() * 2);
    painter->drawRect(ret);
}

自定义场景

当我们绘制多边形时,需要每点击一下绘制点和连线。此时我们的鼠标点击事件是在场景中的,而我们需要把场景中的坐标传递到图元中,所以我们需要自定义场景来重写场景中的鼠标事件

class BQGraphicsScene : public QGraphicsScene
{
    Q_OBJECT

public:
    BQGraphicsScene(QObject *parent = nullptr);

    void startCreate();

protected:
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);

signals:
    void updatePoint(QPointF p, QList<QPointF> list, bool isCenter);
    void createFinished();

protected:
    QList<QPointF> m_list;
    bool is_creating_BPolygon;
};
BQGraphicsScene::BQGraphicsScene(QObject *parent) : QGraphicsScene(parent)
{
    is_creating_BPolygon = false;
}

void BQGraphicsScene::startCreate()
{
    is_creating_BPolygon = true;
    m_list.clear();
}

void BQGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (is_creating_BPolygon) {
        QPointF p(event->scenePos().x(), event->scenePos().y());

        switch ( event->buttons() )
        {
        case Qt::LeftButton: {
            m_list.push_back(p);
            emit updatePoint(p, m_list, false);
        } break;
        case Qt::RightButton: {
            if (m_list.size() >= 3) {
                emit updatePoint(p, m_list, true);
                emit createFinished();
                is_creating_BPolygon = false;
                m_list.clear();
            }
        } break;
        default: break;
        }
    } else {
        QGraphicsScene::mousePressEvent(event);
    }
}
void MainWindow::on_polygonBtn_clicked()
{
    m_scene.startCreate();
    setBtnEnabled(false);
    BPolygon *m_polygon = new BPolygon(BGraphicsItem::ItemType::Polygon);
    m_scene.addItem(m_polygon);

    connect(&m_scene, SIGNAL(updatePoint(QPointF, QList<QPointF>, bool)), m_polygon, SLOT(pushPoint(QPointF, QList<QPointF>, bool)));
    connect(&m_scene, &BQGraphicsScene::createFinished, [=](){
        setBtnEnabled(true);
    });
}

右键弹窗修改属性

在这里插入图片描述

右键跳出弹窗可以重写 contextMenuEvent 函数来完成,弹窗中需要哪些控件可以自定义实现;

void BEllipse::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
    if ( !this->isSelected() )
        return;

    QMenu* menu = new QMenu();
    menu->setStyleSheet("QMenu { background-color:rgb(89,87,87); border: 5px solid rgb(235,110,36); }");

    QSpinBox* width_spinBox = new QSpinBox(menu);
    width_spinBox->setStyleSheet("QSpinBox{ width:120px; height:30px;}");
    width_spinBox->setRange(0, 1000);
    width_spinBox->setPrefix("w: ");
    width_spinBox->setSuffix(" mm");
    width_spinBox->setSingleStep(1);
    width_spinBox->setValue(2 * abs(m_edge.x()));
    connect(width_spinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [=](int v){
        m_edge.setX(v/2);
        m_pointList.at(0)->setPoint(m_edge);
        this->hide();
        this->update();
        this->show();
    });

    QSpinBox* height__spinBox = new QSpinBox(menu);
    height__spinBox->setStyleSheet("QSpinBox{ width:120px; height:30px;}");
    height__spinBox->setRange(0, 1000);
    height__spinBox->setPrefix("h: ");
    height__spinBox->setSuffix(" mm");
    height__spinBox->setSingleStep(1);
    height__spinBox->setValue(2 * abs(m_edge.y()));
    connect(height__spinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [=](int v){
        m_edge.setY(v/2);
        m_pointList.at(0)->setPoint(m_edge);
        this->hide();
        this->update();
        this->show();
    });

    QWidgetAction* width_widgetAction = new QWidgetAction(menu);
    width_widgetAction->setDefaultWidget(width_spinBox);
    menu->addAction(width_widgetAction);

    QWidgetAction* height_widgetAction = new QWidgetAction(menu);
    height_widgetAction->setDefaultWidget(height__spinBox);
    menu->addAction(height_widgetAction);

    menu->exec(QCursor::pos());
    delete menu;

    QGraphicsItem::contextMenuEvent(event);
}

更多请参考

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt之QGraphicsView实战篇 的相关文章

  • 右键单击 QPushButton 上的 contextMenu

    对于我的应用程序 我在 Qt Designer 中创建了一个 GUI 并将其转换为 python 2 6 代码 关于一些QPushButton 与设计器创建 我想添加右键单击上下文菜单 菜单选项取决于应用程序状态 如何实现这样的上下文菜单
  • 另一个宏中的 Q_PROPERTY 宏

    如何放置Q PROPERTY另一个宏里面 辅助宏 define SimpleAllinOne member type public void Set member type arg member m member arg member ty
  • 第一个随机数始终小于其余随机数

    我碰巧注意到 在 C 中 使用 std rand 方法调用的第一个随机数大多数时候都明显小于第二个随机数 关于 Qt 实现 第一个几乎总是小几个数量级 qsrand QTime currentTime msec qDebug lt lt q
  • 如何从浮点数组创建新的 QImage

    我有一个代表图像的浮点数数组 列在前 我想在 QGraphicsSecene 上将图像显示为 QPixmap 为了做到这一点 我尝试使用 QImage 构造函数 QImage const uchar data int width int h
  • PyQt - 如何从给定的小部件获取顶级父级?

    给定的小部件如何访问它最旧的父级 即顶级小部件 我需要 showMinimized it 现在我正在使用 self parent parent parent showMinimized 但这似乎不是最好的方法 如果它移动了 我需要手动更改父
  • Qml 和模糊图像

    我想使用 QML 实现模糊效果 我找到了有关 效果 模糊 的参考资料 例子 http qt gitorious org lscunha qt components lscunha qt components blobs d78feec567
  • Qt qDebug() 在 Windows shell 中不起作用

    我正在使用一个qDebug Qt 框架的printf屏幕上有东西 当我从 Qt Creator 运行应用程序时它工作得很好 但是当我尝试从 Windows 执行它时cmd它什么也没显示 为什么会发生这种情况 你必须添加 CONFIG con
  • QComboBox 下拉项边距

    我想设计我的风格QComboBox为下拉项目留出边距 现在是这样的 我想要这样的东西 我尝试过 QComboBox QAbstractItemView item margin 3px 但它不起作用 你能帮我解决这个问题吗 您想在项目之间设置
  • 在 QtCreator 中使用 .pro 和 .pri 正确(树)显示 Qt 项目

    是否可以使用项目包含文件 pri 显示不止一个额外级别 例如如果 pro文件包括 pri文件包含两个 pri files pro pri pri pri 在 QtCreator 项目 中显示具有误导性 pro pri pri pri 它根本
  • 如何在 OS X 上的 Qt 应用程序中设置应用程序图标,足以进行分发?

    跟进这个答案 https stackoverflow com a 20918932 368896 to 这个问题 https stackoverflow com questions 20909341 what is the fastest
  • 如何在QT上暂停和重新启动Qtimer

    我有 Ubuntu 我正在使用 IDEQT on C 我将暂停和恢复计时器 例如 void Ordonnancer les taches on pushButton clicked connect dataTimer SIGNAL time
  • Qt 为什么使用 QString::number() 而不是 QLocale().toString()?

    我正在开发的应用程序将在许多国家推出 并且需要支持他们的语言 我一直在回顾我的代码并替换以下每个实例 QString number and QString toDouble with QLocale toString and QLocale
  • 如何声明一个带有成员函数指针的函数

    我有一个类 其中的成员变量指向库对象 class myClassA private libraryClass libraryObject 该库类发出事件 以字符串为特征 并提供一种机制 允许客户端类指定在发出事件时应调用的成员函数 因此在m
  • Qt 嵌入式触摸屏 QMouseEvents 在收到 MouseButtonRelease 之前未收到

    我在带有触摸屏的小型 ARM 嵌入式 Linux 设备上使用 Qt 4 8 3 我的触摸屏配置了 tslib 并对其进行了校准 因此 etc 中有一个 pointcal 文件 我的触摸事件的位置工作得很好 但无论如何我都会在鼠标按下或鼠标释
  • 如何doxygen注释Qt属性?

    我想将 Doxygen 注释附加到我的 Q PROPERTY 例如 song h class Song public QObject Q OBJECT private Q PROPERTY QString title READ title
  • Qt中用于线程间通信的类设计

    问题陈述 用相机跟踪物体并相应地移动相机的方位角和仰角 Process 相机获取物体的图像 处理相机的每一帧以查找物体 应该被跟踪 并将每帧中生成的信息传递给机械设备 万向节 以平移和倾斜方式移动摄像机 Design 主 Gui 在一个线程
  • 了解 Qt3D 创建的网格

    我创建了一个 Qt3D 网格 如下所示 Qt3DCore QEntity newEntity new Qt3DCore QEntity Qt3DExtras QConeMesh mesh new Qt3DExtras QConeMesh m
  • PyQt4 信号和槽

    我正在使用 PyQt4 编写我的第一个 Python 应用程序 我有一个 MainWindow 和一个 Dialog 类 它是 MainWindow 类的一部分 self loginDialog LoginDialog 我使用插槽和信号 这
  • 无法运行 Qt 应用程序:找不到版本“Qt_5”

    我运行 Ubuntu 16 04 LTS 我的问题是我无法运行可以编译的 Qt5 应用程序 这是我尝试运行它时得到的结果 home user Desktop sconfig dist Release GNU Linux SCongif us
  • Qt QML 数据模型似乎不适用于 C++

    我一直在使用中的示例http doc qt digia com 4 7 qdeclarativemodels html http doc qt digia com 4 7 qdeclarativemodels html这是 QML 声明性数

随机推荐

  • 【Unity疑难杂症#持续更新……】

    目录 1 Unity打包时 自定义shader没效果 2 Unity打包ios时动画不播放 3 Android Unable to merge android manifest Error 4 Unity打到刘海屏的Android手机上不能
  • php上传图片到MySQL数据库代码如下

    效果图入下 用到两个php文件 index php上传图片 img php获取图片 index php代码如下
  • 2023华为OD机试真题 C++【分奖金/单调栈】

    题目 代码 include
  • C++ 标准库

    C 标准库可以分为两部分 标准函数库 这个库是由通用的 独立的 不属于任何类的函数组成的 函数库继承自 C 语言 面向对象类库 这个库是类及其相关函数的集合 C 标准库包含了所有的 C 标准库 为了支持类型安全 做了一定的添加和修改 标准函
  • Seaborn系列教程(5)

    seaborn countplot 计数图 柱状图 解析 使用条形图 柱状图 显示每个分类数据中的数量统计 seaborn countplot x None y None hue None data None order None hue
  • 【Linux】缓冲区/磁盘inode/动静态库制作

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 目录 一 缓冲区 1 缓冲区的概念 2 缓冲区的意义 3 缓冲区
  • 【mitmproxy】python gui工具与mitmproxy的结合使用

    mitmproxy介绍 mitmproxy是一款流行的开源工具 用于拦截 修改和检查网络流量 它可以在中间人攻击 Man in the Middle 的场景下用于分析 调试和测试网络应用程序 其他地方很多介绍 可以跳过 以下是mitmpro
  • linux内核oops错误码说明,调试 – 了解内核oops错误代码

    在ARM的内核oops中跟踪日志打印在内核日志中 lt 1 gt 4205 112835 I 0 swapper 0 0 c0 Unable to handle kernel paging request at virtual addres
  • FPGA开发(4)——AXI_LITE总线协议

    一 AXI总线简介 对于axi总线的学习我主要是参考了赛灵思的ug1037文档以及arm的INI0022D手册 对其中的内容做了总结 AXI是amba总线的一种 包含三种 axi full axi lite和axi stream AXI工作
  • 关于CentOS7虚拟机配置静态IP地址及ping不通通百度等官网

    一 IP地址的配置 1 查看MAC地址 点击 虚拟机 gt gt gt 设置 gt gt gt 网络适配器 gt gt gt 高级 gt gt gt 记住MAC地址 后面会用到 2 查看子网掩码和网关IP 点击 编辑 gt gt gt 虚拟
  • Vue极简使用

    Vue 安装Vue 模板语法 安装Vue 安装nodejs 这里我安装的是14 5 4版本 https nodejs org download release v14 15 4 解压后配置一下环境变量就行 安装cnpm镜像 这个安装的版本可
  • python小游戏——走出迷宫代码开源

    作者 小刘在C站 个人主页 小刘主页 每天分享云计算网络运维课堂笔记 努力不一定有回报 但一定会有收获加油 一起努力 共赴美好人生 夕阳下 是最美的 绽放 愿所有的美好 再疫情结束后如约而至 目录 一 游戏代码效果呈现 二 游戏主代码 1
  • 矩阵运算(一)最小二乘法

    最小二乘法 前言 最小二乘法拟合一元多项式的简单推导 进一步的思考 CPP开发者封装最小二乘法 QR分解 Matlab中使用QR分解 Python numpy中使用 参考文献 作者说 前言 最小二乘法在函数拟合的过程中广为应用 不少读者使用
  • Spring Boot Maven Plugin -- repackage目标;spring-boot-maven-plugin的executable配置

    Spring Boot Maven Plugin repackage目标 Spring Boot Maven Plugin插件提供spring boot在maven中的支持 允许你打包可运行的jar包或war包 插件提供了几个maven目标
  • nginx server.conf demo

    server access log Users xx software nginx logs access log error log Users xx software nginx logs error log listen 50001
  • 大数据毕设 - 深度学习安全帽佩戴检测系统(python OpenCV yolov5)

    文章目录 0 前言 1 课题背景 2 效果演示 3 Yolov5框架 4 数据处理和训练 4 1 安全帽检测 4 2 检测危险区域内是否有人 5 最后 0 前言 Hi 大家好 这里是丹成学长的毕设系列文章 对毕设有任何疑问都可以问学长哦 这
  • rke2 在线部署 kubernetes

    文章目录 1 还原虚拟机 2 背景 3 介绍 4 预备条件 5 1 配置网卡 5 配置主机名 6 配置互信 7 安装 ansible 8 系统初始化 9 kube master01 部署 9 1 定制配置文件 可选 9 2 部署 9 3 命
  • 微信二维码的生成(java后端)--邀请新人

    目录 写在前言 1 微信官方文档 2 具体分析 写在前言 最近因为在学习微信小程序邀请新用户的功能 所以需要后端生成二维码并且携带本人的用户id或者其他的信息 传给前端 用户通过这个二维码去进行登录或者其他的操作 这时候前端人员记录下来邀请
  • Qt 2D绘图坐标系统

    一 坐标系简介 Qt中每一个窗口都有一个坐标系 默认的 窗口左上角为坐标原点 然后水平向右依次增大 水平向左依次减小 垂直向下依次增大 垂直向上依次减小 原点即为 0 0 点 然后以像素为单位增减 例如 void Dialog paintE
  • Qt之QGraphicsView实战篇

    作者 billy 版权声明 著作权归作者所有 商业转载请联系作者获得授权 非商业转载请注明出处 前言 前面的章节介绍了 Graphics View 绘图架构 终于到实战了 真的是千呼万唤始出来 这一章节就用 Graphics View 绘图