QT:paintEvent、QPainter、QPaintDevice

news2025/2/25 19:52:59

paintEvent 介绍

在 Qt 编程中,paintEvent 是 QWidget 类中的一个非常重要的虚函数,用于处理绘图事件。当一个 QWidget 或其派生类的实例需要进行重绘操作时,Qt 会自动调用该控件的 paintEvent 函数。

触发时机

窗口首次显示:当一个窗口或控件第一次显示在屏幕上时,会触发 paintEvent 来绘制其初始外观。
窗口大小改变:当用户调整窗口大小,或者通过代码改变窗口大小时,为了适应新的尺寸,会触发 paintEvent 重新绘制内容。
窗口被遮挡后恢复显示:如果窗口被其他窗口遮挡,之后遮挡窗口移开,窗口需要重新绘制可见部分,此时会触发 paintEvent。
调用 update() 或 repaint() 方法:在代码中调用 update() 或 repaint() 方法可以手动触发 paintEvent。update() 会在 Qt 的事件循环中安排一次重绘,它会合并多个 update() 调用以避免不必要的重绘;而 repaint() 会立即触发重绘操作。

void QWidget::paintEvent(QPaintEvent *event);

QPaintEvent *event 是一个指向 QPaintEvent 对象的指针,该对象包含了与绘制事件相关的信息,例如需要重绘的区域(通过 event->rect() 可以获取)。

响应窗口大小变化

#include <QApplication>
#include <QWidget>
#include <QPainter>

class ResizableWidget : public QWidget {
public:
    ResizableWidget(QWidget *parent = nullptr) : QWidget(parent) {}

protected:
    void paintEvent(QPaintEvent *event) override {
        Q_UNUSED(event);
        QPainter painter(this);

        // 绘制一个与窗口大小相关的矩形
        int x = width() / 4;
        int y = height() / 4;
        int w = width() / 2;
        int h = height() / 2;
        painter.drawRect(x, y, w, h);
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    ResizableWidget widget;
    widget.setWindowTitle("Resizable Drawing");
    widget.resize(300, 300);
    widget.show();
    return app.exec();
}

定义了 ResizableWidget 类,重写 paintEvent 函数。
在 paintEvent 中,根据窗口的当前宽度和高度计算矩形的位置和大小,然后绘制矩形。这样当窗口大小改变时,矩形会自动调整以适应新的窗口尺寸。
在这里插入图片描述

QPainter

QPainter 是 Qt 框架中用于执行 2D 绘图操作的核心类。它提供了丰富的 API,允许开发者在各种 QPaintDevice(如画布、窗口、图像等)上绘制基本图形(如点、线、矩形、椭圆等)、文本、图像,还能应用渐变、变换等效果,从而实现复杂的图形和界面绘制。

QPainter 的主要特点和功能包括:

基本图形绘制:支持绘制点、线、矩形、椭圆、多边形等多种基本图形。
文本绘制:可以在指定位置绘制文本,并能设置字体、颜色、对齐方式等属性。
图像绘制:能够将图像绘制到指定位置,还可以进行缩放、旋转等操作。
渐变填充:支持线性渐变、径向渐变、锥形渐变等填充效果。
变换操作:如平移、旋转、缩放等,可对绘制的图形进行变换。
抗锯齿:可以开启抗锯齿功能,使绘制的图形边缘更加平滑。

绘制基本图形

#include <QApplication>
#include <QWidget>
#include <QPainter>

class DrawingWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        Q_UNUSED(event);
        QPainter painter(this);

        // 绘制直线
        painter.drawLine(20, 20, 200, 20);

        // 绘制矩形
        painter.drawRect(20, 40, 180, 100);

        // 绘制椭圆
        painter.drawEllipse(20, 160, 180, 100);
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    DrawingWidget widget;
    widget.setWindowTitle("Basic Shapes Drawing");
    widget.resize(300, 300);
    widget.show();
    return app.exec();
}

在这里插入图片描述

DrawingWidget 类继承自 QWidget,并重写了 paintEvent 方法,在该方法中进行绘图操作。
QPainter 对象 painter 用于实际的绘图,它以 this(即 DrawingWidget 实例)作为绘图设备。
drawLine 方法用于绘制直线,传入起点和终点的坐标。
drawRect 方法绘制矩形,参数分别为矩形左上角的坐标以及矩形的宽度和高度。
drawEllipse 方法绘制椭圆,参数与矩形类似。

绘制文本

#include <QApplication>
#include <QWidget>
#include <QPainter>

class TextDrawingWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        Q_UNUSED(event);
        QPainter painter(this);

        // 设置字体
        QFont font("Arial", 20);
        painter.setFont(font);

        // 设置文本颜色
        painter.setPen(Qt::blue);

        // 绘制文本
        painter.drawText(50, 100, "Hello, Qt!");
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    TextDrawingWidget widget;
    widget.setWindowTitle("Text Drawing");
    widget.resize(300, 300);
    widget.show();
    return app.exec();
}

创建 QFont 对象设置字体的名称和大小,并通过 setFont 方法将其应用到 QPainter 上。
setPen 方法设置文本的颜色为蓝色。
drawText 方法在指定位置绘制文本,第一个参数是文本左上角的横坐标,第二个参数是纵坐标,第三个参数是要绘制的文本内容。

在这里插入图片描述

变换操作

#include <QApplication>
#include <QWidget>
#include <QPainter>

class TransformationWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        Q_UNUSED(event);
        QPainter painter(this);

        // 保存当前的绘图状态
        painter.save();

        // 平移坐标系
        painter.translate(width() / 2, height() / 2);

        // 旋转坐标系
        painter.rotate(45);

        // 绘制旋转后的矩形
        painter.drawRect(-50, -25, 100, 50);

        // 恢复之前保存的绘图状态
        painter.restore();
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    TransformationWidget widget;
    widget.setWindowTitle("Transformation Drawing");
    widget.resize(300, 300);
    widget.show();
    return app.exec();
}

save 方法保存当前的绘图状态,包括坐标系、画笔、画刷等属性。
translate 方法将坐标系原点平移到窗口的中心。
rotate 方法将坐标系旋转 45 度。
绘制矩形时,使用的是平移和旋转后的坐标系。
restore 方法恢复之前保存的绘图状态,以便后续绘图不受影响。
在这里插入图片描述

QPaintDevice

QPaintDevice 是 Qt 中所有可以进行绘制操作的对象的基类,它为 QPainter 提供了一个绘图的目标设备抽象接口。也就是说,QPainter 可以在任何继承自 QPaintDevice 的对象上进行绘图操作,比如窗口、图像、打印机等。

主要特点

抽象性:QPaintDevice 是一个抽象基类,不能直接实例化,它定义了一些纯虚函数,这些函数由具体的子类来实现,从而实现不同类型设备的绘图功能。
多设备支持:Qt 提供了多个继承自 QPaintDevice 的子类,常见的有 QWidget(用于窗口和控件)、QImage(用于内存中的图像)、QPixmap(用于屏幕优化的图像)、QPrinter(用于打印机)等,这使得开发者可以在不同的设备上进行统一的绘图操作。
与 QPainter 协作:QPainter 是绘图的执行者,而 QPaintDevice 是绘图的目标,二者紧密协作完成绘图任务。在使用 QPainter 进行绘图时,需要在构造函数中传入一个 QPaintDevice 对象作为参数。

常用子类及用途

QWidget:用于创建窗口和各种用户界面控件,QWidget 的 paintEvent 函数中通常会使用 QPainter 在窗口上进行绘图。
QImage:可以在内存中创建和操作图像,它支持直接访问像素数据,适合进行图像处理和生成图像文件。
QPixmap:是一种专门为屏幕显示优化的图像表示,它可以高效地在屏幕上绘制,常用于快速显示图像。
QPrinter:用于将绘图内容输出到打印机,实现打印功能。

在 QWidget 上绘图

#include <QApplication>
#include <QWidget>
#include <QPainter>

class DrawingWidget : public QWidget {
public:
    DrawingWidget(QWidget *parent = nullptr) : QWidget(parent) {}

protected:
    void paintEvent(QPaintEvent *event) override {
        Q_UNUSED(event);
        QPainter painter(this);  // this 是 QWidget 对象,作为 QPaintDevice
        painter.setPen(Qt::blue);
        painter.drawLine(20, 20, 200, 20);
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    DrawingWidget widget;
    widget.setWindowTitle("Drawing on QWidget");
    widget.resize(300, 200);
    widget.show();
    return app.exec();
}

在这里插入图片描述
定义了一个自定义的 DrawingWidget 类,继承自 QWidget。
在 paintEvent 函数中,创建了一个 QPainter 对象,将 this(即 DrawingWidget 实例,它是 QWidget 类型,继承自 QPaintDevice)作为绘图设备。
使用 QPainter 的 setPen 方法设置画笔颜色为蓝色,然后调用 drawLine 方法在窗口上绘制一条直线

在 QImage 上绘图并保存为文件

在这里插入图片描述

#include <QApplication>
#include <QImage>
#include <QPainter>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 创建一个 QImage 对象作为绘图设备
    QImage image(200, 200, QImage::Format_RGB32);
    image.fill(Qt::white);  // 填充背景为白色

    QPainter painter(&image);  // 将 QImage 作为绘图设备
    painter.setPen(Qt::red);
    painter.drawRect(50, 50, 100, 100);
    painter.end();  // 结束绘图

    // 保存图像为文件
    image.save("output.png", "PNG");

    return app.exec();
}

创建了一个 QImage 对象,指定了图像的宽度、高度和像素格式。
使用 fill 方法将图像背景填充为白色。
创建 QPainter 对象,将 QImage 对象的指针作为绘图设备传入。
使用 QPainter 的 setPen 方法设置画笔颜色为红色,然后调用 drawRect 方法在图像上绘制一个矩形。
调用 end 方法结束绘图操作。
最后使用 save 方法将绘制好的图像保存为 output.png 文件。

使用 QPrinter 打印图形

QT       += core gui printsupport
#include <QApplication>
#include <QPrinter>
#include <QPainter>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 创建一个 QPrinter 对象
    QPrinter printer;
    printer.setOutputFormat(QPrinter::PdfFormat);
    printer.setOutputFileName("output.pdf");

    // 创建 QPainter 对象,将 QPrinter 作为绘图设备
    QPainter painter(&printer);
    painter.setPen(Qt::green);
    painter.drawEllipse(50, 50, 100, 100);
    painter.end();

    return app.exec();
}

创建了一个 QPrinter 对象,并设置输出格式为 PDF,指定输出文件名为 output.pdf。
创建 QPainter 对象,将 QPrinter 对象的指针作为绘图设备传入。
使用 QPainter 的 setPen 方法设置画笔颜色为绿色,然后调用 drawEllipse 方法绘制一个椭圆。
调用 end 方法结束绘图操作,此时绘制的内容会被输出到指定的 PDF 文件中。

在这里插入图片描述

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

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

相关文章

OpenHarmony-4.基于dayu800 GPIO 实践(2)

基于dayu800 GPIO 进行开发 1.DAYU800开发板硬件接口 LicheePi 4A 板载 2x10pin 插针&#xff0c;其中有 16 个原生 IO&#xff0c;包括 6 个普通 IO&#xff0c;3 对串口&#xff0c;一个 SPI。TH1520 SOC 具有4个GPIO bank&#xff0c;每个bank最大有32个IO&#xff1a;  …

HTML项目一键打包工具:HTML2EXE 最新版

HTML2EXE 工具可以一键打包生成EXE可执行文件。可以打包任意HTML项目或者是一个网址为单个EXE文件&#xff0c;直接打开即可运行。支持KRPano全景VR项目、WebGL游戏项目、视频播放、,课件打包、网址打包等。 下载地址&#xff1a; 最新版HTML2EXE首次发布下载地址 一、功能特点…

BGP配置华为——路径优选验证

实验拓扑 实验要求 实现通过修改AS-Path属性来影响路径选择实现通过修改Local_Preference属性来影响路径选择实现通过修改MED属性来影响路径选择实现通过修改preferred-value属性来影响路径选择 实验配置与效果 1.改名与IP配置 2.as300配置OSPF R3已经学到R2和R4的路由 3.…

深度学习基础--ResNet网络的讲解,ResNet50的复现(pytorch)以及用复现的ResNet50做鸟类图像分类

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 如果说最经典的神经网络&#xff0c;ResNet肯定是一个&#xff0c;这篇文章是本人学习ResNet的学习笔记&#xff0c;并且用pytorch复现了ResNet50&…

TMDS视频编解码算法

因为使用的是DDR进行传输&#xff0c;即双倍频率采样&#xff0c;故时钟只用是并行数据数据的5倍&#xff0c;而不是10倍。 TMDS算法流程&#xff1a; 视频编码TMDS算法流程实现&#xff1a; timescale 1 ps / 1ps //DVI编码通常用于视频传输&#xff0c;将并行数据转换为适合…

深度解析SmartGBD助力Android音视频数据接入GB28181平台

在当今数字化时代&#xff0c;视频监控与音视频通信技术在各行各业的应用愈发广泛。GB28181协议作为中国国家标准&#xff0c;为视频监控设备的互联互通提供了规范&#xff0c;但在实际应用中&#xff0c;许多Android终端设备并不具备国标音视频能力&#xff0c;这限制了其在相…

前端兼容处理接口返回的文件流或json数据

参考文档&#xff1a;JavaScript | MDN 参考链接&#xff1a;Blob格式转json格式&#xff0c;拿到后端返回的json数据_blob转json-CSDN博客 参考链接&#xff1a;https://juejin.cn/post/7117939029567340557 场景&#xff1a;导入上传文件&#xff0c;导入成功&#xff0c;…

Java基础常见的面试题(易错!!)

