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

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

ただいまの
回答率

89.25%

QTimerの使い方を教えてください

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 98

pythonnoob1

score 2

前提・実現したいこと

素人質問で大変申し訳ありません。
raspberrypi3b と pyqt5 を使ってロボットを動かすためのGUIを作っています。
そこで、処理Xと処理Yを繰り返しで実行するプログラムを作りたいと思っています。

該当のソースコード

python

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import subprocess
import time
import numpy as np
import cv2
import RPi.GPIO as GPIO



class Tab1Widget(QWidget):

    def __init__(self):
        super().__init__() 
        self.title = "GUI test"
        self.left = 10
        self.top = 10
        self.width = 640
        self.height = 480
        self.initUI()
        self.counter = 0

    def initUI(self):

        super(Tab1Widget, self).__init__()


        btn1 = QPushButton("停止", self)
        btn2 = QPushButton("スタート", self)

        btn1.clicked.connect(self.stop)
        btn2.clicked.connect(self.X)



        layoutA = QGridLayout()

        layoutA.addWidget(btn1,0,0)
        layoutA.addWidget(btn2,0,1)



        self.setLayout(layoutA)

        self.show()

    def X(self):

        "処理したいプログラム"

        self.Y
        timer=QTimer()
        timer.timeout.comect(self.Y)
        time.start(100)

    def Y(self):

        "処理したいプログラム"


if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = Tab1Widget()
    sys.exit(app.exec_())   

教えてほしいこと

上のプログラムでで「スタートボタン」を押すと処理Xが実行されると思います。
これで、処理Xと処理Yが0.1秒間隔で交互に実行されている認識でいます。

①QTimer()の()内は何か入れないといけませんか?入れないといけないのであれば何を入れたらいいのか教えてください
②これは処理を止めない限り処理Xと処理Yが0.1秒ごとに繰り返されますか?
③もし②の回答が「はい」であった場合、処理Xが1秒以上かかる処理であったらどんなことが起きますか?(抽象的な質問で申し訳ありません)
④もし②の回答が「はい」であった場合、処理Xは「while 1:」を使ったループ関数にしなくてもループしますか?

素人丸出しのとんでもない質問で大変申し訳ありませんが、教えてくださると幸いです。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

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

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

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

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

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

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

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

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+3

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 というロボット用のフレームワーク (ライブラリ) がよく使われます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/05/21 21:03

    丁寧な回答と、ご指摘・訂正ありがとうございます!!

    「物体を検知してドアを開閉する」処理と「ドアの開閉状態をテキストボックスに入力する」処理をマルチスレッド(threading)で並列処理する『処理Z』をつくればよろしいのでしょうか?

    いまいち、GUI アプリケーション上での while の使い方が理解できておりません。

    キャンセル

  • 2020/05/22 21:32

    追記しました。「物体を検知してドアを開閉する」は別スレッドで実行する必要がありそうです。ドアが開閉したら、Qt のシグナルスロットを使い、メインスレッド側に通知し、テキストボックスに表示するという処理はメインスレッドで行うことになると思います。

    キャンセル

  • 2020/05/22 21:40

    ありがとうございます!!
    ここまで丁寧にご教授いただき、申し訳なさと感謝で心がいっぱいです!!
    本当に本当にありがとうございました!

    キャンセル

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

  • ただいまの回答率 89.25%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる