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

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

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

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

while

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

Raspberry Pi

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

Q&A

解決済

2回答

872閲覧

QTheadを用いての自動ドアの開閉コードの添削をお願いします。

pythonnoob1

総合スコア18

Python 3.x

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

while

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

Raspberry Pi

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

0グッド

0クリップ

投稿2020/06/08 11:04

環境

Python 3.x
RaspberryPi 3 B
PyQt5

内容

自動ドアの開閉コードを作成してみました。
おかしな点があれば教えてください。

「この自動ドア、ドアが閉まってるときに物体検知をしても完全に閉まるまで開かないじゃん」というツッコミがあるかもしれませんが、今後改良をしていきたいと考えています。

今回は、このコードを実行させても動作上問題ないかということの添削をお願いします。
特に、def auto(self) 内の while 1: 以降をよろしくお願いします。

作成したコード

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 12Shoden_PIN=22 13 14Motor_PIN_1=24 15Motor_PIN_2=25 16 17Reed_PIN_1=17 18Reed_PIN_2=27 19 20GPIO.setmode(GPIO.BCM) 21GPIO.setup(Shoden_PIN, GPIO.IN) 22GPIO.setup(Reed_PIN_1, GPIO.IN) 23GPIO.setup(Reed_PIN_2, GPIO.IN) 24 25def _init_GPIO(): 26 GPIO.setmode(GPIO.BCM) 27 GPIO.setup(Motor_PIN_1, GPIO.OUT) 28 GPIO.setup(Motor_PIN_2, GPIO.OUT) 29 30def Motor_Forward(): 31 _init_GPIO() 32 GPIO.output(Motor_PIN_1, 1) 33 GPIO.output(Motor_PIN_2, 0) 34 35def Motor_Backward(): 36 _init_GPIO() 37 GPIO.output(Motor_PIN_1, 0) 38 GPIO.output(Motor_PIN_2, 1) 39 40def Motor_Stop(): 41 _init_GPIO() 42 GPIO.output(Motor_PIN_1, 0) 43 GPIO.output(Motor_PIN_2, 0) 44 45 46class Tab1Widget(QWidget): 47 48 def __init__(self, parent=None): 49 super().__init__(parent) 50 51 self.qt_thread = None 52 #self.running = False 53 self.title = "GUI test" 54 self.left = 10 55 self.top = 10 56 self.width = 640 57 self.height = 480 58 self.initUI() 59 self.counter = 0 60 61 def initUI(self): 62 63 64 super(Tab1Widget, self).__init__() 65 66 btn1 = self.auto_button = QPushButton("自動", self) 67 btn2 = self.stop_button = QPushButton("停止", self) 68 69 btn1.clicked.connect(self.auto ) 70 btn2.clicked.connect(self.stop ) 71 72 self.textbox4 = QLineEdit(self) 73 74 75 label3 = QLabel("自動ドア") 76 label4 = QLabel("ドアの状態") 77 78 layoutA = QGridLayout() 79 layoutA.addWidget(label4,0,0) 80 layoutA.addWidget(self.textbox4,0,1) 81 layoutA.addWidget(btn1,1,0) 82 layoutA.addWidget(btn2,1,1) 83 84 layoutB = QVBoxLayout() 85 layoutB.addWidget(label3) 86 layoutB.addLayout(layoutA) 87 88 self.setLayout(layoutB) 89 90 self.show() 91 92 def closeEvent(self, event): 93 self.stop() 94 95 if self.qt_thread: 96 self.qt_thread.wait(2000) 97 98 super().closeEvent(event) 99 100 def stop(self): 101 GPIO.cleanup() 102 Motor_Stop() 103 time.sleep(1) 104 GPIO.cleanup() 105 if self.qt_thread: 106 self.qt_thread.requestInterruption() 107 108 109 110 def setCount(self, alpha): 111 self.textbox4.setText("{}".format(alpha)) 112 113 114 def auto(self): 115 116 class MyQtThread(QThread): 117 118 sendString = pyqtSignal(str) 119 120 def run(self): 121 while 1: 122 if self.isInterruptionRequested(): 123 break 124 125 if GPIO.input(Shoden_PIN)==GPIO.HIGH: #物体検知 126 door="OPENED" 127 self.sendString.emit(door) 128 Motor_Forward() #ドアが開く 129 130 while 1: 131 if self.isInterruptionRequested(): 132 return 133 134 if GPIO.input(Reed_PIN_2)==GPIO.HIGH: #ドアが開ききる 135 GPIO.cleanup() 136 Motor_Stop() 137 time.sleep(1) 138 139 if self.isInterruptionRequested(): 140 return 141 142 GPIO.cleanup() 143 Motor_Backward() #ドア閉まる 144 145 while 1: 146 if self.isInterruptionRequested(): 147 return 148 149 if GPIO.input(Reed_PIN_1)==GPIO.HIGH: #ドアが閉まりきる 150 GPIO.cleanup() 151 Motor_Stop() 152 door="CLOSED" 153 self.sendString.emit(door) 154 time.sleep(1) 155 GPIO.cleanup() 156 break 157 158 else: 159 pass 160 161 break 162 163 else: 164 pass 165 break 166 167 else: 168 pass 169 170 171 thread = self.qt_thread = MyQtThread() 172 173 thread.sendString.connect(self.setCount) 174 175 self.stop_button.clicked.connect(self.stop) 176 177 thread.finished.connect(lambda: print("Qt Thread Finished")) 178 thread.start() 179 180 181if __name__ == "__main__": 182 app = QApplication(sys.argv) 183 ex = Tab1Widget() 184 sys.exit(app.exec_())

