🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

2回答

3628閲覧

[PyQt]サブスレッドの終了を通知で知りたい

hachimitu

総合スコア36

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/03/14 04:03

#やりたいこと
サブスレッドの終了を通知で知りたい
#問題
以下コードではサブスレッド終了条件を"A"が押されたらにしています。(class Worker部分)
サブスレッド終了時にQMessage boxで通知終了を知らせたいのですがエラーがでてしまいました。
<エラーメッセージ>
QObject::setParent: Cannot set parent, new parent is in a different thread
サブスレッドでは呼び出せない模様

python

1import sys 2import logging 3import time 4import signal 5from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot, QTimer,Qt 6from PyQt5.QtWidgets import ( 7 QApplication, 8 QWidget, 9 QLineEdit, 10 QPlainTextEdit, 11 QPushButton, 12 QGridLayout, 13 QShortcut, 14 QAction, 15 QMessageBox, 16 ) 17from PyQt5.QtGui import QKeySequence 18from PyQt5 import QtGui 19FLAG = False 20class Worker(QObject): 21 22 done = pyqtSignal(str) 23 24 @pyqtSlot(str) 25 def execute(self, text): 26 global FLAG 27 FLAG = False 28 # シグナル経由で呼び出すと別スレッドで実行されますが、 29 # シグナル&スロット機構を使う場合は、イベントループを使う必要があり、 30 # スレッドのイベントループを正常に稼働させるためには、 31 # 個々の関数(スロット)は即時に終了しなければなりません。 32 # 33 # 時間のかかる処理を行いたい場合は、 34 # 毎回新しいスレッドを生成するか、この中で更に別スレッドを使います。 35 36 while not FLAG: 37 print("Waiting for signal...") 38 QMessageBox.information(None, "通知", "Aが押されました。") 39 time.sleep(1) 40 41 42class View(QWidget): 43 44 sendMessage = pyqtSignal(str) 45 46 def __init__(self, parent=None): 47 super().__init__(parent) 48 lineEdit = QLineEdit(self) 49 button = QPushButton("Execute", self) 50 textEdit = QPlainTextEdit(self) 51 52 lineEdit.returnPressed.connect(self._onClicked) 53 button.clicked.connect(self._onClicked) 54 55 layout = QGridLayout(self) 56 layout.addWidget(lineEdit, 0, 0) 57 layout.addWidget(button, 0, 1) 58 layout.addWidget(textEdit, 1, 0, 1, 2) 59 60 self._button = button 61 self._textEdit = textEdit 62 self._lineEdit = lineEdit 63 64 def _onClicked(self): 65 text = self._lineEdit.text() 66 self.sendMessage.emit(text) 67 self._lineEdit.clear() 68 69 @pyqtSlot(str) 70 def addResultText(self, text): 71 global FLAG 72 logging.info("addResultText: %s", text) 73 self._textEdit.appendPlainText(text) 74 FLAG = True 75 76def onControlC(): 77 global FLAG 78 print("A pressed") 79 FLAG = True 80 81 82def main(): 83 84 app = QApplication(sys.argv) 85 view = View() 86 87 # スレッド開始 88 thread = QThread(app) 89 worker = Worker() 90 worker.moveToThread(thread) 91 thread.start() 92 93 # view: メインスレッド, worker: 別スレッド 94 view.sendMessage.connect(worker.execute) 95 worker.done.connect(view.addResultText) 96 97 98 99 action = QAction(view) 100 action.setShortcut(QKeySequence('A')) 101 action.triggered.connect(onControlC) 102 view.addAction(action) 103 104 # プログラム終了時にエラーが出ないように、スレッドの後始末 105 # ※ QThread の使い方によって後始末の方法が異なるので注意 106 # QThread.quit は、スレッドのイベントループを終了します。 107 app.aboutToQuit.connect(thread.quit) 108 109 view.show() 110 111 sys.exit(app.exec_()) 112 113 114if __name__ == '__main__': 115 logging.basicConfig( 116 level=logging.DEBUG, 117 # ログにスレッド名を表示するための書式設定 118 # 119 # ※ 制限: QThread のスレッド名は Dummy-1 のように表示されますが、 120 # 今回は、メインスレッドかそうでないかを区別できれば充分です。 121 format="[%(threadName)-10s][%(levelname)-8s] %(message)s", 122 ) 123 main()

#質問
サブスレッド終了通知を知らせるための他の良い方法はありますでしょうか
よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

