Python3 桌面右下角图标动态显示数据如何实现,适合新手入门!

我想实现动态读取串口数据,当收到数据时,已经在桌面右下角建立的图标闪烁显示有数据开始处理,无数据时显示等待图标,我弄了下,只能手动点击触发,对这个类有点不太明白如何实现收到数据自动切换图标,请论坛高手给指点一下,在此谢过!

代码如下


#encoding:utf-8
import os
import sys
import time
import win32api
import win32con
import win32gui_struct
import threading
try:
    import winxpgui as win32gui
except ImportError:
    import win32gui

mainfile = os.path.dirname(sys.argv[0])

class SysTrayIcon(object): QUIT = ‘QUIT’ SPECIAL_ACTIONS = [QUIT]

FIRST_ID = 1023

def __init__(self,
             icon,
             hover_text,
             menu_options,
             on_quit=None,
             default_menu_index=None,
             window_class_name=None,):

    self.icon = icon
    self.hover_text = hover_text
    self.on_quit = on_quit

    menu_options = menu_options + (('退出', None, self.QUIT),)
    self._next_action_id = self.FIRST_ID
    self.menu_actions_by_id = set()
    self.menu_options = self._add_ids_to_menu_options(list(menu_options))
    self.menu_actions_by_id = dict(self.menu_actions_by_id)
    del self._next_action_id


    self.default_menu_index = (default_menu_index or 0)
    self.window_class_name = window_class_name or "SysTrayIconPy"

    message_map = {win32gui.RegisterWindowMessage("TaskbarCreated"): self.restart,
                   win32con.WM_DESTROY: self.destroy,
                   win32con.WM_COMMAND: self.command,
                   win32con.WM_USER+20 : self.notify,}
    # Register the Window class.
    window_class = win32gui.WNDCLASS()
    hinst = window_class.hInstance = win32gui.GetModuleHandle(None)
    window_class.lpszClassName = self.window_class_name
    window_class.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW;
    window_class.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
    window_class.hbrBackground = win32con.COLOR_WINDOW
    window_class.lpfnWndProc = message_map # could also specify a wndproc.
    classAtom = win32gui.RegisterClass(window_class)
    # Create the Window.
    style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
    self.hwnd = win32gui.CreateWindow(classAtom,
                                      self.window_class_name,
                                      style,
                                      0,
                                      0,
                                      win32con.CW_USEDEFAULT,
                                      win32con.CW_USEDEFAULT,
                                      0,
                                      0,
                                      hinst,
                                      None)
    win32gui.UpdateWindow(self.hwnd)
    self.notify_id = None
    self.refresh_icon()

    win32gui.PumpMessages()

def _add_ids_to_menu_options(self, menu_options):
    result = []
    for menu_option in menu_options:
        option_text, option_icon, option_action = menu_option
        if callable(option_action) or option_action in self.SPECIAL_ACTIONS:
            self.menu_actions_by_id.add((self._next_action_id, option_action))
            result.append(menu_option + (self._next_action_id,))
        elif non_string_iterable(option_action):
            result.append((option_text,
                           option_icon,
                           self._add_ids_to_menu_options(option_action),
                           self._next_action_id))
        else:
            pass
            #print ('Unknown item', option_text, option_icon, option_action)
        self._next_action_id += 1
    return result

def refresh_icon(self):
    # Try and find a custom icon
    hinst = win32gui.GetModuleHandle(None)
    if os.path.isfile(self.icon):
        icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
        hicon = win32gui.LoadImage(hinst,
                                   self.icon,
                                   win32con.IMAGE_ICON,
                                   0,
                                   0,
                                   icon_flags)
    else:
        #print ("Can't find icon file - using default.")
        hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)

    if self.notify_id: message = win32gui.NIM_MODIFY
    else: message = win32gui.NIM_ADD
    self.notify_id = (self.hwnd,
                      0,
                      win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
                      win32con.WM_USER+20,
                      hicon,
                      self.hover_text)
    win32gui.Shell_NotifyIcon(message, self.notify_id)

def restart(self, hwnd, msg, wparam, lparam):
    self.refresh_icon()

def destroy(self, hwnd, msg, wparam, lparam):
    if self.on_quit: self.on_quit(self)
    nid = (self.hwnd, 0)
    win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
    win32gui.PostQuitMessage(0) # Terminate the app.