面试题一&#xff1a;为什么 Java 不支持多继承 Java 不支持多继承主要是为避免 “菱形继承问题”&#xff08;又称 “钻石问题”&#xff09;&#xff0c;即一个子类从多个父类继承到同名方法或属性时&#xff0c;编译器无法确定该调用哪个父类的成员。同时&#xff0c;多继承…

DPVS-2:单臂负载均衡测试

上一篇编译安装了DPVS&#xff0c;这一篇开启DPVS的负载均衡测试 &#xff1a; 单臂 FULL NAT模式 拓扑-单臂 单臂模式 DPVS 单独物理机 CLINET&#xff0c;和两个RS都是另一个物理机的虚拟机&#xff0c;它们网卡都绑定在一个桥上br0 &#xff0c; 二层互通。 启动DPVS …

Classic Control Theory | 12 Real Poles or Zeros (第12课笔记-中文版)

笔记链接&#xff1a;https://m.tb.cn/h.Tt876SW?tkQaITejKxnFLhttps://m.tb.cn/h.Tt876SW?tkQaITejKxnFL

Kubernetes开发环境minikube | 开发部署MySQL单节点应用

minikube是一个主要用于开发与测试Kubernetes应用的运行环境 本文主要描述在minikube运行环境中部署MySQL单节点应用 minikube start --force kubectl get nodes 如上所示&#xff0c;启动minikube单节点运行环境 minikube ssh docker pull 如上所示&#xff0c;从MySQL官…

安装可视化jar包部署平台JarManage

一、下载 下载地址&#xff1a;JarManage 发行版 - Gitee.com &#x1f692; 下载 最新发行版 下载zip的里面linux和windows版本都有 二、运行 上传到服务器&#xff0c;解压进入目录 &#x1f69a; 执行java -jar jarmanage-depoly.jar 命令运行 java -jar jarmanage-dep…

基于数据可视化+SpringBoot+安卓端的数字化OA公司管理平台设计和实现

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…

输入搜索、分组展示选项、下拉选取,全局跳转页,el-select 实现 —— 后端数据处理代码,抛砖引玉展思路

详细前端代码写于上一篇&#xff1a;输入搜索、分组展示选项、下拉选取&#xff0c;el-select 实现&#xff1a;即输入关键字检索&#xff0c;返回分组选项&#xff0c;选取跳转到相应内容页 —— VUE项目-全局模糊检索 【效果图】&#xff1a;分组展示选项 >【去界面操作体…

性能巅峰对决:Rust vs C++ —— 速度、安全与权衡的艺术

??关注&#xff0c;带你探索Java的奥秘&#xff01;?? ??超萌技术攻略&#xff0c;轻松晋级编程高手&#xff01;?? ??技术宝库已备好&#xff0c;就等你来挖掘&#xff01;?? ??订阅&#xff0c;智趣学习不孤单&#xff01;?? ??即刻启航&#xff0c;编…

unity学习53:UI的子容器:面板panel

目录 1 UI的最底层容器&#xff1a;canvas 1.1 UI的最底层容器&#xff1a;canvas 1.2 UI的合理结构 2 UI的子容器&#xff1a;面板panel 2.1 创建panel 2.2 面板的本质&#xff1a; image &#xff0c;就是一个透明的图片&#xff0c;1个空容器 3 面板的属性 4 面板的…

4-知识图谱的抽取与构建-4_2实体识别与分类

&#x1f31f; 知识图谱的实体识别与分类&#x1f525; &#x1f50d; 什么是实体识别与分类&#xff1f; 实体识别&#xff08;Entity Recognition&#xff09;是从文本中提取出具体的事物&#xff0c;如人名、地名、组织名等。分类&#xff08;Entity Classification&#x…

elasticsearch在windows上的配置

写在最前面&#xff1a; 上资源 第一步 解压&#xff1a; 第二步 配置两个环境变量 第三步 如果是其他资源需要将标蓝的文件中的内容加一句 xpack.security.enabled: false 不同版本的yaml文件可能配置不同&#xff0c;末尾加这个 xpack.security.enabled: true打开bin目…

详解分布式ID实践

引言 分布式ID&#xff0c;所谓的分布式ID&#xff0c;就是针对整个系统而言&#xff0c;任何时刻获取一个ID&#xff0c;无论系统处于何种情况&#xff0c;该值不会与之前产生的值重复&#xff0c;之后获取分布式ID时&#xff0c;也不会再获取到与其相同的值&#xff0c;它是…

【Rust中级教程】2.8. API设计原则之灵活性(flexible) Pt.4:显式析构函数的问题及3种解决方案

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 说句题外话&#xff0c;这篇文章一共5721个字&#xff0c;是我截至目前写的最长的一篇文章&a…