(七)QT——消息事件机制&绘图&文件

server/2025/2/8 9:55:04/

目录

前言

消息事件机制 (Event System)

绘图 (Graphics & Drawing)

绘图设备

Qt 提供的主要绘图设备

Qt 主要绘图设备的特点

各个绘图设备的详细介绍

文件处理 (File Handling)

总结


前言

QT 是一个非常强大的图形用户界面(GUI)开发框架,它的消息事件机制、绘图功能以及文件处理能力都是其重要组成部分。下面是关于这些部分的一些概述。


消息事件机制 (Event System)

Qt 采用 事件驱动 的机制,即 应用程序的运行是由事件触发的。所有用户交互(如鼠标、键盘)或系统行为(如窗口重绘、计时器触发)都以 事件(Event) 的形式传递到 Qt 应用程序。

工作原理

Qt 通过 事件队列(Event Queue)事件循环(Event Loop) 来处理事件:

  1. 用户或系统产生事件(如鼠标点击)。
  2. 事件被添加到 Qt 的事件队列QApplication::exec() 进入事件循环)。
  3. Qt 事件分发机制 将事件发送给对应的 QObject 处理(如 mousePressEvent()keyPressEvent())。
  4. 组件根据事件执行相应的逻辑

QT 使用事件驱动的方式来管理应用程序的操作。每个 UI 元素(如按钮、窗口、文本框等)都会生成事件,这些事件会被送到事件队列中,并由应用程序的事件循环(Event Loop)逐一处理。基本上,QT 的事件机制涉及以下几个方面:

  • 事件循环:QT 应用程序的主线程通常会有一个事件循环,负责处理来自用户的输入事件(如鼠标点击、键盘输入等)以及其他系统事件(如定时器触发)。
  • 事件处理:UI 元素的类(如 QWidgetQPushButton)可以重写事件处理函数(如 mouseEventkeyEvent),从而对不同类型的事件作出响应。
  • 事件传播:事件可以通过父子组件链传播。例如,点击事件先在子组件中处理,如果没有处理,才会传递到父组件。
  • 自定义事件:你可以通过继承 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.hEventLabel.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()));
}

运行效果

  1. 在 Qt Creator 中创建并运行这个项目后,你会看到一个窗口,显示有一个标签。
  2. 当你点击鼠标、移动鼠标或者释放鼠标时,标签上的文本会相应地更新,显示鼠标的当前坐标。

这样,你就能够在 Qt Creator 中创建并运行一个带有事件处理的简单应用程序了!

mouseMoveEvent() 默认情况下只会在按下鼠标后触发,因为 QWidgetmouseTracking 属性默认是 false。如果你希望鼠标在移动时无需点击就触发 mouseMoveEvent(),可以在 EventLabel 的构造函数中启用鼠标追踪功能:

修改 mainwindow.cpp 使鼠标追踪生效

// 构造函数
EventLabel::EventLabel(QWidget *parent) : QLabel(parent)
{setAlignment(Qt::AlignCenter); // 设置文本居中setText("<h1>Move the mouse here</h1>"); // 初始文本setMouseTracking(true);  // 启用鼠标追踪
}

解释

  1. 默认情况下 (mouseTracking = false)
    • 只有当鼠标按下并拖动时,mouseMoveEvent() 才会被触发。
  2. 启用 mouseTracking = true
    • 只要鼠标移动到 QWidget 上,就会触发 mouseMoveEvent(),即使没有点击鼠标。

消息事件机制和信号和槽机制的关系

信号和槽机制(异步回调)

Qt 的 信号和槽(Signal & Slot) 机制是一种 高级的消息通信机制,用于对象之间解耦的事件处理。它是一种 基于发布-订阅模式的回调机制,允许不同对象之间进行交互,而不需要显式调用对方的方法。

工作原理

  1. 信号(Signal):对象 A 发送一个信号,例如按钮被点击 clicked()
  2. 槽(Slot):对象 B 连接了 A 的信号,并定义了相应的槽函数(例如 onButtonClicked())。
  3. Qt 内部管理信号-槽连接,当信号被触发时,Qt 事件系统会自动调用对应的槽函数。
