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. }

以上





转载于:https://www.cnblogs.com/LingjieLi-vera/p/d0b86630f939da87ff0e04249df9bf87.html

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值