また、while内をもう1パターン作成してみました。

python

1while 1: 2 if self.isInterruptionRequested(): 3 break 4 5 while GPIO.input(Shoden_PIN)==GPIO.HIGH: #物体検知 6 door="OPENED" 7 self.sendString.emit(door) 8 if self.isInterruptionRequested(): 9 return 10 time.sleep(0.5) 11 12 Motor_Forward() #ドアが開く 13 14 while if GPIO.input(Reed_PIN_2)==GPIO.HIGH: #ドアが完全に開く 15 if self.isInterruptionRequested(): 16 return 17 time.sleep(0.5) 18 19 GPIO.cleanup() 20 Motor_Stop() 21 time.sleep(0.5) 22 GPIO.cleanup() 23 24 Motor_Backward() #ドアが閉まる 25 26 while GPIO.input(Reed_PIN_1)==GPIO.HIGH: #ドアが完全に閉まる 27 door="CLOSED" 28 self.sendString.emit(door) 29 if self.isInterruptionRequested(): 30 return 31 time.sleep(0.5) 32 33 GPIO.cleanup() 34 Motor_Stop() 35 time.sleep(0.5) 36 GPIO.cleanup()

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

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

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

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

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

teamikl

2020/06/08 11:31

>while if GPIO.input(Reed_PIN_2)==GPIO.HIGH: "while if" という構文はないのと、 while に書く場合は、これでは入力がHIGHの場合にループ内を実行となり 入力がHIGHになるまでの待機ループになってません。 コードのレイアウトとしては、ループの構造が過剰にネストしない後者のコードが良いのですが 中のロジックが全く変わってしまってます。
pythonnoob1

2020/06/08 11:41

while if はコピペ時のミスでした。申し訳ありません。 while GPIO.input(Reed_PIN_2)==GPIO.HIGH: ↓ while GPIO.input(Reed_PIN_2) != GPIO.HIGH: に直します。
guest

回答2

0

GUIコード

