《十三》QT绘图原理双缓冲机制

一、原理与设计

        所谓双缓冲机制,是指在绘制控件时,首先将要绘制的内容绘制在一个图片中,再将图片一次性地绘制到控件上。在早期的 Qt 版本中,若直接在控件上进行绘制工作,则在控件重绘时会产生闪烁地现象,控件重绘频繁时,闪烁尤为明显。双缓冲机制可以有效地消除这种闪烁现象。自 Qt5 版本之后,QWidget 控件已经能够自动处理闪烁的问题。因此,在控件上直接绘图时,不用再操心显示的闪烁问题,但双缓冲机制在很多场合仍然有其用武之地。当所需绘制的内容较复杂并需要频繁刷新,或者每次只需要刷新整个控件的一小部分时,仍应尽量采用双缓冲机制。

        实现一个简单的绘图工具,可以选择线型、线宽、颜色等基本要素。QMainWindow 对象作为主窗口,QToolBar 对象作为工具栏,QWidget 对象作为主窗口的中央窗体,也就是绘图区。通过响应鼠标事件进行绘图,而这是在绘图区窗体完成的,所以首先实现此窗体 DrawWidget 对鼠标事件进行重定义;然后实现可以选择线型、线宽及颜色等基本要素的主窗口。

二、绘图区的实现 

        DrawWidget 类继承自 QWidget 类,在类声明中对鼠标事件 mousePressEvent() 和 mouseMoveEvent(),重绘事件 paintEvent()、尺寸变化事件 resizeEvent() 进行了重定义。setStyle()、setWidth() 及 setColor() 函数主要用于为主窗口传递各种与绘图有关的参数。

(1)DrawWidget 构造函数完成对窗体参数及部分功能的初始化工作,具体代码如下:

DrawWidget::DrawWidget(QWidget *parent) : QWidget(parent)
{
    setAutoFillBackground(true);    //对窗体背景色的设置
    setPalette(QPalette(Qt::white));
    pix = new QPixmap(size());      //此QPixmap对象用于准备随时接收绘制的内容
    pix->fill(Qt::white);           //填充背景色为白色
    setMinimumSize(600, 400);       //设置绘制区窗体的最小尺寸
}

 (2)setStyle() 函数接收主窗口传来的线型风格参数,setWidth() 函数接收主窗口传来的线宽参数值,setColor() 函数接收主窗口传来的画笔颜色值。具体代码如下:

void DrawWidget::setStyle(int s)
{
    style = s;
}

void DrawWidget::setWidth(int w)
{
    weight = w;
}

void DrawWidget::setColor(QColor c)
{
    color = c;
}

(3)重定义鼠标按下事件 mousePressEvent(),在按下鼠标按键时,记录当前的鼠标位置值 startPos,具体代码如下:

void DrawWidget::mousePressEvent(QMouseEvent *e)
{
    startPos = e->pos();
}

(4)重定义鼠标移动事件 mouseMoveEvent() ,鼠标移动事件在默认情况下,在鼠标按键被按下的同时拖拽鼠标时被触发。
        QWidget 的 mouseTracking 属性指示窗体是否追踪鼠标,默认为 false(不追踪),即在至少有一个鼠标按键被按下的前提下移动鼠标才触发 mouseMoveEvent() 事件,可以通过 setMouseTracking(bool enable) 方法对该属性值进行设置。如果设置为追踪,则无论鼠标按键是否被按下,只要鼠标移动,就会触发 mouseMoveEvent() 事件。在此事件处理函数中,完成向 QPixmap 对象中绘图的工作。具体代码如下:

void DrawWidget::mouseMoveEvent(QMouseEvent *e)
{
    QPainter *painter = new QPainter();     //新建一个QPainter对象
    QPen pen;                               //新建一个QPen对象
    pen.setStyle((Qt::PenStyle)style);      //(a)
    pen.setWidth(weight);                   //设置画笔的线宽值
    pen.setColor(color);                    //设置画笔的颜色
    painter->begin(pix);                    //(b)
    painter->setPen(pen);                   //将QPen对象应用到绘制对象中
    //绘制从startPos到鼠标当前位置的直线
    painter->drawLine(startPos, e->pos());
    painter->end();
    startPos = e->pos();                    //更新鼠标的当前位置,为下次绘制做准备
    update();                               //重绘绘制区窗体
}

