実現したいこと
※(1/11追記)実用できる範囲まで改修できたので解決とさせていただきます、ありがとうございました!
https://94.gigafile.nu/0325-c7f527087ad73dda5963f92a783e34fbb
上記ファイルのrun.batを押して動くプログラムをPythonで再現したいと思っています
一例として、マウス座標をリアルタイムで表示するプログラムですが、他にも移植したいものがあり、だいたいのコードは再現できるもののある機能がPythonに用意しておらず、1から作成する必要があります
上記プログラムはexeファイルで、rustで作られたインタプリタ言語、「UWSCR」の実行ファイルとUWSCRの実行用ソースコードです
UWSCRの組み込み関数「balloon」をPythonで再現することが第一の目的となります
基本的にプログラムの書き方はこのUWSCRと同じ書き方で再現させる方針です、
※UWSCR公式github
https://github.com/stuncloud/UWSCR
発生している問題・分からないこと
マルチスレッドを使用するため、なるべくスレッドセーフなコードにする必要がありましたが、UWSCRと同じ書き方という点は妥協できないのでその上でのコードの改修
該当のソースコード ※2025/1/8更新
Python
1import tkinter as tk 2import threading 3import queue 4 5global_balloon = None 6 7class balloon_class: 8 9 thread_creation_count = 0 10 root_creation_count = 0 11 12 def __init__(self, queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border): 13 # スレッドを2重生成しないためのフラグ 14 type(self).thread_creation_count += 1 15 if 1 >= type(self).thread_creation_count: 16 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)) 17 self.thread.start() 18 else: 19 print("Error: スレッド生成メソッドが2回以上参照されています") 20 21 def create_balloon(self, queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border): 22 23 # rootを2重生成しないためのフラグ 24 type(self).root_creation_count += 1 25 if 1 >= type(self).root_creation_count: 26 self.root = tk.Tk() 27 self.root.withdraw() 28 self.root.wm_overrideredirect(True) 29 self.sub_window = tk.Toplevel(self.root) 30 self.sub_window.deiconify() 31 32 self.previous_should_hide_balloon = False 33 34 if show_border: 35 border = 'solid' 36 else: 37 border = 'flat' 38 39 #サイズ計算用のラベル 40 self.dummy_label = tk.Label(self.sub_window, text=message, font=(font_name, font_size)) 41 # ラベルサイズを計算 42 width = self.dummy_label.winfo_reqwidth() 43 height = self.dummy_label.winfo_reqheight() 44 45 self.sub_window.attributes("-topmost", True) 46 # 全体を半透明にする(ウィンドウ全体が透明度を持つ) 47 self.sub_window.wm_attributes("-alpha", transparency) 48 self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}") 49 self.sub_window.configure(bg=back_color) 50 self.frame = tk.Frame(self.sub_window, bg=back_color, bd=1, relief=border) 51 self.frame.place(x=0, y=0, width=width+20, height=height+20) 52 self.label = tk.Label(self.frame, text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left") 53 self.label.place(x=8, y=10, width=width, height=height) 54 self.sub_window.wm_overrideredirect(True) 55 self.update_balloon(queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border) 56 self.root.mainloop() 57 else: 58 print("Error: 初期ウィンドウ生成メソッドが2回以上参照されています") 59 60 61 62 def update_balloon(self, queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border): 63 64 # キューから変数を取得 65 try: 66 balloon_args, should_hide_balloon = queue.get_nowait() 67 except Exception: 68 balloon_args = None 69 should_hide_balloon = False 70 71 # 比較用に使う前回使用の引数とqueueで読み取った表示更新用の引数のリスト 72 previous_args = [message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border] 73 new_args = balloon_args 74 75 previous_should_hide_balloon = self.previous_should_hide_balloon 76 new_should_hide_balloon = should_hide_balloon 77 78 # ウィンドウを消しても引数が前と同じ値だとGUI表示を更新しない仕組みなので、ウィンドウ再生成用のフラグを作成、要再生成なら再生成と更新処理を実行 79 try: 80 should_redefine_sub_window = not self.sub_window.winfo_exists() #sub_windowが存在しない場合True 81 except Exception: 82 should_redefine_sub_window = False 83 84 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): #値が更新された場合かウィンドウを消した場合に更新処理を実行 85 message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border = new_args 86 self.previous_should_hide_balloon = new_should_hide_balloon #次回表示更新時の値比較用に代入 87 try: 88 if new_should_hide_balloon: 89 self.sub_window.withdraw() 90 else: 91 self.sub_window.deiconify() 92 except Exception: 93 # ウィンドウをAlt+F4で消した場合も再生成 94 self.sub_window = tk.Toplevel(self.root) 95 self.sub_window.wm_overrideredirect(True) 96 self.sub_window.deiconify() 97 self.dummy_label = tk.Label(self.sub_window) 98 self.frame = tk.Frame(self.sub_window) 99 self.label = tk.Label(self.frame) 100 101 102 if show_border: 103 border = 'solid' 104 else: 105 border = 'flat' 106 107 self.dummy_label.config(text=message, font=(font_name, font_size)) 108 width = self.dummy_label.winfo_reqwidth() 109 height = self.dummy_label.winfo_reqheight() 110 111 self.sub_window.wm_attributes("-alpha", transparency) 112 self.sub_window.geometry(f"{width+20}x{height+20}+{x}+{y}") 113 self.sub_window.configure(bg=back_color) 114 self.frame.config(bg=back_color, bd=1, relief=border) 115 self.frame.place(x=0, y=0, width=width+20, height=height+20) 116 self.label.config(text=message, fg=fore_color, bg=back_color, font=(font_name, font_size), justify="left") 117 self.label.place(x=8, y=10, width=width, height=height) 118 119 self.root.after(30, self.update_balloon, queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border) 120 121 122 123def balloon(message=" ", x=0, y=0, font_name="Arial", font_size=14, fore_color="#000000", back_color="#FFFF00", transparency=1, show_border=True): 124 global global_balloon, balloon_queue 125 126 if not isinstance(global_balloon, balloon_class): 127 balloon_queue = queue.Queue() 128 global_balloon=balloon_class(balloon_queue, message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border) 129 elif isinstance(global_balloon, balloon_class): 130 try: 131 if balloon_queue.empty(): 132 balloon_args = [message, x, y, font_name, font_size, fore_color, back_color, transparency, show_border] 133 should_hide_balloon = False 134 balloon_queue.put((balloon_args, should_hide_balloon)) 135 except Exception: 136 pass 137 138 139def hide_balloon(): 140 try: 141 if balloon_queue.empty(): 142 dummy_args = [" ", 0, 0, "Arial", 14, "#000000", "#FFFF00", 1, True] 143 should_hide_balloon = True 144 balloon_queue.put((dummy_args, should_hide_balloon)) 145 except Exception: 146 pass 147 148 149 150if __name__ == "__main__": 151 import time 152 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) 153 154 time.sleep(3) 155 hide_balloon() 156 time.sleep(1) 157 hide_balloon() 158 time.sleep(1) 159 balloon(message="Updated Content!", x=300, y=500, font_size=14, back_color="#00FFFF", transparency=0.7, show_border=False) 160 time.sleep(3) 161 balloon(message="Final Update!", x=300, y=500, font_size=14, back_color="#00FFFF", transparency=0.7, show_border=True) 162 time.sleep(3)
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
当初はwin32apiで実現しようと思いましたが難しいことがわかりTkinterでの実装中です
補足
WIn11 Python 3.10.4で実行しています
追記
rust製のインタプリタ言語「UWSCR」での挙動
UWSCRソースコード
UWSCR
1FUNCTION GETSTATE() 2 x=G_MOUSE_X 3 y=G_MOUSE_Y 4 s="マウス座標:" + x + "," + y 5 result=s 6FEND 7 8WHILE True 9 s = GETSTATE() 10 balloon(s, 10, 10) 11 Sleep(0.01) 12WEND
※マルチプロセス版も作成しました(下記リンク)、実行側のソースコードはマルチスレッド版用そのままで使えますが、マルチプロセスを使う仕様上処理をif name == "main":またはif name != "mp_main":のブロック中に記載しないとエラーが出る点に注意してください
https://incandescent-belekoy-906db2.netlify.app/balloon_multiprocessing_v20250108.txt
・実行側pyファイルソースコード
※balloon.pyのソースコードは省略
Python
1from balloon import * 2import time 3import pyautogui 4 5def getstate(): 6 x,y =pyautogui.position() 7 s=f"マウス座標:{x},{y}" 8 return s 9 10def func(): 11 while True: 12 s=getstate() 13 balloon(s,10,10) 14 time.sleep(0.01) 15 16if __name__ != "__mp_main__": 17 func()
回答2件
あなたの回答
tips
プレビュー