[关闭]
@Dmaxiya 2019-06-12T14:55:00.000000Z 字数 4130 阅读 1429

Qt 画板

其他


原有 bug

先从原有代码的明显的小 bug 和需要改进的地方说起吧。
1. 刚运行程序,不选择画什么图形就在画布上作画的话,会让程序崩溃,原因是在 CenterWidget 的构造函数中没有初始化drawType
2. 无法显示工具栏的图标,原因是对应路径下没有图片;
3. 保存文件不可以任意命名文件名,原因不明;
4. 在不需要 tr() 的地方都使用了 trtr 主要是用来实现国际化的,详见关于qt中的tr()函数,建议都修改成 QString
5. 画布背景修改为白色;
6. 析构函数没有完全释放所有指针。

功能分析

  1. 支持画圆、矩形、直线,支持随手写功能;
  2. 可以改变画笔的颜色与粗细;
  3. 可以保存为文件(专用格式),并且可以读取显示;
  4. 可以另存为图像(支持 JPG, BMP 等常用的文件格式);
  5. 增加笔型。即可以选择使用毛笔,铅笔, 蜡笔三种不同的笔来书写,画板上呈现出模拟三种不同笔书写的效果;
  6. 在前面的画笔程序中,增加 Redo, Undo 功能。可以指定 Undo 最多的次数,比如 10 次。

首先是绘画部分,查了一下比较高效的画法是画在 QImage 中,然后在 QWidgeupdatedrawImage,详见题Qt/C++:高效绘图,因此改进是在 CenterWidget 中增加 privateQImage 数据成员,以 QImageQPainter 的画布,最后直接保存 QImage 对象。
其次是画的对象,有几种对象类型:椭圆、矩形、直线、随手写,其中随手写又可以支持不同的笔型,除了随手写的其他类型都不应该支持笔型,在 CenterWidget 的设置中可以设置笔型,但是只有在随手写时才能显示出笔型的效果,目前笔型比较好的支持是用 QImage 来储存,类似于 PS 的笔刷,QImage 支持修改大小、颜色。
对于椭圆、矩形、直线,画笔的颜色与粗细都可以直接作为 setPen 的参数传入,颜色、粗细、形状类型的参数都存在 CenterWidget 的数据成员中,在 CenterWidget 的构造函数中应初始化它们,并且在椭圆、矩形和直线的构造函数中应该显式传入这些参数;但是对于随手写则需要对应地修改 QImage,为提高效率,不在 mousePressEvent 的时候才设置画笔的 QImage,而在选中画笔时就修改 CenterWidget 的笔刷数据成员为对应的粗细及颜色,在开始随手写时将 CenterWidget 的笔刷数据成员传入随手写的数据成员中。
粗细和图形类型都设为枚举型,所有颜色直接采用 Qcolor 内置的颜色值(如 Qt::black, Qt::green, Qt::red 等),便于扩展程序,同时注意在设置粗细的菜单栏下应显示对应的粗细预览,就像 Windows 中的画图一样。一个比较好的体验是在使用随手写时能预览到要画的笔刷(在移动鼠标时可以看到笔刷的形状与颜色,但是只有在点下鼠标时才会开始绘画),但是这个暂时可以先放一下,等完成基本功能后再补充。
由于随手写需要用一系列点来储存,因此将起点 p1 和终点 p2 记录在 CenterWidget 中做 update 已经不再合适,应该用一个临时的 Shape 对象来作为 update 的对象,在 mouseReleaseEvent 中直接将临时 Shape 存入 Redo QList 中。
关于 Redo 和 Undo,由于涉及到两端的操作,而 Qt 中没有 QDeque,就用两个 QList 来记录 Redo 和 Undo 操作中的形状对象,且大小最大为 10。
最后是保存与读取,由于可以打开 *.jpg*.bmp 格式,正常来说我们也应该能打开其他来源的同类型文件,但是大小可能与我们的默认窗口大小不符合,因此在绘图时应该有一个默认的窗口大小与打开文件的窗口大小,为简单起见,先窗口大小设为不可调整(不可以最大化,不可以被 Resize)。保存为专用格式暂时先不做,由于原先的代码不能将文件保存为任意格式,所以最后在保存文件时建议加上文件名的格式检查,除了文件名中禁止出现的字符,还要检查后缀,是否为 .jpg 或者 .bmp,若是则直接保存,否则自动加上后缀并保存。
读取一个新的文件时,Redo 和 Undo 的 QList 完全清空失效(在新建文件时也一样)。
最后关于信号与槽的部分,在初始化 MainWindow 时设置的 connect 实际上都是先调用自己的槽函数,再调用 CenterWidget 的函数,这样的设计看起来是不合理的,实际上可以通过加 QSignalMapper 的方式(详见Qt菜单QMenu QAction连接信号槽函数),与枚举型配合,直接将参数传到 CenterWindow 的函数中,再用对应的参数设定值,因此应该把这些被 MainWindow 的槽函数调用的 CenterWidget 中的函数设为 public 槽函数。由于 QColor 不能作为 mapping 参数,因此暂时也为 QColor 增加有限的枚举值,若要改为用调色板,想来应该可以设置对应的 signal 函数,这样就不需要颜色的枚举值了。
最后关于 CenterWidgetnewDrawingopenDrawingsaveDrawing 三个函数体部分做的事情,看起来更像是应该在 MainWindow 中做的,什么弹出保存文件窗口之类的,CenterWidget 本应该只管画布部分的事就好了,这部分的代码需要大改,一部分保留一部分移到 MainWindow 中,函数声明暂定,具体后面会再修改。

