@Dmaxiya
2019-06-12T06:55:00.000000Z
字数 4130
阅读 1679
其他
先从原有代码的明显的小 bug 和需要改进的地方说起吧。
1. 刚运行程序,不选择画什么图形就在画布上作画的话,会让程序崩溃,原因是在 CenterWidget 的构造函数中没有初始化drawType;
2. 无法显示工具栏的图标,原因是对应路径下没有图片;
3. 保存文件不可以任意命名文件名,原因不明;
4. 在不需要 tr() 的地方都使用了 tr,tr 主要是用来实现国际化的,详见关于qt中的tr()函数,建议都修改成 QString;
5. 画布背景修改为白色;
6. 析构函数没有完全释放所有指针。
JPG, BMP 等常用的文件格式);首先是绘画部分,查了一下比较高效的画法是画在 QImage 中,然后在 QWidge 的 update 中 drawImage,详见题Qt/C++:高效绘图,因此改进是在 CenterWidget 中增加 private 的 QImage 数据成员,以 QImage 为 QPainter 的画布,最后直接保存 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 函数,这样就不需要颜色的枚举值了。
最后关于 CenterWidget 的 newDrawing、openDrawing、saveDrawing 三个函数体部分做的事情,看起来更像是应该在 MainWindow 中做的,什么弹出保存文件窗口之类的,CenterWidget 本应该只管画布部分的事就好了,这部分的代码需要大改,一部分保留一部分移到 MainWindow 中,函数声明暂定,具体后面会再修改。
enum ShapeType {Line, Ellipse, Rectangle, RandomWrite};enum Color {black, green, red};enum Width {Thin = 1, Light = 5, Thick = 10, Heavy = 20};// 随便写的,英文单词不对就改,粗细主要看显示效果class Shape {public:Width getWidth();const QColor& getColor();ShapeType getType();virtual void draw(QPainter &painter) = 0;virtual void setNextPoint(const QPoint &p) = 0;protected:Width width;QColor color;ShapeType type;Shape(const Width &w, const QColor &c, const ShapeType &t);};
class Line: public Shape {public:Line(const QLine &l, const Width &w, const QColor &c);void draw(QPainter &painter);void setNextPoint(const QPoint &p);private:QLine line;};
class Ellipse: public Shape {public:Ellipse(const QRect &r, const Width &w, const QColor &c);void draw(QPainter &painter);void setNextPoint(const QPoint &p);private:QRect rect;};
class Rectangle: public Shape {public:Rectangle(const QRect &r, const Width &w, const QColor &c);void draw(QPainter &painter);void setNextPoint(const QPoint &p);private:QRect rect;};
class RandomWrite: public Shape {public:RandomWrite(const QImage &i, const Width &w, const QColor &c);void appendPoint(const QPoint);void draw(QPainter &painter);void setNextPoint(const QPoint &p);private:QVector<QPoint> points;QImage image;};
class CenterWidget: public QWidget {Q_OBJECTpublic:explicit CenterWidget(QWidget *parent);void paintEvent(QPaintEvent *p);void initDrawing();void loadImage(const QImage &i);const QImage& getImage();bool getModifiedFlag();public slots:void setShapeType(const ShapeType &t);void setColorType(const Color &c);protected:void mousePressEvent(QMouseEvent *e);void mouseMoveEvent(QMouseEvent *e);void mouseReleaseEvent(QMouseEvent *e);private:QList<Shape> Redo;QList<Shape> Undo;Shape *shape;ShapeType shapeType;QColor color;Width width;bool beginDraw;QLabel mousePosLabel;bool isModified;QString fileName;};
class MainWindow: public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent = 0);~MainWindow();public slots:void newDrawing();void openDrawing();void saveDrawing();void closeEvent();private:CenterWidget CenterWidget;};