排队的自定义类型
队列自定义类型示例展示了如何在具有队列信号和插槽的线程之间发送自定义类型。
概述
在此示例中,我们创建了一个值类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
类,该类的公共槽接受一个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
对象提供的工作线程。它将发出信号,向窗口的addBlock(Block)
插槽发送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
类处理图像,创建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[]) { QApplicationapp(argc,argv); qRegisterMetaType<Block>(); 窗口 window; window.show(); window.loadImage(createImage(256, 256));returnapp.exec(); }
在此调用是为了确保在使用该类型的信号槽连接之前,该类型已被注册。
main()
函数的其余部分涉及为伪随机数生成器设置种子、创建和显示窗口以及设置默认图像。有关createImage()
函数的实现,请参阅源代码。
更多阅读
本示例展示了如何在元对象系统中注册自定义类型,以便在线程之间使用信号槽连接。
实际上,Q_DECLARE_METATYPE() 宏和qRegisterMetaType() 模板函数都可用于注册自定义类型,但只有在需要执行信号槽通信或需要在运行时创建和销毁自定义类型的对象时,才需要使用qRegisterMetaType()。
有关在 Qt 中使用自定义类型的更多信息,请参阅创建自定义 Qt 类型文档。
© 2025 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.