队列自定义类型

队列自定义类型示例展示了如何使用队列信号和槽在线程间发送自定义类型。

目录

概述

在这个示例中,我们创建了一个值对象类 Block,并将其注册到元对象系统中,以便我们可以使用队列信号和槽在线程间发送其实例。

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类注册为自定义类型,使用元对象系统。

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

    Window window;
    window.show();

    window.loadImage(createImage(256, 256));
    return app.exec();
}

将此调用放在这里是为了确保在建立任何使用该类型的信号-槽连接之前已注册类型。

main()函数的其余部分涉及设置伪随机数发生器的种子、创建并显示窗口以及设置默认图像。请参阅createImage()函数的实现代码。

进一步阅读

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

实际上,可以使用Q_DECLARE_METATYPE()宏和qRegisterMetaType()模板函数来注册自定义类型,但如果需要执行信号-槽通信或需要在运行时创建和销毁自定义类型的对象,则必须使用qRegisterMetaType()。

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

示例项目 @ code.qt.io

© 2024 Qt公司有限公司。本文档中包含的文档贡献是各自所有人的版权。本文件所提供的文档是根据自由软件基金会发布的GNU自由文档许可证版本1.3的条款授予的。Qt及其相应的标志是芬兰和/或在其他国家/地区的Qt公司有限公司的商标。所有其他商标均为其各自所有者的财产。