質問編集履歴
18
コードの修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
### 実現したいこと
|
2
|
+
※(1/11追記)実用できる範囲まで改修できたので解決とさせていただきます、ありがとうございました!
|
3
|
+
|
2
4
|
[https://94.gigafile.nu/0325-c7f527087ad73dda5963f92a783e34fbb](url)
|
3
5
|
上記ファイルのrun.batを押して動くプログラムをPythonで再現したいと思っています
|
4
6
|
一例として、マウス座標をリアルタイムで表示するプログラムですが、他にも移植したいものがあり、だいたいのコードは再現できるもののある機能がPythonに用意しておらず、1から作成する必要があります
|
5
7
|
上記プログラムはexeファイルで、rustで作られたインタプリタ言語、「UWSCR」の実行ファイルとUWSCRの実行用ソースコードです
|
6
8
|
UWSCRの組み込み関数「balloon」をPythonで再現することが第一の目的となります
|
7
9
|
基本的にプログラムの書き方はこのUWSCRと同じ書き方で再現させる方針です、
|
10
|
+
※UWSCR公式github
|
11
|
+
https://github.com/stuncloud/UWSCR
|
8
12
|
|
9
13
|
### 発生している問題・分からないこと
|
10
|
-
|
14
|
+
マルチスレッドを使用するため、なるべくスレッドセーフなコードにする必要がありましたが、UWSCRと同じ書き方という点は妥協できないのでその上でのコードの改修
|
11
15
|
|
12
16
|
### 該当のソースコード ※2025/1/8更新
|
13
17
|
|
@@ -112,7 +116,7 @@
|
|
112
116
|
self.frame = tk.Frame(self.sub_window)
|
113
117
|
self.label = tk.Label(self.frame)
|
114
118
|
|
115
|
-
|
119
|
+
|
116
120
|
if show_border:
|
117
121
|
border = 'solid'
|
118
122
|
else:
|
@@ -186,7 +190,7 @@
|
|
186
190
|
当初はwin32apiで実現しようと思いましたが難しいことがわかりTkinterでの実装中です
|
187
191
|
|
188
192
|
### 補足
|
189
|
-
Python 3.10.4で実行しています
|
193
|
+
WIn11 Python 3.10.4で実行しています
|
190
194
|
|
191
195
|
### 追記
|
192
196
|
rust製のインタプリタ言語「UWSCR」での挙動
|
@@ -206,8 +210,13 @@
|
|
206
210
|
Sleep(0.01)
|
207
211
|
WEND
|
208
212
|
```
|
209
|
-
|
213
|
+
こちらをTkinterで再現することに成功しました
|
210
|
-
|
214
|
+
![イメージ説明](https://ddjkaamml8q8x.cloudfront.net/questions/2025-01-11/39803a67-6166-4121-9153-a5101d27967f.gif)
|
215
|
+
|
216
|
+
※マルチプロセス版も作成しました(下記リンク)、実行側のソースコードはマルチスレッド版用そのままで使えますが、マルチプロセスを使う仕様上処理をif __name__ == "__main__":またはif __name__ != "__mp_main__":のブロック中に記載しないとエラーが出る点に注意してください
|
217
|
+
https://incandescent-belekoy-906db2.netlify.app/balloon_multiprocessing_v20250108.txt
|
218
|
+
|
219
|
+
・実行側pyファイルソースコード
|
211
220
|
※balloon.pyのソースコードは省略
|
212
221
|
```Python
|
213
222
|
from balloon import *
|
17
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -111,7 +111,7 @@
|
|
111
111
|
self.dummy_label = tk.Label(self.sub_window)
|
112
112
|
self.frame = tk.Frame(self.sub_window)
|
113
113
|
self.label = tk.Label(self.frame)
|
114
|
-
|
114
|
+
|
115
115
|
|
116
116
|
if show_border:
|
117
117
|
border = 'solid'
|
16
コード誤字
test
CHANGED
File without changes
|
test
CHANGED
@@ -111,7 +111,7 @@
|
|
111
111
|
self.dummy_label = tk.Label(self.sub_window)
|
112
112
|
self.frame = tk.Frame(self.sub_window)
|
113
113
|
self.label = tk.Label(self.frame)
|
114
|
-
s
|
114
|
+
should_redefine_sub_window = False #ウィンドウを再生成したので再定義フラグをFalseに戻す
|
115
115
|
|
116
116
|
if show_border:
|
117
117
|
border = 'solid'
|
15
コードの修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
### 発生している問題・分からないこと
|
10
10
|
文字数制限により略
|
11
11
|
|
12
|
-
### 該当のソースコード ※2025/1/
|
12
|
+
### 該当のソースコード ※2025/1/8更新
|
13
13
|
|
14
14
|
```Python
|
15
15
|
import tkinter as tk
|
14
コードの修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -14,51 +14,48 @@
|
|
14
14
|
```Python
|
15
15
|
import tkinter as tk
|
16
16
|
import threading
|
17
|
+
import queue
|
17
18
|
|
18
19
|
global_balloon = None
|
19
20
|
|
20
21
|
class balloon_class:
|
21
22
|
|
23
|
+
thread_creation_count = 0
|
24
|
+
root_creation_count = 0
|
25
|
+
|
22
|
-
def __init__(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
26
|
+
def __init__(self, queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
23
|
-
# スレッド
|
27
|
+
# スレッドを2重生成しないためのフラグ
|
24
|
-
tr
|
28
|
+
type(self).thread_creation_count += 1
|
25
|
-
|
29
|
+
if 1 >= type(self).thread_creation_count:
|
30
|
+
self.thread = threading.Thread(target=self.create_balloon, daemon=True, args=(queue,message,x,y,font_name,font_size,fore_color,back_color,transparency, show_border))
|
31
|
+
self.thread.start()
|
32
|
+
else:
|
26
33
|
print("Error: スレッド生成メソッドが2回以上参照されています")
|
27
|
-
|
34
|
+
|
28
|
-
self.thread = threading.Thread(target=self.create_balloon, daemon=True, args=(message,x,y,font_name,font_size,fore_color,back_color,transparency, show_border))
|
29
|
-
self.thread.start()
|
30
|
-
|
31
|
-
def create_balloon(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
35
|
+
def create_balloon(self, queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
32
|
-
|
36
|
+
|
33
|
-
# rootを2重生成しないための
|
37
|
+
# rootを2重生成しないためのフラグ
|
34
|
-
try:
|
35
|
-
|
38
|
+
type(self).root_creation_count += 1
|
36
|
-
|
39
|
+
if 1 >= type(self).root_creation_count:
|
37
|
-
except Exception:
|
38
40
|
self.root = tk.Tk()
|
39
41
|
self.root.withdraw()
|
40
42
|
self.root.wm_overrideredirect(True)
|
41
43
|
self.sub_window = tk.Toplevel(self.root)
|
42
44
|
self.sub_window.deiconify()
|
43
|
-
|
45
|
+
|
44
46
|
self.previous_should_hide_balloon = False
|
45
|
-
|
47
|
+
|
46
|
-
self.should_redefine_sub_window = False
|
47
|
-
|
48
|
-
#事前にクラス変数を定義しておく
|
49
|
-
self.read_balloon_args(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
50
|
-
|
51
48
|
if show_border:
|
52
49
|
border = 'solid'
|
53
50
|
else:
|
54
51
|
border = 'flat'
|
55
|
-
|
52
|
+
|
56
53
|
#サイズ計算用のラベル
|
57
54
|
self.dummy_label = tk.Label(self.sub_window, text=message, font=(font_name, font_size))
|
58
55
|
# ラベルサイズを計算
|
59
56
|
width = self.dummy_label.winfo_reqwidth()
|
60
57
|
height = self.dummy_label.winfo_reqheight()
|
61
|
-
|
58
|
+
|
62
59
|
self.sub_window.attributes("-topmost", True)
|
63
60
|
# 全体を半透明にする(ウィンドウ全体が透明度を持つ)
|
64
61
|
self.sub_window.wm_attributes("-alpha", transparency)
|
@@ -69,20 +66,36 @@
|
|
69
66
|
self.label = tk.Label(self.frame, text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
70
67
|
self.label.place(x=8, y=10, width=width, height=height)
|
71
68
|
self.sub_window.wm_overrideredirect(True)
|
72
|
-
self.update_balloon(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
69
|
+
self.update_balloon(queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
73
70
|
self.root.mainloop()
|
74
|
-
|
71
|
+
else:
|
75
|
-
|
72
|
+
print("Error: 初期ウィンドウ生成メソッドが2回以上参照されています")
|
76
|
-
|
73
|
+
|
74
|
+
|
75
|
+
|
77
|
-
def update_balloon(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
76
|
+
def update_balloon(self, queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
77
|
+
|
78
|
+
# キューから変数を取得
|
79
|
+
try:
|
80
|
+
balloon_args, should_hide_balloon = queue.get_nowait()
|
81
|
+
except Exception:
|
82
|
+
balloon_args = None
|
83
|
+
should_hide_balloon = False
|
84
|
+
|
78
|
-
# 比較用に使う前回使用の引数と
|
85
|
+
# 比較用に使う前回使用の引数とqueueで読み取った表示更新用の引数のリスト
|
79
86
|
previous_args = [message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border]
|
80
|
-
new_args =
|
87
|
+
new_args = balloon_args
|
81
|
-
|
88
|
+
|
82
89
|
previous_should_hide_balloon = self.previous_should_hide_balloon
|
83
|
-
new_should_hide_balloon = s
|
90
|
+
new_should_hide_balloon = should_hide_balloon
|
91
|
+
|
84
|
-
|
92
|
+
# ウィンドウを消しても引数が前と同じ値だとGUI表示を更新しない仕組みなので、ウィンドウ再生成用のフラグを作成、要再生成なら再生成と更新処理を実行
|
93
|
+
try:
|
94
|
+
should_redefine_sub_window = not self.sub_window.winfo_exists() #sub_windowが存在しない場合True
|
95
|
+
except Exception:
|
96
|
+
should_redefine_sub_window = False
|
97
|
+
|
85
|
-
if previous_args != new_args or previous_should_hide_balloon != new_should_hide_balloon or s
|
98
|
+
if new_args is not None and (previous_args != new_args or previous_should_hide_balloon != new_should_hide_balloon or should_redefine_sub_window == True): #値が更新された場合かウィンドウを消した場合に更新処理を実行
|
86
99
|
message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border = new_args
|
87
100
|
self.previous_should_hide_balloon = new_should_hide_balloon #次回表示更新時の値比較用に代入
|
88
101
|
try:
|
@@ -104,11 +117,11 @@
|
|
104
117
|
border = 'solid'
|
105
118
|
else:
|
106
119
|
border = 'flat'
|
107
|
-
|
120
|
+
|
108
121
|
self.dummy_label.config(text=message, font=(font_name, font_size))
|
109
122
|
width = self.dummy_label.winfo_reqwidth()
|
110
123
|
height = self.dummy_label.winfo_reqheight()
|
111
|
-
|
124
|
+
|
112
125
|
self.sub_window.wm_attributes("-alpha", transparency)
|
113
126
|
self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
114
127
|
self.sub_window.configure(bg=back_color)
|
@@ -116,51 +129,33 @@
|
|
116
129
|
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
117
130
|
self.label.config(text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
118
131
|
self.label.place(x=8, y=10, width=width, height=height)
|
119
|
-
|
132
|
+
|
120
|
-
self.root.after(30, self.update_balloon, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
133
|
+
self.root.after(30, self.update_balloon, queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
121
|
-
|
122
|
-
|
134
|
+
|
123
|
-
self.should_hide_balloon = True
|
124
|
-
|
125
|
-
def show(self):
|
126
|
-
self.should_hide_balloon = False
|
127
|
-
|
128
|
-
|
129
|
-
def read_balloon_args(self, message=" ", x=0, y=0, font_name="Arial", font_size=14, fore_color="#000000", back_color="#FFFF00", transparency=1, show_border=True):
|
130
|
-
self.message = message
|
131
|
-
self.x = x
|
132
|
-
self.y = y
|
133
|
-
self.font_name = font_name
|
134
|
-
self.font_size = font_size
|
135
|
-
self.fore_color = fore_color
|
136
|
-
self.back_color = back_color
|
137
|
-
self.transparency = transparency
|
138
|
-
self.show_border = show_border
|
139
|
-
|
140
|
-
# ウィンドウを消しても引数が前と同じ値だとGUI表示を更新しない仕組みなので、ウィンドウ再生成用のフラグを作成、要再生成なら再生成と更新処理を実行
|
141
|
-
try:
|
142
|
-
self.should_redefine_sub_window = not self.sub_window.winfo_exists() #sub_windowが存在しない場合True
|
143
|
-
except Exception:
|
144
|
-
self.should_redefine_sub_window = False
|
145
135
|
|
146
136
|
|
147
137
|
def balloon(message=" ", x=0, y=0, font_name="Arial", font_size=14, fore_color="#000000", back_color="#FFFF00", transparency=1, show_border=True):
|
148
|
-
global global_balloon
|
138
|
+
global global_balloon, balloon_queue
|
149
139
|
|
150
140
|
if not isinstance(global_balloon, balloon_class):
|
141
|
+
balloon_queue = queue.Queue()
|
151
|
-
global_balloon=balloon_class(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
142
|
+
global_balloon=balloon_class(balloon_queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
152
143
|
elif isinstance(global_balloon, balloon_class):
|
153
144
|
try:
|
154
|
-
if
|
145
|
+
if balloon_queue.empty():
|
155
|
-
global_balloon.show()
|
156
|
-
|
146
|
+
balloon_args = [message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border]
|
147
|
+
should_hide_balloon = False
|
148
|
+
balloon_queue.put((balloon_args, should_hide_balloon))
|
157
149
|
except Exception:
|
158
150
|
pass
|
159
151
|
|
160
152
|
|
161
153
|
def hide_balloon():
|
162
154
|
try:
|
155
|
+
if balloon_queue.empty():
|
156
|
+
dummy_args = [" ", 0, 0, "Arial", 14, "#000000", "#FFFF00", 1, True]
|
163
|
-
|
157
|
+
should_hide_balloon = True
|
158
|
+
balloon_queue.put((dummy_args, should_hide_balloon))
|
164
159
|
except Exception:
|
165
160
|
pass
|
166
161
|
|
13
コード内容の修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -7,97 +7,155 @@
|
|
7
7
|
基本的にプログラムの書き方はこのUWSCRと同じ書き方で再現させる方針です、
|
8
8
|
|
9
9
|
### 発生している問題・分からないこと
|
10
|
-
当該pyファイルをimportしてライブラリ的な使用方法を想定しているので、非推奨ではあるもののTkinterを別スレッドで動かす必要があります
|
11
|
-
|
10
|
+
文字数制限により略
|
12
|
-
|
13
|
-
|
11
|
+
|
14
|
-
### 該当のソースコード ※2025/1/
|
12
|
+
### 該当のソースコード ※2025/1/6更新
|
15
13
|
|
16
14
|
```Python
|
17
15
|
import tkinter as tk
|
18
16
|
import threading
|
19
17
|
|
18
|
+
global_balloon = None
|
20
19
|
|
21
20
|
class balloon_class:
|
22
21
|
|
23
22
|
def __init__(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
23
|
+
# スレッドが2重起動しないための万が一の措置、デバッグ時はtryブロックの中は消して例外処理扱いせずに試してみること
|
24
|
+
try:
|
25
|
+
dummy = threading.Thread.is_alive(self.thread)
|
26
|
+
print("Error: スレッド生成メソッドが2回以上参照されています")
|
27
|
+
except Exception:
|
24
|
-
self.thread = threading.Thread(target=self.create_balloon, daemon=True, args=(message,x,y,font_name,font_size,fore_color,back_color,transparency, show_border))
|
28
|
+
self.thread = threading.Thread(target=self.create_balloon, daemon=True, args=(message,x,y,font_name,font_size,fore_color,back_color,transparency, show_border))
|
25
|
-
self.thread.start()
|
29
|
+
self.thread.start()
|
26
30
|
|
27
31
|
def create_balloon(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
28
32
|
|
33
|
+
# rootを2重生成しないための万が一の措置、デバッグ時はtryブロックの中は消して例外処理扱いせずに試してみること
|
34
|
+
try:
|
35
|
+
self.root.withdraw()
|
36
|
+
print("Error: 初期ウィンドウ生成メソッドが2回以上参照されています")
|
37
|
+
except Exception:
|
29
|
-
self.root = tk.Tk()
|
38
|
+
self.root = tk.Tk()
|
30
|
-
self.root.withdraw()
|
39
|
+
self.root.withdraw()
|
31
|
-
self.root.wm_overrideredirect(True)
|
40
|
+
self.root.wm_overrideredirect(True)
|
32
|
-
self.sub_window = tk.Toplevel(self.root)
|
41
|
+
self.sub_window = tk.Toplevel(self.root)
|
33
|
-
self.sub_window.deiconify()
|
42
|
+
self.sub_window.deiconify()
|
43
|
+
|
34
|
-
|
44
|
+
self.previous_should_hide_balloon = False
|
45
|
+
self.should_hide_balloon = False
|
46
|
+
self.should_redefine_sub_window = False
|
47
|
+
|
48
|
+
#事前にクラス変数を定義しておく
|
49
|
+
self.read_balloon_args(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
50
|
+
|
35
|
-
if show_border:
|
51
|
+
if show_border:
|
36
|
-
border = 'solid'
|
52
|
+
border = 'solid'
|
37
|
-
else:
|
53
|
+
else:
|
38
|
-
border = 'flat'
|
54
|
+
border = 'flat'
|
39
|
-
|
55
|
+
|
40
|
-
#サイズ計算用のラベル
|
56
|
+
#サイズ計算用のラベル
|
41
|
-
self.dummy_label = tk.Label(self.sub_window, text=message, font=(font_name, font_size))
|
57
|
+
self.dummy_label = tk.Label(self.sub_window, text=message, font=(font_name, font_size))
|
42
|
-
# ラベルサイズを計算
|
58
|
+
# ラベルサイズを計算
|
43
|
-
width = self.dummy_label.winfo_reqwidth()
|
59
|
+
width = self.dummy_label.winfo_reqwidth()
|
44
|
-
height = self.dummy_label.winfo_reqheight()
|
60
|
+
height = self.dummy_label.winfo_reqheight()
|
45
|
-
|
61
|
+
|
46
|
-
self.sub_window.attributes("-topmost", True)
|
62
|
+
self.sub_window.attributes("-topmost", True)
|
47
|
-
# 全体を半透明にする(ウィンドウ全体が透明度を持つ)
|
63
|
+
# 全体を半透明にする(ウィンドウ全体が透明度を持つ)
|
48
|
-
self.sub_window.wm_attributes("-alpha", transparency)
|
64
|
+
self.sub_window.wm_attributes("-alpha", transparency)
|
49
|
-
self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
65
|
+
self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
50
|
-
self.sub_window.configure(bg=back_color)
|
66
|
+
self.sub_window.configure(bg=back_color)
|
51
|
-
self.frame = tk.Frame(self.sub_window, bg=back_color, bd=1, relief=border)
|
67
|
+
self.frame = tk.Frame(self.sub_window, bg=back_color, bd=1, relief=border)
|
52
|
-
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
68
|
+
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
53
|
-
self.label = tk.Label(self.frame, text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
69
|
+
self.label = tk.Label(self.frame, text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
54
|
-
self.label.place(x=8, y=10, width=width, height=height)
|
70
|
+
self.label.place(x=8, y=10, width=width, height=height)
|
55
|
-
self.sub_window.wm_overrideredirect(True)
|
71
|
+
self.sub_window.wm_overrideredirect(True)
|
56
|
-
|
72
|
+
self.update_balloon(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
57
|
-
self.root.mainloop()
|
73
|
+
self.root.mainloop()
|
74
|
+
|
58
75
|
|
59
76
|
|
60
77
|
def update_balloon(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
61
|
-
|
78
|
+
# 比較用に使う前回使用の引数とread_balloon_args関数で読み取った表示更新用の引数のリスト
|
79
|
+
previous_args = [message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border]
|
80
|
+
new_args = [self.message, self.x, self.y, self.font_name, self.font_size, self.fore_color, self.back_color, self.transparency, self.show_border]
|
81
|
+
|
82
|
+
previous_should_hide_balloon = self.previous_should_hide_balloon
|
83
|
+
new_should_hide_balloon = self.should_hide_balloon
|
84
|
+
|
85
|
+
if previous_args != new_args or previous_should_hide_balloon != new_should_hide_balloon or self.should_redefine_sub_window == True: #値が更新された場合かウィンドウを消した場合に更新処理を実行
|
86
|
+
message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border = new_args
|
87
|
+
self.previous_should_hide_balloon = new_should_hide_balloon #次回表示更新時の値比較用に代入
|
62
|
-
try:
|
88
|
+
try:
|
89
|
+
if new_should_hide_balloon:
|
90
|
+
self.sub_window.withdraw()
|
91
|
+
else:
|
63
|
-
self.sub_window.deiconify()
|
92
|
+
self.sub_window.deiconify()
|
64
|
-
except Exception:
|
93
|
+
except Exception:
|
65
|
-
# ウィンドウをAlt+F4で消した場合も再生成
|
94
|
+
# ウィンドウをAlt+F4で消した場合も再生成
|
66
|
-
self.sub_window = tk.Toplevel(self.root)
|
95
|
+
self.sub_window = tk.Toplevel(self.root)
|
67
|
-
self.sub_window.wm_overrideredirect(True)
|
96
|
+
self.sub_window.wm_overrideredirect(True)
|
68
|
-
self.sub_window.deiconify()
|
97
|
+
self.sub_window.deiconify()
|
69
|
-
self.dummy_label = tk.Label(self.sub_window)
|
98
|
+
self.dummy_label = tk.Label(self.sub_window)
|
70
|
-
self.frame = tk.Frame(self.sub_window)
|
99
|
+
self.frame = tk.Frame(self.sub_window)
|
71
|
-
self.label = tk.Label(self.frame)
|
100
|
+
self.label = tk.Label(self.frame)
|
72
|
-
|
101
|
+
self.should_redefine_sub_window = False #ウィンドウを再生成したので再定義フラグをFalseに戻す
|
102
|
+
|
73
|
-
if show_border:
|
103
|
+
if show_border:
|
74
|
-
border = 'solid'
|
104
|
+
border = 'solid'
|
75
|
-
else:
|
105
|
+
else:
|
76
|
-
border = 'flat'
|
106
|
+
border = 'flat'
|
77
|
-
|
107
|
+
|
78
|
-
self.dummy_label.config(text=message, font=(font_name, font_size))
|
108
|
+
self.dummy_label.config(text=message, font=(font_name, font_size))
|
79
|
-
width = self.dummy_label.winfo_reqwidth()
|
109
|
+
width = self.dummy_label.winfo_reqwidth()
|
80
|
-
height = self.dummy_label.winfo_reqheight()
|
110
|
+
height = self.dummy_label.winfo_reqheight()
|
81
|
-
|
111
|
+
|
82
|
-
self.sub_window.wm_attributes("-alpha", transparency)
|
112
|
+
self.sub_window.wm_attributes("-alpha", transparency)
|
83
|
-
self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
113
|
+
self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
84
|
-
self.sub_window.configure(bg=back_color)
|
114
|
+
self.sub_window.configure(bg=back_color)
|
85
|
-
self.frame.config(bg=back_color, bd=1, relief=border)
|
115
|
+
self.frame.config(bg=back_color, bd=1, relief=border)
|
86
|
-
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
116
|
+
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
87
|
-
self.label.config(text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
117
|
+
self.label.config(text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
88
|
-
self.label.place(x=8, y=10, width=width, height=height)
|
118
|
+
self.label.place(x=8, y=10, width=width, height=height)
|
119
|
+
|
120
|
+
self.root.after(30, self.update_balloon, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
89
121
|
|
90
122
|
def hide(self):
|
123
|
+
self.should_hide_balloon = True
|
124
|
+
|
125
|
+
def show(self):
|
126
|
+
self.should_hide_balloon = False
|
127
|
+
|
128
|
+
|
129
|
+
def read_balloon_args(self, message=" ", x=0, y=0, font_name="Arial", font_size=14, fore_color="#000000", back_color="#FFFF00", transparency=1, show_border=True):
|
130
|
+
self.message = message
|
131
|
+
self.x = x
|
132
|
+
self.y = y
|
133
|
+
self.font_name = font_name
|
134
|
+
self.font_size = font_size
|
135
|
+
self.fore_color = fore_color
|
136
|
+
self.back_color = back_color
|
137
|
+
self.transparency = transparency
|
138
|
+
self.show_border = show_border
|
139
|
+
|
140
|
+
# ウィンドウを消しても引数が前と同じ値だとGUI表示を更新しない仕組みなので、ウィンドウ再生成用のフラグを作成、要再生成なら再生成と更新処理を実行
|
141
|
+
try:
|
142
|
+
self.should_redefine_sub_window = not self.sub_window.winfo_exists() #sub_windowが存在しない場合True
|
143
|
+
except Exception:
|
91
|
-
self.
|
144
|
+
self.should_redefine_sub_window = False
|
92
145
|
|
93
146
|
|
94
147
|
def balloon(message=" ", x=0, y=0, font_name="Arial", font_size=14, fore_color="#000000", back_color="#FFFF00", transparency=1, show_border=True):
|
95
148
|
global global_balloon
|
149
|
+
|
96
|
-
if
|
150
|
+
if not isinstance(global_balloon, balloon_class):
|
97
151
|
global_balloon=balloon_class(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
152
|
+
elif isinstance(global_balloon, balloon_class):
|
98
|
-
|
153
|
+
try:
|
99
|
-
|
154
|
+
if global_balloon.thread.is_alive():
|
155
|
+
global_balloon.show()
|
100
|
-
|
156
|
+
global_balloon.read_balloon_args(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
157
|
+
except Exception:
|
158
|
+
pass
|
101
159
|
|
102
160
|
|
103
161
|
def hide_balloon():
|
@@ -154,8 +212,7 @@
|
|
154
212
|
WEND
|
155
213
|
```
|
156
214
|
|
157
|
-
|
215
|
+
|
158
|
-
![イメージ説明](https://ddjkaamml8q8x.cloudfront.net/questions/2024-12-22/cfb35254-41e1-40b5-865e-66a3a4e3e127.gif)
|
159
216
|
※balloon.pyのソースコードは省略
|
160
217
|
```Python
|
161
218
|
from balloon import *
|
12
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -162,15 +162,18 @@
|
|
162
162
|
import time
|
163
163
|
import pyautogui
|
164
164
|
|
165
|
-
|
166
165
|
def getstate():
|
167
166
|
x,y =pyautogui.position()
|
168
167
|
s=f"マウス座標:{x},{y}"
|
169
168
|
return s
|
170
169
|
|
170
|
+
def func():
|
171
|
-
while True:
|
171
|
+
while True:
|
172
|
-
s=getstate()
|
172
|
+
s=getstate()
|
173
|
-
balloon(s,10,10)
|
173
|
+
balloon(s,10,10)
|
174
|
-
time.sleep(0.01)
|
174
|
+
time.sleep(0.01)
|
175
|
+
|
176
|
+
if __name__ != "__mp_main__":
|
177
|
+
func()
|
175
178
|
```
|
176
179
|
|
11
バグ修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -37,18 +37,20 @@
|
|
37
37
|
else:
|
38
38
|
border = 'flat'
|
39
39
|
|
40
|
+
#サイズ計算用のラベル
|
40
|
-
self.
|
41
|
+
self.dummy_label = tk.Label(self.sub_window, text=message, font=(font_name, font_size))
|
41
|
-
self.label = tk.Label(self.frame, text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
42
42
|
# ラベルサイズを計算
|
43
|
-
width = self.label.winfo_reqwidth()
|
43
|
+
width = self.dummy_label.winfo_reqwidth()
|
44
|
-
height = self.label.winfo_reqheight()
|
44
|
+
height = self.dummy_label.winfo_reqheight()
|
45
45
|
|
46
46
|
self.sub_window.attributes("-topmost", True)
|
47
47
|
# 全体を半透明にする(ウィンドウ全体が透明度を持つ)
|
48
48
|
self.sub_window.wm_attributes("-alpha", transparency)
|
49
49
|
self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
50
50
|
self.sub_window.configure(bg=back_color)
|
51
|
+
self.frame = tk.Frame(self.sub_window, bg=back_color, bd=1, relief=border)
|
51
52
|
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
53
|
+
self.label = tk.Label(self.frame, text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
52
54
|
self.label.place(x=8, y=10, width=width, height=height)
|
53
55
|
self.sub_window.wm_overrideredirect(True)
|
54
56
|
|
@@ -64,6 +66,7 @@
|
|
64
66
|
self.sub_window = tk.Toplevel(self.root)
|
65
67
|
self.sub_window.wm_overrideredirect(True)
|
66
68
|
self.sub_window.deiconify()
|
69
|
+
self.dummy_label = tk.Label(self.sub_window)
|
67
70
|
self.frame = tk.Frame(self.sub_window)
|
68
71
|
self.label = tk.Label(self.frame)
|
69
72
|
|
@@ -72,15 +75,16 @@
|
|
72
75
|
else:
|
73
76
|
border = 'flat'
|
74
77
|
|
75
|
-
self.frame.config(bg=back_color, bd=1, relief=border)
|
76
|
-
self.label.config(text=message, f
|
78
|
+
self.dummy_label.config(text=message, font=(font_name, font_size))
|
77
|
-
width = self.label.winfo_reqwidth()
|
79
|
+
width = self.dummy_label.winfo_reqwidth()
|
78
|
-
height = self.label.winfo_reqheight()
|
80
|
+
height = self.dummy_label.winfo_reqheight()
|
79
81
|
|
80
82
|
self.sub_window.wm_attributes("-alpha", transparency)
|
81
83
|
self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
82
84
|
self.sub_window.configure(bg=back_color)
|
85
|
+
self.frame.config(bg=back_color, bd=1, relief=border)
|
83
86
|
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
87
|
+
self.label.config(text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
84
88
|
self.label.place(x=8, y=10, width=width, height=height)
|
85
89
|
|
86
90
|
def hide(self):
|
10
コードの修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
そのうえでのコードに改善点があればご助言頂けると幸いです
|
12
12
|
|
13
13
|
|
14
|
-
### 該当のソースコード ※
|
14
|
+
### 該当のソースコード ※2025/1/2更新
|
15
15
|
|
16
16
|
```Python
|
17
17
|
import tkinter as tk
|
@@ -27,31 +27,46 @@
|
|
27
27
|
def create_balloon(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
28
28
|
|
29
29
|
self.root = tk.Tk()
|
30
|
+
self.root.withdraw()
|
31
|
+
self.root.wm_overrideredirect(True)
|
32
|
+
self.sub_window = tk.Toplevel(self.root)
|
33
|
+
self.sub_window.deiconify()
|
30
34
|
|
31
35
|
if show_border:
|
32
36
|
border = 'solid'
|
33
37
|
else:
|
34
38
|
border = 'flat'
|
35
39
|
|
36
|
-
self.frame = tk.Frame(self.
|
40
|
+
self.frame = tk.Frame(self.sub_window, bg=back_color, bd=1, relief=border)
|
37
41
|
self.label = tk.Label(self.frame, text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
38
42
|
# ラベルサイズを計算
|
39
43
|
width = self.label.winfo_reqwidth()
|
40
44
|
height = self.label.winfo_reqheight()
|
41
45
|
|
42
|
-
self.
|
46
|
+
self.sub_window.attributes("-topmost", True)
|
43
47
|
# 全体を半透明にする(ウィンドウ全体が透明度を持つ)
|
44
|
-
self.
|
48
|
+
self.sub_window.wm_attributes("-alpha", transparency)
|
45
|
-
self.
|
49
|
+
self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
46
|
-
self.
|
50
|
+
self.sub_window.configure(bg=back_color)
|
47
51
|
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
48
52
|
self.label.place(x=8, y=10, width=width, height=height)
|
49
|
-
self.
|
53
|
+
self.sub_window.wm_overrideredirect(True)
|
54
|
+
|
50
55
|
self.root.mainloop()
|
51
56
|
|
52
57
|
|
53
58
|
def update_balloon(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
59
|
+
|
54
|
-
|
60
|
+
try:
|
61
|
+
self.sub_window.deiconify()
|
62
|
+
except Exception:
|
63
|
+
# ウィンドウをAlt+F4で消した場合も再生成
|
64
|
+
self.sub_window = tk.Toplevel(self.root)
|
65
|
+
self.sub_window.wm_overrideredirect(True)
|
66
|
+
self.sub_window.deiconify()
|
67
|
+
self.frame = tk.Frame(self.sub_window)
|
68
|
+
self.label = tk.Label(self.frame)
|
69
|
+
|
55
70
|
if show_border:
|
56
71
|
border = 'solid'
|
57
72
|
else:
|
@@ -62,42 +77,41 @@
|
|
62
77
|
width = self.label.winfo_reqwidth()
|
63
78
|
height = self.label.winfo_reqheight()
|
64
79
|
|
65
|
-
self.
|
80
|
+
self.sub_window.wm_attributes("-alpha", transparency)
|
66
|
-
self.
|
81
|
+
self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
67
|
-
self.
|
82
|
+
self.sub_window.configure(bg=back_color)
|
68
83
|
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
69
84
|
self.label.place(x=8, y=10, width=width, height=height)
|
70
85
|
|
71
|
-
def de
|
86
|
+
def hide(self):
|
72
|
-
self.root.after(0, self.
|
87
|
+
self.root.after(0, self.sub_window.withdraw)
|
73
|
-
|
74
88
|
|
75
89
|
|
76
90
|
def balloon(message=" ", x=0, y=0, font_name="Arial", font_size=14, fore_color="#000000", back_color="#FFFF00", transparency=1, show_border=True):
|
77
|
-
try:
|
78
|
-
|
91
|
+
global global_balloon
|
79
|
-
|
92
|
+
if 'global_balloon' not in globals() or not global_balloon.thread.is_alive():
|
80
|
-
|
93
|
+
global_balloon=balloon_class(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
81
94
|
else:
|
82
95
|
root = tk._get_default_root()
|
83
|
-
root.after_idle(lambda:
|
96
|
+
root.after_idle(lambda: global_balloon.update_balloon(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border))
|
84
97
|
|
85
98
|
|
86
|
-
def de
|
99
|
+
def hide_balloon():
|
87
100
|
try:
|
88
|
-
|
101
|
+
global_balloon.hide()
|
89
|
-
delattr(tk, "global_balloon")
|
90
|
-
except
|
102
|
+
except Exception:
|
91
103
|
pass
|
104
|
+
|
105
|
+
|
92
106
|
|
93
107
|
if __name__ == "__main__":
|
94
108
|
import time
|
95
109
|
balloon(message="Hello World!\nThis is a test message.\nこんにちは", x=300, y=500, font_name="Arial", font_size=14, fore_color="#000000", back_color="#00FFFF", transparency=0.7, show_border=False)
|
96
110
|
|
97
111
|
time.sleep(3)
|
98
|
-
de
|
112
|
+
hide_balloon()
|
99
113
|
time.sleep(1)
|
100
|
-
de
|
114
|
+
hide_balloon()
|
101
115
|
time.sleep(1)
|
102
116
|
balloon(message="Updated Content!", x=300, y=500, font_size=14, back_color="#00FFFF", transparency=0.7, show_border=False)
|
103
117
|
time.sleep(3)
|
9
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -76,7 +76,7 @@
|
|
76
76
|
def balloon(message=" ", x=0, y=0, font_name="Arial", font_size=14, fore_color="#000000", back_color="#FFFF00", transparency=1, show_border=True):
|
77
77
|
try:
|
78
78
|
tk.global_balloon
|
79
|
-
except
|
79
|
+
except AttributeError:
|
80
80
|
tk.global_balloon=balloon_class(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
81
81
|
else:
|
82
82
|
root = tk._get_default_root()
|
@@ -86,9 +86,8 @@
|
|
86
86
|
def destroy_balloon():
|
87
87
|
try:
|
88
88
|
tk.global_balloon.destroy()
|
89
|
-
tk.global_balloon=None
|
90
|
-
delatt
|
89
|
+
delattr(tk, "global_balloon")
|
91
|
-
except
|
90
|
+
except AttributeError:
|
92
91
|
pass
|
93
92
|
|
94
93
|
if __name__ == "__main__":
|
@@ -97,7 +96,9 @@
|
|
97
96
|
|
98
97
|
time.sleep(3)
|
99
98
|
destroy_balloon()
|
100
|
-
time.sleep(
|
99
|
+
time.sleep(1)
|
100
|
+
destroy_balloon()
|
101
|
+
time.sleep(1)
|
101
102
|
balloon(message="Updated Content!", x=300, y=500, font_size=14, back_color="#00FFFF", transparency=0.7, show_border=False)
|
102
103
|
time.sleep(3)
|
103
104
|
balloon(message="Final Update!", x=300, y=500, font_size=14, back_color="#00FFFF", transparency=0.7, show_border=True)
|
8
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -11,10 +11,10 @@
|
|
11
11
|
そのうえでのコードに改善点があればご助言頂けると幸いです
|
12
12
|
|
13
13
|
|
14
|
-
### 該当のソースコード ※12/2
|
14
|
+
### 該当のソースコード ※12/25更新
|
15
15
|
|
16
16
|
```Python
|
17
|
-
i
|
17
|
+
import tkinter as tk
|
18
18
|
import threading
|
19
19
|
|
20
20
|
|
@@ -27,57 +27,46 @@
|
|
27
27
|
def create_balloon(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
28
28
|
|
29
29
|
self.root = tk.Tk()
|
30
|
+
|
31
|
+
if show_border:
|
32
|
+
border = 'solid'
|
33
|
+
else:
|
34
|
+
border = 'flat'
|
35
|
+
|
36
|
+
self.frame = tk.Frame(self.root, bg=back_color, bd=1, relief=border)
|
37
|
+
self.label = tk.Label(self.frame, text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
38
|
+
# ラベルサイズを計算
|
39
|
+
width = self.label.winfo_reqwidth()
|
30
|
-
|
40
|
+
height = self.label.winfo_reqheight()
|
41
|
+
|
31
42
|
self.root.attributes("-topmost", True)
|
32
43
|
# 全体を半透明にする(ウィンドウ全体が透明度を持つ)
|
33
44
|
self.root.wm_attributes("-alpha", transparency)
|
34
45
|
self.root.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
35
46
|
self.root.configure(bg=back_color)
|
36
|
-
if show_border:
|
37
|
-
border = 'solid'
|
38
|
-
else:
|
39
|
-
border = 'flat'
|
40
|
-
self.frame = tk.Frame(self.root, bg=back_color, bd=1, relief=border)
|
41
47
|
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
42
|
-
self.label = tk.Label(self.frame, text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
43
48
|
self.label.place(x=8, y=10, width=width, height=height)
|
44
49
|
self.root.wm_overrideredirect(True)
|
45
50
|
self.root.mainloop()
|
46
51
|
|
47
52
|
|
48
|
-
def calculate_label_size(self, message, font_name, font_size):
|
49
|
-
"""指定されたメッセージとフォントを使用して、Labelの幅と高さを計算"""
|
50
|
-
try:
|
51
|
-
self.dummy_label.config(text=message, font=(font_name, font_size)) # 2回目以降はラベルを更新
|
52
|
-
except Exception:
|
53
|
-
self.dummy_root = tk.Toplevel(self.root)
|
54
|
-
self.dummy_root.withdraw() # メインウィンドウを表示しない
|
55
|
-
# 仮のLabelを作成してサイズを計算
|
56
|
-
self.dummy_label = tk.Label(self.dummy_root, text=message, font=(font_name, font_size))
|
57
|
-
|
58
|
-
self.dummy_label.after(0, self.dummy_label.update_idletasks) # レイアウト情報を更新
|
59
|
-
width = self.dummy_label.winfo_reqwidth() # 必要な幅
|
60
|
-
height = self.dummy_label.winfo_reqheight() # 必要な高さ
|
61
|
-
|
62
|
-
return width, height
|
63
|
-
|
64
|
-
|
65
|
-
|
66
53
|
def update_balloon(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
67
54
|
|
68
|
-
width, height = self.calculate_label_size(message, font_name, font_size)
|
69
|
-
self.root.wm_attributes("-alpha", transparency)
|
70
|
-
self.root.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
71
|
-
self.root.configure(bg=back_color)
|
72
55
|
if show_border:
|
73
56
|
border = 'solid'
|
74
57
|
else:
|
75
58
|
border = 'flat'
|
59
|
+
|
76
60
|
self.frame.config(bg=back_color, bd=1, relief=border)
|
61
|
+
self.label.config(text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
62
|
+
width = self.label.winfo_reqwidth()
|
63
|
+
height = self.label.winfo_reqheight()
|
64
|
+
|
65
|
+
self.root.wm_attributes("-alpha", transparency)
|
66
|
+
self.root.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
67
|
+
self.root.configure(bg=back_color)
|
77
68
|
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
78
|
-
self.label.config(text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
79
69
|
self.label.place(x=8, y=10, width=width, height=height)
|
80
|
-
self.root.after(0, self.root.update)
|
81
70
|
|
82
71
|
def destroy(self):
|
83
72
|
self.root.after(0, self.root.destroy)
|
@@ -87,16 +76,18 @@
|
|
87
76
|
def balloon(message=" ", x=0, y=0, font_name="Arial", font_size=14, fore_color="#000000", back_color="#FFFF00", transparency=1, show_border=True):
|
88
77
|
try:
|
89
78
|
tk.global_balloon
|
79
|
+
except Exception:
|
80
|
+
tk.global_balloon=balloon_class(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
81
|
+
else:
|
90
82
|
root = tk._get_default_root()
|
91
83
|
root.after_idle(lambda: tk.global_balloon.update_balloon(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border))
|
92
|
-
except Exception:
|
93
|
-
tk.global_balloon=balloon_class(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
94
84
|
|
95
85
|
|
96
86
|
def destroy_balloon():
|
97
87
|
try:
|
98
88
|
tk.global_balloon.destroy()
|
99
89
|
tk.global_balloon=None
|
90
|
+
delatter(tk, "global_balloon")
|
100
91
|
except Exception:
|
101
92
|
pass
|
102
93
|
|
7
追記
test
CHANGED
File without changes
|
test
CHANGED
@@ -89,7 +89,7 @@
|
|
89
89
|
tk.global_balloon
|
90
90
|
root = tk._get_default_root()
|
91
91
|
root.after_idle(lambda: tk.global_balloon.update_balloon(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border))
|
92
|
-
except
|
92
|
+
except Exception:
|
93
93
|
tk.global_balloon=balloon_class(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
94
94
|
|
95
95
|
|
@@ -97,7 +97,7 @@
|
|
97
97
|
try:
|
98
98
|
tk.global_balloon.destroy()
|
99
99
|
tk.global_balloon=None
|
100
|
-
except
|
100
|
+
except Exception:
|
101
101
|
pass
|
102
102
|
|
103
103
|
if __name__ == "__main__":
|
@@ -105,11 +105,12 @@
|
|
105
105
|
balloon(message="Hello World!\nThis is a test message.\nこんにちは", x=300, y=500, font_name="Arial", font_size=14, fore_color="#000000", back_color="#00FFFF", transparency=0.7, show_border=False)
|
106
106
|
|
107
107
|
time.sleep(3)
|
108
|
+
destroy_balloon()
|
109
|
+
time.sleep(2)
|
108
110
|
balloon(message="Updated Content!", x=300, y=500, font_size=14, back_color="#00FFFF", transparency=0.7, show_border=False)
|
109
111
|
time.sleep(3)
|
110
112
|
balloon(message="Final Update!", x=300, y=500, font_size=14, back_color="#00FFFF", transparency=0.7, show_border=True)
|
111
113
|
time.sleep(3)
|
112
|
-
|
113
114
|
```
|
114
115
|
|
115
116
|
### 試したこと・調べたこと
|
6
コード修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
### 該当のソースコード ※12/24更新
|
15
15
|
|
16
16
|
```Python
|
17
|
-
import tkinter as tk
|
17
|
+
iimport tkinter as tk
|
18
18
|
import threading
|
19
19
|
|
20
20
|
|
@@ -86,8 +86,10 @@
|
|
86
86
|
|
87
87
|
def balloon(message=" ", x=0, y=0, font_name="Arial", font_size=14, fore_color="#000000", back_color="#FFFF00", transparency=1, show_border=True):
|
88
88
|
try:
|
89
|
+
tk.global_balloon
|
90
|
+
root = tk._get_default_root()
|
89
|
-
tk.global_balloon.update_balloon(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
91
|
+
root.after_idle(lambda: tk.global_balloon.update_balloon(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border))
|
90
|
-
except
|
92
|
+
except AttributeError:
|
91
93
|
tk.global_balloon=balloon_class(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
92
94
|
|
93
95
|
|
@@ -95,7 +97,7 @@
|
|
95
97
|
try:
|
96
98
|
tk.global_balloon.destroy()
|
97
99
|
tk.global_balloon=None
|
98
|
-
except
|
100
|
+
except AttributeError:
|
99
101
|
pass
|
100
102
|
|
101
103
|
if __name__ == "__main__":
|
5
追記
test
CHANGED
File without changes
|
test
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
### 実現したいこと
|
2
2
|
[https://94.gigafile.nu/0325-c7f527087ad73dda5963f92a783e34fbb](url)
|
3
3
|
上記ファイルのrun.batを押して動くプログラムをPythonで再現したいと思っています
|
4
|
+
一例として、マウス座標をリアルタイムで表示するプログラムですが、他にも移植したいものがあり、だいたいのコードは再現できるもののある機能がPythonに用意しておらず、1から作成する必要があります
|
4
5
|
上記プログラムはexeファイルで、rustで作られたインタプリタ言語、「UWSCR」の実行ファイルとUWSCRの実行用ソースコードです
|
5
6
|
UWSCRの組み込み関数「balloon」をPythonで再現することが第一の目的となります
|
6
7
|
基本的にプログラムの書き方はこのUWSCRと同じ書き方で再現させる方針です、
|
@@ -9,7 +10,6 @@
|
|
9
10
|
当該pyファイルをimportしてライブラリ的な使用方法を想定しているので、非推奨ではあるもののTkinterを別スレッドで動かす必要があります
|
10
11
|
そのうえでのコードに改善点があればご助言頂けると幸いです
|
11
12
|
|
12
|
-
![イメージ説明](https://ddjkaamml8q8x.cloudfront.net/questions/2024-12-18/41069cd0-e6c7-4fc4-b4d2-922476354f23.gif)
|
13
13
|
|
14
14
|
### 該当のソースコード ※12/24更新
|
15
15
|
|
4
使うライブラリを変えました
test
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
Tkinterによる可変サイズダイアログの作成
|
test
CHANGED
@@ -1,204 +1,112 @@
|
|
1
1
|
### 実現したいこと
|
2
2
|
[https://94.gigafile.nu/0325-c7f527087ad73dda5963f92a783e34fbb](url)
|
3
3
|
上記ファイルのrun.batを押して動くプログラムをPythonで再現したいと思っています
|
4
|
-
|
4
|
+
上記プログラムはexeファイルで、rustで作られたインタプリタ言語、「UWSCR」の実行ファイルとUWSCRの実行用ソースコードです
|
5
|
-
|
5
|
+
UWSCRの組み込み関数「balloon」をPythonで再現することが第一の目的となります
|
6
|
-
Tkinterで似たようなものは作れますが、見た目をなるべく同じにしたいため、上記プログラムと同じようにwin32apiを利用したウィンドウ描写を試みています
|
7
|
-
そこで上記プログラム(Rust製)のballoon関数のソースコードをChatGPTにトランスパイルしてもらい修正を繰り返したのですがある問題が解決できずにいます
|
8
|
-
こちらのバルーンですが
|
9
|
-
指定した文字列をバルーン上に表示、バルーンの大きさは文字列の文字数と行数に応じて変化する
|
10
|
-
|
6
|
+
基本的にプログラムの書き方はこのUWSCRと同じ書き方で再現させる方針です、
|
11
|
-
内容に変更があれば、シームレスに変更が可能で、すぐに文字列が変えられます
|
12
|
-
ひとまずテスト用プログラムとして最初に"Hello World!"とバルーンに表示
|
13
|
-
3秒後に"Updated Content!"とバルーンに表示し、また3秒後に"Final Update!"と表示するプログラムを作成しましたが、問題が解決できずにいるので助言いただけると幸いです
|
14
7
|
|
15
8
|
### 発生している問題・分からないこと
|
16
|
-
こちらのコードですが
|
17
|
-
|
9
|
+
当該pyファイルをimportしてライブラリ的な使用方法を想定しているので、非推奨ではあるもののTkinterを別スレッドで動かす必要があります
|
18
|
-
"Updated Content!"
|
19
|
-
|
10
|
+
そのうえでのコードに改善点があればご助言頂けると幸いです
|
20
|
-
balloon.update(message="Final Update!")
|
21
|
-
を処理するときにウィンドウの表示が乱れてメッセージが正常に表示されません
|
22
|
-
別ウィンドウをアクティブにしないままなら正常に動きます
|
23
|
-
3回内容を更新させて実験してみたところ、バルーンが非アクティブかつupdateメソッドが2回目以降の場合に表示が乱れるようです
|
24
|
-
プログラムの用途上、ウィンドウのアクティブ状態にかかわらず内容が表示される必要があります
|
25
|
-
なぜ、1回目のupdateではウィンドウが非アクティブでも正常に更新されるのに2回目以降は表示が乱れるのですか?
|
26
|
-
どうやったら問題を解決できますか?
|
27
11
|
|
28
12
|
![イメージ説明](https://ddjkaamml8q8x.cloudfront.net/questions/2024-12-18/41069cd0-e6c7-4fc4-b4d2-922476354f23.gif)
|
29
13
|
|
30
|
-
### 該当のソースコード ※12/2
|
14
|
+
### 該当のソースコード ※12/24更新
|
31
15
|
|
32
16
|
```Python
|
33
|
-
import
|
17
|
+
import tkinter as tk
|
34
|
-
import
|
18
|
+
import threading
|
35
|
-
import win32con
|
36
|
-
import win32ui
|
37
|
-
from ctypes import windll
|
38
19
|
|
39
|
-
class Balloon:
|
40
|
-
class_name = "BalloonWindow"
|
41
|
-
class_registered = False
|
42
20
|
|
21
|
+
class balloon_class:
|
22
|
+
|
43
|
-
def __init__(self, message, x
|
23
|
+
def __init__(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
24
|
+
self.thread = threading.Thread(target=self.create_balloon, daemon=True, args=(message,x,y,font_name,font_size,fore_color,back_color,transparency, show_border))
|
44
|
-
self.
|
25
|
+
self.thread.start()
|
45
|
-
|
26
|
+
|
46
|
-
self.y = y
|
47
|
-
self.font_name = font_name
|
48
|
-
self.font_size = font_size
|
49
|
-
self.fore_color = fore_color
|
50
|
-
self.back_color = back_color
|
51
|
-
|
27
|
+
def create_balloon(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
52
28
|
|
29
|
+
self.root = tk.Tk()
|
30
|
+
width, height = self.calculate_label_size(message, font_name, font_size)
|
31
|
+
self.root.attributes("-topmost", True)
|
32
|
+
# 全体を半透明にする(ウィンドウ全体が透明度を持つ)
|
33
|
+
self.root.wm_attributes("-alpha", transparency)
|
34
|
+
self.root.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
35
|
+
self.root.configure(bg=back_color)
|
53
|
-
|
36
|
+
if show_border:
|
37
|
+
border = 'solid'
|
38
|
+
else:
|
39
|
+
border = 'flat'
|
40
|
+
self.frame = tk.Frame(self.root, bg=back_color, bd=1, relief=border)
|
41
|
+
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
42
|
+
self.label = tk.Label(self.frame, text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
43
|
+
self.label.place(x=8, y=10, width=width, height=height)
|
54
|
-
self.
|
44
|
+
self.root.wm_overrideredirect(True)
|
45
|
+
self.root.mainloop()
|
46
|
+
|
47
|
+
|
48
|
+
def calculate_label_size(self, message, font_name, font_size):
|
49
|
+
"""指定されたメッセージとフォントを使用して、Labelの幅と高さを計算"""
|
50
|
+
try:
|
51
|
+
self.dummy_label.config(text=message, font=(font_name, font_size)) # 2回目以降はラベルを更新
|
52
|
+
except Exception:
|
55
|
-
|
53
|
+
self.dummy_root = tk.Toplevel(self.root)
|
56
|
-
|
54
|
+
self.dummy_root.withdraw() # メインウィンドウを表示しない
|
57
|
-
|
55
|
+
# 仮のLabelを作成してサイズを計算
|
56
|
+
self.dummy_label = tk.Label(self.dummy_root, text=message, font=(font_name, font_size))
|
57
|
+
|
58
|
+
self.dummy_label.after(0, self.dummy_label.update_idletasks) # レイアウト情報を更新
|
59
|
+
width = self.dummy_label.winfo_reqwidth() # 必要な幅
|
60
|
+
height = self.dummy_label.winfo_reqheight() # 必要な高さ
|
61
|
+
|
62
|
+
return width, height
|
63
|
+
|
58
64
|
|
65
|
+
|
66
|
+
def update_balloon(self, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border):
|
67
|
+
|
68
|
+
width, height = self.calculate_label_size(message, font_name, font_size)
|
69
|
+
self.root.wm_attributes("-alpha", transparency)
|
70
|
+
self.root.geometry(f"{width+20}x{height+20}+{x}+{y}")
|
71
|
+
self.root.configure(bg=back_color)
|
59
|
-
|
72
|
+
if show_border:
|
73
|
+
border = 'solid'
|
74
|
+
else:
|
75
|
+
border = 'flat'
|
76
|
+
self.frame.config(bg=back_color, bd=1, relief=border)
|
77
|
+
self.frame.place(x=0, y=0, width=width+20, height=height+20)
|
78
|
+
self.label.config(text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
|
79
|
+
self.label.place(x=8, y=10, width=width, height=height)
|
80
|
+
self.root.after(0, self.root.update)
|
81
|
+
|
82
|
+
def destroy(self):
|
60
|
-
self.
|
83
|
+
self.root.after(0, self.root.destroy)
|
84
|
+
|
61
85
|
|
62
|
-
# 描画
|
63
|
-
self.draw()
|
64
86
|
|
87
|
+
def balloon(message=" ", x=0, y=0, font_name="Arial", font_size=14, fore_color="#000000", back_color="#FFFF00", transparency=1, show_border=True):
|
88
|
+
try:
|
89
|
+
tk.global_balloon.update_balloon(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
65
|
-
|
90
|
+
except Exception:
|
66
|
-
def register_class(cls):
|
67
|
-
if not cls.class_registered:
|
68
|
-
wnd_class = win32gui.WNDCLASS()
|
69
|
-
|
91
|
+
tk.global_balloon=balloon_class(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
|
70
|
-
wnd_class.lpszClassName = cls.class_name
|
71
|
-
wnd_class.lpfnWndProc = cls.wnd_proc
|
72
|
-
win32gui.RegisterClass(wnd_class)
|
73
|
-
cls.class_registered = True
|
74
92
|
|
75
|
-
def create_window(self):
|
76
|
-
self.__class__.register_class()
|
77
93
|
|
78
|
-
hwnd = win32gui.CreateWindowEx(
|
79
|
-
win32con.WS_EX_LAYERED | win32con.WS_EX_TOPMOST | win32con.WS_EX_TOOLWINDOW,
|
80
|
-
|
94
|
+
def destroy_balloon():
|
95
|
+
try:
|
96
|
+
tk.global_balloon.destroy()
|
81
|
-
|
97
|
+
tk.global_balloon=None
|
82
|
-
win32con.WS_POPUP,
|
83
|
-
self.x,
|
84
|
-
self.y,
|
85
|
-
|
98
|
+
except Exception:
|
86
|
-
0,
|
87
|
-
0,
|
88
|
-
win32api.GetModuleHandle(None),
|
89
|
-
|
99
|
+
pass
|
90
|
-
)
|
91
100
|
|
92
|
-
win32gui.SetLayeredWindowAttributes(
|
93
|
-
hwnd, 0, self.transparency, win32con.LWA_ALPHA
|
94
|
-
)
|
95
|
-
win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
|
96
|
-
return hwnd
|
97
|
-
|
98
|
-
def draw(self):
|
99
|
-
hdc = win32gui.GetDC(self.hwnd)
|
100
|
-
hdc_mem = win32gui.CreateCompatibleDC(hdc)
|
101
|
-
|
102
|
-
text_width, text_height = self.calculate_text_size(hdc_mem)
|
103
|
-
|
104
|
-
new_width = text_width + 20
|
105
|
-
new_height = text_height + 20
|
106
|
-
win32gui.MoveWindow(self.hwnd, self.x, self.y, new_width, new_height, True)
|
107
|
-
|
108
|
-
brush = win32gui.CreateSolidBrush(self.back_color)
|
109
|
-
bitmap = win32gui.CreateCompatibleBitmap(hdc, new_width, new_height)
|
110
|
-
win32gui.SelectObject(hdc_mem, bitmap)
|
111
|
-
win32gui.FillRect(hdc_mem, (0, 0, new_width, new_height), brush)
|
112
|
-
|
113
|
-
win32gui.SelectObject(hdc_mem, self.font.GetSafeHandle())
|
114
|
-
win32gui.SetTextColor(hdc_mem, self.fore_color)
|
115
|
-
win32gui.SetBkMode(hdc_mem, win32con.TRANSPARENT)
|
116
|
-
win32gui.DrawText(
|
117
|
-
hdc_mem,
|
118
|
-
self.message,
|
119
|
-
-1,
|
120
|
-
(10, 10, new_width - 10, new_height - 10),
|
121
|
-
win32con.DT_LEFT | win32con.DT_WORDBREAK
|
122
|
-
)
|
123
|
-
|
124
|
-
win32gui.BitBlt(hdc, 0, 0, new_width, new_height, hdc_mem, 0, 0, win32con.SRCCOPY)
|
125
|
-
|
126
|
-
win32gui.DeleteObject(bitmap)
|
127
|
-
win32gui.DeleteDC(hdc_mem)
|
128
|
-
|
129
|
-
def calculate_text_size(self, hdc):
|
130
|
-
win32gui.SelectObject(hdc, self.font.GetSafeHandle())
|
131
|
-
|
132
|
-
lines = self.message.split("\n")
|
133
|
-
max_width = 0
|
134
|
-
line_height = abs(self.font_size)
|
135
|
-
total_height = len(lines) * line_height
|
136
|
-
|
137
|
-
for line in lines:
|
138
|
-
size = win32gui.GetTextExtentPoint32(hdc, line)
|
139
|
-
max_width = max(max_width, size[0])
|
140
|
-
|
141
|
-
return max_width, total_height
|
142
|
-
|
143
|
-
def update(self, message=None, x=None, y=None, font_name=None, font_size=None, fore_color=None, back_color=None, transparency=None):
|
144
|
-
""" バルーンの内容や位置を更新 """
|
145
|
-
if message is not None:
|
146
|
-
self.message = message
|
147
|
-
if x is not None:
|
148
|
-
self.x = x
|
149
|
-
if y is not None:
|
150
|
-
self.y = y
|
151
|
-
if font_name is not None:
|
152
|
-
self.font_name = font_name
|
153
|
-
if font_size is not None:
|
154
|
-
self.font_size = font_size
|
155
|
-
if fore_color is not None:
|
156
|
-
self.fore_color = fore_color
|
157
|
-
if back_color is not None:
|
158
|
-
self.back_color = back_color
|
159
|
-
if transparency is not None:
|
160
|
-
self.transparency = transparency
|
161
|
-
self.draw()
|
162
|
-
|
163
|
-
@staticmethod
|
164
|
-
def wnd_proc(hwnd, msg, wparam, lparam):
|
165
|
-
|
101
|
+
if __name__ == "__main__":
|
166
|
-
win32gui.PostQuitMessage(0)
|
167
|
-
return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)
|
168
|
-
|
169
|
-
def destroy(self):
|
170
|
-
win32gui.DestroyWindow(self.hwnd)
|
171
|
-
|
172
|
-
import time
|
102
|
+
import time
|
173
|
-
win32gui.PumpWaitingMessages()
|
174
|
-
|
175
|
-
# 初期バルーン
|
176
|
-
balloon = Balloon(
|
177
|
-
message="Hello World!",
|
103
|
+
balloon(message="Hello World!\nThis is a test message.\nこんにちは", x=300, y=500, font_name="Arial", font_size=14, fore_color="#000000", back_color="#00FFFF", transparency=0.7, show_border=False)
|
178
|
-
|
104
|
+
|
179
|
-
y=300,
|
180
|
-
font_name="Arial",
|
181
|
-
font_size=20,
|
182
|
-
fore_color=0x000000,
|
183
|
-
back_color=0xFFFF00,
|
184
|
-
transparency=200
|
185
|
-
)
|
186
|
-
win32gui.PumpWaitingMessages()
|
187
|
-
time.sleep(3)
|
105
|
+
time.sleep(3)
|
188
|
-
|
189
|
-
# 内容と位置を更新
|
190
|
-
balloon
|
106
|
+
balloon(message="Updated Content!", x=300, y=500, font_size=14, back_color="#00FFFF", transparency=0.7, show_border=False)
|
191
|
-
win32gui.PumpWaitingMessages()
|
192
|
-
time.sleep(3)
|
107
|
+
time.sleep(3)
|
193
|
-
|
194
|
-
# さらに更新
|
195
|
-
balloon
|
108
|
+
balloon(message="Final Update!", x=300, y=500, font_size=14, back_color="#00FFFF", transparency=0.7, show_border=True)
|
196
|
-
win32gui.PumpWaitingMessages()
|
197
|
-
time.sleep(3)
|
109
|
+
time.sleep(3)
|
198
|
-
|
199
|
-
# 終了
|
200
|
-
balloon.destroy()
|
201
|
-
|
202
110
|
|
203
111
|
```
|
204
112
|
|
@@ -209,12 +117,10 @@
|
|
209
117
|
- [x] その他
|
210
118
|
|
211
119
|
##### 上記の詳細・結果
|
212
|
-
ChatGPTに症状を伝え、修正を依頼することを何度も繰り返しましたが、修正してもらったコードでも正常な結果が得られません
|
213
|
-
win32api
|
120
|
+
当初はwin32apiで実現しようと思いましたが難しいことがわかりTkinterでの実装中です
|
214
121
|
|
215
122
|
### 補足
|
216
123
|
Python 3.10.4で実行しています
|
217
|
-
ライブラリはpywin32です
|
218
124
|
|
219
125
|
### 追記
|
220
126
|
rust製のインタプリタ言語「UWSCR」での挙動
|
3
追記
test
CHANGED
File without changes
|
test
CHANGED
@@ -216,4 +216,42 @@
|
|
216
216
|
Python 3.10.4で実行しています
|
217
217
|
ライブラリはpywin32です
|
218
218
|
|
219
|
-
|
219
|
+
### 追記
|
220
|
+
rust製のインタプリタ言語「UWSCR」での挙動
|
221
|
+
![イメージ説明](https://ddjkaamml8q8x.cloudfront.net/questions/2024-12-22/604bfdbf-7954-4a06-af9e-3b6f628170ac.gif)
|
222
|
+
UWSCRソースコード
|
223
|
+
```UWSCR
|
224
|
+
FUNCTION GETSTATE()
|
225
|
+
x=G_MOUSE_X
|
226
|
+
y=G_MOUSE_Y
|
227
|
+
s="マウス座標:" + x + "," + y
|
228
|
+
result=s
|
229
|
+
FEND
|
230
|
+
|
231
|
+
WHILE True
|
232
|
+
s = GETSTATE()
|
233
|
+
balloon(s, 10, 10)
|
234
|
+
Sleep(0.01)
|
235
|
+
WEND
|
236
|
+
```
|
237
|
+
|
238
|
+
上記についてTkinterで再現できました、今のところ問題ありません
|
239
|
+
![イメージ説明](https://ddjkaamml8q8x.cloudfront.net/questions/2024-12-22/cfb35254-41e1-40b5-865e-66a3a4e3e127.gif)
|
240
|
+
※balloon.pyのソースコードは省略
|
241
|
+
```Python
|
242
|
+
from balloon import *
|
243
|
+
import time
|
244
|
+
import pyautogui
|
245
|
+
|
246
|
+
|
247
|
+
def getstate():
|
248
|
+
x,y =pyautogui.position()
|
249
|
+
s=f"マウス座標:{x},{y}"
|
250
|
+
return s
|
251
|
+
|
252
|
+
while True:
|
253
|
+
s=getstate()
|
254
|
+
balloon(s,10,10)
|
255
|
+
time.sleep(0.01)
|
256
|
+
```
|
257
|
+
|
2
コードの修正
test
CHANGED
File without changes
|
test
CHANGED
@@ -27,7 +27,7 @@
|
|
27
27
|
|
28
28
|
![イメージ説明](https://ddjkaamml8q8x.cloudfront.net/questions/2024-12-18/41069cd0-e6c7-4fc4-b4d2-922476354f23.gif)
|
29
29
|
|
30
|
-
### 該当のソースコード
|
30
|
+
### 該当のソースコード ※12/21追記
|
31
31
|
|
32
32
|
```Python
|
33
33
|
import win32gui
|
@@ -170,6 +170,7 @@
|
|
170
170
|
win32gui.DestroyWindow(self.hwnd)
|
171
171
|
|
172
172
|
import time
|
173
|
+
win32gui.PumpWaitingMessages()
|
173
174
|
|
174
175
|
# 初期バルーン
|
175
176
|
balloon = Balloon(
|
@@ -182,20 +183,22 @@
|
|
182
183
|
back_color=0xFFFF00,
|
183
184
|
transparency=200
|
184
185
|
)
|
185
|
-
|
186
|
+
win32gui.PumpWaitingMessages()
|
186
187
|
time.sleep(3)
|
187
188
|
|
188
189
|
# 内容と位置を更新
|
189
190
|
balloon.update(message="Updated Content!", x=500, y=300)
|
190
|
-
|
191
|
+
win32gui.PumpWaitingMessages()
|
191
192
|
time.sleep(3)
|
192
193
|
|
193
194
|
# さらに更新
|
194
195
|
balloon.update(message="Final Update!")
|
196
|
+
win32gui.PumpWaitingMessages()
|
195
197
|
time.sleep(3)
|
196
198
|
|
197
199
|
# 終了
|
198
200
|
balloon.destroy()
|
201
|
+
|
199
202
|
|
200
203
|
```
|
201
204
|
|
@@ -212,3 +215,5 @@
|
|
212
215
|
### 補足
|
213
216
|
Python 3.10.4で実行しています
|
214
217
|
ライブラリはpywin32です
|
218
|
+
|
219
|
+
|
1
再現動画をgifで載せました
test
CHANGED
File without changes
|
test
CHANGED
@@ -25,7 +25,7 @@
|
|
25
25
|
なぜ、1回目のupdateではウィンドウが非アクティブでも正常に更新されるのに2回目以降は表示が乱れるのですか?
|
26
26
|
どうやったら問題を解決できますか?
|
27
27
|
|
28
|
-
|
28
|
+
![イメージ説明](https://ddjkaamml8q8x.cloudfront.net/questions/2024-12-18/41069cd0-e6c7-4fc4-b4d2-922476354f23.gif)
|
29
29
|
|
30
30
|
### 該当のソースコード
|
31
31
|
|