(a)设置画笔的线型,style 表示当前选择的线型是 Qt::PenStyle 枚举数据中的第几个元素。
(b)以 QPixmap 对象为 QPaintDevice 参数绘制。在构造一个 QPainter 对象时,就立即开始对绘画设备进行绘制。此构造 QPainter 对象是短时期的,如应定义在 QWidget::PaintEvent() 中,并只能调用一次。此构造函数调用开始于 begin() 函数,并且在 QPainter 的析构函数中自动调用 end() 函数。由于当一个 QPainter 对象的初始化失败时构造函数不能提供反馈信息,所以在绘制外部设备时应使用 begin() 和 end() 函数,如打印机等外部设备。
(5)重绘函数 paintEvent() 完成绘制区窗体的更新工作,只需调用 drawPixmap() 函数将用于接收图形绘制的 QPixmap 对象绘制在绘制区窗体控件上即可。具体代码如下:

void DrawWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.drawPixmap(QPoint(0, 0), *pix);
}

(6)调整绘制区大小函数 resizeEvent() ,当窗体的大小发生改变时,效果看起来虽然像是绘制区大小改变了,但实际能够进行绘制的区域仍然没有改变。因为绘图的大小并没有改变,还是原来绘制区窗口的大小,所以在窗体尺寸变化时应及时调整用于绘制的 QPixmap 对象的大小。具体代码如下:

void DrawWidget::resizeEvent(QResizeEvent *event)
{
    if(height() > pix->height() || width() > pix->width())  //(a)
    {
        QPixmap *newPix = new QPixmap(size());              //创建一个新的QPixmap对象
        newPix->fill(Qt::white);                            //填充新QPixmap对象newPix的颜色为白色背景
        QPainter p(newPix);
        p.drawPixmap(QPoint(0, 0), *pix);                   //在newPix中绘制原pix中的内容
        pix = newPix;                                       //将newPix赋值给pix作为新的绘制图形接收对象
    }
    QWidget::resizeEvent(event);                            //完成其余工作
}

(a)判断改变后的窗体长或宽是否大于原窗体的长和宽。若大于则进行相应的调整,否则直接调用 QWidget 的 resizeEvent() 函数返回。
(7)clear() 函数完成绘制区的清除工作,只需要调用一个新的、干净的 QPixmap 对象来代替 pix ,并调用 update() 函数重绘即可。具体代码如下:

void DrawWidget::clear()
{
    QPixmap *clearPix = new QPixmap(size());
    clearPix->fill(Qt::white);
    pix = clearPix;
    update();
}

三、主窗口的实现

        主窗口类 MainWindow 继承自 QMainWindow 类,只包含一个工具栏和一个中央窗体。首先,声明一个构造函数、一个用于创建工具栏的函数 createToolBar()、一个用于进行选择线型风格的槽函数 showStyle() 和一个用于进行颜色选择的槽函数 showColor()。然后,声明一个 DrawWidget 类对象作为主窗口的私有变量,以及声明代表线型风格、线宽选择、颜色选择及清除按钮的私有变量。

(1)MainWindow 类的构造函数完成初始化工作,具体代码如下:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    drawWidget = new DrawWidget();  //新建一个DrawWidget对象
    setCentralWidget(drawWidget);   //新建的DrawWidget对象作为主窗口的中央窗体
    createToolBar();                //实现一个工具栏
    setMinimumSize(600, 400);       //设置主窗口的最小尺寸
    showStyle();                    //初始化线型,设置控件中的当前值作为初始值
    drawWidget->setWidth(widthSpinBox->value());    //初始化线宽
    drawWidget->setColor(Qt::black);                //初始化颜色
}

(2)createToolBar() 函数完成工具栏创建,具体代码如下:

void MainWindow::createToolBar()
{
    QToolBar *toolBar = addToolBar("Tool");            //为主窗口新建一个工具栏对象
    styleLabel = new QLabel(QObject::tr("线型风格:"));  //创建线型风格选择控件
    styleComboBox = new QComboBox();
    styleComboBox->addItem(QObject::tr("SolidLine"), static_cast<int>(Qt::SolidLine));
    styleComboBox->addItem(QObject::tr("DashLine"), static_cast<int>(Qt::DashLine));
    styleComboBox->addItem(QObject::tr("DotLine"), static_cast<int>(Qt::DotLine));
    styleComboBox->addItem(QObject::tr("DashDotLine"), static_cast<int>(Qt::DashDotLine));
    styleComboBox->addItem(QObject::tr("DashDotDotLine"), static_cast<int>(Qt::DashDotDotLine));
    connect(styleComboBox, SIGNAL(activated(int)), this, SLOT(showStyle()));

    widthLabel = new QLabel(QObject::tr("线宽:"));      //创建线宽选择控件
    widthSpinBox = new QSpinBox();
    connect(widthSpinBox, SIGNAL(valueChanged(int)), drawWidget, SLOT(setWidth(int)));

    colorBtn = new QToolButton();                       //创建颜色选择控件
    QPixmap pixmap(20, 20);
    pixmap.fill(Qt::black);
    colorBtn->setIcon(QIcon(pixmap));
    connect(colorBtn, SIGNAL(clicked()), this, SLOT(showColor()));

    clearBtn = new QToolButton();                       //创建清除按钮
    clearBtn->setText(QObject::tr("清除"));
    connect(clearBtn, SIGNAL(clicked()), drawWidget, SLOT(clear()));

    toolBar->addWidget(styleLabel);
    toolBar->addWidget(styleComboBox);
    toolBar->addWidget(widthLabel);
    toolBar->addWidget(widthSpinBox);
    toolBar->addWidget(colorBtn);
    toolBar->addWidget(clearBtn);
}

