Qt图形视图框架实战:用QGraphicsScene和QGraphicsView打造简易绘图工具(附完整代码)

2D绘图QT的图形视图框架(QGraphicsScene、QGraphicsView、QGraphicsItem) 利用Qt的图形视图框架进行2D绘图 阅读详情

Qt图形视图框架实战:用QGraphicsScene和QGraphicsView打造简易绘图工具

在Qt生态中,图形视图框架一直是最具生产力的模块之一。想象一下,当你需要开发一个矢量绘图工具、流程图编辑器甚至简单的2D游戏时,这套框架能让你摆脱底层绘制的繁琐,专注于业务逻辑的实现。不同于直接使用QPainter进行绘制,图形视图框架通过场景(Scene)-视图(View)-图形项(Item)的三层架构,提供了更高级别的抽象和更完善的交互支持。

本文将带你从零构建一个功能完整的绘图工具,涵盖图形项的创建、场景管理、视图交互等核心功能。不同于理论讲解,我们会采用"代码先行"的方式,每个功能点都配有可运行的代码示例。你只需要基本的Qt开发环境(Qt 5.15+或Qt 6.x)和C++基础就能跟上节奏。

1. 环境准备与基础架构

1.1 项目初始化

首先创建一个标准的Qt Widgets Application项目。在.pro文件中确保包含以下模块:

QT += core gui widgets

对于主窗口,我们继承QMainWindow并添加核心组件:

#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>

class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    
private:
    QGraphicsScene *scene;
    QGraphicsView *view;
};

在构造函数中初始化场景和视图:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // 创建场景(虚拟画布)
    scene = new QGraphicsScene(this);
    scene->setSceneRect(0, 0, 800, 600);
    
    // 创建视图(显示窗口)
    view = new QGraphicsView(scene);
    setCentralWidget(view);
    
    // 视图优化设置
    view->setRenderHint(QPainter::Antialiasing);
    view->setDragMode(QGraphicsView::RubberBandDrag);
    view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
}

1.2 场景坐标系理解

Qt图形视图框架使用三层坐标系系统:

  1. 场景坐标:虚拟的无限大画布,原点默认为(0,0)
  2. 视图坐标:实际显示窗口的像素坐标
  3. 图形项坐标:每个图形项自身的局部坐标系

关键转换方法:

方法作用
mapToScene()视图坐标→场景坐标
mapFromScene()场景坐标→视图坐标
mapToItem()场景坐标→图形项坐标

提示:在实现交互功能时,务必注意坐标系的转换,否则会出现点击位置不准等问题

2. 实现基本绘图功能

2.1 图形项创建与管理

我们首先实现矩形、椭圆和直线三种基本图形。创建自定义图形项基类:

class DrawItem : public QGraphicsItem {
public:
    enum { Type = UserType + 1 };
    int type() const override { return Type; }
    
    // 公共接口
    virtual void updateShape(const QPointF &start, const QPointF &end) = 0;
};

具体实现矩形图形项:

class RectItem : public DrawItem {
public:
    QRectF boundingRect() const override {
        return QRectF(startPoint, endPoint).normalized();
    }
    
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override {
        painter->setPen(pen());
        painter->setBrush(brush());
        painter->drawRect(QRectF(startPoint, endPoint));
    }
    
    void updateShape(const QPointF &start, const QPointF &end) override {
        prepareGeometryChange();
        startPoint = start;
        endPoint = end;
        update();
    }
    
private:
    QPointF startPoint, endPoint;
};

2.2 绘图交互实现

在MainWindow中添加绘图状态管理:

enum DrawMode {
    None,
    DrawRect,
    DrawEllipse,
    DrawLine
};

class MainWindow {
    // ...
    void setupToolBar();
    
    DrawMode currentMode = None;
    DrawItem *currentItem = nullptr;
    QPointF startPos;
};

实现鼠标事件处理:

