鍍金池/ 教程/ C/ 一個簡易畫板的實現(xiàn)(QWidget)
Qt 容器和算法拾遺
自定義 model 之一
反走樣
Hello, world!
Qt 容器類之關聯(lián)存儲容器
QStringListModel
拖放技術之一
狀態(tài)欄
QTreeWidget
拖放技術之二
通用算法
event()
Qt 學習之路(18): Qt 標準對話框之 QInputDialog
Qt 容器類之遍歷器和隱式數(shù)據(jù)共享
QListWidget
Meta-Object 系統(tǒng)
事件接收與忽略
Qt 學習之路(tip): parent 參數(shù)
Qt 標準對話框之 QColorDialog
QPainter(續(xù))
國際化(下)
漸變填充
自定義委托
創(chuàng)建 shared library
model-view 架構
Graphics View Framework
自定義拖放數(shù)據(jù)對象
QSortFilterProxyModel
國際化(上)
組件布局
自定義 Model 之三
事件過濾器
QDirModel
Hello, world!(續(xù))
Qt 標準對話框之 QFileDialog
自定義 model 之二
深入了解信號槽
坐標變換
剪貼板操作
QTableWidget
QByteArray 和 QVariant
創(chuàng)建一個對話框(下)
Qt 學習之路(32): 一個簡易畫板的實現(xiàn)(Graphics View)
文本文件讀寫
自定義事件
編寫跨平臺的程序
MainWindow
初探信號槽
Qt 學習之路(17): Qt 標準對話框之 QMessageBox
繪圖設備
菜單和工具條(續(xù))
二進制文件讀寫
QString
事件(event)
菜單和工具條
QPainter
Qt 容器類之順序存儲容器
進程間交互
API 文檔的使用
創(chuàng)建一個對話框(上)
一個簡易畫板的實現(xiàn)(QWidget)

一個簡易畫板的實現(xiàn)(QWidget)

說實話,本來我是沒有打算放一個很大的例子的,一則比較復雜,二來或許需要很多次才能說得完。不過,現(xiàn)在已經(jīng)說完了繪圖部分,所以計劃還是上一個這樣的例子。這里我會只做出一個簡單的畫板程序,大體上就是能夠畫直線和矩形吧。這樣,我計劃分成兩種實現(xiàn),一是使用普通的 QWidget 作為畫板,第二則是使用 Graphcis View Framework 來實現(xiàn)。因為前面有朋友說不大明白 Graphics View 的相關內容,所以計劃如此。

好了,現(xiàn)在先來看看我們的主體框架。我們的框架還是使用 Qt Creator 創(chuàng)建一個 Gui Application工程。

簡單的 main()函數(shù)就不再贅述了,這里首先來看 MainWindow。順便說一下,我一般不會使用 ui 文件,所以這些內容都是手寫的。首先先來看看最終的運行結果:

http://wiki.jikexueyuan.com/project/learn-road-qt/images/54.png" alt="" />

或許很簡單,但是至少我們能夠把前面所說的各種知識串連起來,這也就達到目的了。

現(xiàn)在先來看看 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); 
}

應該說,從以往的學習中可以看出,這里的代碼沒有什么奇怪的了。我們在 MainWindow 類里面聲明了一個信號,changeCurrentShape(Shape::Code),用于按鈕按下后通知畫圖板。注意,QActio 的triggered()信號是沒有參數(shù)的,因此,我們需要在 QAction 的槽函數(shù)中重新 emit 我們自己定義的信號。構造函數(shù)里面創(chuàng)建了兩個 QAction,一個是 drawLineAction,一個是 drawRectAction,分別用于繪制直線和矩形。MainWindow 的中心組件是 PainWidget,也就是我們的畫圖板。下面來看看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 類定義了一個 slot,用于接收改變后的新的 ShapeCode。最主要的是,PaintWidget重定義了三個關于鼠標的事件:mousePressEvent,mouseMoveEvent和mouseReleaseEvent。

我們來想象一下如何繪制一個圖形:圖形的繪制與鼠標操作息息相關。以畫直線為例,首先我們需要按下鼠標,確定直線的第一個點,所以在 mousePressEvent 里面,我們讓 shape 保存下 start 點。然后在鼠標按下的狀態(tài)下移動鼠標,此時,直線就會發(fā)生變化,實際上是直線的終止點在隨著鼠標移動,所以在 mouseMoveEvent 中我們讓 shape 保存下 end 點,然后調用 update()函數(shù),這個函數(shù)會自動調用 paintEvent()函數(shù),顯示出我們繪制的內容。最后,當鼠標松開時,圖形繪制完畢,我們將一個標志位置為 true,此時說明這個圖形繪制完畢。

為了保存我們曾經(jīng)畫下的圖形,我們使用了一個 List。每次按下鼠標時,都會把圖形存入這個 List??梢钥吹?,我們在 paintEvent()函數(shù)中使用了 foreach 遍歷了這個 List,繪制出歷史圖形。foreach 是 Qt 提供的一個宏,用于遍歷集合中的元素。

最后我們來看看 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 兩個點。為什么只要這兩個點呢?因為我們要繪制的是直線和矩形。對于直線來說,有了兩個點就可以確定這條直線,對于矩形來說,有了兩個點作為左上角的點和右下角的點也可以確定這個矩形,因此我們只要保存兩個點,就足夠保存這兩種圖形的位置和大小的信息。paint()函數(shù)是 Shape 類的一個純虛函數(shù),子類都必須實現(xiàn)這個函數(shù)。我們現(xiàn)在有兩個子類: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ù)兩個點的數(shù)據(jù),Line 和 Rect 都可以繪制出它們自身來。此時就可以看出,我們之所以要建立一個 Shape 作為父類,因為這兩個類有幾乎完全相似的數(shù)據(jù)對象,并且從語義上來說,Line、Rect 與 Shape 也完全是一個 is-a 的關系。如果你想要添加顏色等的信息,完全可以在Shape 類進行記錄。這也就是類層次結構的好處。

代碼很多也會比較亂,附件里面是整個工程的文件,有興趣的朋友不妨看看哦! 本文出自 “豆子空間” 博客,請務必保留此出處 http://devbean.blog.51cto.com/448512/193918

上一篇:漸變填充下一篇:Graphics View Framework