(3)改变线型参数的槽函数 showStyle(),通过调用 DrawWidget 类的 setStyle() 函数将当前线型选择控件中的线型参数传给绘制区;设置画笔颜色的槽函数 showColor(),通过调用 DrawWidget 类的 setColor() 函数将用户在标准颜色对话框中选择的颜色值传给绘制区。具体代码如下:

void MainWindow::showStyle()
{
    drawWidget->setStyle(styleComboBox->itemData(styleComboBox->currentIndex(), Qt::UserRole).toInt());
}

void MainWindow::showColor()
{
    QColor color = QColorDialog::getColor(static_cast<int>(Qt::black), this);
    //使用颜色对话框QColorDialog获得一个颜色值
    if(color.isValid())
    {
        //将新选择的颜色传给绘制区,用于改变画笔的颜色值
        drawWidget->setColor(color);
        QPixmap p(20, 20);
        p.fill(color);
    }
}

运行结果:

 

感谢阅读!!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/590522.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

零基础学习数据库SQL语句之定义数据库对象的DDL语句

DDL语句 DDL Date Definition Language 数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库&#xff0c;表&#xff0c;字段&#xff09; 基本操作 数据库操作 查询所有数据库 SHOW DATEBASES查询当前数据库 SELECT DATEBASE() 创建 CREATE DATEBASE [IF …

利用大语言模型(KIMI)构建智能产品的控制信息模型

数字化的核心是数字化建模&#xff0c;为一个事物构建数字模型是一项十分复杂的工作。不同的应用场景&#xff0c;对事物的关注重点的不同的。例如&#xff0c;对于一个智能传感器而言&#xff0c;从商业的角度看&#xff0c;产品的信息模型中应该包括产品的类型&#xff0c;名…

Mysql的关联查询以及语句

一、mysql的连接查询 1、等值连接 这里是三张表的等值连接 select rp.role_id,rp.permission_id from role_permission rp, role r, permission p where rp.role_idr.id and rp.permission_idp.id 2、内连接&#xff1a; 角色&#xff1a;系统管理员 是否拥有权限&#xf…

DHCPv4_CLIENT_ALLOCATING_03: 发送DHCPREQUEST - 必须包含‘服务器标识符‘

测试目的&#xff1a; 验证客户端发送的DHCPREQUEST消息中是否包含“服务器标识符”选项&#xff0c;以指示它选择的服务器。 描述&#xff1a; 本测试用例旨在确保DHCP客户端在广播DHCPREQUEST消息时&#xff0c;必须包含“服务器标识符”选项。该选项用于指明客户端选择了…

2024-5-1我把QQ群聊天记录分析工具重写了一下

【下载地址】 https://www.lanzoub.com/b00rn0g47e 密码:9hww 【项目背景】 2020年我用Tkinter写过一个QQ群聊天记录分析的工具exe&#xff0c;后续也写过一个纯JS前端的版本&#xff0c;前阵子有个用户反馈不能用了&#xff0c;顺便看能不能加入一个分析关键词的功能&…

第76天:WAF攻防-信息收集识别被动探针代理池仿指纹白名单

目录 基础知识 案例一: 信息收集-被动扫描-黑暗引擎&三方接口 案例二: 信息收集-目录扫描-Python 代理加载脚本 案例三: 信息收集-爬虫扫描-Awvs&Xray&Goby内置 基础知识 什么是 WAF &#xff1f; Web Application Firewall &#xff08; web 应用防火墙&am…

jvm垃圾回收机制介绍

JVM&#xff08;Java虚拟机&#xff09;是Java程序的运行环境&#xff0c;它负责执行字节码文件。JVM的工作原理主要包括以下几个部分&#xff1a;类加载器、执行引擎、垃圾收集器和内存管理。类加载器负责加载字节码文件并将其转换成Java平台上的机器码&#xff0c;执行引擎负…

三维图形学知识分享---求平面与模型相交线

