Qt 簡易畫板


用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/8619688
  • Tuitor

    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;//是否繪制完畢
      QList shapeList;//保存圖形對象
      QVector polygon_point_array;//保存正在繪制的多邊形的點
      QVector freehand_point_array;//保存正在繪制的曲線的點
      QPoint freePoint;//鼠標當前位置
 
 
 
         
  1. * 添加成員函數
  2. ```cpp
  3. protected:
  4. void paintEvent(QPaintEvent *event);//繪圖事件,用update()調用
  5. void mousePressEvent(QMouseEvent *event);//鼠標按下
  6. void mouseMoveEvent(QMouseEvent *event);//鼠標移動
  7. 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

 
 
 
         
  1. ```cpp
  2. //shape.cpp
  3. #include "shape.h"
  4. Shape::Shape()
  5. {
  6. }

2.1創建子類line、Rectangle、Ellipse、Polygon、FreeHand——主要是重寫paint函數

  • line
    ```cpp
    void line::paint(QPainter &painter)
    {
    painter.drawLine(start,end);
    }
 
 
 
         
  1. * **rectangle**
  2. ```cpp
  3. void Rectangle::paint(QPainter &painter)
  4. {
  5. painter.drawRect(start.x(),start.y(),end.x()-start.x(),end.y()-start.y());
  6. }
  • ellipse
 
 
 
         
  1. void Ellipse::paint(QPainter &painter)
  2. {
  3. painter.drawEllipse(start.x(),start.y(),end.x()-start.x(),end.y()-start.y());
  4. }

以上這些都很簡單

  • freehand
    ```cpp
    //freehand.h

ifndef FREEHAND_H

define FREEHAND_H

include"shape.h"

include

class FreeHand:public Shape
{
public:
FreeHand();
FreeHand(const QVector &point_array);//利用Vector構造
void paint(QPainter &painter);
protected:
QVector free_point_array;//保存曲線上的一系列點
};

endif // FREEHAND_H

 
 
 
         
  1. ```cpp
  2. //構造函數和繪制函數的實現
  3. FreeHand::FreeHand(const QVector<QPoint> &point_array)
  4. {
  5. for(size_t i=0;i<point_array.size();i++)
  6. {
  7. //free_point_array[i]=point_array[i];
  8. free_point_array.push_back(point_array[i]);
  9. }
  10. }
  11. void FreeHand::paint(QPainter &painter)
  12. {
  13. for(size_t i=0;i<free_point_array.size()-1;i++)
  14. {
  15. painter.drawLine(free_point_array[i],free_point_array[i+1]);
  16. }
  17. }
  • 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());
    }
 
 
 
         
  1. **以上兩個是比較難的,需要考慮一下**
  2. **3整體邏輯——對成員函數的實現**
  3. **3.1給按鈕添加槽函數**
  4. (這里我是采用Qt Creator 添加的,這不是我們講的重點)
  5. ```cpp
  6. void MainWindow::on_actionLine_triggered()
  7. {
  8. mode=Shape::drawMode::LINE;
  9. }
  10. void MainWindow::on_actionRectangle_triggered()
  11. {
  12. mode=Shape::drawMode::RECT;
  13. }
  14. void MainWindow::on_actionEllipse_triggered()
  15. {
  16. mode=Shape::drawMode::ELLIPSE;
  17. }
  18. void MainWindow::on_actionPolygon_triggered()
  19. {
  20. mode=Shape::drawMode::POLYGON;
  21. }
  22. void MainWindow::on_actionFreehand_triggered()
  23. {
  24. mode=Shape::drawMode::FREEHAND;
  25. }
  26. void MainWindow::on_actionDelete_triggered()
  27. {
  28. mode=Shape::drawMode::DELETE;
  29. if(!shapeList.isEmpty())
  30. {
  31. shapeList.pop_back();//最后一個圖形對象出棧
  32. mode=Shape::DEFAULT;//設置模式為默認模式
  33. update();
  34. }
  35. }
  • 邏輯就是點擊不同的按鈕進入不同的繪制模式
    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);//將圖形對象入棧
    }
    }
    }

 
 
 
         
  1. ```cpp
  2. //鼠標移動
  3. void MainWindow::mouseMoveEvent(QMouseEvent *event)
  4. {
  5. if(shape&&!isDone)
  6. {
  7. if(mode==Shape::FREEHAND)
  8. {
  9. freehand_point_array.push_back(event->pos());//添加曲線點序列
  10. update();
  11. }else
  12. {
  13. shape->setEnd(event->pos());
  14. freePoint=event->pos();
  15. update();
  16. }
  17. }
  18. }
 
 
 
         
  1. //鼠標釋放
  2. void MainWindow::mouseReleaseEvent(QMouseEvent *event)
  3. {
  4. if(event->button()==Qt::LeftButton)//僅在左鍵釋放時有效,結束繪制直線、矩形、橢圓、曲線
  5. {
  6. switch (mode) {
  7. case Shape::POLYGON:
  8. break;
  9. case Shape::FREEHAND:
  10. isDone=true;
  11. shape=new FreeHand(freehand_point_array);
  12. freehand_point_array.clear();
  13. shapeList.push_back(shape);
  14. update();
  15. shape=NULL;
  16. break;
  17. default:
  18. isDone=true;
  19. update();
  20. shape=NULL;
  21. break;
  22. }
  23. }
  24. }

以上






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM