From 5be4e1cc9c36a04785c6be2d626441941dc247d2 Mon Sep 17 00:00:00 2001 From: xystudio_u <173288240@qq.com> Date: Tue, 10 Mar 2026 22:01:30 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E2=9C=85feat(clickmouse)=203.2.1.20alpha1:?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B0=E7=9A=84feature:=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 之后,预览版的新功能会先加入到实验室中,到beta阶段才会逐一合并到正式版 - 新增实验项目:更多的设置,包括: - - 重置开机启动组件 - - 反馈软件网址 - - 重置文档链接按钮 - - 通知设置 - [🛒ci]修改了makefile,现在不必人工合并文件夹了。 - [📃docs]修改readme和协作文档内容 - [📃docs]修改issue模板,让语言变得正式一点。 --- .github/ISSUE_TEMPLATE/SPA.yml | 18 +- .github/ISSUE_TEMPLATE/bug.yml | 14 +- .github/ISSUE_TEMPLATE/feature.yml | 7 +- .github/ISSUE_TEMPLATE/tasks.yml | 3 +- .gitignore | 2 +- CONTRIBUTING.md | 81 +- Gui/install_pack.py | 24 +- Gui/main.py | 823 +++++++++--------- Gui/res/defaultsetting.json | 2 +- Gui/res/dev_settings.json | 7 + .../icons/{entensions => extensions}/cge.ico | Bin .../icons/{entensions => extensions}/cle.ico | Bin .../icons/{entensions => extensions}/cmm.ico | Bin .../icons/{entensions => extensions}/cms.ico | Bin Gui/res/icons/extensions/cmspre.ico | Bin 0 -> 102134 bytes Gui/res/langs/init.json | 6 +- Gui/res/langs/langs.json | 52 +- Gui/res/vars/caches.json | 2 +- Gui/res/versions.json | 2 +- Gui/sharelibs.py | 21 +- Gui/txtinfo.py | 195 +++++ README.md | 78 +- makefile | 27 +- merge-distFolders.ps1 | 128 +++ 24 files changed, 958 insertions(+), 534 deletions(-) create mode 100644 Gui/res/dev_settings.json rename Gui/res/icons/{entensions => extensions}/cge.ico (100%) rename Gui/res/icons/{entensions => extensions}/cle.ico (100%) rename Gui/res/icons/{entensions => extensions}/cmm.ico (100%) rename Gui/res/icons/{entensions => extensions}/cms.ico (100%) create mode 100644 Gui/res/icons/extensions/cmspre.ico create mode 100644 Gui/txtinfo.py create mode 100644 merge-distFolders.ps1 diff --git a/.github/ISSUE_TEMPLATE/SPA.yml b/.github/ISSUE_TEMPLATE/SPA.yml index ef9b93f..e72c2d3 100644 --- a/.github/ISSUE_TEMPLATE/SPA.yml +++ b/.github/ISSUE_TEMPLATE/SPA.yml @@ -1,4 +1,4 @@ -name: 🗒️新的标准立项 +name: 🗒️新标准 description: 将会建议、建立或修改一个标准。 title: "🗒️[SPA]" labels: enhancement @@ -12,11 +12,14 @@ body: > [!IMPORTANT] > 我们不会在gitee上处理issue,请使用github发布。🙋‍♂️ + + > [!TIP] + > 请一次只报告1个立项。😀 - type: textarea attributes: label: 🧾新增的立项 placeholder: | - 请在这里输入立项内容。。。 + 请在这里输入立项内容。 description: 🧾请详细描述你想要立项的标准。 validations: required: true @@ -25,26 +28,25 @@ body: label: ℹ️立项施行的clickmouse版本 placeholder: | >=X.X.X 或 >=X.X.X.X - description: 🔠反馈大于等于一个三位或四位版本号 + description: 🔠反馈一个三位或四位版本号。 value: | 下一个clickmouse正式版 validations: - required: false + required: true - type: textarea attributes: - label: ❓需要这些功能的原因 + label: ❓需要这些立项的原因 placeholder: | 解决了什么问题? - 增加了什么功能? 改进了什么? 其他原因... - description: ❓为什么需要这个功能? + description: ❓为什么需要这个立项? validations: required: false - type: textarea attributes: label: ➕其他相关信息 - description: ✉️你了解到的更多内容 + description: ✉️你了解到的更多内容。 placeholder: | 更多信息。 validations: diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 8c52125..6d9315e 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,4 +1,4 @@ -name: "🐛Bug report" +name: "🐛Bug" title: "🐛[BUG]" description: 报告一个bug。 labels: @@ -37,8 +37,8 @@ body: - id: modules type: dropdown attributes: - label: 🐛你运行出现bug的是什么模块? - description: 🐛你运行出现bug的是什么模块? + label: 🐛你运行出现bug的模块 + description: 🐛请选择你运行出现bug的模块,可以多选。 multiple: true options: - 🖱️连点功能 @@ -66,7 +66,7 @@ body: label: 🐍bug描述 placeholder: | 请在这描述 - description: 🐍描述你的bug + description: 🐍描述你的bug。 validations: required: true - type: textarea @@ -74,7 +74,7 @@ body: label: 🔦这个bug的影响 placeholder: | 请在这描述 - description: 🔦描述这个bug的影响 + description: 🔦描述这个bug的影响。 validations: required: false - type: textarea @@ -85,7 +85,7 @@ body: 2. 步骤2 3. 步骤3 4. 步骤... - description: 🔧描述如何重现这个bug + description: 🔧描述如何重现这个bug。 validations: required: false - type: input @@ -93,7 +93,7 @@ body: label: ℹ️你预估的受影响的Clickmouse版本 placeholder: | >=X.X.X 或 >=X.X.X.X - description: 🔠反馈大于等于一个三位或四位版本号 + description: 🔠反馈大于等于一个三位或四位版本号。 validations: required: false - type: textarea diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 3ef203d..afa3d8e 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -1,4 +1,4 @@ -name: ❇️Feature +name: ❇️新功能 description: 一个你建议添加的新功能。 title: "❇️[FEATURE]" labels: enhancement @@ -45,7 +45,7 @@ body: attributes: label: 📄新增功能描述 placeholder: | - 详细描述你想要新增的功能 + 详细描述你想要新增的功能。 description: 📄请详细描述你想要新增的功能。 validations: required: true @@ -54,7 +54,6 @@ body: label: ❔需要这个功能的原因 placeholder: | 解决了什么问题? - 增加了什么功能? 改进了什么? 其他原因... description: ❔为什么需要这个功能? @@ -86,7 +85,7 @@ body: - type: textarea attributes: label: ➕其他相关信息 - description: 📄你了解到的更多内容 + description: 📄你了解到的更多内容。 placeholder: | 更多信息。 validations: diff --git a/.github/ISSUE_TEMPLATE/tasks.yml b/.github/ISSUE_TEMPLATE/tasks.yml index 7b6269c..95f47f7 100644 --- a/.github/ISSUE_TEMPLATE/tasks.yml +++ b/.github/ISSUE_TEMPLATE/tasks.yml @@ -1,4 +1,4 @@ -name: ☑️Tasks +name: ☑️任务单 description: 一些任务单,可以用来起草新版本规划等。 title: "☑️[TASK]" labels: enhancement @@ -54,7 +54,6 @@ body: label: ❓需要这些功能的原因 placeholder: | 解决了什么问题? - 增加了什么功能? 改进了什么? 其他原因... description: ❓为什么需要这个功能? diff --git a/.gitignore b/.gitignore index ac70dfc..ccfdfc2 100644 --- a/.gitignore +++ b/.gitignore @@ -414,7 +414,7 @@ gui/dev_list/* !gui/dev_list/in_dev res/packages/ Gui/packages.json -extensions/ + dev.dat *.zip *.7z diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ad9565..55a5ba3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,19 +40,33 @@ 创建的分支需要以`feature/`开头,以表示功能分支,或创建一个fork,并在fork的分支开发。 -发布pr时要选择**合并到`develop`分支**。 +发布pr时不限定合并分支,只要不是`main`分支都可以。 ## 🔠版本号 -版本号格式使用语义化版本号,具体规则如下: -``` -A.B.C.D((.dev | alpha | beta | rc)E) -``` +clickmouse版本格式为:`A.B.C.D[(alpha | beta |.dev | rc) E]` +## 😊正式版本 +正式版不带.dev、alpha、beta或rc后缀。 + +A位代表有重大更新,有代码级的变动。如1.0升级到2.0就重构了代码。 + +B位代表有普通更新,通常是更新一些大功能。 + +C位代表有修复更新,通常会更新一些小功能和一些bug。 + +D位代表版本代号,通常每A, B, C位有变动时候+1。也有可能A, B, C位没有变动,D位+1,这代表紧急更新,通常是修复几个重大影响的bug。 + +## 🅱️测试版本 +测试版本带.dev、alpha、beta或rc后缀。 + +通常前面的`A.B.C.D`在一个测试周期内不变,代表下一个版本。 + +`.dev`代表早期开发更新,功能不稳定,bug很多,位于版本项目初期。这阶段新增的功能将会被放到实验室中,并默认关闭。 -- A: 主版本号,当有重大功能更新时,比如重构等。 -- B: 次版本号,当有新功能或功能改进时,比如增加新功能。 -- C: 修订号,当有bug修复或功能改进时,比如修复bug等。 -- D: 开发版本号,每发布一个正式版,D位版本号加1。 -- (.dev | alpha | beta | rc): 测试版本阶段,dev表示开发版,alpha表示内部测试版,beta表示公开测试版,rc表示候选版本,E位越大版本越新,且在开发阶段更新时E为变为1,在.dev时候以0开始。版本新旧顺序为.dev < alpha < beta < rc。 +`alpha`代表晚期开发更新,功能不完善,bug较多,位于版本项目早期。这阶段新增的功能将会被放到实验室中,并默认关闭。 + +`beta`代表发布测试更新,功能完善,bug较少,不会再新增功能,位于版本项目中期,并且会逐步合并实验室中的feature。 + +`rc`代表预备发布版本,功能完善,bug较少,会修复一些重要安全问题或bug,最接近正式版,即将发布正式版,位于版本项目末期。 ## ❓issue - 标题格式:`[类型] 标题` @@ -74,7 +88,7 @@ A.B.C.D((.dev | alpha | beta | rc)E) 我们pr合并的顺序为: ```mermaid graph LR -A(其他用户的功能开发分支) --> B(develop分支) +A(其他用户的功能开发分支) --> B(develop/rp分支) B --> C(main分支) ``` @@ -97,14 +111,41 @@ pr无特定格式,但是必须清晰描述更新内容,关联到版本号的 - milestone格式为:`dev_版本号` ## ⬇️配置仓库 +1. 下载仓库:`git clone https://github.com/xystudiocode/pyclickmouse.git` +2. 对于python版本安装python,推荐使用3.13,和软件开发者的版本一一致,[下载连接](https://www.python.org/downloads/release/python-31312/) +3. 对于头文件和dll版本,可以安装[visual studio](https://visualstudio.microsoft.com/)。 +### 🖥️GUI 1. 下载源码 2. 放置一个`7z.exe`和`7z.dll`到`gui`目录 -3. 使用`make extension`编译扩展,放入`gui/res/packages`目录下 -4. 使用`make clickmouse`制作clickmouse的安装包,把`dist/clickmouse/`下的所有除了`main.dist`和`updater.dist`的.dist文件夹的**子文件**移动到`dist/clickmouse/main.dist`下 -> [!WARNING] -> 请不要直接把这些文件夹复制,要把子文件复制,否则程序无法运行 -5. 把`dist/clickmouse/updater.dist`重命名为`updater`后**把整个文件夹**移动到`main.dist`下 -> [!WARNING] -> 请不要直接把`updater`的子文件复制,要把整个文件夹复制,否则程序无法运行 -6. 可选择把`dist/clickmouse/main.dist`这个文件夹重命名 -7. 运行`main.exe`就可以加载clickmouse了。 \ No newline at end of file +3. 安装chocolately +```powershell +Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) +``` +4. 安装make工具 +```powershell +choco install make +``` +5. 配置python包 +```powershell +pip install -r requirements.txt +``` +6. 编译 +```powershell +make clickmouse # 编译clickmouse +make extension # 编译扩展 +``` +7. 运行`dist/clickmouse/clickmouse/main.exe`就可以加载clickmouse了。 +### 🥴头文件 +仅需修改头文件,就可以被调用 +### ⚙️dll调用 +使用visual studio修改`./dll/dll.sln`里的`源文件/dllmain.cpp` +### 💾gui旧版本 +> [!NOTE] +> gui旧版本的再编译不接受pull request +使用visual studio修改`./ClickMouse-old/ClickMouse.sln`里的`源文件/clickmouse.cpp` +### 🐍python库调用 +修改`clickmouse/`下的代码,运行`pip install .`安装 +### 🦎pyd调用 +修改`cython/main.py`的代码,然后执行 +```python cython/setup.py build_ext --inplace``` +编译结束后,该目录下应该会有个以`.pyd`结尾的文件。 \ No newline at end of file diff --git a/Gui/install_pack.py b/Gui/install_pack.py index c63a2f1..12ccc73 100644 --- a/Gui/install_pack.py +++ b/Gui/install_pack.py @@ -242,6 +242,8 @@ def __init__(self): new_color_bar(self) self.install_status = '' + self.total_progress = 0 + self.current_progress = 0 def init_ui(self): '''初始化UI''' @@ -301,6 +303,7 @@ def init_ui(self): self.next_btn = QPushButton(get_ipk_lang('03')) self.next_btn.clicked.connect(self.on_next) self.next_error_layout.addWidget(self.next_btn) + self.next_btn.setProperty('class', StyleClass.selected) # 设置样式类名 # 错误重叠容器 self.copy_error_btn = QPushButton(get_ipk_lang('04')) @@ -320,6 +323,7 @@ def init_ui(self): self.finish_btn = QPushButton(get_ipk_lang('06')) self.finish_btn.clicked.connect(self.close) self.action_button_layout.addWidget(self.finish_btn) + self.finish_btn.setProperty('class', StyleClass.selected) # 设置样式类名 self.button_layout.addWidget(self.next_error_container) self.button_layout.addWidget(self.action_button_container) @@ -460,7 +464,13 @@ def show_page(self, page_index): self.template_combo.currentTextChanged.connect(self.apply_template) case self.PAGE_install: # 第五页:安装 - pass + self.status_label = QLabel(get_ipk_lang('30')) + self.progress = QProgressBar() + self.progress.setRange(0, 100) + self.progress.setValue(0) + + page_layout.addWidget(self.status_label) + page_layout.addWidget(self.progress) case self.PAGE_finish: # 第六页:完成 if is_ipk: @@ -604,6 +614,9 @@ def update_buttons(self): self.install_ipk() else: self.install() + + command = self.install_ipk if is_ipk else self.install + self.install_thread = QtThread(command) if is_ipk: if self.PAGE_read_license <= self.current_page <= self.PAGE_set_link: # ipk模式跳过这些步骤 @@ -615,12 +628,16 @@ def update_buttons(self): def set_status(self, status): '''设置状态栏''' self.install_status = status + self.current_progress += 1 + self.progress.setValue((self.total_progress / self.current_progress) * 100) + self.status_label.setText(get_ipk_lang('30') + format(self.install_status)) def install_ipk(self): '''ipk模式安装''' global package_name try: + self.total_progress = 4 self.set_status(get_ipk_lang('2b')) if not self.changes: self.set_page(self.PAGE_finish_nochanges) @@ -660,6 +677,8 @@ def install(self): self.set_status(get_ipk_lang('2b')) install_path = Path.cwd() + self.total_progress = 7 if has_package else 6 + if has_package: self.set_status(get_ipk_lang('26')) with open(fr'{install_path}\packages.json', 'w', encoding='utf-8') as f: @@ -806,7 +825,7 @@ def closeEvent(self, event): sys.exit(2) 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, run_as_admin) + 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) import win32com.client import winreg import zipfile @@ -816,6 +835,7 @@ def closeEvent(self, event): import os from pathlib import Path import json + from txtinfo import StyleClass # 系统api import ctypes diff --git a/Gui/main.py b/Gui/main.py index 6d9e7c9..7ff6d3d 100644 --- a/Gui/main.py +++ b/Gui/main.py @@ -7,9 +7,9 @@ from datetime import datetime # 检查时间 from uiStyles import (SelectUI, UCheckBox, UMessageBox, MessageButtonTemplate) # 软件界面样式 from pynput import keyboard # 热键功能库 -from sharelibs import (get_lang) +from txtinfo import SettingValue as BaseSettingValue # 字段值 -# TODO: 添加更新设置,使用hashlib.algorithms_available获取支持的hash算法 +# TODO: 添加更多更新设置,使用hashlib.algorithms_available获取支持的hash算法 def get_windows_version(): '''获取winmdows版本''' @@ -87,21 +87,7 @@ def should_check_update(): return True return False -def load_settings(): - ''' - 加载设置 - ''' - try: - with open(data_path / 'settings.json', 'r', encoding='utf-8') as f: - settings = json.load(f) - return settings - except FileNotFoundError: - logger.warning('配置文件不存在,创建默认配置文件') - with open(data_path / 'settings.json', 'w', encoding='utf-8') as f: - f.write('{}') - return {} - -def save_settings(settings): +def save_settings(): ''' 保存设置 ''' @@ -271,7 +257,7 @@ def on_update_setting_window(): values = setting_window.values.copy() setting_window.close() setting_window = SettingWindow(values) - setting_window.click_setting_changed.connect(lambda: on_input_change(type='main')) + setting_window.click_setting_changed.connect(lambda: on_input_change(type=InputChange.main_window)) setting_window.window_restarted.connect(on_update_setting_window) setting_window.on_page_button_clicked(page) setting_window.show() @@ -363,7 +349,7 @@ def revert_update(): def on_input_change(*, type:str ): '''输入延迟改变''' # 判断参数有效性 - if type == 'main': + if type == InputChange.main_window: global is_inf, is_error, delay_num, time_num delay_text = main_window.input_delay delay_times = main_window.input_times @@ -373,7 +359,7 @@ def on_input_change(*, type:str ): delay_num = setting_value.click_delay time_num = setting_value.click_times is_error = False - elif type =='setting': + elif type ==InputChange.setting_window: delay_text = setting_window.default_delay delay_times = setting_window.default_time total = setting_window.total_time_label @@ -384,17 +370,17 @@ def on_input_change(*, type:str ): is_inf = False delay = 0 - delay_times.setEnabled(not(times_combo.currentIndex() == latest_index or (setting_value.times_unit == latest_index) and type == 'main')) + 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': is_inf = True - if setting_value.times_unit == latest_index and type == 'main': + if setting_value.times_unit == latest_index and type == InputChange.main_window: is_inf = True def on_delay_error(error_text=get_lang('14')): '''输入延迟错误''' total.setText(f'{get_lang('2c')}: {error_text}') - if type == 'main': + if type == InputChange.main_window: global is_error main_window.right_click_button.setEnabled(False) @@ -411,7 +397,7 @@ def check_default_var(value): raise ValueError return True except ValueError: - if type == 'main': + if type == InputChange.main_window: on_delay_error(get_lang('60')) else: on_delay_error() @@ -467,7 +453,7 @@ def check_default_var(value): on_delay_error() return - if type == 'main': + if type == InputChange.main_window: main_window.right_click_button.setEnabled(True) main_window.left_click_button.setEnabled(True) is_error = False @@ -491,7 +477,7 @@ def check_default_var(value): if is_inf: total.setText(f'{get_lang('2c')}: {get_lang('2b')}') - if type == 'main': + if type == InputChange.main_window: if delay_num == 0: on_delay_error() else: @@ -565,7 +551,7 @@ def create_reg(self): '''检查是否已启用开机自启动''' start_path = Path(os.environ['APPDATA'], 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', self.app_name) if not(start_path.exists()): - create_shortcut(str(start_path), str(Path.cwd() / 'main.exe'), 'ClickMouse', work_dir=str(Path.cwd())) + create_shortcut(str(start_path), str(Path.cwd() / 'main.exe') + ' --quiet', 'ClickMouse', work_dir=str(Path.cwd())) self.disable() def is_enabled(self): @@ -609,21 +595,6 @@ def new_msg(parent, new_color_bar(msg_box) return msg_box - -class QtThread(QThread): - '''检查更新工作线程''' - finished = Signal(object) # 爬取完成信号 - - def __init__(self, func, args=(), kwargs={}, parent=None): - super().__init__(parent) - self.func = func - self.args = args - self.kwargs = kwargs - - def run(self): - '''线程执行函数''' - result = self.func(*self.args, **self.kwargs) - self.finished.emit(result) class UHotkeyLineEdit(QLineEdit): '''能够捕获热键组合的输入框,只有获得焦点时才更新''' @@ -663,7 +634,7 @@ def on_combination_pressed(self, keys_str_list): class UFrame(QFrame): def __init__(self, parent=None): super().__init__(parent) - set_style(self, 'frame') + set_style(self, StyleClass.frame) class HotkeyListener(QObject): '''热键监听器类,用于在后台线程中监听全局热键''' @@ -848,17 +819,17 @@ def refresh_title(self): def left_check(self): if clicker.left_clicked: - set_style(main_window.left_click_button, 'selected') + set_style(main_window.left_click_button, StyleClass.selected) else: logger.debug('左键未连点') - set_style(main_window.left_click_button, '') + set_style(main_window.left_click_button, StyleClass.none) def right_check(self): if clicker.right_clicked: - set_style(main_window.right_click_button,'selected') + set_style(main_window.right_click_button,StyleClass.selected) else: logger.debug('右键未连点') - set_style(main_window.right_click_button, '') + set_style(main_window.right_click_button, StyleClass.none) class RunAfter: def __init__(self): @@ -898,7 +869,7 @@ def __init__(self): self.current_theme = self.current_theme.replace('auto-', '') except AttributeError: settings['select_style'] = 0 - save_settings(settings) + save_settings() MessageBox.critical(None, get_lang('14'), get_lang('12')) logger.critical('设置的样式索引超出范围,已恢复默认样式设置。') run_software('main.py', 'main.exe') @@ -1003,7 +974,7 @@ def apply_global_theme(self): current_theme = self.current_theme.replace('auto-', '') select_styles = styles[current_theme] - + if self.use_windows_color: if select_styles.css_data['.meta']['mode'] == 'dark': select_styles = select_styles.replace(['.selected', 'background-color'], StyleReplaceMode.ALL, lighten_color_hex(self.windows_color, 0.4), output_json=False) @@ -1016,40 +987,11 @@ def apply_global_theme(self): select_styles = select_styles.replace(['.selected', 'background-color'], StyleReplaceMode.ALL, self.windows_color, output_json=False) select_styles = select_styles.replace(['.selected:hover', 'background-color'], StyleReplaceMode.ALL, lighten_color_hex(self.windows_color, 0.4), output_json=False) select_styles = select_styles.replace(['.selected:pressed', 'background-color'], StyleReplaceMode.ALL, lighten_color_hex(self.windows_color, -0.165), output_json=False) - + app.setStyleSheet(select_styles.css_text) # 全局应用 self.refresh() -class SettingText: - select_lang = 'select_lang' - show_tray_icon ='show_tray_icon' - soft_delay ='soft_delay' - click_delay = 'click_delay' - click_times = 'click_times' - delay_unit = 'delay_unit' - times_unit = 'times_unit' - delay_error_use_default = 'failed_use_default' - times_error_use_default = 'times_failed_use_default' - update_enabled = 'update_enabled' - update_notify = 'update_notify' - quiet_update = 'quiet_update' - update_ok_notify = 'update_ok_notify' - update_frequency = 'update_frequency' - 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' - default_doc_link = 'default_doc_link' - lang_doc = 'lang_doc' - update_log_path = 'update_log_path' - -class SettingValue: +class SettingValue(BaseSettingValue): def get(self, value): default_value = default_settings.get(value, None) if isinstance(default_value, str): @@ -1057,124 +999,7 @@ def get(self, value): var_name = default_value[5:] default_value = eval(var_name) return settings.get(value, default_value) - - def __getitem__(self, key): - return self.get(key) - - def __setitem__(self, key, value): - raise ValueError('SettingValue is readonly') - - def __delitem__(self, key): - raise ValueError('SettingValue is readonly') - - @property - def select_lang(self): - return self[SettingText.select_lang] - - @property - def show_tray_icon(self): - return self[SettingText.show_tray_icon] - - @property - def soft_delay(self): - return self[SettingText.soft_delay] - - @property - def click_delay(self): - return self[SettingText.click_delay] - - @property - def click_times(self): - return self[SettingText.click_times] - - @property - def delay_unit(self): - return self[SettingText.delay_unit] - - @property - def times_unit(self): - return self[SettingText.times_unit] - - @property - def delay_error_use_default(self): - return self[SettingText.delay_error_use_default] - - @property - def times_error_use_default(self): - return self[SettingText.times_error_use_default] - - @property - def update_enabled(self): - return self[SettingText.update_enabled] - - @property - def update_notify(self): - return self[SettingText.update_notify] - - @property - def quiet_update(self): - return self[SettingText.quiet_update] - - @property - def update_ok_notify(self): - return self[SettingText.update_ok_notify] - - @property - def update_frequency(self): - return self[SettingText.update_frequency] - - @property - def select_style(self): - return self[SettingText.select_style] - - @property - def use_windows_color(self): - return self[SettingText.use_windows_color] - - @property - 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] - - @property - def fast_click_hotkey(self): - return self[SettingText.fast_click_hotkey] - - @property - def main_window_hotkey(self): - return self[SettingText.main_window_hotkey] - - @property - def default_doc_link(self): - return self[SettingText.default_doc_link] - - @property - def lang_doc(self): - return self[SettingText.lang_doc] - - @property - def update_log_path(self): - return self[SettingText.update_log_path] - + class MainWindow(UMainWindow): def __init__(self): logger.debug('初始化主窗口') @@ -1210,7 +1035,7 @@ def init_ui(self): title = QLabel(get_lang('0b')) # 创建标题风格 - set_style(title, 'big_text_24') + set_style(title, StyleClass.big_24) title.setAlignment(Qt.AlignHCenter | Qt.AlignTop) # 按钮 @@ -1261,7 +1086,7 @@ def init_ui(self): # 总连点时长提示 self.total_time_label = ULabel(get_lang('2c')) self.total_time_label.setAlignment(Qt.AlignHCenter) - set_style(self.total_time_label, 'big_text_16') + set_style(self.total_time_label, StyleClass.big_16) self.total_time_label.textChanged.emit() # 创建状态栏 @@ -1292,10 +1117,10 @@ def init_ui(self): self.pause_button.clicked.connect(clicker.pause_click) self.stop_button.clicked.connect(self.on_stop) - self.input_delay.textChanged.connect(lambda: on_input_change(type='main')) - self.input_times.textChanged.connect(lambda: on_input_change(type='main')) - self.delay_combo.currentIndexChanged.connect(lambda: on_input_change(type='main')) - self.times_combo.currentIndexChanged.connect(lambda: on_input_change(type='main')) + self.input_delay.textChanged.connect(lambda: on_input_change(type=InputChange.main_window)) + self.input_times.textChanged.connect(lambda: on_input_change(type=InputChange.main_window)) + self.delay_combo.currentIndexChanged.connect(lambda: on_input_change(type=InputChange.main_window)) + self.times_combo.currentIndexChanged.connect(lambda: on_input_change(type=InputChange.main_window)) self.status_bar.messageChanged.connect(self.reload_status) @@ -1395,7 +1220,7 @@ def create_menu_bar(self): update_check.triggered.connect(lambda: self.on_update(True)) settings_action.triggered.connect(self.show_setting) exit_action.triggered.connect(app.quit) - create_issue_action.triggered.connect(lambda: open_url('https://github.com/xystudiocode/pyClickMouse/issues/new/choose')) + create_issue_action.triggered.connect(lambda: open_url(setting_value.feedback)) def open_doc(self, *, path: str=''): '''打开文档''' @@ -1619,8 +1444,8 @@ def on_stop(self): self.is_ready = True # 重置按钮样式 - set_style(self.left_click_button, '') - set_style(self.right_click_button, '') + set_style(self.left_click_button, StyleClass.none) + set_style(self.right_click_button, StyleClass.none) # 重置文本 self.pause_button.setText(get_lang('0f')) @@ -1640,20 +1465,20 @@ def on_click_changed(self, left, right): '''click按钮状态改变''' if left: # 左键点击 - set_style(self.left_click_button, 'selected') - set_style(self.right_click_button, '') + set_style(self.left_click_button, StyleClass.selected) + set_style(self.right_click_button, StyleClass.none) self.right_click_button.setEnabled(False) self.left_click_button.setEnabled(True) elif right: # 右键点击 - set_style(self.right_click_button, 'selected') - set_style(self.left_click_button, '') + set_style(self.right_click_button, StyleClass.selected) + set_style(self.left_click_button, StyleClass.none) self.right_click_button.setEnabled(True) self.left_click_button.setEnabled(False) else: # 未点击 - set_style(self.left_click_button, '') - set_style(self.right_click_button, '') + set_style(self.left_click_button, StyleClass.none) + set_style(self.right_click_button, StyleClass.none) self.right_click_button.setEnabled(True) self.left_click_button.setEnabled(True) @@ -1716,7 +1541,7 @@ def init_ui(self): # 按钮 logger.debug('创建按钮') ok_button = QPushButton(get_lang('1e')) - set_style(ok_button, 'selected') + set_style(ok_button, StyleClass.selected) # 布局 central_layout.addWidget(self.image_label, 0, 0, 1, 1) @@ -1759,10 +1584,10 @@ def init_ui(self): logger.debug('加载列表标题') title = QLabel(get_lang('3d')) - set_style(title, 'big_text_20') + set_style(title, StyleClass.big_20) dest = QLabel(get_lang('3e')) - set_style(dest, 'dest') + set_style(dest, StyleClass.dest) # 布局1 logger.debug('加载布局-1') @@ -1777,10 +1602,10 @@ def init_ui(self): dest = QLabel(get_lang('35')) size = QLabel(get_lang('36')) - set_style(file, 'bold') - set_style(path, 'bold') - set_style(dest, 'bold') - set_style(size, 'bold') + set_style(file, StyleClass.b) + set_style(path, StyleClass.b) + set_style(dest, StyleClass.b) + set_style(size, StyleClass.b) # 布局2 logger.debug('加载布局-2') layout.addWidget(file, 2, 0) @@ -1858,7 +1683,7 @@ def init_ui(self): scan_cache = QPushButton(get_lang('38')) ok = QPushButton(get_lang('1f')) clean_cache = QPushButton(get_lang('39')) - set_style(clean_cache, 'selected') + set_style(clean_cache, StyleClass.selected) # 布局4 logger.debug('加载布局-4') @@ -2131,11 +1956,11 @@ def init_ui(self): title = QLabel(get_lang('24')) version = QLabel(get_lang('25').format(__version__, result[1])) - set_style(title, 'big_text_16') + set_style(title, StyleClass.big_16) # 按钮 update = QPushButton(get_lang('26')) # 更新按钮 - set_style(update, 'selected') + set_style(update, StyleClass.selected) update_log = QPushButton(get_lang('27')) # 查看更新日志按钮 cancel = QPushButton(get_lang('1f')) # 取消按钮 @@ -2235,11 +2060,11 @@ def init_ui(self): title = QLabel(get_lang('b3')) tip = QLabel(get_lang('b8')) - set_style(title, 'big_text_16') + set_style(title, StyleClass.big_16) # 按钮 update = QPushButton(get_lang('7e')) # 更新按钮 - set_style(update, 'selected') + set_style(update, StyleClass.selected) update_log = QPushButton(get_lang('27')) # 查看更新日志按钮 revert = QPushButton(get_lang('6a')) # 回滚更新按钮 cancel = QPushButton(get_lang('1f')) # 取消按钮 @@ -2336,7 +2161,7 @@ def init_ui(self): # 总连点时长提示 self.total_time_label = QLabel(main_window.total_time_label.text()) self.total_time_label.setAlignment(Qt.AlignHCenter) - set_style(self.total_time_label, 'big_text_16') + set_style(self.total_time_label, StyleClass.big_16) # 创建布局 logger.debug('创建按钮布局') @@ -2386,8 +2211,8 @@ def init_ui(self): central_layout = QVBoxLayout() # 内容 - self.left_clicked = QLabel(f'{get_lang('69')}:') - self.right_clicked = QLabel(f'{get_lang('6a')}:') + 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')}:') @@ -2397,7 +2222,7 @@ def init_ui(self): # 底边栏 bottom_layout = QHBoxLayout() ok_button = QPushButton(get_lang('1e')) - set_style(ok_button, 'selected') + set_style(ok_button, StyleClass.selected) ok_button.clicked.connect(self.close) # 布局 @@ -2419,8 +2244,8 @@ def init_ui(self): def update_attr(self): '''更新属性''' - self.left_clicked.setText(f'{get_lang('69')}: {get_lang('7b') if clicker.left_clicked else get_lang('7c')}') - self.right_clicked.setText(f'{get_lang('6a')}: {get_lang('7b') if clicker.right_clicked else get_lang('7c')}') + 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')}') @@ -2446,13 +2271,16 @@ def __init__(self, values:dict | None = None): self.setFixedSize(self.width(), self.height()) self.setWindowTitle(filter_hotkey(get_lang('04'))) self.setParent(main_window) - self.setWindowIcon(icon) + self.setWindowIcon(QIcon(get_resource_path('icons', 'extensions', f'cms{'pre' if is_pre else ''}.ico'))) self.setWindowFlags( Qt.Window | Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint ) # 设置窗口属性 # 变量 - self.page_choice_buttons = [get_lang('42'), get_lang('a6'), get_lang('43'), get_lang('44'), get_lang('69'), filter_hotkey(get_lang('5f'))] + if dev_flags.get('new_settings', False): + 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('cb'), get_lang('d3')] + else: + 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.last_page = None self.now_page = 0 self.values = {} if values is None else values @@ -2496,14 +2324,20 @@ def parse_hotkey(input: UHotkeyLineEdit): self.page_update = self.page_choice_buttons[3] # 更新设置 self.page_hotkey = self.page_choice_buttons[4] # 热键设置 self.page_doc = self.page_choice_buttons[5] # 文档设置 + if dev_flags.get('new_settings', False): + self.page_notify = self.page_choice_buttons[6] # 提示设置 + self.page_flags = self.page_choice_buttons[7] # 实验室 + else: + self.page_notify = '' + self.page_flags = self.page_choice_buttons[6] # 实验室 # 标题标签 title_label = QLabel(title) - set_style(title_label, 'big_text_24') + set_style(title_label, StyleClass.big_24) # 内容标签 content_label = QLabel(get_lang('7d')) - set_style(content_label, 'dest') + set_style(content_label, StyleClass.dest) # 布局 layout.addWidget(title_label) @@ -2546,7 +2380,18 @@ def parse_hotkey(input: UHotkeyLineEdit): 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')) + + repair_tip = QLabel(get_lang('d1')) + set_style(repair_tip, StyleClass.d_11) + + repair_start_layout.addWidget(repair_start_button) + repair_start_layout.addWidget(repair_tip) + repair_start_layout.addStretch(1) + # 延迟 soft_delay_layout = QHBoxLayout() # 颜色延迟布局 soft_delay_setting = setting_value.soft_delay @@ -2560,7 +2405,7 @@ def parse_hotkey(input: UHotkeyLineEdit): soft_delay.setFixedWidth(200) delay_tip_label = QLabel(get_lang("8a")) - set_style(delay_tip_label, 'dest_small') + set_style(delay_tip_label, StyleClass.d_11) # 布局 soft_delay_layout.addWidget(QLabel(f'{get_lang('b0')}\n{get_lang('b5')}:')) @@ -2568,7 +2413,20 @@ def parse_hotkey(input: UHotkeyLineEdit): 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, 'big_text_16') + 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() # 重置布局 @@ -2580,7 +2438,13 @@ def parse_hotkey(input: UHotkeyLineEdit): # 布局 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) @@ -2589,12 +2453,15 @@ def parse_hotkey(input: UHotkeyLineEdit): layout.addLayout(repair_layout) # 绑定事件 - self.lang_choice.currentIndexChanged.connect(lambda: self.on_need_restart_setting_changed(self.lang_choice.currentIndex, 'select_lang')) - tray.checkStateChanged.connect(lambda: self.on_setting_changed(tray.isChecked,'show_tray_icon')) + self.lang_choice.currentIndexChanged.connect(lambda: self.on_need_restart_setting_changed(self.lang_choice.currentIndex, 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, 'soft_delay')) + 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) case self.page_click: set_content_label(get_lang('84')) # 选择默认连点器延迟 @@ -2642,10 +2509,9 @@ def parse_hotkey(input: UHotkeyLineEdit): 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, 'big_text_16') + set_style(self.total_time_label, StyleClass.big_16) # 布局 layout_time.addLayout(unit_time_layout) @@ -2660,22 +2526,25 @@ def parse_hotkey(input: UHotkeyLineEdit): layout.addStretch(1) # 连接信号 - self.default_delay.textChanged.connect(lambda: self.on_default_input_changed(self.default_delay, 'click_delay', use_default_delay)) - self.default_delay.textChanged.connect(lambda: on_input_change(type='setting')) - use_default_delay.checkStateChanged.connect(lambda: self.on_setting_changed(use_default_delay.isChecked, 'failed_use_default')) - self.default_time.textChanged.connect(lambda: self.on_default_input_changed(self.default_time, 'click_times', use_default_time)) - self.default_time.textChanged.connect(lambda: on_input_change(type='setting')) - use_default_time.checkStateChanged.connect(lambda: self.on_setting_changed(use_default_time.isChecked, 'times_failed_use_default')) - self.delay_combo.currentIndexChanged.connect(lambda: self.on_setting_changed(self.delay_combo.currentIndex, 'delay_unit')) - self.delay_combo.currentIndexChanged.connect(lambda: on_input_change(type='setting')) - self.times_combo.currentIndexChanged.connect(lambda: self.on_setting_changed(self.times_combo.currentIndex, 'times_unit')) - self.times_combo.currentIndexChanged.connect(lambda: on_input_change(type='setting')) + 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) @@ -2693,10 +2562,9 @@ def parse_hotkey(input: UHotkeyLineEdit): update_frequency_layout.addWidget(self.update_frequency) update_frequency_layout.addStretch(1) - self.on_enable_update(self.enable_update.isChecked()) - # 布局 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) @@ -2704,10 +2572,15 @@ def parse_hotkey(input: UHotkeyLineEdit): # 连接信号 self.enable_update.checkStateChanged.connect(self.on_enable_update_changed) - self.update_notify.checkStateChanged.connect(lambda: self.on_setting_changed(self.update_notify.isChecked, 'update_notify')) - self.quiet_install.checkStateChanged.connect(lambda: self.on_setting_changed(self.quiet_install.isChecked, 'quiet_update')) - self.update_ok.checkStateChanged.connect(lambda: self.on_setting_changed(self.update_ok.isChecked, 'update_ok_notify')) - self.update_frequency.currentIndexChanged.connect(lambda: self.on_setting_changed(self.update_frequency.currentIndex, 'update_frequency')) + 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_style: set_content_label(get_lang('a7')) # 选择窗口风格 @@ -2727,7 +2600,7 @@ def parse_hotkey(input: UHotkeyLineEdit): style_use_windows_layout = QHBoxLayout() # 颜色使用windows按钮布局 style_choice_use_windows = UCheckBox(get_lang('a8')) tip_label = QLabel(get_lang('b4')) - set_style(tip_label, 'dest_small') + set_style(tip_label, StyleClass.d_11) style_choice_use_windows.setChecked(setting_value.use_windows_color) # 布局 @@ -2736,7 +2609,7 @@ def parse_hotkey(input: UHotkeyLineEdit): theme_layout = QHBoxLayout() # 主题布局 theme_tip_window = QLabel(get_lang('4b')) - set_style(theme_tip_window, 'dest_small') + set_style(theme_tip_window, StyleClass.d_11) theme_combo = QComboBox() theme_combo.addItems(QStyleFactory.keys()) theme_combo.setCurrentText(setting_value.theme) @@ -2757,137 +2630,150 @@ def parse_hotkey(input: UHotkeyLineEdit): layout.addWidget(create_horizontal_line()) # 连接信号 - self.style_choice.currentIndexChanged.connect(lambda: self.on_setting_changed(self.style_choice.currentIndex, 'select_style')) - style_choice_use_windows.checkStateChanged.connect(lambda: self.on_setting_changed(style_choice_use_windows.isChecked, 'use_windows_color')) - theme_combo.currentIndexChanged.connect(lambda: self.on_setting_changed(theme_combo.currentText, 'theme')) + 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: 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) + # 左键连点 - left_click_layout = QHBoxLayout() # 左键连点布局 - left_click_input = UHotkeyLineEdit() # 左键连点输入框 - left_click_input.setText(format_keys(setting_value.left_click_hotkey)) - left_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + 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')) # 还原默认设置按钮 # 布局 - left_click_layout.addWidget(QLabel(f'{get_lang('0c')}: '), 1) # 左键连点提示 - left_click_layout.addWidget(left_click_input, 6) - left_click_layout.addWidget(left_repair_button, 2) - left_click_layout.addStretch() + 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() # 右键连点 - right_click_layout = QHBoxLayout() # 右键连点布局 - right_click_input = UHotkeyLineEdit() # 右键连点输入框 - right_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + self.right_click_layout = QHBoxLayout() # 右键连点布局 + self.right_click_input = UHotkeyLineEdit() # 右键连点输入框 + self.right_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮 - right_click_input.setText(format_keys(setting_value.right_click_hotkey)) + self.right_click_input.setText(format_keys(setting_value.right_click_hotkey)) # 布局 - right_click_layout.addWidget(QLabel(f'{get_lang('0d')}: '), 1) # 右键连点提示 - right_click_layout.addWidget(right_click_input, 6) - right_click_layout.addWidget(right_repair_button, 2) - right_click_layout.addStretch() + 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() # 暂停/重启连点 - pause_click_layout = QHBoxLayout() # 暂停/重启连点布局 - pause_click_input = UHotkeyLineEdit() # 暂停/重启连点输入框 - pause_click_input.setText(format_keys(setting_value.pause_click_hotkey)) - pause_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + 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')) # 还原默认设置按钮 # 布局 - pause_click_layout.addWidget(QLabel(f'{get_lang('6b')}: '), 1) # 暂停/重启连点提示 - pause_click_layout.addWidget(pause_click_input, 6) - pause_click_layout.addWidget(pause_repair_button, 2) - pause_click_layout.addStretch() + 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() # 停止连点 - stop_click_layout = QHBoxLayout() # 停止连点布局 - stop_click_input = UHotkeyLineEdit() # 停止连点输入框 - stop_click_input.setText(format_keys(setting_value.stop_click_hotkey)) - stop_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + 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')) # 还原默认设置按钮 # 布局 - stop_click_layout.addWidget(QLabel(f'{get_lang('6c')}: '), 1) # 停止连点提示 - stop_click_layout.addWidget(stop_click_input, 6) - stop_click_layout.addWidget(stop_repair_button, 2) - stop_click_layout.addStretch() + 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() # 连点属性 - click_attr_layout = QHBoxLayout() # 连点属性布局 - click_attr_input = UHotkeyLineEdit() # 连点属性输入框 - click_attr_input.setText(format_keys(setting_value.click_attr_hotkey)) - click_attr_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + 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')) # 还原默认设置按钮 # 布局 - click_attr_layout.addWidget(QLabel(f'{get_lang('8c')}: '), 1) # 连点属性提示 - click_attr_layout.addWidget(click_attr_input, 6) - click_attr_layout.addWidget(click_attr_button, 2) - click_attr_layout.addStretch() + 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() # 快速连点 - fast_click_layout = QHBoxLayout() # 快速连点布局 - fast_click_input = UHotkeyLineEdit() # 快速连点输入框 - fast_click_input.setText(format_keys(setting_value.fast_click_hotkey)) - fast_click_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + 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')) # 还原默认设置按钮 # 布局 - fast_click_layout.addWidget(QLabel(f'{get_lang('75')}: '), 1) # 快速连点提示 - fast_click_layout.addWidget(fast_click_input, 6) - fast_click_layout.addWidget(fast_click_button, 2) - fast_click_layout.addStretch() + 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() # 主窗口 - main_window_layout = QHBoxLayout() # 主窗口布局 - main_window_input = UHotkeyLineEdit() # 主窗口输入框 - main_window_input.setText(format_keys(setting_value.main_window_hotkey)) - main_window_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + 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')) # 还原默认设置按钮 # 布局 - main_window_layout.addWidget(QLabel(f'{get_lang('76')}: '), 1) # 主窗口提示 - main_window_layout.addWidget(main_window_input, 6) - main_window_layout.addWidget(main_window_button, 2) - main_window_layout.addStretch() + 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() # 布局 - layout.addLayout(left_click_layout) - layout.addLayout(right_click_layout) - layout.addLayout(pause_click_layout) - layout.addLayout(stop_click_layout) - layout.addLayout(click_attr_layout) - layout.addLayout(fast_click_layout) - layout.addLayout(main_window_layout) + 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) # 连接信号 - left_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(left_click_input), 'left_click_hotkey')) - right_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(right_click_input), 'right_click_hotkey')) - pause_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(pause_click_input), 'pause_click_hotkey')) - stop_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(stop_click_input),'stop_click_hotkey')) - click_attr_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(click_attr_input), 'click_attr_hotkey')) - fast_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(fast_click_input), 'fast_click_hotkey')) + 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)) - left_repair_button.clicked.connect(lambda: self.repair_settings('left_click_hotkey')) - right_repair_button.clicked.connect(lambda: self.repair_settings('right_click_hotkey')) - pause_repair_button.clicked.connect(lambda: self.repair_settings('pause_click_hotkey')) - stop_repair_button.clicked.connect(lambda: self.repair_settings('stop_click_hotkey')) - click_attr_button.clicked.connect(lambda: self.repair_settings('click_attr_hotkey')) - fast_click_button.clicked.connect(lambda: self.repair_settings('fast_click_hotkey')) - main_window_button.clicked.connect(lambda: self.repair_settings('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('控制文档打开时候的行为。') + 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'))) # 默认打开文档提示 - default_doc_layout.addWidget(default_doc_link) + 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('c3'), get_lang('c4')] + [i['lang_name'] for i in langs if i['supported']]) + 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) # 布局 @@ -2895,35 +2781,96 @@ def parse_hotkey(input: UHotkeyLineEdit): default_lang_layout.addWidget(lang_choice) default_lang_layout.addStretch() - uppdate_log_path_layout = QHBoxLayout() # 更新日志路径布局 - uppdate_log_path_input = QLineEdit() # 更新日志路径输入框 - uppdate_log_path_input.setText(setting_value.update_log_path) + 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')) # 还原默认路径按钮 # 布局 - uppdate_log_path_layout.addWidget(QLabel(get_lang('c6'))) # 更新日志路径提示 - uppdate_log_path_layout.addWidget(uppdate_log_path_input) - uppdate_log_path_layout.addStretch() + 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, 'dest_small') + set_style(label, StyleClass.d_11) layout.addLayout(default_doc_layout) layout.addLayout(default_lang_layout) layout.addWidget(create_horizontal_line()) - layout.addLayout(uppdate_log_path_layout) + 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, 'default_doc_link')) - lang_choice.currentIndexChanged.connect(lambda: self.on_setting_changed(self.lang_choice.currentIndex, 'lang_doc')) - uppdate_log_path_input.textChanged.connect(lambda: self.on_setting_changed(uppdate_log_path_input.text, 'update_log_path')) + 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(),)) + 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, 'selected') + set_style(self.restart_button, StyleClass.selected) self.restart_button.clicked.connect(self.restart) restart_layout.addStretch() @@ -2939,6 +2886,54 @@ def parse_hotkey(input: UHotkeyLineEdit): return page + def save_dev_config(self, checked: bool, flag_name: str): + dev_flags[flag_name] = checked + with open('data/dev_flags.json', 'w', encoding='utf-8') as f: + json.dump(dev_flags, f) + + def on_warning_update(self, state): + '''启用软件启动警告''' + self.package_warning.setEnabled(state) + + def on_enable_warn(self, state): + '''启用软件启动警告''' + self.on_warning_update(state) + self.on_setting_changed(self.start_warning.isChecked, SettingText.show_warning) + + def on_sync_notice(self, state): + '''提示同步''' + self.notice_update_notify.setChecked(state) + self.notice_update_notify.setEnabled(setting_value.update_enabled) + self.update_notify.setChecked(state) + self.update_notify.setEnabled(setting_value.update_enabled) + + def on_sync_ok_notice(self, state): + '''提示同步''' + self.notice_update_ok_notify.setChecked(state) + self.notice_update_ok_notify.setEnabled(setting_value.update_enabled) + self.update_ok.setChecked(state) + self.update_ok.setEnabled(setting_value.update_enabled) + + 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) + def on_enable_update_changed(self, state): '''更新提示复选框状态改变''' global should_check_update_res @@ -2951,7 +2946,12 @@ def on_enable_update_changed(self, state): else: should_check_update_res = should_check_update() main_window.on_check_update() - self.on_setting_changed(self.enable_update.isChecked, 'update_enabled') + self.on_setting_changed(self.enable_update.isChecked, SettingText.update_enabled) + + def on_enable_hotkey_changed(self, state): + '''热键复选框状态改变''' + self.on_enable_hotkey(state) + self.on_setting_changed(self.hotkey_enabled.isChecked, SettingText.hotkey_enabled) def on_enable_update(self, state): '''更新提示复选框状态改变''' @@ -2959,6 +2959,14 @@ def on_enable_update(self, state): self.quiet_install.setEnabled(state) self.update_ok.setEnabled(state) self.update_frequency.setEnabled(state) + if dev_flags.get('new_settings', False): + self.notice_update_notify.setEnabled(state) + self.notice_update_ok_notify.setEnabled(state) + + def repair_auto_start(self): + os.remove(Path(os.environ['APPDATA'], 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', 'Clickmouse.lnk')) + auto_start_manager.create_reg() + MessageBox.information(self, get_lang('16'), get_lang('d2')) def showEvent(self, event): '''窗口显示事件''' @@ -2974,7 +2982,7 @@ def repair_settings(self, key: str): del settings[key] except KeyError: pass - save_settings(settings) + save_settings() self.window_restarted.emit() def repair_all_settings(self): @@ -2982,7 +2990,7 @@ def repair_all_settings(self): if MessageBox.warning(self, get_lang('15'), get_lang('22'), MessageButtonTemplate.YESNO) != 2: # 不确认重置 return settings = {} - save_settings(settings) + save_settings() self.app.setStyle(default_theme) self.values.update({'need_restart': True}) # values 用于存储需要重启后还原的内容 self.window_restarted.emit() @@ -3019,7 +3027,7 @@ def restart_window(self): def on_setting_changed(self, handle, key, *args): '''更新检查提示选择事件''' settings[key] = handle(*args) - save_settings(settings) + save_settings() def on_default_input_changed(self, default: QLineEdit, key: str, use_default: UCheckBox): '''默认延迟输入框内容变化事件''' @@ -3042,16 +3050,16 @@ def on_page_button_clicked(self, index): # 更新按钮样式 for i, button in enumerate(self.buttons): if i == index: - set_style(button, 'selected') + set_style(button, StyleClass.selected) else: - set_style(button, '') + set_style(button, StyleClass.none) def restart(self): app.quit(lambda: run_software('main.py', 'main.exe')) def init_right_pages(self): super().init_right_pages() - set_style(self.buttons[0], 'selected') + set_style(self.buttons[0], StyleClass.selected) def on_clicker_started(self): '''连点器启动事件''' @@ -3078,7 +3086,7 @@ def init_ui(self): # 提示 mode_label = QLabel(get_lang('ab')) mode_label.setAlignment(Qt.AlignCenter) - set_style(mode_label, 'big_text_16') + set_style(mode_label, StyleClass.big_16) # 选择框 self.mode_combo = QComboBox() @@ -3112,11 +3120,13 @@ def __init__(self): self.app.setQuitOnLastWindowClosed(False) # 关闭窗口时不退出应用 # 激活主窗口 - main_window.show() + if '--quiet' not in sys.argv: + main_window.show() # 加载警告 - if not has_packages: - MessageBox.warning(None, get_lang('15'), get_lang('ae')) + if setting_value.show_warning: + if (not has_packages) and setting_value.show_package_warning: + MessageBox.warning(None, get_lang('15'), get_lang('ae')) # 创建热键监听器 self.hotkey_listener = get_hotkey_listener_instance() @@ -3158,30 +3168,33 @@ def create_menu(self): # 添加'打开应用'菜单项 show_action = QAction(get_lang('68'), self.app) - show_action.triggered.connect(self.show_main_window) + show_action.triggered.connect(lambda: self.show_window(main_window)) tray_menu.addAction(show_action) # 添加分隔线 tray_menu.addSeparator() # 控制类按钮 - left_click_action = QAction(get_lang('69'), self.app) - right_click_action = QAction(get_lang('6a'), self.app) + left_click_action = QAction(get_lang('0c'), self.app) + right_click_action = QAction(get_lang('0d'), self.app) pause_action = QAction(get_lang('6b'), self.app) stop_action = QAction(get_lang('6c'), self.app) - set_delay_action = QAction(get_lang('6d'), self.app) - - set_delay_action.triggered.connect(lambda: pyautogui.press('f1')) - left_click_action.triggered.connect(lambda: pyautogui.press('f2')) - right_click_action.triggered.connect(lambda: pyautogui.press('f3')) - pause_action.triggered.connect(lambda: pyautogui.press('f4')) - stop_action.triggered.connect(lambda: pyautogui.press('f6')) - + set_delay_action = QAction(get_lang('75'), self.app) + click_attr_action = QAction(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)) + 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) tray_menu.addAction(stop_action) tray_menu.addAction(set_delay_action) + tray_menu.addAction(click_attr_action) # 添加分割线 tray_menu.addSeparator() @@ -3204,7 +3217,7 @@ def start_hotkey_listener(self): def on_tray_icon_activated(self, reason): '''处理托盘图标激活事件''' if reason == QSystemTrayIcon.ActivationReason.Trigger: # 左键点击 - self.show_main_window() + self.show_window(main_window) self.refresh() def check_delay(self, input_delay): @@ -3217,11 +3230,6 @@ def check_delay(self, input_delay): return False return True - def show_main_window(self): - '''显示主窗口''' - main_window.is_start_from_tray = True - main_window.show() - def quit_application(self): '''退出应用程序''' # 停止热键监听 @@ -3261,7 +3269,7 @@ def quit(self, code=lambda: None): def run_combination(self, combination): '''运行组合键''' - if can_run_hotkey: + if can_run_hotkey and setting_value.hotkey_enabled: self.on_combination_pressed(combination) def on_start_clicker_tray(self, direction): @@ -3308,18 +3316,18 @@ def on_combination_pressed(self, combination): combination = format_keys(combination, source=True) if all_in_list(combination, setting_value.fast_click_hotkey): - # 处理Ctrl+Alt+F组合键 + # 处理快速连点组合键 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): - # 处理Ctrl+Alt+M组合键 + # 处理主窗口组合键 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): - # 处理Ctrl+Alt+A组合键 + # 处理连点属性组合键 self.show_window(click_attr_window) elif all_in_list(combination, setting_value.left_click_hotkey): self.on_start_clicker_tray('left') # 左键 @@ -3348,7 +3356,7 @@ def on_start(self): self.tray_icon.showMessage(get_lang('14'), get_lang('af'), QSystemTrayIcon.MessageIcon.Critical, 1000) if __name__ == '__main__': - from sharelibs import (mem_id, get_resource_path, run_as_admin) # 共享库 + from sharelibs import (mem_id, get_resource_path, run_as_admin, get_lang) # 共享库 import json # 用于读取json文件 from pathlib import Path # 路径库 @@ -3425,7 +3433,7 @@ def on_start(self): from uiStyles import (UnitInputLayout, styles, maps, StyleReplaceMode, ULabel, CustonMessageButton) # 软件界面样式 from uiStyles import indexes as style_indexes # 界面组件样式索引 from sharelibs import (run_software, langs, create_shortcut, __version__, is_pre, get_icon, default_button_text, - get_unit_value, unit_lang, get_size_text, get_file_hash, system_lang) # 共享库 + get_unit_value, unit_lang, get_size_text, get_file_hash, system_lang, settings, QtThread, default_settings) # 共享库 import parse_dev # 解析开发固件配置 import winreg # 注册表库 import math # 数学库 @@ -3435,6 +3443,7 @@ def on_start(self): from traceback import format_exc # 异常格式化 from itertools import chain # 迭代器库 import platform # 系统信息 + from txtinfo import * # 系统api import ctypes @@ -3453,11 +3462,6 @@ def on_start(self): DWMWCP_ROUND = 2 DWMNCRP_ENABLED = 1 - logger.info('加载设置') - settings = load_settings() - with open(get_resource_path('defaultsetting.json'), 'r', encoding='utf-8') as f: - default_settings: dict = json.load(f) - logger.info('加载服务程序') setting_value = SettingValue() clicker = Click() @@ -3485,7 +3489,29 @@ def on_start(self): settings_need_restart = False can_update = False + + try: + with open(data_path / 'dev_flags.json', 'r', encoding='utf-8') as f: + dev_flags = json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + dev_flags = {} + + try: + with open(get_resource_path('dev_settings.json'), 'r', encoding='utf-8') as f: + dev_settings = json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + dev_settings = {} + + dev_back = dev_flags.copy() + for k in dev_flags.keys(): + if k not in [i['key'] for i in dev_settings]: + del dev_back[k] + logger.warning(f'测试版配置ID{k}不存在,自动清除') + dev_flags = dev_back.copy() + with open(data_path / 'dev_flags.json', 'w', encoding='utf-8') as f: + json.dump(dev_flags, f) + # 单位控制 latest_index = 2 select_lang = setting_value.select_lang @@ -3521,7 +3547,7 @@ def on_start(self): # 加载窗口 logger.info('加载ui') main_window = MainWindow() - on_input_change(type='main') # 更新时间估计状态 + on_input_change(type=InputChange.main_window) # 更新时间估计状态 about_window = AboutWindow() clean_cache_window = CleanCacheWindow() @@ -3529,10 +3555,9 @@ def on_start(self): update_window = UpdateWindow() click_attr_window = ClickAttrWindow() fast_click_window = FastSetClickWindow() - setting_window = SettingWindow() - on_input_change(type='setting') # 更新时间估计状态 - setting_window.click_setting_changed.connect(lambda: on_input_change(type='setting')) + on_input_change(type=InputChange.setting_window) # 更新时间估计状态 + setting_window.click_setting_changed.connect(lambda: on_input_change(type=InputChange.setting_window)) setting_window.window_restarted.connect(on_update_setting_window) set_import_extension_window = SetImportExtensionModeWindow() diff --git a/Gui/res/defaultsetting.json b/Gui/res/defaultsetting.json index aff7a98..c85939f 100644 --- a/Gui/res/defaultsetting.json +++ b/Gui/res/defaultsetting.json @@ -1 +1 @@ -{"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/clickmouse_docs/{lang}", "lang_doc": 0, "update_log_path": "updatelog"} \ No newline at end of file +{"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/clickmouse_docs/{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"} \ No newline at end of file diff --git a/Gui/res/dev_settings.json b/Gui/res/dev_settings.json new file mode 100644 index 0000000..6deaaa9 --- /dev/null +++ b/Gui/res/dev_settings.json @@ -0,0 +1,7 @@ +[ + { + "name": "More settings", + "key": "new_settings", + "desc": "Can add more settings include:\nToast settings\nEnable hotkey checkbox\nFeedback settings." + } +] \ No newline at end of file diff --git a/Gui/res/icons/entensions/cge.ico b/Gui/res/icons/extensions/cge.ico similarity index 100% rename from Gui/res/icons/entensions/cge.ico rename to Gui/res/icons/extensions/cge.ico diff --git a/Gui/res/icons/entensions/cle.ico b/Gui/res/icons/extensions/cle.ico similarity index 100% rename from Gui/res/icons/entensions/cle.ico rename to Gui/res/icons/extensions/cle.ico diff --git a/Gui/res/icons/entensions/cmm.ico b/Gui/res/icons/extensions/cmm.ico similarity index 100% rename from Gui/res/icons/entensions/cmm.ico rename to Gui/res/icons/extensions/cmm.ico diff --git a/Gui/res/icons/entensions/cms.ico b/Gui/res/icons/extensions/cms.ico similarity index 100% rename from Gui/res/icons/entensions/cms.ico rename to Gui/res/icons/extensions/cms.ico diff --git a/Gui/res/icons/extensions/cmspre.ico b/Gui/res/icons/extensions/cmspre.ico new file mode 100644 index 0000000000000000000000000000000000000000..d7b635242703caf1bda01b951ff97f6e561d21d0 GIT binary patch literal 102134 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
    >Li%>hi8Rxka=Wf~fc`cmFa+~&>HEZ_rldiVS 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 Vf)$SfdI|B)o={{W%>R)hcm literal 0 HcmV?d00001 diff --git a/Gui/res/langs/init.json b/Gui/res/langs/init.json index d1ccd35..16f003a 100644 --- a/Gui/res/langs/init.json +++ b/Gui/res/langs/init.json @@ -49,7 +49,8 @@ "2c": "\nDesktop shortcut", "2d": "\nStart menu shortcut", "2e": "Checking for update files...", - "2f": "No package change" + "2f": "No package change", + "30": "Current status:" } }, { @@ -102,7 +103,8 @@ "2c": "\n桌面快捷方式", "2d": "\n开始菜单快捷方式", "2e": "检查需要更新的文件...", - "2f": "没有包变动" + "2f": "没有包变动", + "30": "当前状态:" } } ] \ No newline at end of file diff --git a/Gui/res/langs/langs.json b/Gui/res/langs/langs.json index 81645bb..19d3c23 100644 --- a/Gui/res/langs/langs.json +++ b/Gui/res/langs/langs.json @@ -37,7 +37,7 @@ "1d": "This software is open source under the MIT license, and you\ncan copy, modify, and distribute it. The author is xystudio.", "1e": "OK", "1f": "Cancel", - "20": "Reset to default settings", + "20": "Reset", "21": "To control the hotkey of the software.", "22": "Do you want to reset this setting?This operation cannot be undone!", "23": "Window theme: ", @@ -74,14 +74,14 @@ "42": "General", "43": "Clicker", "44": "Update", - "45": "Language:", + "45": "Software language", "46": "Default click delay(ms) when empty", "47": "Use default click delay when non-positive integer", "48": "Enable update", "49": "Quiet update", - "4a": "Update notify", - "4b": "Tip: Some themes may not be adapted well for dark or inverted mode, if you use\ndark or inverted mode, it's better to use the default theme.", - "4c": "Show update complete message", + "4a": "Update toast", + "4b": "Tip: Some themes may not be adapted well for dark or inverted mode, if you\nuse dark or inverted mode, it's better to use the default theme.", + "4c": "Update complete toast", "4d": "There is already an update operation.", "4e": "Clickmouse update is disabled, please open the settings to enable it.", "4f": "Some languages need to restart the software to take effect.", @@ -193,12 +193,24 @@ "c0": "Every month", "c1": "Update frequency: ", "c2": "Documentation link: ", - "c3": "Language", - "c4": "System Settings", + "c3": "Feedback link:", + "c4": "System language", "c5": "Default language: ", "c6": "Update log path: ", "c7": "Language of {lang} documentation settings", - "c8": "We do not recommend closing the update, because it will make your clickmouse unstable, still want to close?" + "c8": "We do not recommend closing the update, because it will make your clickmouse unstable, still want to close?", + "c9": "Enable hotkey", + "ca": "Control the default document website.", + "cb": "Toast", + "cc": "To set the software toast.", + "cd": "Clickmouse Startup Warning", + "ce": "Clickmouse startup will check the resources, and if some resources are missing,\nit will warn.", + "cf": "Official extension package missing warning", + "d0": "We do not recommend closing the update, because it will make your clickmouse\nunstable.", + "d1": "If your startup is not working, or startup opened the clickmouse\nwindow, you can try to click to fix it.", + "d2": "Repair successfully.", + "d3": "Lab", + "d4": "To test some experimental features, which may not be stable." } }, { @@ -239,7 +251,7 @@ "1d": "本软件使用MIT许可证开源,可以复制,修改,分发等;作者为\nxystudio。", "1e": "确定", "1f": "取消", - "20": "还原默认设置", + "20": "重置", "21": "用于控制软件的热键。", "22": "是否重置此设置?此操作不可逆!", "23": "窗口主题: ", @@ -276,13 +288,13 @@ "42": "常规", "43": "连点器", "44": "更新", - "45": "语言", + "45": "软件语言", "46": "连点延迟默认值", "47": "连点延迟错误时使用默认值", "48": "开启更新", "49": "静默更新", "4a": "更新提示", - "4b": "注意: 部分主题可能不能很好的适配深色或反色模式,若你使用深色或反色模式,建议使用\n默认主题。", + "4b": "注意: 部分主题可能不能很好的适配深色或反色模式,若你使用深色或反色模式,建议\n使用默认主题。", "4c": "更新完成后弹出提示", "4d": "已经有一个更新操作。", "4e": "Clickmouse更新被禁用,请打开设置以启用它。", @@ -395,12 +407,24 @@ "c0": "每月", "c1": "更新频率: ", "c2": "文档默认链接: ", - "c3": "软件语言", - "c4": "系统设置", + "c3": "反馈链接: ", + "c4": "系统语言", "c5": "文档默认语言: ", "c6": "更新日志路径: ", "c7": "{lang}对应文档设置的语言", - "c8": "我们不建议关闭更新,因为这会让你的clickmouse变得不稳定,仍要关闭?" + "c8": "我们不建议关闭更新,因为这会让你的clickmouse变得不稳定,仍要关闭?", + "c9": "启用热键", + "ca": "控制文档功能默认打开的网站。", + "cb": "通知", + "cc": "设置软件的提示。", + "cd": "软件启动警告", + "ce": "软件启动会检查资源,如果缺少一些资源,将会警告。", + "cf": "官方扩展包丢失警告", + "d0": "我们不建议关闭更新,因为这会让你的clickmouse变得不稳定。", + "d1": "如果你的开机自启动出现问题,或打开了clickmouse窗口,可尝试点击\n它来修复。", + "d2": "修复成功。", + "d3": "实验室", + "d4": "用于测试一些功能,可能不稳定。" } } ] \ No newline at end of file diff --git a/Gui/res/vars/caches.json b/Gui/res/vars/caches.json index c716460..da1514f 100644 --- a/Gui/res/vars/caches.json +++ b/Gui/res/vars/caches.json @@ -5,7 +5,7 @@ }, { "check_index": 1, - "path": {"dirs": [], "files": ["update.json", "update_log.md"]} + "path": {"dirs": [], "files": ["update.json"]} }, { "check_index": 2, diff --git a/Gui/res/versions.json b/Gui/res/versions.json index 0165816..1be508a 100644 --- a/Gui/res/versions.json +++ b/Gui/res/versions.json @@ -1 +1 @@ -{"package_format_version": "1.2.0", "clickmouse":"3.2.0.19rc2"} +{"package_format_version": "1.2.0", "clickmouse":"3.2.1.20alpha1"} diff --git a/Gui/sharelibs.py b/Gui/sharelibs.py index 1eba649..f023f02 100644 --- a/Gui/sharelibs.py +++ b/Gui/sharelibs.py @@ -4,6 +4,7 @@ from pathlib import Path from PySide6.QtWidgets import QMessageBox from PySide6.QtGui import QIcon +from PySide6.QtCore import QThread, Signal import os import subprocess import winreg @@ -67,6 +68,9 @@ def load_settings(): return {} settings = load_settings() +with open(get_resource_path('defaultsetting.json'), 'r', encoding='utf-8') as f: + default_settings: dict = json.load(f) + with open(get_resource_path('vars', 'mem_id.json'), 'r') as f: mem_id = json.load(f) @@ -265,4 +269,19 @@ def get_file_hash(file_path, algorithm): return None except Exception as e: print(f'计算哈希时出错: {e}') - return None \ No newline at end of file + return None + +class QtThread(QThread): + '''检查更新工作线程''' + finished = Signal(object) # 爬取完成信号 + + def __init__(self, func, args=(), kwargs={}, parent=None): + super().__init__(parent) + self.func = func + self.args = args + self.kwargs = kwargs + + def run(self): + '''线程执行函数''' + result = self.func(*self.args, **self.kwargs) + self.finished.emit(result) \ No newline at end of file diff --git a/Gui/txtinfo.py b/Gui/txtinfo.py new file mode 100644 index 0000000..8423429 --- /dev/null +++ b/Gui/txtinfo.py @@ -0,0 +1,195 @@ +from sharelibs import default_settings, settings + +__all__ = ['SettingText', 'StyleClass', 'InputChange'] + +class SettingText: + select_lang = 'select_lang' + show_tray_icon ='show_tray_icon' + soft_delay = 'soft_delay' + click_delay = 'click_delay' + click_times = 'click_times' + delay_unit = 'delay_unit' + times_unit = 'times_unit' + delay_error_use_default = 'failed_use_default' + times_error_use_default = 'times_failed_use_default' + update_enabled = 'update_enabled' + update_notify = 'update_notify' + quiet_update = 'quiet_update' + update_ok_notify = 'update_ok_notify' + update_frequency = 'update_frequency' + 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' + default_doc_link = 'default_doc_link' + lang_doc = 'lang_doc' + update_log_path = 'update_log_path' + hotkey_enabled = 'hotkey_enabled' + show_warning = 'show_warning' + show_package_warning ='show_package_warning' + feedback = 'feedback' + +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): + return self.get(key) + + def __setitem__(self, key, value): + raise ValueError('SettingValue is readonly') + + def __delitem__(self, key): + raise ValueError('SettingValue is readonly') + + @property + def select_lang(self): + return self[SettingText.select_lang] + + @property + def show_tray_icon(self): + return self[SettingText.show_tray_icon] + + @property + def soft_delay(self): + return self[SettingText.soft_delay] + + @property + def click_delay(self): + return self[SettingText.click_delay] + + @property + def click_times(self): + return self[SettingText.click_times] + + @property + def delay_unit(self): + return self[SettingText.delay_unit] + + @property + def times_unit(self): + return self[SettingText.times_unit] + + @property + def delay_error_use_default(self): + return self[SettingText.delay_error_use_default] + + @property + def times_error_use_default(self): + return self[SettingText.times_error_use_default] + + @property + def update_enabled(self): + return self[SettingText.update_enabled] + + @property + def update_notify(self): + return self[SettingText.update_notify] + + @property + def quiet_update(self): + return self[SettingText.quiet_update] + + @property + def update_ok_notify(self): + return self[SettingText.update_ok_notify] + + @property + def update_frequency(self): + return self[SettingText.update_frequency] + + @property + def select_style(self): + return self[SettingText.select_style] + + @property + def use_windows_color(self): + return self[SettingText.use_windows_color] + + @property + 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] + + @property + def fast_click_hotkey(self): + return self[SettingText.fast_click_hotkey] + + @property + def main_window_hotkey(self): + return self[SettingText.main_window_hotkey] + + @property + def default_doc_link(self): + return self[SettingText.default_doc_link] + + @property + def lang_doc(self): + return self[SettingText.lang_doc] + + @property + def update_log_path(self): + return self[SettingText.update_log_path] + + @property + def hotkey_enabled(self): + return self[SettingText.hotkey_enabled] + + @property + def show_warning(self): + return self[SettingText.show_warning] + + @property + def show_package_warning(self): + return self[SettingText.show_package_warning] + + @property + def feedback(self): + return self[SettingText.feedback] + +class StyleClass: + big_16 = 'big_text_16' + big_20 = 'big_text_20' + big_24 = 'big_text_24' + + frame = 'frame' + selected = 'selected' + dest = 'dest' + b = 'bold' + d_11 = 'dest_small' + + none = '' + +class InputChange: + main_window = 'main' + setting_window = 'setting' diff --git a/README.md b/README.md index 840e4de..05a2449 100644 --- a/README.md +++ b/README.md @@ -112,44 +112,8 @@ clickMouse.click_mouse(clickmouse.LEFT, 1000, 10, 10) # 连点10次左键,间 ClickMouse.exe /h # 查看帮助 ``` ## 💻再次编译方法 -请先`cd`到这个项目的根目录 -### C/C++ -#### 🥴头文件 -仅需修改头文件,就可以被调用 -#### ⚙️dll调用 -使用visual studio修改`./dll/dll.sln`里的`源文件/dllmain.cpp` -#### 🟥gui旧版本 ->[!NOTE] ->gui旧版本的再编译不接受pull request -使用visual studio修改`./ClickMouse-old/ClickMouse.sln`里的`源文件/clickmouse.cpp` -### 🐍python -建议先执行`pip install -r requirements.txt` -#### 🔦python库调用 -修改`clickmouse/`下的代码,运行`pip install .`安装 -#### ⚙️pyd调用 -修改`cython/main.py`的代码,然后执行 -```python cython-setup.py build_ext --inplace``` -编译结束后,该目录下应该会有个以`.pyd`结尾的文件。 -#### 🟥gui版本 -使用python打包工具打包,注意需要添加`res/`目录。 - -## 🔨功能 -- 鼠标连点 -- 自定义连点间隔 - -## ⬇️下载 -前往[releases](https://github.com/xystudio889/pyClickMouse/releases)下载 - -## 💊使用方法 -鼠标连点,目前支持左键和右键。 -下方的输入框输入间隔,再选择想要点击的类型即可开始连点。 - +见[协作文档](./CONTRIBUTING.md),找到`## ⬇️配置仓库`这一段,按照说明配置仓库。 ### 📊使用优先级 -普通用户: -```mermaid -graph LR -A[exe] --> B[交互式命令行] -``` 开发人员: ```mermaid graph LR @@ -160,10 +124,9 @@ C[C/C++] --> E[dll调用] --> D 目前支持暂停和停止功能。 ## 🖥️Clickmouse 软件 -### 🔠版本 -clickmouse版本格式为:`A.B.C.D[alpha E][beta F]` -#### 😊正式版本 -正式版不带alpha或beta后缀。 +clickmouse版本格式为:`A.B.C.D[(alpha | beta |.dev | rc) E]` +## 😊正式版本 +正式版不带.dev、alpha、beta或rc后缀。 A位代表有重大更新,有代码级的变动。如1.0升级到2.0就重构了代码。 @@ -173,26 +136,19 @@ C位代表有修复更新,通常会更新一些小功能和一些bug。 D位代表版本代号,通常每A, B, C位有变动时候+1。也有可能A, B, C位没有变动,D位+1,这代表紧急更新,通常是修复几个重大影响的bug。 -#### 🅱️测试版本 -测试版本带alpha或beta后缀。 +## 🅱️测试版本 +测试版本带.dev、alpha、beta或rc后缀。 通常前面的`A.B.C.D`在一个测试周期内不变,代表下一个版本。 -`alpha`代表开发更新,功能不完善,bug较多,不会发布release。 - -`beta`代表发布预备更新,功能完善,bug较少,将不会更新功能,会发布release,但无法被更新工具捕获。 - -## 📃内容展望 -- [x] 连点功能 -- [x] 输入间隔 -- [x] 热键启动 -- [x] 输入次数 -- [x] 自动检查更新 -- [x] 自动下载和安装更新 -- [x] 设置 -- [ ] 命令行参数 -- [ ] 扩展 -- [x] 官方安装助手 -- [x] 包管理 -- [x] 后台运行 -- [ ] 第三方api工具 \ No newline at end of file +`.dev`代表早期开发更新,功能不稳定,bug很多,位于版本项目初期。这阶段新增的功能将会被放到实验室中,并默认关闭。 + +`alpha`代表晚期开发更新,功能不完善,bug较多,位于版本项目早期。这阶段新增的功能将会被放到实验室中,并默认关闭。 + +`beta`代表发布测试更新,功能完善,bug较少,不会再新增功能,位于版本项目中期。并且会逐步合并实验室中的feature。 + +`rc`代表预备发布版本,功能完善,bug较少,会修复一些重要安全问题或bug,最接近正式版,即将发布正式版,位于版本项目末期。 + +::: tip 提示 +rc最后一个版本将直接合并到测试版,不再单独发布;功能完全一样。 +::: diff --git a/makefile b/makefile index fb28fbf..c01ce6e 100644 --- a/makefile +++ b/makefile @@ -10,6 +10,7 @@ clickmouse: gui/main.py $(command) --file-description="Clickmouse repair" --product-name="CmRepair" --windows-icon-from-ico=gui/res/icons/clickmouse/repair.ico --file-version="2.2.3.6" gui/repair.py --enable-plugin=pyside6 --windows-console-mode="disable" --windows-uac-admin $(command) --file-version="1.0.1.3" gui/check_reg_ver.py --windows-console-mode="disable" $(command) --file-version="1.0.0.2" gui/updater.py --windows-console-mode="disable" + powershell -ExecutionPolicy Bypass -Command "./merge-distFolders.ps1 -SourcePath ./dist/clickmouse/" clickmouse_lib: setup.py python setup.py bdist_wheel @@ -19,19 +20,25 @@ clickmouse_lib: setup.py extension: echo No extension! -clean: +clean_pyd: del -s -q -f build\ clickmouse.egg-info cython\*.c gitclean: git gc --aggressive --prune=now pyd: - "C:\program files\python38\python.exe" cython/setup.py build_ext - "C:\program files\python39\python.exe" cython/setup.py build_ext - "C:\program files\python310\python.exe" cython/setup.py build_ext - "C:\program files\python311\python.exe" cython/setup.py build_ext - "C:\program files\python312\python.exe" cython/setup.py build_ext - "C:\program files\python313\python.exe" cython/setup.py build_ext - "C:\program files\python313\python3.13t.exe" cython/setup.py build_ext - "C:\program files\python314\python.exe" cython/setup.py build_ext - "C:\program files\python314\python3.14t.exe" cython/setup.py build_ext \ No newline at end of file + @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 diff --git a/merge-distFolders.ps1 b/merge-distFolders.ps1 new file mode 100644 index 0000000..e404298 --- /dev/null +++ b/merge-distFolders.ps1 @@ -0,0 +1,128 @@ +<# +.SYNOPSIS + ϲǰĿ¼ .dist ļеֱ test ļУɺɾյ .dist ļС +.DESCRIPTION + ɨ赱ǰĿ¼ .dist βļС + - ͨ .dist ļУֱļ/ļУļƶ test ļУ + ĿѴͬĿƶɺ .dist ļΪգɾ + - Ϊ 2.dist ļУΪ 2 ƶ test ļУ + ĿѴΪ 2 ļ +.NOTES + ˽űʼմ PowerShell ĵǰĿ¼ +.EXAMPLE + cd D:\Projects + .\Merge-DistFolders.ps1 + D:\Projects µ .dist ļС +#> + +param( + [string]$SourcePath = (Get-Location).Path +) + +# ȷԴ· +if (-not (Test-Path -Path $SourcePath -PathType Container)) { + Write-Error "Source path '$SourcePath' does not exist or is not a folder." + exit 1 +} + +# Ŀļ testλڵǰĿ¼£ +$targetDir = Join-Path -Path $SourcePath -ChildPath "clickmouse" + +$allAllowdNotNoneFolder = $false + +# ȡ .dist ļУļУ +$distFolders = Get-ChildItem -Path $SourcePath -Directory -Force | Where-Object { $_.Name -like "*.dist" } + +if ($distFolders.Count -eq 0) { + Write-Warning "No .dist folders found." + exit 0 +} + +# Ŀļвڣ򴴽 +if (-not (Test-Path -Path $targetDir)) { + New-Item -Path $targetDir -ItemType Directory | Out-Null + Write-Host "Created target folder: $targetDir" +} else { + Write-Host "Target folder already exists: $targetDir" + $choice = Read-Host "Do you want to force delete it? (Y/N, default is N)" + $choice = $choice.Trim().ToUpper() + if ($choice -eq "Y") { + Remove-Item -Path $targetDir -Recurse -Force + Write-Host "Deleted target folder: $targetDir" + New-Item -Path $targetDir -ItemType Directory | Out-Null + Write-Host "Created target folder: $targetDir" + } else { + Write-Warning "You refused to delete the target folder, which may cause merge conflicts, and the software cannot run." + } +} + +foreach ($folder in $distFolders) { + Write-Host "Process folder: $($folder.FullName)" + + # ⴦ 2.dist + if ($folder.Name -eq "updater.dist") { + $destPath = Join-Path -Path $targetDir -ChildPath "updater" + if (Test-Path -Path $destPath) { + Write-Warning "Skip move 'updater.dist', because target location already has 'updater' folder." + Remove-Item -Path $folder.FullName -Force -Recurse -ErrorAction SilentlyContinue + } else { + try { + # ֱƶ + Move-Item -Path $folder.FullName -Force -Destination $destPath + Write-Host "'updater.dist' rename to 'updater' and move to 'clickmouse'" + } catch { + Write-Error "Move 'updater.dist' failed: $_" + } + } + continue + } + + # ͨ .dist ļУȡֱļļУ + $items = Get-ChildItem -Path $folder.FullName -Force + + foreach ($item in $items) { + $destItemPath = Join-Path -Path $targetDir -ChildPath $item.Name + + if (Test-Path -Path $destItemPath) { + Remove-Item -Path $item.FullName -Force -Recurse -ErrorAction SilentlyContinue + continue + } + + try { + Move-Item -Path $item.FullName -Destination $targetDir -Force + Write-Host "Moved item: $($item.FullName) -> $targetDir" + } catch { + Write-Error "Move '$($item.FullName)' failed: $_" + } + } + + # ƶɺ .dist ļǷΪգΪɾ + if (-not $allAllowdNotNoneFolder) { + $remaining = Get-ChildItem -Path $folder.FullName -Force + if ($remaining.Count -eq 0) { + try { + Remove-Item -Path $folder.FullName -Force + Write-Host "Deleted empty dist folder: $($folder.FullName)" + } catch { + Write-Error "Delete dist folder failed: $_" + } + } else { + $choice = Read-Host "Please choose whether to force delete non-empty dist folder '$($folder.FullName)'? (Y/A/N, default is N)" + $choice = $choice.Trim().ToUpper() + if ($choice -eq "Y") { + try { + Remove-Item -Path $folder.FullName -Force -Recurse + Write-Host "Deleted non-empty dist folder: $($folder.FullName)" + } catch { + Write-Error "Delete dist folder failed: $_" + } + } elseif ($choice -eq "A") { + $allAllowdNotNoneFolder = $true + } else { + Write-Warning "Dist folder '$($folder.FullName)' is not empty, skip delete." + } + } + } +} + +Write-Host "Done." \ No newline at end of file From d85edc31a81b557e9864db4ed9251673b73bd5e1 Mon Sep 17 00:00:00 2001 From: xystudio_u <173288240@qq.com> Date: Sat, 14 Mar 2026 08:06:44 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gui/res/versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gui/res/versions.json b/Gui/res/versions.json index 9729c0b..11d19e7 100644 --- a/Gui/res/versions.json +++ b/Gui/res/versions.json @@ -1 +1 @@ -{"package_format_version": "1.2.0", "clickmouse":"3.2.0.19rc3"} +{"package_format_version": "1.2.0", "clickmouse":"3.2.0.19"} From 18c271d1cbc972083c7ca1ef4ab41095532fe3e7 Mon Sep 17 00:00:00 2001 From: xystudio_u <173288240@qq.com> Date: Sat, 14 Mar 2026 10:58:23 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E2=9C=85feat(clickmouse)=203.2.1.20:?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0"=E6=97=A0=E5=AE=9E=E9=AA=8C=E9=A1=B9?= =?UTF-8?q?=E6=97=B6=E9=9A=90=E8=97=8F=E5=AE=9E=E9=AA=8C=E5=AE=A4"?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gui/main.py | 42 +++++++++++++++++++++++++------------ Gui/res/defaultsetting.json | 2 +- Gui/res/dev_settings.json | 2 +- Gui/res/langs/langs.json | 8 ++++--- Gui/txtinfo.py | 5 +++++ makefile | 4 ++-- 6 files changed, 43 insertions(+), 20 deletions(-) diff --git a/Gui/main.py b/Gui/main.py index 7ff6d3d..f3064c2 100644 --- a/Gui/main.py +++ b/Gui/main.py @@ -2281,6 +2281,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('cb'), get_lang('d3')] else: 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.create_setting_page_value() + self.last_page = None self.now_page = 0 self.values = {} if values is None else values @@ -2293,6 +2296,23 @@ def __init__(self, values:dict | None = None): logger.debug('初始化设置窗口完成') + def create_setting_page_value(self): + self.page_general = self.page_choice_buttons[0] # 默认设置 + self.page_style = self.page_choice_buttons[1] # 样式设置 + self.page_click = self.page_choice_buttons[2] # 连点器设置 + self.page_update = self.page_choice_buttons[3] # 更新设置 + self.page_hotkey = self.page_choice_buttons[4] # 热键设置 + self.page_doc = self.page_choice_buttons[5] # 文档设置 + if dev_flags.get('new_settings', False): + self.page_notify = self.page_choice_buttons[6] # 提示设置 + self.page_flags = self.page_choice_buttons[7] # 实验室 + else: + self.page_notify = '' + self.page_flags = self.page_choice_buttons[6] # 实验室 + + if (setting_value.hide_flags and len(dev_settings) == 0): + del self.page_choice_buttons[self.page_choice_buttons.index(self.page_flags)] + def check_values(self): '''检查设置值''' # 热键设置 @@ -2318,19 +2338,6 @@ def create_horizontal_line(): def parse_hotkey(input: UHotkeyLineEdit): return input.text().split('+') - self.page_general = self.page_choice_buttons[0] # 默认设置 - self.page_style = self.page_choice_buttons[1] # 样式设置 - self.page_click = self.page_choice_buttons[2] # 连点器设置 - self.page_update = self.page_choice_buttons[3] # 更新设置 - self.page_hotkey = self.page_choice_buttons[4] # 热键设置 - self.page_doc = self.page_choice_buttons[5] # 文档设置 - if dev_flags.get('new_settings', False): - self.page_notify = self.page_choice_buttons[6] # 提示设置 - self.page_flags = self.page_choice_buttons[7] # 实验室 - else: - self.page_notify = '' - self.page_flags = self.page_choice_buttons[6] # 实验室 - # 标题标签 title_label = QLabel(title) set_style(title_label, StyleClass.big_24) @@ -2434,6 +2441,10 @@ def parse_hotkey(input: UHotkeyLineEdit): 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) @@ -2450,6 +2461,9 @@ def parse_hotkey(input: UHotkeyLineEdit): 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) # 绑定事件 @@ -2462,6 +2476,8 @@ def parse_hotkey(input: UHotkeyLineEdit): 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')) # 选择默认连点器延迟 diff --git a/Gui/res/defaultsetting.json b/Gui/res/defaultsetting.json index c85939f..294a851 100644 --- a/Gui/res/defaultsetting.json +++ b/Gui/res/defaultsetting.json @@ -1 +1 @@ -{"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/clickmouse_docs/{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"} \ No newline at end of file +{"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/clickmouse_docs/{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 diff --git a/Gui/res/dev_settings.json b/Gui/res/dev_settings.json index 6deaaa9..962ed0f 100644 --- a/Gui/res/dev_settings.json +++ b/Gui/res/dev_settings.json @@ -2,6 +2,6 @@ { "name": "More settings", "key": "new_settings", - "desc": "Can add more settings include:\nToast settings\nEnable hotkey checkbox\nFeedback settings." + "desc": "Can add more settings include:\nToast settings\nEnable hotkey checkbox\nFeedback settings and more..." } ] \ No newline at end of file diff --git a/Gui/res/langs/langs.json b/Gui/res/langs/langs.json index 19d3c23..3b02723 100644 --- a/Gui/res/langs/langs.json +++ b/Gui/res/langs/langs.json @@ -179,7 +179,7 @@ "b2": "res/key.json file not found. Please configure res/key.json and recompile.", "b3": "Install the update successfully", "b4": "Note: This operation will not completely change the component style.\nComponents such as selection boxes will still use the system accent color.", - "b5": "(Restart the program to take effect.)", + "b5": "(Restart program to effect.)", "b6": "Auto startup", "b7": "Clicker is running, please stop it first.", "b8": "Close the clickmouse to update.", @@ -210,7 +210,8 @@ "d1": "If your startup is not working, or startup opened the clickmouse\nwindow, you can try to click to fix it.", "d2": "Repair successfully.", "d3": "Lab", - "d4": "To test some experimental features, which may not be stable." + "d4": "To test some experimental features, which may not be stable.", + "d5": "Hide \"lab\" when no experimental features" } }, { @@ -424,7 +425,8 @@ "d1": "如果你的开机自启动出现问题,或打开了clickmouse窗口,可尝试点击\n它来修复。", "d2": "修复成功。", "d3": "实验室", - "d4": "用于测试一些功能,可能不稳定。" + "d4": "用于测试一些功能,可能不稳定。", + "d5": "无实验项时候隐藏\"实验室\"设置项" } } ] \ No newline at end of file diff --git a/Gui/txtinfo.py b/Gui/txtinfo.py index 8423429..b2a30f1 100644 --- a/Gui/txtinfo.py +++ b/Gui/txtinfo.py @@ -34,6 +34,7 @@ class SettingText: show_warning = 'show_warning' show_package_warning ='show_package_warning' feedback = 'feedback' + hide_flags = 'hide_flags' class SettingValue: def get(self, value): @@ -177,6 +178,10 @@ def show_package_warning(self): def feedback(self): return self[SettingText.feedback] + @property + def hide_flags(self): + return self[SettingText.hide_flags] + class StyleClass: big_16 = 'big_text_16' big_20 = 'big_text_20' diff --git a/makefile b/makefile index c01ce6e..e279bdf 100644 --- a/makefile +++ b/makefile @@ -1,10 +1,10 @@ -command = python -m nuitka --msvc=latest --remove-output --company-name="xystudio" --copyright="Copyright 2026 xystudio" --trademarks="xystudio" --product-version="3.2.0" --standalone --output-dir=dist/clickmouse/ +command = python -m nuitka --msvc=latest --remove-output --company-name="xystudio" --copyright="Copyright 2026 xystudio" --trademarks="xystudio" --product-version="3.2.1" --standalone --output-dir=dist/clickmouse/ 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.2.0.19" --enable-plugin=pyside6 --windows-console-mode="disable" --include-data-file=gui/7z.exe=7z.exe --include-data-file=gui/7z.dll=7z.dll + $(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.2.1.20" --enable-plugin=pyside6 --windows-console-mode="disable" --include-data-file=gui/7z.exe=7z.exe --include-data-file=gui/7z.dll=7z.dll $(command) --file-description="Clickmouse uninstall" --product-name="uninstall" --windows-icon-from-ico=gui/res/icons/clickmouse/uninstall.ico --file-version="2.1.4.7" gui/uninstall.py --enable-plugin=pyside6 --windows-console-mode="disable" --windows-uac-admin $(command) --file-description="Clickmouse IPK" --product-name="CmIPK" --windows-icon-from-ico=gui/res/icons/clickmouse/init.ico --file-version="2.0.6.10" gui/install_pack.py --enable-plugin=pyside6 --windows-console-mode="disable" $(command) --file-description="Clickmouse repair" --product-name="CmRepair" --windows-icon-from-ico=gui/res/icons/clickmouse/repair.ico --file-version="2.2.3.6" gui/repair.py --enable-plugin=pyside6 --windows-console-mode="disable" --windows-uac-admin From c8641b1e999915ee8a710e3d44194cc901e35311 Mon Sep 17 00:00:00 2001 From: xystudio_u <173288240@qq.com> Date: Wed, 1 Apr 2026 19:39:32 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=88=9B=E5=BB=BAmodify=20clickmouse=E7=9A=84=E5=BC=80?= =?UTF-8?q?=E5=A7=8B=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gui/install_pack.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Gui/install_pack.py b/Gui/install_pack.py index 12ccc73..47eefd1 100644 --- a/Gui/install_pack.py +++ b/Gui/install_pack.py @@ -742,6 +742,7 @@ def install(self): create_shortcut(os.path.join(start_menu_path, 'ClickMouse.lnk'), fr'{install_path}\main.exe', 'Clickmouse') create_shortcut(os.path.join(start_menu_path, 'Uninstall clickmouse.lnk'), fr'{install_path}\uninstall.exe', 'Uninstall clickmouse') create_shortcut(os.path.join(start_menu_path, 'Repair clickmouse.lnk'), fr'{install_path}\repair.exe', 'Repair clickmouse') + create_shortcut(os.path.join(start_menu_path, 'Repair clickmouse.lnk'), fr'{install_path}\install_pack.exe', 'Modify clickmouse') if self.create_desktop_shortcut: create_shortcut(fr'{os.path.expanduser('~')}\Desktop\clickmouse.lnk', fr'{install_path}\main.exe', 'clickmouse') self.set_page(self.PAGE_finish) From c3369970dba2fcf665679e322ef745c438274684 Mon Sep 17 00:00:00 2001 From: xystudio_u <173288240@qq.com> Date: Sat, 11 Apr 2026 21:02:29 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=90=9Bfix(clickmouse)=203.2.1:=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=BA=9Bbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug.yml | 1 + .github/ISSUE_TEMPLATE/feature.yml | 1 + .github/ISSUE_TEMPLATE/tasks.yml | 1 + CONTRIBUTING.md | 1 + ClickMouse-old/README.md | 2 +- Gui/hooks/hotkey.py | 18 +- Gui/install_pack.py | 36 +- Gui/logger.py | 14 +- Gui/main.py | 482 ++--- Gui/repair.py | 14 +- Gui/res/langs/langs.json | 4 +- Gui/res/rules/dev_data.json | 1 - Gui/res/rules/vars.json | 1 - Gui/res/update.json | 2 +- Gui/res/versions.json | 2 +- Gui/sharelibs.py | 9 + Gui/txtinfo.py | 2 +- Gui/uninstall.py | 23 +- Gui/updater.py | 12 +- README.md | 16 +- clickmouse_api/pyproject.toml | 2 +- clickmouse_api/setup.py | 2 +- guiclean/clickclean.py | 1829 +++++++++++++++++++ guiclean/dev_list/in_dev | 0 guiclean/logger.py | 123 ++ guiclean/res/LICENSE.txt | 21 + guiclean/res/defaultsetting.json | 1 + guiclean/res/icons/clickmouse/cms.ico | Bin 0 -> 458870 bytes guiclean/res/icons/clickmouse/icon.ico | Bin 0 -> 458870 bytes guiclean/res/langs/default_button_text.json | 26 + guiclean/res/langs/langs.json | 187 ++ guiclean/res/langs/units.json | 30 + guiclean/res/styles/dark.qss | 236 +++ guiclean/res/styles/light.qss | 219 +++ guiclean/sharelibs.py | 185 ++ guiclean/txtinfo.py | 125 ++ guiclean/uiStyles/QUI.py | 4 + guiclean/uiStyles/WidgetStyles.py | 287 +++ guiclean/uiStyles/__init__.py | 7 + guiclean/uiStyles/uiTemplate.py | 88 + guiclean/uiStyles/widgets.py | 299 +++ makefile | 18 +- merge-distFolders.ps1 | 2 +- pyproject.toml | 2 +- release_note.md | 2 +- rename-extensionPath.ps1 | 0 requirements.txt | 2 +- setup.py | 2 +- 48 files changed, 4026 insertions(+), 315 deletions(-) delete mode 100644 Gui/res/rules/dev_data.json delete mode 100644 Gui/res/rules/vars.json create mode 100644 guiclean/clickclean.py create mode 100644 guiclean/dev_list/in_dev create mode 100644 guiclean/logger.py create mode 100644 guiclean/res/LICENSE.txt create mode 100644 guiclean/res/defaultsetting.json create mode 100644 guiclean/res/icons/clickmouse/cms.ico create mode 100644 guiclean/res/icons/clickmouse/icon.ico create mode 100644 guiclean/res/langs/default_button_text.json create mode 100644 guiclean/res/langs/langs.json create mode 100644 guiclean/res/langs/units.json create mode 100644 guiclean/res/styles/dark.qss create mode 100644 guiclean/res/styles/light.qss create mode 100644 guiclean/sharelibs.py create mode 100644 guiclean/txtinfo.py create mode 100644 guiclean/uiStyles/QUI.py create mode 100644 guiclean/uiStyles/WidgetStyles.py create mode 100644 guiclean/uiStyles/__init__.py create mode 100644 guiclean/uiStyles/uiTemplate.py create mode 100644 guiclean/uiStyles/widgets.py create mode 100644 rename-extensionPath.ps1 diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 6d9315e..ef3234e 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -58,6 +58,7 @@ body: - ♻️清理缓存 - 🏳️‍🌈主题/样式 - 📄文档 + - 🟦clickClean - ❓其他/未知 validations: required: true diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index afa3d8e..beb6d94 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -38,6 +38,7 @@ body: - ♻️清理缓存 - 📄文档 - 🏳️‍🌈主题/样式 + - 🟦clickClean - ❓其他/未知 validations: required: true diff --git a/.github/ISSUE_TEMPLATE/tasks.yml b/.github/ISSUE_TEMPLATE/tasks.yml index 95f47f7..07965b1 100644 --- a/.github/ISSUE_TEMPLATE/tasks.yml +++ b/.github/ISSUE_TEMPLATE/tasks.yml @@ -35,6 +35,7 @@ body: - ♻️清理缓存 - 📄文档 - 🏳️‍🌈主题/样式 + - 🟦clickClean - ❓其他/未知 validations: required: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55a5ba3..9712a08 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -133,6 +133,7 @@ pip install -r requirements.txt ```powershell make clickmouse # 编译clickmouse make extension # 编译扩展 +make clickclean # 如果你要编译精简版,请用这个。 ``` 7. 运行`dist/clickmouse/clickmouse/main.exe`就可以加载clickmouse了。 ### 🥴头文件 diff --git a/ClickMouse-old/README.md b/ClickMouse-old/README.md index 4041460..20a6d21 100644 --- a/ClickMouse-old/README.md +++ b/ClickMouse-old/README.md @@ -1,6 +1,6 @@ # Clickmouse >[!IMPORTANT] -> 本库已经停止更新,请转移到[pyClickMouse](https://github.com/xystudio889/pyClickMouse),C++代码写不动QAQ,转为python了 +> 本库已经停止更新,请转移到[pyClickMouse](https://github.com/xystudiocode/pyClickMouse)来获取最新版本。 [更新日志](./updates.md) diff --git a/Gui/hooks/hotkey.py b/Gui/hooks/hotkey.py index 34144ff..56a8451 100644 --- a/Gui/hooks/hotkey.py +++ b/Gui/hooks/hotkey.py @@ -1,10 +1,18 @@ from PySide6.QtCore import QObject, Signal from PySide6.QtWidgets import QApplication from pynput import keyboard -import sys +import re count = 0 +def multi_replace(text, replace_dict): + '''一次性替换多个子串''' + # 将字典键按长度降序排序,避免长词被短词部分覆盖 + sorted_keys = sorted(replace_dict.keys(), key=len, reverse=True) + # 构建正则模式,注意转义特殊字符 + pattern = '|'.join(re.escape(key) for key in sorted_keys) + return re.sub(pattern, lambda m: replace_dict[m.group(0)], text) + class HotkeyListener(QObject): '''热键监听器类,用于在后台线程中监听全局热键''' combination_pressed = Signal(list) # 新增信号,用于发送组合键信息 @@ -69,7 +77,13 @@ def combination_pressed(self, combination): temp_combination = combination.copy() for index, i in enumerate(temp_combination): - temp_combination[index] = i.replace('Key.', '').replace('_l', '').replace('_r', '').replace('_gr', '') + replacements = { + 'Key.': '', + '_l': '', + '_r': '', + '_gr': '' + } + temp_combination[index] = multi_replace(i, replacements) combination = temp_combination.copy() print(combination) diff --git a/Gui/install_pack.py b/Gui/install_pack.py index 47eefd1..9f225e7 100644 --- a/Gui/install_pack.py +++ b/Gui/install_pack.py @@ -3,7 +3,23 @@ import sys app = QApplication(sys.argv) from uiStyles.QUI import * -from uiStyles import PagesUI, UMessageBox, MessageButtonTemplate + +from uiStyles import PagesUI, UMessageBox, MessageButtonTemplate, 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 +import winreg +import zipfile +from shutil import rmtree +import traceback +import os +from pathlib import Path +import json +from txtinfo import StyleClass + +# 系统api +import ctypes +from ctypes import wintypes def import_package(package_id: str): for i in packages_info: @@ -813,7 +829,6 @@ def closeEvent(self, event): event.accept() if __name__ == '__main__': - from sharelibs import mem_id shared_memory = QSharedMemory(mem_id[2]) if shared_memory.attach(): # 已经有一个实例在运行 @@ -824,23 +839,6 @@ def closeEvent(self, event): if is_running: # 已经有一个实例在运行 sys.exit(2) - - 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) - import win32com.client - import winreg - import zipfile - from shutil import rmtree - import traceback - from uiStyles import styles, maps, UCheckBox - import os - from pathlib import Path - import json - from txtinfo import StyleClass - - # 系统api - import ctypes - from ctypes import wintypes is_ipk = '--ipk' in app.arguments() get_ipk_lang = get_inst_lang if is_ipk else get_init_lang diff --git a/Gui/logger.py b/Gui/logger.py index 5540796..4f0666b 100644 --- a/Gui/logger.py +++ b/Gui/logger.py @@ -79,9 +79,12 @@ def __init__(self, name): # 控制台处理器 - 仅WARNING及以上级别 console_handler = logging.StreamHandler() console_handler.setLevel(logging.WARNING) # 设置控制台日志级别 + format = { + 'default_format': '%(asctime)s - %(levelname)s - %(message)s', + 'simple_format': '%(message)s' # 仅输出消息 + } console_fmt = ConditionalFormatter( - '%(asctime)s-%(levelname)s-%(message)s', - simple_format='%(message)s' # 直接输出消息 + **format ) console_handler.setFormatter(console_fmt) @@ -89,8 +92,7 @@ def __init__(self, name): file_handler = logging.FileHandler(folder_path / f'{log_id}.log', mode='a',encoding='utf-8') file_handler.setLevel(logging.INFO) # 设置文件日志级别 file_fmt = ConditionalFormatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s', - simple_format='%(message)s' # 仅输出消息 + **format ) file_handler.setFormatter(file_fmt) @@ -99,7 +101,7 @@ def __init__(self, name): self.logger.addHandler(file_handler) self.info(f'{"-" * 50}', extra={'simple_format': True}) - self.info(f'logger启动') + self.info(f'logger Started') remove_old_log(folder_path) def debug(self, msg, extra=None): @@ -118,4 +120,4 @@ def critical(self, msg, extra=None): self.logger.critical(msg, extra=extra) def exception(self, service, trace, msg='', extra=None): - self.critical(f'{msg}在{service}发生错误:\n{trace}', extra=extra) \ No newline at end of file + self.critical(f'{msg}: An error occurred in {service}:\n{trace}', extra=extra) \ No newline at end of file diff --git a/Gui/main.py b/Gui/main.py index f3064c2..938eb4c 100644 --- a/Gui/main.py +++ b/Gui/main.py @@ -1,13 +1,41 @@ # 加载ui框架 from PySide6.QtWidgets import QApplication import sys +from logger import Logger app = QApplication(sys.argv) +logger = Logger('Main') from uiStyles.QUI import * +# 加载框架 from datetime import datetime # 检查时间 -from uiStyles import (SelectUI, UCheckBox, UMessageBox, MessageButtonTemplate) # 软件界面样式 from pynput import keyboard # 热键功能库 -from txtinfo import SettingValue as BaseSettingValue # 字段值 +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 (run_software, langs, create_shortcut, __version__, is_pre, get_icon, default_button_text, + get_unit_value, unit_lang, get_size_text, get_file_hash, system_lang, settings, QtThread, + default_settings, mem_id, get_resource_path, run_as_admin, get_lang, multi_replace) # 共享库 +import parse_dev # 解析开发固件配置 +import winreg # 注册表库 +import math # 数学库 +import colorsys # 颜色库 +import struct # 字节处理库 +import pytz # 时区库 +from traceback import format_exc # 异常格式化 +from itertools import chain # 迭代器库 +import platform # 系统信息 +from txtinfo import * +import os # 系统库 +import shutil # 用于删除文件夹 +import json # 用于读取json文件 +from pathlib import Path # 路径库 + +# 系统api +import ctypes +from ctypes import wintypes # TODO: 添加更多更新设置,使用hashlib.algorithms_available获取支持的hash算法 @@ -36,20 +64,20 @@ def load_update_cache(): ''' 加载更新缓存文件 ''' - logger.info('加载更新缓存') + logger.info('Load update cache.') if update_cache_path.exists(): with open(update_cache_path, 'r', encoding='utf-8') as f: cache = json.load(f) return cache else: - logger.warning('缓存文件不存在,创建默认缓存文件') + logger.warning('Update cache file not found, will create a new one.') with open(update_cache_path, 'w', encoding='utf-8') as f: f.write('{}') return {} def save_update_cache(**kwargs): '''写入更新缓存文件''' - logger.info('写入缓存文件') + logger.info('Save update cache') cache_data = { 'last_check_time': time(), @@ -63,7 +91,7 @@ def should_check_update(): ''' 检查是否应该检查更新 ''' - logger.info('检查是否应该检查更新') + logger.info('Checking update.') last_check_time = update_cache.get('last_check_time') if not last_check_time: return True @@ -91,7 +119,7 @@ def save_settings(): ''' 保存设置 ''' - logger.info('保存设置') + logger.info('Saving settings.') with open(data_path / 'settings.json', 'w', encoding='utf-8') as f: json.dump(settings, f) @@ -328,7 +356,7 @@ def get_hotkey_listener_instance(): if not hasattr(get_hotkey_listener_instance, "instance"): global hotkey_thread # 驻留线程,防止自动销毁 get_hotkey_listener_instance.instance = HotkeyListener() - logger.info('启动热键监听器') + logger.info('Starting hotkey listener.') # 在后台线程中启动热键监听 hotkey_thread = QtThread(get_hotkey_listener_instance.instance.start_listening) hotkey_thread.start() @@ -336,7 +364,7 @@ def get_hotkey_listener_instance(): def revert_update(): '''回滚更新''' - logger.info('回滚更新') + logger.info('Revert update.') try: os.rename('updater.old', 'updater') except: @@ -508,7 +536,7 @@ def check_default_var(value): class UMainWindow(QMainWindow): '''自定义窗口基类''' def __init__(self, parent=None): - logger.debug('初始化主窗口') + logger.debug('Initializing window.') super().__init__(parent=parent) self.setWindowIcon(icon) @@ -522,7 +550,7 @@ def showEvent(self, event): class UDialog(QDialog): '''自定义对话框基类''' def __init__(self, parent=None): - logger.debug('初始化对话框') + logger.debug('Initializing window.') super().__init__(parent=parent) self.setWindowIcon(icon) @@ -699,13 +727,13 @@ def __init__(self): self.left_clicked = False def mouse_left(self, delay, times): - logger.info('左键连点') + logger.info('Left click') if not self.running: self.mouse_click(button='left', input_delay=delay, times=times) def mouse_right(self, delay, times): # 停止当前运行的点击线程 - logger.info('右键连点') + logger.info('Right click') if not self.running: self.mouse_click(button='right', input_delay=delay, times=times) @@ -716,7 +744,7 @@ def set_default_clicked(self): def mouse_click(self, button: str, input_delay, times): '''鼠标连点''' - logger.info('开始连点') + logger.info('Start click') # 重置状态 if self.click_thread and self.click_thread.isRunning(): self.running = False @@ -746,7 +774,7 @@ def mouse_click(self, button: str, input_delay, times): except Exception: trace = format_exc() MessageBox.critical(None, get_lang('14'), f'{get_lang('1b')}\n{trace}') - logger.exception('连点服务', trace) + logger.exception('Clicker', trace) return # 创建独立线程避免阻塞GUI @@ -770,7 +798,7 @@ def click_loop(): except Exception: trace = format_exc() MessageBox.critical(None, get_lang('14'), f'{get_lang('1b')}\n{trace}') - logger.exception('连点服务', trace) + logger.exception('Clicker', trace) self.stopped.emit() break @@ -780,13 +808,16 @@ def click_loop(): self.stopped.emit() # 启动线程 - logger.info(f'启动连点线程') + logger.info(f'Starting click thread') self.started.emit() self.click_thread = QtThread(click_loop) self.click_thread.start() def pause_click(self): - logger.info('连点器暂停或重启') + if self.paused: + logger.info('Clicker resumed') + else: + logger.info('Clicker paused') self.paused = not self.paused self.pause.emit(self.paused) @@ -799,20 +830,20 @@ def __init__(self): ] def run(self): - logger.info('运行刷新服务') + logger.info('Running refresh service') self.do_step(self.steps) def do_step(self, codes): # 尝试执行代码 for code in codes: - logger.debug(f'执行步骤{code.__name__}') + logger.debug(f'Running step {code.__name__}') try: code() - logger.debug(f'步骤{code.__name__}执行成功') + logger.debug(f'Step {code.__name__} running successfully.') except NameError as e: - logger.warning(f'步骤{code.__name__}操作存在未定义:{e}') + logger.warning(f'Step {code.__name__} not defined: {e}') except Exception as e: - logger.error(f'步骤{code.__name__}执行失败:{e}') + logger.error(f'Step {code.__name__} Running failed: {e}') def refresh_title(self): QTimer.singleShot(setting_value.soft_delay, color_getter.style_changed.emit) @@ -821,14 +852,14 @@ def left_check(self): if clicker.left_clicked: set_style(main_window.left_click_button, StyleClass.selected) else: - logger.debug('左键未连点') + logger.warning('Left click is not enabled.') set_style(main_window.left_click_button, StyleClass.none) def right_check(self): if clicker.right_clicked: set_style(main_window.right_click_button,StyleClass.selected) else: - logger.debug('右键未连点') + logger.warning('Right click is not enabled.') set_style(main_window.right_click_button, StyleClass.none) class RunAfter: @@ -836,17 +867,17 @@ def __init__(self): self.program_list = {} def add(self, name, python_path, exe_path, run_as_admin=False): - logger.debug('添加计划') + 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')) def remove(self, name): - logger.debug('移除计划') + logger.info('Remove run-after plan') del self.program_list[name] MessageBox.information(main_window, get_lang('59'), get_lang('88')) def run(self): - logger.info('运行运行计划') + 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) @@ -871,7 +902,7 @@ def __init__(self): settings['select_style'] = 0 save_settings() MessageBox.critical(None, get_lang('14'), get_lang('12')) - logger.critical('设置的样式索引超出范围,已恢复默认样式设置。') + logger.critical('Setting style index is out of range, will reset to default.') run_software('main.py', 'main.exe') sys.exit(0) @@ -887,7 +918,7 @@ def __init__(self): self.timer.start(setting_value.soft_delay) def load_theme(self): - logger.debug('获取最新的主题') + logger.debug('Get latest theme') theme = None windows_theme = None @@ -918,7 +949,7 @@ def load_theme(self): def check_and_apply_theme(self): '''检查主题是否变化,变化则重新应用''' - logger.debug('检查主题是否变化') + logger.debug('Check theme') self.style = setting_value.select_style @@ -945,7 +976,7 @@ def refresh(self): def apply_titleBar(self, window: QMainWindow | QDialog): '''应用标题栏样式''' - logger.debug('应用标题栏样式') + logger.debug('Apply titleBar style') hwnd = window.winId().__int__() @@ -966,7 +997,7 @@ def apply_global_theme(self): '''根据当前主题,为整个应用设置全局样式表''' global select_styles - logger.info('应用全局样式表') + logger.info('Use style') app = get_application_instance() self.style_changed.emit() @@ -976,22 +1007,30 @@ def apply_global_theme(self): select_styles = styles[current_theme] if self.use_windows_color: + steps = [ + [['.selected:pressed', 'background-color'], lighten_color_hex(self.windows_color, -0.165)] + ] if select_styles.css_data['.meta']['mode'] == 'dark': - select_styles = select_styles.replace(['.selected', 'background-color'], StyleReplaceMode.ALL, lighten_color_hex(self.windows_color, 0.4), output_json=False) - select_styles = select_styles.replace(['.selected:hover', 'background-color'], StyleReplaceMode.ALL, lighten_color_hex(self.windows_color, 0.45), output_json=False) - select_styles = select_styles.replace(['.selected', 'color'], StyleReplaceMode.ALL, 'black', output_json=False) - select_styles = select_styles.replace(['.selected:hover', 'color'], StyleReplaceMode.ALL, 'black', output_json=False) - select_styles = select_styles.replace(['.selected:pressed', 'color'], StyleReplaceMode.ALL, 'black', output_json=False) - select_styles = select_styles.replace(['QCheckBox', 'color'], StyleReplaceMode.ALL, 'black', output_json=False) + 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)], + [['.selected', 'color'], 'black'], + [['.selected:hover', 'color'], 'black'], + [['.selected:pressed', 'color'], 'black'], + [['QCheckBox', 'color'], 'black'], + ]) else: - select_styles = select_styles.replace(['.selected', 'background-color'], StyleReplaceMode.ALL, self.windows_color, output_json=False) - select_styles = select_styles.replace(['.selected:hover', 'background-color'], StyleReplaceMode.ALL, lighten_color_hex(self.windows_color, 0.4), output_json=False) - select_styles = select_styles.replace(['.selected:pressed', 'background-color'], StyleReplaceMode.ALL, lighten_color_hex(self.windows_color, -0.165), output_json=False) + steps.extend([ + [['.selected', 'background-color'], self.windows_color], + [['.selected:hover', 'background-color'], lighten_color_hex(self.windows_color, 0.4)], + ]) + for step in steps: + select_styles = select_styles.replace(step[0], StyleReplaceMode.ALL, step[1], output_json=False) app.setStyleSheet(select_styles.css_text) # 全局应用 self.refresh() -class SettingValue(BaseSettingValue): +class SettingValue(SettingValue): def get(self, value): default_value = default_settings.get(value, None) if isinstance(default_value, str): @@ -1002,8 +1041,6 @@ def get(self, value): class MainWindow(UMainWindow): def __init__(self): - logger.debug('初始化主窗口') - super().__init__() self.setWindowTitle('ClickMouse') self.setGeometry(100, 100, 500, 375) @@ -1013,16 +1050,16 @@ def __init__(self): self.setFixedSize(self.width(), self.height()) # 固定窗口大小 - logger.debug('初始化状态控制变量') + logger.debug('Initializing value') self.show_update_in_start = False # 是否在启动时显示更新提示 self.total_run_time = 0 # 总运行时间 self.is_ready = True # 是否状态栏为“就绪” self.is_start_from_tray = False # 是否从托盘启动 - logger.debug('初始化ui') + logger.debug('Initializing clicker') self.init_ui() - logger.debug('检查更新') + logger.debug('Check updates') self.on_check_update() def init_ui(self): @@ -1058,7 +1095,7 @@ def init_ui(self): self.stop_button.setFixedSize(100, 40) self.stop_button.setEnabled(False) - logger.debug('初始化布局') + logger.debug('Initializing layout') # 单位输入框 unit_layout = UnitInputLayout() @@ -1097,7 +1134,7 @@ def init_ui(self): self.status_bar.showMessage(get_lang('5d')) # 创建布局 - logger.debug('创建按钮布局') + logger.debug('Setting layout') grid_layout.addWidget(self.left_click_button, 0, 0) grid_layout.addWidget(self.right_click_button, 0, 2) grid_layout.addWidget(self.pause_button, 1, 1) @@ -1110,7 +1147,7 @@ def init_ui(self): self.setLayout(central_layout) # 按钮信号连接 - logger.debug('信号连接') + logger.debug('Singnal connection') self.left_click_button.clicked.connect(lambda:clicker.mouse_left(delay_num, time_num)) self.right_click_button.clicked.connect(lambda:clicker.mouse_right(delay_num, time_num)) @@ -1125,13 +1162,12 @@ def init_ui(self): self.status_bar.messageChanged.connect(self.reload_status) # 创建菜单栏 - logger.debug('创建菜单栏') + logger.debug('Creating menu bar') self.create_menu_bar() # 刷新按钮状态 - logger.debug('刷新按钮状态') - logger.debug('初始化完成') + logger.debug('Initializing color successd.') def reload_status(self): '''刷新状态栏''' @@ -1168,7 +1204,7 @@ def create_menu_bar(self): help_menu = menu_bar.addMenu(get_lang('09')) about_action = help_menu.addAction(get_lang('0a')) - # 热键帮助 + # 发送反馈 create_issue_action = help_menu.addAction(get_lang('ba')) # 文档菜单 @@ -1239,6 +1275,8 @@ def open_doc(self, *, path: str=''): doc_choice_lang = 'en' # 默认英文 open_url(f'{setting_value.default_doc_link}/{path}'.format(lang=doc_choice_lang)) + + logger.info(f'{setting_value.default_doc_link}/{path}'.format(lang=doc_choice_lang)) def do_extension(self, index): '''执行扩展''' @@ -1255,22 +1293,22 @@ def do_extension(self, index): except Exception: trace = format_exc() MessageBox.critical(self, get_lang('14'), get_lang('9c').format(trace)) - logger.exception('扩展运行服务', trace) + logger.exception('Extension runner', trace) def show_manage_extension(self): '''管理扩展''' - logger.info('打开扩展管理窗口') + logger.info('Open extension management window') run_software('install_pack.py' ,'install_pack.exe', ['--ipk']) def show_import_extension_mode(self): '''导入扩展模式''' - logger.info('打开导入扩展窗口') + logger.info('Open extension import window') set_import_extension_window.exec() def show_import_extension(self, mode): '''导入扩展''' - logger.info('导入扩展') + logger.info('Import extension') if mode == 1: file_name, _ = QFileDialog.getOpenFileName(self, get_lang('9e'), '', get_lang('9f')) else : @@ -1286,20 +1324,20 @@ def show_import_extension(self, mode): except Exception: trace = format_exc() MessageBox.critical(self, get_lang('a1'), get_lang('a5').format(trace)) - logger.exception('导入扩展', trace) + logger.exception('Import extension', trace) return else: return def show_manage_not_official_extension(self): '''管理第三方扩展''' - logger.info('打开第三方扩展管理窗口') + logger.info('Opening threerd-party extension management window') MessageBox.information(self, get_lang('a1'), get_lang('a4')) def show_import_macro(self): '''导入宏''' - logger.info('导入宏') + logger.info('Import macro') file_name, _ = QFileDialog.getOpenFileName(self, get_lang('9e'), '', get_lang('9f').split(';;')[2]) @@ -1309,7 +1347,7 @@ def show_import_macro(self): MessageBox.information(self, get_lang('a1'), get_lang('a4')) except Exception: trace = format_exc() - logger.exception('导入扩展', trace) + logger.exception('Import macro', trace) MessageBox.critical(self, get_lang('a1'), get_lang('a5').format(trace)) return else: @@ -1317,22 +1355,21 @@ def show_import_macro(self): def show_about(self): '''显示关于窗口''' - logger.info('打开关于窗口') + logger.info('Opening about window') about_window.exec() def show_update_log(self): '''显示更新日志''' - logger.info('打开更新日志窗口') + logger.info('Opening update log') self.open_doc(path=setting_value.update_log_path) def show_clean_cache(self): - '''清理缓存''' - logger.info('打开清理缓存窗口') + logger.info('Opening clean cache window') clean_cache_window.exec() def show_setting(self): '''显示设置窗口''' - logger.info('打开设置窗口') + logger.info('Opening setting window') setting_window.show() def on_check_update(self): @@ -1344,7 +1381,7 @@ def on_check_update(self): self.check_update_thread.finished.connect(self.on_check_update_result) self.check_update_thread.start() else: - logger.info('使用缓存检查更新') + logger.info('Checking update by cache') self.on_check_update_result(update_cache) def save(self): @@ -1355,6 +1392,8 @@ def save(self): def on_check_update_result(self, check_data): '''检查更新结果''' global result + + logger.info('Checking update result') # 判断是否需要缓存 if should_check_update_res: @@ -1372,7 +1411,7 @@ def on_check_update_result(self, check_data): if result[1] != -1: # -1表示函数出错 self.save() if result[0]: # 检查到需要更新 - logger.info('检查到更新') + logger.info('Check update result: need update') # 弹出更新窗口 self.show_update_in_start = True if should_check_update_res: @@ -1380,11 +1419,12 @@ def on_check_update_result(self, check_data): self.on_update() else: if self.check_update_thread.isFinished(): - logger.error(f'检查更新错误:\n{result[0]}') + logger.critical(f'Check update failed:\n{result[0]}') MessageBox.critical(self, get_lang('14'), f'{get_lang('18')}\n{result[0]}') def on_update(self, judge=False): '''显示更新提示''' + logger.info('Showing update window') if judge: if setting_value.update_enabled: if result[0]: # 检查到需要更新 @@ -1422,7 +1462,7 @@ def on_pause(self, paused): def on_stop(self): '''停止连点''' - logger.info('停止连点') + logger.info('Stopping clicker') # 禁用按钮 self.pause_button.setEnabled(False) @@ -1453,7 +1493,7 @@ def on_stop(self): def on_start(self): '''开始连点''' - logger.info('开始连点') + logger.info('Starting clicker') # 禁用按钮 self.input_times.setEnabled(False) @@ -1484,6 +1524,7 @@ def on_click_changed(self, left, right): def on_click_counter(self, totel, now, delay): '''连点计数器''' + logger.debug('Update click counter') self.is_ready = False now = int(now) delay = int(delay) @@ -1512,7 +1553,7 @@ def sync_input(self, get_handle, set_handle, source, dest): class AboutWindow(UDialog): def __init__(self): super().__init__() - logger.debug('初始化关于窗口') + logger.debug('Initizing about window') self.setWindowTitle(filter_hotkey(get_lang('0a'))) self.setGeometry(100, 100, 375, 175) self.setFixedSize(self.width(), self.height()) @@ -1520,14 +1561,11 @@ def __init__(self): def init_ui(self): # 创建面板 - logger.debug('创建面板') + logger.debug('Create panel') central_layout = QGridLayout() - # 面板控件 - logger.debug('创建组件') - # 绘制内容 - logger.debug('绘制内容') + logger.debug('Draw content') self.image_label = QLabel() # 加载图片 @@ -1539,7 +1577,7 @@ def init_ui(self): about = QLabel(get_lang('1d')) # 按钮 - logger.debug('创建按钮') + logger.debug('Create Button') ok_button = QPushButton(get_lang('1e')) set_style(ok_button, StyleClass.selected) @@ -1552,21 +1590,21 @@ def init_ui(self): self.setLayout(central_layout) # 绑定事件 - logger.debug('绑定事件') + logger.debug('Singal connection') ok_button.clicked.connect(self.close) - logger.debug('初始化关于窗口完成') + logger.debug('Initializing about window') class CleanCacheWindow(UDialog): def __init__(self): - logger.debug('初始化清理缓存窗口') + logger.debug('Initizing clean cache window') super().__init__() self.setWindowTitle(filter_hotkey(get_lang('02'))) # 加载常量 - logger.debug('加载常量') + logger.debug('Loading values') self.locked_checkbox = False # 锁定选择框模式,按下后将不会产生来自非手动操作的更新选择框 # 清理缓存 - logger.debug('加载清理项') + logger.debug('Loading cache config') with open(get_resource_path('vars', 'caches.json'), 'r', encoding='utf-8') as f: self.cache_config = json.load(f) self.path_list = [i['path'] for i in self.cache_config if i['path']] @@ -1575,13 +1613,11 @@ def __init__(self): self.init_ui() def init_ui(self): - logger.debug('加载ui') + logger.debug('Loading ui') # 创建面板 layout = QGridLayout() - # 面板控件 - logger.debug('加载ui') - logger.debug('加载列表标题') + logger.debug('Loading title') title = QLabel(get_lang('3d')) set_style(title, StyleClass.big_20) @@ -1590,12 +1626,9 @@ def init_ui(self): set_style(dest, StyleClass.dest) # 布局1 - logger.debug('加载布局-1') layout.addWidget(title, 0, 0, 1, 4) layout.addWidget(dest, 1, 0, 1, 4) - logger.debug('加载动态数据') - # 加载ui file = QLabel(get_lang('33')) path = QLabel(get_lang('34')) @@ -1606,14 +1639,15 @@ def init_ui(self): set_style(path, StyleClass.b) set_style(dest, StyleClass.b) set_style(size, StyleClass.b) + # 布局2 - logger.debug('加载布局-2') layout.addWidget(file, 2, 0) layout.addWidget(path, 2, 1) layout.addWidget(dest, 2, 2) layout.addWidget(size, 2, 3) # 从json读取缓存列表 + logger.info('Loading cache list.') cache_list = {} with open(get_resource_path('vars', 'cleancache.json'), 'r', encoding='utf-8') as f: @@ -1638,6 +1672,8 @@ def init_ui(self): cache_list[get_lang(k[1:])].append(value) else: cache_list[k].append(value) + + logger.info('Load cache list successful.') self.cache_dir_list = {'logs'} # 缓存文件路径的列表 self.cache_file_list = {'update.json'} # 缓存文件列表 @@ -1658,7 +1694,7 @@ def init_ui(self): self.checkbox_list: list[UCheckBox] = [] # 缓存文件选择框的列表 self.cache_path_list: list[QLabel] = [] # 文件路径字符的列表 self.cache_size_list: list[QLabel] = [] # 缓存文件大小字符的列表 - logger.debug('加载动态内容') + logger.debug('Loading cache list') for i, d in enumerate(cache_list.items()): # 遍历缓存列表 k = d[0] v = d[1] @@ -1679,14 +1715,12 @@ def init_ui(self): layout.addWidget(size, line, 3) # 按钮 - logger.debug('创建按钮') scan_cache = QPushButton(get_lang('38')) ok = QPushButton(get_lang('1f')) clean_cache = QPushButton(get_lang('39')) set_style(clean_cache, StyleClass.selected) - # 布局4 - logger.debug('加载布局-4') + # 布局4 bottom_layout = QHBoxLayout() bottom_layout.addStretch(1) bottom_layout.addWidget(scan_cache) @@ -1696,20 +1730,20 @@ def init_ui(self): layout.addLayout(bottom_layout, line + 1, 2) # 绑定事件 + logger.debug('Singal connection') self.all_checkbox.stateChanged.connect(self.on_check) scan_cache.clicked.connect(self.on_scan_cache) - clean_cache.clicked.connect(self.on_clean_cache) + clean_cache.clicked.connect(lambda: self.on_control_cache(clean=True)) ok.clicked.connect(self.close) for checkbox in self.checkbox_list: checkbox.checkStateChanged.connect(self.update_all_check_status) # 设置布局 - logger.debug('设置布局') - + logger.debug('Set layout') self.setLayout(layout) - logger.debug('清理缓存窗口初始化完成') + logger.debug('Initializing clean cache window successful.') def update_all_check_status(self): '''当任何复选框状态变化时自动更新全选按钮状态''' @@ -1727,8 +1761,8 @@ def update_all_check_status(self): def on_scan_cache(self): '''扫描缓存''' - logger.info('扫描缓存') - cache_size = self.calc_cache_size() + logger.info('Scaning cache') + cache_size = self.on_control_cache(clean=False) total_size = 0 for text, cache in zip(self.cache_size_list, cache_size): if cache is not None: @@ -1740,10 +1774,20 @@ def on_scan_cache(self): def try_to_remove_file(self, file_path: str): '''尝试删除文件''' try: - size = self.get_dir_or_file_size(file_path) - os.remove(file_path) + size = 0 + if os.path.isfile(file_path): + size = self.get_dir_or_file_size(file_path) + os.remove(file_path) + elif os.path.isdir(file_path): + for root, dirs, files in os.walk(file_path): + for file in files: + size += self.try_to_remove_file(os.path.join(root, file)) + else: + raise OSError('Not a file or directory') + logger.info(f'Remove {file_path} successful.') return size - except: + except OSError as e: + logger.warning(f'Remove {file_path} failed: {e}.') return 0 def delete_empty_folders(self, root_path): @@ -1759,52 +1803,78 @@ def delete_empty_folders(self, root_path): item_path = os.path.join(root_path, item) if os.path.isdir(item_path): if self.delete_empty_folders(item_path): + logger.debug(f'Add remove empty folder list: {item_path}') deleted_any = True # 检查当前文件夹是否为空 try: + logger.debug(f'Checking {root_path}') items = os.listdir(root_path) except PermissionError: + logger.warning(f'Permission denied when listing {root_path}') return deleted_any # 如果为空则删除 if len(items) == 0: try: + logger.info(f'Remove empty folder {root_path}') os.rmdir(root_path) return True except OSError: + logger.warning(f'Remove empty folder {root_path} failed') pass return deleted_any - def on_clean_cache(self): - '''清理缓存''' - logger.info('清理缓存') + def on_control_cache(self, clean: bool): + '''清理或计算缓存''' + logger.info('Calcuate cache cache') cache_clicked = list(map(lambda x: x.isChecked(), self.checkbox_list)) - cache_size = 0 + if clean: + cache_size = 0 + else: + cache_size = [None for _ in cache_clicked] for i in self.cache_config: if cache_clicked[i['check_index']]: # 选择了该项 if i['path'] is not None: # 是否全选 for items in chain(i['path']['dirs'], i['path']['files']): - cache_size += self.try_to_remove_file(cache_path / items) + if clean: + logger.info(f'Remove: {cache_path / items}') + cache_size += self.try_to_remove_file(cache_path / items) + else: + logger.info(f'Calculate size: {cache_path / items}') + cache_size[i['check_index']] = self.get_dir_or_file_size(cache_path / items) else: + size = 0 for root, dirs, files in os.walk(cache_path): for file in files: if file in i['exclude']['files'] or self.contains_substring(i['exclude']['dirs'], root): + logger.info(f'Exclude: {os.path.join(root, file)}') continue - cache_size += self.try_to_remove_file(os.path.join(root, file)) - # 清理空文件夹 - for root, dirs, files in os.walk(cache_path): - for dir in dirs: - self.delete_empty_folders(os.path.join(root, dir)) - - # 弹出提示窗口 - MessageBox.information(self, get_lang('16'), get_lang('3b').format(get_size_text(cache_size))) + if clean: + cache_size += self.try_to_remove_file(os.path.join(root, file)) + else: + size += self.get_dir_or_file_size(os.path.join(root, file)) + if not clean: + cache_size[i['check_index']] = size + + if clean: + # 清理空文件夹 + for root, dirs, files in os.walk(cache_path): + for dir in dirs: + logger.info(f'Try toemove empty folder: {os.path.join(root, dir)}') + self.delete_empty_folders(os.path.join(root, dir)) + + # 弹出提示窗口 + MessageBox.information(self, get_lang('16'), get_lang('3b').format(get_size_text(cache_size))) + else: + return cache_size def get_dir_or_file_size(self, dir_or_file_path: str) -> int: '''获取目录或文件大小''' + logger.info('Calculate size of directory or file') if os.path.isfile(dir_or_file_path): # 是文件的情况 size = os.path.getsize(dir_or_file_path) @@ -1894,31 +1964,9 @@ def contains_substring(self, str_list, target_str): ''' return any(substring in target_str for substring in str_list) - def calc_cache_size(self) -> list: - '''扫描缓存''' - logger.info('计算缓存大小') - - cache_clicked = list(map(lambda x: x.isChecked(), self.checkbox_list)) - every_cache_size = [None for _ in cache_clicked] - - for i in self.cache_config: - if cache_clicked[i['check_index']]: # 选择了该项 - if i['path'] is not None: # 是否全选 - for items in chain(i['path']['dirs'], i['path']['files']): - every_cache_size[i['check_index']] = self.get_dir_or_file_size(cache_path / items) - else: - size = 0 - for root, dirs, files in os.walk(cache_path): - for file in files: - if file in i['exclude']['files'] or self.contains_substring(i['exclude']['dirs'], root): - continue - size += self.get_dir_or_file_size(os.path.join(root, file)) - every_cache_size[i['check_index']] = size - - return every_cache_size - def on_check(self, state): '''全选按钮点击事件''' + logger.info('All check box updated.') if state == Qt.CheckState.Unchecked: # 未选中 if not self.locked_checkbox: # 非手动操作 for checkbox in self.checkbox_list: @@ -1934,7 +1982,7 @@ def on_check(self, state): class UpdateWindow(UDialog): def __init__(self): # 初始化 - logger.debug('初始化更新窗口') + logger.debug('Initizalizing update window') super().__init__() self.setWindowTitle(get_lang('29')) self.setGeometry(100, 100, 300, 110) @@ -1948,11 +1996,11 @@ def __init__(self): def init_ui(self): # 创建面板 - logger.debug('创建面板') + logger.debug('Create layout') layout = QVBoxLayout() # 面板控件 - logger.debug('创建面板控件') + logger.debug('Create widget') title = QLabel(get_lang('24')) version = QLabel(get_lang('25').format(__version__, result[1])) @@ -1966,13 +2014,13 @@ def init_ui(self): bottom_layout = QHBoxLayout() # 绑定事件 - logger.debug('绑定事件') + logger.debug('Signal connection') update.clicked.connect(self.on_update) update_log.clicked.connect(self.on_open_update_log) cancel.clicked.connect(self.close) # 布局 - logger.debug('布局') + logger.debug('Layout') layout.addWidget(title) layout.addWidget(version) @@ -1985,7 +2033,7 @@ def init_ui(self): self.setLayout(layout) - logger.debug('初始化更新窗口完成') + logger.debug('Initizalizing update window successful.') def exec(self): if setting_value.quiet_update: @@ -2005,7 +2053,7 @@ def on_update(self): self.down_thread.start() except: trace = format_exc() - logger.exception('更新安装', trace) + logger.exception('update failed', trace) revert_update() MessageBox.critical(self, get_lang('14'), f'{get_lang('bb')}:\n{trace}') else: @@ -2021,15 +2069,15 @@ def on_update_finished(self, state): if setting_value.update_ok_notify: update_ok_window.show() else: - logger.exception('更新安装', '文件校验失败') + logger.exception('Update install', 'Update file hash check failed') MessageBox.critical(self, get_lang('14'), get_lang('bc')) else: - logger.exception('更新安装', state[1]) + logger.exception('Update install', state[1]) MessageBox.critical(self, get_lang('14'), f'{get_lang('bb')}:\n{state[1]}') def on_open_update_log(self): # 打开更新日志 - logger.debug('打开更新日志') + logger.debug('Open update log') version = result[1] version = version.replace('.', '').replace('beta', 'b').replace('alpha', 'a') @@ -2052,11 +2100,11 @@ def __init__(self): def init_ui(self): # 创建面板 - logger.debug('创建面板') + logger.debug('Create layout') layout = QVBoxLayout() # 面板控件 - logger.debug('创建面板控件') + logger.debug('Create widget') title = QLabel(get_lang('b3')) tip = QLabel(get_lang('b8')) @@ -2071,14 +2119,14 @@ def init_ui(self): bottom_layout = QHBoxLayout() # 绑定事件 - logger.debug('绑定事件') + logger.debug('Signal connection') update.clicked.connect(self.on_update) revert.clicked.connect(self.on_revert) update_log.clicked.connect(self.on_open_update_log) cancel.clicked.connect(self.close) # 布局 - logger.debug('布局') + logger.debug('Layout') layout.addWidget(title) layout.addWidget(tip) @@ -2092,7 +2140,7 @@ def init_ui(self): self.setLayout(layout) - logger.debug('初始化更新窗口完成') + logger.debug('Initizalizing update window successful.') def on_update(self): '''更新''' @@ -2102,7 +2150,7 @@ def on_update(self): def on_revert(self): '''回滚''' global can_update - logger.info('回滚更新') + logger.info('Revert update') revert_update() self.close() can_update = False @@ -2114,7 +2162,7 @@ def on_open_update_log(self): class FastSetClickWindow(UDialog): def __init__(self): - logger.debug('初始化快速连点窗口') + logger.debug('Initizalizing fast set click window') super().__init__() self.setWindowTitle(get_lang('75')) @@ -2125,10 +2173,10 @@ def __init__(self): self.setFixedSize(self.width(), self.height()) # 固定窗口大小 - logger.debug('初始化状态控制变量') + logger.debug('Initizalizing value') self.total_run_time = 0 # 总运行时间 - logger.debug('初始化ui') + logger.debug('Initizalizing ui') self.init_ui() def init_ui(self): @@ -2164,14 +2212,14 @@ def init_ui(self): set_style(self.total_time_label, StyleClass.big_16) # 创建布局 - logger.debug('创建按钮布局') + logger.debug('Create button layout') central_layout.addLayout(unit_layout) central_layout.addWidget(self.total_time_label) self.setLayout(central_layout) # 按钮信号连接 - logger.debug('信号连接') + logger.debug('Signal connection') # 双向同步 # 主窗口同步 @@ -2187,7 +2235,7 @@ def init_ui(self): 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('初始化快速连点窗口完成') + logger.debug('Initizalizing fast set click window successful.') def sync_input(self, get_handle, set_handle, source, dest): '''同步输入框''' @@ -2195,7 +2243,7 @@ def sync_input(self, get_handle, set_handle, source, dest): class ClickAttrWindow(UDialog): def __init__(self): - logger.debug('初始化连点器属性窗口') + logger.debug('Initizalizing click attribute window') super().__init__() self.setWindowTitle(get_lang('8c')) @@ -2240,10 +2288,11 @@ def init_ui(self): self.setLayout(central_layout) - logger.debug('初始化连点器属性窗口完成') + logger.debug('Initizalizing click attribute window successful.') def update_attr(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)}') @@ -2266,7 +2315,7 @@ class SettingWindow(SelectUI): def __init__(self, values:dict | None = None): super().__init__() - logger.debug('初始化设置窗口') + logger.debug('Initizalizing setting window') self.setGeometry(300, 300, 625, 400) self.setFixedSize(self.width(), self.height()) self.setWindowTitle(filter_hotkey(get_lang('04'))) @@ -2294,7 +2343,7 @@ def __init__(self, values:dict | None = None): # 连接信号 clicker.started.connect(self.on_clicker_started) - logger.debug('初始化设置窗口完成') + logger.debug('Initizalizing setting window successful.') def create_setting_page_value(self): self.page_general = self.page_choice_buttons[0] # 默认设置 @@ -2321,16 +2370,16 @@ def check_values(self): self.values.clear() def create_setting_page(self, title): - logger.debug(f'创建设置页面: {title}') + logger.info(f'Loading setting page: {title}') page = QWidget() layout = QVBoxLayout(page) def set_content_label(text): - logger.debug(f'设置内容标签') + logger.debug(f'Set content label: {text}') content_label.setText(text) def create_horizontal_line(): - logger.debug(f'创建水平线') + logger.debug('Create horizontal line') line = UFrame() line.setFrameShape(UFrame.Shape.HLine) # 水平线 return line @@ -2980,6 +3029,7 @@ def on_enable_update(self, state): self.notice_update_ok_notify.setEnabled(state) def repair_auto_start(self): + logger.info('Repair auto start') os.remove(Path(os.environ['APPDATA'], 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', 'Clickmouse.lnk')) auto_start_manager.create_reg() MessageBox.information(self, get_lang('16'), get_lang('d2')) @@ -3002,6 +3052,7 @@ def repair_settings(self, key: str): self.window_restarted.emit() 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: # 不确认重置 return @@ -3042,6 +3093,7 @@ def restart_window(self): def on_setting_changed(self, handle, key, *args): '''更新检查提示选择事件''' + logger.info(f'Setting changed: {key}') settings[key] = handle(*args) save_settings() @@ -3087,7 +3139,7 @@ def on_clicker_started(self): class SetImportExtensionModeWindow(UDialog): def __init__(self): super().__init__() - logger.debug('初始化管理扩展提示窗口') + logger.debug('Initializing import extension mode window') self.setWindowTitle(filter_hotkey(get_lang('92'))) self.setGeometry(100, 100, 200, 125) self.setWindowIcon(icon) @@ -3120,7 +3172,7 @@ def init_ui(self): # 连接信号 mode_button.clicked.connect(self.on_mode_button_clicked) - logger.debug('管理扩展提示窗口初始化完成') + logger.debug('Init import extension mode window finished') def on_mode_button_clicked(self): self.close() @@ -3128,7 +3180,7 @@ def on_mode_button_clicked(self): class TrayApp: def __init__(self): - logger.info('加载托盘程序') + logger.info('Loading tray app framework') self.app = get_application_instance() show_tray_icon = setting_value.show_tray_icon @@ -3158,11 +3210,12 @@ def __init__(self): clicker.started.connect(self.on_start) clicker.started.connect(main_window.on_start) - logger.info('托盘程序框架加载完成') + logger.info('Initializing tray app finished') + logger.info('Start finished.') def setup_tray_icon(self): '''设置系统托盘图标''' - logger.info('设置系统托盘图标') + logger.info('Setting up tray icon') self.tray_icon = QSystemTrayIcon() self.tray_icon.setIcon(icon) @@ -3179,7 +3232,7 @@ def setup_tray_icon(self): self.tray_icon.show() def create_menu(self): - logger.info('创建右键菜单') + logger.info('Creatting tray icon menu') tray_menu = QMenu() # 添加'打开应用'菜单项 @@ -3225,7 +3278,7 @@ def create_menu(self): def start_hotkey_listener(self): '''启动热键监听器''' - logger.info('启动热键监听器') + logger.info('Starting hotkey listener') # 在后台线程中启动热键监听 self.hotkey_thread = QtThread(self.hotkey_listener.start_listening) self.hotkey_thread.start() @@ -3242,7 +3295,7 @@ def check_delay(self, input_delay): except Exception: trace = format_exc() MessageBox.critical(main_window, get_lang('13'), f'{get_lang('ae')}\n{trace}') - logger.exception('延迟处理', trace) + logger.exception('Delay control', trace) return False return True @@ -3254,15 +3307,15 @@ def quit_application(self): def run(self): '''运行应用程序''' - logger.info('运行托盘程序') + logger.info('Running tray app') code = self.app.exec() 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() - logger.info('主程序退出') sys.exit(code) def refresh(self): @@ -3270,10 +3323,12 @@ def refresh(self): def quit(self, code=lambda: None): if update_window.down_thread is not None: + logger.info('Waiting for update thread to exit') if update_window.down_thread.isRunning(): update_window.down_thread.quit() update_window.down_thread.wait(1000) # 等待一段时间 if update_window.down_thread.isRunning(): # 仍然运行,强制退出 + logger.warning('Update thread is still running, force quit') update_window.down_thread.terminate() # 作为最后手段 update_window.down_thread.wait() revert_update() @@ -3291,22 +3346,22 @@ def run_combination(self, combination): def on_start_clicker_tray(self, direction): '''启动托盘连点''' if direction == 'left': # 左键 - warn_text = '左' + warn_text = 'left' button = main_window.left_click_button start_lang_id = '6f' func = clicker.mouse_left elif direction == 'right': # 右键 - warn_text = '右' + warn_text = 'right' button = main_window.right_click_button start_lang_id = '70' func = clicker.mouse_right else: - logger.critical('未知的方向,代码退出') + logger.critical('Invalid direction') return # 判断参数有效性 if not button.isEnabled(): - logger.warning(f'{warn_text}键未启用') + logger.warning(f'{warn_text} click is not enabled.') self.tray_icon.showMessage(get_lang('14'), get_lang('1a'), QSystemTrayIcon.MessageIcon.Critical, 1000) return @@ -3372,10 +3427,6 @@ def on_start(self): self.tray_icon.showMessage(get_lang('14'), get_lang('af'), QSystemTrayIcon.MessageIcon.Critical, 1000) if __name__ == '__main__': - from sharelibs import (mem_id, get_resource_path, run_as_admin, get_lang) # 共享库 - import json # 用于读取json文件 - from pathlib import Path # 路径库 - shared_memory = QSharedMemory(mem_id[0]) if shared_memory.attach(): # 已经有一个实例在运行 @@ -3395,12 +3446,6 @@ def on_start(self): run_as_admin('install_pack.py', 'install_pack.exe') sys.exit(0) else: - import os # 系统库 - import shutil # 用于删除文件夹 - from logger import Logger - - logger = Logger('主程序日志') - with open(get_resource_path('package_info.json')) as f: packages_info = json.load(f) try: @@ -3412,7 +3457,7 @@ def on_start(self): try: packages.append(import_package(i)) except ValueError as e: - logger.warning(f'扩展{i}已过期,自动清除') + logger.warning(f'Extension {i} is deprecated. Auto delete.') shutil.rmtree(f'extensions/{i}', ignore_errors=True) del packages_name[packages_name.index(i)] for file in os.listdir('extensions'): @@ -3421,10 +3466,10 @@ def on_start(self): if os.path.isfile(full_path): if file != 'packages.json': os.remove(full_path) - logger.warning(f'错误文件{file}自动清除') + logger.warning(f'Invalid extension {file} found. Auto delete.') elif os.path.isdir(full_path): if file not in packages_name: - logger.warning(f'扩展{file}已过期,自动清除') + logger.warning(f'Extension {file} is deprecated. Auto delete.') shutil.rmtree(full_path, ignore_errors=True) if (os.path.exists('packages.json')) and (os.path.exists('extensions/packages.json')): os.remove('extensions/packages.json') @@ -3441,44 +3486,20 @@ def on_start(self): shutil.rmtree('extensions') pass - # 加载框架 - 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) # 软件界面样式 - from uiStyles import indexes as style_indexes # 界面组件样式索引 - from sharelibs import (run_software, langs, create_shortcut, __version__, is_pre, get_icon, default_button_text, - get_unit_value, unit_lang, get_size_text, get_file_hash, system_lang, settings, QtThread, default_settings) # 共享库 - import parse_dev # 解析开发固件配置 - import winreg # 注册表库 - import math # 数学库 - import colorsys # 颜色库 - import struct # 字节处理库 - import pytz # 时区库 - from traceback import format_exc # 异常格式化 - from itertools import chain # 迭代器库 - import platform # 系统信息 - from txtinfo import * - - # 系统api - import ctypes - from ctypes import wintypes - - logger.info('加载变量') - logger.debug('定义常量') + logger.info('Loading value') + logger.debug('Loading const value') has_packages = os.path.exists(get_resource_path('packages')) package_names, show_list, package_ids = get_packages() # Windows API常量 - logger.debug('定义Windows API常量') + logger.debug('sSetting WinAPI const value') DWMWA_USE_IMMERSIVE = 20 DWMWA_USE_IMMERSIVE_DARK_MODE = 20 DWM_WINDOW_CORNER_PREFERENCE = 33 DWMWCP_ROUND = 2 DWMNCRP_ENABLED = 1 - logger.info('加载服务程序') + logger.info('Loading services') setting_value = SettingValue() clicker = Click() auto_start_manager = StartManager() @@ -3486,7 +3507,7 @@ def on_start(self): run_after = RunAfter() # 变量 - logger.info('定义数据路径') + logger.info('Define pathes') # 定义数据路径 cache_path = Path('cache') @@ -3527,7 +3548,6 @@ def on_start(self): with open(data_path / 'dev_flags.json', 'w', encoding='utf-8') as f: json.dump(dev_flags, f) - # 单位控制 latest_index = 2 select_lang = setting_value.select_lang @@ -3550,9 +3570,9 @@ def on_start(self): else: # 未知 default_theme = 'Fusion' - logger.info('定义资源完成') + logger.info('Loading data successed') - logger.info('检查更新注册表') + logger.info('Check and update registry version.') # 检查版本号与注册表是否一致,不一样就修改注册表 run_software('check_reg_ver.py', 'check_reg_ver.exe') @@ -3561,7 +3581,7 @@ def on_start(self): shutil.rmtree('updater.old', ignore_errors=True) # 加载窗口 - logger.info('加载ui') + logger.info('Loading ui') main_window = MainWindow() on_input_change(type=InputChange.main_window) # 更新时间估计状态 diff --git a/Gui/repair.py b/Gui/repair.py index ee05487..51cc88e 100644 --- a/Gui/repair.py +++ b/Gui/repair.py @@ -2,6 +2,12 @@ from PySide6.QtWidgets import QApplication app = QApplication(sys.argv) from uiStyles.QUI import * +from sharelibs import mem_id +import json +from shutil import rmtree +import os +import winreg +from sharelibs import get_resource_path, get_icon def remove_file(file_path): try: @@ -164,8 +170,6 @@ def on_repair(self): sys.exit(0) if __name__ == '__main__': - from sharelibs import mem_id - shared_memory = QSharedMemory(mem_id[3]) if shared_memory.attach(): # 已经有一个实例在运行 @@ -179,18 +183,12 @@ def on_repair(self): QMessageBox.critical(None, get_control_lang('04'), get_control_lang('08')) sys.exit(2) - import json - with open('packages.json', 'r', encoding='utf-8') as f: packages = json.load(f) if 'xystudio.clickmouse.repair' in packages: from sharelibs import is_admin if is_admin(): - from shutil import rmtree - import os - import winreg - from sharelibs import get_resource_path, get_icon window = MainWindow() window.show() else: diff --git a/Gui/res/langs/langs.json b/Gui/res/langs/langs.json index 3b02723..d608bea 100644 --- a/Gui/res/langs/langs.json +++ b/Gui/res/langs/langs.json @@ -102,7 +102,7 @@ "64": "Total {0} clicks, {1} clicks completed, {2} clicks remaining; Total {3} seconds spent, {4} seconds spent, {5} seconds remaining, click interval {6}", "65": "preview", "66": "times", - "67": "This clickmouse is not an official version", + "67": null, "68": "Open Application", "69": "Hotkey", "6a": "Revert", @@ -317,7 +317,7 @@ "64": "共{0}次连点,已完成{1}次,剩余{2}次;共花费{3},已花费{4},剩余{5},连点间隔{6}", "65": "预览版", "66": "次", - "67": "此clickmouse不是官方版本", + "67": null, "68": "打开应用", "69": "热键", "6a": "回滚", diff --git a/Gui/res/rules/dev_data.json b/Gui/res/rules/dev_data.json deleted file mode 100644 index 805674a..0000000 --- a/Gui/res/rules/dev_data.json +++ /dev/null @@ -1 +0,0 @@ -{"$schema": "https://json-schema.org/draft-07/schema#", "type": "array", "items": {"type": "object", "properties": {"config_in_data": {"type": "string", "minLength": 1}, "cases": {"type": "object", "properties": {}, "minProperties": 1}}, "required": ["config_in_data", "cases"]}, "minItems": 1} \ No newline at end of file diff --git a/Gui/res/rules/vars.json b/Gui/res/rules/vars.json deleted file mode 100644 index cc66dc1..0000000 --- a/Gui/res/rules/vars.json +++ /dev/null @@ -1 +0,0 @@ -{"$schema": "https://json-schema.org/draft-07/schema#", "type": "object"} diff --git a/Gui/res/update.json b/Gui/res/update.json index c00bf74..1375296 100644 --- a/Gui/res/update.json +++ b/Gui/res/update.json @@ -2,7 +2,7 @@ { "website_name": "github", "down_web": "https://github.com/xystudiocode/pyclickmouse/releases/download/{latest_version}/clickmouse.7z", - "open_web": "https://github.com/xystudio889/pyclickmouse/releases/latest", + "open_web": "https://github.com/xystudiocode/pyClickMouse/releases/latest", "api_web": "https://api.github.com/repos/xystudiocode/pyclickmouse/releases", "header": {"Authorization": "token {0}"}, "addtional_info": {"verify": false}, diff --git a/Gui/res/versions.json b/Gui/res/versions.json index 1be508a..fb6be45 100644 --- a/Gui/res/versions.json +++ b/Gui/res/versions.json @@ -1 +1 @@ -{"package_format_version": "1.2.0", "clickmouse":"3.2.1.20alpha1"} +{"package_format_version": "1.2.0", "clickmouse":"3.2.1.20"} diff --git a/Gui/sharelibs.py b/Gui/sharelibs.py index f023f02..8356cc7 100644 --- a/Gui/sharelibs.py +++ b/Gui/sharelibs.py @@ -12,6 +12,7 @@ import ctypes import win32com.client import hashlib +import re setting_path = Path('data', 'settings.json') setting_path.parent.mkdir(parents=True, exist_ok=True) @@ -24,6 +25,14 @@ def _show_message(message, title, status): elif status == 2: QMessageBox.critical(None, title, message) +def multi_replace(text, replace_dict): + '''一次性替换多个子串''' + # 将字典键按长度降序排序,避免长词被短词部分覆盖 + sorted_keys = sorted(replace_dict.keys(), key=len, reverse=True) + # 构建正则模式,注意转义特殊字符 + pattern = '|'.join(re.escape(key) for key in sorted_keys) + return re.sub(pattern, lambda m: replace_dict[m.group(0)], text) + def get_resource_path(*paths): ''' 获取资源文件路径 diff --git a/Gui/txtinfo.py b/Gui/txtinfo.py index b2a30f1..19825be 100644 --- a/Gui/txtinfo.py +++ b/Gui/txtinfo.py @@ -1,6 +1,6 @@ from sharelibs import default_settings, settings -__all__ = ['SettingText', 'StyleClass', 'InputChange'] +__all__ = ['SettingText', 'StyleClass', 'InputChange', 'SettingValue'] class SettingText: select_lang = 'select_lang' diff --git a/Gui/uninstall.py b/Gui/uninstall.py index 0edce69..9614503 100644 --- a/Gui/uninstall.py +++ b/Gui/uninstall.py @@ -1,3 +1,13 @@ +from PySide6.QtCore import QSharedMemory +import sys +from sharelibs import mem_id, is_admin +from PySide6.QtWidgets import QApplication, QMessageBox +from sharelibs import get_control_lang +import os +import shutil +import winreg +from pathlib import Path + def remove_reg_key(sub_key): root_key = winreg.HKEY_LOCAL_MACHINE try: @@ -56,18 +66,11 @@ def main(): sys.exit(0) if __name__ == '__main__': - from PySide6.QtCore import QSharedMemory - import sys - from sharelibs import mem_id - shared_memory = QSharedMemory(mem_id[4]) if shared_memory.attach(): # 已经有一个实例在运行 sys.exit(2) shared_memory.create(1) - - from PySide6.QtWidgets import QApplication, QMessageBox - from sharelibs import get_control_lang app = QApplication(sys.argv) is_running = any(list(map(lambda x: QSharedMemory(x).attach(), [i for i in mem_id if i != mem_id[4]]))) @@ -75,15 +78,9 @@ def main(): # 已经有一个实例在运行 QMessageBox.critical(None, get_control_lang('04'), get_control_lang('08')) sys.exit(2) - - from sharelibs import is_admin if is_admin(): if QMessageBox.question(None, get_control_lang('04'), get_control_lang('06'), QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: - import os - import shutil - import winreg - from pathlib import Path main() else: sys.exit(3) diff --git a/Gui/updater.py b/Gui/updater.py index d744cd4..ee4042b 100644 --- a/Gui/updater.py +++ b/Gui/updater.py @@ -1,3 +1,9 @@ +import os +from pathlib import Path +import subprocess +import shutil +import sys + def try_to_delete(path): if os.path.isdir(path): shutil.rmtree(path, ignore_errors=True) @@ -102,12 +108,6 @@ def move_contents_to_parent(folder_path): return if __name__ == '__main__': - import os - from pathlib import Path - import subprocess - import shutil - import sys - clickmouse_path = Path.cwd() shutil.move(clickmouse_path / 'packages.json', clickmouse_path / 'extensions') extract_7z('updater.old/clickmouse.7z') diff --git a/README.md b/README.md index 05a2449..f259df6 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ license - + commit