C++学习笔记之鼠标绘图

来源:互联网 发布:淘宝代理免费加盟 编辑:程序博客网 时间:2024/06/04 20:04

今天我所要分享的内容为如何在Qt中编写鼠标绘图的程序。

绘制的图形可以有直线、矩形、椭圆、多边形、自由曲线。

当然还在其中添加了一个扫描线算法,对自己绘制的多边形进行填充,不过还有一两点特殊情况没有考虑清楚,导致填充不完整。不过其他画图的程序都是没有问题的。Qt中有自带的填充算法,所以我用QPainterPath绘制自由曲线时,自动填充没有问题。

主程序如下:

#include "minidraw.h"#include <QtWidgets/QApplication>#include <QLabel>#include "viewwidget.h"int main(int argc, char *argv[]){_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);//检查内存泄漏QApplication a(argc, argv);MiniDraw  mini_draw_action_;mini_draw_action_.show();mini_draw_action_.setMinimumSize(600, 400);return a.exec();}

Qt界面按钮设置的H文件和C文件:

H文件如下:


#ifndef MINIDRAW_H#define MINIDRAW_H#include <QtWidgets/QMainWindow>#include "ui_minidraw.h"#include "viewwidget.h"class MiniDraw : public QMainWindow{Q_OBJECTpublic:MiniDraw(QWidget *parent = 0);~MiniDraw();void Init();void set_figure_type_to_line();void set_figure_type_to_ellipse();void set_figure_type_to_rectangle();void set_figure_type_to_polygon();void set_figure_type_to_freehand();void set_figure_type_to_delete();void set_figure_type_to_color();void set_figure_type_to_freehan1();void color_filling();public:Ui::MiniDrawClass ui;viewwidget*view_widget_; //声明viewwidget指针QAction* mini_draw_action_;  //声明动作QMenu* main_menu_;          //声明菜单QToolBar* main_toolbar_;   //声明工具栏void CreateButtons();  //声明函数};#endif // MINIDRAW_H


C文件如下:


#include "minidraw.h"#include <QMessageBox>#include <QColorDialog>#include "viewwidget.h"#include "figures.h"MiniDraw::MiniDraw(QWidget *parent): QMainWindow(parent){ui.setupUi(this);CreateButtons();Init();}MiniDraw::~MiniDraw(){}void MiniDraw::CreateButtons(){main_menu_ = menuBar()->addMenu(tr("&Figure Tool"));  //创建菜单,标签为Figure Toolmain_toolbar_ = addToolBar(tr("&Figure Tool"));     //创建工具栏,标签为Figure Toolmini_draw_action_ = new QAction(tr("&Line"), this);  //&的作用:设置此动作的快捷键为标签字符串的首字母,此例为’L’main_menu_->addAction(mini_draw_action_);            //将动作Line加载到Main菜单中main_toolbar_->addAction(mini_draw_action_);       //将动作Line加载到Main工具栏中connect(mini_draw_action_, &QAction::triggered,this, &MiniDraw::set_figure_type_to_line); mini_draw_action_ = new QAction(tr("&Ellipse"), this);  //&的作用:设置此动作的快捷键为标签字符串的首字母,此例为’E’ main_menu_->addAction(mini_draw_action_);            //将动作Ellipse加载到Main菜单中 main_toolbar_->addAction(mini_draw_action_);       //将动作Ellipse加载到Main工具栏中connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_ellipse);mini_draw_action_ = new QAction(tr("&Rectangle"), this);  //&的作用:设置此动作的快捷键为标签字符串的首字母,此例为’R’main_menu_->addAction(mini_draw_action_);            //将动作Rectangle加载到Main菜单中main_toolbar_->addAction(mini_draw_action_);       //将动作Rectangle加载到Main工具栏中connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_rectangle);mini_draw_action_ = new QAction(tr("&Polygon"), this);  //&的作用:设置此动作的快捷键为标签字符串的首字母,此例为’P’main_menu_->addAction(mini_draw_action_);            //将动作Polygon加载到Main菜单中main_toolbar_->addAction(mini_draw_action_);       //将动作Polygon加载到Main工具栏中connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_polygon);mini_draw_action_ = new QAction(tr("&Freehand"), this);  main_menu_->addAction(mini_draw_action_);            //将动作Freehand加载到Main菜单中main_toolbar_->addAction(mini_draw_action_);       //将动作Freehand加载到Main工具栏中connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_freehand);mini_draw_action_ = new QAction(tr("&Freehand1"), this);main_menu_->addAction(mini_draw_action_);            //将动作Freehand1加载到Main菜单中main_toolbar_->addAction(mini_draw_action_);       //将动作Freehand1加载到Main工具栏中connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_freehan1);mini_draw_action_ = new QAction(tr("&Delete"), this);  main_menu_->addAction(mini_draw_action_);            //将动作Delete加载到Main菜单中main_toolbar_->addAction(mini_draw_action_);       //将动作Delete加载到Main工具栏中connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_delete);mini_draw_action_ = new QAction(tr("&Color"), this);main_menu_->addAction(mini_draw_action_);            //将动作设置颜色加载到Main菜单中main_toolbar_->addAction(mini_draw_action_);       //将动作设置颜色加载到Main工具栏中connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::set_figure_type_to_color);mini_draw_action_ = new QAction(tr("&Color_Fill"), this);main_menu_->addAction(mini_draw_action_);         //将动作颜色填充加载到主菜单中main_toolbar_->addAction(mini_draw_action_);connect(mini_draw_action_, &QAction::triggered, this, &MiniDraw::color_filling);}void MiniDraw::Init(){view_widget_ = new viewwidget();setCentralWidget(view_widget_);//实例代码中将绘图控件的实例化放在Init函数中//Init函数将在MiniDraw的构造函数中被调用//实例化viewwidgett控件窗口//将viewwidget控件设置为主窗口的中心位置}void MiniDraw::set_figure_type_to_line(){view_widget_->figure_type_ = viewwidget::kLine;}void MiniDraw::set_figure_type_to_ellipse(){view_widget_->figure_type_ = viewwidget::kEllipse;}void MiniDraw::set_figure_type_to_rectangle(){view_widget_->figure_type_ = viewwidget::kRectangle;}void MiniDraw::set_figure_type_to_polygon(){view_widget_->figure_type_ = viewwidget::kPolygon;}void MiniDraw::set_figure_type_to_freehand(){view_widget_->figure_type_ = viewwidget::kFreehand;}void MiniDraw::set_figure_type_to_freehan1(){view_widget_->figure_type_ = viewwidget::kFreehand1;}void MiniDraw::set_figure_type_to_delete(){if (!view_widget_->figure_array_.empty()){view_widget_->figure_array_.pop_back();}}void MiniDraw::set_figure_type_to_color(){view_widget_->color = QColorDialog::getColor(Qt::white, this);}void MiniDraw::color_filling(){view_widget_->figure_type_ = viewwidget::kColorFill;}


具体的鼠标响应函数的声明和定义:

H文件如下:

#ifndef VIEWWIDGET_H#define VIEWWIDGET_H#include <QWidget>#include <QPainterPath>#include "ui_viewwidget.h"#include <QMouseEvent>#include <vector>#include <iostream>#include<QColor>#include "figures.h"class viewwidget : public QWidget{Q_OBJECTpublic:viewwidget(QWidget *parent = 0);  //绘制函数声明,Qt所有的绘制都只能在此函数中完成~viewwidget();QColor color;enum CFigureType{ kLine = 0, kRectangle = 1, kEllipse = 2, kPolygon = 3,kFreehand=4,kColorFill=5,kFreehand1};std::vector <CFigures*>figure_array_;CFigureType figure_type_;protected:void paintEvent(QPaintEvent *);void mousePressEvent(QMouseEvent *event);        //鼠标击发响应函数(左右键,单双击)void mouseMoveEvent(QMouseEvent *event);        //鼠标移动响应函数void mouseReleaseEvent(QMouseEvent *event);    //鼠标释放响应函数(左右键,单双击)void colorFilling(std::vector<QPoint> ThePolygon,QPainter &paint);int calculate_x_intersect(QPoint point_a, QPoint point_b, int y);public:Ui::viewwidget ui;bool draw_status_;//当前绘制状态,true为绘制当前鼠标拖动的图元,false为不绘制QPointstart_point_;//当前图元的起始点QPointend_point_;//当前图元的终止点QPoint current_point_;std::vector<QPoint>points;  //保存鼠标经过的点QPainterPath *path;};#endif // VIEWWIDGET_H

C文件如下:


#include "viewwidget.h"#include <QMouseEvent>#include <QPaintEvent>#include <QPainter>#include <QPoint>#include <QScreen>#include <QPixmap>#include <QImage>#include <iostream>#include <QColor>#include <vector>#include <QColorDialog>#include <QMessageBox>#include <algorithm>#include "figure.h"viewwidget::viewwidget(QWidget *parent): QWidget(parent){ui.setupUi(this);draw_status_ = false;  //设置初始绘制状态为 – 不绘制this->path = NULL;}viewwidget::~viewwidget(){for (size_t i = 0; i < figure_array_.size(); i++){if (figure_array_[i]){delete figure_array_[i];figure_array_[i] = NULL;}}}//鼠标击发响应函数函数体void viewwidget::mousePressEvent(QMouseEvent *event){if (Qt::LeftButton == event->button())               //判断是否是鼠标左击{draw_status_ = true;                        //设置绘制状态为 – 绘制start_point_ = end_point_ = event->pos();  //将图元初始点设置为当前鼠标击发点if (figure_type_==kPolygon){points.push_back(start_point_);}if (figure_type_==kFreehand){path = new QPainterPath;path->moveTo(event->pos());}if (figure_type_==kFreehand1){points.push_back(start_point_);}}}//鼠标移动响应函数函数体void viewwidget::mouseMoveEvent(QMouseEvent *event){if (draw_status_)    //判断当前绘制状态{end_point_ = event->pos();  //若为真,则设置图元终止点为鼠标当前位置}if (figure_type_ == kFreehand){path->lineTo(event->pos());}if (figure_type_==kFreehand1){points.push_back(start_point_);}}//鼠标释放响应函数函数体void viewwidget::mouseReleaseEvent(QMouseEvent *event){ CFigures* current_figure_ = NULL; switch(figure_type_) { case kLine: current_figure_ = new Line(start_point_.rx(), start_point_.ry(), end_point_.rx(), end_point_.ry());current_figure_->color3 = color; figure_array_.push_back(current_figure_); draw_status_ = false; //设置绘制状态为 – 不绘制 break; case kEllipse: current_figure_ = new Ellipse(start_point_.rx(), start_point_.ry(), end_point_.rx(), end_point_.ry());current_figure_->color3 = color;  figure_array_.push_back(current_figure_); draw_status_ = false; //设置绘制状态为 – 不绘制 break; case kPolygon:if (Qt::RightButton==event->button()){current_figure_ = new Polygon(points);current_figure_->color3 = color;figure_array_.push_back(current_figure_);//points.clear();    //清除这些点,若不用进行颜色填充则不用注释//draw_status_ = false; //设置绘制状态为 – 不绘制}break;case kRectangle:current_figure_ = new Rectangle(start_point_.rx(), start_point_.ry(), end_point_.rx(), end_point_.ry());current_figure_->color3 = color;figure_array_.push_back(current_figure_);draw_status_ = false; //设置绘制状态为 – 不绘制break;  //必不可少case kFreehand:current_figure_ = new Freehand(*path);current_figure_->color3 = color;figure_array_.push_back(current_figure_);draw_status_ = false;delete path;path = NULL;break;case  kFreehand1:if (Qt::RightButton == event->button()){current_figure_ = new Polygon(points);current_figure_->color3 = color;figure_array_.push_back(current_figure_);//points.clear(); //清除这些点,若不用进行颜色填充则不用注释//draw_status_ = false; //设置绘制状态为 – 不绘制}                break;case kColorFill:if (Qt::RightButton == event->button()){points.clear();}break; default: break; } current_figure_ = NULL;}//鼠标绘制函数函数体void viewwidget::paintEvent(QPaintEvent *){ QPainter  painter(this); //定义painter在this指向的控件(此例为ViewWidget)中绘图for (size_t i = 0; i < figure_array_.size(); i++){painter.setPen(figure_array_[i]->color3);figure_array_[i]->Draw(painter);}if (draw_status_)   {switch (figure_type_){case kLine:painter.setPen(color);painter.drawLine(start_point_, end_point_);break;case kRectangle:painter.setPen(color);painter.drawRect(start_point_.rx(), start_point_.ry(), end_point_.rx() - start_point_.rx(), end_point_.ry() - start_point_.ry());break;case kEllipse:painter.setPen(color);painter.drawEllipse(start_point_.rx(), start_point_.ry(), end_point_.rx() - start_point_.rx(), end_point_.ry() - start_point_.ry());break;case kPolygon:{painter.setPen(color);                        painter.drawLine(start_point_, end_point_); if (points.size() == 0) { break; } QPoint pre = points[0];  for (int i = 1; i < points.size(); i++) { painter.drawLine(pre, points[i]); pre = points[i]; }}break;case kFreehand:painter.setPen(color);if (path!=NULL){painter.drawPath(*path);}break;case  kFreehand1:{painter.setPen(color);if (points.size() == 0){break;}QPoint pre = points[0];for (int i = 1; i < points.size(); i++){painter.drawLine(pre, points[i]);pre = points[i];}}break;case kColorFill:{ if (points.size() == 0) {  break; } QPen pen = painter.pen(); painter.setPen(color); pen.setColor(Qt::red);colorFilling(points, painter);}break;default:break;}}update();    //更新窗口}void viewwidget::colorFilling(std::vector<QPoint> ThePolygon, QPainter &paint){int minY, maxY;minY = maxY = (*ThePolygon.begin()).ry();for (int i = 0; i < ThePolygon.size(); i++){if (ThePolygon[i].ry()<minY){minY = ThePolygon[i].ry();}if (ThePolygon[i].ry()>maxY){maxY = ThePolygon[i].ry();}}std::vector<bool> isUpDown(ThePolygon.size(), false);for (int i = 0; i < ThePolygon.size(); i++){QPoint p = ThePolygon[i];QPoint p_pre = ThePolygon[(i == 0) ? ThePolygon.size() - 1 : i - 1];QPoint p_next = ThePolygon[(i == ThePolygon.size() - 1) ? 0 : i + 1];int tp = (p.ry() - p_pre.ry())*(p.ry() - p_next.ry());if (tp < 0){isUpDown[i] = true;}}for (int h = minY; h <= maxY; h++){std::vector<int> intersects;std::vector<bool> isVertsUsed(ThePolygon.size(), false);for (int j = 0; j < ThePolygon.size(); j++){QPoint start_pt = ThePolygon[j];int end_idx = (j == ThePolygon.size() - 1) ? 0 : j + 1;QPoint end_pt = ThePolygon[end_idx];int res_x = calculate_x_intersect(start_pt, end_pt, h);if (res_x == -1){continue;}if (res_x==-2&&start_pt.ry() == end_pt.ry()){QPoint p_pre_pre = ThePolygon[(j == 0) ? ThePolygon.size() - 1 : j - 1];QPoint p_next_next = ThePolygon[(j == ThePolygon.size() - 1) ? 0 : j + 1];if ((start_pt.ry() - p_pre_pre.ry())*(start_pt.ry()-p_next_next.ry())<0){intersects.push_back(start_pt.rx());}continue;}if (h == start_pt.ry()){if (isUpDown[j]){if (!isVertsUsed[j]){isVertsUsed[j] = true;intersects.push_back(start_pt.rx());}else{continue;}}else{intersects.push_back(start_pt.rx());}continue;}if (h == end_pt.ry()){if (isUpDown[end_idx]){if (!isVertsUsed[end_idx]){isVertsUsed[end_idx] = true;intersects.push_back(end_pt.rx());}else{continue;}}else{intersects.push_back(end_pt.rx());}continue;}intersects.push_back(res_x);}if (intersects.size() % 2 == 1){intersects.push_back(intersects[intersects.size()-1]);}std::sort(intersects.begin(), intersects.end());if (intersects.size() < 2){continue;}for (int i = 0; i < intersects.size(); i = i + 2){QPoint p(intersects[i], h);QPoint q(intersects[i + 1], h);paint.drawLine(p, q);}intersects.clear();}}int viewwidget::calculate_x_intersect(QPoint point_a, QPoint point_b, int y){float slop = 0;            //多边形的任意边的斜率float intercept = 0;      //多边形的任意边的直线方程中的截距int x_inter = 0;          //交点坐标xfloat y_1;    if ((point_a.ry() < y&&point_b.ry() > y || point_a.ry() > y&&point_b.ry() < y) && (point_a.rx() == point_b.rx())){x_inter = point_a.rx();    //与斜率不存在的线段相交}else if (point_a.ry() == point_b.ry()){//x_inter = point_a.rx();x_inter = -2;            //斜率为0,返回-2}else if (point_a.ry() == y&&point_b.ry() != y){x_inter = point_a.rx();   //与线段的端点相交}else if (point_b.ry() == y&&point_a.ry() != y){x_inter = point_b.rx();   //与线段的端点相交}else if (point_a.ry() > y&&point_b.ry() > y || point_a.ry() < y&&point_b.ry() < y){x_inter = -1;           //没有交点,返回-1}else if ((point_a.ry() < y&&point_b.ry() > y || point_a.ry() > y&&point_b.ry() < y) && (point_a.ry() != point_b.ry()) && (point_a.rx() != point_b.rx())){slop = float((point_b.rx() - point_a.rx())) / float((point_b.ry() - point_a.ry())); //计算直线方程的参数intercept = float(point_a.rx()) - slop*float(point_a.ry());x_inter = int(slop*float(y) + intercept+0.5);     //交点坐标x}return x_inter;}


接下来是不同的绘制类型的定义,便于多次绘制:


#ifndef _FIGURES_H_#define _FIGURES_H_#include <QPainter>#include <vector>#include <QPainterPath>#include <QColor>#include <algorithm>#include <iostream>class CFigures{public:CFigures();virtual ~CFigures();QColor color3;virtual void Draw(QPainter &paint);};class Line :public CFigures{public:Line(void);Line(int start_point_x, int start_point_y, int end_point_x, int end_point_y){start_point_x_ = start_point_x;start_point_y_ = start_point_y;end_point_x_ = end_point_x;end_point_y_ = end_point_y;}~Line(){;}public:void Draw(QPainter &paint){paint.drawLine(start_point_x_, start_point_y_, end_point_x_, end_point_y_);}private:int start_point_x_, start_point_y_, end_point_x_, end_point_y_;};class Rectangle:public CFigures{public:Rectangle(void);Rectangle(int start_point_x, int start_point_y, int end_point_x, int end_point_y){start_point_x_ = start_point_x;start_point_y_ = start_point_y;end_point_x_ = end_point_x;end_point_y_ = end_point_y;}~Rectangle(){;}public:void Draw(QPainter &paint){paint.drawRect(start_point_x_, start_point_y_, end_point_x_ - start_point_x_, end_point_y_ - start_point_y_);}private:int start_point_x_, start_point_y_, end_point_x_, end_point_y_;};class Ellipse : public CFigures{public:Ellipse(void);Ellipse(int start_point_x, int start_point_y, int end_point_x, int end_point_y){start_point_x_ = start_point_x;start_point_y_ = start_point_y;end_point_x_ = end_point_x;end_point_y_ = end_point_y;}~Ellipse(){;}public:void Draw(QPainter &paint){paint.drawEllipse(start_point_x_, start_point_y_, end_point_x_ - start_point_x_, end_point_y_ -start_point_y_);}private:int start_point_x_, start_point_y_, end_point_x_, end_point_y_;};class Polygon : public CFigures{public:Polygon(void);Polygon(std::vector<QPoint>points){point = points;}~Polygon(){;}public:void Draw(QPainter &paint){paint.drawPolygon(&point.at(0), point.size());}private:   std::vector<QPoint>point; };class Freehand : public CFigures{public:Freehand(void);Freehand(QPainterPath paths){path = paths;}~Freehand(){;}public:void Draw(QPainter &paint){QLinearGradient myGradient;//QPen myPen;myGradient.setColorAt(0, Qt::black);paint.setBrush(myGradient);//paint.setPen(myPen);//以上几句话对颜色填充有作用,注释掉就不填充颜色paint.drawPath(path);}private:QPainterPath path;};#endif

C文件如下:

#include "figures.h"#include "viewwidget.h"#include <QColorDialog>CFigures::CFigures(){;}CFigures::~CFigures(){;}void CFigures::Draw(QPainter &paint){;}


这次分享的代码中,其实还有很多不完善的地方。改了很久,还是有些小漏洞没改完。


以下是简单的绘图结果截屏:



以下这张图是Qt中自带的path绘制自由曲线以及自带的颜色填充结果:




以下这张图是简单的自由曲线:




以下这张图是对自己绘制的多边形进行颜色填充,图形不负责,所以填充效果不错:




以下这张图就比较差劲了,扫描线程序没有写好。就是对于一些特殊情况没有考虑到。