質問編集履歴

18

コードの修正

2025/01/10 23:56

投稿

hiro12345
hiro12345

スコア1

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

コード修正

2025/01/07 16:54

投稿

hiro12345
hiro12345

スコア1

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
- should_redefine_sub_window = False #ウィンドウを再生成したので再定義フラグをFalseに戻す
114
+
115
115
 
116
116
  if show_border:
117
117
  border = 'solid'

16

コード誤字

2025/01/07 16:50

投稿

hiro12345
hiro12345

スコア1

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
- self.should_redefine_sub_window = False #ウィンドウを再生成したので再定義フラグをFalseに戻す
114
+ should_redefine_sub_window = False #ウィンドウを再生成したので再定義フラグをFalseに戻す
115
115
 
116
116
  if show_border:
117
117
  border = 'solid'

15

コードの修正

2025/01/07 16:39

投稿

hiro12345
hiro12345

スコア1

test CHANGED
File without changes
test CHANGED
@@ -9,7 +9,7 @@
9
9
  ### 発生している問題・分からないこと
10
10
  文字数制限により略
11
11
 
12
- ### 該当のソースコード ※2025/1/6更新
12
+ ### 該当のソースコード ※2025/1/8更新
13
13
 
14
14
  ```Python
15
15
  import tkinter as tk

14

コードの修正

2025/01/07 16:38

投稿

hiro12345
hiro12345

スコア1

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
- # スレッド2重起動しないための万が一の措置、デバッ時はtryブロックの中は消して例外処理扱いせずに試してみること
27
+ # スレッド2重生成しないためのフラ
24
- try:
28
+ type(self).thread_creation_count += 1
25
- dummy = threading.Thread.is_alive(self.thread)
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
- except Exception:
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重生成しないための万が一の措置、デバッ時はtryブロックの中は消して例外処理扱いせずに試してみること
37
+ # rootを2重生成しないためのフラ
34
- try:
35
- self.root.withdraw()
38
+ type(self).root_creation_count += 1
36
- print("Error: 初期ウィンドウ生成メソッドが2回以上参照されています")
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
- self.should_hide_balloon = False
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
- # 比較用に使う前回使用の引数とread_balloon_args関数で読み取った表示更新用の引数のリスト
85
+ # 比較用に使う前回使用の引数とqueueで読み取った表示更新用の引数のリスト
79
86
  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]
87
+ new_args = balloon_args
81
-
88
+
82
89
  previous_should_hide_balloon = self.previous_should_hide_balloon
83
- new_should_hide_balloon = self.should_hide_balloon
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 self.should_redefine_sub_window == True: #値が更新された場合かウィンドウを消した場合に更新処理を実行
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
- def hide(self):
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 global_balloon.thread.is_alive():
145
+ if balloon_queue.empty():
155
- global_balloon.show()
156
- global_balloon.read_balloon_args(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
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
- global_balloon.hide()
157
+ should_hide_balloon = True
158
+ balloon_queue.put((dummy_args, should_hide_balloon))
164
159
  except Exception:
165
160
  pass
166
161
 

13

コード内容の修正

2025/01/06 10:10

投稿

hiro12345
hiro12345

スコア1

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/2更新
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.root.after(0, self.sub_window.withdraw)
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 'global_balloon' not in globals() or not global_balloon.thread.is_alive():
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
- else:
153
+ try:
99
- root = tk._get_default_root()
154
+ if global_balloon.thread.is_alive():
155
+ global_balloon.show()
100
- root.after_idle(lambda: global_balloon.update_balloon(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border))
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
- 上記についてTkinterで再現できました、今のところ問題ありません
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

コード修正

2025/01/03 16:52

投稿

hiro12345
hiro12345

スコア1

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

バグ修正

2025/01/02 13:07

投稿

hiro12345
hiro12345

スコア1

test CHANGED
File without changes
test CHANGED
@@ -37,18 +37,20 @@
37
37
  else:
38
38
  border = 'flat'
39
39
 
40
+ #サイズ計算用のラベル
40
- self.frame = tk.Frame(self.sub_window, bg=back_color, bd=1, relief=border)
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, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left")
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

コードの修正

2025/01/02 09:00

投稿

hiro12345
hiro12345

スコア1

test CHANGED
File without changes
test CHANGED
@@ -11,7 +11,7 @@
11
11
  そのうえでのコードに改善点があればご助言頂けると幸いです
12
12
 
13
13
 
14
- ### 該当のソースコード ※12/25更新
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.root, bg=back_color, bd=1, relief=border)
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.root.attributes("-topmost", True)
46
+ self.sub_window.attributes("-topmost", True)
43
47
  # 全体を半透明にする(ウィンドウ全体が透明度を持つ)
44
- self.root.wm_attributes("-alpha", transparency)
48
+ self.sub_window.wm_attributes("-alpha", transparency)
45
- self.root.geometry(f"{width+20}x{height+20}+{x}+{y}")
49
+ self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
46
- self.root.configure(bg=back_color)
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.root.wm_overrideredirect(True)
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.root.wm_attributes("-alpha", transparency)
80
+ self.sub_window.wm_attributes("-alpha", transparency)
66
- self.root.geometry(f"{width+20}x{height+20}+{x}+{y}")
81
+ self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}")
67
- self.root.configure(bg=back_color)
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 destroy(self):
86
+ def hide(self):
72
- self.root.after(0, self.root.destroy)
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
- tk.global_balloon
91
+ global global_balloon
79
- except AttributeError:
92
+ if 'global_balloon' not in globals() or not global_balloon.thread.is_alive():
80
- tk.global_balloon=balloon_class(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border)
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: tk.global_balloon.update_balloon(message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border))
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 destroy_balloon():
99
+ def hide_balloon():
87
100
  try:
88
- tk.global_balloon.destroy()
101
+ global_balloon.hide()
89
- delattr(tk, "global_balloon")
90
- except AttributeError:
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
- destroy_balloon()
112
+ hide_balloon()
99
113
  time.sleep(1)
100
- destroy_balloon()
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

コード修正

2024/12/24 17:25

投稿

hiro12345
hiro12345

スコア1

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 Exception:
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
- delatter(tk, "global_balloon")
89
+ delattr(tk, "global_balloon")
91
- except Exception:
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(2)
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

コード修正

2024/12/24 17:00

投稿

hiro12345
hiro12345

スコア1

test CHANGED
File without changes
test CHANGED
@@ -11,10 +11,10 @@
11
11
  そのうえでのコードに改善点があればご助言頂けると幸いです
12
12
 
13
13
 
14
- ### 該当のソースコード ※12/24更新
14
+ ### 該当のソースコード ※12/25更新
15
15
 
16
16
  ```Python
