回答編集履歴

4

queue版(ポーリング)追加

2023/04/17 12:39

投稿

dameo
dameo

スコア943

test CHANGED
@@ -196,3 +196,100 @@
196
196
  if __name__ == '__main__':
197
197
  main()
198
198
  ```
199
+
200
+ ついでにqueue版。別スレッドからbindを使うのは怖かったので(使えれば自前Queueも不要だし)、結局UIスレッドからポーリング。
201
+ ```python
202
+ import tkinter as tk
203
+ from tkinter import ttk
204
+ from time import sleep
205
+ from threading import Thread
206
+ import psycopg2
207
+ from queue import Queue
208
+
209
+ def query(obj, commands, queries, listbox, comp):
210
+ with psycopg2.connect(
211
+ host = "localhost",
212
+ user = "postgres",
213
+ password = "pass",
214
+ dbname = "postgres"
215
+ ) as con:
216
+ with con.cursor() as cur:
217
+ try:
218
+ obj['con'] = con
219
+ cur.execute('select pg_sleep(10), version(), timeofday()')
220
+ obj['con'] = None
221
+ data = cur.fetchall()
222
+ obj['result'] = data
223
+ except Exception as e:
224
+ import traceback
225
+ traceback.print_exc()
226
+ obj['con'] = None
227
+ obj['finished'] = True
228
+
229
+ def query_on_ui_thread(obj, queries, listbox, comp):
230
+ obj['thread'].join()
231
+ listbox.delete(search_index_from_listbox(obj['name'], listbox))
232
+ print(obj['result'])
233
+ queries.pop(obj['name'])
234
+ comp.pack()
235
+
236
+ commands.put(lambda: query_on_ui_thread(obj, queries, listbox, comp))
237
+
238
+ query_index = 1
239
+ def new_query_index():
240
+ global query_index
241
+ result = query_index
242
+ query_index += 1
243
+ return result
244
+
245
+ def add_list(queries, listbox, comp, commands):
246
+ number = new_query_index()
247
+ key = f'query-{number}'
248
+ listbox.insert(listbox.size(), key)
249
+ obj = {'name' : key, 'number' : number, 'finished' : False, 'thread' : None, 'result' : None, 'con': None}
250
+ t = Thread(target = query, args=[obj, commands, queries, listbox, comp])
251
+ obj['thread'] = t
252
+ queries[key] = obj
253
+ t.start()
254
+ comp.pack()
255
+
256
+ def search_index_from_listbox(value, listbox):
257
+ content = listbox.get(0, listbox.size())
258
+ for i, name in enumerate(content):
259
+ if name == value:
260
+ return i
261
+ return -1
262
+
263
+ def cancel(queries, listbox):
264
+ tpl = listbox.curselection()
265
+ for i in tpl:
266
+ name = listbox.get(i)
267
+ obj = queries[name]
268
+ con = obj['con']
269
+ if con and not obj['finished']:
270
+ con.cancel()
271
+
272
+ def main():
273
+ queries = {}
274
+ commands = Queue()
275
+ def polling():
276
+ while not commands.empty():
277
+ commands.get()()
278
+ frame.after(100, polling)
279
+
280
+ root = tk.Tk()
281
+ root.geometry('220x300')
282
+ root.title('スレッド終了のポーリング監視')
283
+ frame = ttk.Frame(root, padding=10)
284
+ listbox = tk.Listbox(frame, selectmode="multiple", height=10)
285
+ listbox.grid(column=0, row=0, columnspan=2)
286
+ frame.grid()
287
+ button_add = ttk.Button(frame, text='追加', command=lambda: add_list(queries, listbox, frame, commands)).grid(column=0, row=1)
288
+ button_cancel = ttk.Button(frame, text='キャンセル', command=lambda: cancel(queries, listbox)).grid(column=1, row=1)
289
+ frame.pack(padx=10, pady=20)
290
+ polling()
291
+ root.mainloop()
292
+
293
+ if __name__ == '__main__':
294
+ main()
295
+ ```

3

グローバル汚染対策

2023/04/17 11:21

投稿

dameo
dameo

スコア943

test CHANGED
@@ -138,17 +138,7 @@
138
138
  query_index += 1
139
139
  return result
140
140
 
141
- queries = {}
142
-
143
- root = tk.Tk()
144
- root.geometry('220x300')
145
- root.title('スレッド終了のポーリング監視')
146
- frame = ttk.Frame(root, padding=10)
147
- listbox = tk.Listbox(frame, selectmode="multiple", height=10)
148
- listbox.grid(column=0, row=0, columnspan=2)
149
- frame.grid()
150
-
151
- def add_list():
141
+ def add_list(queries, listbox, comp):
152
142
  number = new_query_index()
153
143
  key = f'query-{number}'
154
144
  listbox.insert(listbox.size(), key)
@@ -157,7 +147,7 @@
157
147
  obj['thread'] = t
158
148
  queries[key] = obj
159
149
  t.start()
160
- frame.pack()
150
+ comp.pack()
161
151
 
162
152
  def search_index_from_listbox(value, listbox):
163
153
  content = listbox.get(0, listbox.size())
@@ -166,7 +156,7 @@
166
156
  return i
167
157
  return -1
168
158
 
169
- def cancel():
159
+ def cancel(queries, listbox):
170
160
  tpl = listbox.curselection()
171
161
  for i in tpl:
172
162
  name = listbox.get(i)
@@ -175,23 +165,34 @@
175
165
  if con and not obj['finished']:
176
166
  con.cancel()
177
167
 
178
- def join_finished_threads():
168
+ def join_finished_threads(queries, listbox, comp):
179
169
  for k, v in list(queries.items()):
180
170
  if v['finished']:
181
171
  v['thread'].join()
182
172
  queries.pop(k)
183
173
  listbox.delete(search_index_from_listbox(v['name'], listbox))
184
174
  print(v['result'])
185
- frame.pack()
175
+ comp.pack()
186
176
 
177
+ def main():
178
+ queries = {}
187
- def polling():
179
+ def polling():
188
- join_finished_threads()
180
+ join_finished_threads(queries, listbox, frame)
189
- frame.after(100, polling)
181
+ frame.after(100, polling)
190
182
 
183
+ root = tk.Tk()
184
+ root.geometry('220x300')
185
+ root.title('スレッド終了のポーリング監視')
186
+ frame = ttk.Frame(root, padding=10)
187
+ listbox = tk.Listbox(frame, selectmode="multiple", height=10)
188
+ listbox.grid(column=0, row=0, columnspan=2)
189
+ frame.grid()
191
- button_add = ttk.Button(frame, text='追加', command=add_list).grid(column=0, row=1)
190
+ button_add = ttk.Button(frame, text='追加', command=lambda: add_list(queries, listbox, frame)).grid(column=0, row=1)
192
- button_cancel = ttk.Button(frame, text='キャンセル', command=cancel).grid(column=1, row=1)
191
+ button_cancel = ttk.Button(frame, text='キャンセル', command=lambda: cancel(queries, listbox)).grid(column=1, row=1)
192
+ frame.pack(padx=10, pady=20)
193
+ polling()
194
+ root.mainloop()
193
195
 
194
- frame.pack(padx=10, pady=20)
196
+ if __name__ == '__main__':
195
- polling()
197
+ main()
196
- root.mainloop()
197
198
  ```

