回答編集履歴
4
queue版(ポーリング)追加
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
グローバル汚染対策
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
|
-
|
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
|
-
|
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
|
-
f
|
196
|
+
if __name__ == '__main__':
|
195
|
-
|
197
|
+
main()
|
196
|
-
root.mainloop()
|
197
198
|
```
|
2
キュー未使用のポーリング例を追加
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についての記述を追加
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
|