From 1c278b82f5f16906b78b8a99073d0b4e6cea13c9 Mon Sep 17 00:00:00 2001 From: hiroi-sora <2230247019@qq.com> Date: Mon, 26 Sep 2022 21:30:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=89=98=E7=9B=98/=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E8=BF=90=E8=A1=8C=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/tray.py | 39 +++++++++++++++++++++++--------- ui/win_main.py | 60 +++++++++++++++++++++++++++++++++---------------- utils/config.py | 31 +++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 29 deletions(-) diff --git a/ui/tray.py b/ui/tray.py index 715a17df..03fc5cb1 100644 --- a/ui/tray.py +++ b/ui/tray.py @@ -1,29 +1,44 @@ +from logging import raiseExceptions from ui.systray.traybar import SysTrayIcon from utils.asset import Asset -from utils.config import Config +from utils.config import Config, ClickTrayModeFlag + +import atexit # 退出处理 class Tray: def __init__(self): - menuOptions = ( - ('屏幕截图', Asset.getPath('screenshot24ico'), self.screenshot), - ('粘贴图片', Asset.getPath('paste24ico'), self.clipboard), - ("显示界面", Asset.getPath('app24ico'), self.showWin), - ) + self.tray = None + + def start(self): + aa = ('显示面板', Asset.getPath('app24ico'), self.showWin) + bb = ('屏幕截图', Asset.getPath('screenshot24ico'), self.screenshot) + cc = ('粘贴图片', Asset.getPath('paste24ico'), self.clipboard) + clickTrayMode = Config.get('clickTrayMode').get( + Config.get('clickTrayModeName'), ClickTrayModeFlag.show) + print(f'clickTrayMode: {clickTrayMode}') + menuOptions = () + if clickTrayMode == ClickTrayModeFlag.show: + menuOptions = (aa, bb, cc) + elif clickTrayMode == ClickTrayModeFlag.screenshot: + menuOptions = (bb, cc, aa) + elif clickTrayMode == ClickTrayModeFlag.clipboard: + menuOptions = (cc, bb, aa) self.tray = SysTrayIcon( Asset.getPath('umiocr24ico'), 'Umi-OCR', menuOptions, quit_name='退出', quit_icon=Asset.getPath('exit24ico'), on_quit=self.quit) - - def start(self): self.tray.start() + atexit.register(self.stop) # 注册程序终止时执行强制停止子进程 self.main = Config.main # 注册事件,防止跨线程调用方法 + # self.main.win.bind( + # '<>', lambda *e: self.main.onClose()) self.main.win.bind( - '<>', lambda *e: self.main.onClose()) + '<>', lambda *e: self.main.exit()) self.main.win.bind( '<>', lambda *e: self.main.runClipboard()) # <> 在主窗类内已注册 @@ -32,7 +47,10 @@ def stop(self): '''关闭托盘显示的接口。必须在主线程调用,禁止在托盘线程内调用!''' # https://github.com/Infinidat/infi.systray/issues/26 # https://github.com/Infinidat/infi.systray/issues/32 + if not self.tray: + return self.tray.shutdown() + self.tray = None # 将引用置空,主窗口第二次按下关闭时可强行关闭 def showWin(self, e=None): self.main.gotoTop() @@ -44,7 +62,8 @@ def clipboard(self, e=None): self.main.win.event_generate('<>') def quit(self, e=None): - self.main.win.event_generate('<>') + if self.main.win: + self.main.win.event_generate('<>') SysTray = Tray() # 托盘单例 diff --git a/ui/win_main.py b/ui/win_main.py index 2f64d3f7..be53396b 100644 --- a/ui/win_main.py +++ b/ui/win_main.py @@ -245,9 +245,22 @@ def initSoftwareFrame(): # 软件行为设置 ipady=2, pady=LabelFramePadY, padx=4) fr1 = tk.Frame(fSoft) fr1.pack(side='top', fill='x', pady=2, padx=5) - wid = ttk.Checkbutton(fr1, text="调试模式", - variable=Config.getTK('isDebug')) - wid.grid(column=0, row=0, sticky="w") + fr1.grid_columnconfigure(1, weight=1) + self.balloon.bind(fr1, '下次打开软件生效') + wid = ttk.Checkbutton(fr1, text='显示系统托盘图标', + variable=Config.getTK('isTray')) + wid.grid(column=0, row=0, sticky='w') + Widget.comboboxFrame(fr1, ',双击图标', 'clickTrayMode').grid( + column=1, row=0, sticky='w') + + fr2 = tk.Frame(fSoft) + fr2.pack(side='top', fill='x', pady=2, padx=5) + self.balloon.bind(fr2, '显示系统托盘图标期间才生效') + tk.Label(fr2, text='  关闭主面板:').pack(side='left') + ttk.Radiobutton(fr2, text='最小化到托盘', + variable=Config.getTK('isBackground'), value=True).pack(side='left') + ttk.Radiobutton(fr2, text='退出软件', + variable=Config.getTK('isBackground'), value=False).pack(side='left', padx=15) self.lockWidget.append(wid) initSoftwareFrame() @@ -260,8 +273,7 @@ def quickOCR(): # 快捷识图设置 # 避免子线程直接唤起截图窗导致的窗口闪烁现象 self.win.bind('<>', self.openScreenshot) # 绑定截图事件 - cbox = Widget.comboboxFrame( - fQuick, '截图模式: ', 'scsMode', self.lockWidget) + cbox = Widget.comboboxFrame(fQuick, '截图模式: ', 'scsMode') cbox.pack(side='top', fill='x', padx=4) frss = tk.Frame(fQuick) frss.pack(side='top', fill='x') @@ -541,6 +553,9 @@ def initAbout(): # 关于面板 labelWeb.pack() # 文字 labelWeb.bind( # 绑定鼠标左键点击,打开网页 '', lambda *e: webOpen(Umi.website)) + wid = ttk.Checkbutton(self.optFrame, text='调试模式', + variable=Config.getTK('isDebug')) + wid.pack(side='right') initAbout() def initOptFrameWH(): # 初始化框架的宽高 @@ -560,10 +575,13 @@ def onCanvasMouseWheel(event): # 绑定画布中滚轮滚动事件 1 if event.delta < 0 else -1, "units") self.optCanvas.bind_all("", onCanvasMouseWheel) initOptFrameWH() + initTab3() - SysTray.start() # 启动托盘 - self.win.protocol('WM_DELETE_WINDOW', self.onCloseWin) + if Config.get('isTray'): + SysTray.start() # 启动托盘 + self.win.wm_protocol( # 注册窗口关闭事件 + 'WM_DELETE_WINDOW', self.onCloseWin) self.gotoTop() self.win.mainloop() @@ -862,24 +880,28 @@ def closeScreenshot(self, flag, errMsg=None): # 关闭截图窗口,返回T表 def onCloseWin(self): # 关闭窗口事件 print('onCloseWin!') - # TODO : 判断 - self.win.withdraw() # 隐藏窗口 + if Config.get('isBackground'): + self.win.withdraw() # 隐藏窗口 + else: + self.onClose() # 直接关闭 - def onClose(self): # 关闭窗口事件 + def onClose(self): # 关闭软件 OCRe.stop() # 强制关闭引擎进程,加快子线程结束 if OCRe.engFlag == EngFlag.none and OCRe.msnFlag == MsnFlag.none: # 未在运行 - self.win.destroy() # 直接关闭 + self.exit() else: self.win.after(50, self.waitClose) # 等待关闭,50ms轮询一次是否已结束子线程 - def waitClose(self): # 等待线程关闭后销毁窗口 - Log.info(f'关闭中,等待 {OCRe.engFlag} | {OCRe.msnFlag}') - if OCRe.engFlag == EngFlag.none and OCRe.msnFlag == MsnFlag.none: # 未在运行 - self.win.destroy() # 销毁窗口 - Log.info(f'主窗 exit =====================') - exit(0) - else: - self.win.after(50, self.waitClose) # 等待关闭,50ms轮询一次是否已结束子进程 + def onClose(self): # 关闭软件 + if SysTray.tray: # 开启了托盘 + SysTray.stop() # 通过托盘线程关闭软件 + else: # 无托盘,直接关 + self.exit() + + def exit(self): + # 等待一段时间,保证托盘线程关闭,图标从系统注销 + # 然后强制终止主进程,防止引擎子线程苟且偷生 + self.win.after(100, lambda: os._exit(0)) def showTips(self, tipsText): # 显示提示 if not OCRe.msnFlag == MsnFlag.none: diff --git a/utils/config.py b/utils/config.py index 1aeecc07..d901901c 100644 --- a/utils/config.py +++ b/utils/config.py @@ -26,6 +26,13 @@ class ScsModeFlag(Enum): system = 1 # 系统截屏模式 +class ClickTrayModeFlag(Enum): + '''点击托盘时模式标志''' + show = 0 # 显示主面板 + screenshot = 1 # 截屏 + clipboard = 2 # 粘贴图片 + + # 配置文件路径 ConfigJsonFile = 'Umi-OCR_config.json' @@ -37,6 +44,30 @@ class ScsModeFlag(Enum): 'isSave': True, 'isTK': True, }, + 'isTray': { # T时展示托盘图标 + 'default': True, + 'isSave': True, + 'isTK': True, + }, + 'isBackground': { # T时点关闭进入后台运行 + 'default': False, + 'isSave': True, + 'isTK': True, + }, + 'clickTrayModeName': { # 当前选择的点击托盘图标模式名称 + 'default': '', + 'isSave': True, + 'isTK': True, + }, + 'clickTrayMode': { # 点击托盘图标模式 + 'default': { + '显示面板': ClickTrayModeFlag.show, + '屏幕截图': ClickTrayModeFlag.screenshot, + '粘贴图片': ClickTrayModeFlag.clipboard, + }, + 'isSave': False, + 'isTK': False, + }, # 快捷识图设置 'isHotkeyClipboard': { # T时启用读剪贴板快捷键 'default': False,