質問するログイン新規登録

回答編集履歴

8

修正

2020/05/22 12:33

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -97,7 +97,7 @@
97
97
  from PyQt5.QtWidgets import *
98
98
  from PyQt5.QtCore import *
99
99
 
100
-
100
+ ### ここは別スレッドで行う ###
101
101
  class Worker(QObject):
102
102
  doorOpened = pyqtSignal()
103
103
 
@@ -150,6 +150,7 @@
150
150
  now = datetime.now()
151
151
  self.lineEdit.setText(f"Door opened! {now}")
152
152
 
153
+ ### メインスレッド ###
153
154
  if __name__ == '__main__':
154
155
  app = QApplication(sys.argv)
155
156
  windows = MainWindow()

7

修正

2020/05/22 12:33

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -79,4 +79,94 @@
79
79
 
80
80
  GUI アプリケーションは、イベントが発生していないかチェックするイベントループというものがメインスレッドで動いています。
81
81
  while やすぐに終了しない重たい処理を行ってしまうと、そのイベントループが止まってしまうので、GUI がフリーズしたようになります。
82
- なので、while や重たい処理を行いたい場合は別にスレッドを作って行う必要があります。
82
+ なので、while や重たい処理を行いたい場合は別にスレッドを作って行う必要があります。
83
+
84
+ ## 追記
85
+
86
+ > 「物体を検知してドアを開閉する」処理と「ドアの開閉状態をテキストボックスに入力する」処理をマルチスレッド(threading)で並列処理する『処理Z』をつくればよろしいのでしょうか?
87
+
88
+ ドアの開閉を行う処理を別スレッドで行う必要があると思います。
89
+ 図にすると以下のようになります。
90
+ ドアが開閉されたら、シグナルを発呼し、メインスレッド側のスロットで受信し、テキストボックスに開閉状態を表示すればよいと思います。
91
+
92
+ ![イメージ説明](3de1d9ecfe1b3ae7202ca0c5c78e4c2a.png)
93
+
94
+ ```python
95
+ import sys
96
+ from datetime import datetime
97
+ from PyQt5.QtWidgets import *
98
+ from PyQt5.QtCore import *
99
+
100
+
101
+ class Worker(QObject):
102
+ doorOpened = pyqtSignal()
103
+
104
+ def __init__(self, parent=None):
105
+ QObject.__init__(self, parent=parent)
106
+
107
+ def process(self):
108
+ while True:
109
+ QThread.sleep(3)
110
+ # 実際はドアを開閉する処理などを書く
111
+
112
+ # ドアの開閉を通知
113
+ self.doorOpened.emit()
114
+
115
+ class MainWindow(QMainWindow):
116
+ stop_signal = pyqtSignal()
117
+
118
+ def __init__(self):
119
+ super().__init__()
120
+ self.initUI()
121
+
122
+ def initUI(self):
123
+ widget = QWidget()
124
+ self.setFixedSize(640, 480)
125
+ self.setCentralWidget(widget)
126
+
127
+ layout = QVBoxLayout()
128
+ widget.setLayout(layout)
129
+
130
+ # LineEdit を配置する。
131
+ self.lineEdit = QLineEdit("処理開始")
132
+ layout.addWidget(self.lineEdit)
133
+
134
+ # スレッドを作成する。
135
+ self.thread = QThread()
136
+
137
+ # worker を作成し、メインスレッドから先程作成したスレッドに移す。
138
+ self.worker = Worker()
139
+ self.worker.moveToThread(self.thread)
140
+
141
+ # signal slot の設定
142
+ self.thread.started.connect(self.worker.process) # スレッド開始後に process() 開始
143
+ self.worker.doorOpened.connect(self.func) # doorOpened() が発呼されたら、func() を呼ぶ
144
+
145
+ # スレッドを開始
146
+ self.thread.start()
147
+
148
+ def func(self):
149
+ # ドアの開閉が通知されたら、テキストボックスに表示する
150
+ now = datetime.now()
151
+ self.lineEdit.setText(f"Door opened! {now}")
152
+
153
+ if __name__ == '__main__':
154
+ app = QApplication(sys.argv)
155
+ windows = MainWindow()
156
+ windows.show()
157
+ app.exec_()
158
+ sys.exit(0)
159
+ ```
160
+
161
+ ここで詳解するのは、スペース的に厳しいので、概要と検索キーワードを示しておきます。
162
+
163
+ * スレッド間のやり取り: Qt のシグナルスロットを使う
164
+ → 検索キーワード「Qt シグナルスロット」
165
+
166
+ * マルチスレッド: QThread を使う
167
+ → 検索キーワード「QThread」
168
+
169
+ ## 蛇足
170
+
171
+ ロボットのシステム開発だと、いくつかの処理を並列で実行しつつ、各処理間でデータをやり取りする必要があるので、システムが複雑になってくると、マルチスレッドのプログラムを書くのは大変になってきます。
172
+ なので、ROS というロボット用のフレームワーク (ライブラリ) がよく使われます。