注意
モーターを用いず動作確認をしました。
モーターを動作させても動くと思います。
また、while内はwhileが重複していますので、もっと改善していくつもりです。

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 12Shoden_PIN=22 13 14Motor_PIN_1=24 15Motor_PIN_2=25 16 17Reed_PIN_1=17 18Reed_PIN_2=27 19 20GPIO.setmode(GPIO.BCM) 21GPIO.setup(Shoden_PIN, GPIO.IN) 22GPIO.setup(Reed_PIN_1, GPIO.IN) 23GPIO.setup(Reed_PIN_2, GPIO.IN) 24 25def setMotor(): 26 GPIO.setmode(GPIO.BCM) 27 GPIO.setup(Motor_PIN_1, GPIO.OUT) 28 GPIO.setup(Motor_PIN_2, GPIO.OUT) 29 30def Motor_Forward(): 31 GPIO.output(Motor_PIN_1, 1) 32 GPIO.output(Motor_PIN_2, 0) 33 34def Motor_Backward(): 35 GPIO.output(Motor_PIN_1, 0) 36 GPIO.output(Motor_PIN_2, 1) 37 38def Motor_Stop(): 39 GPIO.output(Motor_PIN_1, 0) 40 GPIO.output(Motor_PIN_2, 0) 41 42def Clean(): 43 GPIO.cleanup(24) 44 GPIO.cleanup(25) 45 46 47class Tab1Widget(QWidget): 48 49 def __init__(self, parent=None): 50 super().__init__(parent) 51 52 self.qt_thread = None 53 #self.running = False 54 self.title = "GUI test" 55 self.left = 10 56 self.top = 10 57 self.width = 640 58 self.height = 480 59 self.initUI() 60 self.counter = 0 61 62 def initUI(self): 63 64 65 #super(Tab1Widget, self).__init__() 66 67 btn1 = self.auto_button = QPushButton("自動", self) 68 btn2 = self.stop_button = QPushButton("停止", self) 69 70 btn1.clicked.connect(self.auto ) 71 btn2.clicked.connect(self.stop ) 72 73 self.textbox4 = QLineEdit(self) 74 self.textbox5 = QLineEdit(self) 75 76 77 label3 = QLabel("自動ドア") 78 label4 = QLabel("ドアの状態") 79 label5 = QLabel("モーター") 80 81 layoutA = QGridLayout() 82 layoutA.addWidget(label4,0,0) 83 layoutA.addWidget(self.textbox4,0,1) 84 layoutA.addWidget(label5,1,0) 85 layoutA.addWidget(self.textbox5,1,1) 86 layoutA.addWidget(btn1,2,0) 87 layoutA.addWidget(btn2,2,1) 88 89 layoutB = QVBoxLayout() 90 layoutB.addWidget(label3) 91 layoutB.addLayout(layoutA) 92 93 self.setLayout(layoutB) 94 95 self.show() 96 97 def closeEvent(self, event): 98 self.stop() 99 100 if self.qt_thread: 101 self.qt_thread.wait(2000) 102 103 super().closeEvent(event) 104 105 def stop(self): 106 if self.qt_thread: 107 self.qt_thread.requestInterruption() 108 109 110 111 def String(self, alpha): 112 self.textbox4.setText("{}".format(alpha)) 113 114 def Motor(self, beta): 115 self.textbox5.setText("{}".format(beta)) 116 117 118 def auto(self): 119 120 class MyQtThread(QThread): 121 122 sendString = pyqtSignal(str) 123 sendMotor = pyqtSignal(str) 124 125 def run(self): 126 while 1: 127 motor="" 128 self.sendMotor.emit(motor) 129 door="" 130 self.sendString.emit(door) 131 self.sleep(0.1) 132 133 if self.isInterruptionRequested(): 134 #Cleanup() 135 motor="Cleanup" 136 self.sendMotor.emit(motor) 137 self.sleep(1) 138 break 139 140 if GPIO.input(Shoden_PIN)==GPIO.HIGH: #物体検知 141 door="OPENED" 142 self.sendString.emit(door) 143 #setMotor() 144 #Motor_Forward() #ドアが開く 145 motor="FORWARD" 146 self.sendMotor.emit(motor) 147 self.sleep(1) 148 149 while 1: 150 151 if self.isInterruptionRequested(): 152 #Cleanup() 153 motor="Cleanup" 154 self.sendMotor.emit(motor) 155 self.sleep(1) 156 return 157 158 if GPIO.input(Reed_PIN_2)==GPIO.HIGH: #ドアが開ききる 159 #Motor_Stop() 160 motor="STOP" 161 self.sendMotor.emit(motor) 162 self.sleep(1) 163 164 if self.isInterruptionRequested(): 165 #Cleanup() 166 motor="Cleanup" 167 self.sendMotor.emit(motor) 168 self.sleep(1) 169 return 170 171 #Motor_Backward() #ドア閉まる 172 motor="BACKWARD" 173 self.sendMotor.emit(motor) 174 self.sleep(1) 175 176 while 1: 177 if self.isInterruptionRequested(): 178 #Cleanup() 179 motor="Cleanup" 180 self.sendMotor.emit(motor) 181 self.sleep(1) 182 return 183 184 if GPIO.input(Reed_PIN_1)==GPIO.HIGH: #ドアが閉まりきる 185 #Motor_Stop() 186 motor="STOP" 187 self.sendMotor.emit(motor) 188 door="CLOSED" 189 self.sendString.emit(door) 190 self.sleep(1) 191 break 192 193 else: 194 pass 195 196 break 197 198 else: 199 pass 200 #break 201 202 else: 203 pass 204 205 206 207 208 209 210 211 thread = self.qt_thread = MyQtThread() 212 213 thread.sendString.connect(self.String) 214 thread.sendMotor.connect(self.Motor) 215 216 217 self.stop_button.clicked.connect(self.stop) 218 219 thread.finished.connect(lambda: print("Qt Thread Finished")) 220 thread.start() 221 222 223if __name__ == "__main__": 224 app = QApplication(sys.argv) 225 ex = Tab1Widget() 226 sys.exit(app.exec_())