2

キュー未使用のポーリング例を追加

2023/04/17 10:23

投稿

dameo
dameo

スコア943

test CHANGED
@@ -94,3 +94,104 @@
94
94
  ```
95
95
  psycopg2公式ドキュメント
96
96
  https://www.psycopg.org/docs/
97
+
98
+ ----
99
+ (追記)
100
+ ## 環境構築後の環境で動作するtkinterキャンセル例
101
+
102
+ 質問がいつまで経っても良くならないので、例だけつけておきました。
103
+ 質問のコメント欄に書いたキューを使わず、スレッドのjoinをポーリングで待ち合わせる例です。
104
+ 基本的にtkinterはtkをどこまで信用できるのか分からないので、アバウトな制御しかしていません。
105
+ ご参考までにw
106
+
107
+ ```python
108
+ import tkinter as tk
109
+ from tkinter import ttk
110
+ from time import sleep
111
+ from threading import Thread
112
+ import psycopg2
113
+
114
+ def query(obj):
115
+ with psycopg2.connect(
116
+ host = "localhost",
117
+ user = "postgres",
118
+ password = "pass",
119
+ dbname = "postgres"
120
+ ) as con:
121
+ with con.cursor() as cur:
122
+ try:
123
+ obj['con'] = con
124
+ cur.execute('select pg_sleep(10), version(), timeofday()')
125
+ obj['con'] = None
126
+ data = cur.fetchall()
127
+ obj['result'] = data
128
+ except Exception as e:
129
+ import traceback
130
+ traceback.print_exc()
131
+ obj['con'] = None
132
+ obj['finished'] = True
133
+
134
+ query_index = 1
135
+ def new_query_index():
136
+ global query_index
137
+ result = query_index
138
+ query_index += 1
139
+ return result
140
+
141
+ queries = {}
142
+
143
+ root = tk.Tk()
144
+ root.geometry('220x300')
145
+ root.title('スレッド終了のポーリング監視')
146
+ frame = ttk.Frame(root, padding=10)
147
+ listbox = tk.Listbox(frame, selectmode="multiple", height=10)
148
+ listbox.grid(column=0, row=0, columnspan=2)
149
+ frame.grid()
150
+
151
+ def add_list():
152
+ number = new_query_index()
153
+ key = f'query-{number}'
154
+ listbox.insert(listbox.size(), key)
155
+ obj = {'name' : key, 'number' : number, 'finished' : False, 'thread' : None, 'result' : None, 'con': None}
156
+ t = Thread(target = query, args=[obj])
157
+ obj['thread'] = t
158
+ queries[key] = obj
159
+ t.start()
160
+ frame.pack()
161
+
162
+ def search_index_from_listbox(value, listbox):
163
+ content = listbox.get(0, listbox.size())
164
+ for i, name in enumerate(content):
165
+ if name == value:
166
+ return i
167
+ return -1
168
+
169
+ def cancel():
170
+ tpl = listbox.curselection()
171
+ for i in tpl:
172
+ name = listbox.get(i)
173
+ obj = queries[name]
174
+ con = obj['con']
175
+ if con and not obj['finished']:
176
+ con.cancel()
177
+
178
+ def join_finished_threads():
179
+ for k, v in list(queries.items()):
180
+ if v['finished']:
181
+ v['thread'].join()
182
+ queries.pop(k)
183
+ listbox.delete(search_index_from_listbox(v['name'], listbox))
184
+ print(v['result'])
185
+ frame.pack()
186
+
187
+ def polling():
188
+ join_finished_threads()
189
+ frame.after(100, polling)
190
+
191
+ button_add = ttk.Button(frame, text='追加', command=add_list).grid(column=0, row=1)
192
+ button_cancel = ttk.Button(frame, text='キャンセル', command=cancel).grid(column=1, row=1)
193
+
194
+ frame.pack(padx=10, pady=20)
195
+ polling()
196
+ root.mainloop()
197
+ ```

1

Ctrl+Cについての記述を追加

2023/04/17 05:58

投稿

dameo
dameo

スコア943

test CHANGED
@@ -66,6 +66,7 @@
66
66
  https://docs.python.org/ja/3/library/venv.html
67
67
  ### psycog2を使ったキャンセル例
68
68
  with使用、GUI排除、pg_sleepによるクエリ結果の遅延により確実な自動キャンセルとスリム化を図っていますが、大体同じです。
69
+ ただ本来の主な用途はCtrl+CによるSIGINT/TERMの処理、pythonだとKeyboardInterruptに当たるので、スレッドを作ることはあまりないかと思います。
69
70
  **hoge.py**
70
71
  ```python
71
72
  import psycopg2