Qt进修之路(32):一个浅易画板的实现(Graphics View)
当前位置:以往代写 > C/C++ 教程 >Qt进修之路(32):一个浅易画板的实现(Graphics View)
2019-06-13

Qt进修之路(32):一个浅易画板的实现(Graphics View)

Qt进修之路(32):一个浅易画板的实现(Graphics View)

副标题#e#

本文配套源码

这一次将先容如何利用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内里。


#p#副标题#e#

我们把PaintWidget当做一个scene,因此PaintWidget此刻是担任QGraphicsScene,而不是前面的QWidget。

paintwidget.h

#ifndef PAINTWIDGET_H
#define PAINTWIDGET_H
#include <QtGui>
#include <QDebug>
#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);
}

#p#副标题#e#

#p#分页标题#e#

我们把担任自QWidget改成担任自QGraphicsScene,同样也会有鼠标事件,只不外在这里我们把鼠标事件全部转发给详细的item举办处理惩罚。这个我们会在下面的代码中看到。别的一点是,每一个鼠标处理惩罚函数都包括了挪用其父类函数的语句。

shape.h
#ifndef SHAPE_H
#define SHAPE_H
#include <QtGui>
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 <QGraphicsLineItem>
#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);
}

#p#副标题#e#

Line类已经和前面有了变革,我们不只仅担任了Shape,并且担任了QGraphicsLineItem类。这里我们利用了C++的多担任机制。这个机制是很危险的,很容易产生错误,可是这里我们的Shape并没有担任其他的类,只要函数没有重名,一般而言是没有问题的。假如不但愿呈现不推荐的多担任(不管怎么说,多担任固然危险,但它是切合面向工具理论的),那就就想步伐利用组合机制。我们之所以利用多担任,目标是让Line类同时具有 Shape和QGraphicsLineItem的性质,从而既可以直接添加到QGraphicsScene中,又可以挪用startDraw()等函数。

同样的尚有Rect这个类:

rect.h
#ifndef RECT_H
#define RECT_H
#include <QGraphicsRectItem>
#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);

#p#分页标题#e#

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

出处:http://devbean.blog.51cto.com/448512/244181

    关键字:

在线提交作业