这一次将介绍如何使用Graphics View来实现前面所说的画板。前面说了很多有关Graphics View的好话,但是没有具体的实例很难说究竟好在哪里。现在我们就把前面的内容使用Graphics View重新实现一下,大家可以对比一下看有什么区别。

同前面相似的内容就不再叙述了,我们从上次代码的基础上进行修改,以便符合我们的需要。首先来看MainWindow的代码:

mainwindow.cpp

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)

        : QMainWindow(parent)

{

        QToolBar *bar = this->addToolBar("Tools");

        QActionGroup *group = new QActionGroup(bar);

        QAction *drawLineAction = new QAction("Line", bar);

        drawLineAction->setIcon(QIcon(":/line.png"));

        drawLineAction->setToolTip(tr("Draw a line."));

        drawLineAction->setStatusTip(tr("Draw a line."));

        drawLineAction->setCheckable(true);

        drawLineAction->setChecked(true);

        group->addAction(drawLineAction);

        bar->addAction(drawLineAction);

        QAction *drawRectAction = new QAction("Rectangle", bar);

        drawRectAction->setIcon(QIcon(":/rect.png"));

        drawRectAction->setToolTip(tr("Draw a rectangle."));

        drawRectAction->setStatusTip(tr("Draw a rectangle."));

        drawRectAction->setCheckable(true);

        group->addAction(drawRectAction);

        bar->addAction(drawRectAction);

        QLabel *statusMsg = new QLabel;

        statusBar()->addWidget(statusMsg);

        PaintWidget *paintWidget = new PaintWidget(this);

        QGraphicsView *view = new QGraphicsView(paintWidget, this);

        setCentralWidget(view);

        connect(drawLineAction, SIGNAL(triggered()),

                        this, SLOT(drawLineActionTriggered()));

        connect(drawRectAction, SIGNAL(triggered()),

                        this, SLOT(drawRectActionTriggered()));

        connect(this, SIGNAL(changeCurrentShape(Shape::Code)),

                        paintWidget, SLOT(setCurrentShape(Shape::Code)));

}

void MainWindow::drawLineActionTriggered()

{

        emit changeCurrentShape(Shape::Line);

}

void MainWindow::drawRectActionTriggered()

{

        emit changeCurrentShape(Shape::Rect);

}

由于mainwindow.h的代码与前文相同,这里就不再贴出。而cpp文件里面只有少数几行与前文不同。由于我们使用Graphics View,所以,我们必须把item添加到QGprahicsScene里面。这里,我们创建了scene的对象,而scene对象需要通过view进行观察,因此,我们需要再使用一个QGraphcisView对象,并且把这个view添加到MainWindow里面。

我们把PaintWidget当做一个scene,因此PaintWidget现在是继承QGraphicsScene,而不是前面的QWidget。

paintwidget.h

#ifndef PAINTWIDGET_H

#define PAINTWIDGET_H

#include

#include

#include "shape.h"

#include "line.h"

#include "rect.h"

class PaintWidget : public QGraphicsScene

{

        Q_OBJECT

public:

        PaintWidget(QWidget *parent = 0);

public slots:

        void setCurrentShape(Shape::Code s)

        {

                if(s != currShapeCode) {

                        currShapeCode = s;

                }

        }

protected:

        void mousePressEvent(QGraphicsSceneMouseEvent *event);

        void mouseMoveEvent(QGraphicsSceneMouseEvent *event);

        void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

private:

        Shape::Code currShapeCode;

        Shape *currItem;

        bool perm;

};

#endif // PAINTWIDGET_H

paintwidget.cpp

#include "paintwidget.h"

PaintWidget::PaintWidget(QWidget *parent)

        : QGraphicsScene(parent), currShapeCode(Shape::Line), currItem(NULL), perm(false)

{

}

void PaintWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)

{

        switch(currShapeCode)

        {

        case Shape::Line:

                {

                        Line *line = new Line;

                        currItem = line;

                        addItem(line);

                        break;

                }

        case Shape::Rect:

                {

                        Rect *rect = new Rect;

                        currItem = rect;

                        addItem(rect);

                        break;

                }

        }

        if(currItem) {

                currItem->startDraw(event);

                perm = false;

        }

        QGraphicsScene::mousePressEvent(event);

}

void PaintWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)

{

        if(currItem && !perm) {

                currItem->drawing(event);

        }

        QGraphicsScene::mouseMoveEvent(event);

}

void PaintWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)

{

        perm = true;

        QGraphicsScene::mouseReleaseEvent(event);

}

我们把继承自QWidget改成继承自QGraphicsScene,同样也会有鼠标事件,只不过在这里我们把鼠标事件全部转发给具体的item进行处理。这个我们会在下面的代码中看到。另外一点是,每一个鼠标处理函数都包含了调用其父类函数的语句。

shape.h

#ifndef SHAPE_H