特性消息事件机制(Event)信号和槽机制(Signal & Slot)
触发方式事件循环调度直接调用
传输机制事件队列直接/异步调用
是否跨线程不能可以
是否解耦否,需要重写是,对象不直接依赖
适用场景低级事件(鼠标、键盘、窗口重绘)高级对象交互(按钮点击、线程通信)
  • 事件机制 适用于 GUI 组件(鼠标、键盘、窗口重绘),需要重写 QEvent 处理函数
  • 信号-槽机制 适用于对象间的通信,更高级,更解耦,可以跨线程
  • 二者可以结合使用,在事件触发后发射信号,供其他组件监听。

🎯 应用建议

  • 简单交互(鼠标、键盘) → 事件处理 mousePressEvent()
  • 按钮点击、跨对象交互connect(signal, slot)
  • 复杂任务、跨线程 → 信号-槽机制

绘图 (Graphics & Drawing)

Qt 的绘图系统允许开发者使用相同的 API 在屏幕、打印设备等不同目标上绘制图形。其核心组成部分包括:

  1. QPainter(画笔):执行绘制操作。
  2. QPaintDevice(绘图设备):提供绘制的空间,例如窗口、图片、打印机等。
  3. QPaintEngine(绘图引擎):连接 QPainterQPaintDevice,提供底层绘图支持(通常无需手动使用)。

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 提供了多个 QPaintDeviceQPainter 需要在这些设备上绘制:

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() 事件中使用,而绘图交互通常涉及:

  1. 鼠标事件(mousePressEvent、mouseMoveEvent)
  2. 定时器事件(QTimer) 实现动画
  3. 自定义控件绘制(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();
}

运行项目

  1. 在 Qt Creator 中构建并运行项目。
  2. 鼠标点击并拖动窗口,你将看到鼠标拖动的路径逐渐绘制出来。

  • 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,例如 QWidgetQPixmapQImage 等。

QPaintDevice 本身是一个基类,不能直接使用,而是通过其子类来提供不同的绘图方式和特性。

Qt 提供的主要绘图设备

Qt 提供了四种常见的绘图设备,分别是:

  1. QPixmap - 适用于屏幕显示的优化图像。
  2. QBitmap - QPixmap 的子类,仅支持单色位图(1 位深度)。
  3. QImage - 适用于像素级访问的图像数据。
  4. 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() 操作单个像素。
适用场景
QPixmapQImage
用途绘图 & 显示处理 & 操作
优化屏幕绘制优化像素处理优化
像素级操作❌ 不能直接访问像素✅ 可直接操作像素
线程支持仅 GUI 线程可用于非 GUI 线程
跨平台显示可能不同保持一致
  • 如果只是显示图片,使用 QPixmap(性能更好)
  • 如果要修改图片像素,使用 QImage
  • 如果要在多线程中操作图片,使用 QImage
  • 如果要在绘图中使用 QImage,先转换为 QPixmap
② QBitmap

QBitmapQPixmap 的子类,表示 单色(黑白)位图,用于 遮罩(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
特性QBitmapQImageQPixmap
颜色深度1-bit (黑白)8-bit, 16-bit, 32-bit (支持透明)平台优化
用途遮罩、透明背景像素级处理、文件读写高效屏幕绘制
像素操作❌(不可修改像素)setPixel() / pixel()❌(不能修改)
存储方式GPU内存GPU
支持 Alpha 透明✅(ARGB)✅(但像素不可直接操作)
多线程支持
④ QPicture 介绍

QPictureQt 提供的一种绘图设备,它可以 记录重现 QPainter 的绘图命令。

特点

  1. 记录绘图操作:可以存储 QPainter 的绘制命令,并在稍后回放。
  2. 跨平台:使用 平台无关的二进制格式,与 Windows、Linux、Mac 兼容。
  3. 支持多种设备:可以绘制到 屏幕、SVG、PDF、PS、打印机等 设备上。
  4. 高效存储:不像 QPixmapQImage 存储像素数据,QPicture 仅存储绘图命令,占用的存储空间较小。
  5. 适用于复杂绘制:在需要 多次重复相同绘制 时,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 使用流程
  1. 创建 QPicture 对象
  2. 使用 QPainter::begin(&picture) 开始记录绘图命令
  3. 执行 QPainter 相关绘图操作(如 drawText()drawLine())。
  4. 使用 QPainter::end() 结束绘制,记录保存。
  5. 调用 QPicture::save("filename") 保存到文件(可选)。
  6. 使用 QPicture::load("filename") 重新加载绘图数据
  7. 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;
}

