《Qt6开发及实例》6-3 双缓冲机制

news2024/9/29 1:28:33

目录

一、原理与设计

1.1 原理

1.2 设计

二、绘图区的实现

2.1 鼠标移动事件

2.2 重绘函数&调整大小函数&清除屏幕

三、主窗口的实现

3.1 代码


一、原理与设计

1.1 原理

双缓冲就是在绘制控件时,将内容绘制在一个图片中,再将图片一次性地绘制到控件上。直接在控件上绘制会产生闪烁的现象,控件重绘频繁,闪烁更明显。QT6 版的 QWidget 控件已经能够自动处理闪烁,但是双缓冲仍有用武之地。当需要绘制的内容较复杂并需要频繁刷新,或者每次只需要刷新整个控件的一小部分时,仍尽量采用双缓冲机制

1.2 设计

界面

 

 两个类

ad158fdc1dff4222818e0f880b31d595.jpg

建立项目 

aa25f7a7a62f481d94797f889fe9f901.png

240d76e61ac04586a7bc2425e608494d.png

二、绘图区的实现

2.1 鼠标移动事件

drawwidget.h

#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H

#include <QWidget>
#include <QtGui>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QResizeEvent>
#include <QColor>
#include <QPixmap>
#include <QPoint>
#include <QPainter>
#include <QPalette>

class DrawWidget : public QWidget
{
    Q_OBJECT
public:
    explicit DrawWidget(QWidget *parent = 0);
    void mousePressEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);
    void paintEvent(QPaintEvent *);
    void resizeEvent(QResizeEvent *);

signals:

public slots:
    void setStyle(int);
    void setWidth(int);
    void setColor(QColor);
    void clear();

private:
    QPixmap *pix;
    QPoint startPos;
    QPoint endPos;
    int style;
    int weight;
    QColor color;
};

#endif // DRAWWIDGET_H

 drawwidget.cpp

#include "drawwidget.h"
#include <QPen>

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

// 记录鼠标点击的位置
void DrawWidget::mousePressEvent(QMouseEvent *e)
{
    startPos = e->pos();
}

// QWidget的mouseTracking属性指示窗体是否追踪,默认为false(不追踪),即只有按下鼠标左键后移动
// 才触发mouseMoveEvent事件。我们可以使用setMouseTracking(bool enable)方法对该属性值进行
// 设置,如果为true(追踪),那鼠标无论是否点击都会触发mouseMoveEvent事件。在此函数中,我们完成
// 向QPixmap对象中绘图的工作
void DrawWidget::mouseMoveEvent(QMouseEvent *e)
{
    QPainter *painter = new QPainter;
    QPen pen;
    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();                           //重新绘制区窗体
}

void DrawWidget::paintEvent(QPaintEvent *)
{

}

void DrawWidget::resizeEvent(QResizeEvent *)
{

}

// 接收主窗口传来的线型风格参数
void DrawWidget::setStyle(int s)
{
    style = s;
}

// 接收主窗口传来的线宽参数值
void DrawWidget::setWidth(int w)
{
    weight = w;
}

// 接收主窗口传来的画笔颜色值
void DrawWidget::setColor(QColor c)
{
    color = c;
}

void DrawWidget::clear()
{

}

(a) pen.setStyle((Qt::PenStyle)style);

上面的函数是设置画笔的线宽。style 是类的成员变量 (int style;),表示当前选择的线型是 Qt::PenStyle 枚举数据中的第几个元素,其他函数可以设置这个值

(b) painter->begin(pix)、painter->end()

Qt 的绘图操作,是使用 QPainter 在 paintevent() 函数中进行的,所有绘图操作都要放进函数 paintevent() 中。

在实际编程中,例如编写计算机图形学作业——编写简易绘图库时,为了封装便利,需要将绘图操作从 paintevent() 中外提。这时候 QPixmap 便派上用场了:在 paintevent() 之外,将所有绘图操作绘制在 QPixmap上,而在paintevent() 之内,仅绘制 QPixmap 即可。

(11条消息) Qt5 绘图 - 利用 QPixmap 和 QPainter 实现在 paintevent() 函数外绘图_sigmarising的博客-CSDN博客_qpainter qpixmaphttps://blog.csdn.net/sigmarising/article/details/79920380

2.2 重绘函数&调整大小函数&清除屏幕

重绘函数 paintEvent() 完成绘制区窗体的更新工作,只需要调用 drawPixmap() 函数将用于接收图形绘制的 QPixmap 对象绘制在绘图区窗体控件上

调整绘制区大小后执行 resizeEvent()

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

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) if(height() > pix->height() || width() > pix->width())

