返回
import tkinter as tk
import random
import threading
import time
class StandaloneToast:
def __init__(self, message, toast_type="style_1",
position=None, duration=4000):
self.message = message
self.toast_type = toast_type
self.position = position
self.duration = duration
# 在新线程中启动 GUI
self.thread = threading.Thread(target=self.create_toast, daemon=True)
self.thread.start()
def create_toast(self):
try:
# 创建根窗口但不显示主窗口
self.root = tk.Tk()
self.root.withdraw() # 隐藏主窗口
# 创建 Toast 窗口
self.toast = tk.Toplevel(self.root)
self.setup_toast_window()
# 这一行控制的是弹窗自动关闭
# self.root.after(self.duration, self.fade_out)
self.root.mainloop()
except Exception as e:
print(f"Toast创建错误: {e}")
finally:
# 确保资源被清理
if hasattr(self, 'root'):
try:
self.root.quit()
self.root.destroy()
except:
pass
def setup_toast_window(self):
self.toast.overrideredirect(True) # 无标题栏
self.toast.attributes('-alpha', 0.0) # 初始透明
self.toast.attributes('-topmost', True)
# 样式配置
styles = {
"style_1": {"bg": "#d4edda", "fg": "#155724", "icon": "@"},
"style_2": {"bg": "#f8d7da", "fg": "#721c24", "icon": "#"},
"style_3": {"bg": "#fff3cd", "fg": "#856404", "icon": "$"},
"style_4": {"bg": "#d1ecf1", "fg": "#0c5460", "icon": "%"}
}
style = styles.get(self.toast_type, styles["style_1"])
# 创建内容框架
frame = tk.Frame(
self.toast,
bg=style["bg"],
relief='solid',
borderwidth=1
)
frame.pack(padx=1, pady=1)
# 图标
icon_label = tk.Label(
frame,
text=style["icon"],
bg=style["bg"],
fg=style["fg"],
font=("Arial", 12, "bold")
)
icon_label.pack(side=tk.LEFT, padx=(10, 5), pady=10)
# 消息
message_label = tk.Label(
frame,
text=self.message,
bg=style["bg"],
fg=style["fg"],
font=("微软雅黑", 9)
)
message_label.pack(side=tk.LEFT, padx=(0, 15), pady=10)
# 设置位置和大小
self.set_position()
# 淡入动画
self.fade_in()
def set_position(self):
self.toast.update_idletasks()
if self.position:
x, y = self.position
else:
screen_width = self.toast.winfo_screenwidth()
screen_height = self.toast.winfo_screenheight()
x = random.randint(100, screen_width - 200)
y = random.randint(100, screen_height - 100)
self.toast.geometry(f"+{x}+{y}")
def fade_in(self):
# 淡入动画
alpha = 0.0
def increase_alpha():
nonlocal alpha
alpha += 0.1
self.toast.attributes('-alpha', alpha)
if alpha < 0.95:
self.toast.after(40, increase_alpha)
increase_alpha()
def fade_out(self):
# 淡出动画并关闭
alpha = 0.95
def decrease_alpha():
nonlocal alpha
alpha -= 0.1
self.toast.attributes('-alpha', alpha)
if alpha > 0:
self.toast.after(40, decrease_alpha)
else:
self.toast.destroy()
self.root.quit()
self.root.destroy()
decrease_alpha()
def wait_for_completion(self):
if hasattr(self, 'thread'):
self.thread.join()
class ToastManager:
def __init__(self):
self.active_toasts = []
def show_toast(self, message, toast_type="style_1", position=None, duration=4000):
toast = StandaloneToast(message, toast_type, position, duration)
self.active_toasts.append(toast)
return toast
def show_multiple_toasts(self, toast_configs, delay=0):
def show_sequential():
for config in toast_configs:
self.show_toast(**config)
time.sleep(delay)
threading.Thread(target=show_sequential, daemon=True).start()
def show_toasts_with_interval(thread_name, count=40):
style_list = ["style_1", "style_2", "style_3", "style_4"]
messages = ["考试通过", "暴富", "天天开心", "梦想成真", "工作顺利", "岁岁平安"]
for i in range(count):
try:
StandaloneToast(
f"{random.choice(messages)}",
# f"{thread_name} - {random.choice(messages)} ({i + 1}/{count})",
random.choice(style_list),
duration=4000 # 4秒后关闭
)
# 固定间隔0.5秒
time.sleep(0.4)
except Exception as e:
print(f"线程 {thread_name} 第 {i + 1} 次执行出错: {e}")
def main_multi_thread():
thread_names = ["线程1", "线程2", "线程3"]
threads = []
for thread_name in thread_names:
thread = threading.Thread(
target=show_toasts_with_interval,
args=(thread_name, 40), # 每个线程显示20个弹窗
daemon=True
)
threads.append(thread)
thread.start()
print(f"启动 {thread_name}")
time.sleep(0.2) # 稍微错开线程启动时间
# 计算预计完成时间用于缓冲
total_time = 40 * 0.4 + 4
# 等待所有线程完成
for i, thread in enumerate(threads):
thread.join(timeout=total_time + 3) # 额外缓冲时间
if __name__ == "__main__":
main_multi_thread()