Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions main/Boss-Key.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from core.config import Config
import platform
import atexit
from GUI.setting.base import SettingWindow

if platform.system() == "Windows":
if platform.release() == "7":
Expand All @@ -43,7 +44,7 @@ def clean():
self.SetAppDisplayName(Config.AppName)
self.SetVendorName(Config.AppAuthor)

lock=os.path.join(os.path.dirname(sys.argv[0]),"Boss-Key.lock")
lock=os.path.join(Config.root_path,"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)
if ask==wx.OK:
Expand Down Expand Up @@ -90,7 +91,7 @@ def is_already_running(self, name):
Config.SettingWindowId = wx.NewIdRef()
Config.TaskBarIcon=taskbar.TaskBarIcon()
Config.HotkeyListener=listener.HotkeyListener()
setting.SettingWindow(Config.SettingWindowId)
SettingWindow(Config.SettingWindowId)
if Config.first_start:
wx.FindWindowById(Config.SettingWindowId).Show()
app.MainLoop()
459 changes: 0 additions & 459 deletions main/GUI/setting.py

This file was deleted.

Empty file added main/GUI/setting/__init__.py
Empty file.
92 changes: 92 additions & 0 deletions main/GUI/setting/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import wx
from core.config import Config
import core.tools as tool
from .binding_page import BindingPage
from .hotkeys_page import HotkeysPage
from .options_page import OptionsPage

class SettingWindow(wx.Frame):
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()
self.Bind_EVT()
self.SetData()
self.SetSize((1500, 800))
self.Center()

def init_UI(self):
panel = wx.Panel(self)
sizer = wx.BoxSizer(wx.VERTICAL)

# 创建notebook
self.notebook = wx.Notebook(panel)

# 添加各个设置页面
self.binding_page = BindingPage(self.notebook)
self.hotkeys_page = HotkeysPage(self.notebook)
self.options_page = OptionsPage(self.notebook)

self.notebook.AddPage(self.binding_page, "窗口绑定")
self.notebook.AddPage(self.hotkeys_page, "热键设置")
self.notebook.AddPage(self.options_page, "其他选项")

sizer.Add(self.notebook, 1, wx.EXPAND | wx.ALL, 5)

# 创建按钮
button_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.reset_btn = wx.Button(panel, label="重置设置")
self.save_btn = wx.Button(panel, 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)

sizer.Add(button_sizer, 0, wx.EXPAND | wx.ALL, 10)

# 设置提示
if Config.first_start:
static_text = wx.StaticText(panel, label="本页面仅在首次启动或内容有更新时自动显示,后续可通过托盘图标打开本页面")
sizer.Add(static_text, 0, wx.EXPAND | wx.BOTTOM, 10)

panel.SetSizer(sizer)
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.Bind(wx.EVT_CLOSE, self.OnClose)

def SetData(self):
Config.load()
self.binding_page.SetData()
self.hotkeys_page.SetData()
self.options_page.SetData()

def OnSave(self, e):
# 从各页面获取数据
self.binding_page.SaveData()
self.hotkeys_page.SaveData()
self.options_page.SaveData()

# 应用更改
Config.HotkeyListener.ShowWindows(load=False)
Config.save()
try:
Config.HotkeyListener.reBind()
wx.MessageDialog(None, "保存成功", "Boss Key", wx.OK | wx.ICON_INFORMATION).ShowModal()
except:
wx.MessageDialog(None, "热键绑定失败,请重试", "Boss Key", wx.OK | wx.ICON_ERROR).ShowModal()

def OnReset(self, e):
# 重置所有设置
self.binding_page.Reset()
self.hotkeys_page.Reset()
self.options_page.Reset()
wx.MessageDialog(None, "已重置选项,请保存设置以启用", "Boss Key", wx.OK | wx.ICON_INFORMATION).ShowModal()

def OnClose(self, e):
self.Hide()

def RefreshLeftList(self, e=None):
"""刷新左侧列表,供外部调用"""
self.binding_page.RefreshLeftList()
220 changes: 220 additions & 0 deletions main/GUI/setting/binding_page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import wx
import wx.dataview as dataview
import wx.lib.buttons as buttons
from core.config import Config
import core.tools as tool
from core.model import WindowInfo

class BindingPage(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
self.init_UI()
self.Bind_EVT()

def init_UI(self):
# 主 sizer
main_sizer = wx.BoxSizer(wx.HORIZONTAL)

# 左边列表
left_staticbox = wx.StaticBox(self, label="现有窗口进程")
left_sizer = wx.StaticBoxSizer(left_staticbox, wx.VERTICAL)
self.left_treelist = dataview.TreeListCtrl(self, style=dataview.TL_CHECKBOX)
self.left_treelist.AppendColumn('窗口标题', width=300)
self.left_treelist.AppendColumn('窗口句柄', width=100)
self.left_treelist.AppendColumn('进程PID', width=150)
left_sizer.Add(self.left_treelist, 1, wx.EXPAND | wx.ALL, 5)

# 中键按钮
middle_sizer = wx.BoxSizer(wx.VERTICAL)
self.add_binding_btn = buttons.GenButton(self, label="添加绑定-->")
self.remove_binding_btn = buttons.GenButton(self, label="<--删除绑定")
self.refresh_btn = buttons.GenButton(self, 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)

# 右边列表
right_staticbox = wx.StaticBox(self, label="已绑定进程")
right_sizer = wx.StaticBoxSizer(right_staticbox, wx.VERTICAL)
self.right_treelist = dataview.TreeListCtrl(self, style=dataview.TL_CHECKBOX)
self.right_treelist.AppendColumn('窗口标题', width=300)
self.right_treelist.AppendColumn('窗口句柄', width=100)
self.right_treelist.AppendColumn('进程PID', width=150)
right_sizer.Add(self.right_treelist, 1, wx.EXPAND | wx.ALL, 5)

# 加到主sizer中
main_sizer.Add(left_sizer, 1, wx.EXPAND | wx.ALL, 5)
main_sizer.Add(middle_sizer, 0, wx.EXPAND | wx.ALL, 5)
main_sizer.Add(right_sizer, 1, wx.EXPAND | wx.ALL, 5)

self.SetSizer(main_sizer)

def Bind_EVT(self):
self.add_binding_btn.Bind(wx.EVT_BUTTON, self.OnAddBinding)
self.remove_binding_btn.Bind(wx.EVT_BUTTON, self.OnRemoveBinding)
self.refresh_btn.Bind(wx.EVT_BUTTON, self.RefreshLeftList)
self.left_treelist.Bind(dataview.EVT_TREELIST_ITEM_CHECKED, self.OnToggleCheck)
self.right_treelist.Bind(dataview.EVT_TREELIST_ITEM_CHECKED, self.OnToggleCheck)

def SetData(self):
self.InsertTreeList(Config.hide_binding, self.right_treelist, True)
self.RefreshLeftList()

def SaveData(self):
# 获取已绑定窗口列表
Config.hide_binding = self.ItemsData(self.right_treelist, only_checked=False)

def Reset(self):
self.InsertTreeList([], self.right_treelist, True)
self.RefreshLeftList()

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):
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 RefreshLeftList(self, e=None):
windows = tool.getAllWindows()
right = self.ItemsData(self.right_treelist, only_checked=False)
list = []
for window in windows:
flag = 0
for i in right:
if tool.isSameWindow(window, i, True):
flag = 1
break
if not flag:
list.append(window)
self.InsertTreeList(list, self.left_treelist, True)

def OnToggleCheck(self, e):
treelist = e.GetEventObject()
item = e.GetItem()
is_checked = treelist.GetCheckedState(item)

# 递归设置子节点状态
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():
# 检查所有兄弟节点状态
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 all_unchecked:
treelist.CheckItem(parent, wx.CHK_UNCHECKED)
else:
treelist.CheckItem(parent, wx.CHK_UNDETERMINED)

# 递归更新上层父节点
self.UpdateParentCheckState(treelist, parent)

def InsertTreeList(self, data: list, treelist: dataview.TreeListCtrl, clear=True):
if clear:
treelist.DeleteAllItems()
root = treelist.GetRootItem()
process_map = {}
for window in data:
# 确保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, window)
treelist.Expand(root)
for process in process_map:
treelist.Expand(process_map[process])

# 初始化所有父节点的选中状态
for process in process_map:
if treelist.GetFirstChild(process_map[process]).IsOk():
self.UpdateParentCheckState(treelist, treelist.GetFirstChild(process_map[process]))

def SearchProcessNode(self, treelist: dataview.TreeListCtrl, process):
item = treelist.GetRootItem()
while item.IsOk():
item = treelist.GetNextItem(item)
if not item.IsOk():
break
data = treelist.GetItemData(item)
if data is not None and hasattr(data, 'process') and data.process == process:
return treelist.GetItemParent(item)

def RemoveItem(self, treelist: dataview.TreeListCtrl, data):
# 确保data是WindowInfo对象
if isinstance(data, dict):
data = WindowInfo.from_dict(data)

node = self.SearchProcessNode(treelist, data.process)
if node is not None:
item = treelist.GetFirstChild(node)
while item.IsOk():
item_data = treelist.GetItemData(item)
if item_data and item_data == data:
treelist.DeleteItem(item)
break
item = treelist.GetNextSibling(item)

if not treelist.GetFirstChild(node).IsOk():
# 如果没有子节点了,删除父节点
treelist.DeleteItem(node)

def ItemsData(self, treelist: dataview.TreeListCtrl, only_checked=False, item_object=False)->list[WindowInfo]:
res = []
item = treelist.GetRootItem()
while item.IsOk():
item = treelist.GetNextItem(item)
if not item.IsOk():
break
if only_checked and treelist.GetCheckedState(item) != wx.CHK_CHECKED:
continue
if item_object:
res.append(item)
else:
data = treelist.GetItemData(item)
if data is not None and data:
res.append(data)
return res
Loading