Benutzerdefinierter Typ in der Warteschlange
Das Beispiel für den benutzerdefinierten Typ in der Warteschlange zeigt, wie benutzerdefinierte Typen zwischen Threads mit Signalen und Slots in der Warteschlange gesendet werden können.
Übersicht
In diesem Beispiel erstellen wir eine Werteklasse, Block
, und registrieren sie im Meta-Objektsystem, um Instanzen davon zwischen Threads mit Signalen und Slots in der Warteschlange zu senden.
Die Blockklasse
Die Klasse Block
bietet den Standardkonstruktor, den Kopierkonstruktor und einen Destruktor im öffentlichen Abschnitt der Klasse, wie vom Metaobjektsystem gefordert. Die Klasse beschreibt ein farbiges Rechteck.
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);
Wir müssen sie dennoch zur Laufzeit beim Meta-Objektsystem registrieren, indem wir die Template-Funktion qRegisterMetaType() aufrufen, bevor wir irgendwelche Signal-Slot-Verbindungen herstellen, die diesen Typ verwenden. Auch wenn wir nicht beabsichtigen, den Typ mit QVariant in diesem Beispiel zu verwenden, ist es gute Praxis, den neuen Typ auch mit Q_DECLARE_METATYPE() zu deklarieren.
Die Implementierung der Klasse Block
ist trivial, weshalb wir sie hier nicht zitieren.
Die Fensterklasse
Wir definieren eine einfache Klasse Window
mit einem öffentlichen Slot, der ein Block
Objekt akzeptiert. Der Rest der Klasse befasst sich mit der Verwaltung der Benutzeroberfläche und dem Umgang mit Bildern.
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; };
Die Klasse Window
enthält auch einen Worker-Thread, der von einem RenderThread
Objekt bereitgestellt wird. Dieser sendet Signale aus, um Block
Objekte an den addBlock(Block)
Slot des Fensters zu senden.
Die wichtigsten Teile der Klasse Window
sind der Konstruktor und der Slot addBlock(Block)
.
Der Konstruktor erstellt einen Thread zum Rendern von Bildern und richtet eine Benutzeroberfläche mit einem Etikett und zwei Drucktasten ein, die mit Slots derselben Klasse verbunden sind.
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);
In der letzten dieser Verbindungen verbinden wir ein Signal im Objekt RenderThread
mit dem Slot addBlock(Block)
im Fenster.
...
setWindowTitle(tr("Queued Custom Type"));
}
Der Rest des Konstruktors legt einfach das Layout des Fensters fest.
Der Slot addBlock(Block)
empfängt Blöcke vom Rendering-Thread über die im Konstruktor eingerichtete Signal-Slot-Verbindung:
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); }
Wir malen diese einfach auf das Etikett, wenn sie ankommen.
Die RenderThread-Klasse
Die Klasse RenderThread
verarbeitet ein Bild, erstellt Block
Objekte und verwendet das Signal sendBlock(Block)
, um sie an andere Komponenten des Beispiels zu senden.
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; };
Der Konstruktor und der Destruktor werden hier nicht aufgeführt. Diese kümmern sich um die Einrichtung des internen Zustands des Threads und die Bereinigung, wenn er zerstört wird.
Die Verarbeitung wird mit der Funktion processImage()
gestartet, die die Neuimplementierung der Funktion QThread::run() durch die Klasse RenderThread
aufruft:
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) {
Ohne auf die Einzelheiten der Bildverarbeitung einzugehen, sehen wir, dass das Signal, das einen Block enthält, auf die übliche Weise ausgegeben wird:
... 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); } } }
Jedes Signal, das ausgegeben wird, wird in eine Warteschlange gestellt und später an den addBlock(Block)
Slot des Fensters geliefert.
Registrierung des Typs
In der Funktion main()
des Beispiels führen wir die Registrierung der Klasse Block
als benutzerdefinierten Typ im Meta-Objektsystem durch, indem wir die Template-Funktion qRegisterMetaType() aufrufen:
int main(int argc, char *argv[]) { QApplication app(argc, argv); qRegisterMetaType<Block>(); Window window; window.show(); window.loadImage(createImage(256, 256)); return app.exec(); }
Dieser Aufruf wird hier platziert, um sicherzustellen, dass der Typ registriert wird, bevor irgendwelche Signal-Slot-Verbindungen hergestellt werden, die ihn verwenden.
Der Rest der Funktion main()
befasst sich mit dem Setzen eines Seeds für den Pseudo-Zufallszahlengenerator, dem Erstellen und Anzeigen des Fensters und dem Setzen eines Standardbildes. Siehe den Quellcode für die Implementierung der Funktion createImage()
.
Weitere Lektüre
In diesem Beispiel wurde gezeigt, wie ein benutzerdefinierter Typ im Meta-Objektsystem registriert werden kann, so dass er mit Signal-Slot-Verbindungen zwischen Threads verwendet werden kann.
In der Praxis können sowohl das Makro Q_DECLARE_METATYPE() als auch die Schablonenfunktion qRegisterMetaType() verwendet werden, um benutzerdefinierte Typen zu registrieren, aber qRegisterMetaType() ist nur erforderlich, wenn Sie eine Signal-Slot-Kommunikation durchführen oder Objekte des benutzerdefinierten Typs zur Laufzeit erstellen und zerstören müssen.
Weitere Informationen zur Verwendung von benutzerdefinierten Typen mit Qt finden Sie im Dokument Creating Custom Qt Types.
© 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.