6

修正

2020/05/22 12:30

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -58,11 +58,11 @@
58
58
 
59
59
  def startTimer(self):
60
60
  self.timer = QTimer()
61
- self.timer.timeout.connect(self.on_timeout) # QTimer がストップして timeout シグナルが発呼された場合に呼び出す関数を登録
61
+ self.timer.timeout.connect(self.on_timeout) # QTimer が timeout た場合に呼び出す関数を登録
62
62
  self.timer.start(2000) # タイマーをスタートさせる
63
63
 
64
64
  def on_timeout(self):
65
- """タイマーがストップしたに呼ぶ関数
65
+ """start() で設定したミリ秒ごとこの処理がばれます
66
66
  """
67
67
  print("timeout")
68
68
 

5

修正

2020/05/21 11:00

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -28,11 +28,11 @@
28
28
  time.start(100) # start() という関数はタイマーをスタートさせるだけでここで処理はブロックされない。このあと、すぐに関数を抜けて、timer 変数が破棄されてしまうので、ローカル変数ではなく、self.timer のように属性として持っておかないとまずい
29
29
  ```
30
30
 
31
+ ### サンプルコード
32
+
31
33
  QTimer の使い方の例を以下に記載します。
32
- 「タイマースタート」をクリックすると、2秒に `on_timeout()` が呼ばれて、lineedit が更新されるサンプルです。
34
+ 「タイマースタート」をクリックすると、2秒ごとに `on_timeout()` が呼ばれす。
33
35
 
34
- ![イメージ説明](7eb4773d03362f35aa6fd48fcd7b30e4.png)
35
-
36
36
  ```python
37
37
  import sys
38
38
  from PyQt5.QtWidgets import *
@@ -56,20 +56,15 @@
56
56
  self.button.clicked.connect(self.startTimer)
57
57
  layout.addWidget(self.button)
58
58
 
59
- # ラベルを配置する。
60
- self.lineEdit = QLineEdit(self)
61
- layout.addWidget(self.lineEdit)
62
-
63
59
  def startTimer(self):
64
60
  self.timer = QTimer()
65
61
  self.timer.timeout.connect(self.on_timeout) # QTimer がストップして timeout シグナルが発呼された場合に呼び出す関数を登録
66
62
  self.timer.start(2000) # タイマーをスタートさせる
67
- self.lineEdit.setText('Timer starts')
68
63
 
69
64
  def on_timeout(self):
70
65
  """タイマーがストップした際に呼ぶ関数
