說(shuō)實(shí)話,本來(lái)我是沒(méi)有打算放一個(gè)很大的例子的,一則比較復(fù)雜,二來(lái)或許需要很多次才能說(shuō)得完。不過(guò),現(xiàn)在已經(jīng)說(shuō)完了繪圖部分,所以計(jì)劃還是上一個(gè)這樣的例子。這里我會(huì)只做出一個(gè)簡(jiǎn)單的畫板程序,大體上就是能夠畫直線和矩形吧。這樣,我計(jì)劃分成兩種實(shí)現(xiàn),一是使用普通的 QWidget 作為畫板,第二則是使用 Graphcis View Framework 來(lái)實(shí)現(xiàn)。因?yàn)榍懊嬗信笥颜f(shuō)不大明白 Graphics View 的相關(guān)內(nèi)容,所以計(jì)劃如此。
好了,現(xiàn)在先來(lái)看看我們的主體框架。我們的框架還是使用 Qt Creator 創(chuàng)建一個(gè) Gui Application工程。
簡(jiǎn)單的 main()函數(shù)就不再贅述了,這里首先來(lái)看 MainWindow。順便說(shuō)一下,我一般不會(huì)使用 ui 文件,所以這些內(nèi)容都是手寫的。首先先來(lái)看看最終的運(yùn)行結(jié)果:
或許很簡(jiǎn)單,但是至少我們能夠把前面所說(shuō)的各種知識(shí)串連起來(lái),這也就達(dá)到目的了。
現(xiàn)在先來(lái)看看 MainWindow 的代碼:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui>
#include "shape.h"
#include "paintwidget.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
signals:
void changeCurrentShape(Shape::Code newShape);
private slots:
void drawLineActionTriggered();
void drawRectActionTriggered();
};
#endif // MAINWINDOW_H
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);
setCentralWidget(paintWidget);
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);
}
應(yīng)該說(shuō),從以往的學(xué)習(xí)中可以看出,這里的代碼沒(méi)有什么奇怪的了。我們?cè)?MainWindow 類里面聲明了一個(gè)信號(hào),changeCurrentShape(Shape::Code),用于按鈕按下后通知畫圖板。注意,QActio 的triggered()信號(hào)是沒(méi)有參數(shù)的,因此,我們需要在 QAction 的槽函數(shù)中重新 emit 我們自己定義的信號(hào)。構(gòu)造函數(shù)里面創(chuàng)建了兩個(gè) QAction,一個(gè)是 drawLineAction,一個(gè)是 drawRectAction,分別用于繪制直線和矩形。MainWindow 的中心組件是 PainWidget,也就是我們的畫圖板。下面來(lái)看看PaintWidget 類:
paintwidget.h
#ifndef PAINTWIDGET_H
#define PAINTWIDGET_H
#include <QtGui>
#include <QDebug>
#include "shape.h"
#include "line.h"
#include "rect.h"
class PaintWidget : public QWidget
{
Q_OBJECT
public:
PaintWidget(QWidget *parent = 0);
public slots:
void setCurrentShape(Shape::Code s)
{
if(s != currShapeCode) {
currShapeCode = s;
}
}
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
Shape::Code currShapeCode;
Shape *shape;
bool perm;
QList<Shape*> shapeList;
};
#endif // PAINTWIDGET_H
paintwidget.cpp
#include "paintwidget.h"
PaintWidget::PaintWidget(QWidget *parent)
: QWidget(parent), currShapeCode(Shape::Line), shape(NULL), perm(false)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}
void PaintWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setBrush(Qt::white);
painter.drawRect(0, 0, size().width(), size().height());
foreach(Shape * shape, shapeList) {
shape->paint(painter);
}
if(shape) {
shape->paint(painter);
}
}
void PaintWidget::mousePressEvent(QMouseEvent *event)
{
switch(currShapeCode)
{
case Shape::Line:
{
shape = new Line;
break;
}
case Shape::Rect:
{
shape = new Rect;
break;
}
}
if(shape != NULL) {
perm = false;
shapeList<<shape;
shape->setStart(event->pos());
shape->setEnd(event->pos());
}
}
void PaintWidget::mouseMoveEvent(QMouseEvent *event)
{
if(shape && !perm) {
shape->setEnd(event->pos());
update();
}
}
void PaintWidget::mouseReleaseEvent(QMouseEvent *event)
{
perm = true;
}
PaintWidget 類定義了一個(gè) slot,用于接收改變后的新的 ShapeCode。最主要的是,PaintWidget重定義了三個(gè)關(guān)于鼠標(biāo)的事件:mousePressEvent,mouseMoveEvent和mouseReleaseEvent。
我們來(lái)想象一下如何繪制一個(gè)圖形:圖形的繪制與鼠標(biāo)操作息息相關(guān)。以畫直線為例,首先我們需要按下鼠標(biāo),確定直線的第一個(gè)點(diǎn),所以在 mousePressEvent 里面,我們讓 shape 保存下 start 點(diǎn)。然后在鼠標(biāo)按下的狀態(tài)下移動(dòng)鼠標(biāo),此時(shí),直線就會(huì)發(fā)生變化,實(shí)際上是直線的終止點(diǎn)在隨著鼠標(biāo)移動(dòng),所以在 mouseMoveEvent 中我們讓 shape 保存下 end 點(diǎn),然后調(diào)用 update()函數(shù),這個(gè)函數(shù)會(huì)自動(dòng)調(diào)用 paintEvent()函數(shù),顯示出我們繪制的內(nèi)容。最后,當(dāng)鼠標(biāo)松開時(shí),圖形繪制完畢,我們將一個(gè)標(biāo)志位置為 true,此時(shí)說(shuō)明這個(gè)圖形繪制完畢。
為了保存我們?cè)?jīng)畫下的圖形,我們使用了一個(gè) List。每次按下鼠標(biāo)時(shí),都會(huì)把圖形存入這個(gè) List??梢钥吹?,我們?cè)?paintEvent()函數(shù)中使用了 foreach 遍歷了這個(gè) List,繪制出歷史圖形。foreach 是 Qt 提供的一個(gè)宏,用于遍歷集合中的元素。
最后我們來(lái)看看 Shape 類。
shape.h
#ifndef SHAPE_H
#define SHAPE_H
#include <QtGui>
class Shape
{
public:
enum Code {
Line,
Rect
};
Shape();
void setStart(QPoint s)
{
start = s;
}
void setEnd(QPoint e)
{
end = e;
}
QPoint startPoint()
{
return start;
}
QPoint endPoint()
{
return end;
}
void virtual paint(QPainter & painter) = 0;
protected:
QPoint start;
QPoint end;
};
#endif // SHAPE_H
shape.cpp
#include "shape.h"
Shape::Shape()
{
}
Shape 類最重要的就是保存了 start 和 end 兩個(gè)點(diǎn)。為什么只要這兩個(gè)點(diǎn)呢?因?yàn)槲覀円L制的是直線和矩形。對(duì)于直線來(lái)說(shuō),有了兩個(gè)點(diǎn)就可以確定這條直線,對(duì)于矩形來(lái)說(shuō),有了兩個(gè)點(diǎn)作為左上角的點(diǎn)和右下角的點(diǎn)也可以確定這個(gè)矩形,因此我們只要保存兩個(gè)點(diǎn),就足夠保存這兩種圖形的位置和大小的信息。paint()函數(shù)是 Shape 類的一個(gè)純虛函數(shù),子類都必須實(shí)現(xiàn)這個(gè)函數(shù)。我們現(xiàn)在有兩個(gè)子類:Line和 Rect,分別定義如下:
line.h
#ifndef LINE_H
#define LINE_H
#include "shape.h"
class Line : public Shape
{
public:
Line();
void paint(QPainter &painter);
};
#endif // LINE_H
line.cpp
#include "line.h"
Line::Line()
{
}
void Line::paint(QPainter &painter)
{
painter.drawLine(start, end);
}
rect.h
#ifndef RECT_H
#define RECT_H
#include "shape.h"
class Rect : public Shape
{
public:
Rect();
void paint(QPainter &painter);
};
#endif // RECT_H
rect.cpp
#include "rect.h"
Rect::Rect()
{
}
void Rect::paint(QPainter &painter)
{
painter.drawRect(start.x(), start.y(),
end.x() - start.x(), end.y() - start.y());
}
使用 paint()函數(shù),根據(jù)兩個(gè)點(diǎn)的數(shù)據(jù),Line 和 Rect 都可以繪制出它們自身來(lái)。此時(shí)就可以看出,我們之所以要建立一個(gè) Shape 作為父類,因?yàn)檫@兩個(gè)類有幾乎完全相似的數(shù)據(jù)對(duì)象,并且從語(yǔ)義上來(lái)說(shuō),Line、Rect 與 Shape 也完全是一個(gè) is-a 的關(guān)系。如果你想要添加顏色等的信息,完全可以在Shape 類進(jìn)行記錄。這也就是類層次結(jié)構(gòu)的好處。
代碼很多也會(huì)比較亂,附件里面是整個(gè)工程的文件,有興趣的朋友不妨看看哦!本文出自 “豆子空間” 博客,請(qǐng)務(wù)必保留此出處 http://devbean.blog.51cto.com/448512/193918
更多建議: