キューに入れられたカスタム・タイプ

キューイングされたカスタム・タイプの例では、キューイングされたシグナルとスロットを持つスレッド間でカスタム・タイプを送信する方法を示します。

概要

この例では、値クラス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 クラスの実装は些細なものなので、ここでの引用は避ける。

ウィンドウ・クラス

Block オブジェクトを受け取るpublicスロットを持つ単純なWindow クラスを定義します。クラスの残りの部分は、ユーザーインターフェイスの管理と画像の処理に関するものです。

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) スロットです。

コンストラクタは画像をレンダリングするスレッドを作成し、同じクラスのスロットに接続されたラベルと2つのプッシュ・ボタンを含むユーザー・インターフェースを設定します。

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 でのカスタム型の使用に関する詳細は、Creating Custom Qt Typesドキュメントを参照してください。

サンプルプロジェクト @ code.qt.io

©2024 The Qt Company Ltd. ここに含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。