From a5c3e1dfc80ea0abcbe70a003d28790c4df37f3d Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 7 Feb 2025 12:01:35 +0800 Subject: [PATCH 01/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9Win7=E7=9A=84?= =?UTF-8?q?=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/Boss-Key.py | 19 ++++++++++++------- requirements.txt | 14 +++++++------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/main/Boss-Key.py b/main/Boss-Key.py index 0e0c780..c5ef6e2 100644 --- a/main/Boss-Key.py +++ b/main/Boss-Key.py @@ -1,12 +1,13 @@ # nuitka-project: --onefile -# nuitka-project: --windows-console-mode=disable # nuitka-project: --standalone +# nuitka-project: --assume-yes-for-downloads # nuitka-project: --follow-import-to=core -# nuitka-project: --windows-icon-from-ico=icon.ico -# nuitka-project: --windows-product-name="Boss Key" -# nuitka-project: --windows-file-description="Boss Key Application" +# nuitka-project: --follow-import-to=GUI # nuitka-project: --copyright="Copyright (C) 2025 Ivan Hanloth All Rights Reserved. " -# nuitka-project: --windows-company-name="Ivan Hanloth" +# nuitka-project: --product-name="Boss Key" +# nuitka-project: --file-description="Boss Key Application" +# nuitka-project: --windows-icon-from-ico=icon.ico +# nuitka-project: --windows-console-mode=disable from GUI import setting, taskbar from core import listener @@ -16,9 +17,13 @@ import psutil import wx from core.config import Config +import platform -ctypes.windll.shcore.SetProcessDpiAwareness(2) # Win10 and Win8 -ctypes.windll.user32.SetProcessDPIAware() #Win7 and below +if platform.system() == "Windows": + if platform.release() == "7": + ctypes.windll.user32.SetProcessDPIAware() + else: + ctypes.windll.shcore.SetProcessDpiAwareness(2) class APP(wx.App): def __init__(self): diff --git a/requirements.txt b/requirements.txt index 47b015d..d39ea99 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,16 +3,16 @@ charset-normalizer==3.4.1 comtypes==1.4.8 idna==3.10 keyboard==0.13.5 -Nuitka==2.5.9 -numpy==2.1.3 +Nuitka +numpy +urllib3 +winsdk==1.0.0b10 +wxPython==4.2.2 +zstandard==0.23.0 ordered-set==4.1.0 psutil==6.1.0 pycaw==20240210 pynput==1.7.7 pywin32==308 requests==2.32.3 -six==1.16.0 -urllib3==2.3.0 -winsdk==1.0.0b10 -wxPython==4.2.2 -zstandard==0.23.0 \ No newline at end of file +six==1.16.0 \ No newline at end of file From 968e37a1c58f81d32cbd3bf31751762c7dcdab2c Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 11:52:38 +0800 Subject: [PATCH 02/15] =?UTF-8?q?=E8=A7=84=E8=8C=83=E5=8C=96=E7=AA=97?= =?UTF-8?q?=E5=8F=A3=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/GUI/about.py | 2 +- main/GUI/setting.py | 28 ++++++++++++++++------- main/core/config.py | 7 ++++-- main/core/listener.py | 6 ++--- main/core/model.py | 46 ++++++++++++++++++++++++++++++++++++++ main/core/tools.py | 52 +++++++++++++++++++++++++++++++------------ 6 files changed, 113 insertions(+), 28 deletions(-) create mode 100644 main/core/model.py diff --git a/main/GUI/about.py b/main/GUI/about.py index ba278e8..f6603a0 100644 --- a/main/GUI/about.py +++ b/main/GUI/about.py @@ -54,7 +54,7 @@ def init_Load_UI(self): def onCheckUpdate(self): def checkUpdate(): try: - info = check_update() + info = checkUpdate() except: wx.CallAfter(self.init_error_UI) return diff --git a/main/GUI/setting.py b/main/GUI/setting.py index f612c9e..e3ae778 100644 --- a/main/GUI/setting.py +++ b/main/GUI/setting.py @@ -6,6 +6,7 @@ import core.tools as tool import json import wx.lib.buttons as buttons +from core.model import WindowInfo class SettingWindow(wx.Frame): def __init__(self): @@ -187,6 +188,8 @@ def OnSave(self,e): Config.hide_current = self.hide_current_checkbox.GetValue() Config.click_to_hide = self.click_to_hide_checkbox.GetValue() Config.hide_icon_after_hide = self.hide_icon_after_hide_checkbox.GetValue() + + # 获取Windows对象列表 Config.hide_binding = self.ItemsData(self.right_treelist, only_checked=False) Config.HotkeyListener.ShowWindows(load=False) @@ -269,17 +272,21 @@ def InsertTreeList(self, data: list, treelist: dataview.TreeListCtrl, clear=True root = treelist.GetRootItem() process_map = {} for window in data: - process = window['process'] + # 确保window是WindowInfo对象 + if isinstance(window, dict): + window = WindowInfo.from_dict(window) + + process = window.process if process not in process_map: exists_node=self.SearchProcessNode(treelist, process) if exists_node is None: process_map[process] = treelist.AppendItem(root, process) else: process_map[process] = exists_node - item = treelist.AppendItem(process_map[process], window['title']) - treelist.SetItemText(item, 1, str(window['hwnd'])) - treelist.SetItemText(item, 2, str(window['PID'])) - treelist.SetItemData(item, {"title":window['title'],"hwnd": window['hwnd'], "process": window['process'], "PID": window['PID']}) + item = treelist.AppendItem(process_map[process], window.title) + treelist.SetItemText(item, 1, str(window.hwnd)) + treelist.SetItemText(item, 2, str(window.PID)) + treelist.SetItemData(item, window) treelist.Expand(root) for process in process_map: treelist.Expand(process_map[process]) @@ -291,15 +298,20 @@ def SearchProcessNode(self, treelist: dataview.TreeListCtrl, process): if not item.IsOk(): break data = treelist.GetItemData(item) - if data is not None and data and data['process'] == process: + if data is not None and hasattr(data, 'process') and data.process == process: return treelist.GetItemParent(item) def RemoveItem(self, treelist: dataview.TreeListCtrl, data): - node=item = self.SearchProcessNode(treelist, data['process']) + # 确保data是WindowInfo对象 + if isinstance(data, dict): + data = WindowInfo.from_dict(data) + + node=item = self.SearchProcessNode(treelist, data.process) if item is not None: item = treelist.GetFirstChild(item) while item.IsOk(): - if treelist.GetItemData(item) == data: + item_data = treelist.GetItemData(item) + if item_data and item_data == data: treelist.DeleteItem(item) break item = treelist.GetNextSibling(item) diff --git a/main/core/config.py b/main/core/config.py index 4784bea..7aefeb1 100644 --- a/main/core/config.py +++ b/main/core/config.py @@ -4,6 +4,7 @@ from .icon import get_icon from configparser import ConfigParser from io import BytesIO +from .model import WindowInfo class Config: AppName = "Boss Key" @@ -93,7 +94,8 @@ def load(): Config.hide_hotkey = config.get("hotkey", {}).get("hide_hotkey", "Ctrl+Q") Config.close_hotkey = config.get("hotkey", {}).get("close_hotkey", "Win+Esc") - Config.hide_binding = config.get("hide_binding", []) + # 将hide_binding从字典列表转换为WindowInfo对象列表 + Config.hide_binding = [WindowInfo.from_dict(item) for item in config.get("hide_binding", [])] if config.get('version', '') != Config.AppVersion: Config.save() @@ -115,7 +117,8 @@ def save(): 'click_to_hide': Config.click_to_hide, 'hide_icon_after_hide': Config.hide_icon_after_hide }, - "hide_binding" : Config.hide_binding + # 将WindowInfo对象列表转换为字典列表用于JSON序列化 + "hide_binding": [item.to_dict() if isinstance(item, WindowInfo) else item for item in Config.hide_binding] } with open(Config.config_path, 'w', encoding='utf-8') as f: diff --git a/main/core/listener.py b/main/core/listener.py index 5447a17..42c5ac1 100644 --- a/main/core/listener.py +++ b/main/core/listener.py @@ -107,11 +107,11 @@ def HideWindows(self): for i in outer: for j in inner: - if tool.isSameWindow(i,j,False): + if tool.isSameWindow(i, j, False): if outer==Config.hide_binding: # 此时i是绑定的元素,j是窗口元素,需要隐藏j - needHide.append(j['hwnd']) + needHide.append(j.hwnd) else: - needHide.append(i['hwnd']) + needHide.append(i.hwnd) break if Config.hide_current: # 插入当前窗口的句柄 diff --git a/main/core/model.py b/main/core/model.py new file mode 100644 index 0000000..1f42fa5 --- /dev/null +++ b/main/core/model.py @@ -0,0 +1,46 @@ +class WindowInfo: + """ + 窗口信息模型 + """ + title:str = None + hwnd:int = None + process:str = None + PID:int = None + path:str = None + + def __init__(self, title:str, hwnd:int, process:str, PID:int, path:str=None): + self.title = title + self.hwnd = hwnd + self.process = process + self.PID = PID + self.path = path + + def to_dict(self): + """将对象转为字典,用于JSON序列化""" + return { + "title": self.title, + "hwnd": self.hwnd, + "process": self.process, + "PID": self.PID, + "path": self.path + } + + @classmethod + def from_dict(cls, data): + """从字典创建对象,用于JSON反序列化""" + return cls( + title=data.get('title', '无标题窗口'), + hwnd=data.get('hwnd', 0), + process=data.get('process', ''), + PID=data.get('PID', 0), + path=data.get('path', '') + ) + + def __eq__(self, other): + """比较两个WindowInfo是否相等""" + if not isinstance(other, WindowInfo): + return False + return (self.hwnd == other.hwnd and + self.process == other.process and + self.PID == other.PID and + self.title == other.title) \ No newline at end of file diff --git a/main/core/tools.py b/main/core/tools.py index 4029d6e..bd6cef8 100644 --- a/main/core/tools.py +++ b/main/core/tools.py @@ -9,7 +9,9 @@ import requests import json -def check_update(): +from core.model import WindowInfo + +def checkUpdate(): requests.packages.urllib3.disable_warnings() # 获取最新版本信息 try: @@ -146,44 +148,66 @@ def hwnd2windowName(hwnd): title=None return title -def getAllWindows(): +def getAllWindows()-> list[WindowInfo]: # 获取所有窗口信息 - def enumHandler(hwnd, windows:list): + def enumHandler(hwnd, windows:list[WindowInfo]): if win32gui.IsWindowVisible(hwnd): title = hwnd2windowName(hwnd) pid = win32process.GetWindowThreadProcessId(hwnd)[1] process_name = psutil.Process(pid).name() - windows.append({'title': title, 'hwnd': int(hwnd), 'process': process_name, 'PID':int(pid)}) + process_path = psutil.Process(pid).exe() + + windows.append(WindowInfo( + title=title, + hwnd=int(hwnd), + process=process_name, + PID=int(pid), + path=process_path + )) return True windows = [] win32gui.EnumWindows(enumHandler, windows) - windows.sort(key=lambda x: x['title']) + windows.sort(key=lambda x: x.title) return windows -def isSameWindow(w1:dict,w2:dict,strict=False): +def isSameWindow(w1:WindowInfo, w2:WindowInfo, auto=False, strict=True): """ 判断两个窗口的信息是否指向同一个窗口 - w1、w2: dict, 包含hwnd、title、process、PID - strict: 启用严格模式 + w1、w2: WindowInfo对象或字典 + auto: 智能匹配模式,默认False + strict: 严格模式,默认True,非严格模式下只判断进程名称是否相同 """ + # 转换可能的字典为WindowInfo对象 + if isinstance(w1, dict): + w1 = WindowInfo.from_dict(w1) + if isinstance(w2, dict): + w2 = WindowInfo.from_dict(w2) ## 一模一样的两个,肯定是同一个 - if w1==w2: + if w1 == w2: return True process_except=["explorer.exe"] - hwnd_same=w1['hwnd']==w2['hwnd'] - title_same=w1['title']==w2['title'] and w1['title']!="无标题窗口" - process_name_same=w1['process']==w2['process'] and w1 not in process_except - PID_same=w1['PID']==w2['PID'] - process_same=process_name_same or PID_same + hwnd_same = w1.hwnd == w2.hwnd + title_same = w1.title == w2.title and w1.title != "无标题窗口" + process_name_same = w1.process == w2.process and w1.process not in process_except + process_path_same = w1.path == w2.path + PID_same = w1.PID == w2.PID + process_same = process_name_same or PID_same + print(w1.process) ## 非严格模式下 if not strict: + ## 进程名称、路径相同则同一个 + if process_name_same and process_path_same: + return True + + ## 非智能模式下 + if not auto: ## 进程名称相同且标题名称相同则同一个 if process_name_same and title_same: return True From 7260c2e77f2daad3ceec435ff5b99395a6bd64be Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 14:30:13 +0800 Subject: [PATCH 03/15] =?UTF-8?q?=E6=94=B9=E7=94=A8ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/GUI/about.py | 61 +++++++------ main/GUI/record.py | 48 ++++++---- main/GUI/setting.py | 210 +++++++++++++++++++++++++++----------------- main/GUI/taskbar.py | 56 ++++++------ 4 files changed, 219 insertions(+), 156 deletions(-) diff --git a/main/GUI/about.py b/main/GUI/about.py index f6603a0..fcc5798 100644 --- a/main/GUI/about.py +++ b/main/GUI/about.py @@ -22,6 +22,15 @@ def Show(self): wx.adv.AboutBox(self.info) class UpdateWindow(wx.Dialog): + # 定义组件ID常量 + ID_ACTIVITY_INDICATOR = wx.NewId() + ID_INFO_TEXT = wx.NewId() + ID_CURRENT_VERSION = wx.NewId() + ID_NEW_VERSION = wx.NewId() + ID_RELEASE_TIME = wx.NewId() + ID_INFO_LABEL = wx.NewId() + ID_INFO_TEXT_CTRL = wx.NewId() + def __init__(self): super().__init__(None, title="检查更新 - Boss Key", style=wx.DEFAULT_DIALOG_STYLE | wx.STAY_ON_TOP | wx.RESIZE_BORDER) self.SetIcon(wx.Icon(wx.Image(Config.icon).ConvertToBitmap())) @@ -35,21 +44,21 @@ def __init__(self): self.onCheckUpdate() def init_Load_UI(self): - self.panel = wx.Panel(self) - self.sizer = wx.BoxSizer(wx.VERTICAL) + panel = wx.Panel(self) + sizer = wx.BoxSizer(wx.VERTICAL) - self.ai=wx.ActivityIndicator(self.panel) - self.ai.Start() + ai = wx.ActivityIndicator(panel, id=self.ID_ACTIVITY_INDICATOR) + ai.Start() - text = wx.StaticText(self.panel, label="正在获取版本信息,请稍后...") + text = wx.StaticText(panel, id=self.ID_INFO_TEXT, label="正在获取版本信息,请稍后...") # 使元素完全居中 - self.sizer.AddStretchSpacer(3) - self.sizer.Add(self.ai, 0, wx.ALIGN_CENTER) - self.sizer.AddStretchSpacer() - self.sizer.Add(text, 0, wx.ALIGN_CENTER) - self.sizer.AddStretchSpacer(3) - self.panel.SetSizer(self.sizer) + sizer.AddStretchSpacer(3) + sizer.Add(ai, 0, wx.ALIGN_CENTER) + sizer.AddStretchSpacer() + sizer.Add(text, 0, wx.ALIGN_CENTER) + sizer.AddStretchSpacer(3) + panel.SetSizer(sizer) def onCheckUpdate(self): def checkUpdate(): @@ -59,10 +68,10 @@ def checkUpdate(): wx.CallAfter(self.init_error_UI) return - wx.CallAfter(self.init_real_UI,info) + wx.CallAfter(self.init_real_UI, info) threading.Thread(target=checkUpdate).start() - def init_real_UI(self,info): + def init_real_UI(self, info): ## 清空原有元素 self.sizer.Clear() self.ai.Stop() @@ -73,17 +82,17 @@ def init_real_UI(self,info): ## 重绘UI - current_version = wx.StaticText(self.panel, label="当前版本:"+Config.AppVersion) + current_version = wx.StaticText(self.panel, id=self.ID_CURRENT_VERSION, label="当前版本:"+Config.AppVersion) self.sizer.Add(current_version, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) - new_version = wx.StaticText(self.panel, label="最新版本:"+info['tag_name']) + new_version = wx.StaticText(self.panel, id=self.ID_NEW_VERSION, label="最新版本:"+info['tag_name']) self.sizer.Add(new_version, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) - release_time = wx.StaticText(self.panel, label="发布时间:"+datetime.datetime.strftime(info['published_at'], "%Y-%m-%d %H:%M:%S")) + release_time = wx.StaticText(self.panel, id=self.ID_RELEASE_TIME, label="发布时间:"+datetime.datetime.strftime(info['published_at'], "%Y-%m-%d %H:%M:%S")) self.sizer.Add(release_time, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) - info_label = wx.StaticText(self.panel, label="更新内容:") - info_text = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE | wx.TE_READONLY) + info_label = wx.StaticText(self.panel, id=self.ID_INFO_LABEL, label="更新内容:") + info_text = wx.TextCtrl(self.panel, id=self.ID_INFO_TEXT_CTRL, style=wx.TE_MULTILINE | wx.TE_READONLY) if Config.AppVersion == info['tag_name']: addtion_info = "您的版本已经是最新版本 :) \n\n" else: @@ -93,9 +102,10 @@ def init_real_UI(self,info): self.sizer.Add(info_text, 1, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) button_sizer = wx.BoxSizer(wx.VERTICAL) - for i in info['assets']: - download_button = wx.Button(self.panel, label=i['name']) - download_button.Bind(wx.EVT_BUTTON, lambda e: self.Btn_click(i['browser_download_url'],Config.AppVersion == info['tag_name'])) + for i, asset in enumerate(info['assets']): + download_button = wx.Button(self.panel, id=wx.NewId(), label=asset['name']) + # 使用lambda捕获当前值 + download_button.Bind(wx.EVT_BUTTON, lambda e, url=asset['browser_download_url'], is_latest=(Config.AppVersion == info['tag_name']): self.Btn_click(url, is_latest)) button_sizer.Add(download_button, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) self.sizer.Add(button_sizer, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) @@ -104,13 +114,14 @@ def init_real_UI(self,info): self.sizer.Layout() def init_error_UI(self): - self.ai.Stop() - wx.MessageBox("无法连接至更新服务器,情稍后再试","检查更新失败",wx.OK | wx.ICON_ERROR) + if self.FindWindowById(self.ID_ACTIVITY_INDICATOR): + self.FindWindowById(self.ID_ACTIVITY_INDICATOR).Stop() + wx.MessageBox("无法连接至更新服务器,情稍后再试", "检查更新失败", wx.OK | wx.ICON_ERROR) self.Close() - def Btn_click(self,url,is_latest): + def Btn_click(self, url, is_latest): if is_latest: - ask=wx.MessageBox("您的版本已经是最新版本,是否仍前往下载","无需更新",wx.OK | wx.ICON_INFORMATION | wx.CANCEL | wx.CANCEL_DEFAULT) + ask = wx.MessageBox("您的版本已经是最新版本,是否仍前往下载", "无需更新", wx.OK | wx.ICON_INFORMATION | wx.CANCEL | wx.CANCEL_DEFAULT) if ask == wx.CANCEL: return webbrowser.open(url) diff --git a/main/GUI/record.py b/main/GUI/record.py index 0b35ae0..018ce96 100644 --- a/main/GUI/record.py +++ b/main/GUI/record.py @@ -12,6 +12,11 @@ class RecordedHotkey: confirm = False class RecordWindow(wx.Dialog): + # 定义组件ID常量 + ID_CONFIRM_BTN = wx.NewId() + ID_STATUS_TEXT = wx.NewId() + ID_KEY_TEXT = wx.NewId() + def __init__(self): wx.Dialog.__init__(self, None, title="录制热键 - Boss Key", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) self.SetIcon(wx.Icon(wx.Image(Config.icon).ConvertToBitmap())) @@ -28,7 +33,7 @@ def __init__(self): RecordedHotkey.recording = True RecordedHotkey.keys_recorded = set() RecordedHotkey.keys_pressing = set() - keyboard.hook(self.onKeyEvent,suppress=True) + keyboard.hook(self.onKeyEvent, suppress=True) def init_UI(self): # 创建面板 @@ -39,45 +44,50 @@ def init_UI(self): # 创建一个静态文本,居中对齐 hbox1 = wx.BoxSizer(wx.HORIZONTAL) - self.text = wx.StaticText(panel, label="正在录制热键",style=wx.ALIGN_CENTER) - self.text.SetFont(wx.Font(16, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)) - self.text.SetSize(self.text.GetBestSize()) - hbox1.Add(self.text, proportion=1, flag=wx.EXPAND|wx.LEFT|wx.RIGHT, border=20) + status_text = wx.StaticText(panel, self.ID_STATUS_TEXT, label="正在录制热键", style=wx.ALIGN_CENTER) + status_text.SetFont(wx.Font(16, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)) + status_text.SetSize(status_text.GetBestSize()) + hbox1.Add(status_text, proportion=1, flag=wx.EXPAND|wx.LEFT|wx.RIGHT, border=20) vbox.Add(hbox1, proportion=1, flag=wx.EXPAND|wx.TOP|wx.BOTTOM, border=20) # 创建一个静态文本,居中对齐,字号变大,加粗 hbox2 = wx.BoxSizer(wx.HORIZONTAL) - self.text2 = wx.StaticText(panel,style=wx.ALIGN_CENTER, label="请按下热键") - self.text2.SetFont(wx.Font(22, wx.FONTFAMILY_SCRIPT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)) - self.text2.SetSize(self.text2.GetBestSize()) - hbox2.Add(self.text2, proportion=1, flag=wx.LEFT|wx.RIGHT, border=20) + key_text = wx.StaticText(panel, self.ID_KEY_TEXT, style=wx.ALIGN_CENTER, label="请按下热键") + key_text.SetFont(wx.Font(22, wx.FONTFAMILY_SCRIPT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)) + key_text.SetSize(key_text.GetBestSize()) + hbox2.Add(key_text, proportion=1, flag=wx.LEFT|wx.RIGHT, border=20) vbox.Add(hbox2, proportion=1, flag=wx.EXPAND|wx.TOP|wx.BOTTOM, border=20) # 创建一个按钮,居中对齐 hbox3 = wx.BoxSizer(wx.HORIZONTAL) - self.record_btn = wx.Button(panel, -1, label="确定") - hbox3.Add(self.record_btn, proportion=1, flag=wx.LEFT|wx.RIGHT, border=20) + confirm_btn = wx.Button(panel, self.ID_CONFIRM_BTN, label="确定") + hbox3.Add(confirm_btn, proportion=1, flag=wx.LEFT|wx.RIGHT, border=20) vbox.Add(hbox3, proportion=1, flag=wx.EXPAND|wx.TOP|wx.BOTTOM, border=20) - self.record_btn.Bind(wx.EVT_BUTTON, self.Confirm) + + # 使用ID绑定事件 + self.Bind(wx.EVT_BUTTON, self.Confirm, id=self.ID_CONFIRM_BTN) self.Bind(wx.EVT_CLOSE, self.onClose) panel.SetSizer(vbox) def onKeyEvent(self, event): - key=keyMux(event) + key = keyMux(event) if event.event_type == 'down': - if len(RecordedHotkey.keys_pressing)==0: + if len(RecordedHotkey.keys_pressing) == 0: RecordedHotkey.keys_recorded.clear() RecordedHotkey.keys_pressing.add(key) RecordedHotkey.keys_recorded.add(key) elif event.event_type == 'up': RecordedHotkey.keys_pressing.discard(key) - if len(RecordedHotkey.keys_pressing)<=2: + if len(RecordedHotkey.keys_pressing) <= 2: self.stopRecording() else: RecordedHotkey.finishOneTime = False - self.text2.SetLabel("+".join(RecordedHotkey.keys_recorded)) - self.text2.SetExtraStyle(wx.ALIGN_CENTER) + + # 使用FindWindowById获取组件 + text = self.FindWindowById(self.ID_KEY_TEXT) + text.SetLabel("+".join(RecordedHotkey.keys_recorded)) + text.SetExtraStyle(wx.ALIGN_CENTER) def stopRecording(self): RecordedHotkey.finishOneTime = True @@ -90,9 +100,9 @@ def Confirm(self, event): self.Destroy() RecordedHotkey.confirm = True - def onClose(self,event): + def onClose(self, event): self.stopRecording() RecordedHotkey.recording = False keyboard.unhook_all() self.Destroy() - RecordedHotkey.confirm= False \ No newline at end of file + RecordedHotkey.confirm = False \ No newline at end of file diff --git a/main/GUI/setting.py b/main/GUI/setting.py index e3ae778..7827b6b 100644 --- a/main/GUI/setting.py +++ b/main/GUI/setting.py @@ -9,6 +9,24 @@ from core.model import WindowInfo class SettingWindow(wx.Frame): + # 定义组件ID常量 + ID_LEFT_TREELIST = wx.NewId() + ID_RIGHT_TREELIST = wx.NewId() + ID_ADD_BINDING_BTN = wx.NewId() + ID_REMOVE_BINDING_BTN = wx.NewId() + ID_REFRESH_BTN = wx.NewId() + ID_HIDE_SHOW_HOTKEY_TEXT = wx.NewId() + ID_HIDE_SHOW_HOTKEY_BTN = wx.NewId() + ID_CLOSE_HOTKEY_TEXT = wx.NewId() + ID_CLOSE_HOTKEY_BTN = wx.NewId() + ID_MUTE_AFTER_HIDE_CHECKBOX = wx.NewId() + ID_SEND_BEFORE_HIDE_CHECKBOX = wx.NewId() + ID_HIDE_CURRENT_CHECKBOX = wx.NewId() + ID_CLICK_TO_HIDE_CHECKBOX = wx.NewId() + ID_HIDE_ICON_AFTER_HIDE_CHECKBOX = wx.NewId() + ID_RESET_BTN = wx.NewId() + ID_SAVE_BTN = wx.NewId() + def __init__(self): super().__init__(None, title="设置 - Boss Key", style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER) self.SetIcon(wx.Icon(wx.Image(Config.icon).ConvertToBitmap())) @@ -33,7 +51,7 @@ def init_UI(self): # 左边列表 left_staticbox = wx.StaticBox(panel, label="现有窗口进程") left_sizer = wx.StaticBoxSizer(left_staticbox, wx.VERTICAL) - self.left_treelist = dataview.TreeListCtrl(panel, style=wx.dataview.TL_CHECKBOX) + self.left_treelist = dataview.TreeListCtrl(panel, self.ID_LEFT_TREELIST, style=wx.dataview.TL_CHECKBOX) self.left_treelist.AppendColumn('窗口标题', width=300) self.left_treelist.AppendColumn('窗口句柄', width=100) self.left_treelist.AppendColumn('进程PID', width=150) @@ -42,17 +60,17 @@ def init_UI(self): # 中键按钮 middle_sizer = wx.BoxSizer(wx.VERTICAL) - self.add_binding_btn = buttons.GenButton(panel, label="添加绑定-->") - self.remove_binding_btn = buttons.GenButton(panel, label="<--删除绑定") - self.refresh_btn = buttons.GenButton(panel, label="刷新进程") - middle_sizer.Add(self.add_binding_btn, 0, wx.EXPAND | wx.ALL, 5) - middle_sizer.Add(self.remove_binding_btn, 0, wx.EXPAND | wx.ALL, 5) - middle_sizer.Add(self.refresh_btn, 0, wx.EXPAND | wx.ALL, 5) + add_binding_btn = buttons.GenButton(panel, self.ID_ADD_BINDING_BTN, label="添加绑定-->") + remove_binding_btn = buttons.GenButton(panel, self.ID_REMOVE_BINDING_BTN, label="<--删除绑定") + refresh_btn = buttons.GenButton(panel, self.ID_REFRESH_BTN, label="刷新进程") + middle_sizer.Add(add_binding_btn, 0, wx.EXPAND | wx.ALL, 5) + middle_sizer.Add(remove_binding_btn, 0, wx.EXPAND | wx.ALL, 5) + middle_sizer.Add(refresh_btn, 0, wx.EXPAND | wx.ALL, 5) # 右边列表 right_staticbox = wx.StaticBox(panel, label="已绑定进程") right_sizer = wx.StaticBoxSizer(right_staticbox, wx.VERTICAL) - self.right_treelist = dataview.TreeListCtrl(panel, style=wx.dataview.TL_CHECKBOX) + self.right_treelist = dataview.TreeListCtrl(panel, self.ID_RIGHT_TREELIST, style=wx.dataview.TL_CHECKBOX) self.right_treelist.AppendColumn('窗口标题', width=300) self.right_treelist.AppendColumn('窗口句柄', width=100) self.right_treelist.AppendColumn('进程PID', width=150) @@ -72,21 +90,21 @@ def init_UI(self): #设置隐显窗口热键 hide_show_hotkey_sizer = wx.BoxSizer(wx.HORIZONTAL) hide_show_hotkey_label = wx.StaticText(panel, label="隐藏/显示窗口热键:") - self.hide_show_hotkey_text = wx.TextCtrl(panel, -1, value=Config.hide_hotkey) - self.hide_show_hotkey_btn = wx.Button(panel, -1, label="录制热键") + hide_show_hotkey_text = wx.TextCtrl(panel, self.ID_HIDE_SHOW_HOTKEY_TEXT, value=Config.hide_hotkey) + hide_show_hotkey_btn = wx.Button(panel, self.ID_HIDE_SHOW_HOTKEY_BTN, label="录制热键") hide_show_hotkey_sizer.Add(hide_show_hotkey_label, proportion=1, flag=wx.EXPAND| wx.ALL, border=10) - hide_show_hotkey_sizer.Add(self.hide_show_hotkey_text, proportion=1, flag=wx.EXPAND| wx.ALL, border=10) - hide_show_hotkey_sizer.Add(self.hide_show_hotkey_btn, proportion=1, flag=wx.EXPAND|wx.ALL, border=10) + hide_show_hotkey_sizer.Add(hide_show_hotkey_text, proportion=1, flag=wx.EXPAND| wx.ALL, border=10) + hide_show_hotkey_sizer.Add(hide_show_hotkey_btn, proportion=1, flag=wx.EXPAND|wx.ALL, border=10) hotkey_sizer.Add(hide_show_hotkey_sizer, proportion=1, flag=wx.EXPAND|wx.ALL, border=10) #设置关闭热键 close_hotkey_sizer = wx.BoxSizer(wx.HORIZONTAL) close_hotkey_label = wx.StaticText(panel, label="一键关闭程序热键:") - self.close_hotkey_text = wx.TextCtrl(panel, -1, value=Config.close_hotkey) - self.close_hotkey_btn = wx.Button(panel, -1, label="录制热键") + close_hotkey_text = wx.TextCtrl(panel, self.ID_CLOSE_HOTKEY_TEXT, value=Config.close_hotkey) + close_hotkey_btn = wx.Button(panel, self.ID_CLOSE_HOTKEY_BTN, label="录制热键") close_hotkey_sizer.Add(close_hotkey_label, proportion=1, flag=wx.EXPAND| wx.ALL, border=10) - close_hotkey_sizer.Add(self.close_hotkey_text, proportion=1, flag=wx.EXPAND| wx.ALL, border=10) - close_hotkey_sizer.Add(self.close_hotkey_btn, proportion=1, flag=wx.EXPAND| wx.ALL, border=10) + close_hotkey_sizer.Add(close_hotkey_text, proportion=1, flag=wx.EXPAND| wx.ALL, border=10) + close_hotkey_sizer.Add(close_hotkey_btn, proportion=1, flag=wx.EXPAND| wx.ALL, border=10) hotkey_sizer.Add(close_hotkey_sizer, proportion=1, flag=wx.EXPAND| wx.ALL, border=10) bottom_sizer.Add(hotkey_sizer, flag=wx.EXPAND| wx.ALL) @@ -96,38 +114,38 @@ def init_UI(self): mute_after_hide_sizer=wx.BoxSizer(wx.HORIZONTAL) mute_after_hide_label = wx.StaticText(panel, label="隐藏窗口后静音") - self.mute_after_hide_checkbox = wx.CheckBox(panel, -1, "") + mute_after_hide_checkbox = wx.CheckBox(panel, self.ID_MUTE_AFTER_HIDE_CHECKBOX, "") mute_after_hide_sizer.Add(mute_after_hide_label,proportion=1, flag=wx.EXPAND| wx.ALL) - mute_after_hide_sizer.Add(self.mute_after_hide_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) + mute_after_hide_sizer.Add(mute_after_hide_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) settings_checkbox_sizer.Add(mute_after_hide_sizer,proportion=1,flag=wx.EXPAND| wx.ALL, border=10) send_before_hide_sizer=wx.BoxSizer(wx.HORIZONTAL) send_before_hide_label = wx.StaticText(panel, label="隐藏前发送暂停键(Beta)") send_before_hide_label.SetToolTip(wx.ToolTip("隐藏窗口前发送暂停键,用于关闭弹出的输入框等,隐藏窗口会存在一定的延迟")) - self.send_before_hide_checkbox = wx.CheckBox(panel, -1, "") + send_before_hide_checkbox = wx.CheckBox(panel, self.ID_SEND_BEFORE_HIDE_CHECKBOX, "") send_before_hide_sizer.Add(send_before_hide_label,proportion=1, flag=wx.EXPAND| wx.ALL) - send_before_hide_sizer.Add(self.send_before_hide_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) + send_before_hide_sizer.Add(send_before_hide_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) settings_checkbox_sizer.Add(send_before_hide_sizer, proportion=1,flag=wx.EXPAND| wx.ALL, border=10) hide_current_sizer=wx.BoxSizer(wx.HORIZONTAL) hide_current_label = wx.StaticText(panel, label="同时隐藏当前活动窗口") - self.hide_current_checkbox = wx.CheckBox(panel, -1, "") + hide_current_checkbox = wx.CheckBox(panel, self.ID_HIDE_CURRENT_CHECKBOX, "") hide_current_sizer.Add(hide_current_label,proportion=1, flag=wx.EXPAND| wx.ALL) - hide_current_sizer.Add(self.hide_current_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) + hide_current_sizer.Add(hide_current_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) settings_checkbox_sizer.Add(hide_current_sizer, proportion=1,flag=wx.EXPAND| wx.ALL, border=10) click_to_hide_sizer=wx.BoxSizer(wx.HORIZONTAL) click_to_hide_label = wx.StaticText(panel, label="点击托盘图标切换隐藏状态") - self.click_to_hide_checkbox = wx.CheckBox(panel, -1, "") + click_to_hide_checkbox = wx.CheckBox(panel, self.ID_CLICK_TO_HIDE_CHECKBOX, "") click_to_hide_sizer.Add(click_to_hide_label,proportion=1, flag=wx.EXPAND| wx.ALL) - click_to_hide_sizer.Add(self.click_to_hide_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) + click_to_hide_sizer.Add(click_to_hide_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) settings_checkbox_sizer.Add(click_to_hide_sizer, proportion=1,flag=wx.EXPAND| wx.ALL, border=10) hide_icon_after_hide_sizer=wx.BoxSizer(wx.HORIZONTAL) hide_icon_after_hide_label = wx.StaticText(panel, label="隐藏窗口后隐藏托盘图标") - self.hide_icon_after_hide_checkbox = wx.CheckBox(panel, -1, "") + hide_icon_after_hide_checkbox = wx.CheckBox(panel, self.ID_HIDE_ICON_AFTER_HIDE_CHECKBOX, "") hide_icon_after_hide_sizer.Add(hide_icon_after_hide_label,proportion=1, flag=wx.EXPAND| wx.ALL) - hide_icon_after_hide_sizer.Add(self.hide_icon_after_hide_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) + hide_icon_after_hide_sizer.Add(hide_icon_after_hide_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) settings_checkbox_sizer.Add(hide_icon_after_hide_sizer, proportion=1,flag=wx.EXPAND| wx.ALL, border=10) bottom_sizer.Add(settings_checkbox_sizer, flag=wx.EXPAND| wx.ALL, border=10) @@ -139,10 +157,10 @@ def init_UI(self): # 创建按钮 button_sizer = wx.BoxSizer(wx.HORIZONTAL) - self.reset_btn = wx.Button(panel,size=(100,60), label="重置设置") - self.save_btn = wx.Button(panel,size=(100,60), label="保存设置") - button_sizer.Add(self.reset_btn, proportion=1, flag=wx.LEFT, border=20) - button_sizer.Add(self.save_btn, proportion=1, flag=wx.RIGHT, border=20) + reset_btn = wx.Button(panel, self.ID_RESET_BTN, size=(100,60), label="重置设置") + save_btn = wx.Button(panel, self.ID_SAVE_BTN, size=(100,60), label="保存设置") + button_sizer.Add(reset_btn, proportion=1, flag=wx.LEFT, border=20) + button_sizer.Add(save_btn, proportion=1, flag=wx.RIGHT, border=20) bottom_sizer.Add(button_sizer, proportion=1, flag=wx.EXPAND|wx.BOTTOM, border=10) # Add top and bottom sizers to the main sizer @@ -153,41 +171,55 @@ def init_UI(self): main_sizer.Fit(self) def Bind_EVT(self): - self.save_btn.Bind(wx.EVT_BUTTON, self.OnSave) - self.reset_btn.Bind(wx.EVT_BUTTON,self.OnReset) - self.hide_show_hotkey_btn.Bind(wx.EVT_BUTTON, self.OnRecordSW) - self.close_hotkey_btn.Bind(wx.EVT_BUTTON, self.OnRecordCL) - self.send_before_hide_checkbox.Bind(wx.EVT_CHECKBOX, self.OnSendBeforeHide) - self.refresh_btn.Bind(wx.EVT_BUTTON, self.RefreshLeftList) - self.add_binding_btn.Bind(wx.EVT_BUTTON, self.OnAddBinding) - self.remove_binding_btn.Bind(wx.EVT_BUTTON, self.OnRemoveBinding) - # self.left_treelist.Bind(dataview.EVT_TREELIST_SELECTION_CHANGED, self.OnToggleCheck) - # self.right_treelist.Bind(dataview.EVT_TREELIST_SELECTION_CHANGED, self.OnToggleCheck) + self.Bind(wx.EVT_BUTTON, self.OnSave, id=self.ID_SAVE_BTN) + self.Bind(wx.EVT_BUTTON, self.OnReset, id=self.ID_RESET_BTN) + self.Bind(wx.EVT_BUTTON, self.OnRecordSW, id=self.ID_HIDE_SHOW_HOTKEY_BTN) + self.Bind(wx.EVT_BUTTON, self.OnRecordCL, id=self.ID_CLOSE_HOTKEY_BTN) + self.Bind(wx.EVT_CHECKBOX, self.OnSendBeforeHide, id=self.ID_SEND_BEFORE_HIDE_CHECKBOX) + self.Bind(wx.EVT_BUTTON, self.RefreshLeftList, id=self.ID_REFRESH_BTN) + self.Bind(wx.EVT_BUTTON, self.OnAddBinding, id=self.ID_ADD_BINDING_BTN) + self.Bind(wx.EVT_BUTTON, self.OnRemoveBinding, id=self.ID_REMOVE_BINDING_BTN) self.left_treelist.Bind(dataview.EVT_TREELIST_ITEM_CHECKED, self.OnToggleCheck) self.right_treelist.Bind(dataview.EVT_TREELIST_ITEM_CHECKED, self.OnToggleCheck) - self.Bind(wx.EVT_CLOSE,self.OnClose) + self.Bind(wx.EVT_CLOSE, self.OnClose) def SetData(self): Config.load() - self.hide_show_hotkey_text.SetValue(Config.hide_hotkey) - self.close_hotkey_text.SetValue(Config.close_hotkey) - self.mute_after_hide_checkbox.SetValue(Config.mute_after_hide) - self.send_before_hide_checkbox.SetValue(Config.send_before_hide) - self.hide_current_checkbox.SetValue(Config.hide_current) - self.click_to_hide_checkbox.SetValue(Config.click_to_hide) - self.hide_icon_after_hide_checkbox.SetValue(Config.hide_icon_after_hide) + hide_show_hotkey_text = self.FindWindowById(self.ID_HIDE_SHOW_HOTKEY_TEXT) + close_hotkey_text = self.FindWindowById(self.ID_CLOSE_HOTKEY_TEXT) + mute_after_hide_checkbox = self.FindWindowById(self.ID_MUTE_AFTER_HIDE_CHECKBOX) + send_before_hide_checkbox = self.FindWindowById(self.ID_SEND_BEFORE_HIDE_CHECKBOX) + hide_current_checkbox = self.FindWindowById(self.ID_HIDE_CURRENT_CHECKBOX) + click_to_hide_checkbox = self.FindWindowById(self.ID_CLICK_TO_HIDE_CHECKBOX) + hide_icon_after_hide_checkbox = self.FindWindowById(self.ID_HIDE_ICON_AFTER_HIDE_CHECKBOX) + + hide_show_hotkey_text.SetValue(Config.hide_hotkey) + close_hotkey_text.SetValue(Config.close_hotkey) + mute_after_hide_checkbox.SetValue(Config.mute_after_hide) + send_before_hide_checkbox.SetValue(Config.send_before_hide) + hide_current_checkbox.SetValue(Config.hide_current) + click_to_hide_checkbox.SetValue(Config.click_to_hide) + hide_icon_after_hide_checkbox.SetValue(Config.hide_icon_after_hide) self.InsertTreeList(Config.hide_binding, self.right_treelist, True) self.RefreshLeftList() - def OnSave(self,e): - Config.hide_hotkey = self.hide_show_hotkey_text.GetValue() - Config.close_hotkey = self.close_hotkey_text.GetValue() - Config.mute_after_hide = self.mute_after_hide_checkbox.GetValue() - Config.send_before_hide = self.send_before_hide_checkbox.GetValue() - Config.hide_current = self.hide_current_checkbox.GetValue() - Config.click_to_hide = self.click_to_hide_checkbox.GetValue() - Config.hide_icon_after_hide = self.hide_icon_after_hide_checkbox.GetValue() + def OnSave(self, e): + hide_show_hotkey_text = self.FindWindowById(self.ID_HIDE_SHOW_HOTKEY_TEXT) + close_hotkey_text = self.FindWindowById(self.ID_CLOSE_HOTKEY_TEXT) + mute_after_hide_checkbox = self.FindWindowById(self.ID_MUTE_AFTER_HIDE_CHECKBOX) + send_before_hide_checkbox = self.FindWindowById(self.ID_SEND_BEFORE_HIDE_CHECKBOX) + hide_current_checkbox = self.FindWindowById(self.ID_HIDE_CURRENT_CHECKBOX) + click_to_hide_checkbox = self.FindWindowById(self.ID_CLICK_TO_HIDE_CHECKBOX) + hide_icon_after_hide_checkbox = self.FindWindowById(self.ID_HIDE_ICON_AFTER_HIDE_CHECKBOX) + + Config.hide_hotkey = hide_show_hotkey_text.GetValue() + Config.close_hotkey = close_hotkey_text.GetValue() + Config.mute_after_hide = mute_after_hide_checkbox.GetValue() + Config.send_before_hide = send_before_hide_checkbox.GetValue() + Config.hide_current = hide_current_checkbox.GetValue() + Config.click_to_hide = click_to_hide_checkbox.GetValue() + Config.hide_icon_after_hide = hide_icon_after_hide_checkbox.GetValue() # 获取Windows对象列表 Config.hide_binding = self.ItemsData(self.right_treelist, only_checked=False) @@ -200,26 +232,35 @@ def OnSave(self,e): except: wx.MessageDialog(None, u"热键绑定失败,请重试", u"Boss Key", wx.OK | wx.ICON_ERROR).ShowModal() - def OnAddBinding(self,e): + def OnAddBinding(self, e): left_checked = self.ItemsData(self.left_treelist, only_checked=True) self.InsertTreeList(left_checked, self.right_treelist, False) for item in left_checked: self.RemoveItem(self.left_treelist, item) - - def OnRemoveBinding(self,e): + def OnRemoveBinding(self, e): right_checked = self.ItemsData(self.right_treelist, only_checked=True) self.InsertTreeList(right_checked, self.left_treelist, False) for item in right_checked: self.RemoveItem(self.right_treelist, item) - def OnReset(self,e): - self.hide_show_hotkey_text.SetValue("Ctrl+Q") - self.close_hotkey_text.SetValue("Win+Esc") - self.mute_after_hide_checkbox.SetValue(True) - self.send_before_hide_checkbox.SetValue(False) - self.hide_current_checkbox.SetValue(True) - self.InsertTreeList([],self.right_treelist,True) + def OnReset(self, e): + hide_show_hotkey_text = self.FindWindowById(self.ID_HIDE_SHOW_HOTKEY_TEXT) + close_hotkey_text = self.FindWindowById(self.ID_CLOSE_HOTKEY_TEXT) + mute_after_hide_checkbox = self.FindWindowById(self.ID_MUTE_AFTER_HIDE_CHECKBOX) + send_before_hide_checkbox = self.FindWindowById(self.ID_SEND_BEFORE_HIDE_CHECKBOX) + hide_current_checkbox = self.FindWindowById(self.ID_HIDE_CURRENT_CHECKBOX) + click_to_hide_checkbox = self.FindWindowById(self.ID_CLICK_TO_HIDE_CHECKBOX) + hide_icon_after_hide_checkbox = self.FindWindowById(self.ID_HIDE_ICON_AFTER_HIDE_CHECKBOX) + + hide_show_hotkey_text.SetValue("Ctrl+Q") + close_hotkey_text.SetValue("Win+Esc") + mute_after_hide_checkbox.SetValue(True) + send_before_hide_checkbox.SetValue(False) + hide_current_checkbox.SetValue(True) + click_to_hide_checkbox.SetValue(False) + hide_icon_after_hide_checkbox.SetValue(False) + self.InsertTreeList([], self.right_treelist, True) self.RefreshLeftList() wx.MessageDialog(None, u"已重置选项,请保存设置以启用", u"Boss Key", wx.OK | wx.ICON_INFORMATION).ShowModal() @@ -239,32 +280,37 @@ def OnToggleCheck(self, e): elif treelist.AreAllChildrenInState(parent, wx.CHK_UNCHECKED): treelist.CheckItem(parent, wx.CHK_UNCHECKED) - def OnSendBeforeHide(self,e): - if self.send_before_hide_checkbox.GetValue(): + def OnSendBeforeHide(self, e): + send_before_hide_checkbox = self.FindWindowById(self.ID_SEND_BEFORE_HIDE_CHECKBOX) + if send_before_hide_checkbox.GetValue(): wx.MessageDialog(None, u"隐藏窗口前向被隐藏的窗口发送空格,用于暂停视频等。启用此功能可能会延迟窗口的隐藏", u"Boss Key", wx.OK | wx.ICON_INFORMATION).ShowModal() def OnRecordSW(self, e): - self.recordHotkey(self.hide_show_hotkey_text, self.hide_show_hotkey_btn) + hide_show_hotkey_text = self.FindWindowById(self.ID_HIDE_SHOW_HOTKEY_TEXT) + hide_show_hotkey_btn = self.FindWindowById(self.ID_HIDE_SHOW_HOTKEY_BTN) + self.recordHotkey(hide_show_hotkey_text, hide_show_hotkey_btn) def OnClose(self, e): self.Hide() def OnRecordCL(self, e): - self.recordHotkey(self.close_hotkey_text, self.close_hotkey_btn) + close_hotkey_text = self.FindWindowById(self.ID_CLOSE_HOTKEY_TEXT) + close_hotkey_btn = self.FindWindowById(self.ID_CLOSE_HOTKEY_BTN) + self.recordHotkey(close_hotkey_text, close_hotkey_btn) - def RefreshLeftList(self,e=None): - windows=tool.getAllWindows() - right=self.ItemsData(self.right_treelist,only_checked=False) - list=[] + def RefreshLeftList(self, e=None): + windows = tool.getAllWindows() + right = self.ItemsData(self.right_treelist, only_checked=False) + list = [] for window in windows: - flag=0 + flag = 0 for i in right: - if tool.isSameWindow(window,i,True): - flag=1 + if tool.isSameWindow(window, i, True): + flag = 1 break if not flag: list.append(window) - self.InsertTreeList(list,self.left_treelist,True) + self.InsertTreeList(list, self.left_treelist, True) def InsertTreeList(self, data: list, treelist: dataview.TreeListCtrl, clear=True): if clear: @@ -278,7 +324,7 @@ def InsertTreeList(self, data: list, treelist: dataview.TreeListCtrl, clear=True process = window.process if process not in process_map: - exists_node=self.SearchProcessNode(treelist, process) + exists_node = self.SearchProcessNode(treelist, process) if exists_node is None: process_map[process] = treelist.AppendItem(root, process) else: @@ -306,7 +352,7 @@ def RemoveItem(self, treelist: dataview.TreeListCtrl, data): if isinstance(data, dict): data = WindowInfo.from_dict(data) - node=item = self.SearchProcessNode(treelist, data.process) + node = item = self.SearchProcessNode(treelist, data.process) if item is not None: item = treelist.GetFirstChild(item) while item.IsOk(): @@ -337,14 +383,14 @@ def ItemsData(self, treelist: dataview.TreeListCtrl, only_checked=False, item_ob res.append(data) return res - def recordHotkey(self, text_ctrl:wx.TextCtrl, btn:wx.Button): + def recordHotkey(self, text_ctrl: wx.TextCtrl, btn: wx.Button): try: Config.HotkeyListener.stop() except: pass btn.Disable() btn.SetLabel("录制中...") - record.RecordedHotkey.confirm=False + record.RecordedHotkey.confirm = False RecordWindow = record.RecordWindow() RecordWindow.ShowModal() btn.Enable() diff --git a/main/GUI/taskbar.py b/main/GUI/taskbar.py index 2bb7d67..f4c5143 100644 --- a/main/GUI/taskbar.py +++ b/main/GUI/taskbar.py @@ -6,7 +6,7 @@ class TaskBarIcon(wx.adv.TaskBarIcon): - MENU_SETTING,MENU_EXIT,MENU_STARTUP,MENU_UPDATE = wx.NewIdRef(count=4) + MENU_SETTING, MENU_EXIT, MENU_STARTUP, MENU_UPDATE = wx.NewIdRef(count=4) def __init__(self): super().__init__() @@ -26,54 +26,50 @@ def BindEVT(self): self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.onLeftClick) def CreatePopupMenu(self): - self.menu = wx.Menu() - self.menu.Append(self.MENU_SETTING, '设置') - self.menu.Append(self.MENU_STARTUP, '开机自启', kind=wx.ITEM_CHECK) - self.menu.Check(self.MENU_STARTUP, tool.checkStartup("Boss Key Application",Config.file_path)) - self.menu.AppendSeparator() - self.menu.Append(self.MENU_UPDATE, '检查更新') - self.menu.Append(wx.ID_ABOUT, '关于') - self.menu.AppendSeparator() - self.menu.Append(self.MENU_EXIT, '退出') - return self.menu + menu = wx.Menu() + menu.Append(self.MENU_SETTING, '设置') + menu.Append(self.MENU_STARTUP, '开机自启', kind=wx.ITEM_CHECK) + menu.Check(self.MENU_STARTUP, tool.checkStartup("Boss Key Application", Config.file_path)) + menu.AppendSeparator() + menu.Append(self.MENU_UPDATE, '检查更新') + menu.Append(wx.ID_ABOUT, '关于') + menu.AppendSeparator() + menu.Append(self.MENU_EXIT, '退出') + return menu - def onLeftClick(self,e=''): + def onLeftClick(self, e=''): if Config.click_to_hide: - if Config.HotkeyListener!="": + if Config.HotkeyListener != "": Config.HotkeyListener.onHide() - def onStartup(self,e): - if tool.checkStartup("Boss Key Application",Config.file_path): + def onStartup(self, e): + if tool.checkStartup("Boss Key Application", Config.file_path): if tool.removeStartup("Boss Key Application"): - tool.sendNotify(title="开机自启状态变化",message="Boss Key开机自启已关闭") - self.menu.Check(self.MENU_STARTUP,False) + tool.sendNotify(title="开机自启状态变化", message="Boss Key开机自启已关闭") else: - tool.sendNotify(title="开机自启状态变化",message="Boss Key开机自启关闭失败") - self.menu.Check(self.MENU_STARTUP,True) + tool.sendNotify(title="开机自启状态变化", message="Boss Key开机自启关闭失败") else: - if tool.addStartup("Boss Key Application",Config.file_path): - tool.sendNotify(title="开机自启状态变化",message="Boss Key开机自启已开启") - self.menu.Check(self.MENU_STARTUP,True) + if tool.addStartup("Boss Key Application", Config.file_path): + tool.sendNotify(title="开机自启状态变化", message="Boss Key开机自启已开启") else: - tool.sendNotify(title="开机自启状态变化",message="Boss Key开机自启开启失败") - self.menu.Check(self.MENU_STARTUP,False) + tool.sendNotify(title="开机自启状态变化", message="Boss Key开机自启开启失败") - def onSetting(self,e): + def onSetting(self, e): Config.SettingWindow.RefreshLeftList() Config.SettingWindow.Show() - def onAbout(self,e): + def onAbout(self, e): about.AboutWindow().Show() - def onExit(self,e): + def onExit(self, e): Config.HotkeyListener.Close() sys.exit(0) - def onUpdate(self,e): - if Config.UpdateWindow!="": + def onUpdate(self, e): + if Config.UpdateWindow != "": Config.UpdateWindow.Show() else: - Config.UpdateWindow=about.UpdateWindow() + Config.UpdateWindow = about.UpdateWindow() Config.UpdateWindow.Show() def HideIcon(self): From b30e3172e964195869959f930d21d2ac99ac92d1 Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 15:11:20 +0800 Subject: [PATCH 04/15] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/Boss-Key.py | 19 ++++++++++++++++++- main/GUI/setting.py | 32 ++++++++++++++++++++++++++++++++ main/core/config.py | 5 ++++- main/core/listener.py | 24 +++++++++++++++++------- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/main/Boss-Key.py b/main/Boss-Key.py index c5ef6e2..9b979b8 100644 --- a/main/Boss-Key.py +++ b/main/Boss-Key.py @@ -79,5 +79,22 @@ def is_already_running(self,name): Config.SettingWindow=setting.SettingWindow() if Config.first_start: Config.SettingWindow.Show() - app.MainLoop() + # 添加事件处理器清理函数 + def cleanup(): + if Config.HotkeyListener: + Config.HotkeyListener.stop() + # 确保所有窗口在退出前销毁 + if hasattr(Config, 'SettingWindow') and Config.SettingWindow: + if Config.SettingWindow.GetEventHandler() != Config.SettingWindow: + Config.SettingWindow.PopEventHandler(True) + Config.SettingWindow.Destroy() + if hasattr(Config, 'TaskBarIcon') and Config.TaskBarIcon: + Config.TaskBarIcon.Destroy() + + # 注册清理函数 + import atexit + atexit.register(cleanup) + + app.MainLoop() + diff --git a/main/GUI/setting.py b/main/GUI/setting.py index 7827b6b..a09f05a 100644 --- a/main/GUI/setting.py +++ b/main/GUI/setting.py @@ -24,6 +24,7 @@ class SettingWindow(wx.Frame): ID_HIDE_CURRENT_CHECKBOX = wx.NewId() ID_CLICK_TO_HIDE_CHECKBOX = wx.NewId() ID_HIDE_ICON_AFTER_HIDE_CHECKBOX = wx.NewId() + ID_PATH_MATCH_CHECKBOX = wx.NewId() ID_RESET_BTN = wx.NewId() ID_SAVE_BTN = wx.NewId() @@ -148,6 +149,14 @@ def init_UI(self): hide_icon_after_hide_sizer.Add(hide_icon_after_hide_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) settings_checkbox_sizer.Add(hide_icon_after_hide_sizer, proportion=1,flag=wx.EXPAND| wx.ALL, border=10) + path_match_sizer=wx.BoxSizer(wx.HORIZONTAL) + path_match_label = wx.StaticText(panel, label="文件路径匹配") + path_match_label.SetToolTip(wx.ToolTip("启用文件路径匹配可以匹配同一程序的不同窗口")) + path_match_checkbox = wx.CheckBox(panel, self.ID_PATH_MATCH_CHECKBOX, "") + path_match_sizer.Add(path_match_label,proportion=1, flag=wx.EXPAND| wx.ALL) + path_match_sizer.Add(path_match_checkbox,proportion=1, flag=wx.EXPAND| wx.ALL) + settings_checkbox_sizer.Add(path_match_sizer, proportion=1,flag=wx.EXPAND| wx.ALL, border=10) + bottom_sizer.Add(settings_checkbox_sizer, flag=wx.EXPAND| wx.ALL, border=10) #设置提示 @@ -193,6 +202,7 @@ def SetData(self): hide_current_checkbox = self.FindWindowById(self.ID_HIDE_CURRENT_CHECKBOX) click_to_hide_checkbox = self.FindWindowById(self.ID_CLICK_TO_HIDE_CHECKBOX) hide_icon_after_hide_checkbox = self.FindWindowById(self.ID_HIDE_ICON_AFTER_HIDE_CHECKBOX) + path_match_checkbox = self.FindWindowById(self.ID_PATH_MATCH_CHECKBOX) hide_show_hotkey_text.SetValue(Config.hide_hotkey) close_hotkey_text.SetValue(Config.close_hotkey) @@ -201,6 +211,7 @@ def SetData(self): hide_current_checkbox.SetValue(Config.hide_current) click_to_hide_checkbox.SetValue(Config.click_to_hide) hide_icon_after_hide_checkbox.SetValue(Config.hide_icon_after_hide) + path_match_checkbox.SetValue(Config.path_match) self.InsertTreeList(Config.hide_binding, self.right_treelist, True) self.RefreshLeftList() @@ -212,6 +223,7 @@ def OnSave(self, e): hide_current_checkbox = self.FindWindowById(self.ID_HIDE_CURRENT_CHECKBOX) click_to_hide_checkbox = self.FindWindowById(self.ID_CLICK_TO_HIDE_CHECKBOX) hide_icon_after_hide_checkbox = self.FindWindowById(self.ID_HIDE_ICON_AFTER_HIDE_CHECKBOX) + path_match_checkbox = self.FindWindowById(self.ID_PATH_MATCH_CHECKBOX) Config.hide_hotkey = hide_show_hotkey_text.GetValue() Config.close_hotkey = close_hotkey_text.GetValue() @@ -220,6 +232,7 @@ def OnSave(self, e): Config.hide_current = hide_current_checkbox.GetValue() Config.click_to_hide = click_to_hide_checkbox.GetValue() Config.hide_icon_after_hide = hide_icon_after_hide_checkbox.GetValue() + Config.path_match = path_match_checkbox.GetValue() # 获取Windows对象列表 Config.hide_binding = self.ItemsData(self.right_treelist, only_checked=False) @@ -252,6 +265,7 @@ def OnReset(self, e): hide_current_checkbox = self.FindWindowById(self.ID_HIDE_CURRENT_CHECKBOX) click_to_hide_checkbox = self.FindWindowById(self.ID_CLICK_TO_HIDE_CHECKBOX) hide_icon_after_hide_checkbox = self.FindWindowById(self.ID_HIDE_ICON_AFTER_HIDE_CHECKBOX) + path_match_checkbox = self.FindWindowById(self.ID_PATH_MATCH_CHECKBOX) hide_show_hotkey_text.SetValue("Ctrl+Q") close_hotkey_text.SetValue("Win+Esc") @@ -260,6 +274,7 @@ def OnReset(self, e): hide_current_checkbox.SetValue(True) click_to_hide_checkbox.SetValue(False) hide_icon_after_hide_checkbox.SetValue(False) + path_match_checkbox.SetValue(False) self.InsertTreeList([], self.right_treelist, True) self.RefreshLeftList() @@ -291,6 +306,23 @@ def OnRecordSW(self, e): self.recordHotkey(hide_show_hotkey_text, hide_show_hotkey_btn) def OnClose(self, e): + # 确保清除所有的事件处理器 + try: + for child in self.GetChildren(): + # 确保每个子组件的事件处理器被清理 + if hasattr(child, 'PopEventHandler'): + while child.GetEventHandler() != child: + child.PopEventHandler(True) + + # 断开树列表的事件绑定 + if hasattr(self, 'left_treelist') and self.left_treelist: + self.left_treelist.Unbind(dataview.EVT_TREELIST_ITEM_CHECKED) + + if hasattr(self, 'right_treelist') and self.right_treelist: + self.right_treelist.Unbind(dataview.EVT_TREELIST_ITEM_CHECKED) + except Exception as e: + print(f"清理事件处理器时出错: {str(e)}") + self.Hide() def OnRecordCL(self, e): diff --git a/main/core/config.py b/main/core/config.py index 7aefeb1..e9101bc 100644 --- a/main/core/config.py +++ b/main/core/config.py @@ -49,6 +49,7 @@ class Config: click_to_hide = True hide_icon_after_hide = False + path_match = False hide_binding = [] @@ -88,6 +89,7 @@ def load(): Config.send_before_hide = config.get("setting", {}).get("send_before_hide", False) Config.hide_current = config.get("setting", {}).get("hide_current", True) Config.hide_icon_after_hide = config.get("setting", {}).get("hide_icon_after_hide", False) + Config.path_match = config.get("setting", {}).get("path_match", False) Config.click_to_hide= config.get("setting", {}).get("click_to_hide", True) @@ -115,7 +117,8 @@ def save(): 'send_before_hide': Config.send_before_hide, 'hide_current': Config.hide_current, 'click_to_hide': Config.click_to_hide, - 'hide_icon_after_hide': Config.hide_icon_after_hide + 'hide_icon_after_hide': Config.hide_icon_after_hide, + 'path_match': Config.path_match }, # 将WindowInfo对象列表转换为字典列表用于JSON序列化 "hide_binding": [item.to_dict() if isinstance(item, WindowInfo) else item for item in Config.hide_binding] diff --git a/main/core/listener.py b/main/core/listener.py index 42c5ac1..2a190a2 100644 --- a/main/core/listener.py +++ b/main/core/listener.py @@ -7,6 +7,7 @@ import multiprocessing import threading import time +import wx class HotkeyListener(): def __init__(self): @@ -34,18 +35,27 @@ def listenToQueue(self): tool.sendNotify("Boss Key已停止服务", "Boss Key已成功退出") self.ShowWindows() self.stop() + # 先解除所有窗口的事件处理器绑定 try: - Config.SettingWindow.Destroy() - Config.TaskBarIcon.Destroy() - Config.UpdateWindow.Destroy() - except: - pass + if Config.SettingWindow: + for child in Config.SettingWindow.GetChildren(): + if hasattr(child, 'Destroy'): + child.Destroy() + Config.SettingWindow.Destroy() + if Config.TaskBarIcon: + Config.TaskBarIcon.Destroy() + if Config.UpdateWindow: + Config.UpdateWindow.Destroy() + except Exception as e: + print(f"清理窗口时出错: {e}") exit_flag = True break - except: + except Exception as e: + print(f"处理队列消息时出错: {e}") pass if exit_flag: + wx.CallAfter(wx.GetApp().ExitMainLoop) sys.exit(0) def reBind(self): @@ -107,7 +117,7 @@ def HideWindows(self): for i in outer: for j in inner: - if tool.isSameWindow(i, j, False): + if tool.isSameWindow(i, j, False, not Config.path_match): if outer==Config.hide_binding: # 此时i是绑定的元素,j是窗口元素,需要隐藏j needHide.append(j.hwnd) else: From 7b3e171b7468fe5c88a5788ef1e495fcc0d7c4b6 Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 15:18:27 +0800 Subject: [PATCH 05/15] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=87=BA=E9=94=99=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/Boss-Key.py | 31 ++++++++++++++++++-------- main/GUI/setting.py | 52 +++++++++++++++++++++++++++++++++++++------ main/core/listener.py | 41 ++++++++++++++++++++++++---------- 3 files changed, 96 insertions(+), 28 deletions(-) diff --git a/main/Boss-Key.py b/main/Boss-Key.py index 9b979b8..0314278 100644 --- a/main/Boss-Key.py +++ b/main/Boss-Key.py @@ -82,15 +82,28 @@ def is_already_running(self,name): # 添加事件处理器清理函数 def cleanup(): - if Config.HotkeyListener: - Config.HotkeyListener.stop() - # 确保所有窗口在退出前销毁 - if hasattr(Config, 'SettingWindow') and Config.SettingWindow: - if Config.SettingWindow.GetEventHandler() != Config.SettingWindow: - Config.SettingWindow.PopEventHandler(True) - Config.SettingWindow.Destroy() - if hasattr(Config, 'TaskBarIcon') and Config.TaskBarIcon: - Config.TaskBarIcon.Destroy() + try: + # 先停止热键监听 + if hasattr(Config, 'HotkeyListener') and Config.HotkeyListener: + Config.HotkeyListener.stop() + + # 确保所有窗口在退出前安全销毁 + if wx.GetApp() and wx.GetApp().IsMainLoopRunning(): + # 先隐藏所有窗口,避免销毁过程中的可视化问题 + if hasattr(Config, 'SettingWindow') and Config.SettingWindow: + if Config.SettingWindow.IsShown(): + Config.SettingWindow.Hide() + + # 然后按正确顺序销毁 + if hasattr(Config, 'TaskBarIcon') and Config.TaskBarIcon: + Config.TaskBarIcon.Destroy() + Config.TaskBarIcon = None + + if hasattr(Config, 'SettingWindow') and Config.SettingWindow: + Config.SettingWindow.Destroy() + Config.SettingWindow = None + except Exception as e: + print(f"退出清理时出错: {str(e)}") # 注册清理函数 import atexit diff --git a/main/GUI/setting.py b/main/GUI/setting.py index a09f05a..3020f28 100644 --- a/main/GUI/setting.py +++ b/main/GUI/setting.py @@ -284,16 +284,50 @@ def OnToggleCheck(self, e): treelist = e.GetEventObject() item = e.GetItem() is_checked = treelist.GetCheckedState(item) - treelist.CheckItemRecursively(item, is_checked) - # 检查父级是否需要修改状态 + + # 递归设置子节点状态 + self.CheckItemRecursively(treelist, item, is_checked) + + # 更新父节点状态 + self.UpdateParentCheckState(treelist, item) + + def CheckItemRecursively(self, treelist, item, check_state): + """递归设置项目及其子项的选中状态""" + treelist.CheckItem(item, check_state) + + # 处理所有子节点 + child = treelist.GetFirstChild(item) + while child.IsOk(): + self.CheckItemRecursively(treelist, child, check_state) + child = treelist.GetNextSibling(child) + + def UpdateParentCheckState(self, treelist, item): + """更新父节点的选中状态""" parent = treelist.GetItemParent(item) - if parent == treelist.GetRootItem(): - return - else: - if treelist.AreAllChildrenInState(parent, wx.CHK_CHECKED): + if parent != treelist.GetRootItem(): + # 检查所有兄弟节点状态 + all_checked = True + all_unchecked = True + + child = treelist.GetFirstChild(parent) + while child.IsOk(): + state = treelist.GetCheckedState(child) + if state != wx.CHK_CHECKED: + all_checked = False + if state != wx.CHK_UNCHECKED: + all_unchecked = False + child = treelist.GetNextSibling(child) + + # 根据子节点状态设置父节点状态 + if all_checked: treelist.CheckItem(parent, wx.CHK_CHECKED) - elif treelist.AreAllChildrenInState(parent, wx.CHK_UNCHECKED): + elif all_unchecked: treelist.CheckItem(parent, wx.CHK_UNCHECKED) + else: + treelist.CheckItem(parent, wx.CHK_UNDETERMINED) + + # 递归更新上层父节点 + self.UpdateParentCheckState(treelist, parent) def OnSendBeforeHide(self, e): send_before_hide_checkbox = self.FindWindowById(self.ID_SEND_BEFORE_HIDE_CHECKBOX) @@ -368,6 +402,10 @@ def InsertTreeList(self, data: list, treelist: dataview.TreeListCtrl, clear=True treelist.Expand(root) for process in process_map: treelist.Expand(process_map[process]) + + # 初始化所有父节点的选中状态 + for process in process_map: + self.UpdateParentCheckState(treelist, treelist.GetFirstChild(process_map[process])) def SearchProcessNode(self, treelist: dataview.TreeListCtrl, process): item = treelist.GetRootItem() diff --git a/main/core/listener.py b/main/core/listener.py index 2a190a2..ce37ce0 100644 --- a/main/core/listener.py +++ b/main/core/listener.py @@ -27,9 +27,11 @@ def listenToQueue(self): try: msg = self.Queue.get() if msg == "showTaskBarIcon": - Config.TaskBarIcon.ShowIcon() + if hasattr(Config, 'TaskBarIcon') and Config.TaskBarIcon and wx.GetApp(): + wx.CallAfter(Config.TaskBarIcon.ShowIcon) elif msg == "hideTaskBarIcon": - Config.TaskBarIcon.HideIcon() + if hasattr(Config, 'TaskBarIcon') and Config.TaskBarIcon and wx.GetApp(): + wx.CallAfter(Config.TaskBarIcon.HideIcon) elif msg == "closeApp": print("收到关闭消息") tool.sendNotify("Boss Key已停止服务", "Boss Key已成功退出") @@ -37,15 +39,8 @@ def listenToQueue(self): self.stop() # 先解除所有窗口的事件处理器绑定 try: - if Config.SettingWindow: - for child in Config.SettingWindow.GetChildren(): - if hasattr(child, 'Destroy'): - child.Destroy() - Config.SettingWindow.Destroy() - if Config.TaskBarIcon: - Config.TaskBarIcon.Destroy() - if Config.UpdateWindow: - Config.UpdateWindow.Destroy() + if wx.GetApp(): # 确保应用程序仍在运行 + wx.CallAfter(self.cleanup_windows) except Exception as e: print(f"清理窗口时出错: {e}") exit_flag = True @@ -55,9 +50,31 @@ def listenToQueue(self): pass if exit_flag: - wx.CallAfter(wx.GetApp().ExitMainLoop) + if wx.GetApp(): + wx.CallAfter(wx.GetApp().ExitMainLoop) sys.exit(0) + def cleanup_windows(self): + """安全清理窗口资源""" + try: + if hasattr(Config, 'SettingWindow') and Config.SettingWindow: + if Config.SettingWindow.IsShown(): + Config.SettingWindow.Hide() + Config.SettingWindow.Destroy() + Config.SettingWindow = None + + if hasattr(Config, 'UpdateWindow') and Config.UpdateWindow: + if Config.UpdateWindow.IsShown(): + Config.UpdateWindow.Hide() + Config.UpdateWindow.Destroy() + Config.UpdateWindow = None + + if hasattr(Config, 'TaskBarIcon') and Config.TaskBarIcon: + Config.TaskBarIcon.Destroy() + Config.TaskBarIcon = None + except Exception as e: + print(f"清理窗口资源时出错: {str(e)}") + def reBind(self): self.stop() self.BindHotKey() From 2130879e908e3d8b6dbca85a6488b04da3338bab Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 15:32:53 +0800 Subject: [PATCH 06/15] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B8=85=E7=90=86?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/Boss-Key.py | 32 +------------------------------- main/GUI/setting.py | 19 +------------------ main/core/listener.py | 43 ++++++++----------------------------------- 3 files changed, 10 insertions(+), 84 deletions(-) diff --git a/main/Boss-Key.py b/main/Boss-Key.py index 0314278..c5ef6e2 100644 --- a/main/Boss-Key.py +++ b/main/Boss-Key.py @@ -79,35 +79,5 @@ def is_already_running(self,name): Config.SettingWindow=setting.SettingWindow() if Config.first_start: Config.SettingWindow.Show() - - # 添加事件处理器清理函数 - def cleanup(): - try: - # 先停止热键监听 - if hasattr(Config, 'HotkeyListener') and Config.HotkeyListener: - Config.HotkeyListener.stop() - - # 确保所有窗口在退出前安全销毁 - if wx.GetApp() and wx.GetApp().IsMainLoopRunning(): - # 先隐藏所有窗口,避免销毁过程中的可视化问题 - if hasattr(Config, 'SettingWindow') and Config.SettingWindow: - if Config.SettingWindow.IsShown(): - Config.SettingWindow.Hide() - - # 然后按正确顺序销毁 - if hasattr(Config, 'TaskBarIcon') and Config.TaskBarIcon: - Config.TaskBarIcon.Destroy() - Config.TaskBarIcon = None - - if hasattr(Config, 'SettingWindow') and Config.SettingWindow: - Config.SettingWindow.Destroy() - Config.SettingWindow = None - except Exception as e: - print(f"退出清理时出错: {str(e)}") - - # 注册清理函数 - import atexit - atexit.register(cleanup) - app.MainLoop() - + diff --git a/main/GUI/setting.py b/main/GUI/setting.py index 3020f28..5582d56 100644 --- a/main/GUI/setting.py +++ b/main/GUI/setting.py @@ -339,24 +339,7 @@ def OnRecordSW(self, e): hide_show_hotkey_btn = self.FindWindowById(self.ID_HIDE_SHOW_HOTKEY_BTN) self.recordHotkey(hide_show_hotkey_text, hide_show_hotkey_btn) - def OnClose(self, e): - # 确保清除所有的事件处理器 - try: - for child in self.GetChildren(): - # 确保每个子组件的事件处理器被清理 - if hasattr(child, 'PopEventHandler'): - while child.GetEventHandler() != child: - child.PopEventHandler(True) - - # 断开树列表的事件绑定 - if hasattr(self, 'left_treelist') and self.left_treelist: - self.left_treelist.Unbind(dataview.EVT_TREELIST_ITEM_CHECKED) - - if hasattr(self, 'right_treelist') and self.right_treelist: - self.right_treelist.Unbind(dataview.EVT_TREELIST_ITEM_CHECKED) - except Exception as e: - print(f"清理事件处理器时出错: {str(e)}") - + def OnClose(self, e): self.Hide() def OnRecordCL(self, e): diff --git a/main/core/listener.py b/main/core/listener.py index ce37ce0..150f883 100644 --- a/main/core/listener.py +++ b/main/core/listener.py @@ -7,7 +7,6 @@ import multiprocessing import threading import time -import wx class HotkeyListener(): def __init__(self): @@ -27,54 +26,28 @@ def listenToQueue(self): try: msg = self.Queue.get() if msg == "showTaskBarIcon": - if hasattr(Config, 'TaskBarIcon') and Config.TaskBarIcon and wx.GetApp(): - wx.CallAfter(Config.TaskBarIcon.ShowIcon) + Config.TaskBarIcon.ShowIcon() elif msg == "hideTaskBarIcon": - if hasattr(Config, 'TaskBarIcon') and Config.TaskBarIcon and wx.GetApp(): - wx.CallAfter(Config.TaskBarIcon.HideIcon) + Config.TaskBarIcon.HideIcon() elif msg == "closeApp": print("收到关闭消息") tool.sendNotify("Boss Key已停止服务", "Boss Key已成功退出") self.ShowWindows() self.stop() - # 先解除所有窗口的事件处理器绑定 try: - if wx.GetApp(): # 确保应用程序仍在运行 - wx.CallAfter(self.cleanup_windows) - except Exception as e: - print(f"清理窗口时出错: {e}") + Config.SettingWindow.Destroy() + Config.TaskBarIcon.Destroy() + Config.UpdateWindow.Destroy() + except: + pass exit_flag = True break - except Exception as e: - print(f"处理队列消息时出错: {e}") + except: pass if exit_flag: - if wx.GetApp(): - wx.CallAfter(wx.GetApp().ExitMainLoop) sys.exit(0) - def cleanup_windows(self): - """安全清理窗口资源""" - try: - if hasattr(Config, 'SettingWindow') and Config.SettingWindow: - if Config.SettingWindow.IsShown(): - Config.SettingWindow.Hide() - Config.SettingWindow.Destroy() - Config.SettingWindow = None - - if hasattr(Config, 'UpdateWindow') and Config.UpdateWindow: - if Config.UpdateWindow.IsShown(): - Config.UpdateWindow.Hide() - Config.UpdateWindow.Destroy() - Config.UpdateWindow = None - - if hasattr(Config, 'TaskBarIcon') and Config.TaskBarIcon: - Config.TaskBarIcon.Destroy() - Config.TaskBarIcon = None - except Exception as e: - print(f"清理窗口资源时出错: {str(e)}") - def reBind(self): self.stop() self.BindHotKey() From bb795e3a2a049b1c068357202be365222284204b Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 20:50:12 +0800 Subject: [PATCH 07/15] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E6=B8=85=E7=90=86=E4=B8=8D=E5=AE=8C=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/Boss-Key.py | 65 +++++++++++++++++++++++---------------- main/GUI/about.py | 71 ++++++++++++++++++------------------------- main/GUI/record.py | 6 ++-- main/GUI/setting.py | 38 +++++++++++------------ main/GUI/taskbar.py | 13 ++++---- main/core/config.py | 5 ++- main/core/listener.py | 49 +++++++++++++++++++---------- 7 files changed, 132 insertions(+), 115 deletions(-) diff --git a/main/Boss-Key.py b/main/Boss-Key.py index c5ef6e2..6b3b122 100644 --- a/main/Boss-Key.py +++ b/main/Boss-Key.py @@ -18,6 +18,7 @@ import wx from core.config import Config import platform +import atexit if platform.system() == "Windows": if platform.release() == "7": @@ -28,12 +29,20 @@ class APP(wx.App): def __init__(self): wx.App.__init__(self) - + def clean(): + try: + Config.HotkeyListener.Close() + except: + pass + atexit.register(clean) + # 设置语言环境为中文 self.locale = wx.Locale(wx.LANGUAGE_CHINESE_SIMPLIFIED) + self.SetAppName(Config.AppName) self.SetAppDisplayName(Config.AppName) self.SetVendorName(Config.AppAuthor) + lock=os.path.join(os.path.dirname(sys.argv[0]),"Boss-Key.lock") if self.is_already_running(lock): ask=wx.MessageBox("Boss Key 可能已在运行\n点击“确定”继续运行新的Boss-Key程序\n点击“取消”直接关闭此窗口","Boss Key", wx.OK | wx.ICON_INFORMATION | wx.CANCEL | wx.CANCEL_DEFAULT) @@ -47,37 +56,41 @@ def write_pid(self,name): with open(name, "w") as f: f.write(str(psutil.Process().pid)) - def is_already_running(self,name): - if os.path.exists(name): - with open(name, "r") as f: - pid=f.read() - if pid == "": + def is_already_running(self, name): + if not os.path.exists(name): + self.write_pid(name) + return False + + with open(name, "r") as f: + pid = f.read() + + if pid == "": + self.write_pid(name) + return False + + try: + process = psutil.Process(int(pid)) + if not process.is_running(): self.write_pid(name) - else: - try: - process=psutil.Process(int(pid)) - if process.is_running(): - this_name=psutil.Process(psutil.Process().pid).name() #获取当前进程名 - if this_name==process.name(): - return True - else: - self.write_pid(name) - return False - else: - self.write_pid(name) - return False - except: - self.write_pid(name) - return False - else: + return False + + this_name = psutil.Process(psutil.Process().pid).name() # 获取当前进程名 + if this_name == process.name(): + return True + + self.write_pid(name) + return False + except: self.write_pid(name) + return False + if __name__ == '__main__': app = APP() + Config.SettingWindowId = wx.NewIdRef() Config.TaskBarIcon=taskbar.TaskBarIcon() Config.HotkeyListener=listener.HotkeyListener() - Config.SettingWindow=setting.SettingWindow() + setting.SettingWindow(Config.SettingWindowId) if Config.first_start: - Config.SettingWindow.Show() + wx.FindWindowById(Config.SettingWindowId).Show() app.MainLoop() - diff --git a/main/GUI/about.py b/main/GUI/about.py index fcc5798..64187cd 100644 --- a/main/GUI/about.py +++ b/main/GUI/about.py @@ -22,17 +22,8 @@ def Show(self): wx.adv.AboutBox(self.info) class UpdateWindow(wx.Dialog): - # 定义组件ID常量 - ID_ACTIVITY_INDICATOR = wx.NewId() - ID_INFO_TEXT = wx.NewId() - ID_CURRENT_VERSION = wx.NewId() - ID_NEW_VERSION = wx.NewId() - ID_RELEASE_TIME = wx.NewId() - ID_INFO_LABEL = wx.NewId() - ID_INFO_TEXT_CTRL = wx.NewId() - - def __init__(self): - super().__init__(None, title="检查更新 - Boss Key", style=wx.DEFAULT_DIALOG_STYLE | wx.STAY_ON_TOP | wx.RESIZE_BORDER) + def __init__(self,id=None): + super().__init__(None,id=id, title="检查更新 - Boss Key", style=wx.DEFAULT_DIALOG_STYLE | wx.STAY_ON_TOP | wx.RESIZE_BORDER) self.SetIcon(wx.Icon(wx.Image(Config.icon).ConvertToBitmap())) self.init_Load_UI() @@ -44,34 +35,34 @@ def __init__(self): self.onCheckUpdate() def init_Load_UI(self): - panel = wx.Panel(self) - sizer = wx.BoxSizer(wx.VERTICAL) + self.panel = wx.Panel(self) + self.sizer = wx.BoxSizer(wx.VERTICAL) - ai = wx.ActivityIndicator(panel, id=self.ID_ACTIVITY_INDICATOR) - ai.Start() + self.ai=wx.ActivityIndicator(self.panel) + self.ai.Start() - text = wx.StaticText(panel, id=self.ID_INFO_TEXT, label="正在获取版本信息,请稍后...") + text = wx.StaticText(self.panel, label="正在获取版本信息,请稍后...") # 使元素完全居中 - sizer.AddStretchSpacer(3) - sizer.Add(ai, 0, wx.ALIGN_CENTER) - sizer.AddStretchSpacer() - sizer.Add(text, 0, wx.ALIGN_CENTER) - sizer.AddStretchSpacer(3) - panel.SetSizer(sizer) + self.sizer.AddStretchSpacer(3) + self.sizer.Add(self.ai, 0, wx.ALIGN_CENTER) + self.sizer.AddStretchSpacer() + self.sizer.Add(text, 0, wx.ALIGN_CENTER) + self.sizer.AddStretchSpacer(3) + self.panel.SetSizer(self.sizer) def onCheckUpdate(self): - def checkUpdate(): + def check(): try: info = checkUpdate() except: wx.CallAfter(self.init_error_UI) return - wx.CallAfter(self.init_real_UI, info) - threading.Thread(target=checkUpdate).start() + wx.CallAfter(self.init_real_UI,info) + threading.Thread(target=check).start() - def init_real_UI(self, info): + def init_real_UI(self,info): ## 清空原有元素 self.sizer.Clear() self.ai.Stop() @@ -82,17 +73,17 @@ def init_real_UI(self, info): ## 重绘UI - current_version = wx.StaticText(self.panel, id=self.ID_CURRENT_VERSION, label="当前版本:"+Config.AppVersion) + current_version = wx.StaticText(self.panel, label="当前版本:"+Config.AppVersion) self.sizer.Add(current_version, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) - new_version = wx.StaticText(self.panel, id=self.ID_NEW_VERSION, label="最新版本:"+info['tag_name']) + new_version = wx.StaticText(self.panel, label="最新版本:"+info['tag_name']) self.sizer.Add(new_version, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) - release_time = wx.StaticText(self.panel, id=self.ID_RELEASE_TIME, label="发布时间:"+datetime.datetime.strftime(info['published_at'], "%Y-%m-%d %H:%M:%S")) + release_time = wx.StaticText(self.panel, label="发布时间:"+datetime.datetime.strftime(info['published_at'], "%Y-%m-%d %H:%M:%S")) self.sizer.Add(release_time, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) - info_label = wx.StaticText(self.panel, id=self.ID_INFO_LABEL, label="更新内容:") - info_text = wx.TextCtrl(self.panel, id=self.ID_INFO_TEXT_CTRL, style=wx.TE_MULTILINE | wx.TE_READONLY) + info_label = wx.StaticText(self.panel, label="更新内容:") + info_text = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE | wx.TE_READONLY) if Config.AppVersion == info['tag_name']: addtion_info = "您的版本已经是最新版本 :) \n\n" else: @@ -102,10 +93,9 @@ def init_real_UI(self, info): self.sizer.Add(info_text, 1, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) button_sizer = wx.BoxSizer(wx.VERTICAL) - for i, asset in enumerate(info['assets']): - download_button = wx.Button(self.panel, id=wx.NewId(), label=asset['name']) - # 使用lambda捕获当前值 - download_button.Bind(wx.EVT_BUTTON, lambda e, url=asset['browser_download_url'], is_latest=(Config.AppVersion == info['tag_name']): self.Btn_click(url, is_latest)) + for i in info['assets']: + download_button = wx.Button(self.panel, label=i['name']) + download_button.Bind(wx.EVT_BUTTON, lambda e: self.Btn_click(i['browser_download_url'],Config.AppVersion == info['tag_name'])) button_sizer.Add(download_button, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) self.sizer.Add(button_sizer, 0, wx.ALIGN_LEFT|wx.EXPAND|wx.ALL, 5) @@ -114,15 +104,14 @@ def init_real_UI(self, info): self.sizer.Layout() def init_error_UI(self): - if self.FindWindowById(self.ID_ACTIVITY_INDICATOR): - self.FindWindowById(self.ID_ACTIVITY_INDICATOR).Stop() - wx.MessageBox("无法连接至更新服务器,情稍后再试", "检查更新失败", wx.OK | wx.ICON_ERROR) + self.ai.Stop() + wx.MessageBox("无法连接至更新服务器,情稍后再试","检查更新失败",wx.OK | wx.ICON_ERROR) self.Close() - def Btn_click(self, url, is_latest): + def Btn_click(self,url,is_latest): if is_latest: - ask = wx.MessageBox("您的版本已经是最新版本,是否仍前往下载", "无需更新", wx.OK | wx.ICON_INFORMATION | wx.CANCEL | wx.CANCEL_DEFAULT) + ask=wx.MessageBox("您的版本已经是最新版本,是否仍前往下载","无需更新",wx.OK | wx.ICON_INFORMATION | wx.CANCEL | wx.CANCEL_DEFAULT) if ask == wx.CANCEL: return webbrowser.open(url) - self.Hide() + self.Hide() \ No newline at end of file diff --git a/main/GUI/record.py b/main/GUI/record.py index 018ce96..fd135e8 100644 --- a/main/GUI/record.py +++ b/main/GUI/record.py @@ -13,9 +13,9 @@ class RecordedHotkey: class RecordWindow(wx.Dialog): # 定义组件ID常量 - ID_CONFIRM_BTN = wx.NewId() - ID_STATUS_TEXT = wx.NewId() - ID_KEY_TEXT = wx.NewId() + ID_CONFIRM_BTN = wx.NewIdRef() + ID_STATUS_TEXT = wx.NewIdRef() + ID_KEY_TEXT = wx.NewIdRef() def __init__(self): wx.Dialog.__init__(self, None, title="录制热键 - Boss Key", style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) diff --git a/main/GUI/setting.py b/main/GUI/setting.py index 5582d56..63d59e0 100644 --- a/main/GUI/setting.py +++ b/main/GUI/setting.py @@ -10,26 +10,26 @@ class SettingWindow(wx.Frame): # 定义组件ID常量 - ID_LEFT_TREELIST = wx.NewId() - ID_RIGHT_TREELIST = wx.NewId() - ID_ADD_BINDING_BTN = wx.NewId() - ID_REMOVE_BINDING_BTN = wx.NewId() - ID_REFRESH_BTN = wx.NewId() - ID_HIDE_SHOW_HOTKEY_TEXT = wx.NewId() - ID_HIDE_SHOW_HOTKEY_BTN = wx.NewId() - ID_CLOSE_HOTKEY_TEXT = wx.NewId() - ID_CLOSE_HOTKEY_BTN = wx.NewId() - ID_MUTE_AFTER_HIDE_CHECKBOX = wx.NewId() - ID_SEND_BEFORE_HIDE_CHECKBOX = wx.NewId() - ID_HIDE_CURRENT_CHECKBOX = wx.NewId() - ID_CLICK_TO_HIDE_CHECKBOX = wx.NewId() - ID_HIDE_ICON_AFTER_HIDE_CHECKBOX = wx.NewId() - ID_PATH_MATCH_CHECKBOX = wx.NewId() - ID_RESET_BTN = wx.NewId() - ID_SAVE_BTN = wx.NewId() + ID_LEFT_TREELIST = wx.NewIdRef() + ID_RIGHT_TREELIST = wx.NewIdRef() + ID_ADD_BINDING_BTN = wx.NewIdRef() + ID_REMOVE_BINDING_BTN = wx.NewIdRef() + ID_REFRESH_BTN = wx.NewIdRef() + ID_HIDE_SHOW_HOTKEY_TEXT = wx.NewIdRef() + ID_HIDE_SHOW_HOTKEY_BTN = wx.NewIdRef() + ID_CLOSE_HOTKEY_TEXT = wx.NewIdRef() + ID_CLOSE_HOTKEY_BTN = wx.NewIdRef() + ID_MUTE_AFTER_HIDE_CHECKBOX = wx.NewIdRef() + ID_SEND_BEFORE_HIDE_CHECKBOX = wx.NewIdRef() + ID_HIDE_CURRENT_CHECKBOX = wx.NewIdRef() + ID_CLICK_TO_HIDE_CHECKBOX = wx.NewIdRef() + ID_HIDE_ICON_AFTER_HIDE_CHECKBOX = wx.NewIdRef() + ID_PATH_MATCH_CHECKBOX = wx.NewIdRef() + ID_RESET_BTN = wx.NewIdRef() + ID_SAVE_BTN = wx.NewIdRef() - def __init__(self): - super().__init__(None, title="设置 - Boss Key", style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER) + def __init__(self,id=None): + super().__init__(None,id=id, title="设置 - Boss Key", style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER) self.SetIcon(wx.Icon(wx.Image(Config.icon).ConvertToBitmap())) self.init_UI() diff --git a/main/GUI/taskbar.py b/main/GUI/taskbar.py index f4c5143..01f3f46 100644 --- a/main/GUI/taskbar.py +++ b/main/GUI/taskbar.py @@ -55,22 +55,21 @@ def onStartup(self, e): tool.sendNotify(title="开机自启状态变化", message="Boss Key开机自启开启失败") def onSetting(self, e): - Config.SettingWindow.RefreshLeftList() - Config.SettingWindow.Show() + window=wx.FindWindowById(Config.SettingWindowId) + window.RefreshLeftList() + window.Show() def onAbout(self, e): about.AboutWindow().Show() def onExit(self, e): Config.HotkeyListener.Close() - sys.exit(0) def onUpdate(self, e): - if Config.UpdateWindow != "": - Config.UpdateWindow.Show() + if Config.UpdateWindowId != -1: + wx.FindWindowById(Config.UpdateWindowId).Show() else: - Config.UpdateWindow = about.UpdateWindow() - Config.UpdateWindow.Show() + about.UpdateWindow(Config.UpdateWindowId).Show() def HideIcon(self): wx.CallAfter(self.RemoveIcon) diff --git a/main/core/config.py b/main/core/config.py index e9101bc..d4a31bf 100644 --- a/main/core/config.py +++ b/main/core/config.py @@ -59,11 +59,10 @@ class Config: # 判断是否为首次启动 first_start = not os.path.exists(config_path) - SettingWindow="" TaskBarIcon="" - UpdateWindow="" - HotkeyListener= "" + SettingWindowId = -1 + UpdateWindowId = -1 recording_hotkey = False recorded_hotkey = None diff --git a/main/core/listener.py b/main/core/listener.py index 150f883..1a40a21 100644 --- a/main/core/listener.py +++ b/main/core/listener.py @@ -7,6 +7,7 @@ import multiprocessing import threading import time +import wx class HotkeyListener(): def __init__(self): @@ -18,6 +19,7 @@ def __init__(self): self.Queue = multiprocessing.Queue() self.listener = None self.reBind() + self.end_flag=False threading.Thread(target=self.listenToQueue,daemon=True).start() def listenToQueue(self): @@ -26,37 +28,48 @@ def listenToQueue(self): try: msg = self.Queue.get() if msg == "showTaskBarIcon": - Config.TaskBarIcon.ShowIcon() + wx.CallAfter(Config.TaskBarIcon.ShowIcon()) elif msg == "hideTaskBarIcon": - Config.TaskBarIcon.HideIcon() + wx.CallAfter(Config.TaskBarIcon.HideIcon()) elif msg == "closeApp": print("收到关闭消息") - tool.sendNotify("Boss Key已停止服务", "Boss Key已成功退出") self.ShowWindows() - self.stop() + tool.sendNotify("Boss Key已停止服务", "Boss Key已成功退出") + self._stop() try: - Config.SettingWindow.Destroy() + wx.FindWindowById(Config.SettingWindowId).Destroy() Config.TaskBarIcon.Destroy() - Config.UpdateWindow.Destroy() - except: + wx.FindWindowById(Config.UpdateWindowId).Destroy() + except Exception as e: + print(e) pass exit_flag = True break except: pass - - if exit_flag: - sys.exit(0) + finally: + if exit_flag: + sys.exit(0) def reBind(self): - self.stop() + self._stop() self.BindHotKey() def ListenerProcess(self,hotkey): - with keyboard.GlobalHotKeys(hotkey) as listener: - while True: #避免意外退出 - listener.join() - print("线程意外退出") + try: + with keyboard.GlobalHotKeys(hotkey) as listener: + self.end_flag = False + while listener.running and not self.end_flag: + time.sleep(0.1) # 减少CPU使用率 + + # 如果是因为 end_flag 退出但监听器仍在运行 + if listener.running and self.end_flag: + listener.stop() + + print("热键监听已停止") + except Exception as e: + self.ShowWindows(False) + print(f"热键监听出错: {e}") def BindHotKey(self): hotkeys = { @@ -136,8 +149,12 @@ def HideWindows(self): def Close(self,e=""): self.Queue.put("closeApp") - def stop(self): + def _stop(self): + """ + 直接关闭listener,应该使用Close + """ if self.listener is not None: + self.end_flag=True try: self.listener.terminate() self.listener.join() From 6b152a03c8fa102f021ef949c3f14c4ffa9aeeb7 Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 20:52:36 +0800 Subject: [PATCH 08/15] =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/core/tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/main/core/tools.py b/main/core/tools.py index bd6cef8..9c0322f 100644 --- a/main/core/tools.py +++ b/main/core/tools.py @@ -199,7 +199,6 @@ def isSameWindow(w1:WindowInfo, w2:WindowInfo, auto=False, strict=True): PID_same = w1.PID == w2.PID process_same = process_name_same or PID_same - print(w1.process) ## 非严格模式下 if not strict: ## 进程名称、路径相同则同一个 From f52e90a2a298fa5d19dc438c8a4ed0ee11775fd8 Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 22:01:53 +0800 Subject: [PATCH 09/15] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/GUI/setting.py | 8 ++--- main/GUI/taskbar.py | 16 ++++++++- main/GUI/window_restore.py | 73 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 main/GUI/window_restore.py diff --git a/main/GUI/setting.py b/main/GUI/setting.py index 63d59e0..4bab24d 100644 --- a/main/GUI/setting.py +++ b/main/GUI/setting.py @@ -1,10 +1,8 @@ import wx import wx.dataview as dataview -import wx.dataview from core.config import Config import GUI.record as record import core.tools as tool -import json import wx.lib.buttons as buttons from core.model import WindowInfo @@ -52,7 +50,7 @@ def init_UI(self): # 左边列表 left_staticbox = wx.StaticBox(panel, label="现有窗口进程") left_sizer = wx.StaticBoxSizer(left_staticbox, wx.VERTICAL) - self.left_treelist = dataview.TreeListCtrl(panel, self.ID_LEFT_TREELIST, style=wx.dataview.TL_CHECKBOX) + self.left_treelist = dataview.TreeListCtrl(panel, self.ID_LEFT_TREELIST, style=dataview.TL_CHECKBOX) self.left_treelist.AppendColumn('窗口标题', width=300) self.left_treelist.AppendColumn('窗口句柄', width=100) self.left_treelist.AppendColumn('进程PID', width=150) @@ -71,7 +69,7 @@ def init_UI(self): # 右边列表 right_staticbox = wx.StaticBox(panel, label="已绑定进程") right_sizer = wx.StaticBoxSizer(right_staticbox, wx.VERTICAL) - self.right_treelist = dataview.TreeListCtrl(panel, self.ID_RIGHT_TREELIST, style=wx.dataview.TL_CHECKBOX) + self.right_treelist = dataview.TreeListCtrl(panel, self.ID_RIGHT_TREELIST, style=dataview.TL_CHECKBOX) self.right_treelist.AppendColumn('窗口标题', width=300) self.right_treelist.AppendColumn('窗口句柄', width=100) self.right_treelist.AppendColumn('进程PID', width=150) @@ -419,7 +417,7 @@ def RemoveItem(self, treelist: dataview.TreeListCtrl, data): # 如果没有子节点了,删除父节点 treelist.DeleteItem(node) - def ItemsData(self, treelist: dataview.TreeListCtrl, only_checked=False, item_object=False): + def ItemsData(self, treelist: dataview.TreeListCtrl, only_checked=False, item_object=False)->list[WindowInfo]: res = [] item = treelist.GetRootItem() while item.IsOk(): diff --git a/main/GUI/taskbar.py b/main/GUI/taskbar.py index 01f3f46..ef6b75c 100644 --- a/main/GUI/taskbar.py +++ b/main/GUI/taskbar.py @@ -2,11 +2,13 @@ from core.config import Config import core.tools as tool from GUI import about +from GUI.window_restore import WindowRestoreDialog import sys class TaskBarIcon(wx.adv.TaskBarIcon): - MENU_SETTING, MENU_EXIT, MENU_STARTUP, MENU_UPDATE = wx.NewIdRef(count=4) + MENU_SETTING, MENU_EXIT, MENU_STARTUP, MENU_UPDATE, MENU_RESTORE = wx.NewIdRef(count=5) + ID_RESTORE = wx.NewIdRef(count=1) def __init__(self): super().__init__() @@ -22,6 +24,7 @@ def BindEVT(self): self.Bind(wx.EVT_MENU, self.onExit, id=self.MENU_EXIT) self.Bind(wx.EVT_MENU, self.onAbout, id=wx.ID_ABOUT) self.Bind(wx.EVT_MENU, self.onUpdate, id=self.MENU_UPDATE) + self.Bind(wx.EVT_MENU, self.onRestore, id=self.MENU_RESTORE) # 绑定任务栏图标单击事件 self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.onLeftClick) @@ -31,6 +34,7 @@ def CreatePopupMenu(self): menu.Append(self.MENU_STARTUP, '开机自启', kind=wx.ITEM_CHECK) menu.Check(self.MENU_STARTUP, tool.checkStartup("Boss Key Application", Config.file_path)) menu.AppendSeparator() + menu.Append(self.MENU_RESTORE, '窗口恢复工具') menu.Append(self.MENU_UPDATE, '检查更新') menu.Append(wx.ID_ABOUT, '关于') menu.AppendSeparator() @@ -71,6 +75,16 @@ def onUpdate(self, e): else: about.UpdateWindow(Config.UpdateWindowId).Show() + def onRestore(self, e): + """显示窗口恢复对话框""" + dialog = wx.FindWindowById(self.ID_RESTORE) + if dialog is None: + WindowRestoreDialog(self.ID_RESTORE).Show() + else: + dialog.Restore() + dialog.Raise() + dialog.RefreshLeftList() + def HideIcon(self): wx.CallAfter(self.RemoveIcon) diff --git a/main/GUI/window_restore.py b/main/GUI/window_restore.py new file mode 100644 index 0000000..f4cccbc --- /dev/null +++ b/main/GUI/window_restore.py @@ -0,0 +1,73 @@ +import wx +import wx.dataview as dataview +import win32gui +import win32con +from .setting import SettingWindow +from core import tools as tool + +class WindowRestoreDialog(SettingWindow): + def __init__(self, id): + super().__init__(id=id) + self.SetSize((700, 600)) + + self.SetTitle("窗口恢复") + + self.Center() + + def init_UI(self): + self.window_info = [] + + # 创建界面 + panel = wx.Panel(self) + vbox = wx.BoxSizer(wx.VERTICAL) + + # 树形列表 + self.left_treelist = dataview.TreeListCtrl(panel, style=dataview.TL_CHECKBOX) + self.left_treelist.AppendColumn('窗口标题', width=300) + self.left_treelist.AppendColumn('窗口句柄', width=100) + self.left_treelist.AppendColumn('进程PID', width=150) + + # 按钮区域 + btn_sizer = wx.BoxSizer(wx.HORIZONTAL) + self.show_btn = wx.Button(panel, label="显示窗口") + self.hide_btn = wx.Button(panel, label="隐藏窗口") + btn_sizer.Add(self.show_btn, proportion=1, flag=wx.RIGHT, border=5) + btn_sizer.Add(self.hide_btn, proportion=1, flag=wx.LEFT, border=5) + + # 布局 + vbox.Add(self.left_treelist, proportion=1, flag=wx.EXPAND|wx.LEFT|wx.RIGHT, border=5) + vbox.Add(btn_sizer, flag=wx.EXPAND|wx.ALL, border=10) + + panel.SetSizer(vbox) + + def SetData(self): + self.RefreshLeftList() + + def RefreshLeftList(self, e=None): + windows = tool.getAllWindows() + list = [] + for window in windows: + list.append(window) + self.InsertTreeList(list, self.left_treelist, True) + + def Bind_EVT(self): + self.show_btn.Bind(wx.EVT_BUTTON, self.on_show_window) + self.hide_btn.Bind(wx.EVT_BUTTON, self.on_hide_window) + self.left_treelist.Bind(dataview.EVT_TREELIST_ITEM_CHECKED, self.OnToggleCheck) + + def on_show_window(self,e=None): + windows = self.ItemsData(self.left_treelist, only_checked=True) + result = wx.MessageBox(f"将恢复{len(windows)}个窗口\r\n恢复未知的窗口可能会导致窗口出错", "警告", wx.OK | wx.CANCEL | wx.ICON_WARNING) + if result != wx.OK: + return + for window in windows: + win32gui.ShowWindow(window.hwnd, win32con.SW_SHOW) + + def on_hide_window(self,e=None): + windows = self.ItemsData(self.left_treelist, only_checked=True) + result = wx.MessageBox(f"将隐藏{len(windows)}个窗口\r\n隐藏未知的窗口可能会导致窗口出错", "警告", wx.OK | wx.CANCEL | wx.ICON_WARNING) + if result != wx.OK: + return + for window in windows: + win32gui.ShowWindow(window.hwnd, win32con.SW_HIDE) + \ No newline at end of file From 2cfa86cdfc80dadb564b60b25c5b65a507b7f260 Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 22:40:59 +0800 Subject: [PATCH 10/15] =?UTF-8?q?=E6=96=B0=E5=A2=9Ewin7=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-win7.yml | 109 +++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 .github/workflows/build-win7.yml diff --git a/.github/workflows/build-win7.yml b/.github/workflows/build-win7.yml new file mode 100644 index 0000000..233bdcc --- /dev/null +++ b/.github/workflows/build-win7.yml @@ -0,0 +1,109 @@ +name: Windows 7 Build + +on: + pull_request: + branches: + - main + workflow_dispatch: # 允许手动触发工作流 + +jobs: + build-main-files: + runs-on: windows-2019 # 使用Windows 2019运行器(更接近Windows 7环境) + + steps: + # 检出仓库代码 + - name: Checkout repository + uses: actions/checkout@v3 + + # 设置 Python 3.8 环境(更适合Windows 7) + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: '3.8' + + # 安装依赖和 Nuitka + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + python --version + shell: cmd + + # 进行编译(添加Windows 7兼容性选项) + - name: Run Compile + run: | + python -m nuitka --windows-file-version=0.0.0.0 --windows-disable-console --windows-company-name="IvanHanloth" --windows-compatibility-vista --show-progress --mingw64 --assume-yes-for-downloads --output-dir=out main/Boss-Key.py + + # 添加版本信息到可执行文件并重命名 + - name: Add version information and rename executable + shell: pwsh + run: | + # 获取标签名作为版本号 + $tag = 'Win7-Build' + # 定义新的文件名 + $newExeName = "Boss-Key-$tag-onefile.exe" + + # 重命名可执行文件 + Rename-Item "out/Boss-Key.exe" $newExeName + + # 压缩out/Boss-Key.dist/文件夹 + Compress-Archive -Path "out/Boss-Key.dist" -DestinationPath "out/Boss-Key-$tag-multifile.zip" + + ls out + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: Win7-Build + path: | + out/Boss-Key.dist + +# deploy-test: +# environment: +# name: win7-buildup-test +# runs-on: windows-2019 +# needs: build-main-files +# steps: +# - name: Download artifact +# uses: actions/download-artifact@v4 +# with: +# name: Win7-Build +# path: ./test + +# - name: Test Main Files +# run: | +# cd ./test +# dir +# ./Boss-Key.exe + + compile-to-installer: + runs-on: windows-2019 + # needs: deploy-test + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Clone Repo + run: | + cd ./.github/inno-script + git clone https://github.com/IvanHanloth/innosetup-action.git + cp -r innosetup-action/* . + dir + + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: Win7-Build + path: ./.github/inno-script/software + + - name: Compile Installer + run: | + dir + .\ISCC.exe /DMyAppVersion='Win7-Build' Boss-Key.iss + working-directory: .github/inno-script + + - name: Upload Installer + uses: actions/upload-artifact@v4 + with: + name: Boss-Key-Win7-Installer + path: .github/inno-script/output/Boss-Key-Win7-Build-Installer.exe From 41e8c8e2f2bcc4c17e2f85a9280ec830264ed7fd Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 22:41:33 +0800 Subject: [PATCH 11/15] =?UTF-8?q?v2.0.3=E7=89=88=E6=9C=AC=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 15 ++++++++++++++- main/core/config.py | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a049f33..b200d65 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ - installer - 安装程序(推荐),完整封装的Boss-Key程序安装程序,提供一键安装、更新、卸载,可以更高效的管理Boss-Key程序 + + ### 基础使用 安装或更新后首次打开Boss-Key,会自动弹出设置页面,可以在其中进行热键修改、进程及窗口绑定的等操作。 @@ -75,6 +77,10 @@ 注意!此功能仍在测试,启用后会导致窗口隐藏出现延时 +#### 文件路径匹配功能 +Boss Key默认使用窗口标题等多因素进行窗口匹配以确保精确隐藏窗口,但是有时可能出现希望根据可执行文件路径来进行匹配: +即只要是由同一个程序启动的窗口都隐藏,此时就可以启用“文件路径匹配”选项进行模糊匹配 + #### 同时隐藏当前活动窗口 启用该功能后,按下隐藏窗口热键时,除了会隐藏绑定的窗口,还会隐藏当前被激活的窗口。 @@ -118,7 +124,8 @@ Boss-Key │   │   ├── about.py 关于页面 │   │   ├── record.py 录制热键页面 │   │   ├── setting.py 设置页面 -│   │   └── taskbar.py 托盘图标 +│   │   ├── taskbar.py 托盘图标 +│   │   └── window_restore.py 窗口恢复工具页面 │   └── Boss-Key.py 项目入口文件 ├── src 网站相关目录 │   └── static 静态文件目录 @@ -134,6 +141,11 @@ Boss-Key - 无法隐藏部分游戏窗口,可能由于游戏窗口加密导致 ## 更新日志 +**V2.0.3 (更新于2025/4/4)** +- 新增文件路径匹配选项 +- 新增窗口恢复工具 +- 修复状态列表问题 +- 优化运行速度和占用 **V2.0.2 (更新于2025/2/2)** - 新增全选窗口选项 @@ -141,6 +153,7 @@ Boss-Key - 修复无法使用热键关闭程序的问题 - 优化界面UI设计 - 优化配置文件读取 + **V2.0.1 (更新于2025/1/17)** - 修改已在运行时的提醒 - 修复窗口销毁后再打开隐藏失败的问题 diff --git a/main/core/config.py b/main/core/config.py index d4a31bf..8df198f 100644 --- a/main/core/config.py +++ b/main/core/config.py @@ -8,8 +8,8 @@ class Config: AppName = "Boss Key" - AppVersion = "v2.0.2.0" - AppReleaseDate = "2025-02-02" + AppVersion = "v2.0.3.0" + AppReleaseDate = "2025-04-04" AppAuthor = "IvanHanloth" AppDescription = "老板来了?快用Boss-Key老板键一键隐藏静音当前窗口!上班摸鱼必备神器" AppCopyRight = "Copyright © 2022-2025 Ivan Hanloth All Rights Reserved." From 18a2798c6ff3a48bf6e7ae55f071ce706de8a6a2 Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Fri, 4 Apr 2025 22:50:44 +0800 Subject: [PATCH 12/15] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-win7.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-win7.yml b/.github/workflows/build-win7.yml index 233bdcc..1cc20d8 100644 --- a/.github/workflows/build-win7.yml +++ b/.github/workflows/build-win7.yml @@ -32,7 +32,7 @@ jobs: # 进行编译(添加Windows 7兼容性选项) - name: Run Compile run: | - python -m nuitka --windows-file-version=0.0.0.0 --windows-disable-console --windows-company-name="IvanHanloth" --windows-compatibility-vista --show-progress --mingw64 --assume-yes-for-downloads --output-dir=out main/Boss-Key.py + python -m nuitka --windows-file-version=0.0.0.0 --windows-disable-console --windows-company-name="IvanHanloth" --show-progress --mingw64 --assume-yes-for-downloads --output-dir=out main/Boss-Key.py # 添加版本信息到可执行文件并重命名 - name: Add version information and rename executable @@ -79,6 +79,7 @@ jobs: compile-to-installer: runs-on: windows-2019 # needs: deploy-test + needs: build-main-files steps: - name: Checkout code uses: actions/checkout@v2 From 1593b43c033e0a7edaa27a90246b8a845d382fc4 Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Sat, 5 Apr 2025 12:16:16 +0800 Subject: [PATCH 13/15] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81=EF=BC=8C=E6=96=B0=E5=A2=9E=E5=88=9B=E5=BB=BA=E5=85=AC?= =?UTF-8?q?=E5=91=8A=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-win7.yml | 110 ------------------------------ .github/workflows/tag-release.yml | 1 + README.md | 7 +- main/core/config.py | 2 +- 4 files changed, 6 insertions(+), 114 deletions(-) delete mode 100644 .github/workflows/build-win7.yml diff --git a/.github/workflows/build-win7.yml b/.github/workflows/build-win7.yml deleted file mode 100644 index 1cc20d8..0000000 --- a/.github/workflows/build-win7.yml +++ /dev/null @@ -1,110 +0,0 @@ -name: Windows 7 Build - -on: - pull_request: - branches: - - main - workflow_dispatch: # 允许手动触发工作流 - -jobs: - build-main-files: - runs-on: windows-2019 # 使用Windows 2019运行器(更接近Windows 7环境) - - steps: - # 检出仓库代码 - - name: Checkout repository - uses: actions/checkout@v3 - - # 设置 Python 3.8 环境(更适合Windows 7) - - name: Set up Python 3.8 - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - # 安装依赖和 Nuitka - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - python --version - shell: cmd - - # 进行编译(添加Windows 7兼容性选项) - - name: Run Compile - run: | - python -m nuitka --windows-file-version=0.0.0.0 --windows-disable-console --windows-company-name="IvanHanloth" --show-progress --mingw64 --assume-yes-for-downloads --output-dir=out main/Boss-Key.py - - # 添加版本信息到可执行文件并重命名 - - name: Add version information and rename executable - shell: pwsh - run: | - # 获取标签名作为版本号 - $tag = 'Win7-Build' - # 定义新的文件名 - $newExeName = "Boss-Key-$tag-onefile.exe" - - # 重命名可执行文件 - Rename-Item "out/Boss-Key.exe" $newExeName - - # 压缩out/Boss-Key.dist/文件夹 - Compress-Archive -Path "out/Boss-Key.dist" -DestinationPath "out/Boss-Key-$tag-multifile.zip" - - ls out - - - name: Upload Artifacts - uses: actions/upload-artifact@v4 - with: - name: Win7-Build - path: | - out/Boss-Key.dist - -# deploy-test: -# environment: -# name: win7-buildup-test -# runs-on: windows-2019 -# needs: build-main-files -# steps: -# - name: Download artifact -# uses: actions/download-artifact@v4 -# with: -# name: Win7-Build -# path: ./test - -# - name: Test Main Files -# run: | -# cd ./test -# dir -# ./Boss-Key.exe - - compile-to-installer: - runs-on: windows-2019 - # needs: deploy-test - needs: build-main-files - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Clone Repo - run: | - cd ./.github/inno-script - git clone https://github.com/IvanHanloth/innosetup-action.git - cp -r innosetup-action/* . - dir - - - name: Download artifact - uses: actions/download-artifact@v4 - with: - name: Win7-Build - path: ./.github/inno-script/software - - - name: Compile Installer - run: | - dir - .\ISCC.exe /DMyAppVersion='Win7-Build' Boss-Key.iss - working-directory: .github/inno-script - - - name: Upload Installer - uses: actions/upload-artifact@v4 - with: - name: Boss-Key-Win7-Installer - path: .github/inno-script/output/Boss-Key-Win7-Build-Installer.exe diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml index 6768da1..f42ac38 100644 --- a/.github/workflows/tag-release.yml +++ b/.github/workflows/tag-release.yml @@ -82,6 +82,7 @@ jobs: draft: false prerelease: false generate_release_notes: true + discussion_category_name: announcements compile-to-installer: runs-on: windows-latest diff --git a/README.md b/README.md index b200d65..19f5fa6 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,7 @@ - multifile - 多文件版,标准的程序版本,所有依赖文件被压缩到一个压缩包里,解压后可用 - installer - 安装程序(推荐),完整封装的Boss-Key程序安装程序,提供一键安装、更新、卸载,可以更高效的管理Boss-Key程序 - - +部分版本会提供win7系统的软件包,带有win7标识的可以在Windows 7系统上运行 ### 基础使用 @@ -97,6 +96,8 @@ Boss Key默认使用窗口标题等多因素进行窗口匹配以确保精确隐 如果你安装了python环境,也可以尝试克隆仓库后,运行Boss-Key.py文件来启动窗口 +如果你是windows7系统,部分版本会提供win7系统的软件包,可以下载带有win7标识的软件使用 + **为什么我一直没办法检查更新** 检查更新的服务依赖Github提供的Github Page,如果你的电脑无法访问[https://ivanhanloth.github.io/Boss-Key](https://ivanhanloth.github.io/Boss-Key),则无法检查更新 @@ -141,7 +142,7 @@ Boss-Key - 无法隐藏部分游戏窗口,可能由于游戏窗口加密导致 ## 更新日志 -**V2.0.3 (更新于2025/4/4)** +**V2.0.3 (更新于2025/4/5)** - 新增文件路径匹配选项 - 新增窗口恢复工具 - 修复状态列表问题 diff --git a/main/core/config.py b/main/core/config.py index 8df198f..4f3dee2 100644 --- a/main/core/config.py +++ b/main/core/config.py @@ -9,7 +9,7 @@ class Config: AppName = "Boss Key" AppVersion = "v2.0.3.0" - AppReleaseDate = "2025-04-04" + AppReleaseDate = "2025-04-05" AppAuthor = "IvanHanloth" AppDescription = "老板来了?快用Boss-Key老板键一键隐藏静音当前窗口!上班摸鱼必备神器" AppCopyRight = "Copyright © 2022-2025 Ivan Hanloth All Rights Reserved." From a21b749d0ea09185c6b6fa8377ad3a3214927978 Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Sat, 5 Apr 2025 12:21:27 +0800 Subject: [PATCH 14/15] =?UTF-8?q?=E5=AE=8C=E5=96=84Issue=E6=A8=A1=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug-report.yml | 49 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 8 ++++ .github/ISSUE_TEMPLATE/feature-request.yml | 30 +++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..63156ed --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,49 @@ +name: 🐛 创建错误报告 +description: 此处只受理 Bug 报告 +title: "[Bug] 描述问题的标题" +labels: bug +body: + - type: markdown + attributes: + value: | + > 💡 请务必遵循标题格式。例如:[Bug] 窗口无法隐藏 + - type: checkboxes + attributes: + label: 议题条件 + description: 在你开始之前,请花几分钟时间确保你已如实完成以下工作,以便让我们更高效地沟通。 + options: + - label: 我确认即使在最新正式版中存在该问题。 + required: true + - label: 我确认已在 [Issues](/IvanHanloth/Boss-Key/issues) 进行搜索并确认没有人反馈过相同的Bug。 + required: true + - label: 我确认已经总结议题内容并按规范设置此Issue的标题 + required: true + - type: input + attributes: + label: 系统环境 + description: 在哪个平台(Windows/Linux/MacOS)上运行?如果是直接使用Python运行,则尽量同时提供Python版本 + placeholder: 如:Windows 11 Python 3.11.9 + validations: + required: true + - type: input + attributes: + label: 使用版本 + description: 请提供您当前使用的 Boss Key 版本号。 + placeholder: 如:v2.0.3 + validations: + required: true + - type: textarea + attributes: + label: 问题描述 + description: 请提供详细的问题描述和操作步骤等信息,以便我们也能够更轻松地将问题复现。 + validations: + required: true + - type: textarea + attributes: + label: 错误日志 + description: 如果有错误日志,请提供以便更好地定位问题。 + render: auto + - type: textarea + attributes: + label: 截图补充 + description: 如上述仍然无法准确地表述问题,可提供必要的截图(可直接粘贴上传) \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..f4da8a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: ❓提出问题 + url: https://github.com/IvanHanloth/BossKey/discussions + about: 提问项目相关的其他问题 + - name: 📄官方文档 + url: https://ivanhanloth.github.io/Boss-Key/ + about: 在创建 Issue 之前,请仔细查阅 Bili23 Downloader 使用手册以确保正确使用。 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..08dc145 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,30 @@ +name: ✨ 提出建议 +description: 此处支持提出建议、请求新功能 +title: "[Feature] 简要描述你的建议或请求" +labels: enhancement +body: + - type: markdown + attributes: + value: | + > 💡 请务必遵循标题格式。例如:[Feature] 希望支持鼠标隐藏 + - type: checkboxes + attributes: + label: 议题条件 + description: 在你开始之前,请花几分钟时间确保你已如实完成以下工作,以便让我们更高效地沟通。 + options: + - label: 我确认即使在最新正式版中存在该问题。 + required: true + - label: 我确认已在 [Issues](/IvanHanloth/Boss-Key/issues) 进行搜索并确认没有人反馈过相同的Bug。 + required: true + - label: 我确认已经总结议题内容并按规范设置此Issue的标题 + required: true + - type: textarea + attributes: + label: 详细描述 + description: 请详细描述你的建议或需求。 + validations: + required: true + - type: textarea + attributes: + label: 其他 + description: 如上述仍然无法准确地表述问题,可提供必要的截图(可直接粘贴上传) \ No newline at end of file From 3677a7e7f689d1298e4d284565e4a7b22a6f378b Mon Sep 17 00:00:00 2001 From: Ivan Hanloth Date: Sat, 5 Apr 2025 12:43:16 +0800 Subject: [PATCH 15/15] =?UTF-8?q?=E5=AE=8C=E5=96=84readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 19f5fa6..019f039 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,23 @@ Boss-Key └── requirements.txt 项目依赖文件 ``` +## 分支说明及规范 +为了高效、安全的完成开发,我们对分支、合并等有一定限制。 + +仓库有两个主要分支`main`和`dev`分支: +- `main`包含正式发布版本的源代码,此分支不能直接提交,原则上只能已完成版本功能开发且决定发布新版本并通过测试后,从`dev`分支合并 +- `dev`用于存储所有正在开发的功能的源代码,此分支不能直接提交,原则上只能包含已经完成开发并通过测试的功能 + +其他分支则用于正常功能开发、提交等。 + +新功能、Bug修复等操作应该通过PR提交至`dev`分支,经过代码审查后合并。最后经过测试后,统一通过PR合并至`main`分支。 + +分支命名没有强制规范,但是为了方便维护,我们推荐使用类似`类型/功能`的命名方式,例如`feat/checkUpdate`、`fix/hideWindow`等。 + +## Project规范 +我们通常使用github提供的Project功能进行统一项目规划管理。 + +应该尽量将Issue-PullRequest-Project三者相互关联,以便实现统一管理。 ## 已知问题 - 无法隐藏部分游戏窗口,可能由于游戏窗口加密导致