#define SHAPE_H

#include

class Shape

{

public:

        enum Code {

                Line,

                Rect

        };

        Shape();

        virtual void startDraw(QGraphicsSceneMouseEvent * event) = 0;

        virtual void drawing(QGraphicsSceneMouseEvent * event) = 0;

};

#endif // SHAPE_H

shape.cpp

#include "shape.h"

Shape::Shape()

{

}

Shape类也有了变化:还记得我们曾经说过,Qt内置了很多item,因此我们不必全部重写这个item。所以,我们要使用Qt提供的类,就不需要在我们的类里面添加新的数据成员了。这样,我们就有了不带有额外的数据成员的Shape。那么,为什么还要提供Shape呢?因为我们在scene的鼠标事件中需要修改这些数据成员,如果没有这个父类,我们就需要按照Code写一个长长的switch来判断是那一个图形,这样是很麻烦的。所以我们依然创建了一个公共的父类,只要调用这个父类的draw函数即可。

line.h

#ifndef LINE_H

#define LINE_H

#include

#include "shape.h"

class Line : public Shape, public QGraphicsLineItem

{

public:

        Line();

        void startDraw(QGraphicsSceneMouseEvent * event);

        void drawing(QGraphicsSceneMouseEvent * event);

};

#endif // LINE_H

line.cpp

#include "line.h"

Line::Line()

{

}

void Line::startDraw(QGraphicsSceneMouseEvent * event)

{

        setLine(QLineF(event->scenePos(), event->scenePos()));

}

void Line::drawing(QGraphicsSceneMouseEvent * event)

{

        QLineF newLine(line().p1(), event->scenePos());

        setLine(newLine);

}

Line类已经和前面有了变化,我们不仅仅继承了Shape,而且继承了QGraphicsLineItem类。这里我们使用了C++的多继承机制。这个机制是很危险的,很容易发生错误,但是这里我们的Shape并没有继承其他的类,只要函数没有重名,一般而言是没有问题的。如果不希望出现不推荐的多继承(不管怎么说,多继承虽然危险,但它是符合面向对象理论的),那就就想办法使用组合机制。我们之所以使用多继承,目的是让Line类同时具有Shape和QGraphicsLineItem的性质,从而既可以直接添加到QGraphicsScene中,又可以调用startDraw()等函数。

同样的还有Rect这个类:

rect.h

#ifndef RECT_H

#define RECT_H

#include

#include "shape.h"

class Rect : public Shape, public QGraphicsRectItem

{

public:

        Rect();

        void startDraw(QGraphicsSceneMouseEvent * event);

        void drawing(QGraphicsSceneMouseEvent * event);

};

#endif // RECT_H

rect.cpp

#include "rect.h"

Rect::Rect()

{

}

void Rect::startDraw(QGraphicsSceneMouseEvent * event)

{

        setRect(QRectF(event->scenePos(), QSizeF(0, 0)));

}

void Rect::drawing(QGraphicsSceneMouseEvent * event)

{

        QRectF r(rect().topLeft(),

                         QSizeF(event->scenePos().x() - rect().topLeft().x(), event->scenePos().y() - rect().topLeft().y()));

        setRect(r);

}

Line和Rect类的逻辑都比较清楚,和前面的基本类似。所不同的是,Qt并没有使用我们前面定义的两个Qpoint对象记录数据,而是在QGraphicsLineItem中使用QLineF,在QGraphicsRectItem中使用QRectF记录数据。这显然比我们的两个点的数据记录高级得多。其实,我们也完全可以使用这样的数据结构去重定义前面那些Line之类。

这样,我们的程序就修改完毕了。运行一下你会发现,几乎和前面的实现没有区别。这里说“几乎”,是在第一个点画下的时候,scene会移动一段距离。这是因为scene是自动居中的,由于我们把Line的第一个点设置为(0, 0),因此当我们把鼠标移动后会有一个偏移。

看到这里或许并没有显示出Graphics View的优势。不过,建议在Line或者Rect的构造函数里面加上下面的语句,

setFlag(QGraphicsItem::ItemIsMovable, true);

setFlag(QGraphicsItem::ItemIsSelectable, true);

此时,你的Line和Rect就已经支持选中和拖放了!值得试一试哦!不过,需要注意的是,我们重写了scene的鼠标控制函数,所以这里的拖动会很粗糙,甚至说是不正确,你需要动动脑筋重新设计我们的类啦!