总结

  • 消息事件机制:通过事件循环和事件处理函数来响应用户输入。
  • 绘图:使用 QPainterQPixmapQGraphicsView 等类绘制图形、文本和图像。
  • 文件操作:通过 QFileQFileDialogQDir 类来处理文件和目录的读写及选择。

这些功能为你在 QT 中开发丰富的 GUI 应用程序提供了强大的支持。


http://www.ppmy.cn/server/165916.html

相关文章

Docker Desktop安装到其他盘

Docker Desktop 默认安装到c盘&#xff0c;占用空间太大了&#xff0c;想给安装到其他盘&#xff0c;网上找了半天的都不对 正确安装命令&#xff1a; start /w "" "Docker Desktop Installer.exe" install --installation-dirF:\docker命令执行成功&am…

LVM 逻辑卷管理器

目录 一、LVM基本概念 二、LVM的优势 三、LVM命令 四、LVM配置实例 1、创建逻辑卷 2、格式化逻辑卷 3、挂载逻辑卷 4、扩展逻辑卷 5、缩减逻辑卷 逻辑卷管理器是Linux核心所提供的逻辑卷管理功能。它在硬盘的硬盘分区之上&#xff0c;又建立一个逻辑层&#xff0c;使得磁…

Spring Boot框架知识总结(超详细)

前言 本篇文章包含Springboot配置文件解释、热部署、自动装配原理源码级剖析、内嵌tomcat源码级剖析、缓存深入、多环境部署等等&#xff0c;如果能耐心看完&#xff0c;想必会有不少收获。 一、Spring Boot基础应用 Spring Boot特征 概念&#xff1a; 约定优于配置&#…

BUU17 [RoarCTF 2019]Easy Calc1

自用 源代码 $(#calc).submit(function(){$.ajax({url:"calc.php?num"encodeURIComponent($("#content").val()),type:GET,success:function(data){$("#result").html(<div class"alert alert-success"><strong>答案:&l…

容器启动时报ssl错误解决办法

容器启动应用时报ssl链接错误时 在源代码中将数据库连接修改 因为容器启动时写的链接数据库位一个容器名&#xff0c;运行是系统理解为域名&#xff0c;域名就会识别进行ssl安全认证&#xff0c;如果链接信息中写上&useSSLfalse 即表示关闭ssl认证

Linux进程间通信:匿名管道与命名管道的详解

目录 1. 进程间通信 1.1 为何需要 1.2 核心原理 1.3 种类 2. 匿名管道 2.1 指令 2.2 原理 2.3 系统调用 2.4 使用情况 2.5 特性 3. 命名管道 3.1 指令 3.2 原理 3.3 函数接口 ​编辑 3.4 CS模式通信 3.4.1 公共端 服务端 客户端 3.4.2 服务端与客户端主函数 …

matlab simulink 模拟光伏电池板在不同光照下的输出功率曲线

1、内容简介 略 matlab simulink 112-模拟光伏电池板在不同光照下的输出功率曲线可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略

小型分布式发电项目优化设计方案

一、项目背景与目标 在能源转型的大趋势下&#xff0c;小型分布式发电项目凭借其高效、灵活等优势&#xff0c;成为满足特定区域用电需求的重要方式。本项目选址于[具体地点]&#xff0c;此地年均日照时长可观&#xff0c;具备良好的太阳能资源开发潜力。项目旨在构建一个稳定…