用Qt框架搭建一個簡易畫板
需求
- 繪制線、橢圓、矩形框、任意凹/凸多邊形、曲線
 - 刪除最近的圖形實例
 
思路
- 用list保存繪制的圖形實例,便於刪除
 - 對於line、rectangle、ellipse 只要保存初始位置和結束位置
 - 對於任意凸/凹多邊形可以用一個list保存點集
 - 利用Graphics View 管理圖形對象
 - Graphics View 是M-V框架,model指的是各種圖形對象,view指的是視角
 - 使用過程是:創建一個scene,創建line和rectangle等圖形實例,再使用scene的add函數將line、rectangle實例等添加到scene中,最后通過視口view就可以看到了;同一個model可以從不同的view進行觀察
 - 創建圖形對象繼承於父類Shape,繪制完畢后將圖形對象存儲在List中,點擊刪除按鈕時,從List中出棧一個圖形對象,重繪
 - 界面樣式 
            

 - 按鈕描述(從左至右) 
直線按鈕:點擊進入繪制直線模式:按下左鍵設置起始點,拖動鼠標至結束點,釋放左鍵繪制完畢
矩形按鈕:點擊進入繪制矩形模式:按下左鍵設置左上角,拖動鼠標至結束點,釋放左鍵繪制完畢
橢圓按鈕:點擊進入繪制橢圓模式:同矩形按鈕
多邊形按鈕:點擊進入繪制多邊形模式:單擊左鍵設置起始點,拖動鼠標至下一個轉折點,繼續單擊鼠標添加轉折點,直到單擊右鍵結束繪制
曲線按鈕:點擊進入繪制曲線模式:左鍵按下並拖動鼠標繪制
刪除按鈕:點擊進入刪除模式:逆序刪除繪制的圖形對象 
問題解決Q&A
1.問題: 在Qt中,添加類之后經常出現“無法解析的外部命令的問題”
解決方法:http://blog.csdn.net/yz960611/article/details/50735406
http://blog.csdn.net/zhoxier/article/details/8619688Tuitor
1.創建主界面
這一步主要是mainWindow.cpp、mainWindow.h、mainWindow.ui三個文件- 新建Qt Widegts Application,項目名是miniDraw,基類是QMainWindow,類名是MainWindow,頭文件mainWindow.h,源文件mainWindow.cpp,勾選創建界面。 

 - 在mainWindow.h中添加頭文件 
#include<vector>
#include<shape.h>//后面會講到 - 添加成員變量 
```cpp
private:
Shape::drawMode mode;//保存目前的繪圖模式,分為七中,在shape.h中定義
Shape *shape;//五種不同的圖形對象
bool isDone=false;//是否繪制完畢
QListshapeList;//保存圖形對象 
QVectorpolygon_point_array;//保存正在繪制的多邊形的點 
QVectorfreehand_point_array;//保存正在繪制的曲線的點 
QPoint freePoint;//鼠標當前位置 
- 新建Qt Widegts Application,項目名是miniDraw,基類是QMainWindow,類名是MainWindow,頭文件mainWindow.h,源文件mainWindow.cpp,勾選創建界面。 
 
 
 
 
         
  
  
  
           * 添加成員函數 
  
  
  
          ```cpp 
  
  
  
          protected: 
  
  
  
           void paintEvent(QPaintEvent *event);//繪圖事件,用update()調用 
  
  
  
           void mousePressEvent(QMouseEvent *event);//鼠標按下 
  
  
  
           void mouseMoveEvent(QMouseEvent *event);//鼠標移動 
  
  
  
           void mouseReleaseEvent(QMouseEvent *event);//鼠標釋放 
 
 
 
         
 
        - 編寫界面 
仿照主界面示意圖添加組件,具體使用方法查看Qt Designer,使用設計模式創建界面
 
(基本步驟:1、創建actionLine;2、編輯圖標;3、添加statusTip;4、轉到槽函數;)
2、創建各種圖形對象的父類——Shape類
(1、右鍵單擊工程;2、添加新文件;2、C++ Class;3、Define Class)
 
```cpp
//shape.h 
ifndef SHAPE_H
define SHAPE_H
include 
         
 
        include 
         
 
        class Shape 
 { 
 public: 
 enum drawMode//繪圖模式 
 { 
 LINE, 
 RECT, 
 ELLIPSE, 
 POLYGON, 
 FREEHAND, 
 DELETE, 
 DEFAULT 
 }; 
 Shape(); 
 virtual ~Shape();//虛函數,防止內存泄漏,具體見后面 
 void setStart(QPoint start) 
 { 
 this->start=start; 
 } 
 void setEnd(QPoint end) 
 { 
 this->end=end; 
 } 
 QPoint getStart() 
 { 
 return start; 
 } 
 QPoint getEnd() 
 { 
 return end; 
 } 
 void virtual paint(QPainter &painter)=0;//需要在子類中重寫的函數設置為虛函數 
 protected: 
 QPoint start,end; 
 };
endif // SHAPE_H
 
 
 
         
  
  
  
           ```cpp 
  
  
  
           //shape.cpp 
  
  
  
          #include "shape.h" 
  
  
  
          Shape::Shape() 
  
  
  
          { 
  
  
  
          } 
 
 
 
         
 
        2.1創建子類line、Rectangle、Ellipse、Polygon、FreeHand——主要是重寫paint函數