while 1 : 内の2パターン目

python

1while 1: 2 motor="" 3 self.sendMotor.emit(motor) 4 door="" 5 self.sendString.emit(door) 6 self.sleep(0.1) 7 8 if self.isInterruptionRequested(): 9 #Cleanup() 10 motor="Cleanup" 11 self.sendMotor.emit(motor) 12 #self.sleep(1) 13 break 14 15 while GPIO.input(Shoden_PIN)!=GPIO.HIGH: #物体検知 16 if self.isInterruptionRequested(): 17 #Cleanup() 18 motor="Cleanup" 19 self.sendMotor.emit(motor) 20 #self.sleep(1) 21 return 22 time.sleep(0.5) 23 24 #setMotor() 25 #Motor_Forward() #ドアが開く 26 motor="FORWARD" 27 self.sendMotor.emit(motor) 28 door="OPENED" 29 self.sendString.emit(door) 30 #self.sleep(1) 31 32 while GPIO.input(Reed_PIN_2)!=GPIO.HIGH: #ドアが完全に開く 33 if self.isInterruptionRequested(): 34 #Cleanup() 35 motor="Cleanup" 36 self.sendMotor.emit(motor) 37 #self.sleep(1) 38 return 39 time.sleep(0.5) 40 41 #Motor_Stop() 42 #time.sleep(0.5) 43 motor="STOP" 44 self.sendMotor.emit(motor) 45 self.sleep(1) 46 47 #Motor_Backward() #ドアが閉まる 48 motor="BACKWARD" 49 self.sendMotor.emit(motor) 50 #self.sleep(1) 51 52 while GPIO.input(Reed_PIN_1)!=GPIO.HIGH: #ドアが完全に閉まる 53 door="CLOSED" 54 self.sendString.emit(door) 55 if self.isInterruptionRequested(): 56 #Cleanup() 57 motor="Cleanup" 58 self.sendMotor.emit(motor) 59 #self.sleep(1) 60 return 61 time.sleep(0.5) 62 63 door="CLOSED" 64 self.sendString.emit(door) 65 #Motor_Stop() 66 #time.sleep(0.5) 67 motor="STOP" 68 self.sendMotor.emit(motor) 69 door="CLOSED" 70 self.sendString.emit(door) 71 self.sleep(1)

投稿2020/06/09 11:48

pythonnoob1

総合スコア18

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

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

0

ベストアンサー

最初に、GPIOは全く解らないので
毎回 setup/cleanup 処理が必要なのかあたりが疑問なのですが、
必要だとしたら関数側で一か所で行い、頻繁にコード中のいろんな場所に書かない方が良いです。


おかしなところ、
意図が解りませんが、ここの super~は、不要ではないですか?
親クラスのコンストラクタは __init__ 内で既に呼んでいます。

python

1 def initUI(self): 2 super(Tab1Widget, self).__init__()

スレッドの中断処理に関して。本題だと思いますが、ここしか確認してません。