qt制作一个画板_Qt学习之路(32): 一个简易画板的实现(Graphics View)-阿里云开发者社区...相关推荐

  1. 一键装机linux_linux系统学习第十八天《搭建一键装机平台》终结篇-阿里云开发者社区...

    在真机上,利用clone-vm7新建一台虚拟机,名字:PXE-Server 1.设置防火墙为trusted 2.当前及永久关闭SELinux 3.配置IP地址:192.168.4.168/24 4.搭 ...

  2. 如何用python开发游戏_手把手教你用Python完成一个控制台小游戏-阿里云开发者社区...

    很多人想学Python程序设计或者已经了解过一点Python程序设计基础,却没办法开发出一个项目. 今天,通过演示一个简单的控制台小游戏制作,手把手教你如何用Python编写一个游戏程序,即便你是个新 ...

  3. python高手之路第三版_《Python高手之路(第3版)》——1.3 版本编号-阿里云开发者社区...

    本节书摘来自异步社区<Python高手之路(第3版)>一书中的第1章,第1.3节,作者[法]Julien Danjou,王飞龙 译,更多章节内容可以访问云栖社区"异步社区&quo ...

  4. abaqus实例手册_《ABAQUS 6.14超级学习手册》——1.6 实例快速入门-阿里云开发者社区...

    本节书摘来自异步社区<ABAQUS 6.14超级学习手册>一书中的第1章,第1.6节,作者: 齐威 更多章节内容可以访问云栖社区"异步社区"公众号查看. 1.6 实例快 ...

  5. php mysql ajax日历记事本_使用PHP和AJAX制作日历-阿里云开发者社区

    在很多网站,日历是一个必要的组成部分.通常这些日历是jquery的一个插件,但是也可以用php来实现.今天我将使用ajax技术展示一个月份的日历,可以通过左右箭头切换月份.由于是ajax,这个日历还有 ...

  6. python读取hdf-eos5数据_《Python和HDF 5大数据应用》——2.4 你的第一个HDF5文件-阿里云开发者社区...

    本节书摘来自异步社区<Python和HDF 5大数据应用>一书中的第2章,第2.4节,作者[美]Andrew Collette(科莱特),胡世杰 译,更多章节内容可以访问云栖社区" ...

  7. qt 控件坐标系_Qt学习之路(28): 坐标变换

    经过前面的章节,我们已经能够画出一些东西来,主要就是使用QPainter的相关函数.今天,我们要看的是QPainter的坐标系统. 同很多坐标系统一样,QPainter的默认坐标的原点(0, 0)位于 ...

  8. python机器人编程与操作_【教程免费下载】机器人系统设计与制作:Python语言实现-问答-阿里云开发者社区-阿里云...

    前言 本书包含12章,主要介绍如何从零开始构建自主移动的机器人,并使用Python进行编程.本书所提到的机器人是用于家庭.宾馆.餐厅的服务机器人,我们将按照顺序介绍如何一步一步构建它.书中从机器人的基 ...

  9. 怎么用python实现回归_手把手教你用Python进行回归(附代码、学习资料)-阿里云开发者社区...

    我刚开始学习数据科学时,第一个接触到的算法就是线性回归.在把这个方法算法应用在到各种各样的数据集的过程中,我总结出了一些它的优点和不足. 首先,线性回归假设自变量和因变量之间存在线性关系,但实际情况却 ...

最新文章

  1. 【sgTopo】强哥古法炮制、纯手工打造简单拓扑图、流程图、思维导图组件(完善中ing)
  2. 博问问题内容页面的前端优化
  3. java 对excel操作 读取、写入、修改数据;导出数据库数据到excel
  4. MV* 框架 与 DOM操作为主 JS库 的案例对比
  5. 本地生活服务 巨头们玩不转的电商蓝海
  6. windows设置右键sublime Text3
  7. 一个层动态放大的例子的一些知识点
  8. 第十篇: Timer 控件
  9. 11-Flutter移动电商实战-首页_屏幕适配方案和制作
  10. 浅谈分布式存储中的网络通信
  11. Golang指针,for循环
  12. myeclipse2017安装与破解
  13. LeetCode-20:有效的括号
  14. c语言 string.h部分常用函数的实现
  15. python如何爬虫股票数据_自学python之爬虫3股票数据爬虫
  16. 拓端tecdat|R语言可视化探索BRFSS数据并逻辑回归Logistic回归预测中风
  17. node 压缩图片_免费的图片最佳化工具,支持JPG、PNG等格式的无损压缩
  18. 时间序列数据的存储和计算 - 概述
  19. Typora、Markdown 字体样式
  20. Linux CentOS安装增强功能--完整版命令

热门文章

  1. 建立属于自己的腾讯云服务器
  2. PHP count() empty() isset() 的应用
  3. Nuxt 动态路由传参
  4. 建筑建模学习笔记4——室内放置物品/设置灯光/设置材质/渲染出图
  5. QCustomPlot设置游标
  6. CAD使qt打开DWG文件
  7. python 操作 千牛
  8. mysqlcheck约束,含面试题+答案
  9. excel部分网格线不见了_精品图表 | Excel绘制y轴带有阈值分割的柱形图和棒棒图...
  10. 四个人分52张扑克牌,同时拿到红桃A和黑桃A的概率?