コンテンツにスキップ

第 3 章: Qt の最重要コンセプト:シグナルとスロット

GUI アプリケーションがインタラクティブであるためには、ユーザーの操作(イベント)に反応して何らかのアクションを実行する仕組みが必要です。 Qt フレームワークでは、この仕組みを シグナルとスロット (Signals and Slots) という非常にエレガントな方法で実現しています。

このコンセプトを理解することが、PySide6 をマスターするための鍵となります。

イベント駆動プログラミングの考え方

第 2 章で学んだように、GUI アプリはイベントループによって常にユーザーの操作を待ち受けています。 ユーザーがボタンをクリックしたり、テキストを入力したりする、その一つ一つが「イベント」です。

シグナルとスロットは、このイベントを処理するための仕組みです。

  • シグナル: あるウィジェットで特定のイベントが発生したことを知らせる「合図」です。例えば、QPushButtonはユーザーにクリックされるとclickedというシグナルを発信(emit)します。
  • スロット: シグナルを受け取って実行されるアクションです。Python では、任意の関数やメソッドをスロットとして使用できます。

そして、この二つを「接続(connect)」することで、「ボタンがクリックされたら、この関数を実行する」という一連の動作を定義します。

Signal Slot Diagram (画像: a Qt wiki より)

この仕組みの美しい点は、シグナルを発信する側(ボタンなど)が、それに誰が(どのスロットが)反応するかを全く知る必要がないことです。 これにより、コンポーネント間の結合度が低く、再利用性の高いコードを書くことができます。

実践:ボタンでラベルの文字を変える

百聞は一見に如かず。実際にシグナルとスロットを使ってみましょう。 ここでは、ボタンをクリックすると、ウィンドウ上のラベルのテキストが変化する簡単なアプリケーションを作成します。

以下のコードを signal_slot_example.py として保存してください。

# signal_slot_example.py
import sys
from PySide6.QtWidgets import (
    QApplication,
    QMainWindow,
    QWidget,
    QVBoxLayout,
    QPushButton,
    QLabel
)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("シグナルとスロットの実践")

        # --- ウィジェットの作成 ---
        self.label = QLabel("ここにテキストが表示されます")
        self.button = QPushButton("ここをクリック")

        # --- シグナルとスロットの接続 ---
        # buttonのclickedシグナルを、self.on_button_clickedメソッド(スロット)に接続
        self.button.clicked.connect(self.on_button_clicked)

        # --- レイアウトの設定 ---
        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.button)

        # QMainWindowは直接レイアウトを持てないため、
        # QWidgetを中央ウィジェットとして設定し、そこにレイアウトを適用する
        central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

    # --- スロットとして機能するメソッド ---
    def on_button_clicked(self):
        print("ボタンがクリックされました!") # コンソールへの出力
        self.label.setText("ボタンがクリックされました!")

# --- アプリケーションの実行 ---
if __name__ == "__main__":
    app = QApplication(sys.argv)
    main_win = MainWindow()
    main_win.show()
    sys.exit(app.exec())

コードの解説

  1. ウィジェットの準備: QLabelQPushButtonのインスタンスを作成します。
  2. レイアウト: QVBoxLayout(垂直ボックスレイアウト)を使って、ラベルとボタンを縦に並べます。QMainWindowでは、setCentralWidgetで中心となるウィジェットを設定するのが定石です。
  3. スロットの定義: on_button_clickedという名前のメソッドを定義します。これがボタンクリック時に実行したい処理(スロット)です。
  4. 接続: self.button.clicked.connect(self.on_button_clicked) がこのコードの核心部分です。
    • self.button.clicked: ボタンがクリックされたときに発信されるシグナル。
    • .connect(): シグナルとスロットを接続するメソッド。
    • self.on_button_clicked: 接続先のメソッド(スロット)。()を付けないことに注意してください。ここではメソッドオブジェクトそのものを渡しています。

このプログラムを実行し、ボタンをクリックしてみてください。 ウィンドウ上のラベルのテキストが変わり、同時にプログラムを実行したコンソールにもメッセージが出力されるはずです。

lambda式の活用

スロットの処理が非常に単純な一行で済む場合、わざわざメソッドを定義する代わりにlambda式を使うとコードを簡潔にできます。

# 接続部分をlambdaで書き換えた例
self.button.clicked.connect(lambda: self.label.setText("Lambdaで変更!"))

ただし、処理が複雑になる場合は、可読性のために独立したメソッドとして定義する方が良いでしょう。

シグナルとスロットは、PySide6 におけるインタラクションの基本です。 次の章では、この仕組みを使いながら、より多くの種類のウィジェットを学んでいきます。