def stop(self): # この部分は、スレッド内の末尾で呼び出す方が良い GPIO.cleanup() Motor_Stop() time.sleep(1) GPIO.cleanup() if self.qt_thread: self.qt_thread.requestInterruption()

ここでの注意点は、stop() が何処で呼び出されるか

requestInterruption() はメインスレッドで呼び出すので、
必然的に前の部分の処理もメインスレッドで呼び出されます。
(前提: 以前の質問からQThread側のイベントループは使わない実装方針)

すると、スレッド内でも GPIO への入出力があるので、
同時アクセスが起こる可能性があります。
GPIOライブラリ内部で排他制御がされているのであれば安全ですが、
少なくともPythonのコード上ではスレッドセーフではなく、
ライブラリの内部仕様を調べる必要がでてきます。

この問題を解決するには、別スレッドからのアクセスの排他制御するよりも
GPIOへのアクセスを単一スレッド内で行うようにする事で対応する方が簡単で、
スレッドの def run(self): のループを抜けた後に、
最後にクリーンナップ処理をして終わるように変更します。

python

1def run(self): 2 3 while not self.isInterruptionRequested(): 4 ... 省略 (ネストしたループの中断方法は別途考える) 5 6 # ここで終了処理 7 # GPIO関連の入出力を全てこのスレッドで実行すれば、別スレッドからの同時アクセスの心配がない 8 GPIO.cleanup() 9 Motor_Stop() 10 time.sleep(1) 11 GPIO.cleanup()

追記:
他には、QThread.finished シグナルで終了時に呼び出す方法もありますが、
その場合も、メインスレッドでの実行になるので、
出来れば GPIO 関連の入出力はスレッド側で完結した方が良いです。

投稿2020/06/08 11:51

編集2020/06/08 12:13
teamikl

総合スコア8664

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

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

pythonnoob1

2020/06/08 12:44

回答ありがとうございます。 >毎回 setup/cleanup 処理が必要なのかあたりが疑問なのですが、 必要だとしたら関数側で一か所で行い、頻繁にコード中のいろんな場所に書かない方が良いです。 分かりました。おそらく毎回必要ではないと思いますが、少しGPIOについて知識不足なところがありますので、コードに変更を加えながら実際にモーター動かしてみて、考えてみたいと思います。 また stop() の中の Motor_Stop() に関してですが、もしかしたら GPIO.cleanup() した時点でモーターが停止するかもしれません。 そのため、 各モーター動作後に GPIO.cleanup() をはさみ、if self.isInterruptionRequested(): を入れれば、 stop() 内の Motor_Stop() は不要になるかと思われます。 ということを、今気づきました。このことが合っているかは、分かりませんが、明日、実際にモーターを動かしてみようと思います。←まだ自作したコード(上のコード)で動かすのは怖いので、また別にモーターだけのコードを作成して試してみます。
teamikl

2020/06/08 13:04

>←まだ自作したコード(上のコード)で動かすのは怖いので、 >また別にモーターだけのコードを作成して試してみます。 そうですよね。 (上のコード)も実機で動かす前に、実際に期待通りの入出力になるのか print文などでログを出力して確認した方が良いですよ。 3重にネストしたループは、仮に正しく動いたとしても修正対象です。 意図しない挙動になった時に、確実に他のループの処理が繰り返されてしまう事になるので、 可読性も問題ですが、デバッグが困難になる傾向があります。フラットなループにした方が安全です。 ---- GPIO 側のからの入力って 開始時・ドアが開いた時・ドアが閉まった時の3種類ですか? この部分を Qt のシグナルに置き換えて、先に UI とスレッドの部分をテストする等はどうでしょう 後々、GPIOとの実際の入出力を別クラスに実装して置き換える感じで。 (「モーターだけのコード」でそれぞれのタイミングでシグナルを投げるように設計します)
pythonnoob1

2020/06/08 14:21

