「Qtの基礎 - シグナルとスロット」の版間の差分
(→概要) |
細 (文字列「__FORCETOC__」を「{{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This pag…) |
||
| 268行目: | 268行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
{{#seo: | |||
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki | |||
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 | |||
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux | |||
|image=/resources/assets/MochiuLogo_Single_Blue.png | |||
}} | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:Qt]] | [[カテゴリ:Qt]] | ||
2024年10月14日 (月) 10:58時点における最新版
概要
シグナルおよびスロットの詳細は、以下のWebサイトを参照すること。
https://blog.qt.io/jp/2010/07/20/create-signals-and-slots-2
connect関数
Qt::ConnectionType
connect関数のQt::ConnectionTypeの設定により動作が異なる。
| 接続タイプ(定数) | 動作 |
|---|---|
| Qt::AutoConnection | デフォルトの設定である。 SenderとReceiverが同じスレッドに存在する場合は、 Qt::DriectConnectionが使用される。それ以外の場合は、 Qt::QueuedConnectionが使用される。接続タイプはシグナルを emitする時に決定する。
|
| Qt::DriectConnection | シグナルを呼び出したスレッドから対象のスロットを呼び出す。(同期呼び出し) |
| Qt::QueuedConnection | Receiverのスレッド上でスロット関数が呼び出される。 シグナルはReceiver側のキューに入れられて、Receiverのイベントルーパーからスロット関数が呼び出される。(非同期呼び出し) |
| Qt::BlockingQueuedConnection | 基本的な動作は、Qt::QueuedConnectionと同様であるが、 シグナルの呼び出した側は、受信側のスロットの実行終了を待ち合わせる。 多用するとデッドロックを招きやすくなる。 例えば、Receiver側がSender側と同じスレッドに存在する場合には、必ず、デッドロックすることに注意する。 |
| Qt::UniqueConnection | 他の接続と組み合わせてビットORで設定する接続タイプである。 この設定が指定されている場合、オブジェクト間のシグナルとスロット、または、シグナルとシグナルの接続の組み合わせは1度しかできなくなる。 2つ以上同じシグナル・スロットの組み合わせで connect関数を実行した場合、connect関数は失敗する。この設定は、Qt 4.6で追加された。 |
connect関数
connect関数は大きく分けて、2種類、細かく分けて4種類の種類が存在する。
| connect関数のシンタックス | スタイルの呼称 |
|---|---|
| Stringベース | Qt 4スタイル |
| Functorベース | Qt 5スタイル |
| Lambdaスタイル 1 | |
| Lambdaスタイル 2 |
// Qt 4スタイルのconnect関数の形式
QMetaObject::Connection QObject::connect(Senderのポインタ, SIGNAL(シグナルの関数名(引数の型, ...)), Receiverのポインタ, SLOT(スロットの関数名(引数の型, ...)), Qt::ConnectionType type = Qt::AutoConnection)
// 例.
connect(sender, SIGNAL(value3Changed(int)), receiver, SLOT(setValue3(int)));
// Qt 5スタイルのconnect関数の形式
QMetaObject::Connection QObject::connect(Senderのポインタ, &シグナルのクラス名::シグナルの関数名, Receiverのポインタ, &スロットのクラス名::スロットの関数名, Qt::ConnectionType type = Qt::AutoConnection)
// 例.
connect(sender, &Sender::value3Changed, receiver, &Receiver::setValue3);
connect関数の第2引数と第4引数において、Qt 4スタイルではSIGNAL、SLOTというマクロを使用する。
このマクロは、Qt側で文字列(const char *signalのように)に変換される。
Qt 5スタイルと異なる点を、以下に示す。
- マクロの有無
- クラス名を記述するかどうか
- 仮引数を明示的に記述するかどうか
// Lambdaスタイル 1のconnect関数の形式
QMetaObject::Connection connect(Senderのポインタ, &Senderのクラス名::シグナルの関数名, [=]() { 処理1; 処理2; ...; });
// 例.
connect(sender, &Sender::valueChanged, [=]() { qDebug() << "Lambda Style1 signal received."; });
// Lambdaスタイル 2のconnect関数の形式
QMetaObject::Connection connect(Senderのポインタ, &Senderのクラス名::シグナルの関数名, コンテキストオブジェクト, [=]() { 処理1; 処理2; ...; }, Qt::QueuedConnection);
// 例.
connect(sender, &MyObject1::valueChanged, this, [=]() { qDebug() << "Lambda Style2 signal received."; }, Qt::QueuedConnection);
Lambda形式は、Receiverのスロット関数を作成しなくてよいため、シグナルが発生しているかどうかログを出力する時に便利である。
Lambdaスタイル 1は、引数にQt::ConnectionTypeが無い。
また、Qt::ConnectionTypeがQt::DirectConnection固定となる。
つまり、SenderとReceiverが同一スレッドで動作することが前提となる。
send関数により、Senderを取得することもできない。
Lambdaスタイル 2は、Qt 5.2で追加されたシンタックスである。
Lambdaスタイル 1と比較すると、第3引数にコンテキストが追加されて、第5引数にQt::ConnectionTypeが追加されている。
Qt::ConnectionTypeが指定できるため、Qt::DirectConnection以外の動作も可能である。
また、QObject::sender関数も使用できる。
上記のLambdaスタイル 1およびLambdaスタイル 2の例では、ラムダ式にキャプチャ式 [=]を使用しているが、キャプチャ式 [=]以外のキャプチャ記法も使用できる。
| Stringベース | Functorベース | |
|---|---|---|
| 型チェックのタイミング | 実行時 | コンパイル時 |
| 暗黙の型変換 | 不可 | 可能 |
| シグナルをラムダ式で接続できる | 不可 | 可能 |
| シグナルより多くの引数を持つスロットに シグナルを接続できる |
可能 | 不可 |
| QMLに接続できる | 可能 | 可能 |
シグナルとスロット
connect関数を使用して、シグナルとスロットの設定を行う。
connect(sender, SIGNAL(signal), receiver, SLOT(slot));
以下に、connenct関数の引数の意味を記載する。
- sender
- 信号が発生するコントロールIDまたはクラスのアドレスを渡す。
- SIGNAL(signal)
- signalに信号とする関数名を渡す。
- 例: プッシュボタンの場合、
SIGNAL(clicked())と記述する。
- receiver
- 信号を受信するコントロールIDまたはクラスのアドレスを渡す。
- SLOT(slot)
- 信号を受信した時に呼び出す関数名を渡す。
connect関数の使用方法
#include "mainwindow.h"
#include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton* button = new QPushButton("Quit");
QObject::connect(button, SIGNAL(clicked()), &app, SLOT(quit()));
button->show();
return app.exec();
}
静的メンバ関数のconnect
静的メンバ関数は、インスタンスを生成せずに実行することができる。
通常のメンバ関数の呼び出しと同様、thisからシグナルを発信して、thisでシグナルを受ければよい。
また、送信側と受信側の両方がthisの場合、受信側インスタンスの指定を省略できるconnectのオーバーロード関数も存在する。
以下の例では、コンストラクタでシグナルとスロットを接続して、画面が表示された時、showEventメソッドでシグナルをemitしている。
また、送信側のクラスのインスタンスは生成していない。
// MainWindow.cpp
#include "MainWindow.h"
#include "ui_MainWindow.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this, &MainWindow::testSignal1, this, &QObjectEx::staticSlot);
connect(this, &MainWindow::testSignal2, this, &QObjectEx::staticSlot);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::showEvent(QShowEvent *event)
{
QMainWindow::showEvent(event);
emit testSignal1(10);
emit testSignal2(20);
}
// MainWindow.h
#pragma once
#include <QMainWindow>
#include "QObjectEx.h"
namespace Ui {class MainWindow;}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void testSignal1(int);
void testSignal2(int);
protected:
void showEvent(QShowEvent *) override;
private:
Ui::MainWindow *ui;
};
// QObjectEx.h
#pragma once
#include <QObject>
class QObjectEx : public QObject
{
Q_OBJECT
public slots:
static void staticSlot(int i)
{
qDebug() << i;
}
};
Qt Designerで設定したシグナルおよびスロット
Qt Designerで設定したシグナルおよびスロットは、connect関数を使用せずに呼び出すことができる。
ソースコード上でconnect関数を使用せずにスロット関数が呼び出される理由を以下に示す。
まず、QWidgetクラスやQMainWindowクラス等を継承した派生クラスにおいて、コンストラクタに自動生成されるソースコードで、以下の記述がある。
ui->setupUi(this);
次に、setupUiメソッドの定義の最後に、以下の記述がある。
以下に示すQMetaObject::connectSlotsByNameメソッドは、オブジェクト(引数)が持つ全てのスロットに対して、
on_<子オブジェクト名>_<子オブジェクトのシグナル名>を満たすスロット名の存在を確認する。
void setupUi(QMainWindow *MainWindow)
{
// ...略
QMetaObject::connectSlotsByName(MainWindow);
}