用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;
}
}
}
以上