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

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

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

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

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Raspberry Pi

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

Q&A

解決済

1回答

1112閲覧

テキストボックスに文字を出力したい

pythonnoob1

総合スコア18

Python 3.x

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

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Raspberry Pi

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

0グッド

0クリップ

投稿2020/06/07 12:52

編集2020/06/07 12:55

環境

Python 3.x
RaspberryPi 3 B
PyQt5

やりたいこと

①テキストボックスに「A」、「B」および「C」を出力したいです。
②QThreadを用いたGUIのコードを作りました。おかしな点があれば教えて下さい。

GUIが起動し、自動ボタンを押してもテキストボックス内に文字が出力されません・・・

作成したコード

python

1import sys 2from PyQt5.QtWidgets import * 3from PyQt5.QtGui import * 4from PyQt5.QtCore import * 5import subprocess 6import time 7import numpy as np 8import cv2 9import RPi.GPIO as GPIO 10 11 12 13 14 15class Tab1Widget(QWidget): 16 17 def __init__(self, parent=None): 18 super().__init__(parent) 19 20 self.qt_thread = None 21 self.running = False 22 23 24 self.title = "GUI test" 25 self.left = 10 26 self.top = 10 27 self.width = 640 28 self.height = 480 29 self.initUI() 30 self.counter = 0 31 32 def initUI(self): 33 34 35 super(Tab1Widget, self).__init__() 36 37 38 btn1 = self.auto_button = QPushButton("自動", self) 39 btn2 = self.stop_button = QPushButton("停止", self) 40 41 btn1.clicked.connect(self.auto ) 42 btn2.clicked.connect(self.stop ) 43 44 self.textbox4 = QLineEdit(self) 45 46 47 label3 = QLabel("自動ドア") 48 label4 = QLabel("ドアの状態") 49 50 layoutA = QGridLayout() 51 layoutA.addWidget(label4,0,0) 52 layoutA.addWidget(self.textbox4,0,1) 53 layoutA.addWidget(btn1,1,0) 54 layoutA.addWidget(btn2,1,1) 55 56 layoutB = QVBoxLayout() 57 layoutB.addWidget(label3) 58 layoutB.addLayout(layoutA) 59 60 self.setLayout(layoutB) 61 62 self.show() 63 64 def closeEvent(self, event): 65 self.stop() 66 67 if self.qt_thread: 68 self.qt_thread.wait(2000) # sleep の 1秒以上待つ 69 70 71 super().closeEvent(event) 72 73 def stop(self): 74 75 76 print("cancel") 77 self.running = False 78 79 def setCount(self, alpha): 80 # 呼び出されたスレッドの確認 81 # print("setCount", count, current_thread()) 82 self.textbox4.setText("{}".format(alpha)) 83 84 85 def auto(self): 86 87 win = self 88 win.running = True 89 90 class MyQtThread(QThread): 91 92 sendCount = pyqtSignal(int) 93 sendString = pyqtSignal(str) 94 95 def run(self): 96 x=1 97 while 1: 98 if x==1: 99 alphabet="A" 100 self.sendCount.emit(alphabet) 101 self.sleep(1) 102 x+=1 103 104 elif x==2: 105 alphabet="B" 106 self.sendCount.emit(alphabet) 107 self.sleep(1) 108 x+=1 109 110 111 else: 112 alphabet="C" 113 self.sendCount.emit(alphabet) 114 self.sleep(1) 115 x=x-2 116 117 118 119 120 #for num in range(500): 121 #if not win.running: 122 #break 123 #self.sendCount.emit(num) 124 #self.sleep(1) 125 126 thread = self.qt_thread = MyQtThread() 127 thread.moveToThread(thread) # WRONG! 128 thread.sendString.connect(self.setCount) 129 130 # XXX: スレッドのイベントループが動いてない為、処理されない例 131 # self.stop_button.clicked.connect(thread.terminate) 132 133 # 直接呼び出しは可能(安全ではないかもしれません) 134 terminate = lambda: thread.terminate() 135 self.stop_button.clicked.connect(terminate) 136 137 # 終了時にストップボタンのスロット解除 138 @thread.finished.connect 139 def tear_down(): 140 self.stop_button.clicked.disconnect(terminate) 141 142 thread.start() 143 144 145 146 # self.destroyed.connect(thread.terminate) 147 148if __name__ == "__main__": 149 app = QApplication(sys.argv) 150 ex = Tab1Widget() 151 sys.exit(app.exec_())

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

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

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

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

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