void MainWindow::mousePressEvent(QMouseEvent *event) {
    if (currentMode == None) return;
    
    startPos = view->mapToScene(event->pos());
    
    switch (currentMode) {
    case DrawRect:
        currentItem = new RectItem;
        break;
    case DrawEllipse:
        currentItem = new EllipseItem;
        break;
    case DrawLine:
        currentItem = new LineItem;
        break;
    }
    
    scene->addItem(currentItem);
}

void MainWindow::mouseMoveEvent(QMouseEvent *event) {
    if (!currentItem) return;
    
    QPointF endPos = view->mapToScene(event->pos());
    currentItem->updateShape(startPos, endPos);
}

void MainWindow::mouseReleaseEvent(QMouseEvent *) {
    currentItem = nullptr;
}

3. 高级功能实现

3.1 图形项选择与变换

Qt内置了对图形项变换的支持,我们可以轻松实现移动、旋转和缩放:

// 在工具栏添加变换按钮
QAction *moveAction = toolbar->addAction("移动");
QAction *rotateAction = toolbar->addAction("旋转");
QAction *scaleAction = toolbar->addAction("缩放");

connect(moveAction, &QAction::triggered, [this]() {
    view->setDragMode(QGraphicsView::ScrollHandDrag);
});

connect(rotateAction, &QAction::triggered, [this]() {
    if (scene->selectedItems().isEmpty()) return;
    
    QGraphicsItem *item = scene->selectedItems().first();
    item->setRotation(item->rotation() + 15);
});

connect(scaleAction, &QAction::triggered, [this]() {
    if (scene->selectedItems().isEmpty()) return;
    
    QGraphicsItem *item = scene->selectedItems().first();
    item->setScale(item->scale() * 1.2);
});

3.2 撤销/重做功能实现

利用Qt的Undo框架,我们可以轻松实现历史记录:

#include <QUndoStack>
#include <QUndoCommand>

class AddCommand : public QUndoCommand {
public:
    AddCommand(QGraphicsScene *scene, DrawItem *item, QUndoCommand *parent = nullptr)
        : QUndoCommand(parent), scene(scene), item(item) {
        item->setFlag(QGraphicsItem::ItemIsSelectable);
        item->setFlag(QGraphicsItem::ItemIsMovable);
    }
    
    void undo() override { scene->removeItem(item); }
    void redo() override { scene->addItem(item); }
    
private:
    QGraphicsScene *scene;
    DrawItem *item;
};

// 在MainWindow中
QUndoStack *undoStack;

// 修改鼠标释放事件
void MainWindow::mouseReleaseEvent(QMouseEvent *) {
    if (currentItem) {
        undoStack->push(new AddCommand(scene, currentItem));
        currentItem = nullptr;
    }
}

4. 性能优化与高级技巧

4.1 大场景优化策略

当场景中包含大量图形项时,性能优化至关重要:

  1. 设置适当的场景矩形:

    scene->setSceneRect(-10000, -10000, 20000, 20000);
    
  2. 使用ItemIndexMethod:

    scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
    
  3. 批量操作优化:

    scene->blockSignals(true);
    // 执行大量添加/删除操作
    scene->blockSignals(false);
    

4.2 自定义图形项进阶

实现一个可编辑的文本项展示高级技巧:

class EditableTextItem : public QGraphicsTextItem {
    Q_OBJECT
public:
    EditableTextItem(QGraphicsItem *parent = nullptr)
        : QGraphicsTextItem(parent) {
        setTextInteractionFlags(Qt::TextEditorInteraction);
        setFlag(QGraphicsItem::ItemIsFocusable);
    }
    
protected:
    void focusOutEvent(QFocusEvent *event) override {
        setTextInteractionFlags(Qt::NoTextInteraction);
        QGraphicsTextItem::focusOutEvent(event);
        emit textChanged(toPlainText());
    }
    
    void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) override {
        setTextInteractionFlags(Qt::TextEditorInteraction);
        setFocus();
    }
    