>(上のコード)も実機で動かす前に、実際に期待通りの入出力になるのか >print文などでログを出力して確認した方が良いですよ。 ありがとうございます。ぜひそうさせていただきます。 >フラットなループにした方が安全です。 できるだけフラットになるように考え直してみます。 >GPIO 側のからの入力って 開始時・ドアが開いた時・ドアが閉まった時の3種類ですか? そうですね。その3種類です。 >この部分を Qt のシグナルに置き換えて、先に UI とスレッドの部分をテストする等はどうでしょう >後々、GPIOとの実際の入出力を別クラスに実装して置き換える感じで。 >(「モーターだけのコード」でそれぞれのタイミングでシグナルを投げるように設計します) すみません。このあたり、少し理解が出来ませんでした。とりあえず、pyqtSignal()やemit()を用いてGUI上に動作しているか出力するということでしょうか?
teamikl

2020/06/08 22:50

>できるだけフラットになるように考え直してみます。 ループを抜けた後のコードを実行させる為に、もう一工夫必要ですが 後者のコードで考え方はあってます。 - 個々のループを別関数にする while not self.isInterruptionRequested(): 〇〇# 略 〇〇if self.waitOpenDoor(): # 関数側でドアが完全に開くのを待つ。中断時にTrueを返す 〇〇〇〇break 〇〇Motor_Stop() - 例外を使ってループを抜ける - 他の解決策: コンテキストマネージャを使う  Pythonの機能ですが、関数を実行した後に必ず後始末の処理を呼び出すようにしたりもできます >とりあえず、pyqtSignal()やemit()を用いてGUI上に動作しているか出力するということでしょうか? UIのテストは、概ねそのような感じです。 スレッドのテストに関しては、GUIでなくても、端末上にログ表示でも十分です。 概要のみですが、2種類のテストを実機を使わずに行えます (1) UI のテスト タイマーで数秒おきに 開始・開く・閉じる のシグナルを送る (3種類のシグナルを定義 or 状態変化のシグナルを定義し引数で変化した状態を送る) 受け取り側のUI で状態変化に応じて表示を変えます。 (2) スレッドのテスト (GPIOで扱うデバイス側のエミュレート) - タイマーで数秒おきに ピンの値(?)を 一時的に HIGH にする - スレッドでピンの値を監視 → 反応があればシグナルを送る 以前の質問より、メインスレッド→サブスレッドへの通信はない GPIOからのinput監視のみという事でしたが、 outputに応じてinputの振る舞いが変わることはないのか等は確認が必要な点。 (双方向通信が必要かどうか) スレッドで 開始・開く・閉じる のシグナルを送るように実装し、 テスト環境ではタイマーを用いてシグナルをシミュレートする事で、 UI のテストからGPIO に依存する処理を切り離せます。→実機なしにテスト可能。
pythonnoob1

2020/06/09 11:38

ありがとうございます。 本日、GPIO及び作成したGUIの動作確認テストをしてきました。 ・GPIO GPIO.cleanup() をすると動作は止まる。 いちいちsetupしなくともモーターは連続して動作する。 cleanup後はsetupしなければならない。 ・GUI 動作させたところ、問題なく動作しました。(やりたかったことが出来るようになりました。) 一応解決欄に、コードを載せておきます。(自己解決ではないので本当はダメなんでしょうけど・・・)
teamikl

2020/06/09 12:49

setup() をプログラム開始時に、cleanup()は終了時に一度のみで良いのでは…と疑問に思ってました。 Qt に依存しない終了時のフックとしては、他に標準ライブラリのatexit モジュールも使えますが スレッドで GPIO を使うなら、スレッドの起動終了時にするなど、 一貫性を持たせるとコードが読みやすくなります。ループ処理の前後でも良いですね。 def run(self): 〇〇setup() 〇〇while 1: 〇〇〇〇省略。終了時は安全にループを抜ける 〇〇cleanup() - 改善できたポイント 停止ボタンの処理でメインスレッドでMoter_Stop()を呼んでいた為 サブスレッドでの GPIO.output と同時アクセスの可能性があった。 - 修正後のコードでの確認点 停止した時にMotor_Stop()が呼ばれるか呼ばれないかで、 元コードと振舞が異なりますが大丈夫でしょうか? 前と同じ動作にするには、「サブスレッド側で」中断時にMotor_Stop()を呼ぶ必要があるはずです。 - 今度の課題 while の多重ネスト、
pythonnoob1

2020/06/09 17:41