guest

回答1

0

ベストアンサー

コードは試してませんが、取り急ぎ目に付く間違いは

sendCount = pyqtSignal(int) sendString = pyqtSignal(str)

と定義してるので、文字列を渡す方のシグナルを使います。

diff

1- self.sendCount.emit(alphabet) 2+ self.sendString.emit(alphabet)

スロットの接続側も併せて変更が必要です
追記: 接続側は sendString.connect となっているのを確認


QThread については、

  • moveToThread は不要
  • terminateで強制終了 → whileループを抜けて終了させる

具体的には

  • ループの条件式を while not self.isInterruptionRequested(): として、
  • 停止処理は thread.requestInterruption() をメインスレッド側で呼び出す。
  • runningフラグは省略できます。

QThread::isInterruptionRequested

投稿2020/06/07 13:24

編集2020/06/07 13:38
teamikl

総合スコア8760

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

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

teamikl

2020/06/07 13:52

Threadは __init__ 内で作って ボタンを押したときに start() とした方が良いかもしれません。 参考元のコード(私の書いたコード)では、状況が少し異なり 複数の実装方法を試していた為、スレッド自体を作り直す必要がありました。 きちんと中断出来るように実装すれば、 もう一度呼び出した時に、同じスレッドを再利用できます。
teamikl

2020/06/07 14:09

後、numpy, cv2, GPIO 辺りのモジュールは実際に使う時までは省いておきましょう。 コードを実行するのに不要なライブラリのインストールを強いられているのと、 コメントアウトするにも、実際に使われていないかを確認しなければいけないので、 出来れば、コードを全部載せるのではなく、 質問用に別途、問題を再現できる最小限のコードを作ってみてください。 (具体的には…未使用のモジュールや変数等は省けるはずです)
pythonnoob1

2020/06/07 15:50

ありがとうございます。 直すところとしましては ①sendCount→sendString ②while 1: → while not self.isInterruptionRequested(): ③質問「QThreadのコードを教えてください」の def cancelThreadLoop(self): をコピペさせていただきました。また、それに伴いつながりがあるところを少し変える。 ④moveToThreadを除く といった感じでしょうか?
teamikl

2020/06/08 02:26

そうですね、①sendCount→sendString で1番の問題は解決できるはずです。 残りはスレッドの使い方ですが、恐らく参考にしてるのは、 よくない例として提示したコードなので、 出来れば後の方に提示したコードを参考にしてもらえばいいと思います。 追加で、「終了時にストップボタンのスロット解除」の所も これもデモコードで複数の実装を切り替える為の処理で、 不要になります。(ストップボタンが1回しか働かなくなる)
teamikl

2020/06/08 02:36

>while 1: → while not self.isInterruptionRequested(): これに合わせて、 > 停止処理は thread.requestInterruption() をメインスレッド側で呼び出す。 も必要です。 ストップボタンを押したときに呼び出されるようにしてください。 直接スロットとして接続することは出来ず、 メインスレッド側で呼び出すには、 - terminate の代わりに self.stop へ connect - def stop内で requestInterruptionを呼び出す
pythonnoob1

2020/06/08 05:08

