質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
87.20%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

解決済

QThreadのコードを教えてください

pythonnoob1
pythonnoob1

総合スコア18

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

5回答

0評価

2クリップ

635閲覧

投稿2020/06/04 11:27

環境

Python 3.x
RaspberryPi 3 B
PyQt5

質問事項

まず、teamikl様に教えていただいたコードを載せさせていただきます。
(おそらく、私のやりたいことは、autoThreadInherit関数であると思いますので、その他のautoFrezz関数、autoTimer関数、autoThread関数は触れないでいきます。)

python

from PyQt5.QtCore import Qt, QObject, QTimer, QThread, pyqtSignal, pyqtSlot from PyQt5.QtWidgets import ( QApplication, QWidget, QPushButton, QLabel, QTextBrowser, QComboBox, QHBoxLayout, QVBoxLayout) class MainWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) self.initUI() def initUI(self): label = self.label = QLabel("") button1 = self.start_button = QPushButton("Start") button2 = self.stop_button = QPushButton("Stop") combo = QComboBox() textarea = QTextBrowser() # 左上コンボボックスで「Start」ボタンを押した時に呼び出す関数を切り替えます。 items = ["autoFreeze", "autoTimer", "autoThread", "autoThreadInherit"] func = None def set_start_func(label): nonlocal func if func: self.start_button.clicked.disconnect(func) func = getattr(self, label) self.start_button.clicked.connect(func) import inspect src = inspect.getsource(func) textarea.setText(src) combo.currentIndexChanged[str].connect(set_start_func) combo.addItems(items) vbox = QVBoxLayout() vbox.addWidget(combo) vbox.addWidget(label) vbox.addWidget(button1) vbox.addWidget(button2) hbox = QHBoxLayout(self) hbox.addLayout(vbox) hbox.addWidget(textarea) def setCount(self, count): # 関数が呼ばれていることは確認 print(count) # ラベルは更新されない -> イベントループが止まっていて描画更新がない self.label.setText("Count: {}".format(count)) # ラベルのプロパティ更新は確認 print(self.label.text()) def autoFreeze(self): """ GUI がフリーズしてしまうコード 原因: メインスレッドでの永久ループ => イベントループが処理されなくなる """ import time count = 0 while True: count += 1 self.setCount(count) time.sleep(1) ## XXX # 以下のコードを有効にし、自分でイベントループの処理を呼ぶと、 # 一時的に描画が更新され、ラベルの更新は確認できます # ただし、GUI は殆どフリーズしたままです(while time.sleepにより阻害されている状態) # QApplication.instance().processEvents() if count >= 30: # テスト用の為、30秒で解除 break def autoTimer(self): """ QTimerを使った改善方法 ※ タイマーはコードの都合上メソッド内に定義してしまいましたが、 実際は普通に関数として定義で良いです。 """ count = 0 def countUp(): nonlocal count # 外側のスコープの変数を使う宣言 count += 1 self.setCount(count) timer = QTimer(self) timer.timeout.connect(countUp) timer.start(1000) self.stop_button.clicked.connect(timer.stop) def autoThread(self): """ QThreadを使う場合 (推奨される使い方) この簡単な例では、全然スレッドを使う恩恵はありあせんが、 使い方のサンプルとして書いてみます。 """ class MyWorker(QObject): sendCount = pyqtSignal(int) finished = pyqtSignal() def __init__(self): super().__init__() self.timer = None @pyqtSlot() def stopTimer(self): if self.timer: self.timer.stop() self.finished.emit() @pyqtSlot() def startCountUp(self): # 注意点: ここで while 1: とするとスレッドのイベントループが止まります。 count = 0 def countUp(): nonlocal count count += 1 self.sendCount.emit(count) timer = self.timer = QTimer(self) timer.timeout.connect(countUp) timer.start(1000) thread = self.thread = QThread() worker = self.worker = MyWorker() worker.moveToThread(thread) worker.sendCount.connect(self.setCount) # <-- スレッドから値を受け取る thread.started.connect(worker.startCountUp) # <-- スレッド開始時に呼び出す thread.finished.connect(worker.deleteLater) # ワーカーのタイマーを止める場合 # worker.finished.connect(thread.quit) # self.stop_button.clicked.connect(worker.stopTimer) # スレッドを直接止める場合 self.stop_button.clicked.connect(thread.quit) # 終了時にエラーを出さない為の後始末 self.destroyed.connect(worker.deleteLater) self.destroyed.connect(thread.quit) # 終了時にストップボタンのスロット解除 @thread.finished.connect def tear_down(): self.stop_button.clicked.disconnect(thread.quit) # スレッド開始 thread.start() def autoThreadInherit(self): """ 動作するが、推奨でない使い方 run()をオーバーライドする使い方もドキュメントに記載はあります。 完全に間違った使い方というわけではありませんが、 何故、別の方法が推奨されているのか把握していないと、 意図しない動作に悩まさせられることがあります。 """ import time # XXX: QThreadのサンプルコードを参考にするとき、継承して使ってるコードには注意 class MyThread(QThread): sendCount = pyqtSignal(int) # XXX: run()を上書きすると、スレッドのイベントループ起動を上書きします # スレッドのイベントループは起動されません。一応、スレッドの実行自体は問題ありません。 def run(self): # XXX: 何故この方法が推奨されないのか # --> QTimer 等がスレッド上で使えない。 # run()を終了するとスレッドも終了なので # スレッドを維持する場合、ループが必要になります => Pythonのスレッドで良い count = 0 while 1: # XXX: <- infinity loop は abuse of thread count += 1 self.sendCount.emit(count) self.sleep(1) # XXX: このループは強制終了以外に終了手段がないため # スレッド終了時のfinished シグナルは送られません thread = self.thread = MyThread() thread.moveToThread(thread) # WRONG! thread.sendCount.connect(self.setCount) # XXX: スレッドのイベントループが動いてない為、処理されない例 # self.stop_button.clicked.connect(thread.terminate) # 直接呼び出しは可能(安全ではないかもしれません) terminate = lambda: thread.terminate() self.stop_button.clicked.connect(terminate) # 終了時にストップボタンのスロット解除 @thread.finished.connect def tear_down(): self.stop_button.clicked.disconnect(terminate) thread.start() def main(): import sys app = QApplication(sys.argv) win = MainWindow() win.resize(800, 600) win.setWindowTitle("CountUp Demo (QTimer/QThread)") win.show() sys.exit(app.exec_()) if __name__ == '__main__': main()

