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] =?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