(七)QT——消息事件机制&绘图&文件
目录
前言
消息事件机制 (Event System)
绘图 (Graphics & Drawing)
绘图设备
Qt 提供的主要绘图设备
Qt 主要绘图设备的特点
各个绘图设备的详细介绍
文件处理 (File Handling)
总结
前言
QT 是一个非常强大的图形用户界面(GUI)开发框架,它的消息事件机制、绘图功能以及文件处理能力都是其重要组成部分。下面是关于这些部分的一些概述。
消息事件机制 (Event System)
Qt 采用 事件驱动 的机制,即 应用程序的运行是由事件触发的。所有用户交互(如鼠标、键盘)或系统行为(如窗口重绘、计时器触发)都以 事件(Event) 的形式传递到 Qt 应用程序。
工作原理
Qt 通过 事件队列(Event Queue) 和 事件循环(Event Loop) 来处理事件:
- 用户或系统产生事件(如鼠标点击)。
- 事件被添加到 Qt 的事件队列(
QApplication::exec()
进入事件循环)。 - Qt 事件分发机制 将事件发送给对应的
QObject
处理(如mousePressEvent()
、keyPressEvent()
)。 - 组件根据事件执行相应的逻辑。
QT 使用事件驱动的方式来管理应用程序的操作。每个 UI 元素(如按钮、窗口、文本框等)都会生成事件,这些事件会被送到事件队列中,并由应用程序的事件循环(Event Loop)逐一处理。基本上,QT 的事件机制涉及以下几个方面:
- 事件循环:QT 应用程序的主线程通常会有一个事件循环,负责处理来自用户的输入事件(如鼠标点击、键盘输入等)以及其他系统事件(如定时器触发)。
- 事件处理:UI 元素的类(如
QWidget
、QPushButton
)可以重写事件处理函数(如mouseEvent
、keyEvent
),从而对不同类型的事件作出响应。 - 事件传播:事件可以通过父子组件链传播。例如,点击事件先在子组件中处理,如果没有处理,才会传递到父组件。
- 自定义事件:你可以通过继承
QEvent
类和QCoreApplication::postEvent
函数来创建并发送自定义事件。
常见的事件类型
在Qt中,QWidget
类定义了很多事件处理函数,这些函数都是protected virtual
的,意味着我们可以在子类中重写它们来响应特定的事件。常见的事件包括:
- keyPressEvent():键盘按键按下事件。
- keyReleaseEvent():键盘按键松开事件。
- mouseDoubleClickEvent():鼠标双击事件。
- mouseMoveEvent():鼠标移动事件。
- mousePressEvent():鼠标按键按下事件。
- mouseReleaseEvent():鼠标按键松开事件。
事件处理的例子
下面是一个基于QLabel
的事件处理示例,它演示了如何响应鼠标事件(鼠标按下、鼠标移动、鼠标释放):
- 打开 Qt Creator。
- 选择 File -> New File or Project,然后选择 Application -> Qt Widgets Application。
- 输入项目名称(例如
MouseEventDemo
)并选择保存路径。- 点击 Next,然后 Finish。
- 右键点击项目名,选择 Add New,然后选择 C++ Class,并命名为
EventLabel
。- 将上述的头文件 (
EventLabel.h
) 和实现文件 (EventLabel.cpp
) 复制到EventLabel.h
和EventLabel.cpp
中。- 在
main.cpp
中包含EventLabel.h
并按照上述代码实现主程序。- 最后点击 Run 或者 ctrl+R 来运行应用程序。
main.cpp
#include <QApplication>
#include "mainwindow.h"int main(int argc, char *argv[])
{QApplication app(argc, argv);// 创建自定义标签EventLabel *label = new EventLabel;label->setWindowTitle("MouseEvent Demo");label->resize(300, 200);label->show();return app.exec();
}
mainwindow.h
#ifndef EVENTLABEL_H
#define EVENTLABEL_H#include <QLabel>
#include <QMouseEvent>class EventLabel : public QLabel
{Q_OBJECTpublic:// 构造函数explicit EventLabel(QWidget *parent = nullptr);protected:// 重写事件处理函数void mousePressEvent(QMouseEvent *event) override;void mouseMoveEvent(QMouseEvent *event) override;void mouseReleaseEvent(QMouseEvent *event) override;
};#endif // EVENTLABEL_H
mainwindow.cpp
#include "mainwindow.h"
#include <QString>// 构造函数
EventLabel::EventLabel(QWidget *parent) : QLabel(parent)
{setAlignment(Qt::AlignCenter); // 设置文本居中setText("<h1>Move the mouse here</h1>"); // 初始文本
}// 处理鼠标按下事件
void EventLabel::mousePressEvent(QMouseEvent *event)
{setText(QString("<h1>Press: (%1, %2)</h1>").arg(event->x()).arg(event->y()));
}// 处理鼠标移动事件
void EventLabel::mouseMoveEvent(QMouseEvent *event)
{setText(QString("<h1>Move: (%1, %2)</h1>").arg(event->x()).arg(event->y()));
}// 处理鼠标释放事件
void EventLabel::mouseReleaseEvent(QMouseEvent *event)
{setText(QString("<h1>Release: (%1, %2)</h1>").arg(event->x()).arg(event->y()));
}
运行效果
- 在 Qt Creator 中创建并运行这个项目后,你会看到一个窗口,显示有一个标签。
- 当你点击鼠标、移动鼠标或者释放鼠标时,标签上的文本会相应地更新,显示鼠标的当前坐标。
这样,你就能够在 Qt Creator 中创建并运行一个带有事件处理的简单应用程序了!
mouseMoveEvent()
默认情况下只会在按下鼠标后触发,因为 QWidget
的 mouseTracking
属性默认是 false
。如果你希望鼠标在移动时无需点击就触发 mouseMoveEvent()
,可以在 EventLabel
的构造函数中启用鼠标追踪功能:
修改 mainwindow.cpp 使鼠标追踪生效
// 构造函数
EventLabel::EventLabel(QWidget *parent) : QLabel(parent)
{setAlignment(Qt::AlignCenter); // 设置文本居中setText("<h1>Move the mouse here</h1>"); // 初始文本setMouseTracking(true); // 启用鼠标追踪
}
解释
- 默认情况下 (
mouseTracking = false
)- 只有当鼠标按下并拖动时,
mouseMoveEvent()
才会被触发。
- 只有当鼠标按下并拖动时,
- 启用
mouseTracking = true
- 只要鼠标移动到
QWidget
上,就会触发mouseMoveEvent()
,即使没有点击鼠标。
- 只要鼠标移动到
消息事件机制和信号和槽机制的关系
信号和槽机制(异步回调)
Qt 的 信号和槽(Signal & Slot) 机制是一种 高级的消息通信机制,用于对象之间解耦的事件处理。它是一种 基于发布-订阅模式的回调机制,允许不同对象之间进行交互,而不需要显式调用对方的方法。
工作原理
- 信号(Signal):对象 A 发送一个信号,例如按钮被点击
clicked()
。 - 槽(Slot):对象 B 连接了 A 的信号,并定义了相应的槽函数(例如
onButtonClicked()
)。 - Qt 内部管理信号-槽连接,当信号被触发时,Qt 事件系统会自动调用对应的槽函数。
特性 | 消息事件机制(Event) | 信号和槽机制(Signal & Slot) |
---|---|---|
触发方式 | 事件循环调度 | 直接调用 |
传输机制 | 事件队列 | 直接/异步调用 |
是否跨线程 | 不能 | 可以 |
是否解耦 | 否,需要重写 | 是,对象不直接依赖 |
适用场景 | 低级事件(鼠标、键盘、窗口重绘) | 高级对象交互(按钮点击、线程通信) |
- 事件机制 适用于 GUI 组件(鼠标、键盘、窗口重绘),需要重写
QEvent
处理函数。 - 信号-槽机制 适用于对象间的通信,更高级,更解耦,可以跨线程。
- 二者可以结合使用,在事件触发后发射信号,供其他组件监听。
🎯 应用建议
- 简单交互(鼠标、键盘) → 事件处理
mousePressEvent()
- 按钮点击、跨对象交互 →
connect(signal, slot)
- 复杂任务、跨线程 → 信号-槽机制
绘图 (Graphics & Drawing)
Qt 的绘图系统允许开发者使用相同的 API 在屏幕、打印设备等不同目标上绘制图形。其核心组成部分包括:
- QPainter(画笔):执行绘制操作。
- QPaintDevice(绘图设备):提供绘制的空间,例如窗口、图片、打印机等。
- QPaintEngine(绘图引擎):连接
QPainter
和QPaintDevice
,提供底层绘图支持(通常无需手动使用)。
QPainter(绘图核心)
QPainter
是 Qt 的 绘图核心类,它提供了一组 API 用于 绘制线条、文本、图片、形状等。
基本用法
要在 QWidget
组件上绘制,需要重写 paintEvent()
事件,并在其中使用 QPainter
:
#include <QApplication>
#include <QWidget>
#include <QPainter>class MyWidget : public QWidget {
protected:void paintEvent(QPaintEvent *) override {QPainter painter(this); // 绑定当前窗口painter.setPen(Qt::blue);painter.setFont(QFont("Arial", 20));painter.drawText(50, 50, "Hello Qt!");painter.drawRect(20, 70, 100, 50); // 绘制矩形painter.drawEllipse(150, 70, 50, 50); // 绘制圆}
};int main(int argc, char *argv[]) {QApplication app(argc, argv);MyWidget w;w.resize(300, 200);w.show();return app.exec();
}
📌 要点:
QPainter
需要绑定this
(当前窗口)。setPen()
设置画笔颜色,setFont()
设置字体。- 使用
drawText()
、drawRect()
、drawEllipse()
等方法绘制不同图形。
QPaintDevice(绘图目标)
Qt 提供了多个 QPaintDevice
,QPainter
需要在这些设备上绘制:
QPaintDevice 子类 | 描述 |
---|---|
QWidget | 用于窗口、按钮等组件 |
QPixmap | 用于绘制离屏位图(更高效) |
QImage | 用于像素级操作(如滤镜、图像处理) |
QPicture | 用于存储绘图命令(可回放) |
QPrinter | 用于打印支持 |
在 QPixmap 上绘制
QPixmap pixmap(300, 200);
pixmap.fill(Qt::white);
QPainter painter(&pixmap);
painter.setPen(Qt::red);
painter.drawLine(10, 10, 100, 100);
pixmap.save("output.png"); // 保存为图片
📌 适用场景:
QPixmap
适合 高效绘制(用于 界面)。QImage
适合 逐像素操作(用于 图像处理)。
QPaintEngine(绘图引擎)
QPaintEngine
负责将 QPainter
的绘图指令翻译成底层的 平台绘图调用(如 OpenGL、Raster、Direct2D)。
📌 你通常不需要直接使用 QPaintEngine
,除非你要 自定义绘图设备。
绘图事件与交互
QPainter
主要在 paintEvent()
事件中使用,而绘图交互通常涉及:
- 鼠标事件(mousePressEvent、mouseMoveEvent)
- 定时器事件(QTimer) 实现动画
- 自定义控件绘制(QCustomPlot 等)
示例:鼠标拖动绘制线条
- 打开 Qt Creator。
- 选择 New Project,然后选择 Qt Widgets Application。
- 设置项目名称和路径,点击 Next,然后选择合适的构建套件。
- 点击 Finish 完成创建。
mainwindow.h
#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H#include <QWidget>
#include <QPoint>
#include <QVector>class DrawWidget : public QWidget
{Q_OBJECTpublic:explicit DrawWidget(QWidget *parent = nullptr);~DrawWidget();protected:void paintEvent(QPaintEvent *event) override; // 重写绘图事件void mousePressEvent(QMouseEvent *event) override; // 鼠标按下事件void mouseMoveEvent(QMouseEvent *event) override; // 鼠标移动事件void mouseReleaseEvent(QMouseEvent *event) override; // 鼠标释放事件private:QVector<QPoint> points; // 用来存储鼠标点击的点bool isDrawing = false; // 标记是否正在绘制
};#endif // DRAWWIDGET_H
mainwindow.cpp
#include "mainwindow.h"
#include <QPainter>
#include <QMouseEvent>DrawWidget::DrawWidget(QWidget *parent): QWidget(parent)
{setWindowTitle("Draw with Mouse");setFixedSize(400, 300); // 设置窗口大小
}DrawWidget::~DrawWidget()
{
}void DrawWidget::paintEvent(QPaintEvent *event)
{QPainter painter(this);painter.setPen(Qt::black); // 设置画笔颜色为黑色// 绘制已存储的所有点for (int i = 1; i < points.size(); ++i) {painter.drawLine(points[i - 1], points[i]); // 绘制线条}
}void DrawWidget::mousePressEvent(QMouseEvent *event)
{// 当鼠标按下时,开始绘制isDrawing = true;points.clear(); // 清除之前的点points.append(event->pos()); // 记录鼠标按下的位置update(); // 更新窗口,触发重绘
}void DrawWidget::mouseMoveEvent(QMouseEvent *event)
{if (isDrawing) {points.append(event->pos()); // 记录鼠标拖动时的位置update(); // 更新窗口,触发重绘}
}void DrawWidget::mouseReleaseEvent(QMouseEvent *event)
{if (isDrawing) {points.append(event->pos()); // 记录鼠标释放的位置isDrawing = false; // 结束绘制update(); // 更新窗口,触发重绘}
}
主文件:main.cpp
#include <QApplication>
#include "mainwindow.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);// 创建窗口并显示DrawWidget w;w.show();return a.exec();
}
运行项目
- 在 Qt Creator 中构建并运行项目。
- 鼠标点击并拖动窗口,你将看到鼠标拖动的路径逐渐绘制出来。
mousePressEvent
:当鼠标按下时,记录当前位置,并清空之前的点,开始绘制。mouseMoveEvent
:鼠标拖动时,不断记录新的点,并更新绘制区域。mouseReleaseEvent
:鼠标释放时,结束当前绘制过程,并记录最后一个点。
在窗口中实现动态绘制线条的效果
mainwindow.h
#ifndef DRAWWIDGET_H
#define DRAWWIDGET_H#include <QWidget>
#include <QVector>
#include <QPoint>class DrawWidget : public QWidget
{Q_OBJECTpublic:explicit DrawWidget(QWidget *parent = nullptr);~DrawWidget();protected:void paintEvent(QPaintEvent *event) override; // 绘图事件void mousePressEvent(QMouseEvent *event) override; // 鼠标按下事件void mouseMoveEvent(QMouseEvent *event) override; // 鼠标移动事件void mouseReleaseEvent(QMouseEvent *event) override; // 鼠标释放事件private:QVector<QVector<QPoint>> lines; // 存储所有绘制的线条QVector<QPoint> currentLine; // 当前绘制的线条
};#endif // DRAWWIDGET_H
mainwindow.cpp
#include "mainwindow.h"
#include <QPainter>
#include <QMouseEvent>DrawWidget::DrawWidget(QWidget *parent): QWidget(parent)
{setWindowTitle("动态鼠标绘制");setFixedSize(600, 400); // 设置窗口大小
}DrawWidget::~DrawWidget()
{
}void DrawWidget::paintEvent(QPaintEvent *event)
{QPainter painter(this);painter.setPen(QPen(Qt::black, 2)); // 设置黑色画笔,线宽 2// 绘制所有历史线条for (const auto &line : lines) {for (int i = 1; i < line.size(); ++i) {painter.drawLine(line[i - 1], line[i]);}}// 绘制当前线条for (int i = 1; i < currentLine.size(); ++i) {painter.drawLine(currentLine[i - 1], currentLine[i]);}
}void DrawWidget::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton) {currentLine.clear(); // 清空当前线条currentLine.append(event->pos()); // 记录鼠标按下的点update(); // 触发重绘}
}void DrawWidget::mouseMoveEvent(QMouseEvent *event)
{if (!currentLine.isEmpty()) {currentLine.append(event->pos()); // 记录鼠标移动轨迹update(); // 触发重绘,实现动态绘制}
}void DrawWidget::mouseReleaseEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton && !currentLine.isEmpty()) {lines.append(currentLine); // 将当前线条加入历史线条currentLine.clear();update(); // 触发重绘}
}
main.cpp
#include <QApplication>
#include "mainwindow.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);DrawWidget w;w.show();return a.exec();
}
✅ 实时绘制:鼠标移动时,线条立即更新,无需等待鼠标释放。
✅ 多段绘制:支持多次绘制,每次释放鼠标都会存入历史线条。
✅ 动态更新:每次 mouseMoveEvent()
都会触发 update()
,保证实时绘制效果。
小结
- QPainter 是 Qt 的核心绘图 API,必须在
paintEvent()
或QPaintDevice
绑定后使用。 - QPaintDevice 提供绘图目标,如 窗口(QWidget)、位图(QPixmap)、图片(QImage)。
- QPaintEngine 负责底层绘图,通常无需直接操作。
- 事件机制(如
mouseMoveEvent()
)可用于绘图交互。
绘图设备
在 Qt 中,绘图设备(QPaintDevice) 是用于支持 QPainter
进行绘制的对象。它提供了一个二维的绘图表面,可以是窗口、图片、缓冲区等。所有可以绘制的对象都继承自 QPaintDevice
,例如 QWidget
、QPixmap
、QImage
等。
QPaintDevice
本身是一个基类,不能直接使用,而是通过其子类来提供不同的绘图方式和特性。
Qt 提供的主要绘图设备
Qt 提供了四种常见的绘图设备,分别是:
- QPixmap - 适用于屏幕显示的优化图像。
- QBitmap -
QPixmap
的子类,仅支持单色位图(1 位深度)。 - QImage - 适用于像素级访问的图像数据。
- QPicture - 记录和重放
QPainter
命令的设备。
Qt 主要绘图设备的特点
绘图设备 | 特点 | 适用场景 |
---|---|---|
QPixmap | 适用于屏幕显示,底层优化,存储格式与设备相关 | UI 组件绘制、按钮、标签、绘制缓冲 |
QBitmap | 单色位图 (1-bit ),是 QPixmap 的子类 | 绘制蒙版、遮罩、位操作 |
QImage | 支持像素级访问,可独立于设备存储 | 图像处理、滤波、像素操作 |
QPicture | 记录 QPainter 命令,适用于重放绘图操作 | 矢量绘图、回放绘制 |
各个绘图设备的详细介绍
① QPixmap
(屏幕优化的图像)
QPixmap
是一种高效的图像存储方式,它主要用于 屏幕显示,在底层进行了特定优化,能在不同操作系统上实现最快的绘制效果。QPixmap
不能 直接访问像素数据,因此 不适合像素级操作,如果需要操作像素,可以使用 QImage
。
示例:加载并显示一张图片
创建 Qt Widgets 应用程序
在 Qt Creator 中,新建一个 Qt Widgets Application 项目,并选择QMainWindow
作为主窗口类型。修改
mainwindow.h
在mainwindow.h
文件中,添加QLabel
作为图片显示组件。修改
mainwindow.cpp
在mainwindow.cpp
文件中,加载并显示图片。运行程序,窗口中会显示
image.png
这张图片。
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QLabel>class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:QLabel *imageLabel; // 用于显示图片的 QLabel
};#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QPixmap>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{// 创建 QLabel 用于显示图片imageLabel = new QLabel(this);// 加载图片QPixmap pixmap("image.png"); // 确保 image.png 在可访问的路径if (pixmap.isNull()) {imageLabel->setText("图片加载失败");} else {imageLabel->setPixmap(pixmap);imageLabel->setScaledContents(true); // 让图片适应 QLabel 大小}// 设置 QLabel 作为主窗口的中心组件setCentralWidget(imageLabel);resize(600, 400); // 设置窗口大小
}MainWindow::~MainWindow()
{
}
main.cpp
#include "mainwindow.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication app(argc, argv);MainWindow window;window.show();return app.exec();
}
如果路径不对
绘制图片
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QPixmap>class MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();protected:void paintEvent(QPaintEvent *event) override; // 监听绘制事件private:QPixmap pixmap;
};#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QPainter>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{// 加载图片pixmap.load("D:/PIC/01.png"); // 替换为你的图片路径// 设置窗口大小resize(400, 300);
}// 监听绘制事件,使用 QPainter 进行绘制
void MainWindow::paintEvent(QPaintEvent *event)
{QMainWindow::paintEvent(event); // 调用基类的 paintEvent 以确保正常绘制QPainter painter(this);// 确保图片加载成功再绘制if (!pixmap.isNull()) {painter.drawPixmap(0, 0, pixmap.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));}
}MainWindow::~MainWindow()
{
}
main.cpp
#include "mainwindow.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}
修改mainwindow.cpp
#include "mainwindow.h"
#include <QPainter>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{// 加载图片pixmap.load("D:/PIC/01.png"); // 替换为你的图片路径// 设置窗口大小resize(800, 400); // 调整窗口大小以适应对比显示
}// 监听绘制事件,绘制原图和缩放后的图
void MainWindow::paintEvent(QPaintEvent *event)
{QMainWindow::paintEvent(event); // 调用基类 paintEvent 确保正常绘制QPainter painter(this);if (!pixmap.isNull()) {// 1. 绘制原图(左侧)painter.drawPixmap(10, 10, pixmap); // 原图按原始大小绘制// 2. 绘制缩放后的图(右侧)QPixmap scaledPixmap = pixmap.scaled(size() / 2, Qt::KeepAspectRatio, Qt::SmoothTransformation);int x = width() / 2; // 放在右侧int y = (height() - scaledPixmap.height()) / 2; // 居中对齐painter.drawPixmap(x, y, scaledPixmap);}
}MainWindow::~MainWindow()
{
}
✅ 左侧:按原始大小显示 原图
✅ 右侧:自适应窗口大小的 缩放图
✅ 缩放窗口时,右侧图像会随窗口大小变化
如果希望固定原图的位置或大小,可以手动调整 drawPixmap()
的 x, y
坐标。
QImage 和 QPixmap 的区别
QImage 和 QPixmap 都用于处理图像,但它们的用途不同。
1. 用途
- QPixmap:用于屏幕显示,针对 GUI 绘图进行了优化,通常用于 QPainter 进行高效绘制。
- QImage:用于图像处理,支持像素级修改,适合I/O 读写、图像转换、过滤处理等。
2. 平台依赖
- QPixmap:依赖于 底层平台的绘图引擎,在不同系统上可能会有不同的显示效果(如 Windows、Linux、Mac)。
- QImage:完全基于 Qt 自身的绘图引擎,在不同平台上的显示效果 一致。
3. 线程
- QPixmap:不能在非 GUI 线程 中操作,因为它依赖于 底层绘图系统。
- QImage:可以在非 GUI 线程 操作,因此适用于 多线程图像处理。
4. 像素操作
- QPixmap:不支持直接像素访问,如果需要修改像素,必须先转换为
QImage
。 - QImage:支持 像素级别访问,可以用
setPixel()
和pixel()
操作单个像素。
适用场景
QPixmap | QImage | |
---|---|---|
用途 | 绘图 & 显示 | 处理 & 操作 |
优化 | 屏幕绘制优化 | 像素处理优化 |
像素级操作 | ❌ 不能直接访问像素 | ✅ 可直接操作像素 |
线程支持 | 仅 GUI 线程 | 可用于非 GUI 线程 |
跨平台显示 | 可能不同 | 保持一致 |
- 如果只是显示图片,使用
QPixmap
(性能更好) - 如果要修改图片像素,使用
QImage
- 如果要在多线程中操作图片,使用
QImage
-
如果要在绘图中使用
QImage
,先转换为QPixmap
② QBitmap
QBitmap
是 QPixmap
的子类,表示 单色(黑白)位图,用于 遮罩(mask)和透明度处理。
特点
- 仅支持 1-bit 深度(0 = 黑色,1 = 白色)。
- 用于透明遮罩,例如创建窗口的 不规则形状 或 按钮透明背景。
- 继承
QPixmap
,受 GPU 加速,在绘制时效率较高。
示例:创建一个黑白遮罩
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QBitmap>class BitmapWidget : public QWidget
{
public:BitmapWidget(QWidget *parent = nullptr) : QWidget(parent) {}protected:void paintEvent(QPaintEvent *event) override{QPainter painter(this);// 创建 QBitmap(黑白位图)QBitmap bitmap(100, 100);bitmap.fill(Qt::color0); // 全部填充为黑色// 在 QBitmap 上绘制白色图形QPainter bmpPainter(&bitmap);bmpPainter.setBrush(Qt::color1);bmpPainter.drawEllipse(20, 20, 60, 60);bmpPainter.end();// 显示位图painter.drawPixmap(50, 50, bitmap);}
};int main(int argc, char *argv[])
{QApplication app(argc, argv);BitmapWidget w;w.resize(200, 200);w.show();return app.exec();
}
应用场景
场景 | 说明 |
---|---|
遮罩效果 | 例如 透明窗口、按钮背景透明化 |
简单二值化图像 | 处理 黑白图形,如 OCR 预处理 |
高效绘制 | 由于 QBitmap 继承 QPixmap ,它使用 GPU 进行加速绘制 |
③ QImage
QImage
是 Qt 提供的 通用图像处理类,用于 像素级 访问和 图像文件读写。
特点
- 支持多种格式(RGB、灰度、ARGB、CMYK)。
- 可直接操作像素数据(
setPixel()
、pixel()
)。 - 支持
QPainter
进行绘制。 - 可独立于 GUI 线程,适合 后台线程 进行图像处理。
- 支持 Alpha 透明度,适用于 复杂的图像编辑。
示例:加载 QImage 并修改像素
#include <QApplication>
#include <QWidget>
#include <QImage>
#include <QPainter>class ImageWidget : public QWidget
{
public:ImageWidget(QWidget *parent = nullptr) : QWidget(parent){// 加载图片image = QImage("D:/PIC/01.png").scaled(300, 200, Qt::KeepAspectRatio);}protected:void paintEvent(QPaintEvent *event) override{QPainter painter(this);// 修改像素值(将左上角 50x50 区域变红)for (int y = 0; y < 50; ++y){for (int x = 0; x < 50; ++x){image.setPixel(x, y, qRgb(255, 0, 0));}}// 绘制 QImagepainter.drawImage(50, 50, image);}private:QImage image;
};int main(int argc, char *argv[])
{QApplication app(argc, argv);ImageWidget w;w.resize(400, 300);w.show();return app.exec();
}
应用场景
场景 | 说明 |
---|---|
图像处理 | 进行 滤波、变换、像素修改 |
图片加载与保存 | QImage::load() / QImage::save() 支持 BMP, JPG, PNG 等格式 |
多线程处理 | 可以在 后台线程 进行图像计算 |
透明度处理 | QImage::Format_ARGB32 处理带透明通道的图像 |
QBitmap vs QImage vs QPixmap
特性 | QBitmap | QImage | QPixmap |
---|---|---|---|
颜色深度 | 1-bit (黑白) | 8-bit, 16-bit, 32-bit (支持透明) | 受 平台优化 |
用途 | 遮罩、透明背景 | 像素级处理、文件读写 | 高效屏幕绘制 |
像素操作 | ❌(不可修改像素) | ✅ setPixel() / pixel() | ❌(不能修改) |
存储方式 | GPU | 内存 | GPU |
支持 Alpha 透明 | ❌ | ✅(ARGB) | ✅(但像素不可直接操作) |
多线程支持 | ❌ | ✅ | ❌ |
④ QPicture 介绍
QPicture
是 Qt 提供的一种绘图设备,它可以 记录 和 重现 QPainter
的绘图命令。
特点
- 记录绘图操作:可以存储
QPainter
的绘制命令,并在稍后回放。 - 跨平台:使用 平台无关的二进制格式,与 Windows、Linux、Mac 兼容。
- 支持多种设备:可以绘制到 屏幕、SVG、PDF、PS、打印机等 设备上。
- 高效存储:不像
QPixmap
或QImage
存储像素数据,QPicture
仅存储绘图命令,占用的存储空间较小。 - 适用于复杂绘制:在需要 多次重复相同绘制 时,
QPicture
可以减少 绘图计算量,提高效率。
QPicture 的使用
1. 记录绘制命令
使用 QPainter::begin()
让 QPicture
记录绘制命令,使用 end()
停止记录。
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QPicture>
#include <QPushButton>class PictureWidget : public QWidget
{
public:PictureWidget(QWidget *parent = nullptr) : QWidget(parent){setFixedSize(400, 300);// 创建 QPicture 并记录绘制操作QPicture picture;QPainter painter;painter.begin(&picture); // 开始记录绘图命令painter.setPen(Qt::blue);painter.setFont(QFont("Arial", 20));painter.drawText(50, 50, "Hello QPicture");painter.drawRect(100, 100, 200, 100);painter.end(); // 结束记录// 保存到文件picture.save("drawing.dat");}protected:void paintEvent(QPaintEvent *event) override{QPainter painter(this);// 读取 QPicture 并绘制QPicture picture;picture.load("drawing.dat");painter.drawPicture(0, 0, picture);}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);PictureWidget w;w.show();return a.exec();
}
QPicture 使用流程
- 创建
QPicture
对象。 - 使用
QPainter::begin(&picture)
开始记录绘图命令。 - 执行
QPainter
相关绘图操作(如drawText()
、drawLine()
)。 - 使用
QPainter::end()
结束绘制,记录保存。 - 调用
QPicture::save("filename")
保存到文件(可选)。 - 使用
QPicture::load("filename")
重新加载绘图数据。 - 在
paintEvent()
里调用QPainter::drawPicture()
进行绘制。
适用场景
适用情况 | 说明 |
---|---|
重复绘制相同图形 | QPicture 存储的是绘制命令,不占用大量存储空间,提高效率。 |
存储复杂绘图 | 适合用于 矢量图形存储,可以绘制到 屏幕、PDF、SVG、打印机 等不同设备上。 |
减少 CPU 计算 | 直接回放绘图命令,而不是重新计算图形,减少 QPainter 计算量。 |
QPicture
适用于 记录和回放绘图操作,节省资源。QPixmap
适用于 高效屏幕绘制,但不易存储和修改。QImage
适用于 像素级处理和 I/O 读写,可以直接访问像素数据。
如果你需要在 多个设备上显示相同的矢量图形,或者需要 保存绘图过程,QPicture
是一个很好的选择!
文件处理 (File Handling)
QT 提供了一些类来帮助你处理文件读写、文件选择、目录遍历等操作。
- QFile:用来处理文件的基本操作,如打开文件、读取、写入、关闭文件等。
QFile file("example.txt");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {QTextStream in(&file);QString content = in.readAll();qDebug() << content;file.close();
}
- QFileDialog:用于显示文件对话框,允许用户选择文件或目录。
QString fileName = QFileDialog::getOpenFileName(this, "Open File", "", "Text Files (*.txt)");
if (!fileName.isEmpty()) {// 处理文件
}
- QDir:用于处理目录的操作,如列出目录中的文件、创建目录等。
QDir dir("/path/to/directory");
QStringList files = dir.entryList(QDir::Files);
for (const QString &file : files) {qDebug() << file;
}
总结
- 消息事件机制:通过事件循环和事件处理函数来响应用户输入。
- 绘图:使用
QPainter
、QPixmap
和QGraphicsView
等类绘制图形、文本和图像。 - 文件操作:通过
QFile
、QFileDialog
和QDir
类来处理文件和目录的读写及选择。
这些功能为你在 QT 中开发丰富的 GUI 应用程序提供了强大的支持。
相关文章:

(七)QT——消息事件机制&绘图&文件
目录 前言 消息事件机制 (Event System) 绘图 (Graphics & Drawing) 绘图设备 Qt 提供的主要绘图设备 Qt 主要绘图设备的特点 各个绘图设备的详细介绍 文件处理 (File Handling) 总结 前言 QT 是一个非常强大的图形用户界面(GUI)开发框架&…...
【虚幻引擎UE】AOI算法介绍与实现案例
【虚幻引擎UE】AOI算法介绍与实现 一、AOI算法介绍AOI算法的典型应用场景二、AOI相关算法1. 边界框法(Bounding Box Method)2. 动态AOI算法3. 布尔运算(Boolean Operations)4. 四叉树(Quadtree)5. R树(R-Tree)6. 圆形AOI算法7. 网格分割(Grid Partitioning)8. 多边形…...

python学opencv|读取图像(六十)先后使用cv2.erode()函数和cv2.dilate()函数实现图像处理
【1】引言 前序学习进程中,先后了解了使用cv2.erode()函数和cv2.dilate()函数实现图像腐蚀和膨胀处理的效果,相关文章链接为: python学opencv|读取图像(五十八)使用cv2.erode()函数实现图像腐蚀处理-CSDN博客 pytho…...

AI能帮谷歌SEO做什么?
现在没用过AI写内容的人,应该不多了,用ChatGPT写文章,用MidJourney画图,用各种工具做调研,AI已经成为SEO玩家的“标配”。但AI到底能帮SEO做到什么?省钱?省时间?还是更重要的东西&am…...

SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来Matlab实现
SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来Matlab实现 目录 SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来Matlab实现预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现SSA-TCN麻雀算法优化时间卷积神经网络时间序列预测未来(优…...

【机器学习】数据预处理之数据归一化
数据预处理之数据归一化 一、摘要二、数据归一化概念三、数据归一化实现方法3.1 最值归一化方法3.2 均值方差归一化方法 一、摘要 本文主要讲述了数据归一化(Feature Scaling)的重要性及其方法。首先通过肿瘤大小和发现时间的例子,说明了不同…...

【专题】2024-2025人工智能代理深度剖析:GenAI 前沿、LangChain 现状及演进影响与发展趋势报告汇总PDF洞察(附原数据表)
原文链接:https://tecdat.cn/?p39630 在科技飞速发展的当下,人工智能代理正经历着深刻的变革,其能力演变已然成为重塑各行业格局的关键力量。从早期简单的规则执行,到如今复杂的自主决策与多智能体协作,人工智能代理…...
非递减子序列(力扣491)
这道题的难点依旧是去重,但是与之前做过的子集类问题的区别就是,这里是求子序列,意味着我们不能先给数组中的元素排序。因为子序列中的元素的相对位置跟原数组中的相对位置是一样的,如果我们改变数组中元素的顺序,子序…...
网站快速收录策略:提升爬虫抓取效率
本文转自:百万收录网 原文链接:https://www.baiwanshoulu.com/102.html 要实现网站快速收录并提升爬虫抓取效率,可以从以下几个方面入手: 一、优化网站结构与内容 清晰的网站结构 设计简洁明了的网站导航,确保爬虫…...

系统思考—自我超越
“人们往往认为是个人的能力限制了他们,但事实上,是组织的结构和惯性思维限制了他们的潜力。”—彼得圣吉 最近和一家行业隐形冠军交流,他们已经是领域第一,老板却依然要求:核心团队都要自我超越,攻坚克难…...

苍穹外卖-菜品分页查询
3. 菜品分页查询 3.1 需求分析和设计 3.1.1 产品原型 系统中的菜品数据很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。 菜品分页原型: 在菜品列表展示时…...
子集II(力扣90)
这道题与子集(力扣78)-CSDN博客 的区别就在于集合中的元素会重复,那么还按照之前的代码来操作就会得到重复的子集,因此这道题的重点就在于去重。需要注意的是,这里的去重指的是在同一层递归中,而在往下递归的子集中可以取重复的元…...
user、assistant、system三大角色在大语言模型中的作用(通俗解释)
1 概述 在大语言模型中,通常涉及到三种角色:用户(user)、助手(assistant)和系统(system)。简单来说,和大模型对话其实是三个人的电影。 2 角色定义 2.1 系统…...
LeetCode 3444.使数组包含目标值倍数的最小增量
给你两个数组 nums 和 target 。 在一次操作中,你可以将 nums 中的任意一个元素递增 1 。 返回要使 target 中的每个元素在 nums 中 至少 存在一个倍数所需的 最少操作次数 。 示例 1: 输入:nums [1,2,3], target [4] 输出:…...

2月9日星期日今日早报简报微语报早读
2月9日星期日,农历正月十二,早报#微语早读。 1、2025WTT新加坡大满贯:王楚钦林诗栋获得男双冠军; 2、海南万宁快查快处一起缺斤短两案件:拟罚款5万元,责令停业3个月; 3、四川宜宾市筠连县山体…...

MOSSE目标跟踪算法详解
1. 引言 MOSSE算法(Multi-Object Spectral Tracking with Energy Regularization)是多目标跟踪领域的一座里程碑式成果,被认为是开创性的工作,为后续研究奠定了重要基础。该算法通过创新性地结合频域特征分析与能量正则化方法&am…...

生成式聊天机器人 -- 基于Pytorch + Global Attention + 双向 GRU 实现的SeqToSeq模型 -- 下
生成式聊天机器人 -- 基于Pytorch Global Attention 双向 GRU 实现的SeqToSeq模型 -- 下 训练Masked 损失单次训练过程迭代训练过程 测试贪心解码(Greedy decoding)算法实现对话函数 训练和测试模型完整代码 生成式聊天机器人 – 基于Pytorch Global Attention 双向 GRU 实…...

本地部署的DeepSeek-R1-32B与DeepSeek-R1-7B模型效果对比
本地部署的DeepSeek-R1-32B与DeepSeek-R1-7B模型效果对比 在当今人工智能快速发展的时代,大语言模型(Large Language Model, LLM)的应用场景日益广泛。无论是企业级应用还是个人开发,本地部署大语言模型已经成为一种趋势。DeepSeek-R1-32B和DeepSeek-R1-7B作为DeepSeek系列…...

AWS Fargate
AWS Fargate 是一个由 Amazon Web Services (AWS) 提供的无服务器容器计算引擎。它使开发者能够运行容器化应用程序,而无需管理底层的服务器或虚拟机。简而言之,AWS Fargate 让你只需关注应用的容器本身,而不需要管理运行容器的基础设施&…...

表单与交互:HTML表单标签全面解析
目录 前言 一.HTML表单的基本结构 基本结构 示例 二.常用表单控件 文本输入框 选择控件 文件上传 按钮 综合案例 三.标签的作用 四.注意事项 前言 HTML(超文本标记语言)是构建网页的基础,其中表单(<form>&…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...

使用ch340继电器完成随机断电测试
前言 如图所示是市面上常见的OTA压测继电器,通过ch340串口模块完成对继电器的分路控制,这里我编写了一个脚本方便对4路继电器的控制,可以设置开启时间,关闭时间,复位等功能 软件界面 在设备管理器查看串口号后&…...

C++ Saucer 编写Windows桌面应用
文章目录 一、背景二、Saucer 简介核心特性典型应用场景 三、生成自己的项目四、以Win32项目方式构建Win32项目禁用最大化按钮 五、总结 一、背景 使用Saucer框架,开发Windows桌面应用,把一个html页面作为GUI设计放到Saucer里,隐藏掉运行时弹…...
LTR-381RGB-01RGB+环境光检测应用场景及客户类型主要有哪些?
RGB环境光检测 功能,在应用场景及客户类型: 1. 可应用的儿童玩具类型 (1) 智能互动玩具 功能:通过检测环境光或物体颜色触发互动(如颜色识别积木、光感音乐盒)。 客户参考: LEGO(乐高&#x…...

详解ZYNQ中的 RC 和 EP
详解ZYNQ中的 RC 和 EP 一、ZYNQ FPGA 开发板基础( ZC706 ) 1. 核心特点 双核大脑 灵活积木: ZC706 集成了 ARM Cortex-A9 双核处理器(相当于电脑 CPU)和 FPGA 可编程逻辑单元(相当于可自定义的硬件积木…...