1+ #!/usr/bin/python3
2+ import tkinter as tk
3+ import os
4+ import time
5+ from PIL import Image , ImageDraw , ImageFont , ImageTk
6+ import _thread as thread
7+ from threading import Thread ,Event
8+ path = os .path .dirname (__file__ )
9+ pathjoin = os .path .join
10+ toasts = [None ,None ,None ,None ,None ]
11+
12+ # 定义常量
13+ ADVANCEMENT = pathjoin (path , "assets" ,"mctoast" ,"textures" ,"advancement.png" )
14+ RECIPE = pathjoin (path , "assets" ,"mctoast" ,"textures" ,"recipe.png" )
15+ SYSTEM = pathjoin (path , "assets" ,"mctoast" ,"textures" ,"system.png" )
16+
17+ def generate_image (toast , image_path , text1 , color1 , text2 , color2 ):
18+ """生成Toast图片"""
19+ # 打开背景图片并缩放
20+ background_image = Image .open (toast ).resize ((320 , 64 ))
21+
22+ # 打开小图片并缩放
23+ if image_path :
24+ small_image = Image .open (image_path ).resize ((30 , 30 ))
25+ background_image .paste (small_image , (17 , 17 ))
26+
27+ # 创建一个绘图对象
28+ draw = ImageDraw .Draw (background_image )
29+
30+ # 加载字体
31+ font = ImageFont .truetype (pathjoin (path ,"assets" ,"mctoast" ,"fonts" ,"unifont.otf" ), 15 )
32+
33+ if toast == SYSTEM :
34+ if text1 and color1 :
35+ draw .text ((34 , 13 ), text1 , fill = color1 , font = font )
36+ if text2 and color2 :
37+ draw .text ((34 , 35 ), text2 , fill = color2 , font = font )
38+ else :
39+ # 在指定位置绘制文字
40+ if text1 and color1 :
41+ draw .text ((60 , 13 ), text1 , fill = color1 , font = font )
42+ if text2 and color2 :
43+ draw .text ((60 , 35 ), text2 , fill = color2 , font = font )
44+
45+ # 将 Pillow 图片转换为 PhotoImage
46+ return ImageTk .PhotoImage (background_image )
47+
48+ class ToastWindowUI :
49+ def __init__ (self , master = None , data_pool = None ):
50+ # build ui
51+ self .root = tk .Tk (master )
52+ self .root .configure (
53+ background = "#EEEEEE" ,
54+ borderwidth = 0 ,
55+ height = 200 ,
56+ takefocus = False ,
57+ width = 200 )
58+ self .root .geometry ("320x320+{}+0" .format (self .root .winfo_screenwidth ()- 320 ))
59+ self .root .withdraw ()
60+ self .root .overrideredirect ("true" )
61+ self .root .title ("MCToast" )
62+ self .root .attributes ("-topmost" , "true" )
63+ self .root .attributes ("-transparentcolor" , "#EEEEEE" )
64+ self .set_no_focus ()
65+ self .set_no_focus ()
66+ self .root .resizable (False , False )
67+ self .canvas = tk .Canvas (self .root , width = 320 , height = 320 , bg = "#EEEEEE" , highlightthickness = 0 )
68+ self .canvas .place (x = 0 ,y = 0 )
69+
70+ def set_no_focus (self ):
71+ """设置窗口为无焦点窗口并穿透鼠标点击"""
72+ import ctypes
73+ if os .name == 'nt' : # Windows
74+ GWL_EXSTYLE = - 20
75+ WS_EX_NOACTIVATE = 0x08000000
76+ WS_EX_TRANSPARENT = 0x00000020
77+ WS_EX_LAYERED = 0x00080000
78+ hwnd = ctypes .windll .user32 .GetParent (self .root .winfo_id ())
79+ style = ctypes .windll .user32 .GetWindowLongW (hwnd , GWL_EXSTYLE )
80+ style = style | WS_EX_NOACTIVATE | WS_EX_TRANSPARENT | WS_EX_LAYERED
81+ ctypes .windll .user32 .SetWindowLongW (hwnd , GWL_EXSTYLE , style )
82+ ctypes .windll .user32 .SetLayeredWindowAttributes (hwnd , 0 , 255 , 0x00000002 )
83+ elif os .name == 'posix' : # Linux
84+ self .root .wm_attributes ('-type' , 'splash' )
85+
86+ def main (self ):
87+ tk .mainloop ()
88+
89+ def new_toast (self , toast = ADVANCEMENT , image_path = None , text1 = "一个弹窗" , color1 = "yellow" , text2 = "MCToast示例" , color2 = "white" ,event = None ):
90+ """弹出Toast,但是阻塞"""
91+ global toasts
92+ while True :
93+ for i in range (5 ):
94+ if toasts [i ] == None :
95+ # 使用 Pillow 生成图片
96+ photo = generate_image (toast , image_path , text1 , color1 , text2 , color2 )
97+ self .root .deiconify ()
98+ toasts [i ] = self .canvas .create_image (320 , i * 64 , anchor = "nw" , image = photo )
99+ event .set ()
100+ x = 320
101+ speed = - 1.96
102+ while x > 0 :
103+ x += speed
104+ self .canvas .move (toasts [i ], speed , 0 )
105+ if speed < - 0.1 :
106+ speed += 0.006
107+ self .canvas .update ()
108+ time .sleep (0.0025 )
109+ self .canvas .move (toasts [i ], 0 , 0 )
110+ x = 0
111+ speed = 0.1
112+ time .sleep (2.5 )
113+ while x < 320 :
114+ x += speed
115+ self .canvas .move (toasts [i ], speed , 0 )
116+ if speed < 2 :
117+ speed += 0.006
118+ self .canvas .update ()
119+ time .sleep (0.0025 )
120+ self .canvas .delete (toasts [i ])
121+ toasts [i ] = None
122+ if toasts == [None ,None ,None ,None ,None ]:
123+ self .root .withdraw ()
124+ return
125+ time .sleep (0.2 )
126+
127+ def wait_no_toast (self ):
128+ """等待所有Toast消失"""
129+ while True :
130+ if all (toast is None for toast in toasts ):
131+ return
132+ time .sleep (0.2 )
133+
134+ def stop (self ):
135+ self .root .destroy ()
136+
137+ def set_no_focus (self ):
138+ """设置窗口为无焦点窗口"""
139+ import ctypes
140+ if os .name == 'nt' : # Windows
141+ GWL_EXSTYLE = - 20
142+ WS_EX_NOACTIVATE = 0x08000000
143+ hwnd = ctypes .windll .user32 .GetParent (self .root .winfo_id ())
144+ style = ctypes .windll .user32 .GetWindowLongW (hwnd , GWL_EXSTYLE )
145+ style = style | WS_EX_NOACTIVATE
146+ ctypes .windll .user32 .SetWindowLongW (hwnd , GWL_EXSTYLE , style )
147+ elif os .name == 'posix' : # Linux
148+ self .root .wm_attributes ('-type' , 'splash' )
149+
150+ window = None
151+ def _init ():
152+ global window
153+ window = ToastWindowUI ()
154+ window .main ()
155+
156+ def init ():
157+ thread .start_new_thread (_init ,())
158+ while type (window )!= ToastWindowUI :
159+ pass
160+
161+ def new_toast (toast = ADVANCEMENT , image_path = None , text1 = "一个弹窗" , color1 = "yellow" , text2 = "MCToast示例" , color2 = "white" ):
162+ global window
163+ #thread.start_new_thread(window.new_toast,(toast, image_path, text1, color1, text2, color2))
164+ #window.new_toast(toast, image_path, text1, color1, text2, color2)
165+ e = Event ()
166+ t = Thread (target = window .new_toast ,args = (toast , image_path , text1 , color1 , text2 , color2 , e ))
167+ t .start ()
168+ e .wait ()
169+
170+ def quit ():
171+ global window
172+ window .stop ()
173+
174+ def wait_no_toast ():
175+ global window
176+ window .wait_no_toast ()
177+
178+ if __name__ == "__main__" :
179+ # demo
180+ init ()
181+ for i in range (1 ,11 ):
182+ new_toast (toast = SYSTEM , text2 = f"第{ i } " )
183+ time .sleep (0.5 )
184+ wait_no_toast ()
0 commit comments