当前位置: 首页 > article >正文

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

目录

前言

消息事件机制 (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_OBJECT

public:
    // 构造函数
    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_OBJECT

public:
    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_OBJECT

public:
    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_OBJECT

public:
    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_OBJECT

public:
    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));
            }
        }

        // 绘制 QImage
        painter.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.kler.cn/a/536736.html

相关文章:

  • STM32的HAL库开发---高级定时器---输出比较模式实验
  • 【万字详细教程】Linux to go——装在移动硬盘里的Linux系统(Ubuntu22.04)制作流程;一口气解决系统安装引导文件迁移显卡驱动安装等问题
  • C++ Primer 数组
  • 2024~2025学年佛山市普通高中教学质量检测(一)【高三数学】
  • OpenCV:特征检测总结
  • C++----继承
  • 【AI论文】SFT铭记,RL泛化:基础模型后训练的比较研究
  • Java泛型详解
  • CSS实现文本自动平衡text-wrap: balance
  • 2024~2025学年佛山市普通高中教学质量检测(一)【高三数学】
  • 青少年编程与数学 02-008 Pyhon语言编程基础 25课题、文件操作
  • excel表中将无规律的文本型数字批量转化成真正的数字
  • 网络安全 | DDoS攻击解析与防御策略
  • nexus部署及配置https访问
  • 洛谷P8218 【深进1.例1】求区间和(前缀和)
  • ubuntu中 使用C++ FFmpeg拉取RTSP视频流
  • mapbox进阶,添加绘图扩展插件,绘制圆形
  • spring boot接收请求常用注解
  • 【华为OD机考】华为OD笔试真题解析(1)--AI处理器组合
  • 音视频的文件封装——AVI、MP4、MKV
  • [深度学习]神经网络-回归项目
  • Unity游戏(Assault空对地打击)开发(7) 爆炸效果
  • 前沿型CLI库——Clipanion
  • Qt 获取鼠标所在点颜色的RGB值,考虑多屏幕情况
  • 机器学习 - 容易混淆的目标函数和损失函数
  • 借助 Cursor 快速实现小程序前端开发