Qt绘图与图形视图之移动鼠标手动绘制任意多边形的简单介绍

news/2024/11/14 22:01:08/

往期回顾

【QT进阶】Qt线程与并发之QtConcurrent返回值与run方法的参数说明-CSDN博客

Qt绘图与图形视图之绘图技术知识点的简单介绍-CSDN博客

Qt绘图与图形视图之常见图形、路径、文字、图片的绘制介绍-CSDN博客

 Qt绘图与图形视图之移动鼠标手动绘制任意多边形的简单介绍

一、最终效果

左键点击画板,就可以移动鼠标开始绘制,双击左键结束绘制,或者右键点击也结束绘制。

二、具体实现

1、简单思路

主要两个类实现,一个负责绘制的逻辑实现,一个负责实现菜单,用了ui设计。

 2、菜单实现

我们要实现一个菜单,当用户单击鼠标右键时,会弹出一个菜单,让用户进行选择。

2.1、为什么在构造函数里创建菜单

在创建菜单的过程中,我们并没有把创建菜单封装成一个类,而是放在了构造函数里。为什么不把菜单栏创建过程放在contextMenuEvent方法里?而要放在构造函数里。

因为如果放在contextMenuEvent方法里,这意味着用户每单击鼠标一次,都会调用这个方法去创建一次菜单栏,而放在构造函数里就只用创建一次

2.2、假动作设计
 QAction* pAc3 = new QAction(QString::fromLocal8Bit("退出菜单"), this);

这是个假动作,为了让菜单消失,且不影响绘制路径

2.3、fromLocal8Bit()方法

fromLocal8Bit()将本地编码转换为QString,使用setShortcut方法为该QAction对象添加了快捷键"Ctrl+E" 

 //fromLocal8Bit()将本地编码转换为QStringQAction* pAc1 = new QAction(QString::fromLocal8Bit("结束绘制"), this);//使用setShortcut方法为该QAction对象添加了快捷键"Ctrl+E"pAc1->setShortcut(QKeySequence("Ctrl+E")); //添加快捷键
2.4、完整代码

 注释很详细,可以慢慢看。

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//setFocusPolicy(Qt::StrongFocus);//setFocus();//下面是菜单栏创建过程,整体是很简单的,就不过多赘述//有一个问题,为什么不把菜单栏创建过程放在contextMenuEvent方法里//因为这意味着用户每单击鼠标一次,都会调用这个方法去创建一次菜单栏,而放在构造函数里就只用创建一次m_pMenu = new QMenu(this);//fromLocal8Bit()将本地编码转换为QStringQAction* pAc1 = new QAction(QString::fromLocal8Bit("结束绘制"), this);//使用setShortcut方法为该QAction对象添加了快捷键"Ctrl+E"pAc1->setShortcut(QKeySequence("Ctrl+E")); //添加快捷键QAction* pAc2 = new QAction(QString::fromLocal8Bit("清除"), this);pAc2->setShortcut(QKeySequence("Ctrl+D"));// 这是个假动作,为了让菜单消失,且不影响绘制路径QAction* pAc3 = new QAction(QString::fromLocal8Bit("退出菜单"), this);m_pMenu->addAction(pAc1);m_pMenu->addAction(pAc2);m_pMenu->addAction(pAc3);m_pMenu->setStyleSheet("QMenu{font:18px;}");connect(pAc1, &QAction::triggered, [=] {ui->graphicsPainter->endDraw();});connect(pAc2, &QAction::triggered, [=] {ui->graphicsPainter->clearPath();});
}MainWindow::~MainWindow()
{delete ui;
}// 右键菜单
void MainWindow::contextMenuEvent(QContextMenuEvent* event)
{//获取鼠标当前位置,并把菜单栏move到该位置m_pMenu->move(cursor().pos());//然后进行显示m_pMenu->show();//如果没有这一步,那么单击鼠标右键,菜单栏会在桌面左上角(0,0)位置处显示
}

3、绘图功能实现 

 3.1、主要的五个类
    void paintEvent(QPaintEvent *) override;void mousePressEvent(QMouseEvent *e) override;       //按下void mouseMoveEvent(QMouseEvent *e) override;        //移动void mouseReleaseEvent(QMouseEvent *e) override;     //松开void mouseDoubleClickEvent(QMouseEvent *event) override;     //双击
3.2、鼠标跟踪函数

首先是构造函数里的鼠标跟踪函数,这个是必须要有的 

MyPainterWidget::MyPainterWidget(QWidget *parent) : QWidget(parent)
{// 设置鼠标跟踪,以便在鼠标移动时能够及时更新绘图setMouseTracking(true);//清空点列表pointList.clear();
}
3.3、鼠标按下前清空画板

每次鼠标按下绘画之前,都会先清空画板 