17
- iimport tkinter as tk
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
- width, height = self.calculate_label_size(message, font_name, font_size)
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

追記

2024/12/23 22:22

投稿

hiro12345
hiro12345

スコア1

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 AttributeError:
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 AttributeError:
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

コード修正

2024/12/23 21:43

投稿

hiro12345
hiro12345

スコア1

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 Exception:
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 Exception:
100
+ except AttributeError:
99
101
  pass
100
102
 
101
103
  if __name__ == "__main__":

5

追記

2024/12/23 21:18

投稿

hiro12345
hiro12345

スコア1

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

使うライブラリを変えました

2024/12/23 21:12

投稿

hiro12345
hiro12345

スコア1

test CHANGED
@@ -1 +1 @@
1
- win32apiを利用したウィンドウ描画て、特定条件下でウィンドウの表示が更新できない
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
- しかしPythonではバルーンを表示する機能がなく、一から作成する必要があります
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/21追記
14
+ ### 該当のソースコード ※12/24更新
31
15
 
32
16
  ```Python
33
- import win32gui
17
+ import tkinter as tk
34
- import win32api
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=0, y=0, font_name="Arial", font_size=20, fore_color=0x000000, back_color=0xFFFFFF, transparency=255):
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.message = message
25
+ self.thread.start()
45
- self.x = x
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
- self.transparency = transparency
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.font = win32ui.CreateFont({
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
- "name": self.font_name,
53
+ self.dummy_root = tk.Toplevel(self.root)
56
- "height": self.font_size,
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.hwnd = self.create_window()
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
- @classmethod
90
+ except Exception:
66
- def register_class(cls):
67
- if not cls.class_registered:
68
- wnd_class = win32gui.WNDCLASS()
69
- wnd_class.hInstance = win32api.GetModuleHandle(None)
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
- self.__class__.class_name,
94
+ def destroy_balloon():
95
+ try:
96
+ tk.global_balloon.destroy()
81
- "Balloon",
97
+ tk.global_balloon=None
82
- win32con.WS_POPUP,
83
- self.x,
84
- self.y,
85
- 300, 150, # 仮の初期サイズ
98
+ except Exception:
86
- 0,
87
- 0,
88
- win32api.GetModuleHandle(None),
89
- None
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
- if msg == win32con.WM_DESTROY:
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
- x=500,
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.update(message="Updated Content!", x=500, y=300)
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.update(message="Final Update!")
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

追記

2024/12/22 11:13

投稿

hiro12345
hiro12345

スコア1

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

コードの修正

2024/12/21 08:29

投稿

hiro12345
hiro12345

スコア1

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で載せました

2024/12/17 21:26

投稿

hiro12345
hiro12345

スコア1

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