- line 
```cpp
void line::paint(QPainter &painter)
{
painter.drawLine(start,end);
} 
 
 
 
         
  
  
  
           * **rectangle** 
  
  
  
          ```cpp 
  
  
  
          void Rectangle::paint(QPainter &painter) 
  
  
  
          { 
  
  
  
           painter.drawRect(start.x(),start.y(),end.x()-start.x(),end.y()-start.y()); 
  
  
  
          } 
 
 
 
         
 
        - ellipse
 
 
 
 
         
  
  
  
          void Ellipse::paint(QPainter &painter) 
  
  
  
          { 
  
  
  
           painter.drawEllipse(start.x(),start.y(),end.x()-start.x(),end.y()-start.y()); 
  
  
  
          } 
 
 
 
         
 
        以上這些都很簡單
- freehand 
```cpp
//freehand.h 
ifndef FREEHAND_H
define FREEHAND_H
include"shape.h"
include 
         
 
        class FreeHand:public Shape 
 { 
 public: 
 FreeHand(); 
 FreeHand(const QVector 
         
 void paint(QPainter &painter); 
          
 protected: 
          
 QVector 
          
 }; 
          
endif // FREEHAND_H
 
 
 
         
  
  
  
          ```cpp 
  
  
  
          //構造函數和繪制函數的實現 
  
  
  
          FreeHand::FreeHand(const QVector<QPoint> &point_array) 
  
  
  
          { 
  
  
  
           for(size_t i=0;i<point_array.size();i++) 
  
  
  
           { 
  
  
  
           //free_point_array[i]=point_array[i]; 
  
  
  
           free_point_array.push_back(point_array[i]); 
  
  
  
           } 
  
  
  
          } 
  
  
  
          void FreeHand::paint(QPainter &painter) 
  
  
  
          { 
  
  
  
           for(size_t i=0;i<free_point_array.size()-1;i++) 
  
  
  
           { 
  
  
  
           painter.drawLine(free_point_array[i],free_point_array[i+1]); 
  
  
  
           } 
  
  
  
          } 
 
 
 
         
 
         
         
        - polygon 
```cpp
//構造函數和繪圖函數的實現
Polygon::Polygon(const QVector&points) 
{
for(size_t i=0;i<points.size();i++)
{
pointList.push_back(points[i]);
}
}
void Polygon::paint(QPainter &painter)
{
for(size_t i=0;i<pointList.size()-1;i++)
{
painter.drawLine(pointList[i],pointList[i+1]);
}
painter.drawLine(pointList.back(),pointList.front());
} 
 
 
 
         
  
  
  
          **以上兩個是比較難的,需要考慮一下** 
  
  
  
           **3整體邏輯——對成員函數的實現** 
  
  
  
           **3.1給按鈕添加槽函數** 
  
  
  
           (這里我是采用Qt Creator 添加的,這不是我們講的重點) 
  
  
  
           
  
  
  
           
  
  
  
          ```cpp 
  
  
  
          void MainWindow::on_actionLine_triggered() 
  
  
  
          { 
  
  
  
           mode=Shape::drawMode::LINE; 
  
  
  
          } 
  
  
  
          void MainWindow::on_actionRectangle_triggered() 
  
  
  
          { 
  
  
  
           mode=Shape::drawMode::RECT; 
  
  
  
          } 
  
  
  
          void MainWindow::on_actionEllipse_triggered() 
  
  
  
          { 
  
  
  
           mode=Shape::drawMode::ELLIPSE; 
  
  
  
          } 
  
  
  
          void MainWindow::on_actionPolygon_triggered() 
  
  
  
          { 
  
  
  
           mode=Shape::drawMode::POLYGON; 
  
  
  
          } 
  
  
  
          void MainWindow::on_actionFreehand_triggered() 
  
  
  
          { 
  
  
  
           mode=Shape::drawMode::FREEHAND; 
  
  
  
          } 
  
  
  
          void MainWindow::on_actionDelete_triggered() 
  
  
  
          { 
  
  
  
           mode=Shape::drawMode::DELETE; 
  
  
  
           if(!shapeList.isEmpty()) 
  
  
  
           { 
  
  
  
           shapeList.pop_back();//最后一個圖形對象出棧 
  
  
  
           mode=Shape::DEFAULT;//設置模式為默認模式 
  
  
  
           update(); 
  
  
  
           } 
  
  
  
          } 
 
 
 
         
 
        邏輯就是點擊不同的按鈕進入不同的繪制模式
