python 使用 tkinter 实现最小化到托盘功能。但最小化到托盘后程序不能循环执行。

LiQianJun 2019-06-25 02:31:59
程序有2个进程
其中子进程调用函数 count

count为无限循环 time.sleep 来控制每次循环事件间隔,每次10秒。

遇到的问题是:
程序不进入托盘一切正常。每隔10秒执行一次。
一但程序最小化进入托盘后。count里的无限循环"被"暂停(奇怪的是到了第一次最小化到托盘。到了时间执行一次输出代码后才暂停)

我想实现。 最小化到托盘后 程序依然正常执行。而不出现 count里的代码被暂停。


全部代码如下:

import os
import win32api
import win32con
import win32gui_struct
import win32gui
from tkinter import *
import json
import requests
import time
import win32com.client
import pypyodbc
import threading

__author__ = 'mayi'

def print_ts(message):
return "[%s] %s" %(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), message)

Main = None

class SysTrayIcon(object):
QUIT = 'QUIT'
SPECIAL_ACTIONS = [QUIT]
FIRST_ID = 1314
def __init__(s,
icon,
hover_text,
menu_options,
on_quit=None,
default_menu_index=None,
window_class_name=None,):
s.icon = icon
s.hover_text = hover_text
s.on_quit = on_quit

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

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

message_map = {win32gui.RegisterWindowMessage("TaskbarCreated"): s.refresh_icon,
win32con.WM_DESTROY: s.destroy,
win32con.WM_COMMAND: s.command,
win32con.WM_USER+20 : s.notify,}
# 注册窗口类。
window_class = win32gui.WNDCLASS()
window_class.hInstance = win32gui.GetModuleHandle(None)
window_class.lpszClassName = s.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 #也可以指定wndproc.
s.classAtom = win32gui.RegisterClass(window_class)

def show_icon(s):
# 创建窗口。
hinst = win32gui.GetModuleHandle(None)
style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
s.hwnd = win32gui.CreateWindow(s.classAtom,
s.window_class_name,
style,
0,
0,
win32con.CW_USEDEFAULT,
win32con.CW_USEDEFAULT,
0,
0,
hinst,
None)
win32gui.UpdateWindow(s.hwnd)
s.notify_id = None
s.refresh_icon()

win32gui.PumpMessages()

def show_menu(s):
menu = win32gui.CreatePopupMenu()
s.create_menu(menu, s.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(s.hwnd)
win32gui.TrackPopupMenu(menu,
win32con.TPM_LEFTALIGN,
pos[0],
pos[1],
0,
s.hwnd,
None)
win32gui.PostMessage(s.hwnd, win32con.WM_NULL, 0, 0)

def destroy(s, hwnd, msg, wparam, lparam):
if s.on_quit: s.on_quit(s) #运行传递的on_quit
nid = (s.hwnd, 0)
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
win32gui.PostQuitMessage(0) # 退出托盘图标

def notify(s, hwnd, msg, wparam, lparam):
if lparam == win32con.WM_LBUTTONDBLCLK: # 双击左键
pass #s.execute_menu_option(s.default_menu_index + s.FIRST_ID)
elif lparam == win32con.WM_RBUTTONUP: # 单击右键
s.show_menu()
elif lparam == win32con.WM_LBUTTONUP: # 单击左键
nid = (s.hwnd, 0)
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
win32gui.PostQuitMessage(0) # 退出托盘图标
if Main: Main.root.deiconify()
return True
""" 可能的鼠标事件:
WM_MOUSEMOVE
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_RBUTTONDOWN
WM_RBUTTONUP
WM_RBUTTONDBLCLK
WM_MBUTTONDOWN
WM_MBUTTONUP
WM_MBUTTONDBLCLK"""

def _add_ids_to_menu_options(s, 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 s.SPECIAL_ACTIONS:
s.menu_actions_by_id.add((s._next_action_id, option_action))
result.append(menu_option + (s._next_action_id,))
else:
result.append((option_text,
option_icon,
s._add_ids_to_menu_options(option_action),
s._next_action_id))
s._next_action_id += 1
return result

def refresh_icon(s, **data):
hinst = win32gui.GetModuleHandle(None)
if os.path.isfile(s.icon): # 尝试找到自定义图标
icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
hicon = win32gui.LoadImage(hinst,
s.icon,
win32con.IMAGE_ICON,
0,
0,
icon_flags)
else: # 找不到图标文件 - 使用默认值
hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)

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

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

if option_id in s.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()
s.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(s, 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)
# 填满背景。
brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU)
win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush)
# "GetSysColorBrush返回缓存的画笔而不是分配新的画笔。"
# - 暗示没有DeleteObject
# 画出图标
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(s, hwnd, msg, wparam, lparam):
id = win32gui.LOWORD(wparam)
s.execute_menu_option(id)

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