// 按下
void MyPainterWidget::mousePressEvent(QMouseEvent *e)
{if (e->button() == Qt::LeftButton){if(!m_bStartDraw){//每次按下后开始绘画之前,也就是m_bStartDraw设置为真之前//都应该先清空整个画板pointList.clear();m_bStartDraw = true;}}   
}
3.4、重写绘图事件

 其次是重写绘图事件,这里创建了一个vector容器来存储线段,用for循环实现相邻点之间的线段连接,最后再绘制所有线段,注意,是先把需要绘制的线段都放在vector容器里,最后一起绘制的

void MyPainterWidget::paintEvent(QPaintEvent *)
{//在窗口上绘制内容QPainter painter(this);painter.setPen(QColor(255,0,0)); // 设置画笔颜色为红色QVector<QLineF> lines; // 存储线段的容器for(int i = 0; i < pointList.size()-1; i++){//在点列表pointList中的相邻点之间创建线段QLineF line(QPointF(pointList[i].x(), pointList[i].y()), QPointF(pointList[i+1].x(), pointList[i+1].y()));lines.push_back(line); // 将线段添加到容器中}如果正在绘制状态,则绘制鼠标移动到的点与最后一个点之间的线段if (m_bStartDraw){int size = pointList.size();if (bMove && size > 0){QLineF line(QPointF(pointList[pointList.size() - 1].x(), pointList[pointList.size() - 1].y()),movePoint);lines.push_back(line); // 将线段添加到容器中}}painter.drawLines(lines); // 绘制所有线段
}

可能会有个疑惑?最后才绘制所有线段,为什么实际操作的过程中感觉是一条一条绘制的

因为每次鼠标按下和松开的时候,都是进行了update()的,也就是说在每次添加新线段到lines容器时,都会触发paintEvent的调用,从而实时更新界面。这样就会看到每次添加新线段时都会立即绘制出来,就有一种逐条绘制的感觉。

// 移动
void MyPainterWidget::mouseMoveEvent(QMouseEvent *e)
{if(m_bStartDraw){movePoint = e->pos(); // 更新移动点的位置this->update(); // 更新绘图// 先刷新再设为true, 防止第一点和(0,0)连在一块bMove = true;}
}
// 松开
void MyPainterWidget::mouseReleaseEvent(QMouseEvent *e)
{if (e->button() == Qt::LeftButton){// 鼠标松开后,将点添加到路径中,并更新绘图if (m_bStartDraw){// 鼠标松开后的点需要添加到路径中pointList.push_back(QPointF(e->x(), e->y()));bMove = false;this->update();}}
}

以上就是Qt里移动鼠标手动绘制任意多边形的简单介绍

都看到这里了,点个赞再走呗朋友~

加油吧,预祝大家变得更强!


http://www.ppmy.cn/news/1443544.html

相关文章

如何免费生成文本二维码?文字生成二维码的方法

随着信息技术的不断发展&#xff0c;文本二维码作为一种简便、高效的信息分享方式&#xff0c;受到了越来越多人的关注和应用。文本二维码是将文本信息编码成二维码的形式&#xff0c;通过扫描二维码即可快速获取文本内容&#xff0c;为信息分享和传播提供了全新的可能性。 便…

Internet Download Manager v6.42.7(IDM)一款功能强大的下载工具!

软件介绍 Internet Download Manager &#xff08;IDM&#xff09;是一个将下载速度提高多达5倍&#xff0c;恢复和提高下载进度的工具。能将由于连接丢失、网络问题、计算机关闭或意外断电而中断的下载全面恢复重新启动。凭借着下载计算的速度优势在外媒网站中均受好评&#…

前端项目学习记录1:svg图标的封装与使用

1.下载svg依赖 pnpm i vite-plugin-svg-icons -D 还有一个&#xff0c;下面的不安装可能会报错 pnpm i fast-glob -D 2.vite.config.ts配置 import { defineConfig } from vite import vue from vitejs/plugin-vue import path from "path"; //引入svg需要用到的…

数据分析:扩增子分析(qiime2平台全流程分析)

Amplicon sequencing analysis pipeline through qiime2 platform qiime2是扩增子数据分析的最佳平台之一&#xff0c;其提供了大量从原始data到统计分析的插件&#xff0c;尤其是它的可重复分析且可扩展插件的理念使得其成为扩增子分析首选的平台。 Platform qiime2是扩增子…

SpringMVC基础篇(三)

文章目录 1.SpringMVC映射请求数据1.获取请求头信息1.VoterHandler.java2.request_parameter.jsp3.结果展示 2.自动封装javabean1.需求分析2.应用实例1.Master.java2.Pet.java3.后端接口4.结果展示 3.底层机制 3.调用servlet-api1.基本说明2.代码实例1.接口2.结果展示 3.注意事…

Vue中使用iconfont-阿里巴巴矢量图标库

一、简介 阿里巴巴矢量图标库&#xff08;Iconfont&#xff09;是一个由阿里巴巴提供的矢量图标库&#xff0c;包含了大量的矢量图标&#xff0c;可以供设计师和开发者在线浏览和下载使用。这个库的图标都是由用户上传和分享的&#xff0c;因此种类非常丰富&#xff0c;几乎涵…

【ChatGPT】AI评论家,适合点评论文和文章的Prompt模

原文&#xff1a;【ChatGPT】AI评论家&#xff0c;适合点评论文和文章的Prompt模板 - 知乎 总结了一个提示词模板提供给大家&#xff0c;适合的场景&#xff1a; 1. 家长辅导孩子写作 2. 老师给学生的作文打分 3. 业余评论家点评文章 4. 小编审稿&#xff08;可以早下班了&…

软件工程物联网方向嵌入式系统复习笔记--嵌入式系统基础

1 嵌入式系统基础 1.1 嵌入式系统基础 1.1.1 嵌入式系统概念 嵌入式系统一般定义 是指以应用为中心、以计算机技术为基础、软件硬件可裁剪、适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。 就像一般的计算机系统包括软件和硬件一样&#xff0c;…