判断改变后的窗体长或宽是否大于原窗体的长或宽。若大于则进行相应的调整,否则直接调用 QWidget 的 resizeEvent() 函数返回 

清除屏幕

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

三、主窗口的实现

3.1 代码

mainwindow.h 

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QToolButton>
#include <QLabel>
#include <QComboBox>
#include <QSpinBox>
#include "drawwidget.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void createToolBar();

public slots:
    void ShowStyle();
    void ShowColor();

private:
    DrawWidget *drawWidget;
    QLabel *styleLabel;
    QComboBox *styleComboBox;
    QLabel *widthLabel;
    QSpinBox *widthSpinBox;
    QToolButton *colorBtn;
    QToolButton *clearBtn;
};
#endif // MAINWINDOW_H

 mainwindow.cpp

#include "mainwindow.h"
#include <QToolBar>
#include <QColorDialog>

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);              //初始化颜色

}

MainWindow::~MainWindow()
{
}

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

    widthLabel = new QLabel(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(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);
}

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);
        colorBtn->setIcon(QIcon(p));  //更新颜色选择按钮上的颜色显示
    }
}

程序存在问题一个是中文字体是乱码,我们可以在 main 里面修改字体,但是我发现界面里面的字还是乱码,这个先不解决。

程序还存在一个问题就是其他风格的线弄不出来,因为每次隔很短的距离就要绘制一次。我不清楚作者在用 QT6 的时候能否显示其他风格的线

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全局状态管理插件 Vuex 介绍及使用

文章目录Vuex 是什么简介Vuex 如何存储数据Vuex 核心概念单向数据流StateGetterMutationActionModuleVuex 使用实例总结Vuex 是什么 简介 官方解释&#xff1a;Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态&#xff0c;并以…

Linux环境下(CentOS 7)安装Java(JDK8)

Linux环境下(CentOS 7)安装Java(JDK8) 一、安装教程 1.1 首先&#xff0c;进入oracle官网下载jdk8的安装包&#xff0c;下载地址如下&#xff0c;这里以 jdk-8u121-linux-x64.tar.gz安装包为例。 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-21…

2023美赛(MCM/ICM)数据汇总

2023美赛已经开始了十二个小时了&#xff0c;对于本次比赛&#xff0c;赛题 难度都不大。主要就是收集数据的问题 &#xff0c;为了更好地帮助大家选题&#xff0c;我们将 我们今天已经收集到的数据进行汇总&#xff0c;分享。其中&#xff0c;首先是A、E题目都提及的天气数据&…

C语言学习_DAY_4_判断语句if_else和分支语句switch_case【C语言学习笔记】

高质量博主&#xff0c;点个关注不迷路&#x1f338;&#x1f338;&#x1f338;&#xff01; 目录 1.案例引入 2.if判断语句的语法与注意事项 3.switch多分支语句的语法与注意事项 前言: 书接上回&#xff0c;我们已经学习了所有的数据类型、运算符&#xff0c;并且可以书写…

基于matlab/simulink的风光柴储微电网仿真建模

模型是基于之前的风光储系统上增加一部分柴油发电机系统&#xff0c;后面文章我会单独介绍柴油机这一部分&#xff0c;主要应用在船舶电力系统&#xff0c;一般小型电网黑启动也会用到。 风光柴储微电网发电系统是一种小型发电系统&#xff0c;同时具备并网运行和孤岛运行的功能…

气敏电阻的原理,结构,分类及应用场景总结

🏡《总目录》 目录 1,概述2,结构3,工作原理4,分类4.1,加热方式分类4.2,材料分类4.3,氧化还原分类5,应用场景6,总结1,概述 气敏电阻是指电阻值随着环境中某种气体的浓度变化而变化的电阻,本文对其工作原理,结构,分类和应用场景进行总结。 2,结构 气敏电阻由防爆…

Leetcode(每日一题)——1237. 找出给定方程的正整数解

摘要 1237. 找出给定方程的正整数解 一、暴力求解 根据题目给出的x和y的取值范围&#xff0c;枚举所有的 x,y数对&#xff0c;保存满足f(x,y)z的数对&#xff0c;最后返回结果。 /*** description 使用的暴力法 直接遍历符合的就添加到结果中* param: customfunction* param…

能不能做好性能测试,要看你有没有性能测试思维

获取性能需求 01、用户数信息 1、调查系统当前和未来使用的用户数 系统用户数 本系统目前注册的用户数&#xff0c;注册用户数并不代表他会每天并且无时无刻的使用着。 在线用户数 同时在线对系统进行操作的用户数量&#xff08;相当于混合场景&#xff09; 并发用户数 …

第9天-商品服务(电商核心概念,属性分组开发及分类和品牌的级联更新)

