回答ではありません
tkは多くの人には不要であり、本質的でもないので、省いています。dockerとpython3が使えるLinux環境を想定したpsycopg2によるpostgres14へのアクセスとクエリのキャンセル処理のサンプルを置いておきます。python部分はtkがないことを除き質問者さんのものと似たようなものです。
環境構築にはvenvを使っています。
環境構築スクリプト
空のディレクトリから、sh create_env.sh
などとして動かせばOKです。
create_env.sh
sh
1 docker run -d --rm -e POSTGRES_PASSWORD=pass -v $(pwd)/data:/var/lib/postgresql/data -p 5432:5432 --name postgres_$$ postgres:14-alpine
2 python3 -m venv env
3 . ./env/bin/activate
4 pip install -U pip
5 pip install -U setuptools
6 pip install psycopg2-binary
7 cat >hoge.py <<EOF
8 import psycopg2
9 from threading import Thread
10 from time import sleep
11 def cancel(t, con):
12 sleep(t)
13 con.cancel()
14 with psycopg2.connect(
15 host = "localhost",
16 user = "postgres",
17 password = "pass",
18 dbname = "postgres"
19 ) as con:
20 with con.cursor() as cur:
21 try:
22 t = Thread(target = cancel, args = [1, con])
23 t.start()
24 cur.execute('select pg_sleep(5), version()')
25 t.join()
26 print(cur.fetchall())
27 except Exception as e:
28 import traceback
29 traceback.print_exc()
30 EOF
31 python hoge.py
32 deactivate
33 docker stop postgres_$$
詳細
PostgreSQL14について
環境構築スクリプト内先頭で起動しています。
sh
1 docker run -d --rm -e POSTGRES_PASSWORD=pass -v $(pwd)/data:/var/lib/postgresql/data -p 5432:5432 --name postgres_$$ postgres:14-alpine
環境構築スクリプト内最後で終了しています。
sh
1 docker stop postgres_$$
PostgreSQLの公式docker image
https://hub.docker.com/_/postgres
venvについて
一度構築した後は、以下で環境に入れます
抜けるには、以下。
python公式のvenv
https://docs.python.org/ja/3/library/venv.html
psycog2を使ったキャンセル例
with使用、GUI排除、pg_sleepによるクエリ結果の遅延により確実な自動キャンセルとスリム化を図っていますが、大体同じです。
ただ本来の主な用途はCtrl+CによるSIGINT/TERMの処理、pythonだとKeyboardInterruptに当たるので、スレッドを作ることはあまりないかと思います。
hoge.py
python
1 import psycopg2
2 from threading import Thread
3 from time import sleep
4 def cancel ( t , con ) :
5 sleep ( t )
6 con . cancel ( )
7 with psycopg2 . connect (
8 host = "localhost" ,
9 user = "postgres" ,
10 password = "pass" ,
11 dbname = "postgres"
12 ) as con :
13 with con . cursor ( ) as cur :
14 try :
15 t = Thread ( target = cancel , args = [ 1 , con ] )
16 t . start ( )
17 cur . execute ( 'select pg_sleep(5), version()' )
18 t . join ( )
19 print ( cur . fetchall ( ) )
20 except Exception as e :
21 import traceback
22 traceback . print_exc ( )
psycopg2公式ドキュメント
https://www.psycopg.org/docs/
(追記)
環境構築後の環境で動作するtkinterキャンセル例
質問がいつまで経っても良くならないので、例だけつけておきました。
質問のコメント欄に書いたキューを使わず、スレッドのjoinをポーリングで待ち合わせる例です。
基本的にtkinterはtkをどこまで信用できるのか分からないので、アバウトな制御しかしていません。
ご参考までにw
python
1 import tkinter as tk
2 from tkinter import ttk
3 from time import sleep
4 from threading import Thread
5 import psycopg2
6
7 def query ( obj ) :
8 with psycopg2 . connect (
9 host = "localhost" ,
10 user = "postgres" ,
11 password = "pass" ,
12 dbname = "postgres"
13 ) as con :
14 with con . cursor ( ) as cur :
15 try :
16 obj [ 'con' ] = con
17 cur . execute ( 'select pg_sleep(10), version(), timeofday()' )
18 obj [ 'con' ] = None
19 data = cur . fetchall ( )
20 obj [ 'result' ] = data
21 except Exception as e :
22 import traceback
23 traceback . print_exc ( )
24 obj [ 'con' ] = None
25 obj [ 'finished' ] = True
26
27 query_index = 1
28 def new_query_index ( ) :
29 global query_index
30 result = query_index
31 query_index += 1
32 return result
33
34 def add_list ( queries , listbox , comp ) :
35 number = new_query_index ( )
36 key = f'query- { number } '
37 listbox . insert ( listbox . size ( ) , key )
38 obj = { 'name' : key , 'number' : number , 'finished' : False , 'thread' : None , 'result' : None , 'con' : None }
39 t = Thread ( target = query , args = [ obj ] )
40 obj [ 'thread' ] = t
41 queries [ key ] = obj
42 t . start ( )
43 comp . pack ( )
44
45 def search_index_from_listbox ( value , listbox ) :
46 content = listbox . get ( 0 , listbox . size ( ) )
47 for i , name in enumerate ( content ) :
48 if name == value :
49 return i
50 return - 1
51
52 def cancel ( queries , listbox ) :
53 tpl = listbox . curselection ( )
54 for i in tpl :
55 name = listbox . get ( i )
56 obj = queries [ name ]
57 con = obj [ 'con' ]
58 if con and not obj [ 'finished' ] :
59 con . cancel ( )
60
61 def join_finished_threads ( queries , listbox , comp ) :
62 for k , v in list ( queries . items ( ) ) :
63 if v [ 'finished' ] :
64 v [ 'thread' ] . join ( )
65 queries . pop ( k )
66 listbox . delete ( search_index_from_listbox ( v [ 'name' ] , listbox ) )
67 print ( v [ 'result' ] )
68 comp . pack ( )
69
70 def main ( ) :
71 queries = { }
72 def polling ( ) :
73 join_finished_threads ( queries , listbox , frame )
74 frame . after ( 100 , polling )
75
76 root = tk . Tk ( )
77 root . geometry ( '220x300' )
78 root . title ( 'スレッド終了のポーリング監視' )
79 frame = ttk . Frame ( root , padding = 10 )
80 listbox = tk . Listbox ( frame , selectmode = "multiple" , height = 10 )
81 listbox . grid ( column = 0 , row = 0 , columnspan = 2 )
82 frame . grid ( )
83 button_add = ttk . Button ( frame , text = '追加' , command = lambda : add_list ( queries , listbox , frame ) ) . grid ( column = 0 , row = 1 )
84 button_cancel = ttk . Button ( frame , text = 'キャンセル' , command = lambda : cancel ( queries , listbox ) ) . grid ( column = 1 , row = 1 )
85 frame . pack ( padx = 10 , pady = 20 )
86 polling ( )
87 root . mainloop ( )
88
89 if __name__ == '__main__' :
90 main ( )
ついでにqueue版。別スレッドからbindを使うのは怖かったので(使えれば自前Queueも不要だし)、結局UIスレッドからポーリング。
python
1 import tkinter as tk
2 from tkinter import ttk
3 from time import sleep
4 from threading import Thread
5 import psycopg2
6 from queue import Queue
7
8 def query ( obj , commands , queries , listbox , comp ) :
9 with psycopg2 . connect (
10 host = "localhost" ,
11 user = "postgres" ,
12 password = "pass" ,
13 dbname = "postgres"
14 ) as con :
15 with con . cursor ( ) as cur :
16 try :
17 obj [ 'con' ] = con
18 cur . execute ( 'select pg_sleep(10), version(), timeofday()' )
19 obj [ 'con' ] = None
20 data = cur . fetchall ( )
21 obj [ 'result' ] = data
22 except Exception as e :
23 import traceback
24 traceback . print_exc ( )
25 obj [ 'con' ] = None
26 obj [ 'finished' ] = True
27
28 def query_on_ui_thread ( obj , queries , listbox , comp ) :
29 obj [ 'thread' ] . join ( )
30 listbox . delete ( search_index_from_listbox ( obj [ 'name' ] , listbox ) )
31 print ( obj [ 'result' ] )
32 queries . pop ( obj [ 'name' ] )
33 comp . pack ( )
34
35 commands . put ( lambda : query_on_ui_thread ( obj , queries , listbox , comp ) )
36
37 query_index = 1
38 def new_query_index ( ) :
39 global query_index
40 result = query_index
41 query_index += 1
42 return result
43
44 def add_list ( queries , listbox , comp , commands ) :
45 number = new_query_index ( )
46 key = f'query- { number } '
47 listbox . insert ( listbox . size ( ) , key )
48 obj = { 'name' : key , 'number' : number , 'finished' : False , 'thread' : None , 'result' : None , 'con' : None }
49 t = Thread ( target = query , args = [ obj , commands , queries , listbox , comp ] )
50 obj [ 'thread' ] = t
51 queries [ key ] = obj
52 t . start ( )
53 comp . pack ( )
54
55 def search_index_from_listbox ( value , listbox ) :
56 content = listbox . get ( 0 , listbox . size ( ) )
57 for i , name in enumerate ( content ) :
58 if name == value :
59 return i
60 return - 1
61
62 def cancel ( queries , listbox ) :
63 tpl = listbox . curselection ( )
64 for i in tpl :
65 name = listbox . get ( i )
66 obj = queries [ name ]
67 con = obj [ 'con' ]
68 if con and not obj [ 'finished' ] :
69 con . cancel ( )
70
71 def main ( ) :
72 queries = { }
73 commands = Queue ( )
74 def polling ( ) :
75 while not commands . empty ( ) :
76 commands . get ( ) ( )
77 frame . after ( 100 , polling )
78
79 root = tk . Tk ( )
80 root . geometry ( '220x300' )
81 root . title ( 'スレッド終了のポーリング監視' )
82 frame = ttk . Frame ( root , padding = 10 )
83 listbox = tk . Listbox ( frame , selectmode = "multiple" , height = 10 )
84 listbox . grid ( column = 0 , row = 0 , columnspan = 2 )
85 frame . grid ( )
86 button_add = ttk . Button ( frame , text = '追加' , command = lambda : add_list ( queries , listbox , frame , commands ) ) . grid ( column = 0 , row = 1 )
87 button_cancel = ttk . Button ( frame , text = 'キャンセル' , command = lambda : cancel ( queries , listbox ) ) . grid ( column = 1 , row = 1 )
88 frame . pack ( padx = 10 , pady = 20 )
89 polling ( )
90 root . mainloop ( )
91
92 if __name__ == '__main__' :
93 main ( )