惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
小众软件
小众软件
博客园 - 叶小钗
宝玉的分享
宝玉的分享
博客园_首页
Hugging Face - Blog
Hugging Face - Blog
人人都是产品经理
人人都是产品经理
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
SegmentFault 最新的问题
B
Blog RSS Feed
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
Google DeepMind News
Google DeepMind News
U
Unit 42
F
Fortinet All Blogs
IT之家
IT之家
Y
Y Combinator Blog
Martin Fowler
Martin Fowler
T
The Blog of Author Tim Ferriss
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The GitHub Blog
The GitHub Blog
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
酷 壳 – CoolShell
酷 壳 – CoolShell

博客园 - jack_Meng

2026年AI编程工具大全,33个主流工具 .NET 异常处理的"暗门":代码里被 catch 吃掉的异常,你依然能抓住它——FirstChanceException Visual Studio 插件 Gittoy , 我做了一个 vs 版的 gittoolbox 用 C 语言实现任务栏图标动画,支持使用表情包gif/webp 如何用0预算推广项目 开源的 Windows 桌面整理工具 被罚了500后,整个人都变老实了 提取网页表格,一键导出 CSV:一个 Bookmarklet 脚本就够了 从零搭建一个最小 AI Agent:Python 完整示例 + AI调用机制详解 CASIO卡西欧计算器的自检模式——2种自检方式 C#把字符串按每两个字符一组,组之间加空格 C#程序从指定路径或网络加载引用的DLL 设置程序PrivatePath,配置引用程序集的指定路径(分离exe和dll) .NET/WPF 程序密钥加密存储:使用 DPAPI 实现安全兼容 一线大厂的Git规范 一线大厂的数据库规范 Python自动化实战:常用的类库和使用场景 工控生态之展示与交互:怎么让工业软件不再"丑" 一个 Python 项目带你入门AI应用开发课程介绍----系列文章 开源好用的酷狗音乐播放器:VibeMusic PowerShell + C# 实现桌面文字提醒,类似于“激活Windows”的功能 【Python】2026动态文字壁纸,一键让你的桌面加上动态效果 用Python在Windows桌面弹出文字提示 在Windows桌面背景上添加自定义文字 常用AI提示词汇总 chrome浏览器,Google新标签页添加快捷图标 油候脚本中,使用GM_info对象,打印脚本信息 使用油候脚本,移除页面无法选择文字 高中免费电子教辅资料合集 Windows防火墙关闭445端口:Windows在防火墙禁用echo-reply(type 0)、time-exceeded(type 11)、destination-unreachable(type 3)类型的ICMP包
Python 用 tkinter 实现在 Windows 上提示文本消息的实现(模...
jack_Meng · 2026-07-28 · via 博客园 - jack_Meng

用 tkinter 实现,在桌面上即时显示文字消息(置顶显示、可拖动),经过 duration 毫秒后消失。

效果演示:

image

代码实现:


import threading
import time
import tkinter as tk
from queue import Queue
import ctypes

ctypes.windll.shcore.SetProcessDpiAwareness(1)

class ToastWindow:
    def __init__(self, parent_root, text, font_size, bg, duration):
        """创建浮动消息窗口"""
        self.parent_root = parent_root
        self.window = tk.Toplevel(parent_root)
        self.window.hang_on = False
        
        # 设置窗口属性
        self.window.overrideredirect(True)
        self.window.attributes('-topmost', True)
        self.window.configure(bg=bg)

        # 绑定可拖动
        self.window.bind('<ButtonPress-1>', self._on_drag_start)
        self.window.bind('<ButtonRelease-1>', self._on_drag_stop)
        self.window.bind('<B1-Motion>', self._on_drag_motion)
        self.window.bind('<Escape>', self._destroy_window)
        
        # 创建文字标签
        label = tk.Label(
            self.window,
            text=text,
            font=('Microsoft YaHei', font_size),
            fg='white',
            bg=bg,
            justify=tk.LEFT,
            wraplength=1200
        )
        label.pack(padx=20, pady=15)
        
        # 更新窗口以确保获取正确的尺寸
        self.window.update_idletasks()
        
        # 设置窗口位置
        self._set_window_position()
        
        # 显示窗口
        self.window.deiconify()
        
        # 设置定时器,在指定时间后销毁窗口
        self.pause = False
        self.window.after(duration, self._destroy_window)
    
    def _set_window_position(self):
        """设置窗口位置"""
        try:
            screen_width = self.window.winfo_screenwidth()
            screen_height = self.window.winfo_screenheight()
            
            window_width = self.window.winfo_width()
            window_height = self.window.winfo_height()
            
            x = (screen_width - window_width) // 2
            y = screen_height // 10 * 8
            
            self.window.geometry(f'+{x}+{y}')
        except:
            pass
    
    def _on_drag_start(self, event):
        self.pause = True
        self.x = event.x
        self.y = event.y

    def _on_drag_stop(self, event):
        self.pause = False

    def _on_drag_motion(self, event):
        deltax = event.x - self.x
        deltay = event.y - self.y
        x = self.window.winfo_x() + deltax
        y = self.window.winfo_y() + deltay
        self.window.geometry(f"+{x}+{y}")
    
    def _destroy_window(self, event=None):
        """销毁窗口"""
        try:
            if self.pause:
                # 如果窗口被暂停(拖动),延迟销毁
                self.window.after(100, self._destroy_window)
            else:
                self.window.destroy()  # 销毁窗口
        except:
            pass


class ToastMessageManager:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        with cls._lock:
            if cls._instance is None:
                cls._instance = super().__new__(cls)
                cls._instance.initialized = False
            return cls._instance
    
    def __init__(self):
        if not self.initialized:
            self.message_queue = Queue()
            self.is_running = False
            self.initialized = True
            self.active_windows = []  # 跟踪活动窗口
            
            # 在子线程中启动 Tkinter
            self.manager_thread = threading.Thread(target=self._run_manager)
            self.manager_thread.daemon = True
            self.manager_thread.start()
    
    def _run_manager(self):
        """在子线程中运行 Tkinter 主循环"""
        # 创建隐藏的主窗口
        self.root = tk.Tk()
        self.root.withdraw()  # 隐藏主窗口
        self.root.tk.call('tk', 'scaling', 2)
        
        # 设置窗口关闭时的行为
        self.root.protocol("WM_DELETE_WINDOW", self._on_close)
        
        # 开始处理队列
        self.is_running = True
        self._process_queue()
        
        # 启动 Tkinter 主循环
        self.root.mainloop()
    
    def _on_close(self):
        """关闭所有窗口并退出"""
        self.is_running = False
        for window in self.active_windows[:]:
            try:
                window.window.destroy()
            except:
                pass
        self.active_windows.clear()
        self.root.quit()
    
    def _process_queue(self):
        """处理队列中的消息"""
        try:
            # 检查是否有新消息
            if not self.message_queue.empty():
                text, font_size, bg, duration = self.message_queue.get_nowait()
                
                # 创建新窗口
                toast_window = ToastWindow(self.root, text, font_size, bg, duration)
                self.active_windows.append(toast_window)
                
                # 设置窗口销毁时的回调
                toast_window.window.bind('<Destroy>', 
                    lambda e, w=toast_window: self._remove_window(w))
            
            # 清理已销毁的窗口
            self.active_windows = [w for w in self.active_windows 
                                 if w.window.winfo_exists()]
            
        except Exception as e:
            # 忽略队列空异常等
            pass
        
        # 继续处理队列
        if self.is_running:
            self.root.after(100, self._process_queue)
    
    def _remove_window(self, window):
        """从活动窗口列表中移除窗口"""
        if window in self.active_windows:
            self.active_windows.remove(window)
    
    def add_message(self, text, font_size, bg, duration):
        """添加消息到队列"""
        self.message_queue.put((text, font_size, bg, duration))


def toast(text, font_size=14, bg="#C41529", duration=2000):
    """显示浮动消息的便捷函数"""
    manager = ToastMessageManager()
    manager.add_message(text, font_size, bg, duration)


# 使用示例
if __name__ == "__main__":
    message_text = """21:14:13 【国际航协:中国四大航空公司加入航班数据交互项目】 财联社11月14日电,从国际航协获悉,中国东方航空公司宣布加入国际航协航班计划数据交互项目(SDEP)。至此,该计划已涵盖中国四大航空公司——中国国际航空公司、中国东方航空公司、中国南方航空公司和海南航空公司,标志着该计划在中国市场的推进迈出了重要一步。随着中国四大航空公司加入航班计划数据交互项目,该项目目前涵盖了中国民航75%以上的运力。 (证券时报)"""
    
    # 测试多个消息
    toast(message_text, bg="#075077", duration=3000)
    time.sleep(4)
    toast(message_text, bg="#C41529", duration=2000)
    time.sleep(4)
    toast(message_text, bg="#008000", duration=1000)
    
    # 保持主线程运行
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print("程序退出")

=======================================================================================