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.

Beispielprojekt @ code.qt.io

© 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.