signals:
    void textChanged(const QString &text);
};

5. 项目扩展与实用功能

5.1 文件保存与加载

实现场景的序列化功能:

void MainWindow::saveScene(const QString &filename) {
    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly)) return;
    
    QDataStream out(&file);
    out << scene->items().size();
    
    foreach (QGraphicsItem *item, scene->items()) {
        if (DrawItem *drawItem = dynamic_cast<DrawItem*>(item)) {
            out << drawItem->type();
            drawItem->save(out);
        }
    }
}

void MainWindow::loadScene(const QString &filename) {
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly)) return;
    
    scene->clear();
    QDataStream in(&file);
    
    int count;
    in >> count;
    
    for (int i = 0; i < count; ++i) {
        int type;
        in >> type;
        
        DrawItem *item = nullptr;
        switch (type) {
        case RectItem::Type: item = new RectItem; break;
        case EllipseItem::Type: item = new EllipseItem; break;
        case LineItem::Type: item = new LineItem; break;
        }
        
        if (item) {
            item->load(in);
            scene->addItem(item);
        }
    }
}

5.2 插件系统设计

通过插件机制支持更多图形类型:

class ShapePlugin {
public:
    virtual ~ShapePlugin() {}
    virtual QString name() const = 0;
    virtual QIcon icon() const = 0;
    virtual DrawItem *createItem() = 0;
};

// 在主窗口中管理插件
QList<ShapePlugin*> plugins;

void MainWindow::loadPlugins() {
    QDir pluginsDir(qApp->applicationDirPath() + "/plugins");
    
    foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
        if (ShapePlugin *plugin = qobject_cast<ShapePlugin*>(loader.instance())) {
            plugins.append(plugin);
            QAction *action = toolbar->addAction(plugin->icon(), plugin->name());
            connect(action, &QAction::triggered, [this, plugin]() {
                currentMode = CustomMode;
                currentPlugin = plugin;
            });
        }
    }
}

在实现过程中,我发现图形项的坐标转换是最容易出错的地方。特别是在处理复合图形项时,一定要清楚当前操作是在哪个坐标系下进行的。另一个实用技巧是为所有自定义图形项实现save()和load()方法,这样序列化功能就能统一处理所有图形类型。

图形绘制QGraphicsView、QGraphicsScene、QGraphicsItem、Qt GUI QGraphicsView、QGraphicsScene、QGraphicsItem 和 Qt GUI(QGuiApplication)的使用及关系 阅读详情

相关推荐

【Qt系列】实时显示鼠标位于QGraphicsView或QGraphicsScene坐标点

演示效果 坐标点的显示在右下角。 前言 在Graphics View框架结构主要包含三个类:场景类(QGraphicsScene)、视图类(QGraphicsView)和图元类(QGraphicsItem),统称为“三要素”。这三要素都有自己的坐标系,各个坐标系之间可以相互转换。这次博客主要实现的就是展现鼠标在其中两个坐标系的坐标点。 场景坐标 场景坐标是所有图元的基础坐标系统。场景坐标系统描述了顶层图元的位置,并且构成从视图传播到场景的所有场景事件的基础。每个图元在场景上都有场景坐标和边界矩形。场景坐标

wuwenbin12的博客 1万+

Qt Graphics View 框架综合架构与性能分析

本文深入解析Qt Graphics View框架的架构设计,分为三部分:首先介绍其基于MVC的架构,将场景(模型)、视图与图元解耦,实现高性能渲染;其次详细分析图元、场景与视图三层坐标系及其转换方法,强调对坐标系的理解是实现交互功能的关键;最后探讨事件传播机制,说明如何通过事件处理实现交互式应用。文章指出,理解这种解耦设计和坐标系转换是高效使用该框架的核心,能显著提升开发效率和调试能力。

greenspan的专栏 1612

QT图形视图框架——QGraphicsView/QGraphicsScene/QGraphicsItem