3.2給鼠標按鍵添加監聽- 繪制直線:按下鼠標左鍵拖動鼠標,直到目的位置釋放鼠標;
 - 繪制矩形:同繪制直線;
 - 繪制橢圓:同繪制矩形
 - 繪制多邊形:點擊鼠標左鍵設置第一個錨點,再次單擊設置第二個錨點,以此類推,設置完最后一個錨點后點擊鼠標右鍵結束繪制;
 - 繪制曲線:按住左鍵不放繪制曲線,釋放左鍵結束繪制
 - 從功能需求我們可以看出,繪制直線、矩形、橢圓實際上只需要兩個點就可以確定,那么就可以按按下鼠標的位置作為起始位置,釋放鼠標的位置作為結束位置
 - 繪制多邊形和繪制曲線很相似,就是往vector里面添加一系列點
 - 因此,在繪制直線、橢圓、矩形的時候,可以在按下左鍵的時候就將圖形對象保存進shapeList
 - 在繪制曲線、多邊形時可以在主函數里面暫存這一系列點,等繪制完畢再把圖形對象保存進shapeList,這樣可以提高繪制的效率
 
```cpp
//鼠標按下
void MainWindow::mousePressEvent(QMouseEvent *event)
{
switch (event->button()) {
case Qt::LeftButton ://在繪制直線、矩形、多邊形、橢圓、曲線時有效,開始繪制
switch (mode) {
case Shape::DEFAULT:
break;
case Shape::DELETE:
//do nothing
break;
case Shape::LINE:
shape=new line;
break;
case Shape::RECT:
shape=new Rectangle;
break;
case Shape::ELLIPSE:
shape=new Ellipse;
break;
case Shape::FREEHAND:
shape=new FreeHand;
break;
case Shape::POLYGON:
shape=new Polygon;
break;
default:
break;
}
break;
case Qt::RightButton: //僅在繪制多邊形時有效,結束繪制
if(mode==Shape::POLYGON)
{
isDone=true;
if(polygon_point_array.size()>0)
{
shape=new Polygon(polygon_point_array);
shapeList.push_back(shape);
polygon_point_array.clear();
update();
shape=NULL;
}
}
break;
default:
break;
}
if(shape!=NULL){//剛開始繪制
if(mode==Shape::POLYGON)//設置轉折點
{
isDone=false;
polygon_point_array.push_back(event->pos());//添加進錨點
freePoint=event->pos();
update();
}else if(mode==Shape::FREEHAND)
{
isDone=false;
freehand_point_array.push_back(event->pos());//添加進錨點
}
else {
isDone=false;
shape->setStart(event->pos());
shape->setEnd(event->pos());
shapeList.push_back(shape);//將圖形對象入棧
}
}
}
 
 
 
         
  
  
  
          ```cpp 
  
  
  
          //鼠標移動 
  
  
  
          void MainWindow::mouseMoveEvent(QMouseEvent *event) 
  
  
  
          { 
  
  
  
           if(shape&&!isDone) 
  
  
  
           { 
  
  
  
           if(mode==Shape::FREEHAND) 
  
  
  
           { 
  
  
  
           freehand_point_array.push_back(event->pos());//添加曲線點序列 
  
  
  
           update(); 
  
  
  
           }else 
  
  
  
           { 
  
  
  
           shape->setEnd(event->pos()); 
  
  
  
           freePoint=event->pos(); 
  
  
  
           update(); 
  
  
  
           } 
  
  
  
           } 
  
  
  
          } 
 
 
 
         
 
        
 
 
 
         
  
  
  
          //鼠標釋放 
  
  
  
          void MainWindow::mouseReleaseEvent(QMouseEvent *event) 
  
  
  
          { 
  
  
  
           if(event->button()==Qt::LeftButton)//僅在左鍵釋放時有效,結束繪制直線、矩形、橢圓、曲線 
  
  
  
           { 
  
  
  
           switch (mode) { 
  
  
  
           case Shape::POLYGON: 
  
  
  
           break; 
  
  
  
           case Shape::FREEHAND: 
  
  
  
           isDone=true; 
  
  
  
           shape=new FreeHand(freehand_point_array); 
  
  
  
           freehand_point_array.clear(); 
  
  
  
           shapeList.push_back(shape); 
  
  
  
           update(); 
  
  
  
           shape=NULL; 
  
  
  
           break; 
  
  
  
           default: 
  
  
  
           isDone=true; 
  
  
  
           update(); 
  
  
  
           shape=NULL; 
  
  
  
           break; 
  
  
  
           } 
  
  
  
           } 
  
  
  
          } 
 
 
 
         
 
        以上