分からないこと

①line(22~28)
func=getattr(self,label) は func=self.label と同じ意味であると勉強しました。ここでのlabelは、GUI上にある空欄のテキストボックス?であると解釈しています。
そこで、if func: とは何を表すのでしょうか?
また、 self.start_button.clicked.disconnect(func) と self.start_button.clicked.connect(func) はどういう意味なのでしょうか?(ボタンを押すとfuncにconnect,disconnectするのは理解しているのですが、funcがどのように動作しているものなのか分からないので、結論何が何だか分からない。となってしまいます。)

②line(161~209)
ここにある while 1: (line 185) の説明に「このループは強制終了しか終了手段がない」とありますが、強制終了というのは、メインスレッドからの run()=Folse による run() の上書きでできるのでしょうか?

line(193~195)について
thread=self.thread=MyThread() →サブスレッドの作成を意味している?
thread.moveToThread(thread) →作成したサブスレッドへ移動している?
thread.sendCount.connect(self.setCount) →サブスレッドからメインスレッドのsetCount関数にデータを送れるようにしている?

line(201,202)について
terminate = lambda: thread.terminate() は
def terminate():
ruturn thread.terminate()
と同じであると勉強しました。 このとき、line 202 の実行でサブスレッドを終了させているのでしょうか?

line(205~207)について
そもそもline(205)は何を表しているのでしょうか?またline(206,207)はどういう動作をしているのでしょうか?

とりあえず、今現在、これらについて分かりません。
分からないことが多すぎて大変申し訳ありませんが、教えてください。
よろしくお願いします。

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

まだ回答がついていません

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

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

ただいまの回答率
87.20%

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

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

質問する

関連した質問

同じタグがついた質問を見る

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。