回答編集履歴

7

追記: 2つのイベントループを結合する方法

2020/12/10 08:52

投稿

teamikl
teamikl

スコア8664

test CHANGED
@@ -193,3 +193,135 @@
193
193
  daemon=False にして join メソッドでプロセスの終了を待ちます。
194
194
 
195
195
  その場合、安全に各GUIのイベントループを終了する仕組みが必要になります。
196
+
197
+
198
+
199
+
200
+
201
+ ----
202
+
203
+ qt/tk のイベントループを結合する方法
204
+
205
+
206
+
207
+ 独自のイベントループを使う為、終了時のイベント処理は未対応。
208
+
209
+
210
+
211
+ デメリット:
212
+
213
+ - Qt の aboutToQuit シグナル等は期待通りに処理されない等、他への影響があります。
214
+
215
+ - 片方の遅延がもう片方に影響する。(tk のイベント処理が遅れると qt も遅くなる → 動作が重く)
216
+
217
+
218
+
219
+ 別プロセスにした方が良いと思いますが、2つ以上のイベントループを使いたい場合、
220
+
221
+ このようなアプローチにすることも有ります。(例: asyncio + GUI 等)
222
+
223
+
224
+
225
+ ```python
226
+
227
+ import sys
228
+
229
+ import time
230
+
231
+ from functools import partial
232
+
233
+ from types import SimpleNamespace
234
+
235
+
236
+
237
+ import tkinter as tk
238
+
239
+ from PyQt5.QtCore import pyqtSignal
240
+
241
+ from PyQt5.QtWidgets import QApplication, QMainWindow
242
+
243
+
244
+
245
+ def my_quit(app, root, shared):
246
+
247
+ shared.stop_request = True
248
+
249
+
250
+
251
+ def my_event_loop(app, root, shared):
252
+
253
+ while not shared.stop_request:
254
+
255
+ root.update()
256
+
257
+ app.processEvents()
258
+
259
+ time.sleep(0.1)
260
+
261
+ app.quit()
262
+
263
+ root.destroy()
264
+
265
+
266
+
267
+ def main():
268
+
269
+ app = QApplication(sys.argv)
270
+
271
+ root = tk.Tk()
272
+
273
+ shared = SimpleNamespace(
274
+
275
+ stop_request = False,
276
+
277
+ )
278
+
279
+ cleanup = partial(my_quit, app, root, shared)
280
+
281
+
282
+
283
+ # tk widgets
284
+
285
+ root.protocol("WM_DELETE_WINDOW", cleanup)
286
+
287
+ button = tk.Button(root, text="OK")
288
+
289
+ button.pack()
290
+
291
+
292
+
293
+ # qt widgets
294
+
295
+ class MyMainWindow(QMainWindow):
296
+
297
+ closed = pyqtSignal()
298
+
299
+ def closeEvent(self, event):
300
+
301
+ self.closed.emit()
302
+
303
+ event.accept()
304
+
305
+ win = MyMainWindow()
306
+
307
+ win.closed.connect(cleanup)
308
+
309
+ win.show()
310
+
311
+
312
+
313
+ # XXX: app.aboutToQuit does not work
314
+
315
+
316
+
317
+ my_event_loop(app, root, shared)
318
+
319
+
320
+
321
+
322
+
323
+ if __name__ == '__main__':
324
+
325
+ main()
326
+
327
+ ```

6

説明補足

2020/12/10 08:52

投稿

teamikl
teamikl

スコア8664

test CHANGED
@@ -55,14 +55,6 @@
55
55
 
56
56
 
57
57
  ```python
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
58
 
67
59
  def tk_main(shared):
68
60
 
@@ -193,3 +185,11 @@
193
185
  qt 側で読み取る Queue や tk 側で読み取る Queue を増やし、
194
186
 
195
187
  Queue を通じてメッセージのやり取りをすることになります。
188
+
189
+
190
+
191
+ 注意点: 終了時に tk/qt 側で何か処理を行う場合は、
192
+
193
+ daemon=False にして join メソッドでプロセスの終了を待ちます。
194
+
195
+ その場合、安全に各GUIのイベントループを終了する仕組みが必要になります。

5

コード修正

2020/12/10 08:36

投稿

teamikl
teamikl

スコア8664

test CHANGED
@@ -58,6 +58,12 @@
58
58
 
59
59
 
60
60
 
61
+
62
+
63
+
64
+
65
+
66
+
61
67
  def tk_main(shared):
62
68
 
63
69
  import tkinter as tk
@@ -104,11 +110,13 @@
104
110
 
105
111
  button = QPushButton("Quit", win)
106
112
 
107
- button.clicked.connect(lambda: shared.queue.put(None))
113
+ button.clicked.connect(win.close)
108
114
 
109
115
  win.show()
110
116
 
111
117
 
118
+
119
+ app.aboutToQuit.connect(lambda: shared.queue.put(None))
112
120
 
113
121
  app.exec_()
114
122
 
@@ -119,6 +127,8 @@
119
127
  def main():
120
128
 
121
129
  import sys
130
+
131
+ from types import SimpleNamespace
122
132
 
123
133
  from multiprocessing import Process, Queue
124
134
 
@@ -158,6 +168,8 @@
158
168
 
159
169
 
160
170
 
171
+
172
+
161
173
  ```