1.电商核心概念 1.1.SPU与SKU SPU&#xff1a;Standard Product Unit&#xff08;标准化产品单元&#xff09; 是商品信息聚合的最小单位&#xff0c;是一组可复用、易检索的标准化信息的集合&#xff0c;该集合描述了一个 产品的特性。 决定商品属性的值 SKU&#xff1a;Stock…

第43天| 123.买卖股票的最佳时机III、 188.买卖股票的最佳时机IV

1.题目链接&#xff1a;123. 买卖股票的最佳时机 III 题目描述&#xff1a; 给定一个数组&#xff0c;它的第 i 个元素是一支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。 注意&#xff1a;你不能同时参与多笔交易&#…

基于深度神经网络的3D模型合成【Transformer vs. CNN】

本文介绍用于3D模型合成的transformer网络与深度卷积网络。 推荐&#xff1a;使用 NSDT场景设计器 快速搭建 3D场景。 1、概述 从单一视角合成 3D 数据是一种基本的人类视觉功能&#xff0c;这对计算机视觉算法来说极具挑战性&#xff0c;这是一个共识。 但在 3D 传感器&#…

Fedora Linux未来五年规划

Fedora 委员会一直致力于起草战略计划&#xff0c;以帮助 Fedora Linux 更好地发展。近日 Fedora 委员会公布了一份 “《未来五年的 Fedora Linux 》” 战略计划草案&#xff0c;这份草案里面包含了他们的雄心壮志&#xff1a;每周将 Fedora 的活跃贡献者人数增加一倍。 Fedora…

如何获取docpelx求解目标函数后的数据;在解决目标优化问题之后,如何获取相关数据;获取决策变量的具体数值

获取优化问题的自变量取值和目标函数取值 说明通过 mdl.integer_var() 定义的决策变量&#xff0c;获取求解值决策变量获取目标函数取值获取具体代码&#xff1a;通过 mdl.continuous_var_list() 定义的决策变量&#xff0c;获取求解值具体代码说明 本次的代码环境是 python中…

【WEB安全】SQL注入挖掘

文章目录前言一、sql注入的分类注入漏洞存在位置二、漏洞挖掘Google语法疑似注入点手工挖掘批量挖取此类漏洞已知sql注入漏洞挖掘总结免责声明&#xff1a;前言 2021年OWASP发布漏洞威胁榜单&#xff0c;SQL注入从第一名下降到第三&#xff08;https://owasp.org/Top10/&#…

从0开始学python -34

Python3 输入和输出-2 读和写文件 open() 将会返回一个 file 对象&#xff0c;基本语法格式如下: open(filename, mode)filename&#xff1a;包含了你要访问的文件名称的字符串值。mode&#xff1a;决定了打开文件的模式&#xff1a;只读&#xff0c;写入&#xff0c;追加等。…

数据分析| Pandas200道练习题,使用Pandas连接MySQL数据库

文章目录使用Pandas连接数据库编码环境依赖包read_sql_query()的使用read_sql_table()的使用read_sql() 函数的使用to_sql()写入数据库的操作删除操作更新操作总结&#xff1a;使用Pandas连接数据库 通过pandas实现数据库的读&#xff0c;写操作时&#xff0c;首先需要进行数据…

kubernetes教程 -- k8s组件

k8s组件 maste节点 apiServer&#xff1a;资源操作的唯一入口&#xff0c;接受用户的输入&#xff0c;提供认证&#xff0c;控制访问等功能Scheduler&#xff1a;负责集群的资源调度&#xff0c;按照预定的调度策略将Pod调度到相应的node节点上controllerManager&#xff1a;负…

JVM学习篇剖析JVM类加载机制

1. 类加载运行全过程 当我们用java命令运行某个类的main函数启动程序时&#xff0c;首先需要通过类加载器把主类加载到JVM。 public class Math {private static int initData 6666;public static User user new User();private int compute() {int a 1;int b 3;int c (…

【vue后台管理系统】基于Vue+Element-UI+ECharts开发通用管理后台(下)

文章目录面包屑导航制作效果展示思路分析代码实现过程需求优化用户管理页面效果展示新增用户表单实现table组件编写分页功能编写搜索功能编写附&#xff1a;ES6的解构赋值数组模型的解构&#xff08;Array&#xff09;对象模型的解构&#xff08;Object&#xff09;面包屑导航制…

[Linux篇] Linux常见命令和权限

文章目录使用XShell登录Linux1.Linux常用基本命令&#xff1a;1.1 ls&#xff08;列出当前的目录下都有哪些文件和目录&#xff09;1.2 cd (change directory 切换目录)1.3 pwd&#xff08;查看当前目录的绝对路径&#xff09;1.4 touch&#xff08;创建文件&#xff09;1.5 ca…