在CGAL&#xff08;Computational Geometry Algorithms Library&#xff09;中&#xff0c;Polygon_mesh_processing模块提供了用于处理多边形网格数据结构的功能。其中&#xff0c;surface_intersection函数是用来计算模型的表面相交线的工具。 CGAL_Mesh mesh_orcl;std::vect…

容器组_生命周期

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d8;相关专栏Rust初阶教程、go语言基础系列、spring教程等&#xff0c;大家有兴趣的可以看一看 &#x1f4d9;Jav…

张大哥笔记:如何选择项目和未来发展方向,实现10倍增长

在过去的10年里&#xff0c;我加入了众多社群&#xff0c;接触了不少行业精英&#xff0c;还参与了各式各样的交流圈。这段时间&#xff0c;我一直在考虑一个问题&#xff1a;未来的发展道路究竟在何方&#xff1f;起初我认为只有那些具有长期积累价值的事业才有真正的前景&…

傅里叶变换理论2

1. 逆变换 2. 时间域和频率域 3. 波的基本单位

C语言例题29:在屏幕上显示一个等腰三角形

#include <stdio.h>void main() {int i, j;int x;printf("输入等腰三角形行数&#xff1a;");scanf("%d", &x);for (i 1; i < x; i) {for (j i; j < x; j) {printf(" "); //输出空格占位}for (j 1; j < 2 * i; j) {printf…

【计算机毕业设计】基于SpringBoot+Vue企业车辆管理系统设计与实现

目录 一、项目介绍 二、项目主要技术 三、系统体系结构 四、系统实现 五、实现代码 一、项目介绍 本系统采用Mysql数据库和java语言&#xff0c;SpringBoot框架进行开发而成&#xff0c;极大程度上的保证了系统的稳定性。管理人员可以管理个人中心、驾驶员管理、用户管…

FR-TSN4206获得“时间敏感网络产业链名录计划”测试认证证书,TSN交换机助力智能工业发展

TSN技术&#xff0c;即时间敏感网络技术&#xff0c;已成为智能工业、自动驾驶等领域的核心。它通过时钟同步、数据调度等功能&#xff0c;确保低延迟、高可靠性的数据传输。 为推动TSN技术在我国的发展&#xff0c;工业互联网产业联盟联合多家单位启动了“时间敏感网络产业链名…

AI制作《曼达洛人4》电影宣传片

AI制作《曼达洛人》电影宣传片 Bounty hunters, legends, and a galaxy in turmoil. The Mandalorian rises. 赏金猎人、传奇和混乱的星系。曼达洛人崛起。 In the shadows of the Outer Rim, a lone warrior walks the path of honor. 在外围边缘的阴影中&#xff0c;一名独…

esp32-cam 2. python opencv 拉取摄像头内容

0. 环境 - win10 python3 - pycharm - esp32-cam http://192.168.4.1 1. 创建工程 File -> Create Project -> -> Location: E:\Workspaces\PycharmProjects\esp32cam_opencv -> Create 2. opencv hello 2.1 添加脚本 File -> New -> Python f…

【跟马少平老师学AI】-【神经网络是怎么实现的】(五)梯度消失问题

一句话归纳&#xff1a; 1&#xff09;用sigmoid激活函数时&#xff0c;BP算法更新公式为&#xff1a; 用sigmoid函数&#xff0c;O取值为0~1&#xff0c;O(1-O)最大值为0.25&#xff0c;若神经网络层数多&#xff0c;则会造成更新项趋近于0&#xff0c;称为梯度消失。 2&#…

我使用Python开发网站的3个主要框架库,值得推荐

虽然Python不是主打后端开发的语言&#xff0c;但目前市场仍有很多大公司在用Python开发网站和软件&#xff0c;比如Youtube、Reddit、Dropbox、Douban等。 目前Python生态有几个受欢迎的后端框架&#xff0c;主要是Django、Flask、FastAPI&#xff0c;咱们如果做Python后端开…

【C++】1.贪心算法:零钱兑换的奇妙之旅

欢迎来CILMY23的博客 本篇主题为 贪心算法&#xff1a;零钱兑换的奇妙之旅 个人主页&#xff1a;CILMY23-CSDN博客 个人专栏&#xff1a; Python | C | C语言 | 数据结构与算法 上一篇C博客&#xff1a;掌握C函数重载和引用开启代码优化的新篇章 感谢观看&#xff0c;支…

vue2项目webpack3.x打包文件分割优化加载

vue2项目webpack3.x打包文件分割优化加载 0. 项目目录和依赖信息1. 开启 gzip&#xff08;建议&#xff09;2. vue2项目配置懒加载&#xff08;建议&#xff09;3. 拆分 vendor 包注意&#xff1a;webpack3使用CommonsChunkPlugin实现 本文使用 3 种方案进行叠加优化 优先级按以…