162
174
 
163
175
 

4

追記: Process を使う方法

2020/12/10 08:32

投稿

teamikl
teamikl

スコア8664

test CHANGED
@@ -47,3 +47,137 @@
47
47
  同時にイベントループを稼働する方法は、終了時に同期を取る方法が難しい為、
48
48
 
49
49
  別プロセスを検討した方が良さそうです。
50
+
51
+
52
+
53
+ ----
54
+
55
+
56
+
57
+ ```python
58
+
59
+
60
+
61
+ def tk_main(shared):
62
+
63
+ import tkinter as tk
64
+
65
+ from tkinter import ttk
66
+
67
+ from functools import partial
68
+
69
+
70
+
71
+ main_quit = partial(shared.queue.put, None)
72
+
73
+
74
+
75
+ root = tk.Tk()
76
+
77
+ button = ttk.Button(root, text="Quit")
78
+
79
+ button.config(command=main_quit)
80
+
81
+ button.pack()
82
+
83
+
84
+
85
+ root.protocol("WM_DELETE_WINDOW", main_quit)
86
+
87
+
88
+
89
+ root.mainloop()
90
+
91
+
92
+
93
+
94
+
95
+ def qt_main(shared):
96
+
97
+ from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
98
+
99
+
100
+
101
+ app = QApplication(shared.argv)
102
+
103
+ win = QMainWindow()
104
+
105
+ button = QPushButton("Quit", win)
106
+
107
+ button.clicked.connect(lambda: shared.queue.put(None))
108
+
109
+ win.show()
110
+
111
+
112
+
113
+ app.exec_()
114
+
115
+
116
+
117
+
118
+
119
+ def main():
120
+
121
+ import sys
122
+
123
+ from multiprocessing import Process, Queue
124
+
125
+
126
+
127
+ shared = SimpleNamespace(
128
+
129
+ argv = sys.argv,
130
+
131
+ queue = Queue(),
132
+
133
+ )
134
+
135
+ procTk = Process(target=tk_main, args=(shared,), daemon=True)
136
+
137
+ procQt = Process(target=qt_main, args=(shared,), daemon=True)
138
+
139
+
140
+
141
+ procTk.start()
142
+
143
+ procQt.start()
144
+
145
+
146
+
147
+ for item in iter(shared.queue.get, None):
148
+
149
+ queue.task_done()
150
+
151
+
152
+
153
+
154
+
155
+ if __name__ == '__main__':
156
+
157
+ main()
158
+
159
+
160
+
161
+ ```
162
+
163
+
164
+
165
+ tkinter と pyqt のウィンドウを同時に開くサンプル
166
+
167
+ このコードでは合計3つのプロセスを使いますが、最少は qt と tk の2つでも良いです。
168
+
169
+ 終了時の処理を解りやすくするために3つにしました。
170
+
171
+
172
+
173
+ メインのプロセスではキューへのメッセージを待ち、
174
+
175
+ None が入れられるとプログラムを終了します。
176
+
177
+
178
+
179
+ qt <-> tk 間の連携には「プロセス間通信」を調べて見て下さい、
180
+
181
+ qt 側で読み取る Queue や tk 側で読み取る Queue を増やし、
182
+
183
+ Queue を通じてメッセージのやり取りをすることになります。

3

文章の修正

2020/12/10 08:30

投稿

teamikl
teamikl

スコア8664

test CHANGED
@@ -46,4 +46,4 @@
46
46
 
47
47
  同時にイベントループを稼働する方法は、終了時に同期を取る方法が難しい為、
48
48
 
49
- 別プロセスで使う方を検討した方が良さそうです。
49
+ 別プロセスを検討した方が良さそうです。

2

修正

2020/12/10 07:12

投稿

teamikl
teamikl

スコア8664

test CHANGED
@@ -36,9 +36,9 @@
36
36
 
37
37
  while True:
38
38
 
39
- print("tk") # update
39
+ print("tk") # root.update() ... tk のイベント処理
40
40
 
41
- print("qt") # processEvents
41
+ print("qt") # app.processEvents() ... qt のイベント処理
42
42
 
43
43
  ```
44
44
 

1

説明補足

2020/12/10 07:02

投稿

teamikl
teamikl

スコア8664

test CHANGED
@@ -41,3 +41,9 @@
41
41
  print("qt") # processEvents
42
42
 
43
43
  ```
44
+
45
+
46
+
47
+ 同時にイベントループを稼働する方法は、終了時に同期を取る方法が難しい為、
48
+
49
+ 別プロセスで使う方を検討した方が良さそうです。