Wizard Notes

音楽信号解析の技術録、作曲活動に関する雑記

PyQt: シグナル/スロットを使ってスレッド中・終了時にウィジェットを描画

f:id:Kurene:20210209232828p:plain

  • 別スレッドで走っている計算処理の終了後、計算終了を通知するメッセージボックスを表示する
  • 別スレッドでオーディオファイルを再生し、再生終了時にQWidgetを操作する

というような、サブスレッド終了後にウィジェットを非同期的に操作する処理を書いてみました。

実装方法としては、PyQtのシグナル/スロットを利用します。

具体的な実装の流れとしては、

  • QWidget/QWindowクラスを継承したクラスで、シグナルsignal=pyqtSignal()をクラス変数として定義
  • スロット(値を受け渡す関数)を定義
  • スロットをシグナルにつなぐ
    • signal.connect(self.slot)
  • サブスレッド起動
    • 呼び出し元のオブジェクトを渡しておく
  • サブスレッドで条件に応じてsignal.emit()

となります。

実装

注意点として、仕様のためpyqtSignal()オブジェクトはクラス変数でなければなりません

# -*- coding: utf-8 -*-
import sys
import time
import threading

from PyQt5.QtWidgets import *
from PyQt5.QtCore import pyqtSignal


def subthread(caller, count):
    for k in range(count, 0, -1):
        caller.signal_countdown.emit(k)
        time.sleep(1)
    caller.signal_countdown.emit(0)
    caller.signal_message.emit()
    
    
class MainWindow(QWidget):
    signal_message   = pyqtSignal()
    signal_countdown = pyqtSignal(int)
    
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.flg = False
        
        self.button = QPushButton(f"Start thread")
        self.button.clicked.connect(self.on_change)
        layout.addWidget(self.button)
        
        # スロットとシグナルを接続
        self.signal_message.connect(self.message)
        self.signal_countdown.connect(self.countdown)

    def message(self):
        QMessageBox.information(None, "Message", "subthread is terminated.")
        self.button.setText(f"Start thread")
        self.flg = False
        
    def countdown(self, value):
        self.button.setText(f"{value}")
        
    def on_change(self):
        if not self.flg:
            self.flg = True
            th = threading.Thread(target=subthread, args=(self, 3))
            th.setDaemon(True)
            th.start()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    app.exit(app.exec_())

デモ

youtu.be