本页内容

已排入队列的自定义类型

“队列式自定义类型”示例演示了如何利用队列式信号和槽在线程之间传递自定义类型。

一个窗口,其中彩色方块由一个工作线程逐步渲染

概述

在此示例中,我们将创建一个值类Block ,并将其注册到元对象系统中,以便能够使用队列信号和槽在线程之间传递该类的实例。

块类

Block 类按照元对象系统的要求,在其公共部分提供了默认构造函数、复制构造函数和析构函数。该类描述了一个彩色矩形。

class Block
{
public:
    Block();
    Block(const Block &other);
    ~Block();

    Block(const QRect &rect, const QColor &color);

    QColor color() const;
    QRect rect() const;

private:
    QRect m_rect;
    QColor m_color;
};

Q_DECLARE_METATYPE(Block);

在使用该类型建立任何信号-插槽连接之前,我们仍需在运行时通过调用模板函数 `qRegisterMetaType()` 将其注册到元对象系统中。尽管本示例中我们不打算将该类型与 `QVariant ` 结合使用,但使用 `Q_DECLARE_METATYPE()` 声明新类型仍是一种良好的编程习惯。

Block 类的实现非常简单,因此此处不再赘述。

Window 类

我们定义了一个简单的Window 类,其中包含一个可接受Block 对象的公共槽。该类的其余部分主要负责管理用户界面和处理图像。

class Window : public QWidget
{
    Q_OBJECT

public:
    Window(QWidget *parent = nullptr);
    void loadImage(const QImage &image);

public slots:
    void addBlock(const Block &block);

private slots:
    void loadImage();
    void resetUi();

private:
    QLabel *label;
    QPixmap pixmap;
    QPushButton *loadButton;
    QPushButton *resetButton;
    QString path;
    RenderThread *thread;
};

Window 类还包含一个由RenderThread 对象提供的工作线程。该线程将发出信号,将Block 对象发送至窗口的addBlock(Block) 槽。

Window 类中最相关的部分是构造函数和addBlock(Block) 槽。

构造函数会创建一个用于渲染图像的线程,并设置一个用户界面,该界面包含一个标签和两个按钮,它们分别连接到该类中的相应槽。

Window::Window(QWidget *parent)
    : QWidget(parent), thread(new RenderThread(this))
{
    label = new QLabel(this);
    label->setAlignment(Qt::AlignCenter);

    loadButton = new QPushButton(tr("&Load image..."), this);
    resetButton = new QPushButton(tr("&Stop"), this);
    resetButton->setEnabled(false);

    connect(loadButton, &QPushButton::clicked,
            this, QOverload<>::of(&Window::loadImage));
    connect(resetButton, &QPushButton::clicked,
            thread, &RenderThread::requestInterruption);
    connect(thread, &RenderThread::finished,
            this, &Window::resetUi);
    connect(thread, &RenderThread::sendBlock,
            this, &Window::addBlock);

在最后一项连接中,我们将RenderThread 对象中的一个信号连接到窗口中的addBlock(Block) 槽。

    ...
    setWindowTitle(tr("Queued Custom Type"));
}

构造函数的其余部分仅用于设置窗口的布局。

addBlock(Block) 槽通过构造函数中建立的信号-槽连接,从渲染线程接收块:

void Window::addBlock(const Block &block)
{
    QColor color = block.color();
    color.setAlpha(64);

    QPainter painter;
    painter.begin(&pixmap);
    painter.fillRect(block.rect(), color);
    painter.end();
    label->setPixmap(pixmap);
}

我们只需在这些块到达时将其绘制到标签上即可。

RenderThread 类

RenderThread 类负责处理图像,创建Block 对象,并利用sendBlock(Block) 信号将它们发送给示例中的其他组件。

class RenderThread : public QThread
{
    Q_OBJECT

public:
    RenderThread(QObject *parent = nullptr);
    ~RenderThread();

    void processImage(const QImage &image);

signals:
    void sendBlock(const Block &block);

protected:
    void run();

private:
    QImage m_image;
};

此处未列出构造函数和析构函数。它们负责设置线程的内部状态,并在线程销毁时进行清理。

处理过程通过processImage() 函数启动,该函数调用了RenderThread 类对QThread::run()函数的重写版本:

void RenderThread::processImage(const QImage &image)
{
    if (image.isNull())
        return;

    m_image = image;
    start();
}

void RenderThread::run()
{
    const int size = qMax(m_image.width()/20, m_image.height()/20);
    for (int s = size; s > 0; --s) {
        for (int c = 0; c < 400; ++c) {

暂且不考虑图像处理的具体细节,我们可以看到,包含代码块的信号是以通常的方式发出的:

    ...
            const Block block(QRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1),
                        QColor(red/n, green/n, blue/n));
            emit sendBlock(block);
            if (isInterruptionRequested())
                return;
            msleep(10);
        }
    }
}

每个发出的信号都会被放入队列,并稍后传递到窗口的addBlock(Block) 槽中。

注册类型

在示例的main() 函数中,我们通过调用模板函数qRegisterMetaType(),将Block 类作为自定义类型注册到元对象系统中:

intmain(intargc, char *argv[])
{
    QApplication app(argc,argv);
    qRegisterMetaType<Block>();

    Window window;
    window.show();

    window.loadImage(createImage(256, 256));
    returnapp.exec();
}

在此处调用该方法,是为了确保在使用该类型的任何信号-插槽连接建立之前,该类型已成功注册。

main() 函数的其余部分负责为伪随机数生成器设置初始值、创建并显示窗口,以及设置默认图像。有关createImage() 函数的实现,请参阅源代码。

进一步阅读

本示例演示了如何将自定义类型注册到元对象系统中,以便在不同线程之间的信号-槽连接中使用该类型。

实际上,Q_DECLARE_METATYPE() 宏和qRegisterMetaType() 模板函数均可用于注册自定义类型,但只有当需要进行信号-插槽通信,或者需要在运行时创建和销毁自定义类型的对象时,才需要使用qRegisterMetaType()。

有关在 Qt 中使用自定义类型的更多信息,请参阅《创建自定义 Qt 类型》文档。

示例项目 @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.