質問: Tab1Widget() sys.exit(app.exec_()) ``` ### 教えてほしいこと 上のプログラムでで「スタートボタン」を押すと処理Xが実行されると思います。 これで、処理Xと処理Yが0.1秒間隔で交互に実行されている認識でいます。 1QTimer()の()内は何か入れないといけませんか?入れないといけないのであれば何を入れたらいいのか教えてください 2これは処理を止めない限り処理Xと処理Yが0.1秒ごとに繰り返されますか? 3もし2の回答が「はい」であった場合、処理Xが1秒以上かかる処理であったらどんなことが起きますか?(抽象的な質問で申し訳ありません) 4もし2の回答が「はい」であった場合、処理Xは「while 1:」を使ったループ関数にしなくてもループしますか? 素人丸出しのとんでもない質問で大変申し訳ありませんが、教えてくださると幸いです。 ### 補足情報(FW/ツールのバージョンなど) ここにより詳細な情報を記載してください。
回答: > > QTimer()の()内は何か入れないといけませんか? > > > なにも入れなくてよいです。 > > これは処理を止めない限り処理Xと処理Yが0.1秒ごとに繰り返されますか? > > > 今のコードはそうなっていません。 QTimer() は名前の通り、ストップウォッチです。 start() に指定したミリ秒分経過すると、`timeout()` シグナルが呼ばれます。 `timeout()` シグナルをスロットに繋いである場合は、タイムアウトしたときにその関数が呼ばれます。 本来どのようなことが行いたかったのかコメントいただければアドバイスできるかもしれません。 追記 > > 今のコードでは、「処理Xが実行された0.1秒後に処理Xは止まり、処理Yが実行される」という解釈でよろしかったでしょうか? > > > 今のコードは X() を呼び出すと、なにも起こらずにただちに終了します。 本来やりたい意図は置いておいて、問題点がいくつかあるのでコメント形式で書きました。 ``` def X(self): self.Y # 関数呼び出しになっていない self.Y() では? timer=QTimer() timer.timeout.comect(self.Y) # comect() は connect() のスペルミスでは? time.start(100) # start() という関数はタイマーをスタートさせるだけでここで処理はブロックされない。このあと、すぐに関数を抜けて、timer 変数が破棄されてしまうので、ローカル変数ではなく、self.timer のように属性として持っておかないとまずい ``` ### サンプルコード QTimer の使い方の例を以下に記載します。 「タイマースタート」をクリックすると、2秒ごとに `on_timeout()` が呼ばれます。 ``` import sys from PyQt5.QtWidgets import * from PyQt5.QtCore import * class MainWindow(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): widget = QWidget() self.setCentralWidget(widget) layout = QVBoxLayout() widget.setLayout(layout) # ボタンを配置する。 self.button = QPushButton("タイマースタート") self.button.clicked.connect(self.startTimer) layout.addWidget(self.button) def startTimer(self): self.timer = QTimer() self.timer.timeout.connect(self.on_timeout) # QTimer が timeout した場合に呼び出す関数を登録 self.timer.start(2000) # タイマーをスタートさせる def on_timeout(self): """start() で設定したミリ秒ごとにこの処理が呼ばれます """ print("timeout") if __name__ == '__main__': app = QApplication(sys.argv) windows = MainWindow() windows.show() app.exec_() sys.exit(0) ``` > > Whileは処理が重くなるため使ってはいけないのかと感じています。 > > > GUI アプリケーションは、イベントが発生していないかチェックするイベントループというものがメインスレッドで動いています。 while やすぐに終了しない重たい処理を行ってしまうと、そのイベントループが止まってしまうので、GUI がフリーズしたようになります。 なので、while や重たい処理を行いたい場合は別にスレッドを作って行う必要があります。 追記 > > 「物体を検知してドアを開閉する」処理と「ドアの開閉状態をテキストボックスに入力する」処理をマルチスレッド(threading)で並列処理する『処理Z』をつくればよろしいのでしょうか? > > > ドアの開閉を行う処理を別スレッドで行う必要があると思います。 図にすると以下のようになります。 ドアが開閉されたら、シグナルを発呼し、メインスレッド側のスロットで受信し、テキストボックスに開閉状態を表示すればよいと思います。 !イメージ説明 ``` import sys from datetime import datetime from PyQt5.QtWidgets import * from PyQt5.QtCore import * ### ここは別スレッドで行う ### class Worker(QObject): doorOpened = pyqtSignal() def __init__(self, parent=None): QObject.__init__(self, parent=parent) def process(self): while True: QThread.sleep(3) # 実際はドアを開閉する処理などを書く # ドアの開閉を通知 self.doorOpened.emit() class MainWindow(QMainWindow): stop_signal = pyqtSignal() def __init__(self): super().__init__() self.initUI() def initUI(self): widget = QWidget() self.setFixedSize(640, 480) self.setCentralWidget(widget) layout = QVBoxLayout() widget.setLayout(layout) # LineEdit を配置する。 self.lineEdit = QLineEdit("処理開始") layout.addWidget(self.lineEdit) # スレッドを作成する。 self.thread = QThread() # worker を作成し、メインスレッドから先程作成したスレッドに移す。 self.worker = Worker() self.worker.moveToThread(self.thread) # signal slot の設定 self.thread.started.connect(self.worker.process) # スレッド開始後に process() 開始 self.worker.doorOpened.connect(self.func) # doorOpened() が発呼されたら、func() を呼ぶ # スレッドを開始 self.thread.start() def func(self): # ドアの開閉が通知されたら、テキストボックスに表示する now = datetime.now() self.lineEdit.setText(f"Door opened! {now}") ### メインスレッド ### if __name__ == '__main__': app = QApplication(sys.argv) windows = MainWindow() windows.show() app.exec_() sys.exit(0) ``` ここで詳解するのは、スペース的に厳しいので、概要と検索キーワードを示しておきます。 * スレッド間のやり取り: Qt のシグナルスロットを使う → 検索キーワード「Qt シグナルスロット」 * マルチスレッド: QThread を使う → 検索キーワード「QThread」 蛇足 ロボットのシステム開発だと、いくつかの処理を並列で実行しつつ、各処理間でデータをやり取りする必要があるので、システムが複雑になってくると、マルチスレッドのプログラムを書くのは大変になってきます。 なので、ROS というロボット用のフレームワーク (ライブラリ) がよく使われます。