diff --git a/.gitignore b/.gitignore
index 0c545709..cb43d78d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -410,12 +410,8 @@ updater.old/
updaterback/
data/
key.json
-gui/dev_list/*
-!gui/dev_list/in_dev
res/packages/
Gui/packages.json
-
-dev.dat
*.zip
*.7z
source/
@@ -743,4 +739,6 @@ vite.config.js.timestamp-*
vite.config.ts.timestamp-*
cache/
-source/
\ No newline at end of file
+source/
+
+demo/
\ No newline at end of file
diff --git a/Gui/install_pack.py b/Gui/install_pack.py
index 18e25075..10d75b8e 100644
--- a/Gui/install_pack.py
+++ b/Gui/install_pack.py
@@ -4,7 +4,7 @@
app = QApplication(sys.argv)
from uiStyles.QUI import *
-from uiStyles import PagesUI, UMessageBox, MessageButtonTemplate, styles, maps, UCheckBox
+from uiStyles import PagesUI, UMessageBox, MessageButton, styles, maps, UCheckBox
import pyperclip
from sharelibs import (get_lang, settings, get_inst_lang, get_icon, system_lang, parse_system_language_to_lang_id, run_software, get_resource_path, is_admin, get_init_lang, QtThread, mem_id)
import win32com.client
@@ -143,8 +143,8 @@ def new_msg(parent,
title: str,
text: str,
icon: QMessageBox.Icon,
- buttons: MessageButtonTemplate = MessageButtonTemplate.OK,
- defaultButton: MessageButtonTemplate = MessageButtonTemplate.OK):
+ buttons: MessageButton = MessageButton.OK,
+ defaultButton: MessageButton = MessageButton.OK):
msg_box = UMessageBox.new_msg(parent, title, text, icon, buttons, defaultButton)
new_color_bar(msg_box)
@@ -216,7 +216,7 @@ def apply_titleBar(self, window: QMainWindow | QDialog):
'''应用标题栏样式'''
hwnd = window.winId().__int__()
- if select_styles.css_data['.meta']['mode'] == 'dark':
+ if select_styles.css_data['.meta']['--mode'] == 'dark':
is_dark_mode = 1
else:
is_dark_mode = 0
@@ -781,7 +781,7 @@ def on_next(self):
self,
get_ipk_lang('1a'),
get_ipk_lang('24').format('\n'.join(self.changes if self.changes else [get_ipk_lang('2f')])),
- MessageButtonTemplate.YESNO,
+ MessageButton.YESNO,
)
for i in packages_info:
@@ -793,7 +793,7 @@ def on_next(self):
self.changes = get_list_diff(select_package_id, package_id_list)
- if message == 3:
+ if message == MessageButton.NO:
return
else:
message =QMessageBox.question(
diff --git a/Gui/logger.py b/Gui/logger.py
index e8ec116d..956e6e81 100644
--- a/Gui/logger.py
+++ b/Gui/logger.py
@@ -49,6 +49,12 @@ def remove_old_log(directory_path):
os.remove(file_path)
except Exception as e:
pass
+
+class ExceptionVal:
+ raise_trace = 0b1
+ output_msg = 0b10
+ exit = 0b100
+ all = raise_trace | output_msg | exit
# 定义日志路径
folder_path = Path("cache/logs")
@@ -120,7 +126,7 @@ def __init__(self, default_service_id):
self.info(f'logger Started')
remove_old_log(folder_path)
- def auto_logger(self, service_id=None, start_extra_text='', end_extra_text='', start_extra=None, end_extra=None, level=logging.INFO):
+ def auto_logger(self, service_id: str | None=None, parent:list | None=None , start_extra_text: str='', end_extra_text:str='', start_extra:str | None=None, end_extra: str | None=None, level=logging.INFO):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
@@ -140,19 +146,21 @@ def wrapper(*args, **kwargs):
out_service_id = self.default_service_id if service_id is None else service_id
start_extra_out = {} if start_extra is None else start_extra
end_extra_out = {} if end_extra is None else end_extra
+ parent_out = [] if parent is None else parent
+
+ function_name = '.'.join(parent_out) + ('.' if parent else '') + func.__name__
- log_func(f'Running function: {func.__name__} args={args} kwargs={kwargs}', extra={'service_id': out_service_id, **start_extra_out})
+ log_func(f'Running function: {function_name} args={args} kwargs={kwargs}', extra={'service_id': out_service_id, **start_extra_out})
if start_extra_text:
log_func(start_extra_text, extra={'service_id': out_service_id, **start_extra_out})
try:
result = func(*args, **kwargs)
except Exception as e:
trace = traceback.format_exc()
- self.exception(out_service_id, trace, f'Function {func.__name__} raised an unexpected exception', extra={'service_id': out_service_id, **end_extra_out})
- QMessageBox.critical(None, 'An unexpected error occurred', f'An unexpected error occurred in {func.__name__}: \n{trace}\nPlease look the log path: {(folder_path / f"{log_id}.log").resolve()} and send me the log file on issue: https://github.com/xystudiocode/pyClickMouse/issues/new/choose')
+ self.exception(out_service_id, trace, f'Function {function_name} raised an unexpected exception', extra={'service_id': out_service_id, **end_extra_out})
+ QMessageBox.critical(None, 'An unexpected error occurred', f'An unexpected error occurred in {function_name}: \n{trace}\nPlease look the log path: {(folder_path / f"{log_id}.log").resolve()} and send me the log file on issue: https://github.com/xystudiocode/pyClickMouse/issues/new/choose')
sys.exit(1) # 退出程序
- raise e
- log_func(f'Function {func.__name__} running successfull, returned: {result}', extra={'service_id': out_service_id, **end_extra_out})
+ log_func(f'Function {function_name} running successfull, returned: {result}', extra={'service_id': out_service_id, **end_extra_out})
if end_extra_text:
log_func(end_extra_text, extra={'service_id': out_service_id, **end_extra_out})
return result
@@ -174,5 +182,13 @@ def error(self, msg, extra=None):
def critical(self, msg, extra=None):
self.logger.critical(msg, extra=extra)
- def exception(self, service, trace, msg='', extra=None):
- self.critical(f'{msg}: An error occurred in {service}\n{trace}', extra=extra)
\ No newline at end of file
+ def exception(self, service, msg='', extra=None, mode: ExceptionVal = ExceptionVal.raise_trace):
+ if mode & ExceptionVal.raise_trace: # 获取堆栈
+ trace = '\n' + traceback.format_exc()
+ else:
+ trace = ''
+ self.critical(f'{msg}: An error occurred in {service}:{trace}', extra=extra)
+ if mode & ExceptionVal.output_msg: # 输出消息
+ QMessageBox.critical(None, 'An unexpected error occurred', f'An unexpected error occurred: {trace}\nPlease look the log path: {(folder_path / f"{log_id}.log").resolve()} and send me the log file on issue: https://github.com/xystudiocode/pyClickMouse/issues/new/choose')
+ if mode & ExceptionVal.exit: # 退出程序
+ sys.exit(1) # 退出程序
diff --git a/Gui/main.py b/Gui/main.py
index 3b50fc2f..8daf7da5 100644
--- a/Gui/main.py
+++ b/Gui/main.py
@@ -1,23 +1,25 @@
# 加载ui框架
from PySide6.QtWidgets import QApplication
import sys
-from logger import Logger, logging
+from logger import Logger, logging, ExceptionVal
app = QApplication(sys.argv)
logger = Logger('clickmouse.main')
-from uiStyles.QUI import *
# 加载框架
+try:
+ from sharelibs import * # 共享库
+except:
+ logger.exception('clickmouse.main', mode=ExceptionVal.all)
+from uiStyles.QUI import *
+from uiStyles import *
from datetime import datetime # 检查时间
from pynput import keyboard # 热键功能库
import pyautogui # 鼠标操作库
from time import sleep, time # 延迟
from webbrowser import open as open_url # 关于作者
from check_update import check_update, web_data, download_file # 更新检查
-from uiStyles import (UnitInputLayout, styles, maps, StyleReplaceMode, ULabel, CustonMessageButton, SelectUI, UCheckBox, UMessageBox, MessageButtonTemplate) # 软件界面样式
-from uiStyles import indexes as style_indexes # 界面组件样式索引
-from sharelibs import * # 共享库
+from uiStyles import indexes as style_indexes
from sharelibs import __version__ # 版本号
-import parse_dev # 解析开发固件配置
import winreg # 注册表库
import math # 数学库
import colorsys # 颜色库
@@ -179,14 +181,6 @@ def get_windows_accent_color():
# 通常我们使用RGB格式,忽略Alpha通道
return f'#{r_str}{g_str}{b_str}'
-@logger.auto_logger('clickmouse.colorGetter')
-def new_color_bar(obj):
- '''
- 给创建添加样式标题栏
- '''
- color_getter.style_changed.connect(lambda: color_getter.apply_titleBar(obj))
- color_getter.style_changed.emit()
-
@logger.auto_logger('clickmouse.colorGetter')
def lighten_color_hex(hex_color, factor):
'''
@@ -374,7 +368,7 @@ def on_input_change(*, type:str ):
delay_num = setting_value.click_delay
time_num = setting_value.click_times
is_error = False
- elif type ==InputChange.setting_window:
+ elif type == InputChange.setting_window:
delay_text = setting_window.default_delay
delay_times = setting_window.default_time
total = setting_window.total_time_label
@@ -387,14 +381,14 @@ def on_input_change(*, type:str ):
delay_times.setEnabled(not(times_combo.currentIndex() == latest_index or (setting_value.times_unit == latest_index) and type == InputChange.main_window))
- if times_combo.currentIndex() == latest_index or input_times == '0':
+ if times_combo.currentIndex() == latest_index:
is_inf = True
if setting_value.times_unit == latest_index and type == InputChange.main_window:
is_inf = True
def on_delay_error(error_text=get_lang('14')):
'''输入延迟错误'''
- logger.warning(f'Input delay error: {error_text}', extra={'service_id': 'clickmouse.main.ui'})
+ logger.debug(f'Input delay error: {error_text}', extra={'service_id': 'clickmouse.main.ui'})
total.setText(f'{get_lang('2c')}: {error_text}')
if type == InputChange.main_window:
global is_error
@@ -402,10 +396,16 @@ def on_delay_error(error_text=get_lang('14')):
main_window.right_click_button.setEnabled(False)
main_window.left_click_button.setEnabled(False)
is_error = True
+ elif type == InputChange.setting_window:
+ if error_text == get_lang('14'):
+ text = get_lang('60')
+ else:
+ text = error_text
+ main_window.total_time_label.setText(f'{get_lang('2c')}: {text}')
+ @logger.auto_logger('clickmouse.main.ui', level=logging.DEBUG)
def check_default_var(value):
'''检查默认延迟是否有效'''
- logger.debug('Checking default var.', extra={'service_id': 'clickmouse.main.ui'})
try:
var = int(settings.get(f'click_{value}', ''))
if not var:
@@ -420,147 +420,170 @@ def check_default_var(value):
on_delay_error()
return False
- logger.debug('Checking input delay.', extra={'service_id': 'clickmouse.main.ui'})
- try:
- delay = math.ceil(float(input_delay))
- if delay < 1:
- raise ValueError
- except ValueError:
- if not setting_value.click_delay == '':
- if input_delay == '':
- if check_default_var('delay'):
- delay = int(setting_value.click_delay)
- else:
- return
- elif setting_value.delay_error_use_default:
- if check_default_var('delay'):
- delay = int(setting_value.click_delay)
- else:
- return
- else:
- on_delay_error()
- return
- except Exception:
- on_delay_error()
- return
-
- logger.debug('Checking input times.', extra={'service_id': 'clickmouse.main.ui'})
- if not is_inf:
+ @logger.auto_logger('clickmouse.main.ui', level=logging.DEBUG)
+ def get_num(input_value, value_default, err_use_default, default_var):
+ value = None
try:
- times = math.ceil(float(input_times))
- if times < 1:
+ value = math.ceil(float(input_value))
+ if value < 1:
raise ValueError
except ValueError:
- if setting_value.click_times == '' and setting_value.click_delay == '':
- on_delay_error(get_lang('61'))
- return
- else:
- if input_times == '':
- if check_default_var('times'):
- times = int(setting_value.click_times)
- else:
- return
- elif setting_value.times_error_use_default:
- if check_default_var('times'):
- times = int(setting_value.click_times)
+ if value_default:
+ if input_delay == '' or err_use_default:
+ if check_default_var(default_var):
+ value = int(value_default)
else:
return
else:
- on_delay_error()
return
except Exception:
- on_delay_error()
return
+ return value
+
+ delay = get_num(input_delay, setting_value.click_delay, setting_value.delay_error_use_default, 'delay')
+
+ if not is_inf:
+ if not(setting_value.click_times) and not(setting_value.click_delay):
+ on_delay_error(get_lang('61'))
+ return 1
+ times = get_num(input_times, setting_value.click_times, setting_value.times_error_use_default, 'times')
+ if times is None:
+ on_delay_error()
+ return -1
+ if delay is None:
+ on_delay_error()
+ return -1
if type == InputChange.main_window:
+ # 先过滤前面的报错
main_window.right_click_button.setEnabled(True)
main_window.left_click_button.setEnabled(True)
is_error = False
+ if bool(delay_text.text()) ^ bool(delay_times.text()): # 状态不同
+ if not((setting_value.modify_using_default_input or is_inf) and dev_flags.get('new_settings')):
+ on_delay_error()
+ return -2
+ if ((delay_combo.currentIndex() != setting_value.delay_unit) and not(delay_text.text())):
+ if not(setting_value.modify_using_default_combo and dev_flags.get('new_settings')):
+ on_delay_error()
+ return -2
+ if ((times_combo.currentIndex() != setting_value.times_unit or is_inf) and not(delay_text.text())):
+ if not(setting_value.modify_using_default_combo and dev_flags.get('new_settings')):
+ on_delay_error()
+ return -2
+
+ match delay_combo.currentIndex():
+ case 0:
+ delay_num = delay
+ case 1:
+ delay_num = delay * 1000
+ case 2:
+ delay_num = delay * 60 * 1000
+ case _:
+ delay_num = delay
- if setting_value.click_delay != '' and input_delay == '':
- match setting_value.delay_unit:
- case 0:
- delay_num = delay
- case 1:
- delay_num = delay * 1000
+ if is_inf:
+ total.setText(f'{get_lang('2c')}: {get_lang('2b')}')
else:
- match delay_combo.currentIndex():
+ match times_combo.currentIndex():
case 0:
- delay_num = delay
+ time_num = times
case 1:
- delay_num = delay * 1000
+ time_num = times * 10000
case 2:
- delay_num = delay * 60 * 1000
+ time_num = times * 100_0000
case _:
- delay_num = delay
-
- if is_inf:
- total.setText(f'{get_lang('2c')}: {get_lang('2b')}')
- if type == InputChange.main_window:
- if delay_num == 0:
- on_delay_error()
- else:
- if setting_value.click_times != '' and input_times == '':
- match setting_value.times_unit:
- case 0:
- time_num = times
- case 1:
- time_num = times * 10000
- else:
- match times_combo.currentIndex():
- case 0:
- time_num = times
- case 1:
- time_num = times * 10000
- case 2:
- time_num = times * 100_0000
- case _:
- time_num = times
-
- if (delay_num == 0 and time_num != 0) or (delay_num != 0 and time_num == 0):
- on_delay_error()
- return
+ time_num = times
try:
total_run_time = get_unit_value(delay_num * time_num)
+ if type == InputChange.setting_window:
+ main_window.delay_combo.setCurrentIndex(delay_combo.currentIndex())
+ main_window.times_combo.setCurrentIndex(times_combo.currentIndex())
except OverflowError:
on_delay_error(get_lang('67'))
- return
+ return -3
total.setText(f'{get_lang('2c')}: {total_run_time[0]}{total_run_time[1]}')
+ if type == InputChange.setting_window:
+ on_input_change(type=InputChange.main_window) # 刷新主窗口
+ return 0
class UMainWindow(QMainWindow):
'''自定义窗口基类'''
def __init__(self):
- logger.debug('Initializing window.')
+ logger.info('Initializing window.', extra={'service_id': 'clickmouse.ui'})
super().__init__()
self.setWindowIcon(icon)
- new_color_bar(self)
-
+ self.func = lambda: color_getter.apply_titleBar(self)
+
+ self.addtional_local_value = {'self': self}
+ logger.info('Window initialized.', extra={'service_id': 'clickmouse.ui'})
+
+ @logger.auto_logger('clickmouse.ui', ['UMainWindow'])
def showEvent(self, event):
'''窗口显示事件'''
- QTimer.singleShot(setting_value.soft_delay, lambda: new_color_bar(self))
+ color_getter.style_changed.connect(self.func)
+ QTimer.singleShot(setting_value.soft_delay, self.func)
return super().showEvent(event)
+ @logger.auto_logger('clickmouse.ui', ['UMainWindow'])
+ def closeEvent(self, event):
+ '''窗口关闭事件'''
+ color_getter.style_changed.disconnect(self.func)
+ return super().closeEvent(event)
+
class UDialog(QDialog):
'''自定义对话框基类'''
- def __init__(self, parent=None):
- logger.debug('Initializing window.')
+ def __init__(self):
+ logger.debug('Initializing window.', extra={'service_id': 'clickmouse.ui'})
- super().__init__(parent=parent)
+ super().__init__()
self.setWindowIcon(icon)
- new_color_bar(self)
+ self.func = lambda: color_getter.apply_titleBar(self)
+
+ self.addtional_local_value = {'self': self}
+
+ logger.debug('Window initialized.', extra={'service_id': 'clickmouse.ui'})
+ @logger.auto_logger('clickmouse.ui', ['UDialog'])
def showEvent(self, event):
'''窗口显示事件'''
- QTimer.singleShot(setting_value.soft_delay, lambda:new_color_bar(self))
+ color_getter.style_changed.connect(self.func)
+ QTimer.singleShot(setting_value.soft_delay, self.func)
return super().showEvent(event)
+
+ @logger.auto_logger('clickmouse.ui', ['UDialog'])
+ def closeEvent(self, event):
+ '''窗口关闭事件'''
+ color_getter.style_changed.disconnect(self.func)
+ return super().closeEvent(event)
+
+class MessageBox(UMessageBox):
+ def __init__(self, parent: QWidget | None, title: str, text: str, icon: MessageIcon, buttons: MessageButton, defaultButton: MessageButton):
+ super().__init__(parent, title, text, icon, buttons, defaultButton)
+ self.func = lambda: color_getter.apply_titleBar(self)
+
+ @logger.auto_logger('clickmouse.ui', ['MessageBox'])
+ def showEvent(self, event):
+ color_getter.style_changed.connect(self.func)
+ evt = super().showEvent(event)
+ QTimer.singleShot(setting_value.soft_delay, self.func)
+ return evt
+
+ @logger.auto_logger('clickmouse.ui', ['MessageBox'])
+ def done(self, result):
+ '''关闭对话框时执行'''
+ color_getter.style_changed.disconnect(self.func)
+ return super().done(result)
class StartManager(QObject):
'''开机自启动管理器'''
updated = Signal(bool)
+
def __init__(self):
super().__init__()
+ logger.info('Starting startup manager.', extra={'service_id': 'clickmouse.setting.startupManager'})
self.app_name = 'clickmouse.lnk'
self.status_path = r'Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder'
self.create_reg()
@@ -569,55 +592,53 @@ def __init__(self):
self.timer = QTimer()
self.timer.timeout.connect(self.check_value)
self.timer.start(setting_value.soft_delay)
+ logger.info('Startup manager initialized.', extra={'service_id': 'clickmouse.setting.startupManager'})
+ @logger.auto_logger('clickmouse.setting.startupManager', ['StartManager'], start_extra_text='Startup folder`s shortcut is not found')
def create_reg(self):
'''检查是否已启用开机自启动'''
start_path = Path(os.environ['APPDATA'], 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', self.app_name)
if not(start_path.exists()):
+ logger.warning('Startup folder`s shortcut is not found,creating shortcut.', extra={'service_id': 'clickmouse.setting.startupManager'})
create_shortcut(str(start_path), str(Path.cwd() / 'main.exe') + ' --quiet', 'ClickMouse', work_dir=str(Path.cwd()))
self.disable()
-
+ else:
+ logger.info('Startup folder`s shortcut is found.', extra={'service_id': 'clickmouse.setting.startupManager'})
+
+ @logger.auto_logger('clickmouse.setting.startupManager', ['StartManager'], level=logging.DEBUG)
def is_enabled(self):
try:
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, self.status_path, 0, winreg.KEY_READ) as key:
+ logger.debug('Try to read value from registry..', extra={'service_id': 'clickmouse.setting.startupManager'})
value, _ = winreg.QueryValueEx(key, self.app_name)
return value[0] == 2
except FileNotFoundError:
+ logger.warning('Registry path not found.', extra={'service_id': 'clickmouse.setting.startupManager'})
return False
+ @logger.auto_logger('clickmouse.setting.startupManager', ['StartManager'], level=logging.DEBUG)
def check_value(self):
- '''检查注册表值是否最新'''
+ '''检查注册表值是否更新'''
new_value = self.is_enabled()
if new_value != self.auto_start:
self.auto_start = new_value
self.updated.emit(self.auto_start)
+ logger.info('Startup value updated.', extra={'service_id': 'clickmouse.setting.startupManager'})
+ @logger.auto_logger('clickmouse.setting.startupManager', ['StartManager'], start_extra_text='Enable startup.')
def enable(self):
'''启用开机自启动'''
with winreg.OpenKey(winreg.HKEY_CURRENT_USER,
self.status_path, 0, winreg.KEY_WRITE) as key:
winreg.SetValueEx(key, self.app_name, 0, winreg.REG_BINARY, bytes([0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+ @logger.auto_logger('clickmouse.setting.startupManager', ['StartManager'], start_extra_text='Disable startup.')
def disable(self):
'''禁用开机自启动'''
with winreg.OpenKey(winreg.HKEY_CURRENT_USER,
self.status_path, 0, winreg.KEY_WRITE) as key:
winreg.SetValueEx(key, self.app_name, 0, winreg.REG_BINARY, bytes([0x03, 0x00, 0x00, 0x00]) + get_now_filetime())
-
-class MessageBox(UMessageBox):
- @staticmethod
- def new_msg(parent,
- title: str,
- text: str,
- icon: QMessageBox.Icon,
- buttons: MessageButtonTemplate = MessageButtonTemplate.OK,
- defaultButton: MessageButtonTemplate = MessageButtonTemplate.OK):
-
- msg_box = UMessageBox.new_msg(parent, title, text, icon, buttons, defaultButton)
- new_color_bar(msg_box)
-
- return msg_box
class UHotkeyLineEdit(QLineEdit):
'''能够捕获热键组合的输入框,只有获得焦点时才更新'''
@@ -626,7 +647,8 @@ def __init__(self, parent=None):
self._connection = None # 保存信号连接对象
self.key_list = [] # 保存按下的热键
self.setReadOnly(True)
- self.listener = get_hotkey_listener_instance()
+ #self.listener = get_hotkey_listener_instance()
+ self.listener = hotkey_listener
def focusInEvent(self, event):
'''获得焦点时连接信号'''
@@ -660,10 +682,12 @@ class HotkeyListener(QObject):
def __init__(self):
super().__init__()
+ logger.info('Initializing hotkey listener.', extra={'service_id': 'clickmouse.setting.hotkeyManager'})
self.listener = None
self.is_listening = False
self.clicked_keys = set() # 用于跟踪当前按下的键
+ @logger.auto_logger('clickmouse.setting.hotkeyManager', ['HotkeyListener'])
def start_listening(self):
'''开始监听热键'''
if self.is_listening:
@@ -678,6 +702,7 @@ def start_listening(self):
self.listener.daemon = True # 设置为守护线程
self.listener.start()
+ @logger.auto_logger('clickmouse.setting.hotkeyManager', ['HotkeyListener'])
def stop_listening(self):
'''停止监听热键'''
if self.listener and self.is_listening:
@@ -717,33 +742,30 @@ class Click(QObject):
def __init__(self):
super().__init__()
+ logger.info('Initializing clicker.', extra={'service_id': 'clickmouse.clicker'})
self.running = False
self.paused = False
self.click_thread = None
self.right_clicked = False
self.left_clicked = False
+ @logger.auto_logger('clickmouse.clicker', ['Click'])
def mouse_left(self, delay, times):
- logger.info('Left click')
if not self.running:
self.mouse_click(button='left', input_delay=delay, times=times)
+ @logger.auto_logger('clickmouse.clicker', ['Click'])
def mouse_right(self, delay, times):
# 停止当前运行的点击线程
- logger.info('Right click')
if not self.running:
self.mouse_click(button='right', input_delay=delay, times=times)
- def set_default_clicked(self):
- self.left_clicked = False
- self.right_clicked = False
- self.click_changed.emit(self.left_clicked, self.right_clicked)
-
+ @logger.auto_logger('clickmouse.clicker', ['Click'], start_extra_text='Start clicker.')
def mouse_click(self, button: str, input_delay, times):
'''鼠标连点'''
- logger.info('Start click')
# 重置状态
if self.click_thread and self.click_thread.isRunning():
+ logger.info('Stop clicker')
self.running = False
self.paused = False
self.pause.emit(False)
@@ -767,14 +789,16 @@ def mouse_click(self, button: str, input_delay, times):
# 判断参数有效性
try:
+ logger.debug(f'Clicking {button} with delay {input_delay} and times {times}')
delay = math.ceil(float(input_delay))
except Exception:
- trace = format_exc()
+ trace = format_exc() # 获取异常堆栈跟踪信息
MessageBox.critical(None, get_lang('14'), f'{get_lang('1b')}\n{trace}')
- logger.exception('Clicker', trace)
+ logger.exception('clickmouse.clicker')
return
# 创建独立线程避免阻塞GUI
+ @logger.auto_logger('clickmouse.clicker', ['Click'])
def click_loop():
self.pause.emit(False)
i = 0
@@ -785,7 +809,8 @@ def click_loop():
break
if not self.paused:
try:
- if times == float('inf'):
+ logger.debug('Clicking.')
+ if times == float('inf'):
self.click_conuter.emit('inf', str(i), str(delay))
else:
self.click_conuter.emit(str(times), str(i), str(delay))
@@ -795,7 +820,7 @@ def click_loop():
except Exception:
trace = format_exc()
MessageBox.critical(None, get_lang('14'), f'{get_lang('1b')}\n{trace}')
- logger.exception('Clicker', trace)
+ logger.exception('clickmouse.clicker', trace)
self.stopped.emit()
break
@@ -810,6 +835,7 @@ def click_loop():
self.click_thread = QtThread(click_loop)
self.click_thread.start()
+ @logger.auto_logger('clickmouse.clicker', ['Click'])
def pause_click(self):
if self.paused:
logger.info('Clicker resumed')
@@ -820,23 +846,25 @@ def pause_click(self):
class Refresh:
def __init__(self):
+ logger.info('Initializing refresh service', extra={'service_id': 'clickmouse.refresh'})
self.steps = [
self.refresh_title,
self.left_check,
self.right_check,
]
+ @logger.auto_logger('clickmouse.refresh', ['Refresh'])
def run(self):
- logger.info('Running refresh service')
self.do_step(self.steps)
-
+
+ @logger.auto_logger('clickmouse.refresh', ['Refresh'])
def do_step(self, codes):
# 尝试执行代码
for code in codes:
- logger.debug(f'Running step {code.__name__}')
try:
+ logger.info(f'Running function: Refresh.{code.__name__}', extra={'service_id': 'clickmouse.refresh'})
code()
- logger.debug(f'Step {code.__name__} running successfully.')
+ logger.info(f'Function {code.__name__} running successfull.')
except NameError as e:
logger.warning(f'Step {code.__name__} not defined: {e}')
except Exception as e:
@@ -847,34 +875,38 @@ def refresh_title(self):
def left_check(self):
if clicker.left_clicked:
+ logger.info('Left click is started.')
set_style(main_window.left_click_button, StyleClass.selected)
else:
- logger.warning('Left click is not enabled.')
+ logger.info('Left click is not started.')
set_style(main_window.left_click_button, StyleClass.none)
def right_check(self):
if clicker.right_clicked:
+ logger.info('Right click is started.')
set_style(main_window.right_click_button,StyleClass.selected)
else:
- logger.warning('Right click is not enabled.')
+ logger.info('Right click is not started.')
set_style(main_window.right_click_button, StyleClass.none)
class RunAfter:
def __init__(self):
+ logger.info('Initializing run-after service', extra={'service_id': 'clickmouse.runafter'})
self.program_list = {}
+ @logger.auto_logger('clickmouse.runafter', ['RunAfter'])
def add(self, name, python_path, exe_path, run_as_admin=False):
- logger.info('Add run-after plan')
self.program_list[name] = (python_path, exe_path, run_as_admin)
MessageBox.information(main_window, get_lang('59'), get_lang('5a'))
+ @logger.auto_logger('clickmouse.runafter', ['RunAfter'])
def remove(self, name):
logger.info('Remove run-after plan')
del self.program_list[name]
MessageBox.information(main_window, get_lang('59'), get_lang('88'))
+ @logger.auto_logger('clickmouse.runafter', ['RunAfter'])
def run(self):
- logger.info('Running run-after plan')
for python_path, exe_path, use_admin in self.program_list.values():
if use_admin:
run_as_admin(python_path, exe_path)
@@ -885,10 +917,10 @@ class ColorGetter(QObject):
style_changed = Signal()
def __init__(self):
- global refresh
-
super().__init__()
+ logger.info('Initializing color getter.', extra={'service_id': 'clickmouse.colorgetter'})
+
# 记录当前主题
self.style = setting_value.select_style
@@ -903,9 +935,6 @@ def __init__(self):
run_software('main.py', 'main.exe')
sys.exit(0)
- # 加载刷新服务
- refresh = Refresh()
-
# 初始化时应用一次主题
self.apply_global_theme()
@@ -914,9 +943,10 @@ def __init__(self):
self.timer.timeout.connect(self.check_and_apply_theme)
self.timer.start(setting_value.soft_delay)
- def load_theme(self):
- logger.debug('Get latest theme')
+ self.need_refresh = False
+ @logger.auto_logger('clickmouse.colorgetter', ['ColorGetter'], level=logging.DEBUG)
+ def load_theme(self):
theme = None
windows_theme = None
windows_color = None
@@ -925,14 +955,18 @@ def load_theme(self):
if self.style == 0:
theme = QApplication.styleHints().colorScheme()
if theme == Qt.ColorScheme.Dark:
+ logger.debug('Dark theme')
theme = 'auto-dark'
elif theme == Qt.ColorScheme.Light:
+ logger.debug('Light theme')
theme = 'auto-light'
windows_theme = QApplication.styleHints().colorScheme()
if theme == Qt.ColorScheme.Dark:
+ logger.debug('Dark theme')
windows_theme = 'dark'
elif theme == Qt.ColorScheme.Light:
+ logger.debug('Light theme')
windows_theme = 'light'
windows_color = get_windows_accent_color()
@@ -940,10 +974,12 @@ def load_theme(self):
for k, v in maps.items():
if v == setting_value.select_style:
+ logger.debug(f'Found theme: {k}')
theme = k
return theme, windows_theme, windows_color, use_windows_color
+ @logger.auto_logger('clickmouse.colorgetter', ['ColorGetter'], level=logging.DEBUG)
def check_and_apply_theme(self):
'''检查主题是否变化,变化则重新应用'''
logger.debug('Check theme')
@@ -953,31 +989,37 @@ def check_and_apply_theme(self):
new_theme, new_windows_theme, new_windows_color, new_use_windows_color = self.load_theme()
if new_theme != self.current_theme:
+ logger.info('Theme changed')
self.current_theme = new_theme
self.apply_global_theme()
if new_windows_color != self.windows_color:
+ logger.info('Windows color changed')
self.windows_color = new_windows_color
self.apply_global_theme()
if new_windows_theme != self.windows_theme:
+ logger.info('Windows theme changed')
self.windows_theme = new_windows_theme
- self.refresh()
+ self.need_refresh = True
if new_use_windows_color != self.use_windows_color:
+ logger.info('Windows color changed')
self.use_windows_color = new_use_windows_color
self.apply_global_theme()
- def refresh(self):
- refresh.run()
-
+ if self.need_refresh and init_success:
+ refresh.run() # 刷新
+ self.need_refresh = False
+
+ @logger.auto_logger('clickmouse.colorgetter', ['ColorGetter'])
def apply_titleBar(self, window: QMainWindow | QDialog):
'''应用标题栏样式'''
- logger.debug('Apply titleBar style')
-
+ if not init_success: # 等待初始化完成加载
+ return -1
hwnd = window.winId().__int__()
- if select_styles.css_data['.meta']['--mode'] == 'dark':
+ if select_styles.css_data['.meta']['mode'] == 'dark':
is_dark_mode = 1
else:
is_dark_mode = 0
@@ -990,14 +1032,14 @@ def apply_titleBar(self, window: QMainWindow | QDialog):
ctypes.sizeof(wintypes.INT)
)
+ return 0
+
+ @logger.auto_logger('clickmouse.colorgetter', ['ColorGetter'])
def apply_global_theme(self):
'''根据当前主题,为整个应用设置全局样式表'''
global select_styles
- logger.info('Use style')
-
app = get_application_instance()
- self.style_changed.emit()
current_theme = self.current_theme.replace('auto-', '')
@@ -1007,7 +1049,7 @@ def apply_global_theme(self):
steps = [
[['.selected:pressed', 'background-color'], lighten_color_hex(self.windows_color, -0.165)]
]
- if select_styles.css_data['.meta']['--mode'] == 'dark':
+ if select_styles.css_data['.meta']['mode'] == 'dark':
steps.extend([
[['.selected', 'background-color'], lighten_color_hex(self.windows_color, 0.4)],
[['.selected:hover', 'background-color'], lighten_color_hex(self.windows_color, 0.45)],
@@ -1024,17 +1066,9 @@ def apply_global_theme(self):
for step in steps:
select_styles = select_styles.replace(step[0], StyleReplaceMode.ALL, step[1], output_json=False)
+ logger.debug('Apply theme')
app.setStyleSheet(select_styles.css_text) # 全局应用
- self.refresh()
-
-class SettingValue(SettingValue):
- def get(self, value):
- default_value = default_settings.get(value, None)
- if isinstance(default_value, str):
- if default_value.startswith('!var '): # 需要加载变量
- var_name = default_value[5:]
- default_value = eval(var_name)
- return settings.get(value, default_value)
+ self.need_refresh = True
class MainWindow(UMainWindow):
def __init__(self):
@@ -1189,6 +1223,7 @@ def create_menu_bar(self):
# 设置菜单
settings_menu = menu_bar.addMenu(get_lang('04'))
settings_action = settings_menu.addAction(get_lang('05'))
+ attr_action = settings_menu.addAction(get_lang('8c'))
# 更新菜单
update_menu = menu_bar.addMenu(get_lang('06'))
@@ -1236,7 +1271,7 @@ def create_menu_bar(self):
# not_official_extension_menu.addAction(get_lang('98')).triggered.connect(self.show_import_extension_mode) # 管理扩展菜单
# not_official_extension_menu.addAction(get_lang('92')).triggered.connect(self.show_manage_not_official_extension) # 管理扩展菜单
- # 宏菜单
+ # # 宏菜单
# macro_menu = menu_bar.addMenu(get_lang('99'))
# run_marco_menu = macro_menu.addMenu(get_lang('9d'))
@@ -1254,6 +1289,7 @@ def create_menu_bar(self):
settings_action.triggered.connect(self.show_setting)
exit_action.triggered.connect(app.quit)
create_issue_action.triggered.connect(lambda: open_url(setting_value.feedback))
+ attr_action.triggered.connect(self.show_attr)
def open_doc(self, *, path: str=''):
'''打开文档'''
@@ -1355,6 +1391,11 @@ def show_about(self):
logger.info('Opening about window')
about_window.exec()
+ def show_attr(self):
+ '''显示属性窗口'''
+ logger.info('Opening attribute window')
+ click_attr_window.show()
+
def show_update_log(self):
'''显示更新日志'''
logger.info('Opening update log')
@@ -1675,7 +1716,7 @@ def init_ui(self):
self.cache_dir_list = {'logs'} # 缓存文件路径的列表
self.cache_file_list = {'update.json'} # 缓存文件列表
- self.all_checkbox = UCheckBox('')
+ self.all_checkbox = UCheckBox(get_lang('db'))
self.all_checkbox.setTristate(True)
self.locked_checkbox = True # 临时切换
self.all_checkbox.setCheckState(Qt.PartiallyChecked) # 初始状态为部分选中
@@ -1989,8 +2030,6 @@ def __init__(self):
self.init_ui()
self.down_thread = None # 下载线程
- new_color_bar(self)
-
def init_ui(self):
# 创建面板
logger.debug('Create layout')
@@ -2157,161 +2196,35 @@ def on_open_update_log(self):
# 打开更新日志
update_window.on_open_update_log()
-class FastSetClickWindow(UDialog):
- def __init__(self):
- logger.debug('Initizalizing fast set click window')
-
- super().__init__()
- self.setWindowTitle(get_lang('75'))
- self.setGeometry(100, 100, 475, 125)
- self.setWindowFlags(
- Qt.Window | Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint
- ) # 设置窗口属性
- self.setFixedSize(self.width(), self.height()) # 固定窗口大小
-
- logger.debug('Initizalizing value')
- self.total_run_time = 0 # 总运行时间
-
- logger.debug('Initizalizing ui')
-
- if dev_flags.get('decoupling', False):
- self.init_ui()
- else:
- self.init_ui_old()
-
- def init_ui_old(self):
- # 创建主控件和布局
- central_layout = QVBoxLayout()
-
- # 单位输入框
- unit_layout = UnitInputLayout()
-
- self.input_delay = QLineEdit()
- self.input_delay.setFixedWidth(300)
- self.input_delay.setFixedHeight(30)
-
- self.delay_combo = QComboBox()
- self.delay_combo.addItems([get_lang('ms', source=unit_lang), get_lang('s', source=unit_lang)])
- self.delay_combo.setFixedWidth(60)
- self.delay_combo.setFixedHeight(30)
-
- unit_layout.addUnitRow(get_lang('11'), self.input_delay, self.delay_combo)
-
- self.input_times = QLineEdit()
- self.input_times.setFixedWidth(300)
- self.input_times.setFixedHeight(30)
-
- self.times_combo = QComboBox()
- self.times_combo.addItems([get_lang('66'), get_lang('2a'), get_lang('2b')])
-
- unit_layout.addUnitRow(get_lang('5c'), self.input_times, self.times_combo)
-
- # 总连点时长提示
- self.total_time_label = QLabel(main_window.total_time_label.text())
- self.total_time_label.setAlignment(Qt.AlignHCenter)
- set_style(self.total_time_label, StyleClass.big_16)
-
- # 创建布局
- logger.debug('Create button layout')
-
- central_layout.addLayout(unit_layout)
- central_layout.addWidget(self.total_time_label)
- self.setLayout(central_layout)
-
- # 按钮信号连接
- logger.debug('Signal connection')
-
- # 双向同步
- # 主窗口同步
- main_window.input_delay.textChanged.connect(lambda: self.sync_input(QLineEdit.text, QLineEdit.setText, main_window.input_delay, self.input_delay))
- main_window.input_times.textChanged.connect(lambda: self.sync_input(QLineEdit.text, QLineEdit.setText, main_window.input_times, self.input_times))
- main_window.delay_combo.currentIndexChanged.connect(lambda: self.sync_input(QComboBox.currentIndex, QComboBox.setCurrentIndex, main_window.delay_combo, self.delay_combo))
- main_window.times_combo.currentIndexChanged.connect(lambda: self.sync_input(QComboBox.currentIndex, QComboBox.setCurrentIndex, main_window.times_combo, self.times_combo))
- main_window.total_time_label.textChanged.connect(lambda: self.sync_input(QLabel.text, QLabel.setText, main_window.total_time_label, self.total_time_label))
-
- # 本窗口同步
- self.input_delay.textChanged.connect(lambda: self.sync_input(QLineEdit.text, QLineEdit.setText, self.input_delay, main_window.input_delay))
- self.input_times.textChanged.connect(lambda: self.sync_input(QLineEdit.text, QLineEdit.setText, self.input_times, main_window.input_times))
- self.delay_combo.currentIndexChanged.connect(lambda: self.sync_input(QComboBox.currentIndex, QComboBox.setCurrentIndex, self.delay_combo, main_window.delay_combo))
- self.times_combo.currentIndexChanged.connect(lambda: self.sync_input(QComboBox.currentIndex, QComboBox.setCurrentIndex, self.times_combo, main_window.times_combo))
-
- logger.debug('Initizalizing fast set click window successful.')
-
- def init_ui(self):
- self.ui = UIWindow(uiml.compile_ui_file(get_resource_path('ui', 'fastClick.gui')))
-
- self.setLayout(self.ui.show())
-
- # 主窗口同步 主窗口独立文件没有完成
- main_window.input_delay.textChanged.connect(lambda: self.sync_input(QLineEdit.text, QLineEdit.setText, main_window.input_delay, self.ui.find_widget('central_layout.unit_layout.input_delay')))
- main_window.input_times.textChanged.connect(lambda: self.sync_input(QLineEdit.text, QLineEdit.setText, main_window.input_times, self.ui.find_widget('central_layout.unit_layout.input_times')))
- main_window.delay_combo.currentIndexChanged.connect(lambda: self.sync_input(QComboBox.currentIndex, QComboBox.setCurrentIndex, main_window.delay_combo, self.ui.find_widget('central_layout.unit_layout.delay_combo')))
- main_window.times_combo.currentIndexChanged.connect(lambda: self.sync_input(QComboBox.currentIndex, QComboBox.setCurrentIndex, main_window.times_combo, self.ui.find_widget('central_layout.unit_layout.times_combo')))
- main_window.total_time_label.textChanged.connect(lambda: self.sync_input(QLabel.text, QLabel.setText, main_window.total_time_label, self.ui.find_widget('central_layout.total_time_label')))
-
- def sync_input(self, get_handle, set_handle, source, dest):
- '''同步输入框'''
- set_handle(dest, get_handle(source))
-
class ClickAttrWindow(UDialog):
def __init__(self):
logger.debug('Initizalizing click attribute window')
super().__init__()
- self.setWindowTitle(get_lang('8c'))
+ self.setWindowTitle(filter_hotkey(get_lang('8c')))
# 定义变量
self.timer = QTimer(self)
- self.timer.timeout.connect(self.update_attr if dev_flags.get('decoupling', False) else self.update_attr_old)
+ self.timer.timeout.connect(self.update_attr)
self.timer.start(setting_value.soft_delay)
- if dev_flags.get('decoupling', False):
- self.init_ui()
- else:
- self.init_ui_old()
+ self.init_ui()
def init_ui(self):
- self.layout_list = UIWindow(uiml.compile_ui_file(get_resource_path('ui', 'clickattr.gui')))
+ self.layout_list = UIWindow(uiml.compile_ui_file(get_resource_path('ui', 'clickattr.gui'), additional=self.addtional_local_value))
self.setLayout(self.layout_list.show())
logger.debug('Initizalizing click attribute window successful.')
-
- def init_ui_old(self):
- # 创建主布局
- central_layout = QVBoxLayout()
-
- # 内容
- self.left_clicked = QLabel(f'{get_lang('0d')}:')
- self.right_clicked = QLabel(f'{get_lang('0e')}:')
- self.click_delay = QLabel(f'{get_lang('78')}:')
- self.click_times = QLabel(f'{get_lang('5c')}:')
- self.paused = QLabel(f'{get_lang('0f')}:')
- self.stopped = QLabel(f'{get_lang('0e')}:')
- self.total_run_time = QLabel(f'{get_lang('2c')}:')
-
- # 底边栏
- bottom_layout = QHBoxLayout()
- ok_button = QPushButton(get_lang('1e'))
- set_style(ok_button, StyleClass.selected)
- ok_button.clicked.connect(self.close)
-
- # 布局
- bottom_layout.addStretch(1)
- bottom_layout.addWidget(ok_button)
-
- central_layout.addWidget(self.left_clicked)
- central_layout.addWidget(self.right_clicked)
- central_layout.addWidget(self.click_delay)
- central_layout.addWidget(self.click_times)
- central_layout.addWidget(self.paused)
- central_layout.addWidget(self.stopped)
- central_layout.addWidget(self.total_run_time)
- central_layout.addLayout(bottom_layout)
-
- self.setLayout(central_layout)
def update_attr(self):
'''更新属性'''
logger.debug('update attribute')
+ if is_error:
+ _delay_num = get_lang('14')
+ _time_num = get_lang('14')
+ else:
+ _delay_num = get_unit_text(delay_num)
+ _time_num = get_lang('2b') if is_inf else str(time_num) + get_lang('66')
+
left_clicked = self.layout_list.find_widget('central_layout.left_clicked')
right_clicked = self.layout_list.find_widget('central_layout.right_clicked')
click_delay = self.layout_list.find_widget('central_layout.click_delay')
@@ -2319,38 +2232,14 @@ def update_attr(self):
paused = self.layout_list.find_widget('central_layout.paused')
stopped = self.layout_list.find_widget('central_layout.stopped')
total_run_time = self.layout_list.find_widget('central_layout.total_run_time')
-
+
left_clicked.setText(f'{get_lang('0c')}: {get_lang('7b') if clicker.left_clicked else get_lang('7c')}')
right_clicked.setText(f'{get_lang('0d')}: {get_lang('7b') if clicker.right_clicked else get_lang('7c')}')
- click_delay.setText(f'{get_lang('78')}: {delay_num}{get_lang('ms', source=unit_lang)}')
- click_times.setText(f'{get_lang('5c')}: {get_lang('2b') if is_inf else time_num}')
+ click_delay.setText(f'{get_lang('78')}: {_delay_num}')
+ click_times.setText(f'{get_lang('5c')}: {_time_num}')
paused.setText(f'{get_lang('0f')}: {get_lang('79') if clicker.paused else get_lang('7a')}')
stopped.setText(f'{get_lang('0e')}: {get_lang('79') if not clicker.running else get_lang('7a')}')
- try:
- if is_inf:
- total_run_time.setText(f'{get_lang('2c')}: {get_lang('2b')}')
- else:
- total_run_time.setText(f'{get_lang('2c')}: {main_window.total_run_time[0]:.2f}{main_window.total_run_time[1]}')
- except TypeError:
- value = get_unit_value(main_window.total_run_time)
- total_run_time.setText(f'{get_lang('2c')}: {value[0]:.2f}{value[1]}')
-
- def update_attr_old(self):
- logger.debug('update attribute')
- self.left_clicked.setText(f'{get_lang('0c')}: {get_lang('7b') if clicker.left_clicked else get_lang('7c')}')
- self.right_clicked.setText(f'{get_lang('0d')}: {get_lang('7b') if clicker.right_clicked else get_lang('7c')}')
- self.click_delay.setText(f'{get_lang('78')}: {delay_num}{get_lang('ms', source=unit_lang)}')
- self.click_times.setText(f'{get_lang('5c')}: {get_lang('2b') if is_inf else time_num}')
- self.paused.setText(f'{get_lang('0f')}: {get_lang('79') if clicker.paused else get_lang('7a')}')
- self.stopped.setText(f'{get_lang('0e')}: {get_lang('79') if not clicker.running else get_lang('7a')}')
- try:
- if is_inf:
- self.total_run_time.setText(f'{get_lang('2c')}: {get_lang('2b')}')
- else:
- self.total_run_time.setText(f'{get_lang('2c')}: {main_window.total_run_time[0]:.2f}{main_window.total_run_time[1]}')
- except TypeError:
- value = get_unit_value(main_window.total_run_time)
- self.total_run_time.setText(f'{get_lang('2c')}: {value[0]:.2f}{value[1]}')
+ total_run_time.setText(main_window.total_time_label.text())
class SettingWindow(SelectUI, UMainWindow):
click_setting_changed = Signal()
@@ -2376,6 +2265,9 @@ def __init__(self, values:dict | None = None):
self.page_choice_buttons = [get_lang('42'), get_lang('a6'), get_lang('43'), get_lang('44'), get_lang('69'), filter_hotkey(get_lang('5f')), get_lang('d3')]
self.ui_file_name = ['general.gui', 'style.gui', 'clicker.gui', 'updater.gui', 'hotkey.gui', 'document.gui', 'notify.gui', 'flags.gui']
+ # 主程序
+ self.app = get_application_instance()
+
self.create_setting_page_value()
self.last_page = None
@@ -2383,33 +2275,13 @@ def __init__(self, values:dict | None = None):
self.values = {} if values is None else values
self.code_list = {}
- if dev_flags.get('decoupling', False):
- self.init_ui()
- else:
- self.init_ui_old()
+ self.init_ui()
self.check_values() # 检查设置值
# 连接信号
clicker.started.connect(self.on_clicker_started)
logger.debug('Initizalizing setting window successful.')
-
- def init_ui_old(self):
- '''创建设置界面'''
- self.draw_page_choice()
- self.init_right_pages_old()
-
- def init_right_pages_old(self):
- '''初始化右侧设置页面'''
- # 创建堆叠窗口部件
- self.stacked_widget = QStackedWidget()
-
- # 为每个左侧选项创建一个对应的右侧页面
- for i, page_title in enumerate(self.page_choice_buttons):
- page = self.create_setting_page_old(page_title)
- self.stacked_widget.addWidget(page)
- self.pages.append(page)
-
# 将堆叠窗口部件设置为右侧滚动区域的内容
self.right_scroll.setWidget(self.stacked_widget)
@@ -2440,10 +2312,9 @@ def check_values(self):
def get_code(self, id) -> UIWindow:
return self.code_list[id+'.gui']
- def create_setting_page_old(self, title):
+ def create_setting_page(self, title):
logger.info(f'Loading setting page: {title}')
page = QWidget()
- layout = QVBoxLayout(page)
def set_content_label(text):
logger.debug(f'Set content label: {text}')
@@ -2452,239 +2323,67 @@ def set_content_label(text):
def create_horizontal_line():
logger.debug('Create horizontal line')
line = UFrame()
- line.setFrameShape(UFrame.Shape.HLine) # 水平线
return line
def parse_hotkey(input: UHotkeyLineEdit):
return input.text().split('+')
-
- # 标题标签
- title_label = QLabel(title)
- set_style(title_label, StyleClass.big_24)
-
- # 内容标签
- content_label = QLabel(get_lang('7d'))
- set_style(content_label, StyleClass.dest)
- # 布局
- layout.addWidget(title_label)
- layout.addWidget(content_label)
- layout.addWidget(create_horizontal_line())
+ self.addtional_local_value.update({'title': title})
+ if title in [self.page_general, self.page_style, self.page_click]:
+ layout_code = UIWindow(uiml.compile_ui_file(get_resource_path('ui', 'settings', self.ui_file_name[self.page_choice_buttons.index(title)]), additional=self.addtional_local_value))
+ self.code_list[self.ui_file_name[self.page_choice_buttons.index(title)]] = layout_code
+
+ layout = layout_code.show()
+ page.setLayout(layout)
+ else:
+ layout = QVBoxLayout(page)
+ # 标题标签
+ title_label = QLabel(title)
+ set_style(title_label, StyleClass.big_24)
- # 主程序
- self.app = get_application_instance()
+ # 内容标签
+ content_label = QLabel(get_lang('7d'))
+ set_style(content_label, StyleClass.dest)
+
+ # 布局
+ layout.addWidget(title_label)
+ layout.addWidget(content_label)
+ layout.addWidget(create_horizontal_line())
# 添加一些示例设置控件
match title:
case self.page_general:
- set_content_label(get_lang('7f'))
- # 选择语言
- lang_choice_layout = QHBoxLayout() # 语言选择布局
- self.lang_choice = QComboBox()
- self.lang_choice.addItems([get_lang('c4')] + [i['lang_name'] for i in langs])
- self.lang_choice.setCurrentIndex(setting_value.select_lang + 1)
- # 布局
- lang_choice_layout.addWidget(QLabel(f'{get_lang('45')}{get_lang('b5')}:')) # 选择语言提示
- lang_choice_layout.addWidget(self.lang_choice)
- lang_choice_layout.addStretch(1)
-
- # 显示托盘图标
- tray_layout = QHBoxLayout() # 窗口风格布局
- tray = UCheckBox(get_lang('80'))
- tray.setChecked(setting_value.show_tray_icon)
-
- tray_layout.addWidget(tray)
- tray_layout.addStretch(1)
-
- # 开机自启动
- start_layout = QHBoxLayout() # 开机自启动布局
- self.start_checkbox = UCheckBox(get_lang('b6'))
- self.start_checkbox.setChecked(auto_start_manager.auto_start)
-
- start_layout.addWidget(self.start_checkbox)
- start_layout.addStretch(1)
-
- auto_start_manager.updated.connect(lambda enb: self.start_checkbox.setChecked(enb))
- self.start_checkbox.checkStateChanged.connect(self.on_auto_start_changed)
-
- # 重置开机自启动
- repair_start_layout = QHBoxLayout() # 重置开机自启动布局
- repair_start_button = QPushButton(get_lang('20'))
+ auto_start_manager.updated.connect(lambda enb: self.get_code('general').find_widget('general.start_layout.start_checkbox').setChecked(enb))
+ self.get_code('general').find_widget('general.delay_layout.delay_label').setText(f'{get_lang('b0')}:{setting_value.soft_delay}{get_lang("ms", source=unit_lang)}')
+ case self.page_click:
+ self.default_delay: QLineEdit = self.get_code('clicker').find_widget('clicker.delay_layout.delay_input_layout.delay_input')
+ self.delay_combo: QComboBox = self.get_code('clicker').find_widget('clicker.delay_layout.delay_input_layout.delay_combo')
+ self.use_default_delay: QCheckBox = self.get_code('clicker').find_widget('clicker.delay_layout.error_use_default_delay')
+ if not self.default_delay.text():
+ self.use_default_delay.setEnabled(False)
+ self.default_time: QLineEdit = self.get_code('clicker').find_widget('clicker.times_layout.times_input_layout.times_input')
+ self.times_combo: QComboBox = self.get_code('clicker').find_widget('clicker.times_layout.times_input_layout.times_combo')
+ self.use_default_times: QCheckBox = self.get_code('clicker').find_widget('clicker.times_layout.error_use_default_times')
+ if not self.default_time.text():
+ self.use_default_times.setEnabled(False)
+ self.total_time_label: QLabel = self.get_code('clicker').find_widget('clicker.total_time_label')
+ if dev_flags.get('new_settings', False):
+ self.modify_using_default_input = self.get_code('clicker').find_widget('clicker.attributes_layout.modify_using_default_input')
+ self.modify_using_default_combo = self.get_code('clicker').find_widget('clicker.attributes_layout.modify_using_default_combo')
+ case self.page_update:
+ set_content_label(get_lang('87'))
+ # 选择更新检查提示
+ self.enable_update = UCheckBox(get_lang('48')) # 开启更新
+ self.enable_update.setChecked(setting_value.update_enabled)
- repair_tip = QLabel(get_lang('d1'))
- set_style(repair_tip, StyleClass.d_11)
+ update_disable_text = QLabel(get_lang('d0')) # 更新禁止提示
+ set_style(update_disable_text, StyleClass.d_11)
- repair_start_layout.addWidget(repair_start_button)
- repair_start_layout.addWidget(repair_tip)
- repair_start_layout.addStretch(1)
+ self.update_notify = UCheckBox(get_lang('4a')) # 更新提示
+ self.update_notify.setChecked(setting_value.update_notify)
- # 延迟
- soft_delay_layout = QHBoxLayout() # 颜色延迟布局
- soft_delay_setting = setting_value.soft_delay
-
- soft_delay = QSlider(Qt.Horizontal)
- soft_delay.setMinimum(5)
- soft_delay.setMaximum(100)
- soft_delay.setValue(soft_delay_setting // 10)
- soft_delay.setTickPosition(QSlider.TicksBelow)
- soft_delay.setTickInterval(10)
- soft_delay.setFixedWidth(200)
-
- delay_tip_label = QLabel(get_lang("8a"))
- set_style(delay_tip_label, StyleClass.d_11)
-
- # 布局
- soft_delay_layout.addWidget(QLabel(f'{get_lang('b0')}\n{get_lang('b5')}:'))
- soft_delay_layout.addWidget(soft_delay)
- soft_delay_layout.addStretch(1)
-
- delay_layout_text = QLabel(f'{get_lang('b0')}:{soft_delay_setting}{get_lang("ms", source=unit_lang)}')
- set_style(delay_layout_text, StyleClass.big_16)
-
- # 反馈路径
- feedback_layout = QHBoxLayout() # 反馈路径布局
-
- feedback_input = QLineEdit()
- feedback_input.setText(setting_value.feedback)
-
- repair_feedback_link_button = QPushButton(get_lang('20'))
-
- # 布局
- feedback_layout.addWidget(QLabel(get_lang('c3')), 1)
- feedback_layout.addWidget(feedback_input, 6)
- feedback_layout.addWidget(repair_feedback_link_button, 1)
-
- # 重置所有设置
- repair_layout = QHBoxLayout() # 重置布局
- self.repair_button = QPushButton(get_lang('5e'))
-
- repair_layout.addWidget(self.repair_button)
- repair_layout.addStretch(1)
-
- # 无实验项时自动隐藏实验室
- hide_flags_checkbox = UCheckBox(get_lang('d5'))
- hide_flags_checkbox.setChecked(setting_value.hide_flags)
-
- # 布局
- layout.addLayout(lang_choice_layout)
- layout.addLayout(tray_layout)
- layout.addWidget(create_horizontal_line())
- layout.addLayout(start_layout)
-
- layout.addLayout(repair_start_layout)
- if dev_flags.get('new_settings', False):
- layout.addWidget(create_horizontal_line())
- layout.addLayout(feedback_layout)
- layout.addWidget(create_horizontal_line())
- layout.addLayout(soft_delay_layout)
- layout.addWidget(delay_layout_text)
- layout.addWidget(delay_tip_label)
- layout.addWidget(create_horizontal_line())
- if dev_flags.get('new_settings', False):
- layout.addWidget(hide_flags_checkbox)
- layout.addWidget(create_horizontal_line())
- layout.addLayout(repair_layout)
-
- # 绑定事件
- self.lang_choice.currentIndexChanged.connect(lambda: self.on_need_restart_setting_changed_old(lambda: self.lang_choice.currentIndex() - 1, SettingText.select_lang))
- tray.checkStateChanged.connect(lambda: self.on_setting_changed(tray.isChecked, SettingText.show_tray_icon))
- tray.checkStateChanged.connect(lambda: self.app.setQuitOnLastWindowClosed(not tray.isChecked())) # 关闭窗口时不退出应用
- soft_delay.valueChanged.connect(lambda: self.on_setting_changed(lambda: soft_delay.value() * 10 if soft_delay.value() > 0 else 1, SettingText.soft_delay))
- soft_delay.valueChanged.connect(lambda: delay_layout_text.setText(f'{get_lang('b0')}: {soft_delay.value() * 10 if soft_delay.value() > 0 else 1}{get_lang("ms", source=unit_lang)}'))
- self.repair_button.clicked.connect(self.repair_all_settings)
- feedback_input.textChanged.connect(lambda: self.on_setting_changed(feedback_input.text, SettingText.feedback))
- repair_feedback_link_button.clicked.connect(lambda: self.repair_settings(SettingText.feedback))
- repair_start_button.clicked.connect(self.repair_auto_start)
- hide_flags_checkbox.checkStateChanged.connect(lambda: self.on_setting_changed(hide_flags_checkbox.isChecked, SettingText.hide_flags))
- hide_flags_checkbox.checkStateChanged.connect(lambda: self.restart_window())
- case self.page_click:
- set_content_label(get_lang('84'))
- # 选择默认连点器延迟
- layout_delay = QVBoxLayout() # 延迟布局
- unit_delay_layout = QHBoxLayout() # 窗口风格布局
- self.default_delay = QLineEdit()
- self.default_delay.setText(setting_value.click_delay)
- self.delay_combo = QComboBox()
- self.delay_combo.addItems([get_lang('ms', source=unit_lang), get_lang('s', source=unit_lang)])
- self.delay_combo.setCurrentIndex(setting_value.delay_unit)
-
- unit_delay_layout.addWidget(QLabel(get_lang('46') + ': '))
- unit_delay_layout.addWidget(self.default_delay)
- unit_delay_layout.addWidget(self.delay_combo)
- unit_delay_layout.addStretch(1)
-
- # 连点出错时使用默认值
- use_default_delay = UCheckBox(get_lang('47'))
- use_default_delay.setChecked(setting_value.delay_error_use_default)
- if not self.default_delay.text():
- use_default_delay.setEnabled(False)
-
- # 布局
- layout_delay.addLayout(unit_delay_layout)
- layout_delay.addWidget(use_default_delay)
- layout_delay.addWidget(create_horizontal_line())
- layout_delay.addStretch(1)
-
- # 连点器默认点击次数
- layout_time = QVBoxLayout() # 次数布局
- unit_time_layout = QHBoxLayout() # 窗口风格布局
- self.default_time = QLineEdit()
- self.default_time.setText(str(setting_value.click_times))
- self.times_combo = QComboBox()
- self.times_combo.addItems([get_lang('66'), get_lang('2a'), get_lang('2b')])
- self.times_combo.setCurrentIndex(setting_value.times_unit)
-
- unit_time_layout.addWidget(QLabel(get_lang('85') + ': '))
- unit_time_layout.addWidget(self.default_time)
- unit_time_layout.addWidget(self.times_combo)
- unit_time_layout.addStretch(1)
-
- # 连点出错时使用默认值
- use_default_time = UCheckBox(get_lang('86'))
- use_default_time.setChecked(setting_value.times_error_use_default)
- if not self.default_time.text():
- use_default_time.setEnabled(False)
- self.total_time_label = QLabel(f'{get_lang('2c')}: {get_lang('61')}')
- self.total_time_label.setAlignment(Qt.AlignHCenter)
- set_style(self.total_time_label, StyleClass.big_16)
-
- # 布局
- layout_time.addLayout(unit_time_layout)
- layout_time.addWidget(use_default_time)
- layout_time.addWidget(create_horizontal_line())
- layout_time.addStretch(1)
-
- # 布局
- layout.addLayout(layout_delay)
- layout.addLayout(layout_time)
- layout.addWidget(self.total_time_label)
- layout.addStretch(1)
-
- # 连接信号
- self.default_delay.textChanged.connect(lambda: self.on_default_input_changed(self.default_delay, SettingText.click_delay, use_default_delay))
- self.default_delay.textChanged.connect(lambda: on_input_change(type=InputChange.setting_window))
- use_default_delay.checkStateChanged.connect(lambda: self.on_setting_changed(use_default_delay.isChecked, SettingText.delay_error_use_default))
- self.default_time.textChanged.connect(lambda: self.on_default_input_changed(self.default_time, SettingText.click_times, use_default_time))
- self.default_time.textChanged.connect(lambda: on_input_change(type=InputChange.setting_window))
- use_default_time.checkStateChanged.connect(lambda: self.on_setting_changed(use_default_time.isChecked, SettingText.times_error_use_default))
- self.delay_combo.currentIndexChanged.connect(lambda: self.on_setting_changed(self.delay_combo.currentIndex, SettingText.delay_unit))
- self.delay_combo.currentIndexChanged.connect(lambda: on_input_change(type=InputChange.setting_window))
- self.times_combo.currentIndexChanged.connect(lambda: self.on_setting_changed(self.times_combo.currentIndex, SettingText.times_unit))
- self.times_combo.currentIndexChanged.connect(lambda: on_input_change(type=InputChange.setting_window))
- case self.page_update:
- set_content_label(get_lang('87'))
- # 选择更新检查提示
- self.enable_update = UCheckBox(get_lang('48')) # 开启更新
- self.enable_update.setChecked(setting_value.update_enabled)
-
- update_disable_text = QLabel(get_lang('d0')) # 更新禁止提示
- set_style(update_disable_text, StyleClass.d_11)
-
- self.update_notify = UCheckBox(get_lang('4a')) # 更新提示
- self.update_notify.setChecked(setting_value.update_notify)
-
- self.quiet_install = UCheckBox(get_lang('49')) # 静默安装
- self.quiet_install.setChecked(setting_value.quiet_update)
+ self.quiet_install = UCheckBox(get_lang('49')) # 静默安装
+ self.quiet_install.setChecked(setting_value.quiet_update)
self.update_ok = UCheckBox(get_lang('4c')) # 更新完成弹出提示
self.update_ok.setChecked(setting_value.update_ok_notify)
@@ -2716,537 +2415,60 @@ def parse_hotkey(input: UHotkeyLineEdit):
self.update_ok.checkStateChanged.connect(self.on_sync_ok_notice)
else:
self.on_enable_update(self.enable_update.isChecked())
- case self.page_style:
- set_content_label(get_lang('a7'))
- # 选择窗口风格
- style_layout = QHBoxLayout() # 窗口风格布局
- self.style_choice = QComboBox()
-
- items = list(style_indexes[select_lang]['lang_package'].values())
-
- self.style_choice.addItems([get_lang('82')] + items)
- self.style_choice.setCurrentIndex(setting_value.select_style)
-
- # 布局
- style_layout.addWidget(QLabel(get_lang('81'))) # 选择窗口风格提示
- style_layout.addWidget(self.style_choice)
- style_layout.addStretch(1)
-
- style_use_windows_layout = QHBoxLayout() # 颜色使用windows按钮布局
- style_choice_use_windows = UCheckBox(get_lang('a8'))
- tip_label = QLabel(get_lang('b4'))
- set_style(tip_label, StyleClass.d_11)
- style_choice_use_windows.setChecked(setting_value.use_windows_color)
-
- # 布局
- style_use_windows_layout.addWidget(style_choice_use_windows)
- style_use_windows_layout.addStretch(1)
-
- theme_layout = QHBoxLayout() # 主题布局
- theme_tip_label = QLabel(get_lang('4b'))
- set_style(theme_tip_label, StyleClass.d_11)
- theme_combo = QComboBox()
- theme_combo.addItems(QStyleFactory.keys())
- theme_combo.setCurrentText(setting_value.theme)
-
- # 布局
- theme_layout.addWidget(QLabel(get_lang('23')))
- theme_layout.addWidget(theme_combo)
- theme_layout.addStretch(1)
-
- # 布局
- layout.addLayout(style_layout)
- layout.addWidget(create_horizontal_line())
- layout.addLayout(style_use_windows_layout)
- layout.addWidget(tip_label)
- layout.addWidget(create_horizontal_line())
- layout.addLayout(theme_layout)
- layout.addWidget(theme_tip_label)
- layout.addWidget(create_horizontal_line())
-
- # 连接信号
- self.style_choice.currentIndexChanged.connect(lambda: self.on_setting_changed(self.style_choice.currentIndex, SettingText.select_style))
- style_choice_use_windows.checkStateChanged.connect(lambda: self.on_setting_changed(style_choice_use_windows.isChecked, SettingText.use_windows_color))
- theme_combo.currentIndexChanged.connect(lambda: self.on_setting_changed(theme_combo.currentText, SettingText.theme))
- theme_combo.currentIndexChanged.connect(lambda: refresh.run())
- theme_combo.currentIndexChanged.connect(lambda: self.app.setStyle(theme_combo.currentText()))
case self.page_hotkey:
set_content_label(get_lang('21'))
self.hotkey_enabled = UCheckBox(get_lang('c9')) # 热键启用
self.hotkey_enabled.setChecked(setting_value.hotkey_enabled)
-
- # 左键连点
- self.left_click_layout = QHBoxLayout()
- self.left_click_input = UHotkeyLineEdit() # 左键连点输入框
- self.left_click_input.setText(format_keys(setting_value.left_click_hotkey))
- self.left_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.left_click_layout.addWidget(QLabel(f'{get_lang('0c')}: '), 1) # 左键连点提示
- self.left_click_layout.addWidget(self.left_click_input, 6)
- self.left_click_layout.addWidget(self.left_repair_button, 1)
- self.left_click_layout.addStretch()
-
- # 右键连点
- self.right_click_layout = QHBoxLayout() # 右键连点布局
- self.right_click_input = UHotkeyLineEdit() # 右键连点输入框
- self.right_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- self.right_click_input.setText(format_keys(setting_value.right_click_hotkey))
-
- # 布局
- self.right_click_layout.addWidget(QLabel(f'{get_lang('0d')}: '), 1) # 右键连点提示
- self.right_click_layout.addWidget(self.right_click_input, 6)
- self.right_click_layout.addWidget(self.right_repair_button, 1)
- self.right_click_layout.addStretch()
-
- # 暂停/重启连点
- self.pause_click_layout = QHBoxLayout() # 暂停/重启连点布局
- self.pause_click_input = UHotkeyLineEdit() # 暂停/重启连点输入框
- self.pause_click_input.setText(format_keys(setting_value.pause_click_hotkey))
- self.pause_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.pause_click_layout.addWidget(QLabel(f'{get_lang('6b')}: '), 1) # 暂停/重启连点提示
- self.pause_click_layout.addWidget(self.pause_click_input, 6)
- self.pause_click_layout.addWidget(self.pause_repair_button, 1)
- self.pause_click_layout.addStretch()
-
- # 停止连点
- self.stop_click_layout = QHBoxLayout() # 停止连点布局
- self.stop_click_input = UHotkeyLineEdit() # 停止连点输入框
- self.stop_click_input.setText(format_keys(setting_value.stop_click_hotkey))
- self.stop_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.stop_click_layout.addWidget(QLabel(f'{get_lang('6c')}: '), 1) # 停止连点提示
- self.stop_click_layout.addWidget(self.stop_click_input, 6)
- self.stop_click_layout.addWidget(self.stop_repair_button, 1)
- self.stop_click_layout.addStretch()
-
- # 连点属性
- self.click_attr_layout = QHBoxLayout() # 连点属性布局
- self.click_attr_input = UHotkeyLineEdit() # 连点属性输入框
- self.click_attr_input.setText(format_keys(setting_value.click_attr_hotkey))
- self.click_attr_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.click_attr_layout.addWidget(QLabel(f'{get_lang('8c')}: '), 1) # 连点属性提示
- self.click_attr_layout.addWidget(self.click_attr_input, 6)
- self.click_attr_layout.addWidget(self.click_attr_button, 1)
- self.click_attr_layout.addStretch()
-
- # 快速连点
- self.fast_click_layout = QHBoxLayout() # 快速连点布局
- self.fast_click_input = UHotkeyLineEdit() # 快速连点输入框
- self.fast_click_input.setText(format_keys(setting_value.fast_click_hotkey))
- self.fast_click_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.fast_click_layout.addWidget(QLabel(f'{get_lang('75')}: '), 1) # 快速连点提示
- self.fast_click_layout.addWidget(self.fast_click_input, 6)
- self.fast_click_layout.addWidget(self.fast_click_button, 1)
- self.fast_click_layout.addStretch()
-
- # 主窗口
- self.main_window_layout = QHBoxLayout() # 主窗口布局
- self.main_window_input = UHotkeyLineEdit() # 主窗口输入框
- self.main_window_input.setText(format_keys(setting_value.main_window_hotkey))
- self.main_window_button = QPushButton(get_lang('20')) # 还原默认设置按钮
- # 布局
- self.main_window_layout.addWidget(QLabel(f'{get_lang('76')}: '), 1) # 主窗口提示
- self.main_window_layout.addWidget(self.main_window_input, 6)
- self.main_window_layout.addWidget(self.main_window_button, 1)
- self.main_window_layout.addStretch()
+ self.hotkeys_widget_list = []
- # 布局
- if dev_flags.get('new_settings', False):
- layout.addWidget(self.hotkey_enabled)
- layout.addLayout(self.left_click_layout)
- layout.addLayout(self.right_click_layout)
- layout.addLayout(self.pause_click_layout)
- layout.addLayout(self.stop_click_layout)
- layout.addLayout(self.click_attr_layout)
- layout.addLayout(self.fast_click_layout)
- layout.addLayout(self.main_window_layout)
-
- # 连接信号
- self.left_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.left_click_input), SettingText.left_click_hotkey))
- self.right_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.right_click_input), SettingText.right_click_hotkey))
- self.pause_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.pause_click_input), SettingText.pause_click_hotkey))
- self.stop_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.stop_click_input), SettingText.stop_click_hotkey))
- self.click_attr_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.click_attr_input), SettingText.click_attr_hotkey))
- self.fast_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.fast_click_input), SettingText.fast_click_hotkey))
- self.main_window_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.main_window_input), SettingText.main_window_hotkey))
-
- self.left_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.left_click_hotkey))
- self.right_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.right_click_hotkey))
- self.pause_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.pause_click_hotkey))
- self.stop_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.stop_click_hotkey))
- self.click_attr_button.clicked.connect(lambda: self.repair_settings(SettingText.click_attr_hotkey))
- self.fast_click_button.clicked.connect(lambda: self.repair_settings(SettingText.fast_click_hotkey))
- self.main_window_button.clicked.connect(lambda: self.repair_settings(SettingText.main_window_hotkey))
-
self.hotkey_enabled.checkStateChanged.connect(self.on_enable_hotkey_changed)
self.on_enable_hotkey_changed(self.hotkey_enabled.isChecked() if dev_flags.get('new_settings', False) else True)
- case self.page_doc:
- set_content_label(get_lang('ca'))
-
- default_doc_layout = QHBoxLayout() # 默认打开文档布局
-
- default_doc_link = QLineEdit() # 默认打开文档链接
- default_doc_link.setText(setting_value.default_doc_link)
- repair_default_doc_link_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- default_doc_layout.addWidget(QLabel(get_lang('c2')), 1) # 默认打开文档提示
- default_doc_layout.addWidget(default_doc_link, 6)
- if dev_flags.get('new_settings', False):
- default_doc_layout.addWidget(repair_default_doc_link_button, 1)
- default_doc_layout.addStretch()
-
- default_lang_layout = QHBoxLayout() # 默认文档语言布局
- lang_choice = QComboBox() # 语言选择框
- lang_choice.addItems([get_lang('45'), get_lang('c4')] + [i['lang_name'] for i in langs if i['supported']])
- lang_choice.setCurrentIndex(setting_value.lang_doc)
-
- # 布局
- default_lang_layout.addWidget(QLabel(get_lang('c5'))) # 默认文档语言提示
- default_lang_layout.addWidget(lang_choice)
- default_lang_layout.addStretch()
-
- update_log_path_layout = QHBoxLayout() # 更新日志路径布局
- update_log_path_input = QLineEdit() # 更新日志路径输入框
- update_log_path_input.setText(setting_value.update_log_path)
-
- repair_update_log_path_button = QPushButton(get_lang('20')) # 还原默认路径按钮
-
- # 布局
- update_log_path_layout.addWidget(QLabel(get_lang('c6')), 1) # 更新日志路径提示
- update_log_path_layout.addWidget(update_log_path_input, 6)
- if dev_flags.get('new_settings', False):
- update_log_path_layout.addWidget(repair_update_log_path_button, 1)
- update_log_path_layout.addStretch()
-
- label = QLabel(get_lang('c7'))
- # 布局
- set_style(label, StyleClass.d_11)
-
- layout.addLayout(default_doc_layout)
- layout.addLayout(default_lang_layout)
- layout.addWidget(create_horizontal_line())
- layout.addLayout(update_log_path_layout)
- layout.addWidget(create_horizontal_line())
- layout.addWidget(label)
-
- # 链接信号
- default_doc_link.textChanged.connect(lambda: self.on_setting_changed(default_doc_link.text, SettingText.default_doc_link))
- lang_choice.currentIndexChanged.connect(lambda: self.on_setting_changed(self.lang_choice.currentIndex, SettingText.lang_doc))
- update_log_path_input.textChanged.connect(lambda: self.on_setting_changed(update_log_path_input.text, SettingText.update_log_path))
- repair_default_doc_link_button.clicked.connect(lambda: self.repair_settings(SettingText.default_doc_link))
- repair_update_log_path_button.clicked.connect(lambda: self.repair_settings(SettingText.update_log_path))
- case self.page_notify:
- set_content_label(get_lang('cc'))
-
- # 更新提示
- self.notice_update_notify = UCheckBox(get_lang('4a'))
- self.notice_update_notify.setChecked(setting_value.update_notify)
-
- # 更新完成提示
- self.notice_update_ok_notify = UCheckBox(get_lang('4c'))
- self.notice_update_ok_notify.setChecked(setting_value.update_ok_notify)
-
- # 启用软件启动警告
- self.start_warning = UCheckBox(get_lang('cd'))
- tip_label = QLabel(get_lang('ce'))
- set_style(tip_label, StyleClass.d_11)
- self.start_warning.setChecked(setting_value.show_warning)
-
- self.package_warning = UCheckBox(get_lang('cf'))
- self.package_warning.setChecked(setting_value.show_package_warning)
-
- # 布局
- layout.addWidget(self.notice_update_notify)
- layout.addWidget(self.notice_update_ok_notify)
- layout.addWidget(create_horizontal_line())
- layout.addWidget(self.start_warning)
- layout.addWidget(tip_label)
- layout.addWidget(self.package_warning)
-
- # 连接信号
- self.notice_update_notify.checkStateChanged.connect(lambda: self.on_setting_changed(self.notice_update_notify.isChecked, SettingText.update_notify))
- self.notice_update_notify.checkStateChanged.connect(self.on_sync_notice)
- self.notice_update_ok_notify.checkStateChanged.connect(lambda: self.on_setting_changed(self.notice_update_ok_notify.isChecked, SettingText.update_ok_notify))
- self.notice_update_ok_notify.checkStateChanged.connect(self.on_sync_ok_notice)
- self.start_warning.checkStateChanged.connect(self.on_enable_warn)
- self.package_warning.checkStateChanged.connect(lambda: self.on_setting_changed(self.package_warning.isChecked, SettingText.show_package_warning))
-
- self.on_enable_update(self.enable_update.isChecked())
- self.on_warning_update(self.start_warning.isChecked())
- case self.page_flags:
- set_content_label(get_lang('d4'))
-
- if not dev_settings:
- layout.addWidget(QLabel('No dev settings found.'))
- else:
- for i in dev_settings:
- checkbox = UCheckBox(i['name'])
- if i['key'] == 'new_settings':
- checkbox.checkStateChanged.connect(lambda chk,idx=i['key']:(self.save_dev_config(chk, idx),self.window_restarted.emit(),))
- else:
- checkbox.checkStateChanged.connect(lambda chk,idx=i['key']:(self.save_dev_config(chk, idx)))
- checkbox.setChecked(dev_flags.get(i['key'], False))
- desc = QLabel(i['desc'])
- set_style(desc, StyleClass.d_11)
-
- layout.addWidget(checkbox)
- layout.addWidget(desc)
- layout.addWidget(create_horizontal_line())
-
- restart_layout = QHBoxLayout() # 重启提示布局
- self.restart_button = QPushButton(get_lang('7e'))
-
- set_style(self.restart_button, StyleClass.selected)
- self.restart_button.clicked.connect(self.restart)
-
- restart_layout.addStretch()
- restart_layout.addWidget(self.restart_button)
-
- if not settings_need_restart:
- self.restart_button.hide()
-
- layout.addLayout(restart_layout)
-
- # 添加弹簧,让内容靠上显示
- layout.addStretch()
-
- return page
-
- def create_setting_page(self, title):
- logger.info(f'Loading setting page: {title}')
- page = QWidget()
-
- def set_content_label(text):
- logger.debug(f'Set content label: {text}')
- content_label.setText(text)
-
- def create_horizontal_line():
- logger.debug('Create horizontal line')
- line = UFrame()
- return line
-
- def parse_hotkey(input: UHotkeyLineEdit):
- return input.text().split('+')
-
- # 主程序
- self.app = get_application_instance()
-
- if title in [self.page_general, self.page_style, self.page_click]:
- layout_code = UIWindow(uiml.compile_ui_file(get_resource_path('ui', 'settings', self.ui_file_name[self.page_choice_buttons.index(title)])))
- self.code_list[self.ui_file_name[self.page_choice_buttons.index(title)]] = layout_code
-
- layout = layout_code.show()
- page.setLayout(layout)
- else:
- layout = QVBoxLayout(page)
- # 标题标签
- title_label = QLabel(title)
- set_style(title_label, StyleClass.big_24)
-
- # 内容标签
- content_label = QLabel(get_lang('7d'))
- set_style(content_label, StyleClass.dest)
-
- # 布局
- layout.addWidget(title_label)
- layout.addWidget(content_label)
- layout.addWidget(create_horizontal_line())
-
- # 添加一些示例设置控件
- match title:
- case self.page_general:
- auto_start_manager.updated.connect(lambda enb: self.get_code('general').find_widget('general.start_layout.start_checkbox').setChecked(enb))
- self.get_code('general').find_widget('general.delay_layout.delay_label').setText(f'{get_lang('b0')}:{setting_value.soft_delay}{get_lang("ms", source=unit_lang)}')
- case self.page_click:
- self.default_delay: QLineEdit = self.get_code('clicker').find_widget('clicker.delay_layout.delay_input_layout.delay_input')
- self.delay_combo: QComboBox = self.get_code('clicker').find_widget('clicker.delay_layout.delay_input_layout.delay_combo')
- self.use_default_delay: QCheckBox = self.get_code('clicker').find_widget('clicker.delay_layout.error_use_default_delay')
- if not self.default_delay.text():
- self.use_default_delay.setEnabled(False)
- self.default_time: QLineEdit = self.get_code('clicker').find_widget('clicker.times_layout.times_input_layout.times_input')
- self.times_combo: QComboBox = self.get_code('clicker').find_widget('clicker.times_layout.times_input_layout.times_combo')
- self.use_default_times: QCheckBox = self.get_code('clicker').find_widget('clicker.times_layout.error_use_default_times')
- if not self.default_time.text():
- self.use_default_times.setEnabled(False)
- self.total_time_label: QLabel = self.get_code('clicker').find_widget('clicker.total_time_label')
- case self.page_update:
- set_content_label(get_lang('87'))
- # 选择更新检查提示
- self.enable_update = UCheckBox(get_lang('48')) # 开启更新
- self.enable_update.setChecked(setting_value.update_enabled)
-
- update_disable_text = QLabel(get_lang('d0')) # 更新禁止提示
- set_style(update_disable_text, StyleClass.d_11)
-
- self.update_notify = UCheckBox(get_lang('4a')) # 更新提示
- self.update_notify.setChecked(setting_value.update_notify)
-
- self.quiet_install = UCheckBox(get_lang('49')) # 静默安装
- self.quiet_install.setChecked(setting_value.quiet_update)
-
- self.update_ok = UCheckBox(get_lang('4c')) # 更新完成弹出提示
- self.update_ok.setChecked(setting_value.update_ok_notify)
-
- update_frequency_layout = QHBoxLayout() # 更新频率布局
- self.update_frequency = QComboBox() # 更新频率
- self.update_frequency.addItems([get_lang('bd'), get_lang('be'), get_lang('bf'), get_lang('c0')])
- self.update_frequency.setCurrentIndex(setting_value.update_frequency)
- update_frequency_layout.addWidget(QLabel(get_lang('c1')))
- update_frequency_layout.addWidget(self.update_frequency)
- update_frequency_layout.addStretch(1)
-
- # 布局
- layout.addWidget(self.enable_update)
- layout.addWidget(update_disable_text)
- layout.addWidget(self.update_notify)
- layout.addWidget(self.quiet_install)
- layout.addWidget(self.update_ok)
- layout.addLayout(update_frequency_layout)
-
- # 连接信号
- self.enable_update.checkStateChanged.connect(self.on_enable_update_changed)
- self.update_notify.checkStateChanged.connect(lambda: self.on_setting_changed(self.update_notify.isChecked, SettingText.update_notify))
- self.quiet_install.checkStateChanged.connect(lambda: self.on_setting_changed(self.quiet_install.isChecked, SettingText.quiet_update))
- self.update_ok.checkStateChanged.connect(lambda: self.on_setting_changed(self.update_ok.isChecked, SettingText.update_ok_notify))
- self.update_frequency.currentIndexChanged.connect(lambda: self.on_setting_changed(self.update_frequency.currentIndex, SettingText.update_frequency))
- if dev_flags.get('new_settings', False):
- self.update_notify.checkStateChanged.connect(self.on_sync_notice)
- self.update_ok.checkStateChanged.connect(self.on_sync_ok_notice)
- else:
- self.on_enable_update(self.enable_update.isChecked())
- case self.page_hotkey:
- set_content_label(get_lang('21'))
-
- self.hotkey_enabled = UCheckBox(get_lang('c9')) # 热键启用
- self.hotkey_enabled.setChecked(setting_value.hotkey_enabled)
-
- # 左键连点
- self.left_click_layout = QHBoxLayout()
- self.left_click_input = UHotkeyLineEdit() # 左键连点输入框
- self.left_click_input.setText(format_keys(setting_value.left_click_hotkey))
- self.left_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.left_click_layout.addWidget(QLabel(f'{get_lang('0c')}: '), 1) # 左键连点提示
- self.left_click_layout.addWidget(self.left_click_input, 6)
- self.left_click_layout.addWidget(self.left_repair_button, 1)
- self.left_click_layout.addStretch()
-
- # 右键连点
- self.right_click_layout = QHBoxLayout() # 右键连点布局
- self.right_click_input = UHotkeyLineEdit() # 右键连点输入框
- self.right_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- self.right_click_input.setText(format_keys(setting_value.right_click_hotkey))
-
- # 布局
- self.right_click_layout.addWidget(QLabel(f'{get_lang('0d')}: '), 1) # 右键连点提示
- self.right_click_layout.addWidget(self.right_click_input, 6)
- self.right_click_layout.addWidget(self.right_repair_button, 1)
- self.right_click_layout.addStretch()
-
- # 暂停/重启连点
- self.pause_click_layout = QHBoxLayout() # 暂停/重启连点布局
- self.pause_click_input = UHotkeyLineEdit() # 暂停/重启连点输入框
- self.pause_click_input.setText(format_keys(setting_value.pause_click_hotkey))
- self.pause_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.pause_click_layout.addWidget(QLabel(f'{get_lang('6b')}: '), 1) # 暂停/重启连点提示
- self.pause_click_layout.addWidget(self.pause_click_input, 6)
- self.pause_click_layout.addWidget(self.pause_repair_button, 1)
- self.pause_click_layout.addStretch()
-
- # 停止连点
- self.stop_click_layout = QHBoxLayout() # 停止连点布局
- self.stop_click_input = UHotkeyLineEdit() # 停止连点输入框
- self.stop_click_input.setText(format_keys(setting_value.stop_click_hotkey))
- self.stop_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.stop_click_layout.addWidget(QLabel(f'{get_lang('6c')}: '), 1) # 停止连点提示
- self.stop_click_layout.addWidget(self.stop_click_input, 6)
- self.stop_click_layout.addWidget(self.stop_repair_button, 1)
- self.stop_click_layout.addStretch()
-
- # 连点属性
- self.click_attr_layout = QHBoxLayout() # 连点属性布局
- self.click_attr_input = UHotkeyLineEdit() # 连点属性输入框
- self.click_attr_input.setText(format_keys(setting_value.click_attr_hotkey))
- self.click_attr_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.click_attr_layout.addWidget(QLabel(f'{get_lang('8c')}: '), 1) # 连点属性提示
- self.click_attr_layout.addWidget(self.click_attr_input, 6)
- self.click_attr_layout.addWidget(self.click_attr_button, 1)
- self.click_attr_layout.addStretch()
-
- # 快速连点
- self.fast_click_layout = QHBoxLayout() # 快速连点布局
- self.fast_click_input = UHotkeyLineEdit() # 快速连点输入框
- self.fast_click_input.setText(format_keys(setting_value.fast_click_hotkey))
- self.fast_click_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.fast_click_layout.addWidget(QLabel(f'{get_lang('75')}: '), 1) # 快速连点提示
- self.fast_click_layout.addWidget(self.fast_click_input, 6)
- self.fast_click_layout.addWidget(self.fast_click_button, 1)
- self.fast_click_layout.addStretch()
-
- # 主窗口
- self.main_window_layout = QHBoxLayout() # 主窗口布局
- self.main_window_input = UHotkeyLineEdit() # 主窗口输入框
- self.main_window_input.setText(format_keys(setting_value.main_window_hotkey))
- self.main_window_button = QPushButton(get_lang('20')) # 还原默认设置按钮
-
- # 布局
- self.main_window_layout.addWidget(QLabel(f'{get_lang('76')}: '), 1) # 主窗口提示
- self.main_window_layout.addWidget(self.main_window_input, 6)
- self.main_window_layout.addWidget(self.main_window_button, 1)
- self.main_window_layout.addStretch()
# 布局
if dev_flags.get('new_settings', False):
layout.addWidget(self.hotkey_enabled)
- layout.addLayout(self.left_click_layout)
- layout.addLayout(self.right_click_layout)
- layout.addLayout(self.pause_click_layout)
- layout.addLayout(self.stop_click_layout)
- layout.addLayout(self.click_attr_layout)
- layout.addLayout(self.fast_click_layout)
- layout.addLayout(self.main_window_layout)
-
- # 连接信号
- self.left_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.left_click_input), SettingText.left_click_hotkey))
- self.right_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.right_click_input), SettingText.right_click_hotkey))
- self.pause_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.pause_click_input), SettingText.pause_click_hotkey))
- self.stop_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.stop_click_input), SettingText.stop_click_hotkey))
- self.click_attr_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.click_attr_input), SettingText.click_attr_hotkey))
- self.fast_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.fast_click_input), SettingText.fast_click_hotkey))
- self.main_window_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.main_window_input), SettingText.main_window_hotkey))
-
- self.left_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.left_click_hotkey))
- self.right_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.right_click_hotkey))
- self.pause_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.pause_click_hotkey))
- self.stop_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.stop_click_hotkey))
- self.click_attr_button.clicked.connect(lambda: self.repair_settings(SettingText.click_attr_hotkey))
- self.fast_click_button.clicked.connect(lambda: self.repair_settings(SettingText.fast_click_hotkey))
- self.main_window_button.clicked.connect(lambda: self.repair_settings(SettingText.main_window_hotkey))
-
- self.hotkey_enabled.checkStateChanged.connect(self.on_enable_hotkey_changed)
- self.on_enable_hotkey_changed(self.hotkey_enabled.isChecked() if dev_flags.get('new_settings', False) else True)
+
+ lang_id = {
+ "left_click": ["0c", False],
+ "right_click": ["0d", False],
+ "pause_click": ["6b", False],
+ "stop_click": ["6c", False],
+ "click_attr": ["8c", True],
+ "main_window": ["76", False]
+ }
+
+ for hotkey, enabled, lang, k in zip(
+ setting_value.hotkey_list.values(),
+ setting_value.hotkey_enabled_list.values(),
+ lang_id.values(),
+ setting_keys,
+ ):
+ # 设置元件
+ input = UHotkeyLineEdit()
+ input.setText(format_keys(hotkey))
+ repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮
+
+ if not enabled:
+ repair_button.setEnabled(False)
+ input.setEnabled(False)
+
+ # 添加布局
+ hotkey_layout = QHBoxLayout()
+ hotkey_layout.addWidget(QLabel(f'{filter_hotkey(get_lang(lang[0])) if lang[1] else get_lang(lang[0])}: '), 1)
+ hotkey_layout.addWidget(input, 6)
+ hotkey_layout.addWidget(repair_button, 1)
+ hotkey_layout.addStretch()
+
+ layout.addLayout(hotkey_layout)
+
+ # 添加列表
+ self.hotkeys_widget_list.append([input, repair_button])
+
+ # 连接信号
+ input.textChanged.connect(lambda val, inp=input, key=k: self.on_setting_changed(lambda: parse_hotkey(inp), f'hotkey,hotkeys,{key}'))
+ repair_button.clicked.connect(lambda b, key=k: self.repair_settings([f'hotkey,hotkeys,{key}', f'hotkey,enabled,{key}']))
case self.page_doc:
set_content_label(get_lang('ca'))
@@ -3361,7 +2583,7 @@ def parse_hotkey(input: UHotkeyLineEdit):
layout.addWidget(desc)
layout.addWidget(create_horizontal_line())
- restart = uiml.compile_ui_file(get_resource_path('ui', 'settings', 'bottom.gui'))
+ restart = uiml.compile_ui_file(get_resource_path('ui', 'settings', 'bottom.gui'), additional=self.addtional_local_value)
layout.addLayout(UIWindow(restart).show())
layout.addStretch(1)
@@ -3432,23 +2654,9 @@ def on_sync_ok_notice(self, state):
def on_enable_hotkey(self, state):
'''启用热键'''
- # 输入框
- self.left_click_input.setEnabled(state)
- self.right_click_input.setEnabled(state)
- self.pause_click_input.setEnabled(state)
- self.stop_click_input.setEnabled(state)
- self.click_attr_input.setEnabled(state)
- self.fast_click_input.setEnabled(state)
- self.main_window_input.setEnabled(state)
-
- # 按钮
- self.left_repair_button.setEnabled(state)
- self.right_repair_button.setEnabled(state)
- self.pause_repair_button.setEnabled(state)
- self.stop_repair_button.setEnabled(state)
- self.click_attr_button.setEnabled(state)
- self.fast_click_button.setEnabled(state)
- self.main_window_button.setEnabled(state)
+ for input, btn in self.hotkeys_widget_list:
+ input.setEnabled(state)
+ btn.setEnabled(state)
def on_enable_update_changed(self, state):
'''更新提示复选框状态改变'''
@@ -3456,7 +2664,7 @@ def on_enable_update_changed(self, state):
self.on_enable_update(state)
if not state:
- if MessageBox.warning(self, get_lang('15'), get_lang('c8'), MessageButtonTemplate.YESNO) == 3:
+ if MessageBox.warning(self, get_lang('15'), get_lang('c8'), MessageButton.YESNO) == MessageButton.ReturnValue.NO:
self.enable_update.setCheckState(Qt.Checked)
return
else:
@@ -3485,13 +2693,23 @@ def repair_auto_start(self):
auto_start_manager.create_reg()
MessageBox.information(self, get_lang('16'), get_lang('d2'))
- def repair_settings(self, key: str):
- '''还原默认设置'''
+ def repair_settings(self, key: str | list):
+ '''
+ 还原默认设置
+
+ :param key: 控制要恢复的设置项,可以是单个键,也可以是多个键组成的列表。
+ '''
global settings
- if MessageBox.warning(self, get_lang('15'), get_lang('22'), MessageButtonTemplate.YESNO) != 2: # 不确认重置
+ if MessageBox.warning(self, get_lang('15'), get_lang('22'), MessageButton.YESNO) != MessageButton.ReturnValue.NO: # 不确认重置
return
try:
- del settings[key]
+ if isinstance(key, str):
+ self.set_nested_value(settings, key, 'del', ) # 删除键:一个
+ elif isinstance(key, list):
+ for k in key:
+ self.set_nested_value(settings, k, 'del', ) # 删除键:多个
+ else:
+ raise ValueError(f'Invalid key type: {type(key)}')
except KeyError:
pass
save_settings()
@@ -3500,7 +2718,7 @@ def repair_settings(self, key: str):
def repair_all_settings(self):
logger.info('Reset all settings')
global settings
- if MessageBox.warning(self, get_lang('15'), get_lang('22'), MessageButtonTemplate.YESNO) != 2: # 不确认重置
+ if MessageBox.warning(self, get_lang('15'), get_lang('22'), MessageButton.YESNO) != MessageButton.ReturnValue.NO: # 不确认重置
return
settings = {}
save_settings()
@@ -3530,34 +2748,8 @@ def on_need_restart_setting_changed(self, handle, key: str, restart_place: list[
restart_place = list(map(lambda x: get_lang(x, lang_id=lang), restart_place))
- selected_lang_yes = CustonMessageButton(get_lang('01', source=default_button_text, lang_id=lang), QMessageBox.YesRole)
- selected_lang_no = CustonMessageButton(get_lang('02', source=default_button_text, lang_id=lang), QMessageBox.AcceptRole)
- need_restart = MessageBox.warning(self, get_lang('15', lang_id=lang), f'{get_lang("89", lang_id=lang)}: {", ".join(restart_place)}', [selected_lang_yes, selected_lang_no], selected_lang_yes)
- if need_restart == 2:
- self.restart()
- else:
- self.restart_window()
-
- def on_need_restart_setting_changed_old(self, handle, key: str, restart_place: list[str] = ['a9'], *args):
- '''托盘图标选择事件'''
- global settings_need_restart
-
- self.on_setting_changed(handle, key, *args)
- settings_need_restart = True
- self.restart_button.show()
-
- lang = self.lang_choice.currentIndex()
- if lang >= 1:
- lang -= 1
- elif lang == 0:
- lang = system_lang
-
- restart_place = list(map(lambda x: get_lang(x, lang_id=lang), restart_place))
-
- selected_lang_yes = CustonMessageButton(get_lang('01', source=default_button_text, lang_id=lang), QMessageBox.YesRole)
- selected_lang_no = CustonMessageButton(get_lang('02', source=default_button_text, lang_id=lang), QMessageBox.AcceptRole)
- need_restart = MessageBox.warning(self, get_lang('15', lang_id=lang), f'{get_lang("89", lang_id=lang)}: {", ".join(restart_place)}', [selected_lang_yes, selected_lang_no], selected_lang_yes)
- if need_restart == 2:
+ need_restart = MessageBox.warning(self, get_lang('15', lang_id=lang), f'{get_lang("89", lang_id=lang)}: {", ".join(restart_place)}', MessageButton.YESNO, MessageButton.YES)
+ if need_restart == MessageButton.ReturnValue.YES:
self.restart()
else:
self.restart_window()
@@ -3568,16 +2760,39 @@ def restart_window(self):
def on_setting_changed(self, handle, key, *args):
'''更新检查提示选择事件'''
logger.info(f'Setting changed: {key}')
- settings[key] = handle(*args)
+ self.set_nested_value(settings, key, 'set', handle(*args))
save_settings()
- def on_default_input_changed(self, default: QLineEdit, key: str, use_default: UCheckBox):
- '''默认延迟输入框内容变化事件'''
- if not default.text():
- use_default.setEnabled(False)
- else:
- use_default.setEnabled(True)
- self.on_setting_changed(default.text, key)
+ def set_nested_value(self, dic: dict, path: str, mode:str, value=None) -> None:
+ '''
+ 在字典中按路径设置值。
+ - 如果路径不含逗号,则直接设置 dic[path] = value。
+ - 如果路径含逗号,则按逗号分割为多级键,逐层递进,在最后一级键处设置值。
+ 若中间键不存在,则自动创建新字典;若中间键存在但不是字典,则覆盖为字典(原值丢失)。
+ '''
+ def check_dic(dic, path, val):
+ if mode == 'set':
+ dic[path] = val
+ elif mode == 'del':
+ del dic[path]
+ else:
+ raise ValueError('Invalid mode: ' + mode)
+
+ if ',' not in path:
+ check_dic(dic, path, value)
+ return
+
+ keys = [k.strip() for k in path.split(',')] # 去除可能的空格
+ current = dic
+ # 逐层深入到倒数第二个键
+ for key in keys[:-1]:
+ if key not in current:
+ check_dic(current, key, {})
+ elif not isinstance(current[key], dict):
+ raise ValueError('Invalid path: ' + path) # 路径中存在无效的键
+ current = current[key]
+ # 在最后一级键处赋值
+ check_dic(current, keys[-1], value)
def on_page_button_clicked(self, index):
'''处理页面按钮点击事件'''
@@ -3625,7 +2840,7 @@ def __init__(self):
self.init_ui_old()
def init_ui(self):
- self.main_layout = UIWindow(uiml.compile_ui_file(get_resource_path('ui', 'importExtension.gui')))
+ self.main_layout = UIWindow(uiml.compile_ui_file(get_resource_path('ui', 'importExtension.gui'), additional=self.addtional_local_value))
self.setLayout(self.main_layout.show())
logger.debug('Init import extension mode window finished')
@@ -3687,7 +2902,7 @@ def __init__(self):
# 创建热键监听器
self.hotkey_listener = get_hotkey_listener_instance()
- self.hotkey_listener.combination_pressed.connect(self.run_combination)
+ hotkey_listener.combination_pressed.connect(self.run_combination)
# 创建系统托盘图标
self.setup_tray_icon()
@@ -3696,7 +2911,6 @@ def __init__(self):
clicker.click_changed.connect(main_window.on_click_changed)
clicker.stopped.connect(main_window.on_stop)
clicker.click_conuter.connect(main_window.on_click_counter)
- clicker.started.connect(self.on_start)
clicker.started.connect(main_window.on_start)
logger.info('Initializing tray app finished')
@@ -3738,15 +2952,14 @@ def create_menu(self):
pause_action = QAction(get_lang('6b'), self.app)
stop_action = QAction(get_lang('6c'), self.app)
set_delay_action = QAction(get_lang('75'), self.app)
- click_attr_action = QAction(get_lang('8c'), self.app)
+ click_attr_action = QAction(filter_hotkey(get_lang('8c')), self.app)
- set_delay_action.triggered.connect(lambda: self.show_window(fast_click_window))
- left_click_action.triggered.connect(lambda: self.on_combination_pressed(setting_value.left_click_hotkey))
- right_click_action.triggered.connect(lambda: self.on_combination_pressed(setting_value.right_click_hotkey))
- pause_action.triggered.connect(lambda: self.on_combination_pressed(setting_value.pause_click_hotkey))
- stop_action.triggered.connect(lambda: self.on_combination_pressed(setting_value.stop_click_hotkey))
+ left_click_action.triggered.connect(lambda: self.run_command(0))
+ right_click_action.triggered.connect(lambda: self.run_command(1))
+ pause_action.triggered.connect(lambda: self.run_command(2))
+ stop_action.triggered.connect(lambda: self.run_command(3))
click_attr_action.triggered.connect(lambda: self.show_window(click_attr_window))
-
+
tray_menu.addAction(left_click_action)
tray_menu.addAction(right_click_action)
tray_menu.addAction(pause_action)
@@ -3776,7 +2989,6 @@ def on_tray_icon_activated(self, reason):
'''处理托盘图标激活事件'''
if reason == QSystemTrayIcon.ActivationReason.Trigger: # 左键点击
self.show_window(main_window)
- self.refresh()
def check_delay(self, input_delay):
try:
@@ -3798,17 +3010,14 @@ def run(self):
'''运行应用程序'''
logger.info('Running tray app')
code = self.app.exec()
+ logger.info(f'Main program exited with {code}')
if can_update:
run_software('updater.old/updater.py', 'updater.old/updater.exe')
else:
# 进行清理
run_after.run()
- logger.info(f'Main program exited with {code}')
self.quit()
sys.exit(code)
-
- def refresh(self):
- refresh.run()
def quit(self, code=lambda: None):
if update_window.down_thread is not None:
@@ -3869,53 +3078,50 @@ def show_window(self, window: QMainWindow | QDialog):
window.hide()
else:
window.show()
- self.refresh()
+ refresh.run()
def on_combination_pressed(self, combination):
'''处理组合键事件'''
combination = format_keys(combination, source=True)
- if all_in_list(combination, setting_value.fast_click_hotkey):
- # 处理快速连点组合键
- if clicker.running:
- self.tray_icon.showMessage(get_lang('14'), get_lang('af'), QSystemTrayIcon.MessageIcon.Critical, 1000)
- else:
- self.show_window(fast_click_window)
- elif all_in_list(combination, setting_value.main_window_hotkey):
- # 处理主窗口组合键
- self.show_window(main_window)
- if not main_window.isVisible():
- main_window.is_start_from_tray = True
- elif all_in_list(combination, setting_value.click_attr_hotkey):
- # 处理连点属性组合键
- self.show_window(click_attr_window)
- elif all_in_list(combination, setting_value.left_click_hotkey):
- self.on_start_clicker_tray('left') # 左键
- elif all_in_list(combination, setting_value.right_click_hotkey):
- self.on_start_clicker_tray('right') # 右键
- elif all_in_list(combination, setting_value.pause_click_hotkey):
- if clicker.running:
- clicker.pause_click()
- if clicker.paused:
- self.tray_icon.showMessage(get_lang('6e'), get_lang('71'), QSystemTrayIcon.MessageIcon.Information, 1000)
- else:
- self.tray_icon.showMessage(get_lang('6e'), get_lang('72'), QSystemTrayIcon.MessageIcon.Information, 1000)
- else:
- self.tray_icon.showMessage(get_lang('6e'), get_lang('74'), QSystemTrayIcon.MessageIcon.Warning, 1000)
- elif all_in_list(combination, setting_value.stop_click_hotkey):
- if clicker.running:
- main_window.on_stop()
- self.tray_icon.showMessage(get_lang('6e'), get_lang('73'), QSystemTrayIcon.MessageIcon.Information, 1000)
- else:
- self.tray_icon.showMessage(get_lang('6e'), get_lang('74'), QSystemTrayIcon.MessageIcon.Warning, 1000)
-
- def on_start(self):
- '''连点器启动事件'''
- if fast_click_window.isVisible():
- fast_click_window.hide()
- self.tray_icon.showMessage(get_lang('14'), get_lang('af'), QSystemTrayIcon.MessageIcon.Critical, 1000)
+ for index, i in enumerate(setting_keys):
+ if all_in_list(combination, setting_value.hotkey_list[i]):
+ self.run_command(index)
+ break
+ def run_command(self, command):
+ '''运行命令'''
+ match command:
+ case 0:
+ self.on_start_clicker_tray('left') # 左键
+ case 1:
+ self.on_start_clicker_tray('right') # 右键
+ case 2:
+ if clicker.running:
+ clicker.pause_click()
+ if clicker.paused:
+ self.tray_icon.showMessage(get_lang('6e'), get_lang('71'), QSystemTrayIcon.MessageIcon.Information, 1000)
+ else:
+ self.tray_icon.showMessage(get_lang('6e'), get_lang('72'), QSystemTrayIcon.MessageIcon.Information, 1000)
+ else:
+ self.tray_icon.showMessage(get_lang('6e'), get_lang('74'), QSystemTrayIcon.MessageIcon.Warning, 1000)
+ case 3:
+ if clicker.running:
+ main_window.on_stop()
+ self.tray_icon.showMessage(get_lang('6e'), get_lang('73'), QSystemTrayIcon.MessageIcon.Information, 1000)
+ else:
+ self.tray_icon.showMessage(get_lang('6e'), get_lang('74'), QSystemTrayIcon.MessageIcon.Warning, 1000)
+ case 4:
+ self.show_window(click_attr_window)
+ case 5:
+ self.show_window(main_window)
+ if not main_window.isVisible():
+ main_window.is_start_from_tray = True
+ case _:
+ raise ValueError(f'Invalid command: {command}')
if __name__ == '__main__':
+ init_success = False
+
shared_memory = QSharedMemory(mem_id[0])
if shared_memory.attach():
# 已经有一个实例在运行
@@ -3985,11 +3191,13 @@ def on_start(self):
DWMNCRP_ENABLED = 1
logger.info('Loading services')
+ refresh = Refresh()
setting_value = SettingValue()
clicker = Click()
auto_start_manager = StartManager()
color_getter = ColorGetter()
run_after = RunAfter()
+ hotkey_listener = get_hotkey_listener_instance()
logger.info('Loading value')
logger.debug('Loading const value')
@@ -4041,10 +3249,19 @@ def on_start(self):
# 单位控制
latest_index = 2
+ # 热键管理相关
+ is_error = True
+ delay_num = 0
+ time_num = 0
+
# 其他
- dev_config = parse_dev.parse() # 开发者模式配置
can_run_hotkey = True # 热键是否可用
result = (None, None, None, None) # 更新检查结果
+ setting_keys = list(setting_value.hotkey_list.keys()) # 热键列表
+
+ if in_dev:
+ logger.info('In development mode')
+ uiml.set_namespace(is_debug=True)
# 系统版本
windows_version = get_windows_version()
@@ -4073,14 +3290,12 @@ def on_start(self):
logger.info('Loading ui')
main_window = MainWindow()
- on_input_change(type=InputChange.main_window) # 更新时间估计状态
about_window = AboutWindow()
clean_cache_window = CleanCacheWindow()
update_ok_window = UpdateOKWindow()
update_window = UpdateWindow()
click_attr_window = ClickAttrWindow()
- fast_click_window = FastSetClickWindow()
setting_window = SettingWindow()
set_import_extension_window = SetImportExtensionModeWindow()
on_input_change(type=InputChange.setting_window) # 更新时间估计状态
@@ -4089,4 +3304,5 @@ def on_start(self):
app = TrayApp()
app.app.setStyle(setting_value.theme)
+ init_success = True
app.run()
\ No newline at end of file
diff --git a/Gui/parse_dev.py b/Gui/parse_dev.py
deleted file mode 100644
index 83e9551c..00000000
--- a/Gui/parse_dev.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import json
-from sharelibs import get_resource_path
-
-with open(get_resource_path('dev_data.json'), 'r', encoding='utf-8') as f:
- dev_data = json.load(f) # 读取规则
-
-def write(bytes:list[int]):
- '''写入数据到文件'''
- array = bytearray(bytes)
- with open('dev.dat', 'wb') as f:
- f.write(array)
-
-def parse():
- '''解析数据'''
- config = {}
-
- try:
- with open('dev.dat', 'rb') as f:
- data = f.read()
- except FileNotFoundError:
- data = b'\x00' * len(dev_data)
-
- # 解析数据
- for index, i in enumerate(dev_data):
- value = data[index]
- for k, v in i['cases'].items():
- k = int(k)
- if value == k:
- config[i['config_in_data']] = v
-
- return config
\ No newline at end of file
diff --git a/Gui/res/defaultsetting.json b/Gui/res/defaultsetting.json
index e31dea76..4ac71c05 100644
--- a/Gui/res/defaultsetting.json
+++ b/Gui/res/defaultsetting.json
@@ -1 +1,47 @@
-{"select_lang": "!var system_lang", "show_tray_icon": true, "soft_delay": 100, "click_delay": "", "click_times": "", "delay_unit": 0, "times_unit": 0, "failed_use_default": false, "times_failed_use_default": false, "update_enabled": true, "update_notify": true, "quiet_update": false, "update_ok_notify": true, "update_frequency": 1, "select_style": 0, "use_windows_color": true, "theme": "!var default_theme", "left_click_hotkey": ["F2"], "right_click_hotkey": ["F3"], "pause_click_hotkey": ["F4"], "stop_click_hotkey": ["F6"], "click_attr_hotkey": ["Ctrl", "Alt", "A"], "fast_click_hotkey": ["Ctrl", "Alt", "F"], "main_window_hotkey": ["Ctrl", "Alt", "M"], "default_doc_link": "https://xystudiocode.github.io/pyClickMouse/{lang}", "lang_doc": 0, "update_log_path": "updatelog", "hotkey_enabled": false, "show_warning": true, "show_package_warning": true, "feedback": "https://github.com/xystudiocode/pyClickMouse/issues/new/choose", "hide_flags": true}
\ No newline at end of file
+{
+ "select_lang": 0,
+ "show_tray_icon": true,
+ "soft_delay": 100,
+ "click_delay": "",
+ "click_times": "",
+ "delay_unit": 0,
+ "times_unit": 0,
+ "failed_use_default": false,
+ "times_failed_use_default": false,
+ "update_enabled": true,
+ "update_notify": true,
+ "quiet_update": false,
+ "update_ok_notify": true,
+ "update_frequency": 1,
+ "select_style": 0,
+ "use_windows_color": true,
+ "theme": "!var default_theme",
+ "hotkey": {
+ "hotkey_enabled": true,
+ "hotkeys": {
+ "left_click": ["F2"],
+ "right_click": ["F3"],
+ "pause_click": ["F4"],
+ "stop_click": ["F6"],
+ "click_attr": ["Ctrl","Alt","A"],
+ "main_window": ["Ctrl","Alt","M"]
+ },
+ "enabled": {
+ "left_click": true,
+ "right_click": true,
+ "pause_click": true,
+ "stop_click": true,
+ "click_attr": true,
+ "main_window": true
+ }
+ },
+ "default_doc_link": "https://xystudiocode.github.io/pyClickMouse/{lang}",
+ "lang_doc": 0,
+ "update_log_path": "updatelog",
+ "show_warning": true,
+ "show_package_warning": true,
+ "feedback": "https://github.com/xystudiocode/pyClickMouse/issues/new/choose",
+ "hide_flags": true,
+ "modify_using_default_input": false,
+ "modify_using_default_combo": false
+}
\ No newline at end of file
diff --git a/Gui/res/dev_data.json b/Gui/res/dev_data.json
deleted file mode 100644
index 32960f8c..00000000
--- a/Gui/res/dev_data.json
+++ /dev/null
@@ -1,2 +0,0 @@
-[
-]
\ No newline at end of file
diff --git a/Gui/res/langs/langs.json b/Gui/res/langs/langs.json
index f654dab4..91ce405b 100644
--- a/Gui/res/langs/langs.json
+++ b/Gui/res/langs/langs.json
@@ -212,7 +212,12 @@
"d3": "Lab",
"d4": "To test some experimental features, which may not be stable.",
"d5": "Hide \"lab\" when no experimental features",
- "d6": "There is already an instance running, if you can't find the window, you can wake it up from the tray."
+ "d6": "There is already an instance running, if you can't find the window, you can wake it up from the tray.",
+ "d7": "Modify click attributes using the default values",
+ "d8": "Modify click units using the default values",
+ "d9": "If you enable it, click input blanks will use the default\nvalue when another one is filled. (by default, only all of the input boxes is\nblank will use the default value).",
+ "da": "If you enable it, unit comboboxes as unit, and unit input as number.\nrepresenting the unit (by default, only the combo box equal to the settings will\nuse the default value).",
+ "db": "All"
}
},
{
@@ -355,7 +360,7 @@
"89": "需要重启以下服务",
"8a": "响应延迟越快,软件使用越顺畅,但会造成更多的占用。",
"8b": "Clickmouse",
- "8c": "连点属性",
+ "8c": "连点属性(&A)",
"8d": "离开菜单栏以显示连点进度",
"8e": "扩展(&X)",
"90": "官方扩展(&O)",
@@ -423,12 +428,17 @@
"ce": "软件启动会检查资源,如果缺少一些资源,将会警告。",
"cf": "官方扩展包丢失警告",
"d0": "我们不建议关闭更新,因为这会让你的clickmouse变得不稳定。",
- "d1": "如果你的开机自启动出现问题,或打开了clickmouse窗口,可尝试点击\n它来修复。",
+ "d1": "如果你的开机自启动出现问题,或打开了clickmouse窗口,可尝试点\n击它来修复。",
"d2": "修复成功。",
"d3": "实验室",
"d4": "用于测试一些功能,可能不稳定。",
"d5": "无实验项时候隐藏\"实验室\"设置项",
- "d6": "已经有一个实例在运行,如果你没有找到窗口,可前往托盘唤醒。"
+ "d6": "已经有一个实例在运行,如果你没有找到窗口,可前往托盘唤醒。",
+ "d7": "使用默认值修改连点属性",
+ "d8": "使用默认值修改连点单位",
+ "d9": "如果启用,点击输入框空白时会使用默认值,当其他输入框有值时,不会使用默认值。\n(默认情况下,只有所有输入框都为空时才会使用默认值)",
+ "da": "如果启用,单位组合框作为单位,单位输入框作为数字代表单位(默认情况下,只有设置\n的值等于组合框的值时才会使用默认值)",
+ "db": "全部"
}
}
]
\ No newline at end of file
diff --git a/Gui/res/styles/dark.qss b/Gui/res/styles/dark.qss
index 962e958b..d18b5463 100644
--- a/Gui/res/styles/dark.qss
+++ b/Gui/res/styles/dark.qss
@@ -227,5 +227,5 @@ QStatusBar {
/* 元数据 */
.meta {
- --mode: dark;
+ mode: dark;
}
\ No newline at end of file
diff --git a/Gui/res/styles/light.qss b/Gui/res/styles/light.qss
index 56468374..0c9441fe 100644
--- a/Gui/res/styles/light.qss
+++ b/Gui/res/styles/light.qss
@@ -219,5 +219,5 @@ QStatusBar {
/* 元数据 */
.meta {
- --mode: light;
+ mode: light;
}
\ No newline at end of file
diff --git a/Gui/res/ui/clickattr.gui b/Gui/res/ui/clickattr.gui
index 17f2fc61..7cb40d19 100644
--- a/Gui/res/ui/clickattr.gui
+++ b/Gui/res/ui/clickattr.gui
@@ -1,12 +1,12 @@
-
-
+
+
-
+
\ No newline at end of file
diff --git a/Gui/res/ui/fastClick.gui b/Gui/res/ui/fastClick.gui
deleted file mode 100644
index 0dd1785f..00000000
--- a/Gui/res/ui/fastClick.gui
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Gui/res/ui/settings/clicker.gui b/Gui/res/ui/settings/clicker.gui
index 7ea422d1..dec300c9 100644
--- a/Gui/res/ui/settings/clicker.gui
+++ b/Gui/res/ui/settings/clicker.gui
@@ -1,6 +1,23 @@
+
+
+
+ 、
+
+
+
@@ -25,7 +42,7 @@
name='error_use_default_delay'
args=['!lang 47']
init_steps=[{'name': 'setChecked', 'args': [setting_value.delay_error_use_default]}]
- signals={'checkStateChanged': lambda: self.on_setting_changed(self.use_default_delay.isChecked, SettingText.delay_error_use_default)}
+ signals={'checkStateChanged': lambda: self.on_clicker_changed(self.use_default_delay.isChecked, SettingText.delay_error_use_default)}
/>
@@ -50,11 +67,11 @@
-
+
\ No newline at end of file
diff --git a/Gui/sharelibs.py b/Gui/sharelibs.py
index efb55db1..20764d1d 100644
--- a/Gui/sharelibs.py
+++ b/Gui/sharelibs.py
@@ -10,7 +10,6 @@
import os
import subprocess
import winreg
-import sys
import ctypes
import win32com.client
import hashlib
@@ -19,14 +18,6 @@
setting_path = Path('data', 'settings.json')
setting_path.parent.mkdir(parents=True, exist_ok=True)
-
-def _show_message(message, title, status):
- if status == 0:
- QMessageBox.information(None, title, message)
- elif status == 1:
- QMessageBox.warning(None, title, message)
- elif status == 2:
- QMessageBox.critical(None, title, message)
def multi_replace(text, replace_dict):
'''一次性替换多个子串'''
@@ -40,31 +31,20 @@ def get_resource_path(*paths):
'''
获取资源文件路径
'''
- try:
- resource = Path('res') # 获取当前目录的资源文件夹路径
- if not resource.exists():
- raise FileNotFoundError('Resource folder missing: res not found')
- return str(resource.joinpath(*paths))
- except Exception as e:
- _show_message(f'Resource file missing: {e}', 'Error', 2)
- sys.exit(1)
-
-try:
- lang_path = Path('res', 'langs')
- with open(lang_path / 'langs.json', 'r', encoding='utf-8') as f:
- langs = json.load(f)
-
- with open(lang_path / 'control.json', 'r', encoding='utf-8') as f:
- control_langs = json.load(f)
+ resource = Path('res') # 获取当前目录的资源文件夹路径
+ if not resource.exists():
+ raise FileNotFoundError('Resource not found')
+ return str(resource.joinpath(*paths))
+
+lang_path = Path('res', 'langs')
+with open(lang_path / 'langs.json', 'r', encoding='utf-8') as f:
+ langs = json.load(f)
- with open(lang_path / 'init.json', 'r', encoding='utf-8') as f:
- init_langs = json.load(f)
-except FileNotFoundError:
- _show_message('Resource file missing: langs not found', 'Error', 2)
- sys.exit(1)
-except json.JSONDecodeError:
- _show_message('Resource file damaged: langs format error', 'Error', 2)
- sys.exit(1)
+with open(lang_path / 'control.json', 'r', encoding='utf-8') as f:
+ control_langs = json.load(f)
+
+with open(lang_path / 'init.json', 'r', encoding='utf-8') as f:
+ init_langs = json.load(f)
def load_settings():
'''
@@ -313,7 +293,8 @@ def widget_replacer(ui_data: str):
else:
return uiml.default_replacer(ui_data) # 交由uiml进行替换
-def layout_parser(ui_data: Dict[str, Any], namespace=None):
+def layout_parser(ui_data: Dict[str, Any], namespace=None, additional=None):
+ additional_value = additional if additional is not None else {} # 额外参数
if not ui_data.get('show_if', True):
return None
if ui_data.get('direction').lower() == 'u':
@@ -323,22 +304,27 @@ def layout_parser(ui_data: Dict[str, Any], namespace=None):
# 索引2 -- 选择框
inputs = ui_data['content'][1]
for input in inputs['content']:
- input_compiled_list.append(uiml.compile_ui(input, namespace)) # 递归解析
+ input_compiled_list.append(uiml.compile_ui(input, namespace, additional_value)) # 递归解析
combos = ui_data['content'][2] # 组合框
combos_compiled_list = []
for combo in combos['content']:
- combos_compiled_list.append(uiml.compile_ui(combo, namespace)) # 递归解析
+ combos_compiled_list.append(uiml.compile_ui(combo, namespace, additional_value)) # 递归解析
return {'name': ui_data.get('name'), 'direction': ui_data.get('direction'), "texts": ui_data['content'][0]['values'], 'inputs': input_compiled_list, 'combos': combos_compiled_list}
- return uiml.default_layout_parser(ui_data, namespace) # 交由uiml进行替换
+ return uiml.default_layout_parser(ui_data, namespace, additional_value) # 交由uiml进行替换
-def widget_parser(ui_data: Dict[str, Any], namespace=None):
+def widget_parser(ui_data: Dict[str, Any], namespace=None, additional=None):
+ additional_value = additional if additional is not None else {} # 额外参数
show_value = ui_data.get('show_if', True)
- uiml_value = uiml.default_widget_parser(ui_data, namespace) # 交由uiml进行替换
+ uiml_value = uiml.default_widget_parser(ui_data, namespace, additional_value) # 交由uiml进行替换
if not show_value:
return None
return uiml_value
-uiml.set_namespace(value_replace_func=widget_replacer, layout_parser_func=layout_parser, widget_parser_func=widget_parser, additional_used_widget_key=['show_if'], additional_used_layout_key=['show_if'], reverse=True) # 设置uiml的控制函数
+uiml.set_namespace(
+ value_replace_func=widget_replacer, layout_parser_func=layout_parser, widget_parser_func=widget_parser,
+ additional_used_widget_key=['show_if'], additional_used_layout_key=['show_if'], reverse=True,
+ enable_value_convert=True
+) # 设置uiml的控制函数
class UIWindow(uiml.UIMLLayout):
def __init__(self, list=None):
diff --git a/Gui/tests/test_nest.py b/Gui/tests/test_nest.py
new file mode 100644
index 00000000..923df50d
--- /dev/null
+++ b/Gui/tests/test_nest.py
@@ -0,0 +1,45 @@
+def set_nested_value(dic: dict, path: str, mode:str, value) -> None:
+ '''
+ 在字典中按路径设置值。
+ - 如果路径不含逗号,则直接设置 dic[path] = value。
+ - 如果路径含逗号,则按逗号分割为多级键,逐层递进,在最后一级键处设置值。
+ 若中间键不存在,则自动创建新字典;若中间键存在但不是字典,则覆盖为字典(原值丢失)。
+ '''
+ def check_dic(dic, path, val):
+ if mode == 'set':
+ dic[path] = val
+ elif mode == 'del':
+ del dic[path]
+ else:
+ raise ValueError('Invalid mode: ' + mode)
+
+ if ',' not in path:
+ check_dic(dic, path, value)
+ return
+
+ keys = [k.strip() for k in path.split(',')] # 去除可能的空格
+ current = dic
+ # 逐层深入到倒数第二个键
+ for key in keys[:-1]:
+ if key not in current:
+ check_dic(current, key, {})
+ elif not isinstance(current[key], dict):
+ raise ValueError('Invalid path: ' + path) # 路径中存在无效的键
+ current = current[key]
+ # 在最后一级键处赋值
+ check_dic(current, keys[-1], value)
+
+def test_main():
+ dic = {'a': {'b': {'c': 1}}}
+ set_nested_value(dic, 'a,b,c', 'set', 2)
+ print(dic) # 输出 {'a': {'b': {'c': 2}}}
+ assert dic['a']['b']['c'] == 2
+ set_nested_value(dic, 'a,b,c', 'del', None)
+ print(dic) # 输出 {'a': {'b': {}}}
+ assert 'c' not in dic['a']['b']
+ set_nested_value(dic, 'a,d', 'set', 3)
+ assert dic['a']['d'] == 3
+ print(dic) # 输出 {'a': {'b': {}, 'd': 3}}
+
+if __name__ == '__main__':
+ test_main()
\ No newline at end of file
diff --git a/Gui/txtinfo.py b/Gui/txtinfo.py
index 19825be0..07ecbda9 100644
--- a/Gui/txtinfo.py
+++ b/Gui/txtinfo.py
@@ -20,13 +20,7 @@ class SettingText:
select_style = 'select_style'
use_windows_color = 'use_windows_color'
theme = 'theme'
- left_click_hotkey = 'left_click_hotkey'
- right_click_hotkey = 'right_click_hotkey'
- pause_click_hotkey = 'pause_click_hotkey'
- stop_click_hotkey ='stop_click_hotkey'
- click_attr_hotkey = 'click_attr_hotkey'
- fast_click_hotkey = 'fast_click_hotkey'
- main_window_hotkey = 'main_window_hotkey'
+ hotkey = 'hotkey'
default_doc_link = 'default_doc_link'
lang_doc = 'lang_doc'
update_log_path = 'update_log_path'
@@ -35,14 +29,13 @@ class SettingText:
show_package_warning ='show_package_warning'
feedback = 'feedback'
hide_flags = 'hide_flags'
+ modify_using_default_input = 'modify_using_default_input'
+ modify_using_default_combo = 'modify_using_default_combo'
class SettingValue:
def get(self, value):
default_value = default_settings.get(value, None)
- if isinstance(default_value, str):
- if default_value.startswith('!var '): # 需要加载变量
- var_name = default_value[5:]
- default_value = eval(var_name)
+
return settings.get(value, default_value)
def __getitem__(self, key):
@@ -122,33 +115,19 @@ def use_windows_color(self):
def theme(self):
return self[SettingText.theme]
- @property
- def left_click_hotkey(self):
- return self[SettingText.left_click_hotkey]
-
- @property
- def right_click_hotkey(self):
- return self[SettingText.right_click_hotkey]
-
- @property
- def pause_click_hotkey(self):
- return self[SettingText.pause_click_hotkey]
-
- @property
- def stop_click_hotkey(self):
- return self[SettingText.stop_click_hotkey]
-
- @property
- def click_attr_hotkey(self):
- return self[SettingText.click_attr_hotkey]
+ def _get_hotkey(self, val_name):
+ setting_value = settings.get(SettingText.hotkey, {}).get(val_name, {})
+ default_value = default_settings.get(SettingText.hotkey, {}).get(val_name, {})
+
+ return default_value | setting_value # 右侧覆盖左侧
@property
- def fast_click_hotkey(self):
- return self[SettingText.fast_click_hotkey]
+ def hotkey_list(self):
+ return self._get_hotkey('hotkeys')
@property
- def main_window_hotkey(self):
- return self[SettingText.main_window_hotkey]
+ def hotkey_enabled_list(self):
+ return self._get_hotkey('enabled')
@property
def default_doc_link(self):
@@ -164,7 +143,7 @@ def update_log_path(self):
@property
def hotkey_enabled(self):
- return self[SettingText.hotkey_enabled]
+ return self[SettingText.hotkey].get(SettingText.hotkey_enabled, True)
@property
def show_warning(self):
@@ -182,6 +161,14 @@ def feedback(self):
def hide_flags(self):
return self[SettingText.hide_flags]
+ @property
+ def modify_using_default_input(self):
+ return self[SettingText.modify_using_default_input]
+
+ @property
+ def modify_using_default_combo(self):
+ return self[SettingText.modify_using_default_combo]
+
class StyleClass:
big_16 = 'big_text_16'
big_20 = 'big_text_20'
diff --git a/Gui/uiStyles/widgets.py b/Gui/uiStyles/widgets.py
index 73abd9b8..8d8b91e4 100644
--- a/Gui/uiStyles/widgets.py
+++ b/Gui/uiStyles/widgets.py
@@ -1,9 +1,18 @@
-from uiStyles.QUI import *
-from sharelibs import get_lang,default_button_text
+# from uiStyles.QUI import *
+from PySide6.QtWidgets import *
+from PySide6.QtCore import *
+from PySide6.QtGui import *
+import ctypes # 用于获取系统风格
+from sharelibs import get_lang, default_button_text
-__all__ = ['UMessageBox', 'VScrollArea', 'HScrollArea', 'UCheckBox', 'UnitInputLayout', 'ULabel', 'MessageButtonTemplate', 'CustonMessageButton']
+__all__ = ['UMessageBox', 'VScrollArea', 'HScrollArea', 'UCheckBox', 'UnitInputLayout', 'ULabel', 'MessageButton', 'CustonMessageButton', 'MessageIcon']
-class MessageButtonTemplate:
+# 定义消息框图标对应的声音常量
+MB_ICONHAND = 0x00000010 # 错误(手形)
+MB_ICONEXCLAMATION = 0x00000030 # 警告
+MB_ICONASTERISK = 0x00000040 # 信息
+
+class MessageButton:
NOBUTTON = 0b0
YES = 0b1
NO = 0b10
@@ -12,85 +21,167 @@ class MessageButtonTemplate:
YESNO = YES | NO
OKCANCEL = OK | CANCEL
+ class ReturnValue:
+ YES = 0
+ NO = 1
+ OK = 2
+ CANCEL = 3
+
+class MessageIcon:
+ INFO = 0b1
+ WARNING = 0b10
+ CRITICAL = 0b100
+ QUESTION = 0b1000
+
class CustonMessageButton:
def __init__(self, text, role):
self.text = text
self.role = role
-class UMessageBox(QMessageBox):
- @staticmethod
- def new_msg(parent,
- title: str,
- text: str,
- icon: QMessageBox.Icon,
- buttons: MessageButtonTemplate = MessageButtonTemplate.OK,
- defaultButton: MessageButtonTemplate = MessageButtonTemplate.OK):
-
- msg_box = QMessageBox(icon, title, text, buttons=QMessageBox.NoButton, parent=parent)
-
- default_btn = None
-
- # 虽然下面的规则匹配有点奇怪,但是为了显示整齐所以要这样写
+class UMessageBox(QDialog):
+ def __init__(self, parent: QWidget | None, title: str, text: str, icon: MessageIcon, buttons: MessageButton, defaultButton: MessageButton):
+ super().__init__(parent)
+
+ self.setWindowTitle(title)
+
+ self.text = text
+ match icon:
+ case MessageIcon.INFO:
+ self.icon = QStyle.SP_MessageBoxInformation
+ case MessageIcon.WARNING:
+ self.icon = QStyle.SP_MessageBoxWarning
+ case MessageIcon.CRITICAL:
+ self.icon = QStyle.SP_MessageBoxCritical
+ case MessageIcon.QUESTION:
+ self.icon = QStyle.SP_MessageBoxQuestion
+ case _:
+ raise ValueError('Invalid icon value')
+
+ self.buttons = {}
+ self.defaultButton = None
+
+ self.btn_id = 5 # 前四个为默认按钮id,后面为自定义按钮id
+
if isinstance(buttons, int):
- if buttons & MessageButtonTemplate.YES:
- btn = msg_box.addButton(get_lang('01', source=default_button_text), QMessageBox.YesRole)
- if defaultButton == MessageButtonTemplate.YES:
- default_btn = btn
-
- if buttons & MessageButtonTemplate.NO:
- btn = msg_box.addButton(get_lang('02', source=default_button_text), QMessageBox.AcceptRole)
- if defaultButton == MessageButtonTemplate.NO:
- default_btn = btn
-
- if buttons & MessageButtonTemplate.OK:
- btn = msg_box.addButton(get_lang('03', source=default_button_text), QMessageBox.NoRole)
- if defaultButton == MessageButtonTemplate.OK:
- default_btn = btn
-
- if buttons & MessageButtonTemplate.CANCEL:
- btn = msg_box.addButton(get_lang('04', source=default_button_text), QMessageBox.RejectRole)
- if defaultButton == MessageButtonTemplate.CANCEL:
- default_btn = btn
- elif isinstance(buttons, CustonMessageButton):
- btn = msg_box.addButton(buttons.text, buttons.role)
- if defaultButton == buttons:
- default_btn = btn
- elif isinstance(buttons, list):
+ if buttons & MessageButton.YES:
+ btn = QPushButton(get_lang('01', source=default_button_text))
+ msg_id = 1
+
+ self.buttons[msg_id] = btn
+ if defaultButton == MessageButton.YES:
+ self.defaultButton = btn
+ if buttons & MessageButton.NO:
+ btn = QPushButton(get_lang('02', source=default_button_text))
+ msg_id = 2
+ self.buttons[msg_id] = btn
+
+ if defaultButton == MessageButton.NO:
+ self.defaultButton = btn
+ if buttons & MessageButton.OK:
+ btn = QPushButton(get_lang('03', source=default_button_text))
+ msg_id = 3
+ self.buttons[msg_id] = btn
+
+ if defaultButton == MessageButton.OK:
+ self.defaultButton = btn
+ if buttons & MessageButton.CANCEL:
+ btn = QPushButton(get_lang('04', source=default_button_text))
+ msg_id = 4
+ self.buttons[msg_id] = btn
+
+ if defaultButton == MessageButton.CANCEL:
+ self.defaultButton = btn
+ if isinstance(buttons, CustonMessageButton):
+ btn = QPushButton(buttons.text)
+ msg_id = self.btn_id
+ self.btn_id += 1
+ self.buttons[msg_id] = btn
+
+ if defaultButton == btn:
+ self.defaultButton = btn
+ if isinstance(buttons, list):
for button in buttons:
if isinstance(button, CustonMessageButton):
- btn = msg_box.addButton(button.text, button.role)
+ btn = QPushButton(button.text, button.role)
+ msg_id = self.btn_id
+ self.buttons[msg_id] = btn
if defaultButton == button:
- default_btn = btn
+ self.defaultButton = btn
else:
- raise ValueError('buttons must be a list of CustonMessageButton') # 报错
- else:
- raise ValueError('buttons must be a int or a list of CustonMessageButton') # 报错
-
- if default_btn:
- msg_box.setDefaultButton(default_btn)
+ raise ValueError('buttons must be a list of CustonMessageButton')
- return msg_box
-
- @classmethod
- def warning(cls, parent, title: str, text: str, buttons: MessageButtonTemplate = MessageButtonTemplate.OK, defaultButton: MessageButtonTemplate = MessageButtonTemplate.OK):
- msg_box = cls.new_msg(parent, title, text, QMessageBox.Icon.Warning, buttons, defaultButton)
- return msg_box.exec()
+ self.init_ui()
+
+ def init_ui(self):
+ layout = QVBoxLayout(self)
+
+ content_layout = QHBoxLayout()
+ icon = QLabel()
+ show_icon = icon.style().standardIcon(self.icon)
+ icon.setPixmap(show_icon.pixmap(32, 32))
+
+ # 将 label 保存为实例属性,以便后续设置最大宽度
+ label = QLabel(self.text)
+ label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
+ label.setWordWrap(True) # 文本换行
+ label.setMaximumWidth(500)
+
+ content_layout.addWidget(icon, 0)
+ content_layout.addWidget(label, 1)
+ buttons_layout = QHBoxLayout()
+ buttons_layout.addStretch()
+ buttons_layout.setContentsMargins(0, 8, 0, 0)
+ for k, button in self.buttons.items():
+ button.clicked.connect(lambda b, key=k: self.done(key)) # 为按钮添加点击事件
+ buttons_layout.addWidget(button)
+ if button == self.defaultButton:
+ button.setDefault(True)
+
+ layout.addLayout(content_layout)
+ layout.addLayout(buttons_layout)
+
+ self.setLayout(layout)
+
+ def showEvent(self, event):
+ match self.icon:
+ case QStyle.SP_MessageBoxInformation:
+ sound = MB_ICONASTERISK
+ case QStyle.SP_MessageBoxWarning:
+ sound = MB_ICONEXCLAMATION
+ case QStyle.SP_MessageBoxCritical:
+ sound = MB_ICONHAND
+ case _:
+ sound = None
+
+ if sound is not None:
+ ctypes.windll.user32.MessageBeep(sound)
+ return super().showEvent(event)
+
@classmethod
- def critical(cls, parent, title: str, text: str, buttons: MessageButtonTemplate = MessageButtonTemplate.OK, defaultButton: MessageButtonTemplate = MessageButtonTemplate.OK):
- msg_box = cls.new_msg(parent, title, text, QMessageBox.Icon.Critical, buttons, defaultButton)
- return msg_box.exec()
+ def infomation(cls, parent: QWidget | None, title: str, text: str, buttons: MessageButton = None, defaultButton: MessageButton = None):
+ buttons = MessageButton.OK if buttons is None else buttons
+ defaultButton = MessageButton.OK if buttons is None else defaultButton
+ return cls(parent, title, text, MessageIcon.INFO, buttons, defaultButton).exec()
@classmethod
- def information(cls, parent, title: str, text: str, buttons: MessageButtonTemplate = MessageButtonTemplate.OK, defaultButton: MessageButtonTemplate = MessageButtonTemplate.OK):
- msg_box = cls.new_msg(parent, title, text, QMessageBox.Icon.Information, buttons, defaultButton)
- return msg_box.exec()
+ def warning(cls, parent: QWidget | None, title: str, text: str, buttons: MessageButton = None, defaultButton: MessageButton = None):
+ buttons = MessageButton.OK if buttons is None else buttons
+ defaultButton = MessageButton.OK if buttons is None else defaultButton
+ return cls(parent, title, text, MessageIcon.WARNING, buttons, defaultButton).exec()
@classmethod
- def question(cls, parent, title: str, text: str, buttons: MessageButtonTemplate = MessageButtonTemplate.YESNO, defaultButton: MessageButtonTemplate = MessageButtonTemplate.YES):
- msg_box = cls.new_msg(parent, title, text, QMessageBox.Icon.Question, buttons, defaultButton)
- return msg_box.exec()
-
+ def critical(cls, parent: QWidget | None, title: str, text: str, buttons: MessageButton = None, defaultButton: MessageButton = None):
+ buttons = MessageButton.OK if buttons is None else buttons
+ defaultButton = MessageButton.OK if buttons is None else defaultButton
+ return cls(parent, title, text, MessageIcon.CRITICAL, buttons, defaultButton).exec()
+
+ @classmethod
+ def question(cls, parent: QWidget | None, title: str, text: str, buttons: MessageButton = None, defaultButton: MessageButton = None):
+ buttons = MessageButton.YESNO if buttons is None else buttons
+ defaultButton = MessageButton.YESNO if buttons is None else defaultButton
+ return cls(parent, title, text, MessageIcon.QUESTION, buttons, defaultButton).exec()
+
class VScrollArea(QScrollArea):
def __init__(self, parent=None):
super().__init__(parent)
diff --git a/clickmouse_api/clickmouse_API/command.py b/MANIFEST.in
similarity index 100%
rename from clickmouse_api/clickmouse_API/command.py
rename to MANIFEST.in
diff --git a/README-py.md b/README-py.md
new file mode 100644
index 00000000..89b00b03
--- /dev/null
+++ b/README-py.md
@@ -0,0 +1,18 @@
+# Python package usage
+
+## 📝 Installation
+```bash
+pip install ClickMouse
+```
+
+## 🚀 Usage
+For Python call or .pyd call, use the following code:
+```python
+import clickmouse
+
+clickmouse.click_mouse(clickmouse.LEFT, 1000, 10, 10) # click left button 10 times, interval 1000ms, press duration 10ms
+```
+~~Command line call~~
+```bash
+ClickMouse.exe /h # show help
+```
diff --git a/clickmouse_api/README.md b/clickmouse_api/README.md
deleted file mode 100644
index ba8adb02..00000000
--- a/clickmouse_api/README.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# ClickMouse API工具库
-
-使用clickmouse api来实现clickmouse扩展开发!
-导入方法:
-```python
-from clickmouse_APIv1 as clickmouse
-```
-语言包格式:
-```json
-[
- {
- "lang_id": 0,
- "lang_system_name":"en",
- "lang_package": {
- "01": "This is the language package file",
- }
- },
- {
- "lang_id": 1,
- "lang_system_name": "zh-CN",
- "lang_package": {
- "01": "这是语言包文件",
- }
- }
-]
-```
-编辑扩展完成后,打包为exe,使用clickmouse导入测试
-若clickmouse已经安装,设置的数据将会导入到clickmouse中
\ No newline at end of file
diff --git a/clickmouse_api/clickmouse_API/GUI/__init__.py b/clickmouse_api/clickmouse_API/GUI/__init__.py
deleted file mode 100644
index 52e36a6f..00000000
--- a/clickmouse_api/clickmouse_API/GUI/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from wx import *
-from clickmouse_API.GUI import styles
diff --git a/clickmouse_api/pyproject.toml b/clickmouse_api/pyproject.toml
deleted file mode 100644
index 3b5ba98d..00000000
--- a/clickmouse_api/pyproject.toml
+++ /dev/null
@@ -1,32 +0,0 @@
-[build-system]
-requires = ["setuptools>=61.0.0"]
-build-backend = "setuptools.build_meta"
-
-[project]
-name = "ClickMouse_api"
-version = "1.0.1"
-authors = [
- {name = "xystudio", email = "173288240@qq.com"}
-]
-description = "基于Python的鼠标连点工具扩展api"
-readme = "README.md"
-requires-python = ">=3.8"
-license = {text = "MIT"}
-keywords = ["mouse", "click", "automation", "clickmouse", "api"]
-classifiers = [
- "Programming Language :: Python :: 3",
- "License :: OSI Approved :: MIT License",
- "Operating System :: OS Independent",
-]
-dependencies = [
- "wxpython"
-]
-
-[project.urls]
-Homepage = "https://github.com/xystudiocode/pyClickMouse"
-
-[project.scripts]
-clickmouse = "clickmouse.__main__:main"
-
-[project.optional-dependencies]
-clickmouse_control = ["clickmouse"]
\ No newline at end of file
diff --git a/clickmouse_api/setup.py b/clickmouse_api/setup.py
deleted file mode 100644
index 887cfd28..00000000
--- a/clickmouse_api/setup.py
+++ /dev/null
@@ -1,37 +0,0 @@
-"""这个是使用setuptools打包的脚本,用于安装使用python调用的clickmouse工具
-这个库仅供开发人员使用,普通人请前往github releases(https://github.com/xystudio/pyClickMouse/releases)
-推荐通过'pip install clickmouse'安装,若网络较差,可以使用清华镜像源'pip install clickmouse -i https://pypi.tuna.tsinghua.edu.cn/simple'
-若要下载本地安装包,建议下载whl格式的安装包,下载地址:https://github.com/xystudio/pyClickMouse/releases,下载结束后在下载位置打开命令行,运行'python setup.py install'"""
-
-# 以下为setup.py文件内容
-# 导入setuptools模块
-import setuptools
-
-# 定义setup函数
-setuptools.setup(
- name="ClickMouse_api", # 包名
- version="1.0.1", # 版本号
- author="xystudio", # 作者
- author_email="173288240@qq.com", # 作者邮箱
- description="基于Python的鼠标连点工具扩展api", # 包描述
- url="https://github.com/xystudiocode/pyClickMouse", # 包的github地址
- long_description=open("README.md", "r", encoding="utf-8").read(), # 包的readme文件
- long_description_content_type="text/markdown", # 指定readme文件格式为markdown
- packages=setuptools.find_packages(), # 包的目录结构
- classifiers=[ # 包的分类列表
- "Programming Language :: Python :: 3", # python版本
- "License :: OSI Approved :: MIT License", # 许可证
- "Operating System :: OS Independent",# 系统
- ],
- keywords=["mouse", "click", "automation", "clickmouse", "api"], # 包的关键字列表
- python_requires='>=3.8', # python版本要求
- install_requires=['wxpython'], # 依赖的包列表
- entry_points={ # 脚本入口
- "console_scripts": [
- "clickmouse = clickmouse.__main__:main",
- ]
- },
- extras_require = {
- "clickmouse_control": ["clickmouse"]
- }
-)
diff --git a/cython/main.py b/cython/main.py
deleted file mode 100644
index 18f9d1ba..00000000
--- a/cython/main.py
+++ /dev/null
@@ -1,16 +0,0 @@
-"""
-ClickMouse - pyd库
----
-该库提供了鼠标点击操作的函数。
-
-使用方法:
-import clickmouse
-
-# 点击鼠标左键
-clickmouse.click_mouse(clickmouse.LEFT, 100, 100, 10) # 鼠标左键点击10次,延迟为100毫秒,按下时间为100毫秒
-clickmouse.click_mouse(clickmouse.RIGHT, 100, 100, 10) # 鼠标右键点击10次,延迟为100毫秒,按下时间为100毫秒
-"""
-
-# 需要的库和功能
-from clickmouse import *
-from clickmouse import __author__, __version__
\ No newline at end of file
diff --git a/cython/setup.py b/cython/setup.py
deleted file mode 100644
index 003136a9..00000000
--- a/cython/setup.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""这个是使用setuptools打包的脚本,用于安装使用pyd调用的clickmouse工具
-这个库仅供开发人员使用,普通人请前往github releases(https://github.com/xystudio/pyClickMouse/releases)下载安装包
-"""
-# 导入模块
-from setuptools import setup
-from Cython.Build import cythonize
-
-# 定义setup函数
-setup(
- ext_modules=cythonize(["main.py"]), # 编译main.py为.pyd文件
- install_requires=["clickmouse"], # 依赖模块
-)
\ No newline at end of file
diff --git a/documents/.vitepress/config.js b/documents/.vitepress/config.js
index 7e31e758..ccd92ef9 100644
--- a/documents/.vitepress/config.js
+++ b/documents/.vitepress/config.js
@@ -233,6 +233,10 @@ export default withMermaid({
text: 'v3.x.x.x',
collapsed: true,
items: [
+ {
+ text: '3.2.3.22',
+ link: '/en/updatelog/beta/3/33023a6',
+ },
{
text: '3.3.0.23alpha5',
link: '/en/updatelog/beta/3/33023a5',
@@ -649,6 +653,10 @@ export default withMermaid({
text: 'v3.x.x.x',
collapsed: true,
items: [
+ {
+ text: '3.2.3.22',
+ link: '/zh-CN/updatelog/beta/3/33023a6',
+ },
{
text: '3.3.0.23alpha5',
link: '/zh-CN/updatelog/beta/3/33023a5',
diff --git a/documents/en/features/settings.md b/documents/en/features/settings.md
index 02b6cce5..0927fe61 100644
--- a/documents/en/features/settings.md
+++ b/documents/en/features/settings.md
@@ -314,6 +314,7 @@ Setting items include:
- - Type: Checkbox
- - Default value: Off
- - Field name: `delay_error_use_default`
+
This operation is disabled when Click Delay parameter is set to empty
@@ -322,6 +323,14 @@ This operation is disabled when Click Delay parameter is set to emp
If turn off this option, only when click is empty will use default value; after enabling, as long as input format error will use default value.
+- Modify click attributes using the default values: If enabled, then in the click delay change, the default value will continue to use.
+- - Type:Checkbox
+- - Field name: Off
+- - Field name: `modify_using_default_input`
+- Modify click units using the default values: If enabled, then in the unit change, the default value will continue to use.
+- - Type:Checkbox
+- - Field name: Off
+- - Field name: `modify_using_default_combo`
- Click Count Default Value: Control default count when click count is empty
- - Type: Input box
- - Default value: Empty
@@ -405,35 +414,31 @@ Setting items include:
- - Type: Checkbox
- - Default value: On
-- - Field name: `hotkey_enabled`
+- - Field name: `hotkey,hotkey_enabled`
- Left Click Hotkey: Set left click hotkey.
- - Type: Input box
- - Default value: `F2`
-- - Field name: `left_click_hotkey`
+- - Field name: `hotkey,hotkeys,left_click_hotkey`
- Right Click Hotkey: Set right click hotkey.
- - Type: Input box
- - Default value: `F3`
-- - Field name: `right_click_hotkey`
+- - Field name: `hotkey,hotkeys,right_click_hotkey`
- Pause/Restart Click Hotkey: Set pause/restart clicker hotkey.
- - Type: Input box
- - Default value: `F4`
-- - Field name: `pause_click_hotkey`
+- - Field name: `hotkey,hotkeys,pause_click_hotkey`
- Stop Click Hotkey: Set stop click hotkey.
- - Type: Input box
- - Default value: `F6`
-- - Field name: `stop_click_hotkey`
+- - Field name: `hotkey,hotkeys,stop_click_hotkey`
- Click Attribute Hotkey: Set open click attribute hotkey.
- - Type: Input box
- - Default value: `Ctrl+Alt+A`
-- - Field name: `click_attr_hotkey`
-- Fast Click Hotkey: Set open fast click hotkey.
-- - Type: Input box
-- - Default value: `Ctrl+Alt+F`
-- - Field name: `fast_click_hotkey`
+- - Field name: `hotkey,hotkeys,click_attr_hotkey`
- Main Window Hotkey: Set open main window hotkey.
- - Type: Input box
- - Default value: `Ctrl+Alt+M`
-- - Field name: `main_window_hotkey`
+- - Field name: `hotkey,hotkeys,main_window_hotkey`
- Reset Left Click Settings: Used to restore default left click settings.
- - Type: Button
- Reset Right Click Settings: Used to restore default right click settings.
diff --git a/documents/en/updatelog/beta/3/33023a6.md b/documents/en/updatelog/beta/3/33023a6.md
new file mode 100644
index 00000000..e5f61c46
--- /dev/null
+++ b/documents/en/updatelog/beta/3/33023a6.md
@@ -0,0 +1,32 @@
+---
+title: 3.0.0.11alpha2
+layout: doc
+---
+
+# 3.0.0.11alpha2
+
+Release Date: 2026/06/27
+
+> This is a development test version, the 5th release in the `3.3.0` development cycle.
+
+## What's New
+- [refactor] Upgraded the logging system, more detailed
+- [refactot] Replaced some UI elements, enforcing the new UI
+- [feat] Added two new settings for the clicker:
+- - Changing some input box content won’t affect calculations
+- - Changing some units won’t affect calculations
+- [fix] Fixed some bugs and improved user experience
+- - Values in the main window don’t change after changing default settings
+- - Modifying default unit automatically updates the main window default unit
+- - Unable to open the "Decoupling UI and program" developer option
+- - Program crashes if total click duration exceeds 1.8e308 days
+- Removed the “Quick Set Delay” interface as it wasn’t very useful
+- [build] Upgraded the build system
+- - Merged API, main library, and Cython version files
+- - Updated pyd version implementation, allowing you to create pyd files for your Python version
+- - Changed the location of runhook, moved it to the root directory
+- - Moved git clean into runhook
+- Added documentation for multiple prophecy versions
+
+## Download Link
+[Click to Download](https://github.com/xystudiocode/pyClickMouse/releases/tag/3.0.0.23alpha6)
\ No newline at end of file
diff --git a/documents/public/imgs/features/en/dark/settings/clicker.png b/documents/public/imgs/features/en/dark/settings/clicker.png
index 08f60828..0842bd9b 100644
Binary files a/documents/public/imgs/features/en/dark/settings/clicker.png and b/documents/public/imgs/features/en/dark/settings/clicker.png differ
diff --git a/documents/public/imgs/features/en/light/settings/clicker.png b/documents/public/imgs/features/en/light/settings/clicker.png
index fe362be6..6b3166fe 100644
Binary files a/documents/public/imgs/features/en/light/settings/clicker.png and b/documents/public/imgs/features/en/light/settings/clicker.png differ
diff --git a/documents/public/imgs/features/zh-CN/dark/settings/clicker.png b/documents/public/imgs/features/zh-CN/dark/settings/clicker.png
index 309f2316..8878cd1c 100644
Binary files a/documents/public/imgs/features/zh-CN/dark/settings/clicker.png and b/documents/public/imgs/features/zh-CN/dark/settings/clicker.png differ
diff --git a/documents/public/imgs/features/zh-CN/light/settings/clicker.png b/documents/public/imgs/features/zh-CN/light/settings/clicker.png
index d362d641..b807938f 100644
Binary files a/documents/public/imgs/features/zh-CN/light/settings/clicker.png and b/documents/public/imgs/features/zh-CN/light/settings/clicker.png differ
diff --git a/documents/zh-CN/features/settings.md b/documents/zh-CN/features/settings.md
index 06b75239..bdc5c219 100644
--- a/documents/zh-CN/features/settings.md
+++ b/documents/zh-CN/features/settings.md
@@ -322,6 +322,14 @@ layout: doc
如果关闭此选项,只有在连点为空的时候才会使用默认值;开启后只要输入格式错误就会使用默认值。
+- 使用默认值修改连点属性:如果启用,那么在一个输入框有值时,另一个无值的输入框会继续沿用。
+- - 类型:开关
+- - 默认值:关
+- - 字段名:`modify_using_default_input`
+- 使用默认值修改连点单位:如果启用,那么在单位变化时,默认的数值会继续沿用。
+- - 类型:开关
+- - 默认值:关
+- - 字段名:`modify_using_default_combo`
- 连点次数默认值:控制连点次数为空的时候的默认次数
- - 类型:输入框
- - 默认值:空
@@ -405,35 +413,31 @@ layout: doc
- - 类型:开关
- - 默认值:开
-- - 字段名:`hotkey_enabled`
+- - 字段名:`hotkey,hotkey_enabled`
- 左键连点热键:设置左键连点的热键。
- - 类型:输入框
- - 默认值:`F2`
-- - 字段名:`left_click_hotkey`
+- - 字段名:`hotkey,hotkeys,left_click_hotkey`
- 右键连点热键:设置右键连点的热键。
- - 类型:输入框
- - 默认值:`F3`
-- - 字段名:`right_click_hotkey`
+- - 字段名:`hotkey,hotkeys,right_click_hotkey`
- 暂停/重启连点热键:设置暂停/重启连点器的热键。
- - 类型:输入框
- - 默认值:`F4`
-- - 字段名:`pause_click_hotkey`
+- - 字段名:`hotkey,hotkeys,pause_click_hotkey`
- 停止连点热键:设置停止连点的热键。
- - 类型:输入框
- - 默认值:`F6`
-- - 字段名:`stop_click_hotkey`
+- - 字段名:`hotkey,hotkeys,stop_click_hotkey`
- 连点属性热键:设置打开连点属性的热键。
- - 类型:输入框
- - 默认值:`Ctrl+Alt+A`
-- - 字段名:`click_attr_hotkey`
-- 快速连点热键:设置打开快速连点的热键。
-- - 类型:输入框
-- - 默认值:`Ctrl+Alt+F`
-- - 字段名:`fast_click_hotkey`
+- - 字段名:`hotkey,hotkeys,click_attr_hotkey`
- 主窗口热键:设置打开主窗口的热键。
- - 类型:输入框
- - 默认值:`Ctrl+Alt+M`
-- - 字段名:`main_window_hotkey`
+- - 字段名:`hotkey,hotkeys,main_window_hotkey`
- 重置左键连点设置:用于恢复默认左键连点设置。
- - 类型:按钮
- 重置右键连点设置:用于恢复默认右键连点设置。
diff --git a/documents/zh-CN/updatelog/beta/3/33023a6.md b/documents/zh-CN/updatelog/beta/3/33023a6.md
new file mode 100644
index 00000000..c701c461
--- /dev/null
+++ b/documents/zh-CN/updatelog/beta/3/33023a6.md
@@ -0,0 +1,32 @@
+---
+title: 3.0.0.11alpha2
+layout: doc
+---
+
+# 3.0.0.11alpha2
+
+发布日期:2026/06/27
+
+> 这是一个开发测试版更新,为`3.3.0`的开发周期的第5个版本。
+
+## 更新内容
+- [refactor]升级日志系统,更详细
+- [refactot]替换部分ui,强制新版ui
+- [feat]添两个连点器新设置:
+- - 更换部分输入框内容不影响计算
+- - 更换部分单位不影响计算
+- [fix]修复部分bug,优化用户体验
+- - 更改默认设置值后,主窗口的值不变
+- - 修改默认值单位自动更新主窗口的默认值单位
+- - 无法打开"Decoupling UI and program"这个开发项
+- - 当连点总耗时大于1.8e308天时候程序会崩溃
+- 移除"快速设置延迟"界面,因为没有太大用
+- [build]升级编译系统
+- - 合并api、主库和cython版本的文件
+- - 修改了pyd的版本实现,可以为你的python版本制作自己的pyd文件
+- - 修改了runhook的位置,移动到根目录
+- - 将git clean移动到runhook
+- 添加多预言版本的文档
+
+## 下载链接
+[点击下载](https://github.com/xystudiocode/pyClickMouse/releases/tag/3.0.0.23alpha6)
diff --git a/guiclean/txtinfo.py b/guiclean/txtinfo.py
index b76f9e05..929a10c4 100644
--- a/guiclean/txtinfo.py
+++ b/guiclean/txtinfo.py
@@ -16,7 +16,6 @@ class SettingText:
pause_click_hotkey = 'pause_click_hotkey'
stop_click_hotkey ='stop_click_hotkey'
click_attr_hotkey = 'click_attr_hotkey'
- fast_click_hotkey = 'fast_click_hotkey'
main_window_hotkey = 'main_window_hotkey'
default_doc_link = 'default_doc_link'
hotkey_enabled = 'hotkey_enabled'
@@ -91,10 +90,6 @@ def stop_click_hotkey(self):
def click_attr_hotkey(self):
return self[SettingText.click_attr_hotkey]
- @property
- def fast_click_hotkey(self):
- return self[SettingText.fast_click_hotkey]
-
@property
def main_window_hotkey(self):
return self[SettingText.main_window_hotkey]
diff --git a/hooks/gitclean.py b/hooks/gitclean.py
new file mode 100644
index 00000000..79036610
--- /dev/null
+++ b/hooks/gitclean.py
@@ -0,0 +1,3 @@
+import os
+
+os.system("git gc --aggressive --prune=now")
\ No newline at end of file
diff --git a/Gui/hooks/hotkey.py b/hooks/hotkey.py
similarity index 100%
rename from Gui/hooks/hotkey.py
rename to hooks/hotkey.py
diff --git a/Gui/hooks/keycrypter.py b/hooks/keycrypter.py
similarity index 100%
rename from Gui/hooks/keycrypter.py
rename to hooks/keycrypter.py
diff --git a/Gui/hooks/light_color.py b/hooks/light_color.py
similarity index 100%
rename from Gui/hooks/light_color.py
rename to hooks/light_color.py
diff --git a/Gui/hooks/ramdom_str.py b/hooks/ramdom_str.py
similarity index 100%
rename from Gui/hooks/ramdom_str.py
rename to hooks/ramdom_str.py
diff --git a/Gui/hooks/zip_json.py b/hooks/zip_json.py
similarity index 100%
rename from Gui/hooks/zip_json.py
rename to hooks/zip_json.py
diff --git a/makefile b/makefile
index a0eebd5b..00ce1da7 100644
--- a/makefile
+++ b/makefile
@@ -4,8 +4,8 @@ main:
echo Please run a build command, such as "make clickmouse".
clickmouse: gui/main.py
- $(command) --file-description="Clickmouse" --product-name="ClickMouse" --windows-icon-from-ico=gui/res/icons/clickmouse/icon.ico --include-data-dir=gui/res/=res/ --include-data-file=gui/key=key gui/main.py --file-version="3.3.0.22" --enable-plugin=pyside6 --windows-console-mode="disable" --include-data-file=gui/7z.exe=7z.exe --include-data-file=gui/7z.dll=7z.dll --output-dir=dist/clickmouse/
- $(command) --file-description="Clickmouse uninstall" --product-name="uninstall" --windows-icon-from-ico=gui/res/icons/clickmouse/uninstall.ico --file-version="2026.05.23.1" gui/uninstall.py --enable-plugin=pyside6 --windows-console-mode="disable" --windows-uac-admin --output-dir=dist/clickmouse/
+ $(command) --file-description="Clickmouse" --product-name="ClickMouse" --windows-icon-from-ico=gui/res/icons/clickmouse/icon.ico --include-data-dir=gui/res/=res/ --include-data-file=gui/key=key gui/main.py --file-version="3.3.0.23" --windows-console-mode="disable" --enable-plugin=pyside6 --include-data-file=gui/7z.exe=7z.exe --include-data-file=gui/7z.dll=7z.dll --output-dir=dist/clickmouse/
+ $(command) --file-description="Clickmouse uninstall" --product-name="uninstall" --windows-icon-from-ico=gui/res/icons/clickmouse/uninstall.ico --file-version="2026.06.27.1" gui/uninstall.py --enable-plugin=pyside6 --windows-console-mode="disable" --windows-uac-admin --output-dir=dist/clickmouse/
$(command) --file-description="Clickmouse IPK" --product-name="CmIPK" --windows-icon-from-ico=gui/res/icons/clickmouse/init.ico --file-version="2026.05.23.1" gui/install_pack.py --enable-plugin=pyside6 --windows-console-mode="disable" --output-dir=dist/clickmouse/
$(command) --file-description="Clickmouse repair" --product-name="CmRepair" --windows-icon-from-ico=gui/res/icons/clickmouse/repair.ico --file-version="2026.05.23.1" gui/repair.py --enable-plugin=pyside6 --windows-console-mode="disable" --windows-uac-admin --output-dir=dist/clickmouse/
$(command) --file-version="2026.05.23.1" gui/check_reg_ver.py --windows-console-mode="disable" --output-dir=dist/clickmouse/
@@ -25,25 +25,7 @@ extension:
echo No extension!
powershell -ExecutionPolicy Bypass -Command "./merge-distFolders.ps1 -SourcePath ./dist/clickmouse/"
-clean_pyd:
- del -s -q -f build\ clickmouse.egg-info cython\*.c
-
-gitclean:
- git gc --aggressive --prune=now
-
pyd:
@echo off
- setlocal enabledelayedexpansion
-
- set items="38" "39" "310" "311" "312" "313" "314"
-
- for %%i in (%items%) do (
- "C:\Program Files\Python%%i\python.exe" cython\setup.py build_ext --inplace
- )
- set versions=3.13 3.14
- for %%v in (%versions%) do (
- set "orig=%%v"
- set "nodot=!orig:.=!"
- "C:\Program Files\Python!nodot!\python%!orig!t.exe" cython\setup.py build_ext --inplace
- )
\ No newline at end of file
+ ./mkpyd.bat
\ No newline at end of file
diff --git a/mkpyd.bat b/mkpyd.bat
new file mode 100644
index 00000000..a18e3f8e
--- /dev/null
+++ b/mkpyd.bat
@@ -0,0 +1,27 @@
+@echo off
+setlocal enabledelayedexpansion
+
+set versions=3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.13t 3.14t 3.15t
+set setupPath=setup.py
+set pydPath="src\clickmouse\"
+
+for %%i in (%versions%) do (
+ py -V:%%i %setupPath% build_ext --inplace || echo "Python %%i is not installed."
+)
+
+del build\ clickmouse.egg-info %pydPath%\*.c /s /q /f
+
+set "merged="
+
+:: 处理第一个列表
+for %%a in (%versions%) do (
+ set "item=%%a"
+ set "item=!item:.=!"
+ set "merged=!merged! !item!"
+)
+
+echo Merged items: !merged!
+
+for %%i in (%merged%) do (
+ ren %pydPath%__init__.cp%%i-win_amd64.pyd clickmouse.cp%%i-win_amd64.pyd || echo Renaming failed for __init__.cp%%i-win_amd64.pyd.
+)
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 1d7e1dec..00000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,28 +0,0 @@
-[build-system]
-requires = ["setuptools", "wheel"]
-build-backend = "setuptools.build_meta"
-
-[project]
-name = "ClickMouse"
-version = "2.1.0"
-authors = [
- {name = "xystudio", email = "173288240@qq.com"},
-]
-description = "基于Python的鼠标连点工具"
-readme = "README.md"
-requires-python = ">=3.8"
-keywords = ["mouse", "click", "automation", "clickmouse"]
-classifiers = [
- "Programming Language :: Python :: 3",
- "License :: OSI Approved :: MIT License",
- "Operating System :: OS Independent",
-]
-dependencies = [
- "pyautogui",
-]
-
-[project.scripts]
-pyplus = "pyplus_cmd:main"
-
-[project.urls]
-homepage = "https://github.com/xystudiocode/pyClickMouse"
\ No newline at end of file
diff --git a/Gui/runhook.bat b/runhook.bat
similarity index 100%
rename from Gui/runhook.bat
rename to runhook.bat
diff --git a/setup.py b/setup.py
index 9317880b..d42276f6 100644
--- a/setup.py
+++ b/setup.py
@@ -6,18 +6,20 @@
# 以下为setup.py文件内容
# 导入setuptools模块
import setuptools
+from Cython.Build import cythonize
# 定义setup函数
setuptools.setup(
name="ClickMouse", # 包名
- version="2.1.1", # 版本号
+ version="3.3.0", # 版本号
+ package_dir={"": "src"},
+ packages=setuptools.find_packages(where="src"),
author="xystudio", # 作者
author_email="173288240@qq.com", # 作者邮箱
description="基于Python的鼠标连点工具", # 包描述
url="https://github.com/xystudiocode/pyClickMouse", # 包的github地址
- long_description=open("README.md", "r", encoding="utf-8").read(), # 包的readme文件
+ long_description=open("README-py.md", "r", encoding="utf-8").read(), # 包的readme文件
long_description_content_type="text/markdown", # 指定readme文件格式为markdown
- packages=setuptools.find_packages(), # 包的目录结构
classifiers=[ # 包的分类列表
"Programming Language :: Python :: 3", # python版本
"License :: OSI Approved :: MIT License", # 许可证
@@ -29,9 +31,8 @@
entry_points={ # 脚本入口
"console_scripts": [
"clickmouse = clickmouse.__main__:main",
+ "clickmouse-apiCli = clickmouse_api.__main__:main"
]
},
- extras_require = {
- "api": ["clickmouse-api"]
- }
+ ext_modules=cythonize(["src/clickmouse/__init__.py"]), # 编译main.py为.pyd文件
)
diff --git a/clickmouse/__init__.py b/src/clickmouse/__init__.py
similarity index 100%
rename from clickmouse/__init__.py
rename to src/clickmouse/__init__.py
diff --git a/clickmouse/__main__.py b/src/clickmouse/__main__.py
similarity index 100%
rename from clickmouse/__main__.py
rename to src/clickmouse/__main__.py
diff --git a/clickmouse/command_tools.py b/src/clickmouse/command_tools.py
similarity index 100%
rename from clickmouse/command_tools.py
rename to src/clickmouse/command_tools.py
diff --git a/clickmouse/version.py b/src/clickmouse/version.py
similarity index 100%
rename from clickmouse/version.py
rename to src/clickmouse/version.py
diff --git a/src/clickmouse_api/GUI/__init__.py b/src/clickmouse_api/GUI/__init__.py
new file mode 100644
index 00000000..1c98b718
--- /dev/null
+++ b/src/clickmouse_api/GUI/__init__.py
@@ -0,0 +1,5 @@
+from PySide6.QtWidgets import *
+from PySide6.QtCore import *
+from PySide6.QtGui import *
+import sys
+from clickmouse_API.GUI import styles
diff --git a/clickmouse_api/clickmouse_API/GUI/styles.py b/src/clickmouse_api/GUI/styles.py
similarity index 100%
rename from clickmouse_api/clickmouse_API/GUI/styles.py
rename to src/clickmouse_api/GUI/styles.py
diff --git a/clickmouse_api/clickmouse_API/__init__.py b/src/clickmouse_api/__init__.py
similarity index 100%
rename from clickmouse_api/clickmouse_API/__init__.py
rename to src/clickmouse_api/__init__.py
diff --git a/clickmouse_api/clickmouse_API/__main__.py b/src/clickmouse_api/__main__.py
similarity index 100%
rename from clickmouse_api/clickmouse_API/__main__.py
rename to src/clickmouse_api/__main__.py
diff --git a/clickmouse_api/api_package_format.json b/src/clickmouse_api/api_package_format.json
similarity index 100%
rename from clickmouse_api/api_package_format.json
rename to src/clickmouse_api/api_package_format.json
diff --git a/src/clickmouse_api/command.py b/src/clickmouse_api/command.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/clickmouse_api/install/install.bat b/src/clickmouse_api/install/install.bat
new file mode 100644
index 00000000..e69de29b
diff --git a/src/clickmouse_api/install/requirements.txt b/src/clickmouse_api/install/requirements.txt
new file mode 100644
index 00000000..bacf386d
--- /dev/null
+++ b/src/clickmouse_api/install/requirements.txt
@@ -0,0 +1,6 @@
+PySide6 >=6.10
+Nuitka >=4
+PyAutogui
+uiml>=0.2
+pynput>=1
+pywin32>=303
\ No newline at end of file
diff --git a/clickmouse_api/clickmouse_API/packageParser/__init__.py b/src/clickmouse_api/packageParser/__init__.py
similarity index 100%
rename from clickmouse_api/clickmouse_API/packageParser/__init__.py
rename to src/clickmouse_api/packageParser/__init__.py