——功能需求
- *图形绘制
- 基本图形绘制
- 选择图形绘制颜色
类似于Windows自带的绘图程序
——界面解决方案
- 以QWidget为基类创建绘图主窗口
- 使用QGroupBox创建图形设置区域
- 使用单选按钮QRadioButton实现目标图形的选择
- 使用QComboBox实现绘图颜色的选择
效果:
问题分析:
1、如何实现*绘图?
*绘图的本质即跟踪鼠标的移动,所以必须考虑鼠标何时开始,何时结束,以及如何记录鼠标的移动。
从绘图参数的角度,可以将已经绘制结束的图形与正在绘制的图形进行分开处理。
*绘图时,必须记录鼠标移动时经过的所有的点的坐标,因此,绘图参数必须有能力保存多个坐标的值(容器存储)。
*绘图解决方案:
通过鼠标事件来处理坐标点,以及paintEvent函数来绘制图形
void mouseMoveEvent(QMouseEvent* evt);//记录鼠标移动时经过的坐标 void mousePressEvent(QMouseEvent* evt);//鼠标按下时,开始记录坐标 void mouseReleaseEvent(QMouseEvent* evt);//鼠标释放时结束,记录结束坐标 void paintEvent(QPaintEvent* );//按照记录,绘制相邻点直接的“直线”,因为相邻点与点之间很近
2、如何实现基础图形绘制?
基础图形的目标是固定的,但是开始点与结束点的不同会导致图形之间的差异。因此,通过鼠标移动时的当前坐标实时绘图,当鼠标松开时确定最终图形。
基础图形绘制要在鼠标按下时进行动态绘图,但是,无论何时都只需要记录两个坐标值,这点很重要。
基础图形绘制解决方案:
通过鼠标事件来处理坐标点,以及paintEvent函数来绘制图形。注意这里注释与上面注释不同的地方。
void mouseMoveEvent(QMouseEvent* evt);//将鼠标移动时经过的目标作为临时结束目标 void mousePressEvent(QMouseEvent* evt);//鼠标按下时,开始记录坐标 void mouseReleaseEvent(QMouseEvent* evt);//鼠标释放时结束,记录最终结束坐标 void paintEvent(QPaintEvent* );//按照记录,在开始目与最终,目标之间绘制需要的接触图形
源码:
工程文件:
paintPro.pro
#------------------------------------------------- # # Project created by QtCreator 2018-04-01T10:16:20 # #------------------------------------------------- QT += core gui #这里是C++11的宏,,代码中没有用到C++11特性,可以去掉,在Qt5+版本这么写Qt5以前的版本 #这么写 QMAKE_CXXFLAGS += -std=c++11 CONFIG += c++11 #在Qt5+版本 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = paintPro TEMPLATE = app # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += main.cpp\ Widget.cpp HEADERS += Widget.h
头文件:Widget.h
/* *authour: lbg *date: 2018.4.1 */ #ifndef WIDGET_H #define WIDGET_H #include <QRadioButton> #include <QComboBox> #include <QGroupBox> #include <QPainter> #include <QPoint> #include <QList> #include <QMouseEvent> #include <QPaintEvent> #include <QWidget> class Widget : public QWidget { Q_OBJECT enum DrawType//绘制类型 { NONE, //无类型 FREE, //*绘图 ELLIPSE, //圆、椭圆 RECT, //矩形、正四边形 LINE //直线 }; struct DrawParam //绘制参数结构体、类 { DrawType m_type; //绘制类型 Qt::GlobalColor m_color; //颜色 QList<QPoint> m_points; //坐标点 }; DrawParam m_currentParam; //当前参数,正在绘制 QList<DrawParam> m_DrawList; //所有参数,把将已经绘制结束的图形与正在绘制的图形进行分开处理 public: Widget(QWidget *parent = 0); ~Widget(); private://内部接口 /* * @brief: getCurrentColor()根据界面选择情况,返回用户选择的颜色 * @param: 无 * @return: 颜色枚举 */ Qt::GlobalColor getCurrentColor(); /* * @brief:根据界面返回用户选择的绘制类型 * @pram:无 * @return:类型枚举 */ Widget::DrawType getCurrentType(); /* * @brief: 根据当前用户选择的绘制类型(*绘制或基础图形绘制)来对当前坐标点进行处理 * @param:当前处理的坐标点pos * @return:无 */ void appendPos(QPoint pos); //辅助存放鼠标点 /* * @brief:辅助paintEvent绘制函数,进行绘制 * @param: QPainter对象, DrawParam当前绘制参数 * @return:无 */ void drawGraphic(QPainter &painter, DrawParam ¶m);// protected: //鼠标移动处理函数 void mouseMoveEvent(QMouseEvent* evt); //鼠标按下处理函数 void mousePressEvent(QMouseEvent* evt); //鼠标释放函数 void mouseReleaseEvent(QMouseEvent* evt); //绘图函数 void paintEvent(QPaintEvent* ); private://界面相关 QRadioButton* m_FreeDraw; //*绘制按钮 QRadioButton* m_EllipseDraw; //椭圆绘制按钮 QRadioButton* m_RectDraw; //矩形绘制按钮 QRadioButton* m_LineDraw; //直线绘制按钮 QComboBox* m_ColorSelect; //颜色绘制 }; #endif // WIDGET_H
Widget.cpp
#include <QAction> #include <QHBoxLayout> #include <QtMath> #include "Widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { //界面布局 this->setFixedSize(800, 600); m_FreeDraw = new QRadioButton("Free"); m_EllipseDraw = new QRadioButton("Ellipse"); m_RectDraw = new QRadioButton("Rect"); m_LineDraw = new QRadioButton("Line"); m_ColorSelect = new QComboBox(this); m_ColorSelect->addItem("Red"); m_ColorSelect->addItem("Black"); m_ColorSelect->addItem("Green"); m_ColorSelect->addItem("Yellow"); m_ColorSelect->addItem("Blue"); QHBoxLayout* m_hlayout = new QHBoxLayout; m_hlayout->addWidget(m_FreeDraw); m_hlayout->addWidget(m_EllipseDraw); m_hlayout->addWidget(m_RectDraw); m_hlayout->addWidget(m_LineDraw); m_hlayout->addWidget(m_ColorSelect); QGroupBox* box = new QGroupBox(this); box->setLayout(m_hlayout); box->resize(width(), height()/15); } Widget::~Widget() { } Qt::GlobalColor Widget::getCurrentColor() { //根据用户的选择情况,返回颜色 Qt::GlobalColor ret = Qt::black; if(m_ColorSelect->currentText() == "Yellow") ret = Qt::yellow; if(m_ColorSelect->currentText() == "Black") ret = Qt::black; if(m_ColorSelect->currentText() == "Red") ret = Qt::red; if(m_ColorSelect->currentText() == "Green") ret = Qt::green; if(m_ColorSelect->currentText() == "Blue") ret = Qt::blue; return ret; } Widget::DrawType Widget::getCurrentType() { //根据用户的选择情况,返回要绘制的类型 Widget::DrawType ret = NONE; if(m_FreeDraw->isChecked()) ret = FREE; if(m_EllipseDraw->isChecked()) ret = ELLIPSE; if(m_RectDraw->isChecked()) ret = RECT; if(m_LineDraw->isChecked()) ret = LINE; return ret; } void Widget::appendPos(QPoint pos) { //这里根据当前绘制参数作相应处理 if(m_currentParam.m_type != NONE) { if(m_currentParam.m_type == FREE) { m_currentParam.m_points.append(pos);//*绘要把每个点都存下来 } else { if(m_currentParam.m_points.size() == 2)//绘制列出的几个图形只要两个点 { m_currentParam.m_points.removeLast();//把最后一个点弹出,后面再存入传进来这个点 } m_currentParam.m_points.push_back(pos); } } } void Widget::drawGraphic(QPainter& painter, DrawParam& param) { if(param.m_type != NULL && param.m_points.count() >= 2) { int x = param.m_points[0].x() < param.m_points[1].x() ? param.m_points[0].x() : param.m_points[1].x(); int y = param.m_points[0].y() < param.m_points[1].y() ? param.m_points[0].y() : param.m_points[1].y(); int w = qAbs(param.m_points[0].x() - param.m_points[1].x()) + 1; int h = qAbs(param.m_points[0].y() - param.m_points[1].y()) + 1; //设置painter的属性 painter.setPen(QPen(param.m_color)); painter.setBrush(QBrush(param.m_color)); switch (param.m_type) { case FREE: for(int i = 0; i != param.m_points.count() - 1; i++) //*绘 { painter.drawLine(param.m_points[i], param.m_points[i + 1]); } break; case LINE: painter.drawLine(param.m_points[0], param.m_points[1]); break; case ELLIPSE: painter.drawEllipse(x, y, w, h); break; case RECT: painter.drawRect(x, y, w, h); break; default: break; } } } void Widget::mouseMoveEvent(QMouseEvent *evt) { appendPos(evt->pos()); update(); } void Widget::mousePressEvent(QMouseEvent *evt) { m_currentParam.m_type = getCurrentType(); m_currentParam.m_color = getCurrentColor(); m_currentParam.m_points.append(evt->pos()); } void Widget::mouseReleaseEvent(QMouseEvent *evt) { appendPos(evt->pos()); m_DrawList.append(m_currentParam); m_currentParam.m_color = Qt::white; m_currentParam.m_type = NONE; m_currentParam.m_points.clear(); update(); } void Widget::paintEvent(QPaintEvent *) { // Q_UNUSED(evt) QPainter paint(this); for(int i = 0; i < m_DrawList.count(); i++) { drawGraphic(paint, m_DrawList[i]); } drawGraphic(paint, m_currentParam); }
main.cpp
#include "Widget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }
总结:
1、绘图程序需要重写鼠标事件处理函数
2、模型视图的思想适用于绘图程序
3、所有的图形绘制都由paintEvent函数完成
4、工程中要避免在绘制时进行浮点运算
扩展:
可以将绘制的图形保存为本地文件(相关描述文件,图片)
可以加入网络数据传输,做成一个可以大家一同使用的绘图板,界面由多个Client实时共享。