def notify(self, hwnd, msg, wparam, lparam):
    if lparam==win32con.WM_LBUTTONDBLCLK:
        self.execute_menu_option(self.default_menu_index + self.FIRST_ID)
    elif lparam==win32con.WM_RBUTTONUP:
        self.show_menu()
    elif lparam==win32con.WM_LBUTTONUP:
        pass
    return True

def show_menu(self):
    menu = win32gui.CreatePopupMenu()
    self.create_menu(menu, self.menu_options)
    #win32gui.SetMenuDefaultItem(menu, 1000, 0)

    pos = win32gui.GetCursorPos()
    # See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/menus_0hdi.asp
    win32gui.SetForegroundWindow(self.hwnd)
    win32gui.TrackPopupMenu(menu,
                            win32con.TPM_LEFTALIGN,
                            pos[0],
                            pos[1],
                            0,
                            self.hwnd,
                            None)
    win32gui.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)

def create_menu(self, menu, menu_options):
    for option_text, option_icon, option_action, option_id in menu_options[::-1]:
        if option_icon:
            option_icon = self.prep_menu_icon(option_icon)

        if option_id in self.menu_actions_by_id:
            item, extras = win32gui_struct.PackMENUITEMINFO(text=option_text,
                                                            hbmpItem=option_icon,
                                                            wID=option_id)
            win32gui.InsertMenuItem(menu, 0, 1, item)
        else:
            submenu = win32gui.CreatePopupMenu()
            self.create_menu(submenu, option_action)
            item, extras = win32gui_struct.PackMENUITEMINFO(text=option_text,
                                                            hbmpItem=option_icon,
                                                            hSubMenu=submenu)
            win32gui.InsertMenuItem(menu, 0, 1, item)

def prep_menu_icon(self, icon):
    # First load the icon.
    ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON)
    ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON)
    hicon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, ico_x, ico_y, win32con.LR_LOADFROMFILE)

    hdcBitmap = win32gui.CreateCompatibleDC(0)
    hdcScreen = win32gui.GetDC(0)
    hbm = win32gui.CreateCompatibleBitmap(hdcScreen, ico_x, ico_y)
    hbmOld = win32gui.SelectObject(hdcBitmap, hbm)
    # Fill the background.
    brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU)
    win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush)
    # unclear if brush needs to be feed.  Best clue I can find is:
    # "GetSysColorBrush returns a cached brush instead of allocating a new
    # one." - implies no DeleteObject
    # draw the icon
    win32gui.DrawIconEx(hdcBitmap, 0, 0, hicon, ico_x, ico_y, 0, 0, win32con.DI_NORMAL)
    win32gui.SelectObject(hdcBitmap, hbmOld)
    win32gui.DeleteDC(hdcBitmap)

    return hbm

def command(self, hwnd, msg, wparam, lparam):
    id = win32gui.LOWORD(wparam)
    self.execute_menu_option(id)

def execute_menu_option(self, id):
    menu_action = self.menu_actions_by_id[id]
    if menu_action == self.QUIT:
        win32gui.DestroyWindow(self.hwnd)
    else:
        menu_action(self)

def non_string_iterable(obj): try: iter(obj) except TypeError: return False else: #return not isinstance(obj, basestring)#python2.7 user return not isinstance(obj, (str)) import itertools, glob

icons = itertools.cycle(glob.glob(’.ico’)) hover_text = “com1 9600 n 8 1 \n load:902” print (glob.glob(’.ico’)) def switch_icon(sysTrayIcon): sysTrayIcon.icon = icons.next() sysTrayIcon.refresh_icon() def switch_icon2(sysTrayIcon): for i in ‘1,2,3,4,5,6,7,8’: sysTrayIcon.icon = icons.next() sysTrayIcon.refresh_icon() time.sleep(0.2) def stop_dec(sysTrayIcon): sysTrayIcon.icon = os.path.join(mainfile,‘ico’,‘stop.ico’) #print (os.path.join(mainfile,‘ico’,‘stop.ico’)) sysTrayIcon.refresh_icon() #time.sleep(0.2) #os.mknod(“stop”) f = open(os.path.join(mainfile,‘stop’),‘w’) f.close()

def opendecini(sysTrayIcon): if os.path.isfile(os.path.join(mainfile,‘dec2017.INI’)): os.startfile(os.path.join(mainfile,‘dec2017.INI’)) def hello(sysTrayIcon): pass#print (“Hello World.”) def simon(sysTrayIcon): pass#print (“Hello Simon.”) def rehandle(sysTrayIcon): pass#print (“rehandling…”)