图形视图框架 1、功能:管理数量庞大的自定义2D图形项 2、组成:视图(QGraphicsView)+场景(QGraphicsScene)+图形项(QGraphicsItem) 场景(QGraphicsScene) 1、功能:图形项QGraphicsItem对象的容器,可以调QgraphicsScene::addItem()将图形项添加到场景中,QGraphicsScene::items()返回符...

weixin_38602865的博客 1381

[Qt教程] 第20篇 2D绘图(十)图形视图框架(下)

[Qt教程] 第20篇 2D绘图(十)图形视图框架(下) 楼主  发表于 2013-5-4 15:43:02 | 查看: 861| 回复: 0 图形视图框架(下) 版权声明 该文章原创于Qter开源社区(www.qter.org),作者yafeilinux,转载请注明出处! 导语

C/C++软件工程师、嵌入式软件工程师、物联网研发工程师、C/C++讲师、物联网讲师、嵌入式讲师、鸿蒙讲师---欢迎大家一起交流(私信添加博主微信) 4343

Qt 之图形视图框架

图形视图(Graphics View)提供了一个平台,用于大量自定义2D图元的管理与交互,并提供了一个视图部件(view widget)来显示可以缩放和旋转的图元。

青春不老,奋斗不止! 3万+

Qt-DrawingTool:基于Qt的Diagram-Scene-Example的绘图工具

Qt-DrawingTool:基于Qt的Diagram-Scene-Example的绘图工具

Qt开发技术:图形视图框架(二)场景QGraphicsScene、QGraphicsItem与QGraphicsView详解

Qt的图形视图框架,最核心的三个类为:QGraphicsScene、QGraphicsItem与QGraphicsView。

长沙红胖子 Qt・西安|省级科研团队从业背景,深耕双一流体系高精尖技术配套研发|剥离高精尖机理,沉淀高端特种行业领域 Qt 工程化落地经验|博文仅分享通用技术总结,输出公开普适工程思路。 5万+

QT的graphicsview高级例程

QT的graphicsview高级例程

Qt图形视图框架:QGraphicsView 详解

Qt图形视图框架:QGraphicsView详解

友善啊,朋友的博客 7万+

QT GraphicsView例子

基于QT GraphicsView三层架构所写的例子,通俗易懂,很经典的教程。代码简单易懂,便于初学者学习。

QGraphicsView类的简单用法

QT 控件QGraphicsView的简单用法,包含头文件和cpp,函数调用参考README.txt

Qt中的QGraphicsView、QGraphicsScene与QGraphicsItem:图形视图框架介绍

Qt中的QGraphicsView、QGraphicsScene与QGraphicsItem:图形视图框架介绍

m0_66847953的博客 3485

QGraphicsView框架实现画板

支持图形的绘制,移动与拖拽、放大缩小、旋转、复制粘贴、撤销重做;绘制图形包括矩形、多边形、椭圆、直线、曲线等

C++QT开发——GraphicsView(图形视图)

C++QT开发——GraphicsView(图形视图)

m0_60259116的博客 1万+

gnuradio+b210实现FM收音机

gnuradio+b210实现FM收音机环境介绍FM接收1 Flow Graph2 变量参数FM发射1 Flow Graph 环境介绍 Name Version gnuradio 3.7.14.0 uhd 3.15.0.0 ubuntu 18.04 usrp b210 参考 https://www.ijedr.org/papers/IJEDR1702038.pdf Audio File Transmission using GNURADIO and USRP FM接

u011745228的博客 5129

S4-FPGA-K7板级原理图硬件实战

xilinx K7板级硬件实战

weixin_66510961的博客 6156

[Qt5] QGraphics图形视图框架概述(Item、Scene和View)