71
66
  """
72
- self.lineEdit.setText('Timer stopped')
67
+ print("timeout")
73
68
 
74
69
 
75
70
  if __name__ == '__main__':

4

修正

2020/05/21 10:57

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  > 今のコードでは、「処理Xが実行された0.1秒後に処理Xは止まり、処理Yが実行される」という解釈でよろしかったでしょうか?
19
19
 
20
- 今のコードは実行すると、X() を呼び出すと、なにも起こらずにただちに終了します。
20
+ 今のコードは X() を呼び出すと、なにも起こらずにただちに終了します。
21
21
  本来やりたい意図は置いておいて、問題点がいくつかあるのでコメント形式で書きました。
22
22
 
23
23
  ```python

3

修正

2020/05/21 10:35

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -11,4 +11,77 @@
11
11
 
12
12
  ----
13
13
 
14
- 本来どのようなことが行いたかったのかコメントいただければアドバイスできるかもしれません。
14
+ 本来どのようなことが行いたかったのかコメントいただければアドバイスできるかもしれません。
15
+
16
+ ## 追記
17
+
18
+ > 今のコードでは、「処理Xが実行された0.1秒後に処理Xは止まり、処理Yが実行される」という解釈でよろしかったでしょうか?
19
+
20
+ 今のコードは実行すると、X() を呼び出すと、なにも起こらずにただちに終了します。
21
+ 本来やりたい意図は置いておいて、問題点がいくつかあるのでコメント形式で書きました。
22
+
23
+ ```python
24
+ def X(self):
25
+ self.Y # 関数呼び出しになっていない self.Y() では?
26
+ timer=QTimer()
27
+ timer.timeout.comect(self.Y) # comect() は connect() のスペルミスでは?
28
+ time.start(100) # start() という関数はタイマーをスタートさせるだけでここで処理はブロックされない。このあと、すぐに関数を抜けて、timer 変数が破棄されてしまうので、ローカル変数ではなく、self.timer のように属性として持っておかないとまずい
29
+ ```
30
+
31
+ QTimer の使い方の例を以下に記載します。
32
+ 「タイマースタート」をクリックすると、2秒後に `on_timeout()` が呼ばれて、lineedit が更新されるサンプルです。
33
+
34
+ ![イメージ説明](7eb4773d03362f35aa6fd48fcd7b30e4.png)
35
+
36
+ ```python
37
+ import sys
38
+ from PyQt5.QtWidgets import *
39
+ from PyQt5.QtCore import *
40
+
41
+
42
+ class MainWindow(QMainWindow):
43
+ def __init__(self):
44
+ super().__init__()
45
+ self.initUI()
46
+
47
+ def initUI(self):
48
+ widget = QWidget()
49
+ self.setCentralWidget(widget)
50
+
51
+ layout = QVBoxLayout()
52
+ widget.setLayout(layout)
53
+
54
+ # ボタンを配置する。
55
+ self.button = QPushButton("タイマースタート")
56
+ self.button.clicked.connect(self.startTimer)
57
+ layout.addWidget(self.button)
58
+
59
+ # ラベルを配置する。
60
+ self.lineEdit = QLineEdit(self)
61
+ layout.addWidget(self.lineEdit)
62
+
63
+ def startTimer(self):
64
+ self.timer = QTimer()
65
+ self.timer.timeout.connect(self.on_timeout) # QTimer がストップして timeout シグナルが発呼された場合に呼び出す関数を登録
66
+ self.timer.start(2000) # タイマーをスタートさせる
67
+ self.lineEdit.setText('Timer starts')
68
+
69
+ def on_timeout(self):
70
+ """タイマーがストップした際に呼ぶ関数
71
+ """
72
+ self.lineEdit.setText('Timer stopped')
73
+
74
+
75
+ if __name__ == '__main__':
76
+ app = QApplication(sys.argv)
77
+ windows = MainWindow()
78
+ windows.show()
79
+ app.exec_()
80
+ sys.exit(0)
81
+ ```
82
+
83
+ > Whileは処理が重くなるため使ってはいけないのかと感じています。
84
+
85
+ GUI アプリケーションは、イベントが発生していないかチェックするイベントループというものがメインスレッドで動いています。
86
+ while やすぐに終了しない重たい処理を行ってしまうと、そのイベントループが止まってしまうので、GUI がフリーズしたようになります。
87
+ なので、while や重たい処理を行いたい場合は別にスレッドを作って行う必要があります。

2

2020/05/21 10:26

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -6,8 +6,8 @@
6
6
 
7
7
  今のコードはそうなっていません。
8
8
  QTimer() は名前の通り、ストップウォッチです。
9
- start() に指定したミリ秒分経過すると、タイムアウトの `timeout()` シグナルが呼ばれます。
9
+ start() に指定したミリ秒分経過すると、`timeout()` シグナルが呼ばれます。
10
- `timeout()` シグナルスロットに繋いである場合は、タイムアウトしたときにその関数が呼ばれます。
10
+ `timeout()` シグナルスロットに繋いである場合は、タイムアウトしたときにその関数が呼ばれます。
11
11
 
12
12
  ----
13
13
 

1

s

2020/05/21 09:50

投稿

tiitoi
tiitoi

スコア21960

answer CHANGED
@@ -7,4 +7,8 @@
7
7
  今のコードはそうなっていません。
8
8
  QTimer() は名前の通り、ストップウォッチです。
9
9
  start() に指定したミリ秒分経過すると、タイムアウトの `timeout()` シグナルが呼ばれます。
10
- `timeout()` シグナルにスロットに繋いである場合は、タイムアウトしたときにその関数が呼ばれます。
10
+ `timeout()` シグナルにスロットに繋いである場合は、タイムアウトしたときにその関数が呼ばれます。
11
+
12
+ ----
13
+
14
+ 本来どのようなことが行いたかったのかコメントいただければアドバイスできるかもしれません。