警告

本节包含从 C++ 自动翻译到 Python 的代码片段,可能包含错误。

队列自定义类型#

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

../_images/queuedcustomtype-example.png

内容

概述#

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

块类#

Block 类在类的公共部分提供了默认构造函数、复制构造函数和析构函数,这些函数是元对象系统所需。该类描述了一个彩色矩形。

class Block():

# public
    Block()
    Block(Block other)
    ~Block()
    Block(QRect rect, QColor color)
    color = QColor()
    rect = QRect()
# private
    m_rect = QRect()
    m_color = QColor()

Q_DECLARE_METATYPE(Block)

我们还需要在设置任何使用此类型的信号-槽连接之前,通过调用模板函数 qRegisterMetaType() 在运行时将其注册到元对象系统中。即使在这个例子中我们无意使用与 QVariant 的类型,也是很好的做法,也要使用 Q_DECLARE_METATYPE() 声明新类型。

Block 类的实现很简单,所以这里省略其代码。

窗口类#

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

class Window(QWidget):

    Q_OBJECT
# public
    Window(QWidget parent = None)
    def loadImage(image):
# public slots
    def addBlock(block):
# private slots
    def loadImage():
    def resetUi():
# private
    label = QLabel()
    pixmap = QPixmap()
    loadButton = QPushButton()
    resetButton = QPushButton()
    path = QString()
    thread = RenderThread()

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

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

构造函数创建了一个用于渲染图像的线程,设置了一个包含标签和两个按钮的用户界面,这些按钮连接到类中的槽。

def __init__(self, parent):
    super().__init__(parent)
    self.thread = RenderThread(self)
label = QLabel(self)
label.setAlignment(Qt.AlignCenter)
loadButton = QPushButton(tr("Load image..."), self)
resetButton = QPushButton(tr("Stop"), self)
resetButton.setEnabled(False)
loadButton.clicked.connect(
        self, QOverload<>.of(Window.loadImage))
resetButton.clicked.connect(
        thread.requestInterruption)
thread.finished.connect(
        self.resetUi)
thread.sendBlock.connect(
        self.addBlock)

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

    ...

setWindowTitle(tr("Queued Custom Type"))

构造函数的其余部分只是设置了窗口的布局。

addBlock(Block) 槽通过在构造函数中设置的信号-槽连接接收来自渲染线程的块

def addBlock(self, block):

    color = block.color()
    color.setAlpha(64)
    painter = QPainter()
    painter.begin(pixmap)
    painter.fillRect(block.rect(), color)
    painter.end()
    label.setPixmap(pixmap)

我们简单地将这些涂在标签上,当它们到达时。

RenderThread#

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

class RenderThread(QThread):

    Q_OBJECT
# public
    RenderThread(QObject parent = None)
    ~RenderThread()
    def processImage(image):
# signals
    def sendBlock(block):
# protected
    def run():
# private
    m_image = QImage()

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

使用 processImage() 函数启动处理,该函数调用 RenderThread 类对 run() 方法的重写

def processImage(self, image):

    if image.isNull():
        return
    m_image = image
    start()

def run(self):

    size = qMax(m_image.width()/20, m_image.height()/20)
    for s in range( 0, size, -1):
        for c in range(0, 400):

忽略图像处理方式的细节,我们看到包含块的信号以通常的方式发射

    ...

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

每个发射的信号将被排队,并稍后发送到窗口的 addBlock(Block) 槽。

注册类型#

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

if __name__ == "__main__":

    app = QApplication([])
    qRegisterMetaType<Block>()
    window = Window()
    window.show()
    window.loadImage(createImage(256, 256))
    sys.exit(app.exec())

将此调用放在这里是为了确保在使用它之前已注册类型。

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

更多阅读#

此示例显示了如何将自定义类型与元对象系统关联,以便在线程之间的信号-槽连接中使用。

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

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

示例项目 @ code.qt.io