以下コードではサブスレッド終了条件を"A"が押されたらにしています。(class Worker部分)

スレッドの終了の通知には finished シグナルが提供されてますが、
質問の前提から認識の違いがあって、上記のコードではサブスレッドは終了しません。
(Worker.execute呼び出しでは、ひとつのサブスレッドを再利用するような構成となってます)

「サブスレッド(側で処理する関数の)終了」であれば、yuki23さんの回答にある通りで

  • サブスレッド側では done シグナルを使い通知して、
  • メインスレッド側でGUI操作 ダイアログを呼び出し。

以上で、目的達成で済む場合は良いのですが、

(イベントループを使う場合の) QThread の扱いに問題が有り、
スロットとして実行する関数は、長時間かかるような処理(while ループ)があると、
「スレッド側のシグナル受取が適切に処理されない問題」があります。

スロットとして呼び出す関数は、すぐに関数の実行が終わるようにする。
時間がかかる場合は、別スレッドに分ける。タイマーを使い実装、等の工夫が必要です。

現状の用途にとっては問題になってないかもしれませんが、while ~が実行されている間は、
サブスレッドのイベントループは適切に処理されてない状態なので、
サブスレッドの他のスロットは処理されず、execute() メソッドの完了待ちになります。

恐らく、私が以前の回答で書いたコメントですが

# 時間のかかる処理を行いたい場合は、 # 毎回新しいスレッドを生成するか、この中で更に別スレッドを使います。

while ループを使いたい場合は、このコメントの状況「時間のかかる処理を行いたい場合」に該当します。


「スレッド」絡みの状況は、少し複雑なのですが、説明のための前提として
QThreadには2通りの使い方があります

  • QThread (スレッド側でイベントループを使う場合 ... 今回の質問に掲載のコードはこちらの使い方)

実装: moveToThread を用いる
利点: シグナルによる通知を受け取れる。
制限: イベントループ内での実行となる為、スロットとして実装した関数はすぐに終わる必要がある。

  • QThread (スレッド側でイベントループを使わない)

実装: QThread を継承し、run メソッドをオーバーライドする (以前の質問にあった使い方)
利点: while や time.sleep が自由に使える
制限: シグナルによる通知で一部制限があり。

サブスレッドの終了について、時間のかかるループ中に、ループを中断しスレッドを終了させたい場合は、
後者の方法でスレッドを使い、以下の方法で処理を実装します。

  • isInterruptionRequested() ループの中断条件として使う。 (FLAGの代替)
  • requestInterruption() で中断要求を出す。

 即時、強制中断ではありません。isInterruptionRequested() が True を返すようになるので
ループを抜けるようにコーディングする必要はあります。
このメソッドは、スレッドセーフなメソッドなので、安全に別スレッドから呼び出せます。

  • 終了時に finished シグナルが発行されます

python

1class MyThread(QThread): 2 def run(self): 3 while self.isInterruptionRequested(): # FLAG の代替 4 ... 5 6## スレッドの使い方も変わってきます。(処理毎にスレッドを新規生成する使い捨て方式に) 7thread = MyThread() 8thread.finished.connect(...) # 終了通知 9thread.start() 10 11## スレッドを安全に中断する 12# thread.requestInterruption() # FLAG の代替 13 14## XXX: スレッドを強制終了 ... 呼び出し自体はスレッドセーフだが、処理内容によっては安全ではない。 15# thread.terminate()

※ 使い分けは用途によって変わってくるため、while ~でやりたいことの内容次第で、
どちらの方法が適してるかは変わってきます。


サブスレッド終了時にQMessage boxで通知終了を知らせたいのですがエラーがでてしまいました。

別スレッドでは GUI 操作は行わないように設計して下さい。大半の操作はスレッド安全では有りません。
GUI に関する操作が必要な場合は、 シグナル等で通知し、必ずメインスレッド側で呼び出すようにします。

投稿2021/03/16 09:15

teamikl

総合スコア8738

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

これでどうですか

python

1 while not FLAG: 2 print("Waiting for signal...") 3 time.sleep(1) 4 self.done.emit('tekitou')

python

1 @pyqtSlot(str) 2 def addResultText(self, text): 3 global FLAG 4 logging.info("addResultText: %s", text) 5 QMessageBox.information(None, "通知", "Aが押されました。") 6 self._textEdit.appendPlainText(text) 7 FLAG = True

投稿2021/03/15 22:49

yuki23

総合スコア1448

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問