yu00’s blog

プログラミングに関する備忘録です

シグナルとスロット詳細

PyQt5入門 PythonでGUI作成 - yu00’s blog


ここではシグナルとスロットについて,
詳しい使い方を説明します.

シグナルとスロットを接続する

シグナルとスロットを接続(設定)するにはconnect()を使います.

connect(slot)

シグナルにスロットslotを接続します.
(一部引数を省略しています.すべての引数はリファレンスを参照してください)

サンプルコード
app = QApplication(sys.argv)
button = QPushButton('button')
# スロットを設定
button.clicked.connect(app.quit)

スロットを解除する

一度接続したスロットを解除するにはdisconnect()を使います.

disconnect([slot])

スロットslotを解除します.slotを省略した場合,
そのシグナルに設定されたすべてのスロットが解除されます.

サンプルコード
app = QApplication(sys.argv)
button = QPushButton('button')
# スロットを設定
button.clicked.connect(app.quit)
# スロットを解除
button.clicked.disconnect(app.quit)    

一つのシグナルに複数のスロットを接続する

一つのシグナルに複数のスロットを設定することができます.
スロットは接続された順に実行されます.

サンプルコード

一つのボタンのシグナルに二つのラベルのスロットを設定しています.

button = QPushButton('button')
label1 = QLabel('label1')
label2 = QLabel('label2')

# ボタンが押された->ラベルを消す
button.clicked.connect(label1.clear)
button.clicked.connect(label2.clear)

複数のシグナルに一つのスロットを接続する

複数のシグナルに同じスロットを接続することができます.

サンプルコード

二つのボタンのシグナルに一つのラベルのスロットを設定しています.

button1 = QPushButton('button1')
button2 = QPushButton('button2')
label = QLabel('label')

# ボタンが押された->ラベルを消す
button1.clicked.connect(label.clear)
button2.clicked.connect(label.clear)

シグナルの引数をスロットに渡す

シグナルとスロットの関数に引数があった場合,
シグナルの引数と同じ値がスロットの引数に渡されます.
引数はいくつあってもよいですが,
引数の型は同じである必要があります.

サンプルコード

シグナルにQLineEdit.textChanged(str),
スロットにQLabel.setText(str)を使っています.

lineEdit = QLineEdit()
label = QLabel('label')

# テキストが変わった->テキストをセット
lineEdit.textChanged.connect(label.setText)

実行例

編集エリアに文字を入力すると同じ文字が下のラベルに
表示されます.
f:id:yu00:20150913175302p:plain

シグナルにオーバーロードがある場合

シグナルにオーバーロードがある場合は
connectのときどれを使うかを指定します.

例えばsignal(int, int)とsignal(QString)という
2つのオーバーロードがあるシグナルがあるとします.
この場合は次のように使い分けます.

signal[int, int].connect(slot1)
signal['QString'].connect(slot2)

このようにC++に対応するPythonの型かC++の型を文字で
指定します.

シグナルを自作する

シグナルを自作するにはpyqtSignal()とemit()を使います.

シグナルを作成するにはQObjectを継承したクラスのクラス変数として
pyqtSignal()を定義します.
そして,emit()を呼ぶことでシグナルが発行されます.

pyqtSignal([types])

シグナルを作成します.

types:
シグナルの引数の型です.型はC++に対応するPythonの型,または
C++の型名の文字で指定します.オーバーロードがある場合,
それぞれの型はリストで指定します.
(一部引数を省略しています)

次はpySignal()を使ってシグナルを作成する例です.

from PyQt5.QtCore import QObject, pyqtSignal

class MyObject(QObject):
    # signal1という名前の引数のないシグナルを作成
    signal1 = pyqtSignal()
    
    # 引数を持つ場合
    # signal2という名前のint型の二つの引数を持つシグナルを作成
    signal2 = pyqtSignal(int, int)
    
    # オーバーロードがある場合
    # signal3という名前のint, strの引数持つシグナルと
    # QStringの引数を持つシグナルを作成
    signal3 = pyqtSignal([int, str], ['QString'])

emit(*args)

シグナルを発行します.
args:スロットに渡される引数です.

サンプルコード

例として,Aボタンが押されたときに
シグナルを発行するウィジェットを作り,
Aボタンが押されたときに閉じるようにしてみます.

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import Qt, pyqtSignal

class MyWidget(QWidget):
    # シグナルを作成
    pressedA = pyqtSignal()
    
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.show()
        
    def init_ui(self):
        # シグナルとスロットを接続
        # Aボタンが押された->閉じる
        self.pressedA.connect(self.close)
    
    def keyPressEvent(self, e):
        if e.key() == Qt.Key_A:
            # Aボタンが押されたときシグナルを発行
            self.pressedA.emit()
            
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    sys.exit(app.exec_())

スロットを自作する

Pythonの関数をスロットとして使用することができます.

サンプルコード

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.show()
        
    def init_ui(self):
        self.button = QPushButton('button', self)

        # シグナルにスロットを接続
        self.button.clicked.connect(self.button_clicked)
        
    def button_clicked(self):
        """ 自作のスロット """
        print('button clicked')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    sys.exit(app.exec_())

シグナルを発行したオブジェクトを取得する

複数のシグナルに1つのスロットが設定されていたとき,
QObject.sender()を使うことでシグナルを発行した
オブジェクトを取得できます.

サンプルコード

2つのボタンどちらが押されたかを表示するプログラムを書いてみます.

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.show()
        
    def init_ui(self):
        self.button1 = QPushButton('1')
        self.button2 = QPushButton('2')

        layout = QVBoxLayout(self)
        layout.addWidget(self.button1)
        layout.addWidget(self.button2)

        # シグナルにスロットを接続
        self.button1.clicked.connect(self.button_clicked)
        self.button2.clicked.connect(self.button_clicked)
        
    def button_clicked(self):
        # シグナルを発行したオブジェクトを取得
        # buttonはself.button1かself.button2になる
        button = self.sender()
        if button is self.button1:
            print('button 1')
        elif button is self.button2:
            print('button 2')
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyWidget()
    sys.exit(app.exec_())