在 Qt 框架内的许多模块,类和子框架下,有一块专门用于简化图形处理的工具,称为**图形视图框架**。 它包含许多类,几乎所有的类都以QGraphics开头,并且所有这些类都可用于处理构建计算机视觉应用时可能遇到的大多数图形任务。 图形视图框架将所有可能的对象简单地分为三个主要类别,即场景类(`QGraphicsScene`)、视图类(`QGraphicsView`)和图元类(`QGraphicsItem`),统称为“三元素”。随之而来的架构允许轻松地添加,删除,修改以及显示图形对象。

Loewen丶的小窝 9520

基于springboot生鲜食品订购系统的设计与实现

近年来互联网络的迅猛发展和电子终端设备的普及,赋予了各行业充足的发展空间。微信小程序的O2O生鲜食品订购相比于传统信息技术,时效性是它最大的特色,已经在电子娱乐、经济等中发挥着举足轻重的作用。短时间内迅速扩大了线上管理系统的规模。尽管O2O生鲜食品订购已经有了很大程度的发展,但在微信小程序的O2O生鲜食品订购领域上却少有建设。

1024

Qt 图形视图框架功能详解QGraphicsScene、QGraphicsView 和 QGraphicsItem

在 main.cpp 文件中先添加头文件运行程序可以看到自定义的图形项(如红色小方块)出现在了视图的正中间,背景图片的位置也有所变化,这些问题会在后续的坐标系统中进行讲解。如果只想添加简单的图形项,也可以直接使用图形视图框架提供的标准图形项,它们的效果如图所示。标准图形项通过上述步骤,即可实现自定义的图形项,满足在 Qt 图形视图框架中展示个性化图形元素的需求,为各种复杂图形界面应用开发提供基础。通过和。

Zy100Papa的博客 1545

博途S7-1500T 使用工艺对象驱动液压轴(含SimaHydTO库)

利用工艺对象控制液压轴位置含PLC控制和仿真程序文档资料下载地址如下:液压比例伺服阀的相关应用知识请参看下面的博客文章:液压位置闭环控制(比例伺服阀应用)_RXXW_Dor的博客-优快云博客。

RXXW_Dor的博客 2901

【雕爷学编程】Arduino智慧交通之RF24通信实现车队之间的实时通信和协调

通过这些示例,车队领导可以发送指令给车队成员,车队成员可以接收指令并执行相应的操作,同时车队成员也可以发送自己的位置信息给车队领导或其他成员,以实现车队之间的实时通信和协调。例如,在高速公路上,车队中的车辆可以根据前方车辆的速度和位置信息,自动调整自身的速度和距离,以保持车队的整体稳定和安全。Arduino智慧交通系统利用RF24通信模块实现车队之间的实时通信和协调,通过无线通信技术,车辆之间可以实时传输数据和指令,以实现车队的协同行驶和交通优化。通过数据处理和分析,可以生成实时的交通信息和统计数据。

雕爷学编程 1076

QGraphicsView,QGraphicsScene和QGraphicsItem

图形视图框架提供了一个基于图形项的模型视图编程方法,主要由场景、视图和图形项三部分组成,这三部分分别由QGraphicsScene、QGraphicsView和QGraphicsItem这三个类来表示。

weixin_43956958的博客 6110

2️⃣ Coze创建智能体教学(2025年全新版本)

扣子平台提供强大支持,可将智能体轻松集成至各类社交平台,使智能体得以服务更为庞大的用户群体。当下支持集成的平台如下:​1️⃣豆包:作为聚焦 AI 内容创作的平台,非常适合发布充满创意、娱乐性强的智能体。​2️⃣飞书:适合发布用于办公协作的智能体,助力团队提升工作效率。​3️⃣微信:借助公众号或小程序的形式,能够服务海量微信用户。​4️⃣抖音:适合发布与短视频相关的智能体,为用户提供创意与娱乐服务。​

李同学Lino的博客 4587
上一篇: 自动化立体仓库堆垛机控制系统STEP7 FC3功能块I/O映射实战解析
下一篇: 单片机内存告急?手搓arctan函数:泰勒级数 vs 连分数法性能实测
devops8pract
博客等级 码龄4年 533粉丝 849原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值