menu_options = ((‘switch_icon2’,icons.next(), switch_icon2), (‘重新接收数据’,icons.next(), del_raw), (‘修改配置’, None, opendecini), (‘other’, None, ((‘stop’, None, hello), (‘star’, None, simon), (‘原始数据’, None, opendeclog), (‘结果数据’, None, opendeclog), (‘生成文件’, None, switch_icon), )) )

def bye(sysTrayIcon): sys.exit(0) print (sys.argv[0])

on_quit=bye default_menu_index=1 t1=threading.Thread(target=SysTrayIcon,args=(icons.next(), hover_text, menu_options,on_quit, default_menu_index))

t1.start() str = input("输入任意字符 exit: ") print (“收到字符:”, str)


Python3 桌面右下角图标动态显示数据如何实现,适合新手入门!

7 回复

python 这个排版。。。


用Python在桌面右下角(系统托盘)动态显示数据,对新手来说,pystray 库是个不错的选择。它跨平台(Windows、macOS、Linux),上手简单。

下面是一个完整的示例,它会创建一个托盘图标,并每隔2秒更新一次图标上显示的当前时间。

首先,安装库:

pip install pystray Pillow

然后是代码:

import pystray
from PIL import Image, ImageDraw, ImageFont
import threading
import time
from datetime import datetime

# 1. 创建一张空图片作为图标画布
def create_image(text):
    # 创建一个 64x64 的空白图像,背景透明
    image = Image.new('RGBA', (64, 64), (255, 255, 255, 0))
    draw = ImageDraw.Draw(image)
    
    # 尝试加载字体,如果失败则使用默认字体
    try:
        font = ImageFont.truetype("arial.ttf", 20)
    except:
        font = ImageFont.load_default()
    
    # 获取文本尺寸并居中绘制
    bbox = draw.textbbox((0, 0), text, font=font)
    text_width = bbox[2] - bbox[0]
    text_height = bbox[3] - bbox[1]
    position = ((64 - text_width) // 2, (64 - text_height) // 2)
    
    # 绘制白色文字和黑色边框(增强可读性)
    draw.text(position, text, font=font, fill=(255, 255, 255, 255))
    draw.text(position, text, font=font, fill=(0, 0, 0, 255), stroke_width=1)
    
    return image

# 2. 动态更新图标的函数
def update_icon(icon):
    while True:
        # 获取当前时间并格式化为字符串
        current_time = datetime.now().strftime("%H:%M")
        
        # 创建新图标
        new_image = create_image(current_time)
        
        # 更新托盘图标
        icon.icon = new_image
        
        # 每隔2秒更新一次
        time.sleep(2)

# 3. 主程序
def main():
    # 创建初始图标(显示"启动")
    initial_image = create_image("启动")
    
    # 创建托盘图标
    icon = pystray.Icon("data_display", initial_image, "数据监控")
    
    # 添加一个简单的退出菜单项
    icon.menu = pystray.Menu(
        pystray.MenuItem("退出", lambda: icon.stop())
    )
    
    # 启动更新线程
    update_thread = threading.Thread(target=update_icon, args=(icon,), daemon=True)
    update_thread.start()
    
    # 运行托盘图标
    icon.run()

if __name__ == "__main__":
    main()

代码说明:

  1. create_image() 函数:用PIL库动态生成带文字的图标图片。你可以修改这个函数来显示任何数据(比如CPU使用率、温度等)。
  2. update_icon() 函数:在一个独立线程中循环运行,定期更新图标内容。这里演示的是显示当前时间。
  3. 主程序:初始化图标,启动更新线程,然后进入系统托盘的事件循环。

运行效果: 程序启动后,会在系统托盘区看到一个图标,上面显示的时间会每2秒更新一次。右键图标可以选择“退出”。

改成显示你的数据: 只需修改 update_icon() 函数中的 current_time = ... 这一行,替换成你自己的数据获取逻辑即可。比如用 psutil 库获取CPU使用率:current_data = f"CPU:{psutil.cpu_percent()}%"

总结:核心就是开个线程定期更新图标图片。

我特意百度在线预览后发的,排班没法复制到到编辑器里面,哟试试,看效果还可以哇

这………排版……………

手机端排版比较乱

我用三星手机谷歌手机浏览器打开,格式对的呀,电脑也显示对的,我在代码前面和后面加代码标记,发图咋发

代码参考的这个写的
https://segmentfault.com/q/1010000008579491/a-1020000008579881
上面网址代码 python2.7 可以运行

回到顶部