class _Main:
def main(s):
import tkinter as tk
s.root = tk.Tk()
s.root.title('测试005')
s.root.geometry("800x900")
sb=Scrollbar(s.root)
lb = Listbox(s.root,yscrollcommand= sb.set,width=700)
sb.pack(side=RIGHT,fill=Y)
lb.pack(side=LEFT,fill=Y)
def count():
interval = 30
command = r"执行准备"

lb.pack(side=LEFT,fill=Y)
sb.pack(side=RIGHT,fill=Y)
lb.insert(END,print_ts("-"*100))
lb.insert(END,print_ts("开始执行 %s"%command))
lb.insert(END,print_ts("时间间隔 %s 分钟."%(interval/60)))
lb.insert(END,print_ts("-"*100))
jss=0
while True:
try:
jss+=1
# sleep for the remaining seconds of interval
# ----------下面为定时执行的具体命令--------
lb.insert(END,"成功1")
time.sleep(1)
lb.insert(END,"成功2")
time.sleep(1)
lb.insert(END,"成功3")
time.sleep(1)
lb.insert(END,"成功4")
time.sleep(1)
lb.insert(END,"成功5")
time.sleep(1)
lb.insert(END,"成功6")
time.sl
...全文
2139 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
菜鸟小白菜 2020-11-26
  • 打赏
  • 举报
回复
大佬,缩到托盘后,程序会暂停怎么解决
三七Novice 2022-05-24
  • 举报
回复
@菜鸟小白菜 将sysTrayIcon.show_icon做线程运行
LiQianJun 2019-06-25
  • 打赏
  • 举报
回复
代码长了。不够如数。下面代码接上面的。
                    # ----------下面为定时执行的具体命令--------
                    lb.insert(END,"成功1")
                    time.sleep(1)
                    lb.insert(END,"成功2")
                    time.sleep(1)
                    lb.insert(END,"成功3")
                    time.sleep(1)
                    lb.insert(END,"成功4")
                    time.sleep(1)
                    lb.insert(END,"成功5")
                    time.sleep(1)
                    lb.insert(END,"成功6")
                    time.sleep(1)
                    lb.insert(END,"成功7")
                    time.sleep(1)
                    lb.insert(END,"成功8")
                    time.sleep(1)
                    lb.insert(END,"成功9")
                    time.sleep(1)
                    lb.insert(END,"成功10")
                    #----------上面为定时执行的具体命令--------
                    lb.insert(END,print_ts('本次处理时间为:' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
                    lb.insert(END,print_ts("-"*100))
                    status='执行完毕'
                    lb.insert(END,print_ts("执行状态 = %s."%status))
                    time_remaining = interval-time.time()%interval
                    lb.insert(END,print_ts("程序运行后这是第 %s 次采集,下次执行时间 %s ( %s 秒后)..."%(jss,(time.ctime(time.time()+time_remaining)), time_remaining)))
                    time.sleep(time_remaining)
                except Exception as e:
                    lb.insert(END,e)

        button = tk.Button(s.root,text='Hit_me',width=20)
        t = threading.Thread(target=count) #创建线程,如果函数里面有参数,args=()
        t.start() #开启线程
        sb.config(command=lb.yview)
        icons = 'caiji.ico'
        hover_text = "SysTrayIcon.py Demo" #悬浮于图标上方时的提示
        menu_options = (('更改 图标', None, s.switch_icon),
                            ('二级 菜单', None, (('更改 图标', None, s.switch_icon),)))
        s.sysTrayIcon = SysTrayIcon(icons, hover_text, menu_options, on_quit = s.exit, default_menu_index = 1)

        s.root.bind("<Unmap>", lambda event: s.Unmap() if s.root.state() == 'iconic' else False)
        s.root.protocol('WM_DELETE_WINDOW', s.exit)
        s.root.resizable(0,0)
        s.root.mainloop()


    def switch_icon(s, _sysTrayIcon, icons = 'caiji.ico'):
        _sysTrayIcon.icon = icons
        _sysTrayIcon.refresh_icon()
        #点击右键菜单项目会传递SysTrayIcon自身给引用的函数,所以这里的_sysTrayIcon = s.sysTrayIcon

    def Unmap(s):
        s.root.withdraw()
        s.sysTrayIcon.show_icon()

    def exit(s, _sysTrayIcon = None):
        s.root.destroy()
        print ('exit...')

if __name__ == '__main__':
    Main = _Main()
    Main.main()

3,424

社区成员

发帖
与我相关
我的任务
社区描述
其他开发语言 其他开发语言
社区管理员
  • 其他开发语言社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