>setup() をプログラム開始時に、cleanup()は終了時に一度のみで良いのでは…と疑問に思ってました。 ありがとうございます。 setup()に関しては、teamiklさんのおっしゃる通りだと思いました。 cleanup()に関しましては、動作確認の際に Stopボタンを押したとき「clean」の文字が出力されなかったため、作成したコードのように if self.isInterruptionRequested(): の後に入れたほうが確実なのかなと思いコードのようにしました。 >修正後のコードでの確認点 Motor_Stop()であろうと、Motor_Forward()であろうとGPIO.cleanup()するとGPIOのは完全にクリアな状態になるので問題ないかと思われます。 > 今度の課題 これについては、もっと複雑な動作(例えば、ドアが閉まっている最中に物体を検出してドアを開くようにするなど)を入れようとしていますので、正直難しいところがあるのかと思います。 ただ、できる限り見やすいフラットなコードを今後も目指していきます。
teamikl

2020/06/10 05:21

>また stop() の中の Motor_Stop() に関してですが、もしかしたら >GPIO.cleanup() した時点でモーターが停止するかもしれません。 Motor_Stop()に関しては、コメントを読みなおして納得しました。 変更点だったのですね、 確認漏れででなく、動作に支障なければ大丈夫です。 > 動作確認の際に Stopボタンを押したとき「clean」の文字が出力されなかったため、 現状ここは仕方ない部分ですが、以下の件に該当します >ループを抜けた後のコードを実行させる為に、もう一工夫必要です https://teratail.com/questions/268335 にコメントした > ループを抜けた後に何かコードを実行したい場合は、逆にreturnでは都合が悪く、 >例外機構(try/except)を使ってループを抜ける等、他の対策が必要なことも有ります。 少し説明補足で、raise でループを抜けて、 try/except で任意の場所で補足という事が出来ます。 ---- >もっと複雑な動作(例えば、ドアが閉まっている最中に物体を検出してドアを開くようにするなど)を入れようとしていますので、正直難しいところがあるのかと思います。 複雑なことを複雑な方法でやろうとすると、余計に難しくなるので 出来るだけシンプルなロジックに整理した方が良い部分ではあります。 ネストしたループの問題点としては、 ネストの深い場所での問題の扱いが難しくなる点。 >ドアが閉まっている最中に物体を検出してドアを開くようにするなど 例ですが、「物体を検出するとドアを開く」は共通 「以前の状態に関わらず」物体を検出した時ですよね? 停止中(検出中) → 物体を検出 → ドアを開く~ 開いてる途中 → 物体を検出 → ドアを開く~ 閉じている途中 → 物体を検出 → ドアを開く~ 開いてる途中に~というのは冗長かもしれませんが、 ロジックをシンプルにできるなら多少の冗長化は許容するか、 無駄であればそこだけ省きます。 共通の処理は共通のコードで行うのが理想で、 少し振舞が異なるのであればその部分は変数で分岐。 これをネストしたループで行うと、コードに不具合があった場合 「閉じている途中に物体を検出した場合のみ起こる」再現性の低いバグになる可能性に繋がります。 他にも、「閉じている途中に*何度か*物体を検出した場合」ループのネストはどうなるのでしょうか。 一般的に、複雑なネストは、ある条件を満たさないと再現しないバグとなり、 発見や修正が困難になる傾向があります。 >ただ、できる限り見やすいフラットなコードを今後も目指していきます。 最初から完璧なものというのも難しいので、 その方針で良いと思います。まずは、本来の目的の達成を目標に。
teamikl

2020/06/10 07:42 編集

> 例外機構(try/except)を使って ~ 代案。後々思いついたのですが、ループの外側をtry/finally で囲み finallyの所で cleanup() 処理をすれば、return 時にも実行されます。 8. エラーと例外 8.6. クリーンアップ動作を定義する https://docs.python.org/ja/3/tutorial/errors.html
pythonnoob1

2020/06/10 10:36

>共通の処理は共通のコードで行うのが理想で、 >少し振舞が異なるのであればその部分は変数で分岐。 及び >ループの外側をtry/finally で囲み >finallyの所で cleanup() 処理をすれば、return 時にも実行されます。 ありがとうございます。ぜひコードに組み込んでいきたいと思います。 また、今後も変わらず改善と改良を施していきたいと考えています。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問