头文件

Shape.h

  1. enum ShapeType {Line, Ellipse, Rectangle, RandomWrite};
  2. enum Color {black, green, red};
  3. enum Width {Thin = 1, Light = 5, Thick = 10, Heavy = 20};
  4. // 随便写的,英文单词不对就改,粗细主要看显示效果
  5. class Shape {
  6. public:
  7. Width getWidth();
  8. const QColor& getColor();
  9. ShapeType getType();
  10. virtual void draw(QPainter &painter) = 0;
  11. virtual void setNextPoint(const QPoint &p) = 0;
  12. protected:
  13. Width width;
  14. QColor color;
  15. ShapeType type;
  16. Shape(const Width &w, const QColor &c, const ShapeType &t);
  17. };

Line.h

  1. class Line: public Shape {
  2. public:
  3. Line(const QLine &l, const Width &w, const QColor &c);
  4. void draw(QPainter &painter);
  5. void setNextPoint(const QPoint &p);
  6. private:
  7. QLine line;
  8. };

Ellipse.h

  1. class Ellipse: public Shape {
  2. public:
  3. Ellipse(const QRect &r, const Width &w, const QColor &c);
  4. void draw(QPainter &painter);
  5. void setNextPoint(const QPoint &p);
  6. private:
  7. QRect rect;
  8. };

Rectangle.h

  1. class Rectangle: public Shape {
  2. public:
  3. Rectangle(const QRect &r, const Width &w, const QColor &c);
  4. void draw(QPainter &painter);
  5. void setNextPoint(const QPoint &p);
  6. private:
  7. QRect rect;
  8. };

RandomWrite.h

  1. class RandomWrite: public Shape {
  2. public:
  3. RandomWrite(const QImage &i, const Width &w, const QColor &c);
  4. void appendPoint(const QPoint);
  5. void draw(QPainter &painter);
  6. void setNextPoint(const QPoint &p);
  7. private:
  8. QVector<QPoint> points;
  9. QImage image;
  10. };

CenterWidget.h

  1. class CenterWidget: public QWidget {
  2. Q_OBJECT
  3. public:
  4. explicit CenterWidget(QWidget *parent);
  5. void paintEvent(QPaintEvent *p);
  6. void initDrawing();
  7. void loadImage(const QImage &i);
  8. const QImage& getImage();
  9. bool getModifiedFlag();
  10. public slots:
  11. void setShapeType(const ShapeType &t);
  12. void setColorType(const Color &c);
  13. protected:
  14. void mousePressEvent(QMouseEvent *e);
  15. void mouseMoveEvent(QMouseEvent *e);
  16. void mouseReleaseEvent(QMouseEvent *e);
  17. private:
  18. QList<Shape> Redo;
  19. QList<Shape> Undo;
  20. Shape *shape;
  21. ShapeType shapeType;
  22. QColor color;
  23. Width width;
  24. bool beginDraw;
  25. QLabel mousePosLabel;
  26. bool isModified;
  27. QString fileName;
  28. };

MainWindow.h

  1. class MainWindow: public QMainWindow {
  2. Q_OBJECT
  3. public:
  4. MainWindow(QWidget *parent = 0);
  5. ~MainWindow();
  6. public slots:
  7. void newDrawing();
  8. void openDrawing();
  9. void saveDrawing();
  10. void closeEvent();
  11. private:
  12. CenterWidget CenterWidget;
  13. };
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注