回答ありがとうございます。 ・ while not self.isInterruptionRequested(): の下の部分 thread = self.qt_thread = MyQtThread() thread.sendString.connect(self.setCount) self.stop_button.clicked.connect(self.stop) thread.finished.connect(lambda: print("Qt Thread Finished")) thread.start() ・def stop(self)内 (※〇はスペースを表しています) def stop(self): 〇〇self.running = False 〇〇if self.qt_thread: 〇〇〇〇self.qt_thread.requestInterruption() ・def closeEvent(self, event)内 def closeEvent(self, event): 〇〇self.stop() 〇〇if self.qt_thread: 〇〇〇〇self.qt_thread.wait(2000) 〇〇super().closeEvent(event) に変えるということでしょうか? また、def closeEvent内の注意書き「sleep の 1秒以上待つ」について ここでいうsleepとは、while内の self.sleep のことで間違いないでしょうか?
teamikl

2020/06/08 06:46 編集

> ・ while not self.isInterruptionRequested(): の下の部分 下の部分が何処を指すのかで、齟齬が出るかもしれませんが、 (while ループの内部という意味ではないことを確認) - while 1: -> while not self.isInterruptionRequested(): へ変更したうえで - スレッド作成している部分で、「停止ボタンのシグナル接続」... OK > ・def stop(self)内 (※〇はスペースを表しています) running のフラグは、現在のコードでは使われていませんが、 コメントアウトしたコード内にあったので、 不要であれば、ここもコメントして省いておいたほうが良いです。 - requestInterruption 呼び出し ... OK > ・def closeEvent(self, event)内 ... OK > ここでいうsleepとは、while内の self.sleep のことで間違いないでしょうか? はい。ここはもう少し詳しく説明すると、 終了時の後始末では、スレッド内のループが終了するのを待つ事が目的なので、 ループが1巡する時間を目安とします。 対象のコードでは sleep が一番時間のかかる処理なので sleep の時間を目安にしました。 他に時間のかかる処理がある場合は、スレッドを中断チェックをする箇所 (self.isInterruptionRequested()) も適切に設定する必要があります。 ---- 実際のケースに置き換えると GPIOからの入力の待機ループが(開始・開く・閉じる)と3つありましたが 各待機ループ内で(計3か所)中断のチェックが必要になります。 これが、例えばドアが閉まるまでスレッドの終了を待ちたいといった場合は、 開閉に掛かる時間が解らないと、waitの時間の予測はできません。 while not self.isInterruptionRequested(): # <-- ループがネストする場合ここでは時間が予測できない 〇〇開始ループ 〇〇開くループ 〇〇閉じるループ # ↑末尾のループが終わるまで、ループの中断ポイントが実行されない while 1: # どこでも中断可能にする場合は、各ループ内でチェックが必要 〇〇開始ループ内 if self.isInterruptionRequested(): return 〇〇開くループ内 if self.isInterruptionRequested(): return 〇〇閉じるループ内 if self.isInterruptionRequested(): return もしくは、ネストしないようにループのロジックを変更する必要があります。
pythonnoob1

2020/06/08 07:39

ありがとうございます。 runningrunningのフラグのこと失念しておりました。ありがとうございます。 whileがネストする場合は、各ループ内に if self.isInterruptionRequested(): return が必要ということですね。 if self.isInterruptionRequested(): return だけでよろしいのでしょうか? ------ if self.isInterruptionRequested(): ○○return もしくは ○○break ------ の意味でしょうか?
teamikl

2020/06/08 08:29

この辺りは、実際の周辺のコードを元に確認した方が良さそうですね。 ループがネストしてる場合は、break だと内側のループしか抜けないことに注意してください。 (if self.isInterruptionRequested(): が何処に置かれるかで、意味合いが変わってきます) return ではループと共に関数自体の実行もそこで終ります。 ループを抜けた後に何かコードを実行したい場合は、逆にreturnでは都合が悪く、 例外機構(try/except)を使ってループを抜ける等、他の対策が必要なことも有ります。
pythonnoob1

2020/06/08 09:18

ありがとうございます。 また改めてコードを書いてみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問