diff --git a/.github/ISSUE_TEMPLATE/SPA-en.yml b/.github/ISSUE_TEMPLATE/SPA-en.yml new file mode 100644 index 00000000..9e6bda85 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/SPA-en.yml @@ -0,0 +1,53 @@ +name: 🗒️New Standard +description: Propose, establish, or modify a standard. +title: "🗒️[SPA]" +labels: enhancement +type: Feature +body: + - type: markdown + attributes: + value: | + > [!NOTE] + > To avoid unnecessary trouble, please check if anyone else has already reported the same proposal before filing an issue. [Check for duplicates](https://github.com/xystudiocode/pyClickMouse/issues) 😊 + + > [!IMPORTANT] + > We do not handle issues on Gitee, please use GitHub. 🙋♂️ + + > [!TIP] + > Please report only one proposal at a time. 😀 + - type: textarea + attributes: + label: 🧾New proposal + placeholder: | + Please enter the proposal content here. + description: 🧾Please describe in detail the standard you want to propose. + validations: + required: true + - type: input + attributes: + label: ℹ️Clickmouse version for the proposal + placeholder: | + >=X.X.X or >=X.X.X.X + description: 🔠Provide a three‑ or four‑part version number. + value: | + next official Clickmouse release + validations: + required: true + - type: textarea + attributes: + label: ❓Reason for this proposal + placeholder: | + What problem does it solve? + What does it improve? + Other reasons... + description: ❓Why is this proposal needed? + validations: + required: false + - type: textarea + attributes: + label: ➕Additional information + description: ✉️Anything else you know. + placeholder: | + More information. + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug-en.yml b/.github/ISSUE_TEMPLATE/bug-en.yml new file mode 100644 index 00000000..4c7924b3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-en.yml @@ -0,0 +1,163 @@ +name: "🐛Bug" +title: "🐛[BUG]" +description: Report a bug. +labels: + - bug +type: Bug +body: +- type: markdown + attributes: + value: | + > [!NOTE] + > To avoid unnecessary trouble, please check if anyone else has already reported the same issue before filing an issue. [Check for duplicates](https://github.com/xystudiocode/pyClickMouse/issues) 😊 + + > [!IMPORTANT] + > We do not handle issues on Gitee, please use GitHub. 🙋♂️ + + > [!TIP] + > Please report only one issue at a time. 😀 +- type: input + attributes: + label: 🔡Clickmouse version + placeholder: X.X.X or X.X.X.X + description: Provide a three‑ or four‑part version number. You can find the current Clickmouse version in "Help" → "About". + validations: + required: true +- type: dropdown + attributes: + label: 🎭Did you find the bug in an official version? + description: ❗Official Clickmouse versions are only published on [GitHub Releases](https://github.com/xystudiocode/pyClickMouse/releases) or [Gitee Releases](https://gitee.com/xystudio889/pyClickMouse/releases). Any other sources are unofficial. + multiple: false + options: + - ✅Yes + - ❌No + - ❓I don't know / forgot + validations: + required: true +- id: modules + type: dropdown + attributes: + label: 🐛Module where the bug occurred + description: 🐛Select the module(s) where you experienced the bug. Multiple selections allowed. + multiple: true + options: + - 🖱️Clicking feature + - 🧮Real‑time calculation and evaluation of click delay (including main window, settings, and quick click) + - ⚙️Settings + - ✳️Adaptive theme switching + - ⌨️Hotkeys + - ️⬜System tray application + - ☁️Other main program features + - ⬇️Initialization program + - 📦Package manager (including the package manager during initialization) + - 🗑️Uninstaller + - 🔄️Update check service + - 🔧Repair tool + - ⤴️Update installation + - 💠Other extensions + - ♻️Cache cleaning + - 🏳️🌈Theme / styling + - 📄Documentation site + - 🟦clickClean + - ❓Other / unknown + validations: + required: true +- type: textarea + attributes: + label: 🐍Bug description + placeholder: | + Please describe it here. + description: 🐍Describe your bug. + validations: + required: true +- type: textarea + attributes: + label: 🔦Impact of this bug + placeholder: | + Please describe it here. + description: 🔦Describe the impact of this bug. + validations: + required: false +- type: textarea + attributes: + label: 📝Steps to reproduce + placeholder: | + 1. Step 1 + 2. Step 2 + 3. Step 3 + 4. Step... + description: 🔧Describe how to reproduce this bug. + validations: + required: false +- type: input + attributes: + label: ℹ️Estimated affected Clickmouse version(s) + placeholder: | + >=X.X.X or >=X.X.X.X + description: 🔠Provide a three‑ or four‑part version number with “>=”. + validations: + required: false +- type: textarea + attributes: + label: 📄Your ideas for fixing the bug + placeholder: | + - Solution 1: + - 1. Step 1 + - 2. Step 2 + - 3. Step 3 + - 4. Step... + - Solution 2: + - 1. Step 1 + - 2. Step 2 + - 3. Step 3 + - 4. Step... + - Solution 3: + - 1. Step 1 + - 2. Step 2 + - 3. Step 3 + - 4. Step... + - Other solutions... + description: ♻️You can provide multiple solutions, but each must be able to resolve your issue. + validations: + required: false +- type: textarea + attributes: + label: 🔄️Workarounds for the bug + placeholder: | + - Workaround 1: + - 1. Step 1 + - 2. Step 2 + - 3. Step 3 + - 4. Step... + - Workaround 2: + - 1. Step 1 + - 2. Step 2 + - 3. Step 3 + - 4. Step... + - Workaround 3: + - 1. Step 1 + - 2. Step 2 + - 3. Step 3 + - 4. Step... + - Other workarounds... + description: 🔄️You can provide multiple workarounds, but each must be able to mitigate your issue. + validations: + required: false +- type: textarea + attributes: + label: 📂Log file + placeholder: | + Paste the last section of the log file here... + description: | + ✉️The log is located at `clickmouse installation directory/cache/logs/ today's date.log`. + **Log sections are separated by `---`. Please copy the last section of the log here**. + validations: + required: false +- type: textarea + attributes: + label: ➕Additional information + placeholder: | + Your operating system version, system language, software that caused the bug, etc. + description: ✉️Provide more information, such as operating system version, system language, software that caused the bug, etc. + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature-en.yml b/.github/ISSUE_TEMPLATE/feature-en.yml new file mode 100644 index 00000000..fde6ac0f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-en.yml @@ -0,0 +1,93 @@ +name: ❇️New Feature +description: A feature you would like to suggest. +title: "❇️[FEATURE]" +labels: enhancement +type: Feature +body: +- type: markdown + attributes: + value: | + > [!NOTE] + > To avoid unnecessary trouble, please check if anyone else has already reported the same issue before filing an issue. [Check for duplicates](https://github.com/xystudiocode/pyClickMouse/issues) 😊 + + > [!IMPORTANT] + > We do not handle issues on Gitee, please use GitHub. 🙋♂️ + + > [!TIP] + > Please report only one feature at a time. 😀 +- type: dropdown + attributes: + label: ❇️Module for the new feature + description: ❇️Which module does the new feature belong to? + multiple: true + options: + - 🖱️Clicking feature + - 🧮Real‑time calculation and evaluation of click delay (including main window, settings, and quick click) + - ⚙️Settings + - ✳️Adaptive theme switching + - ⌨️Hotkeys + - ️⬜System tray application + - ☁️Other main program features + - ⬇️Initialization program + - 📦Package manager (including the package manager during initialization) + - 🗑️Uninstaller + - 🔄️Update check service + - 🔧Repair tool + - ⤴️Update installation + - 💠Other extensions + - ♻️Cache cleaning + - 📄Documentation site + - 🏳️🌈Theme / styling + - 🟦clickClean + - ❓Other / unknown + validations: + required: true +- type: textarea + attributes: + label: 📄New feature description + placeholder: | + Describe in detail the feature you want to add. + description: 📄Please describe in detail the feature you want to add. + validations: + required: true +- type: textarea + attributes: + label: ❔Reason for this feature + placeholder: | + What problem does it solve? + What does it improve? + Other reasons... + description: ❔Why is this feature needed? + validations: + required: false +- type: textarea + attributes: + label: 🧾Implementation steps ideas + placeholder: | + - Plan 1: + - 1. Step 1 + - 2. Step 2 + - 3. Step 3 + - 4. Step... + - Plan 2: + - 1. Step 1 + - 2. Step 2 + - 3. Step 3 + - 4. Step... + - Plan 3: + - 1. Step 1 + - 2. Step 2 + - 3. Step 3 + - 4. Step... + - Other plans... + description: ♻️You can provide multiple implementation plans, but each must be able to fulfill your requirement. + validations: + required: false +- type: textarea + attributes: + label: ➕Additional information + description: 📄Anything else you know. + placeholder: | + More information. + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/tasks-en.yml b/.github/ISSUE_TEMPLATE/tasks-en.yml new file mode 100644 index 00000000..f42f6e8a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tasks-en.yml @@ -0,0 +1,129 @@ +name: ☑️Task List +description: Task lists, e.g., for drafting a new release plan. +title: "☑️[TASK]" +labels: enhancement +type: Task +body: +- type: markdown + attributes: + value: | + > [!NOTE] + > To avoid unnecessary trouble, please check if anyone else has already reported the same issue before filing an issue. [Check for duplicates](https://github.com/xystudiocode/pyClickMouse/issues) 😊 + + > [!IMPORTANT] + > We do not handle issues on Gitee, please use GitHub. 🙋♂️ +- type: dropdown + attributes: + label: ☑️Modules covered by the new task + description: ☑️Which modules should the new task cover? + multiple: true + options: + - 🖱️Clicking feature + - 🧮Real‑time calculation and evaluation of click delay (including main window, settings, and quick click) + - ⚙️Settings + - ✳️Adaptive theme switching + - ⌨️Hotkeys + - ️⬜System tray application + - ☁️Other main program features + - ⬇️Initialization program + - 📦Package manager (including the package manager during initialization) + - 🗑️Uninstaller + - 🔄️Update check service + - 🔧Repair tool + - ⤴️Update installation + - 💠Other extensions + - ♻️Cache cleaning + - 📄Documentation site + - 🏳️🌈Theme / styling + - 🟦clickClean + - ❓Other / unknown + validations: + required: true +- type: textarea + attributes: + label: 🧾Description of each new feature + placeholder: | + - New feature 1 + - New feature 2 + - New feature 3 + - New feature... + description: 🧾Please describe in detail the features you want to add. + validations: + required: true +- type: textarea + attributes: + label: ❓Reason for these features + placeholder: | + What problem does it solve? + What does it improve? + Other reasons... + description: ❓Why are these features needed? + validations: + required: false +- type: textarea + attributes: + label: 📄Ideas for implementation steps + placeholder: | + - New feature 1: + - - Plan 1: + - - 1. Step 1 + - - 2. Step 2 + - - 3. Step 3 + - - 4. Step... + - - Plan 2: + - - 1. Step 1 + - - 2. Step 2 + - - 3. Step 3 + - - 4. Step... + - - Plan 3: + - - 1. Step 1 + - - 2. Step 2 + - - 3. Step 3 + - - 4. Step... + - - Other plans... + - New feature 2: + - - Plan 1: + - - 1. Step 1 + - - 2. Step 2 + - - 3. Step 3 + - - 4. Step... + - - Plan 2: + - - 1. Step 1 + - - 2. Step 2 + - - 3. Step 3 + - - 4. Step... + - - Plan 3: + - - 1. Step 1 + - - 2. Step 2 + - - 3. Step 3 + - - 4. Step... + - - Other plans... + - New feature 3: + - - Plan 1: + - - 1. Step 1 + - - 2. Step 2 + - - 3. Step 3 + - - 4. Step... + - - Plan 2: + - - 1. Step 1 + - - 2. Step 2 + - - 3. Step 3 + - - 4. Step... + - - Plan 3: + - - 1. Step 1 + - - 2. Step 2 + - - 3. Step 3 + - - 4. Step... + - - Other plans... + - Other features... + description: ♻️You can provide multiple implementation plans for multiple new features, but each plan must be able to fulfill your requirement. + validations: + required: false +- type: textarea + attributes: + label: ➕Additional information + description: ✉️Anything else you know + placeholder: | + More information. + validations: + required: false \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..ca37ba6c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" \ No newline at end of file diff --git a/CONTRIBUTING-zh_CN.md b/CONTRIBUTING-zh_CN.md new file mode 100644 index 00000000..3e0bcd96 --- /dev/null +++ b/CONTRIBUTING-zh_CN.md @@ -0,0 +1,159 @@ +# ❓怎么贡献? +
+ +## ⬆️commit message + +需要遵循以下格式: +``` +更新类型(更新模块):(可选)(版本号) (更新概括): + +- [(可选)子更新类型]更新内容1 +- [(可选)子更新类型]更新内容2 +- ... +``` + +更新类型有这些内容: +| 类型 | 说明 | +| --- | --- | +| ✅feat | 新功能 | +| 🔧modify | 修改功能 | +| 🐛fix | 修bug | +| ⚒️refactor | 重构 | +| 📃docs | 改文档,比如README | +| ❇️style | 改代码风格,不影响功能 | +| 🔎test | 加测试、改测试 | +| 📆chore | 杂项,比如改.gitignore | +| ⏫perf | 性能优化 | +| 🛒ci | CI/CD相关改动 | +| 🚅build | 改构建系统或依赖 | +| ◀️revert | 回滚 | +| 🔡dependency | 依赖更新 | +| ❌remove | 删除弃用的组件 | +| ↪️move | 移动了组件 | +| ❓unknown | 未知类型 | +| 自定义 | 尽量以一个直观的英文单词描述,最好配上emoji | + +内容较多时需要对更新内容添加更新类型提示 + +## 🗂️分支 + +请按此图所示的分支结构来更新: +
+
+创建的分支需要以`feature/`开头,以表示功能分支,或创建一个fork,并在fork的分支开发。
+
+发布pr时不限定合并分支,只要不是`main`分支都可以。
+
+## 🔠版本号
+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很多,位于版本项目初期。这阶段新增的功能将会被放到实验室中,并默认关闭。
+
+`alpha`代表晚期开发更新,功能不完善,bug较多,位于版本项目早期。这阶段新增的功能将会被放到实验室中,并默认关闭。
+
+`beta`代表发布测试更新,功能完善,bug较少,不会再新增功能,位于版本项目中期,并且会逐步合并实验室中的feature。
+
+`rc`代表预备发布版本,功能完善,bug较少,会修复一些重要安全问题或bug,最接近正式版,即将发布正式版,位于版本项目末期。
+
+## ❓issue
+- 标题格式:`[类型] 标题`
+- 内容应准确写出你的需求,并选择性给出解决方案,上传截图,添加附加信息(如clickmouse版本号)
+- 类型为`bug`、`enhancement`、`question`等。
+- 我们给了一些模板,可直接使用。
+- 使用`labels`来标记issue的类型,比如`bug`、`enhancement`、`question`等。
+- 设置issue的`milestone`为你想应用的issue版本。
+- 安全问题请见[安全说明文档](./SECURITY.md)。
+
+## ❇️pr
+- 标题格式:`[类型] 标题`
+- 使用`labels`来标记pr的类型,比如`bug`、`enhancement`、`question`等。
+- 关联issue,这样我们就可以知道这个pr解决了哪个issue。
+- 需要准确写出更新内容,关联到版本号的milestone。
+- 可选添加实现思路
+
+### 🎫规范
+我们pr合并的顺序为:
+```mermaid
+graph LR
+A(其他用户的功能开发分支) --> B(develop/rp分支)
+B --> C(main分支)
+```
+
+pr无特定格式,但是必须清晰描述更新内容,关联到版本号的milestone;标题要简略描述更新内容,若修复或添加了issue里的建议,把该issue编号写进该行为,若出现多个重复issue,则只用写一个,并简单描述此bug。
+
+### ✈️快车pr
+> [!WARNING]
+> 快车pr请谨慎使用
+- 快车pr的意思是跳过部分正常的pr合并分支步骤,以更快的合并到目标分支的功能。
+- 标题格式:`[✈️快车] 标题`
+- 使用快车必须在pr描述中说明使用的原因
+
+如果有人快车合并,但没写快车合并的原因,则拒绝合并该人的分支。
+
+快车pr有高优先级,会优先进行处理。
+
+## 📊milestone
+- 我们给每个版本都设置了一个milestone,用来管理该版本的issue和pr。
+- 需要每个issue或pr都关联到一个milestone,这样我们才能知道该issue或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. 安装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 # 编译扩展
+make clickclean # 如果你要编译精简版,请用这个。
+```
+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/CONTRIBUTING.md b/CONTRIBUTING.md
index 3cecee0a..c0bed8fa 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,152 +1,180 @@
-# ❓怎么贡献?
-## ⬆️commit message
+# ❓ How to Contribute?
+
+
+
+## ⬆️ Commit Message
+
+Must follow the format below:
-需要遵循以下格式:
```
-更新类型(更新模块):(可选)(版本号) (更新概括):
+UpdateType(Module):(optional)(Version) Summary:
-- [(可选)子更新类型]更新内容1
-- [(可选)子更新类型]更新内容2
+- [(optional) Sub-updateType] Update content 1
+- [(optional) Sub-updateType] Update content 2
- ...
```
-更新类型有这些内容:
-| 类型 | 说明 |
+Update types include:
+
+| Type | Description |
| --- | --- |
-| ✅feat | 新功能 |
-| 🔧modify | 修改功能 |
-| 🐛fix | 修bug |
-| ⚒️refactor | 重构 |
-| 📃docs | 改文档,比如README |
-| ❇️style | 改代码风格,不影响功能 |
-| 🔎test | 加测试、改测试 |
-| 📆chore | 杂项,比如改.gitignore |
-| ⏫perf | 性能优化 |
-| 🛒ci | CI/CD相关改动 |
-| 🚅build | 改构建系统或依赖 |
-| ◀️revert | 回滚 |
-| 🔡dependency | 依赖更新 |
-| ❌remove | 删除弃用的组件 |
-| ↪️move | 移动了组件 |
-| ❓unknown | 未知类型 |
-| 自定义 | 尽量以一个直观的英文单词描述,最好配上emoji |
+| ✅feat | New feature |
+| 🔧modify | Modify functionality |
+| 🐛fix | Bug fix |
+| ⚒️refactor | Refactor |
+| 📃docs | Documentation changes (e.g., README) |
+| ❇️style | Code style changes, no functional impact |
+| 🔎test | Add or modify tests |
+| 📆chore | Chores, e.g., modify .gitignore |
+| ⏫perf | Performance improvements |
+| 🛒ci | CI/CD related changes |
+| 🚅build | Build system or dependency changes |
+| ◀️revert | Revert changes |
+| 🔡dependency | Dependency updates |
+| ❌remove | Remove deprecated components |
+| ↪️move | Move components |
+| ❓unknown | Unknown type |
+| custom | Use an intuitive English word, preferably with an emoji |
-内容较多时需要对更新内容添加更新类型提示
+When there are many updates, add an update type hint for each update item.
-## 🗂️分支
+## 🗂️ Branches
-请按此图所示的分支结构来更新:
-
+Please follow the branching structure shown in the image below:
-创建的分支需要以`feature/`开头,以表示功能分支,或创建一个fork,并在fork的分支开发。
+
-发布pr时不限定合并分支,只要不是`main`分支都可以。
+Create branches with the prefix `feature/` to indicate a feature branch, or create a fork and develop on a branch within the fork.
-## 🔠版本号
-clickmouse版本格式为:`A.B.C.D[(alpha | beta |.dev | rc) E]`
-## 😊正式版本
-正式版不带.dev、alpha、beta或rc后缀。
+When opening a PR, the target branch is not restricted, as long as it is not the `main` branch.
-A位代表有重大更新,有代码级的变动。如1.0升级到2.0就重构了代码。
+## 🔠 Version Number
-B位代表有普通更新,通常是更新一些大功能。
+The clickmouse version format is: `A.B.C.D[(alpha | beta | .dev | rc) E]`
-C位代表有修复更新,通常会更新一些小功能和一些bug。
+## 😊 Official Releases
-D位代表版本代号,通常每A, B, C位有变动时候+1。也有可能A, B, C位没有变动,D位+1,这代表紧急更新,通常是修复几个重大影响的bug。
+Official releases do not include the suffixes .dev, alpha, beta, or rc.
-## 🅱️测试版本
-测试版本带.dev、alpha、beta或rc后缀。
+- The A digit indicates major updates with code-level changes. For example, upgrading from 1.0 to 2.0 involves a code refactor.
+- The B digit indicates regular updates, usually introducing major features.
+- The C digit indicates patch updates, typically including minor features and bug fixes.
+- The D digit indicates the version codename, incremented whenever A, B, or C change. It may also be incremented without changes to A, B, or C for emergency updates (e.g., fixing several critical bugs).
-通常前面的`A.B.C.D`在一个测试周期内不变,代表下一个版本。
+## 🅱️ Pre-release Versions
-`.dev`代表早期开发更新,功能不稳定,bug很多,位于版本项目初期。这阶段新增的功能将会被放到实验室中,并默认关闭。
+Pre-release versions include .dev, alpha, beta, or rc suffixes. Typically, the preceding `A.B.C.D` remains unchanged during a test cycle, representing the next version.
-`alpha`代表晚期开发更新,功能不完善,bug较多,位于版本项目早期。这阶段新增的功能将会被放到实验室中,并默认关闭。
+- `.dev` represents early development updates, unstable features, many bugs, at the project's initial stage. New features will be placed in the lab and disabled by default.
+- `alpha` represents late development updates, incomplete features, many bugs, at the project's early stage. New features will be placed in the lab and disabled by default.
+- `beta` represents release candidate testing updates, complete features, fewer bugs, no new features added, at the project's mid stage, gradually merging lab features.
+- `rc` represents a release candidate, complete features, fewer bugs, critical security or bug fixes, closest to the official release, at the project's final stage.
-`beta`代表发布测试更新,功能完善,bug较少,不会再新增功能,位于版本项目中期,并且会逐步合并实验室中的feature。
+## ❓ Issues
-`rc`代表预备发布版本,功能完善,bug较少,会修复一些重要安全问题或bug,最接近正式版,即将发布正式版,位于版本项目末期。
+- Title format: `[Type] Title`
+- Content should accurately describe your request, optionally provide a solution, upload screenshots, and add additional information (e.g., clickmouse version number).
+- Types include `bug`, `enhancement`, `question`, etc.
+- We provide templates that can be used directly.
+- Use `labels` to mark the issue type, such as `bug`, `enhancement`, `question`, etc.
+- Set the issue's `milestone` to the version you intend to apply the issue to.
+- For security issues, refer to the [Security Documentation](./SECURITY.md).
-## ❓issue
-- 标题格式:`[类型] 标题`
-- 内容应准确写出你的需求,并选择性给出解决方案,上传截图,添加附加信息(如clickmouse版本号)
-- 类型为`bug`、`enhancement`、`question`等。
-- 我们给了一些模板,可直接使用。
-- 使用`labels`来标记issue的类型,比如`bug`、`enhancement`、`question`等。
-- 设置issue的`milestone`为你想应用的issue版本。
-- 安全问题请见[安全说明文档](./SECURITY.md)。
+## ❇️ Pull Requests
-## ❇️pr
-- 标题格式:`[类型] 标题`
-- 使用`labels`来标记pr的类型,比如`bug`、`enhancement`、`question`等。
-- 关联issue,这样我们就可以知道这个pr解决了哪个issue。
-- 需要准确写出更新内容,关联到版本号的milestone。
-- 可选添加实现思路
+- Title format: `[Type] Title`
+- Use `labels` to mark the PR type, such as `bug`, `enhancement`, `question`, etc.
+- Link to an issue so we know which issue this PR resolves.
+- Accurately describe the changes and link to the version milestone.
+- Optionally provide implementation ideas.
+
+### 🎫 Guidelines
+
+The order of PR merging is as follows:
-### 🎫规范
-我们pr合并的顺序为:
```mermaid
graph LR
-A(其他用户的功能开发分支) --> B(develop/rp分支)
-B --> C(main分支)
+A(Other user's feature branch) --> B(develop/rp branch)
+B --> C(main branch)
```
-pr无特定格式,但是必须清晰描述更新内容,关联到版本号的milestone;标题要简略描述更新内容,若修复或添加了issue里的建议,把该issue编号写进该行为,若出现多个重复issue,则只用写一个,并简单描述此bug。
+PRs do not have a strict format but must clearly describe the changes, link to the version milestone, and have a title that briefly summarizes the changes. If the PR fixes or implements a suggestion from an issue, include the issue number in that line. For multiple duplicate issues, only write one and briefly describe the bug.
+
+### ✈️ Express PR
-### ✈️快车pr
> [!WARNING]
-> 快车pr请谨慎使用
-- 快车pr的意思是跳过部分正常的pr合并分支步骤,以更快的合并到目标分支的功能。
-- 标题格式:`[✈️快车] 标题`
-- 使用快车必须在pr描述中说明使用的原因
-
-如果有人快车合并,但没写快车合并的原因,则拒绝合并该人的分支。
-
-快车pr有高优先级,会优先进行处理。
-
-## 📊milestone
-- 我们给每个版本都设置了一个milestone,用来管理该版本的issue和pr。
-- 需要每个issue或pr都关联到一个milestone,这样我们才能知道该issue或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. 安装chocolately
+> Use express PRs with caution.
+
+- Express PR means skipping some normal PR merge steps to merge into the target branch faster.
+- Title format: `[✈️Express] Title`
+- You must explain the reason for using an express PR in the PR description.
+
+If someone merges via express PR without stating the reason, the merge will be rejected.
+
+Express PRs have high priority and will be handled first.
+
+## 📊 Milestones
+
+- We set a milestone for each version to manage issues and PRs for that version.
+- Each issue or PR must be linked to a milestone so we know whether it will be included in the next version.
+- Milestone format: `dev_version_number`
+
+## ⬇️ Setting Up the Repository
+
+1. Clone the repository: `git clone https://github.com/xystudiocode/pyClickMouse.git`
+2. For the Python version, install Python (recommended version 3.13, consistent with the software developer's version). [Download link](https://www.python.org/downloads/release/python-31312/)
+3. For the header file and DLL versions, you can install [Visual Studio](https://visualstudio.microsoft.com/).
+
+### 🖥️ GUI
+
+1. Download the source code.
+2. Place `7z.exe` and `7z.dll` in the `gui` directory.
+3. Install Chocolatey:
```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工具
+4. Install make:
```powershell
choco install make
```
-5. 配置python包
+5. Install Python packages:
```powershell
pip install -r requirements.txt
```
-6. 编译
+6. Build:
```powershell
-make clickmouse # 编译clickmouse
-make extension # 编译扩展
-make clickclean # 如果你要编译精简版,请用这个。
+make clickmouse # Build clickmouse
+make extension # Build extension
+make clickclean # Use this if you want to build the slim version
```
-7. 运行`dist/clickmouse/clickmouse/main.exe`就可以加载clickmouse了。
-### 🥴头文件
-仅需修改头文件,就可以被调用
-### ⚙️dll调用
-使用visual studio修改`./dll/dll.sln`里的`源文件/dllmain.cpp`
-### 💾gui旧版本
+7. Run `dist/clickmouse/clickmouse/main.exe` to load clickmouse.
+
+### 🥴 Header Files
+
+You only need to modify the header files to call the library.
+
+### ⚙️ DLL Usage
+
+Modify `源文件/dllmain.cpp` in `./dll/dll.sln` using Visual Studio.
+
+### 💾 Old GUI Version
+
> [!NOTE]
-> gui旧版本的再编译不接受pull request
-使用visual studio修改`./ClickMouse-old/ClickMouse.sln`里的`源文件/clickmouse.cpp`
-### 🐍python库调用
-修改`clickmouse/`下的代码,运行`pip install .`安装
-### 🦎pyd调用
-修改`cython/main.py`的代码,然后执行
+> Recompilation of the old GUI version will not accept pull requests.
+
+Modify `源文件/clickmouse.cpp` in `./ClickMouse-old/ClickMouse.sln` using Visual Studio.
+
+### 🐍 Python Library Usage
+
+Modify the code in `clickmouse/` and run `pip install .` to install.
+
+### 🦎 PYD Usage
+
+Modify the code in `cython/main.py`, then execute:
```python cython/setup.py build_ext --inplace```
-编译结束后,该目录下应该会有个以`.pyd`结尾的文件。
\ No newline at end of file
+After compilation, there should be a file ending with `.pyd` in the directory.
\ No newline at end of file
diff --git a/Gui/logger.py b/Gui/logger.py
index 4f0666b6..e8ec116d 100644
--- a/Gui/logger.py
+++ b/Gui/logger.py
@@ -3,6 +3,10 @@
from datetime import datetime
from pathlib import Path
import logging
+import functools
+import traceback
+from PySide6.QtWidgets import QMessageBox
+import sys
def remove_old_log(directory_path):
'''
@@ -69,18 +73,29 @@ def format(self, record):
return record.getMessage()
else:
return self.default_formatter.format(record)
+
+class LogFilter(logging.Filter):
+ def __init__(self, default_service_id):
+ super().__init__()
+ self.default_service_id = default_service_id
+
+ def filter(self, record):
+ if not hasattr(record, 'service_id'):
+ record.service_id = self.default_service_id
+ return True
class Logger:
- def __init__(self, name):
+ def __init__(self, default_service_id):
+ self.default_service_id = default_service_id
# 创建self.logger对象
- self.logger = logging.getLogger(name)
+ self.logger = logging.getLogger(default_service_id)
self.logger.setLevel(logging.DEBUG) # 设置最低日志级别
# 控制台处理器 - 仅WARNING及以上级别
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING) # 设置控制台日志级别
format = {
- 'default_format': '%(asctime)s - %(levelname)s - %(message)s',
+ 'default_format': '%(asctime)s - %(service_id)s - %(levelname)s - %(message)s',
'simple_format': '%(message)s' # 仅输出消息
}
console_fmt = ConditionalFormatter(
@@ -99,11 +114,51 @@ def __init__(self, name):
# 添加处理器
self.logger.addHandler(console_handler)
self.logger.addHandler(file_handler)
+ self.logger.addFilter(LogFilter(default_service_id=default_service_id))
self.info(f'{"-" * 50}', extra={'simple_format': True})
self.info(f'logger Started')
remove_old_log(folder_path)
+ def auto_logger(self, service_id=None, start_extra_text='', end_extra_text='', start_extra=None, end_extra=None, level=logging.INFO):
+ def decorator(func):
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs):
+ match level:
+ case logging.DEBUG:
+ log_func = self.debug
+ case logging.INFO:
+ log_func = self.info
+ case logging.WARNING:
+ log_func = self.warning
+ case logging.ERROR:
+ log_func = self.error
+ case logging.CRITICAL:
+ log_func = self.critical
+ case _:
+ log_func = self.info
+ out_service_id = self.default_service_id if service_id is None else service_id
+ start_extra_out = {} if start_extra is None else start_extra
+ end_extra_out = {} if end_extra is None else end_extra
+
+ log_func(f'Running function: {func.__name__} args={args} kwargs={kwargs}', extra={'service_id': out_service_id, **start_extra_out})
+ if start_extra_text:
+ log_func(start_extra_text, extra={'service_id': out_service_id, **start_extra_out})
+ try:
+ result = func(*args, **kwargs)
+ except Exception as e:
+ trace = traceback.format_exc()
+ self.exception(out_service_id, trace, f'Function {func.__name__} raised an unexpected exception', extra={'service_id': out_service_id, **end_extra_out})
+ QMessageBox.critical(None, 'An unexpected error occurred', f'An unexpected error occurred in {func.__name__}: \n{trace}\nPlease look the log path: {(folder_path / f"{log_id}.log").resolve()} and send me the log file on issue: https://github.com/xystudiocode/pyClickMouse/issues/new/choose')
+ sys.exit(1) # 退出程序
+ raise e
+ log_func(f'Function {func.__name__} running successfull, returned: {result}', extra={'service_id': out_service_id, **end_extra_out})
+ if end_extra_text:
+ log_func(end_extra_text, extra={'service_id': out_service_id, **end_extra_out})
+ return result
+ return wrapper
+ return decorator
+
def debug(self, msg, extra=None):
self.logger.debug(msg, extra=extra)
@@ -120,4 +175,4 @@ def critical(self, msg, extra=None):
self.logger.critical(msg, extra=extra)
def exception(self, service, trace, msg='', extra=None):
- self.critical(f'{msg}: An error occurred in {service}:\n{trace}', extra=extra)
\ No newline at end of file
+ self.critical(f'{msg}: An error occurred in {service}\n{trace}', extra=extra)
\ No newline at end of file
diff --git a/Gui/main.py b/Gui/main.py
index 0b5339d0..3b50fc2f 100644
--- a/Gui/main.py
+++ b/Gui/main.py
@@ -1,9 +1,9 @@
# 加载ui框架
from PySide6.QtWidgets import QApplication
import sys
-from logger import Logger
+from logger import Logger, logging
app = QApplication(sys.argv)
-logger = Logger('Main')
+logger = Logger('clickmouse.main')
from uiStyles.QUI import *
# 加载框架
@@ -38,6 +38,7 @@
# TODO: 添加更多更新设置,使用hashlib.algorithms_available获取支持的hash算法
+@logger.auto_logger()
def get_windows_version():
'''获取winmdows版本'''
# 检查系统
@@ -55,28 +56,29 @@ def get_windows_version():
else:
return major_version
+@logger.auto_logger()
def filter_hotkey(text:str):
return text.split('(')[0]
+@logger.auto_logger('clickmouse.update')
def load_update_cache():
'''
加载更新缓存文件
'''
- logger.info('Load update cache.')
+ service = 'clickmouse.update'
if update_cache_path.exists():
with open(update_cache_path, 'r', encoding='utf-8') as f:
cache = json.load(f)
return cache
else:
- logger.warning('Update cache file not found, will create a new one.')
+ logger.warning('Update cache file not found, will create a new one.', extra={'service_id': service})
with open(update_cache_path, 'w', encoding='utf-8') as f:
f.write('{}')
return {}
+@logger.auto_logger('clickmouse.update')
def save_update_cache(**kwargs):
'''写入更新缓存文件'''
- logger.info('Save update cache')
-
cache_data = {
'last_check_time': time(),
**kwargs
@@ -85,11 +87,11 @@ def save_update_cache(**kwargs):
with open(update_cache_path, 'w', encoding='utf-8') as f:
json.dump(cache_data, f)
+@logger.auto_logger('clickmouse.update')
def should_check_update():
'''
检查是否应该检查更新
'''
- logger.info('Checking update.')
last_check_time = update_cache.get('last_check_time')
if not last_check_time:
return True
@@ -113,14 +115,15 @@ def should_check_update():
return True
return False
+@logger.auto_logger('clickmouse.setting')
def save_settings():
'''
保存设置
'''
- logger.info('Saving settings.')
with open(data_path / 'settings.json', 'w', encoding='utf-8') as f:
json.dump(settings, f)
+@logger.auto_logger('clickmouse.ipk.main')
def get_packages():
lang_index = [] # 语言包索引
show = []
@@ -133,6 +136,7 @@ def get_packages():
show.append(package.get('show_in_extension_list', True))
return (lang_index, show, package_id)
+@logger.auto_logger()
def get_application_instance():
'''获取或创建 QApplication 实例'''
app = QApplication.instance()
@@ -140,17 +144,20 @@ def get_application_instance():
app = QApplication(sys.argv)
return app
+@logger.auto_logger('clickmouse.setting.hotkeyManager', level=logging.DEBUG)
def all_in_list(list1, list2):
if len(list1) != len(list2):
return False
return all(item in list2 for item in list1)
+@logger.auto_logger('clickmouse.ipk.main')
def import_package(package_id: str):
for i in packages_info:
if i['package_name'] == package_id:
return i
raise ValueError(f'包名 {package_id} 不存在')
+@logger.auto_logger('clickmouse.colorGetter', level=logging.DEBUG)
def get_windows_accent_color():
'''读取Windows强调色'''
# 主题色存储在 HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\DWM
@@ -172,6 +179,7 @@ def get_windows_accent_color():
# 通常我们使用RGB格式,忽略Alpha通道
return f'#{r_str}{g_str}{b_str}'
+@logger.auto_logger('clickmouse.colorGetter')
def new_color_bar(obj):
'''
给创建添加样式标题栏
@@ -179,6 +187,7 @@ def new_color_bar(obj):
color_getter.style_changed.connect(lambda: color_getter.apply_titleBar(obj))
color_getter.style_changed.emit()
+@logger.auto_logger('clickmouse.colorGetter')
def lighten_color_hex(hex_color, factor):
'''
使用HSL色彩空间提亮颜色
@@ -221,6 +230,7 @@ def lighten_color_hex(hex_color, factor):
return hex_result
+@logger.auto_logger('clickmouse.setting.startupManager')
def datetime_to_filetime(dt_utc: datetime):
'''
将datetime对象转换为FILETIME(64位整数)
@@ -238,6 +248,7 @@ def datetime_to_filetime(dt_utc: datetime):
return int(filetime_units)
+@logger.auto_logger('clickmouse.setting.startupManager')
def get_now_filetime():
'''
获取当前UTC时间对应的FILETIME值
@@ -250,16 +261,7 @@ def get_now_filetime():
little_endian = struct.pack('UIWindow: + return self.code_list[id+'.gui'] - def create_setting_page(self, title): + def create_setting_page_old(self, title): logger.info(f'Loading setting page: {title}') page = QWidget() layout = QVBoxLayout(page) @@ -2441,9 +2481,8 @@ def parse_hotkey(input: UHotkeyLineEdit): # 选择语言 lang_choice_layout = QHBoxLayout() # 语言选择布局 self.lang_choice = QComboBox() - self.lang_choice.addItems([i['lang_name'] for i in langs]) - self.lang_choice.setCurrentIndex(setting_value.select_lang) - + self.lang_choice.addItems([get_lang('c4')] + [i['lang_name'] for i in langs]) + self.lang_choice.setCurrentIndex(setting_value.select_lang + 1) # 布局 lang_choice_layout.addWidget(QLabel(f'{get_lang('45')}{get_lang('b5')}:')) # 选择语言提示 lang_choice_layout.addWidget(self.lang_choice) @@ -2547,7 +2586,7 @@ 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, SettingText.select_lang)) + self.lang_choice.currentIndexChanged.connect(lambda: self.on_need_restart_setting_changed_old(lambda: self.lang_choice.currentIndex() - 1, SettingText.select_lang)) tray.checkStateChanged.connect(lambda: self.on_setting_changed(tray.isChecked, SettingText.show_tray_icon)) tray.checkStateChanged.connect(lambda: self.app.setQuitOnLastWindowClosed(not tray.isChecked())) # 关闭窗口时不退出应用 soft_delay.valueChanged.connect(lambda: self.on_setting_changed(lambda: soft_delay.value() * 10 if soft_delay.value() > 0 else 1, SettingText.soft_delay)) @@ -2985,6 +3024,384 @@ def parse_hotkey(input: UHotkeyLineEdit): return page + def create_setting_page(self, title): + logger.info(f'Loading setting page: {title}') + page = QWidget() + + def set_content_label(text): + logger.debug(f'Set content label: {text}') + content_label.setText(text) + + def create_horizontal_line(): + logger.debug('Create horizontal line') + line = UFrame() + return line + + def parse_hotkey(input: UHotkeyLineEdit): + return input.text().split('+') + + # 主程序 + self.app = get_application_instance() + + if title in [self.page_general, self.page_style, self.page_click]: + layout_code = UIWindow(uiml.compile_ui_file(get_resource_path('ui', 'settings', self.ui_file_name[self.page_choice_buttons.index(title)]))) + self.code_list[self.ui_file_name[self.page_choice_buttons.index(title)]] = layout_code + + layout = layout_code.show() + page.setLayout(layout) + else: + layout = QVBoxLayout(page) + # 标题标签 + title_label = QLabel(title) + set_style(title_label, StyleClass.big_24) + + # 内容标签 + content_label = QLabel(get_lang('7d')) + set_style(content_label, StyleClass.dest) + + # 布局 + layout.addWidget(title_label) + layout.addWidget(content_label) + layout.addWidget(create_horizontal_line()) + + # 添加一些示例设置控件 + match title: + case self.page_general: + auto_start_manager.updated.connect(lambda enb: self.get_code('general').find_widget('general.start_layout.start_checkbox').setChecked(enb)) + self.get_code('general').find_widget('general.delay_layout.delay_label').setText(f'{get_lang('b0')}:{setting_value.soft_delay}{get_lang("ms", source=unit_lang)}') + case self.page_click: + self.default_delay: QLineEdit = self.get_code('clicker').find_widget('clicker.delay_layout.delay_input_layout.delay_input') + self.delay_combo: QComboBox = self.get_code('clicker').find_widget('clicker.delay_layout.delay_input_layout.delay_combo') + self.use_default_delay: QCheckBox = self.get_code('clicker').find_widget('clicker.delay_layout.error_use_default_delay') + if not self.default_delay.text(): + self.use_default_delay.setEnabled(False) + self.default_time: QLineEdit = self.get_code('clicker').find_widget('clicker.times_layout.times_input_layout.times_input') + self.times_combo: QComboBox = self.get_code('clicker').find_widget('clicker.times_layout.times_input_layout.times_combo') + self.use_default_times: QCheckBox = self.get_code('clicker').find_widget('clicker.times_layout.error_use_default_times') + if not self.default_time.text(): + self.use_default_times.setEnabled(False) + self.total_time_label: QLabel = self.get_code('clicker').find_widget('clicker.total_time_label') + case self.page_update: + set_content_label(get_lang('87')) + # 选择更新检查提示 + self.enable_update = UCheckBox(get_lang('48')) # 开启更新 + self.enable_update.setChecked(setting_value.update_enabled) + + update_disable_text = QLabel(get_lang('d0')) # 更新禁止提示 + set_style(update_disable_text, StyleClass.d_11) + + self.update_notify = UCheckBox(get_lang('4a')) # 更新提示 + self.update_notify.setChecked(setting_value.update_notify) + + self.quiet_install = UCheckBox(get_lang('49')) # 静默安装 + self.quiet_install.setChecked(setting_value.quiet_update) + + self.update_ok = UCheckBox(get_lang('4c')) # 更新完成弹出提示 + self.update_ok.setChecked(setting_value.update_ok_notify) + + update_frequency_layout = QHBoxLayout() # 更新频率布局 + self.update_frequency = QComboBox() # 更新频率 + self.update_frequency.addItems([get_lang('bd'), get_lang('be'), get_lang('bf'), get_lang('c0')]) + self.update_frequency.setCurrentIndex(setting_value.update_frequency) + update_frequency_layout.addWidget(QLabel(get_lang('c1'))) + update_frequency_layout.addWidget(self.update_frequency) + update_frequency_layout.addStretch(1) + + # 布局 + layout.addWidget(self.enable_update) + layout.addWidget(update_disable_text) + layout.addWidget(self.update_notify) + layout.addWidget(self.quiet_install) + layout.addWidget(self.update_ok) + layout.addLayout(update_frequency_layout) + + # 连接信号 + self.enable_update.checkStateChanged.connect(self.on_enable_update_changed) + self.update_notify.checkStateChanged.connect(lambda: self.on_setting_changed(self.update_notify.isChecked, SettingText.update_notify)) + self.quiet_install.checkStateChanged.connect(lambda: self.on_setting_changed(self.quiet_install.isChecked, SettingText.quiet_update)) + self.update_ok.checkStateChanged.connect(lambda: self.on_setting_changed(self.update_ok.isChecked, SettingText.update_ok_notify)) + self.update_frequency.currentIndexChanged.connect(lambda: self.on_setting_changed(self.update_frequency.currentIndex, SettingText.update_frequency)) + if dev_flags.get('new_settings', False): + self.update_notify.checkStateChanged.connect(self.on_sync_notice) + self.update_ok.checkStateChanged.connect(self.on_sync_ok_notice) + else: + self.on_enable_update(self.enable_update.isChecked()) + case self.page_hotkey: + set_content_label(get_lang('21')) + + self.hotkey_enabled = UCheckBox(get_lang('c9')) # 热键启用 + self.hotkey_enabled.setChecked(setting_value.hotkey_enabled) + + # 左键连点 + self.left_click_layout = QHBoxLayout() + self.left_click_input = UHotkeyLineEdit() # 左键连点输入框 + self.left_click_input.setText(format_keys(setting_value.left_click_hotkey)) + self.left_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + + # 布局 + self.left_click_layout.addWidget(QLabel(f'{get_lang('0c')}: '), 1) # 左键连点提示 + self.left_click_layout.addWidget(self.left_click_input, 6) + self.left_click_layout.addWidget(self.left_repair_button, 1) + self.left_click_layout.addStretch() + + # 右键连点 + self.right_click_layout = QHBoxLayout() # 右键连点布局 + self.right_click_input = UHotkeyLineEdit() # 右键连点输入框 + self.right_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + + self.right_click_input.setText(format_keys(setting_value.right_click_hotkey)) + + # 布局 + self.right_click_layout.addWidget(QLabel(f'{get_lang('0d')}: '), 1) # 右键连点提示 + self.right_click_layout.addWidget(self.right_click_input, 6) + self.right_click_layout.addWidget(self.right_repair_button, 1) + self.right_click_layout.addStretch() + + # 暂停/重启连点 + self.pause_click_layout = QHBoxLayout() # 暂停/重启连点布局 + self.pause_click_input = UHotkeyLineEdit() # 暂停/重启连点输入框 + self.pause_click_input.setText(format_keys(setting_value.pause_click_hotkey)) + self.pause_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + + # 布局 + self.pause_click_layout.addWidget(QLabel(f'{get_lang('6b')}: '), 1) # 暂停/重启连点提示 + self.pause_click_layout.addWidget(self.pause_click_input, 6) + self.pause_click_layout.addWidget(self.pause_repair_button, 1) + self.pause_click_layout.addStretch() + + # 停止连点 + self.stop_click_layout = QHBoxLayout() # 停止连点布局 + self.stop_click_input = UHotkeyLineEdit() # 停止连点输入框 + self.stop_click_input.setText(format_keys(setting_value.stop_click_hotkey)) + self.stop_repair_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + + # 布局 + self.stop_click_layout.addWidget(QLabel(f'{get_lang('6c')}: '), 1) # 停止连点提示 + self.stop_click_layout.addWidget(self.stop_click_input, 6) + self.stop_click_layout.addWidget(self.stop_repair_button, 1) + self.stop_click_layout.addStretch() + + # 连点属性 + self.click_attr_layout = QHBoxLayout() # 连点属性布局 + self.click_attr_input = UHotkeyLineEdit() # 连点属性输入框 + self.click_attr_input.setText(format_keys(setting_value.click_attr_hotkey)) + self.click_attr_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + + # 布局 + self.click_attr_layout.addWidget(QLabel(f'{get_lang('8c')}: '), 1) # 连点属性提示 + self.click_attr_layout.addWidget(self.click_attr_input, 6) + self.click_attr_layout.addWidget(self.click_attr_button, 1) + self.click_attr_layout.addStretch() + + # 快速连点 + self.fast_click_layout = QHBoxLayout() # 快速连点布局 + self.fast_click_input = UHotkeyLineEdit() # 快速连点输入框 + self.fast_click_input.setText(format_keys(setting_value.fast_click_hotkey)) + self.fast_click_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + + # 布局 + self.fast_click_layout.addWidget(QLabel(f'{get_lang('75')}: '), 1) # 快速连点提示 + self.fast_click_layout.addWidget(self.fast_click_input, 6) + self.fast_click_layout.addWidget(self.fast_click_button, 1) + self.fast_click_layout.addStretch() + + # 主窗口 + self.main_window_layout = QHBoxLayout() # 主窗口布局 + self.main_window_input = UHotkeyLineEdit() # 主窗口输入框 + self.main_window_input.setText(format_keys(setting_value.main_window_hotkey)) + self.main_window_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + + # 布局 + self.main_window_layout.addWidget(QLabel(f'{get_lang('76')}: '), 1) # 主窗口提示 + self.main_window_layout.addWidget(self.main_window_input, 6) + self.main_window_layout.addWidget(self.main_window_button, 1) + self.main_window_layout.addStretch() + + # 布局 + if dev_flags.get('new_settings', False): + layout.addWidget(self.hotkey_enabled) + layout.addLayout(self.left_click_layout) + layout.addLayout(self.right_click_layout) + layout.addLayout(self.pause_click_layout) + layout.addLayout(self.stop_click_layout) + layout.addLayout(self.click_attr_layout) + layout.addLayout(self.fast_click_layout) + layout.addLayout(self.main_window_layout) + + # 连接信号 + self.left_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.left_click_input), SettingText.left_click_hotkey)) + self.right_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.right_click_input), SettingText.right_click_hotkey)) + self.pause_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.pause_click_input), SettingText.pause_click_hotkey)) + self.stop_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.stop_click_input), SettingText.stop_click_hotkey)) + self.click_attr_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.click_attr_input), SettingText.click_attr_hotkey)) + self.fast_click_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.fast_click_input), SettingText.fast_click_hotkey)) + self.main_window_input.textChanged.connect(lambda: self.on_setting_changed(lambda: parse_hotkey(self.main_window_input), SettingText.main_window_hotkey)) + + self.left_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.left_click_hotkey)) + self.right_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.right_click_hotkey)) + self.pause_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.pause_click_hotkey)) + self.stop_repair_button.clicked.connect(lambda: self.repair_settings(SettingText.stop_click_hotkey)) + self.click_attr_button.clicked.connect(lambda: self.repair_settings(SettingText.click_attr_hotkey)) + self.fast_click_button.clicked.connect(lambda: self.repair_settings(SettingText.fast_click_hotkey)) + self.main_window_button.clicked.connect(lambda: self.repair_settings(SettingText.main_window_hotkey)) + + self.hotkey_enabled.checkStateChanged.connect(self.on_enable_hotkey_changed) + self.on_enable_hotkey_changed(self.hotkey_enabled.isChecked() if dev_flags.get('new_settings', False) else True) + case self.page_doc: + set_content_label(get_lang('ca')) + + default_doc_layout = QHBoxLayout() # 默认打开文档布局 + + default_doc_link = QLineEdit() # 默认打开文档链接 + default_doc_link.setText(setting_value.default_doc_link) + repair_default_doc_link_button = QPushButton(get_lang('20')) # 还原默认设置按钮 + + # 布局 + default_doc_layout.addWidget(QLabel(get_lang('c2')), 1) # 默认打开文档提示 + default_doc_layout.addWidget(default_doc_link, 6) + if dev_flags.get('new_settings', False): + default_doc_layout.addWidget(repair_default_doc_link_button, 1) + default_doc_layout.addStretch() + + default_lang_layout = QHBoxLayout() # 默认文档语言布局 + lang_choice = QComboBox() # 语言选择框 + lang_choice.addItems([get_lang('45'), get_lang('c4')] + [i['lang_name'] for i in langs if i['supported']]) + lang_choice.setCurrentIndex(setting_value.lang_doc) + + # 布局 + default_lang_layout.addWidget(QLabel(get_lang('c5'))) # 默认文档语言提示 + default_lang_layout.addWidget(lang_choice) + default_lang_layout.addStretch() + + update_log_path_layout = QHBoxLayout() # 更新日志路径布局 + update_log_path_input = QLineEdit() # 更新日志路径输入框 + update_log_path_input.setText(setting_value.update_log_path) + + repair_update_log_path_button = QPushButton(get_lang('20')) # 还原默认路径按钮 + + # 布局 + update_log_path_layout.addWidget(QLabel(get_lang('c6')), 1) # 更新日志路径提示 + update_log_path_layout.addWidget(update_log_path_input, 6) + if dev_flags.get('new_settings', False): + update_log_path_layout.addWidget(repair_update_log_path_button, 1) + update_log_path_layout.addStretch() + + label = QLabel(get_lang('c7')) + # 布局 + set_style(label, StyleClass.d_11) + + layout.addLayout(default_doc_layout) + layout.addLayout(default_lang_layout) + layout.addWidget(create_horizontal_line()) + layout.addLayout(update_log_path_layout) + layout.addWidget(create_horizontal_line()) + layout.addWidget(label) + + # 链接信号 + default_doc_link.textChanged.connect(lambda: self.on_setting_changed(default_doc_link.text, SettingText.default_doc_link)) + lang_choice.currentIndexChanged.connect(lambda: self.on_setting_changed(self.lang_choice.currentIndex, SettingText.lang_doc)) + update_log_path_input.textChanged.connect(lambda: self.on_setting_changed(update_log_path_input.text, SettingText.update_log_path)) + repair_default_doc_link_button.clicked.connect(lambda: self.repair_settings(SettingText.default_doc_link)) + repair_update_log_path_button.clicked.connect(lambda: self.repair_settings(SettingText.update_log_path)) + case self.page_notify: + set_content_label(get_lang('cc')) + + # 更新提示 + self.notice_update_notify = UCheckBox(get_lang('4a')) + self.notice_update_notify.setChecked(setting_value.update_notify) + + # 更新完成提示 + self.notice_update_ok_notify = UCheckBox(get_lang('4c')) + self.notice_update_ok_notify.setChecked(setting_value.update_ok_notify) + + # 启用软件启动警告 + self.start_warning = UCheckBox(get_lang('cd')) + tip_label = QLabel(get_lang('ce')) + set_style(tip_label, StyleClass.d_11) + self.start_warning.setChecked(setting_value.show_warning) + + self.package_warning = UCheckBox(get_lang('cf')) + self.package_warning.setChecked(setting_value.show_package_warning) + + # 布局 + layout.addWidget(self.notice_update_notify) + layout.addWidget(self.notice_update_ok_notify) + layout.addWidget(create_horizontal_line()) + layout.addWidget(self.start_warning) + layout.addWidget(tip_label) + layout.addWidget(self.package_warning) + + # 连接信号 + self.notice_update_notify.checkStateChanged.connect(lambda: self.on_setting_changed(self.notice_update_notify.isChecked, SettingText.update_notify)) + self.notice_update_notify.checkStateChanged.connect(self.on_sync_notice) + self.notice_update_ok_notify.checkStateChanged.connect(lambda: self.on_setting_changed(self.notice_update_ok_notify.isChecked, SettingText.update_ok_notify)) + self.notice_update_ok_notify.checkStateChanged.connect(self.on_sync_ok_notice) + self.start_warning.checkStateChanged.connect(self.on_enable_warn) + self.package_warning.checkStateChanged.connect(lambda: self.on_setting_changed(self.package_warning.isChecked, SettingText.show_package_warning)) + + self.on_enable_update(self.enable_update.isChecked()) + self.on_warning_update(self.start_warning.isChecked()) + case self.page_flags: + set_content_label(get_lang('d4')) + + if not dev_settings: + layout.addWidget(QLabel('No dev settings found.')) + else: + for i in dev_settings: + checkbox = UCheckBox(i['name']) + if i['key'] == 'new_settings': + checkbox.checkStateChanged.connect(lambda chk,idx=i['key']:(self.save_dev_config(chk, idx),self.window_restarted.emit(),)) + else: + checkbox.checkStateChanged.connect(lambda chk,idx=i['key']:(self.save_dev_config(chk, idx))) + checkbox.setChecked(dev_flags.get(i['key'], False)) + desc = QLabel(i['desc']) + set_style(desc, StyleClass.d_11) + + layout.addWidget(checkbox) + layout.addWidget(desc) + layout.addWidget(create_horizontal_line()) + + restart = uiml.compile_ui_file(get_resource_path('ui', 'settings', 'bottom.gui')) + + layout.addLayout(UIWindow(restart).show()) + layout.addStretch(1) + + return page + + def on_clicker_changed(self, func, value, checkbox: UCheckBox=None): + '''clicker更新状态改变事件''' + if checkbox is not None: + if not func(): + checkbox.setEnabled(False) + else: + checkbox.setEnabled(True) + self.on_setting_changed(func, value) + on_input_change(type=InputChange.setting_window) + + def on_theme_updated(self, theme: str): + style_list = QStyleFactory.keys() + + self.on_setting_changed(lambda: style_list[theme], SettingText.theme) + refresh.run() + self.app.setStyle(style_list[theme]) + + def on_hide_flag_changed(self, state): + self.on_setting_changed(self.get_code('general').find_widget('general.hide_flag').isChecked, SettingText.hide_flags) + self.restart_window() + + def on_soft_delay_changed(self, value): + soft_delay: QSlider = self.get_code('general').find_widget('general.delay_layout.software_delay_layout.software_delay') + delay_layout_text: QLabel = self.get_code('general').find_widget('general.delay_layout.delay_label') + + self.on_setting_changed(lambda: soft_delay.value() * 10 if soft_delay.value() > 0 else 1, SettingText.soft_delay) + 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)}') + + def on_tray_checked(self, state): + '''托盘图标选择事件''' + tray = self.get_code('general').find_widget('general.tray_layout.tray') + self.on_setting_changed(tray.isChecked, SettingText.show_tray_icon) + self.app.setQuitOnLastWindowClosed(not tray.isChecked()) + 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: @@ -3105,7 +3522,35 @@ def on_need_restart_setting_changed(self, handle, key: str, restart_place: list[ self.on_setting_changed(handle, key, *args) settings_need_restart = True + lang = self.code_list['general.gui'].find_widget('general.lang_choice_layout.lang_choice').currentIndex() + if lang >= 1: + lang -= 1 + elif lang == 0: + lang = system_lang + + restart_place = list(map(lambda x: get_lang(x, lang_id=lang), restart_place)) + + selected_lang_yes = CustonMessageButton(get_lang('01', source=default_button_text, lang_id=lang), QMessageBox.YesRole) + selected_lang_no = CustonMessageButton(get_lang('02', source=default_button_text, lang_id=lang), QMessageBox.AcceptRole) + need_restart = MessageBox.warning(self, get_lang('15', lang_id=lang), f'{get_lang("89", lang_id=lang)}: {", ".join(restart_place)}', [selected_lang_yes, selected_lang_no], selected_lang_yes) + if need_restart == 2: + self.restart() + else: + self.restart_window() + + def on_need_restart_setting_changed_old(self, handle, key: str, restart_place: list[str] = ['a9'], *args): + '''托盘图标选择事件''' + global settings_need_restart + + self.on_setting_changed(handle, key, *args) + settings_need_restart = True + self.restart_button.show() + lang = self.lang_choice.currentIndex() + if lang >= 1: + lang -= 1 + elif lang == 0: + lang = system_lang restart_place = list(map(lambda x: get_lang(x, lang_id=lang), restart_place)) @@ -3173,9 +3618,19 @@ def __init__(self): self.setGeometry(100, 100, 200, 125) self.setWindowIcon(icon) self.setFixedSize(self.width(), self.height()) - self.init_ui() - + + if dev_flags.get('decoupling', False): + self.init_ui() + else: + self.init_ui_old() + def init_ui(self): + self.main_layout = UIWindow(uiml.compile_ui_file(get_resource_path('ui', 'importExtension.gui'))) + self.setLayout(self.main_layout.show()) + + logger.debug('Init import extension mode window finished') + + def init_ui_old(self): layout = QVBoxLayout() self.setLayout(layout) @@ -3199,13 +3654,18 @@ def init_ui(self): layout.addWidget(mode_button) # 连接信号 - mode_button.clicked.connect(self.on_mode_button_clicked) + mode_button.clicked.connect(self.on_mode_button_clicked_old) logger.debug('Init import extension mode window finished') - def on_mode_button_clicked(self): + def on_mode_button_clicked_old(self): self.close() main_window.show_import_extension(self.mode_combo.currentIndex()) + + def on_mode_button_clicked(self): + self.close() + mode_combo = self.main_layout.find_widget('main_layout.mode_combo') + # main_window.show_import_extension(mode_combo.currentIndex()) class TrayApp: def __init__(self): @@ -3515,14 +3975,9 @@ def on_start(self): if os.path.exists('extensions') and os.path.isdir('extensions'): shutil.rmtree('extensions') pass - - logger.info('Loading value') - logger.debug('Loading const value') - has_packages = os.path.exists(get_resource_path('packages')) - package_names, show_list, package_ids = get_packages() - + # Windows API常量 - logger.debug('sSetting WinAPI const value') + logger.debug('Setting WinAPI const value') DWMWA_USE_IMMERSIVE = 20 DWMWA_USE_IMMERSIVE_DARK_MODE = 20 DWM_WINDOW_CORNER_PREFERENCE = 33 @@ -3536,6 +3991,11 @@ def on_start(self): color_getter = ColorGetter() run_after = RunAfter() + logger.info('Loading value') + logger.debug('Loading const value') + has_packages = os.path.exists(get_resource_path('packages')) + package_names, show_list, package_ids = get_packages() + # 变量 logger.info('Define pathes') @@ -3580,7 +4040,6 @@ def on_start(self): # 单位控制 latest_index = 2 - select_lang = setting_value.select_lang # 其他 dev_config = parse_dev.parse() # 开发者模式配置 @@ -3623,11 +4082,10 @@ def on_start(self): click_attr_window = ClickAttrWindow() fast_click_window = FastSetClickWindow() setting_window = SettingWindow() + set_import_extension_window = SetImportExtensionModeWindow() 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() app = TrayApp() app.app.setStyle(setting_value.theme) diff --git a/Gui/res/langs/langs.json b/Gui/res/langs/langs.json index 658cdd83..f654dab4 100644 --- a/Gui/res/langs/langs.json +++ b/Gui/res/langs/langs.json @@ -102,7 +102,7 @@ "64": "Total {0} clicks, {1} clicks completed, {2} clicks remaining; Total {3} seconds spent, {4} seconds spent, {5} seconds remaining, click interval {6}", "65": "preview", "66": "times", - "67": null, + "67": "Number too large", "68": "Open Application", "69": "Hotkey", "6a": "Revert", @@ -318,7 +318,7 @@ "64": "共{0}次连点,已完成{1}次,剩余{2}次;共花费{3},已花费{4},剩余{5},连点间隔{6}", "65": "预览版", "66": "次", - "67": null, + "67": "数值过大", "68": "打开应用", "69": "热键", "6a": "回滚", diff --git a/Gui/res/ui/clickattr.gui b/Gui/res/ui/clickattr.gui index 43230d76..17f2fc61 100644 --- a/Gui/res/ui/clickattr.gui +++ b/Gui/res/ui/clickattr.gui @@ -1,12 +1,12 @@ -- - - - - - - - - + + \ No newline at end of file diff --git a/Gui/res/ui/fastClick.gui b/Gui/res/ui/fastClick.gui index ed6636cb..0dd1785f 100644 --- a/Gui/res/ui/fastClick.gui +++ b/Gui/res/ui/fastClick.gui @@ -1,14 +1,44 @@ -+ + + + + + + + - + + \ No newline at end of file diff --git a/Gui/res/ui/importExtension.gui b/Gui/res/ui/importExtension.gui new file mode 100644 index 00000000..ac1a0916 --- /dev/null +++ b/Gui/res/ui/importExtension.gui @@ -0,0 +1,5 @@ +- - - + + - - + + + + \ No newline at end of file diff --git a/Gui/res/ui/settings/bottom.gui b/Gui/res/ui/settings/bottom.gui new file mode 100644 index 00000000..6fb8a38f --- /dev/null +++ b/Gui/res/ui/settings/bottom.gui @@ -0,0 +1,8 @@ ++ + + + \ No newline at end of file diff --git a/Gui/res/ui/settings/clicker.gui b/Gui/res/ui/settings/clicker.gui new file mode 100644 index 00000000..7ea422d1 --- /dev/null +++ b/Gui/res/ui/settings/clicker.gui @@ -0,0 +1,60 @@ ++ + \ No newline at end of file diff --git a/Gui/res/ui/settings/general.gui b/Gui/res/ui/settings/general.gui new file mode 100644 index 00000000..7caad654 --- /dev/null +++ b/Gui/res/ui/settings/general.gui @@ -0,0 +1,78 @@ ++ + + + ++ ++ + + + + + ++ ++ + + + + + + \ No newline at end of file diff --git a/Gui/res/ui/settings/style.gui b/Gui/res/ui/settings/style.gui new file mode 100644 index 00000000..ff2d263c --- /dev/null +++ b/Gui/res/ui/settings/style.gui @@ -0,0 +1,34 @@ ++ + + + ++ + + ++ + + ++ + ++ + + + ++ + + + + ++ ++ + + + + + + + ++ + \ No newline at end of file diff --git a/Gui/sharelibs.py b/Gui/sharelibs.py index 3cefdaad..efb55db1 100644 --- a/Gui/sharelibs.py +++ b/Gui/sharelibs.py @@ -88,7 +88,7 @@ def load_settings(): def get_lang(lang_package_id, lang_id = None, source = None): source = langs if source is None else source - lang_id = settings.get('select_lang', 0) if lang_id is None else lang_id + lang_id = select_lang if lang_id is None else lang_id for i in source: if i['lang_id'] == 0: # 设置默认语言包 default_lang_text = i['lang_package'] @@ -121,6 +121,9 @@ def parse_system_language_to_lang_id(): return 0 system_lang = parse_system_language_to_lang_id() +select_lang = settings.get('select_lang', 0) +if select_lang == -1: + select_lang = system_lang def get_control_lang(lang_id): return get_lang(lang_id, source=control_langs) @@ -303,11 +306,16 @@ def widget_replacer(ui_data: str): 替换UI中的widget,使用函数式返回 ''' if ui_data.startswith('!lang '): - return get_lang(ui_data[6:]) # 语言解析 + if len(ui_data.split(' ')) == 2: + return get_lang(ui_data.split(' ')[1]) # 语言解析 + else: + return get_lang(ui_data.split(' ')[1], source=globals()[ui_data.split(' ')[2]]) # 语言解析 else: - uiml.default_replacer(ui_data) # 交由uiml进行替换 + return uiml.default_replacer(ui_data) # 交由uiml进行替换 -def layout_parser(ui_data: Dict[str, Any]): +def layout_parser(ui_data: Dict[str, Any], namespace=None): + if not ui_data.get('show_if', True): + return None if ui_data.get('direction').lower() == 'u': input_compiled_list = [] # 索引0 -- 字 @@ -315,15 +323,22 @@ def layout_parser(ui_data: Dict[str, Any]): # 索引2 -- 选择框 inputs = ui_data['content'][1] for input in inputs['content']: - input_compiled_list.append(uiml.compile_ui(input)) # 递归解析 + input_compiled_list.append(uiml.compile_ui(input, namespace)) # 递归解析 combos = ui_data['content'][2] # 组合框 combos_compiled_list = [] for combo in combos['content']: - combos_compiled_list.append(uiml.compile_ui(combo)) # 递归解析 + combos_compiled_list.append(uiml.compile_ui(combo, namespace)) # 递归解析 return {'name': ui_data.get('name'), 'direction': ui_data.get('direction'), "texts": ui_data['content'][0]['values'], 'inputs': input_compiled_list, 'combos': combos_compiled_list} - return uiml.default_layout_parser(ui_data) # 交由uiml进行替换 + return uiml.default_layout_parser(ui_data, namespace) # 交由uiml进行替换 + +def widget_parser(ui_data: Dict[str, Any], namespace=None): + show_value = ui_data.get('show_if', True) + uiml_value = uiml.default_widget_parser(ui_data, namespace) # 交由uiml进行替换 + if not show_value: + return None + return uiml_value -uiml.set_namespace(value_replace_func=widget_replacer, layout_parser_func=layout_parser) # 设置uiml的控制函数 +uiml.set_namespace(value_replace_func=widget_replacer, layout_parser_func=layout_parser, widget_parser_func=widget_parser, additional_used_widget_key=['show_if'], additional_used_layout_key=['show_if'], reverse=True) # 设置uiml的控制函数 class UIWindow(uiml.UIMLLayout): def __init__(self, list=None): @@ -417,6 +432,9 @@ def find_widget(self, path: str, data=None): # 正常流程不会执行到这里 return None + + def return_layout(self, layout): + return layout, 'layout', 0 def extend_layout(self, list_info): if list_info['direction'].lower() == 'u': @@ -426,6 +444,23 @@ def extend_layout(self, list_info): if text.startswith('!lang '): # 语言解析 text = get_lang(text[6:]) layout.addUnitRow(text, input['content'], combo['content']) - return layout, 'layout' + return layout, 'layout', 0 else: - uiml.WidgetError.direction_error() # 交由 uiml 进行处理 \ No newline at end of file + uiml.WidgetError.direction_error() # 交由 uiml 进行处理 + + def adder(self, widget_info): + return (widget_info[0], ), {'stretch': widget_info[2]} + + def add_layout(self, list_content: Dict, layout: uiml.QLayout): + draw_on_end = list_content.get('stretch_place', 'end') == 'end' # 是否在布局末尾绘制拉伸 + if not draw_on_end: # 在布局前绘制拉伸 + self._add_stretch(list_content, layout) # 添加伸缩 + self._for_loop(list_content, layout) # 递归绘制子布局 + if draw_on_end: # 在布局末尾绘制拉伸 + self._add_stretch(list_content, layout) # 添加伸缩 + return self.return_layout(layout) + + def extend_widget(self, widget_info): + widget = list(super().extend_widget(widget_info)) + widget.append(widget_info.get('stretch', 0)) + return tuple(widget) \ No newline at end of file diff --git a/Gui/uiStyles/uiTemplate.py b/Gui/uiStyles/uiTemplate.py index 6e734b16..cabf6013 100644 --- a/Gui/uiStyles/uiTemplate.py +++ b/Gui/uiStyles/uiTemplate.py @@ -17,20 +17,20 @@ def __init__(self): def init_base_ui(self): '''创建基础UI''' # 创建中心部件和主布局 - central_widget = QWidget() - self.setCentralWidget(central_widget) - main_layout = QHBoxLayout(central_widget) + self.central_widget = QWidget() + self.setCentralWidget(self.central_widget) + self.main_layout = QHBoxLayout(self.central_widget) # 设置两个滚动区域之间的间距为15像素 - main_layout.setSpacing(15) + self.main_layout.setSpacing(15) # 创建左侧滚动区域 self.left_scroll = QScrollArea() - main_layout.addWidget(self.left_scroll, 1) # 拉伸系数为1 + self.main_layout.addWidget(self.left_scroll, 1) # 拉伸系数为1 # 创建右侧滚动区域 self.right_scroll = VScrollArea() - main_layout.addWidget(self.right_scroll, 5) # 拉伸系数为5 + self.main_layout.addWidget(self.right_scroll, 5) # 拉伸系数为5 def init_ui(self): '''创建设置界面''' @@ -46,7 +46,7 @@ def draw_page_choice(self): for i, page_title in enumerate(self.page_choice_buttons): button = QPushButton(page_title) # 连接按钮点击信号到槽函数 - button.clicked.connect(lambda checked, idx=i: self.on_page_button_clicked(idx)) + button.clicked.connect(lambda checked, idx=i: (self.on_page_button_clicked(idx))) content_layout.addWidget(button) self.buttons.append(button) @@ -54,6 +54,11 @@ def draw_page_choice(self): content_layout.addStretch() self.left_scroll.setWidget(content) + + def on_page_button_clicked(self, idx): + '''页面按钮点击槽函数''' + # 请自行实现 + pass def init_right_pages(self): '''初始化右侧设置页面''' diff --git a/SECURITY-zh_CN.md b/SECURITY-zh_CN.md new file mode 100644 index 00000000..a8804be2 --- /dev/null +++ b/SECURITY-zh_CN.md @@ -0,0 +1,36 @@ +# 安全漏洞报告 + + + +## 怎么提交漏洞? + +提交一个github security,详细说明漏洞的影响范围、触发条件、漏洞详情等。 + +## 提交漏洞格式 + +```markdown +# Security Policy + +## Supported Versions + +clickmouse >= **version** + +## Reporting a Vulnerability + +Use this section to tell people how to report a vulnerability. + +Tell them where to go, how often they can expect to get an update on a +reported vulnerability, what to expect if the vulnerability is accepted or +declined, etc. + +## How to fix +> [!TIP] +> This section is optional. + +If the vulnerability is accepted, provide information on how to fix the vulnerability. +``` \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index 95d1d80b..cd0ed9d4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,10 +1,17 @@ -# 安全漏洞报告 +# Security Vulnerability Report -## 怎么提交漏洞? + -提交一个github security,详细说明漏洞的影响范围、触发条件、漏洞详情等。 +## How to submit a vulnerability? -## 提交漏洞格式 +Submit to GitHub Security, detailing the impact range of the vulnerability, triggering conditions, vulnerability details, etc. + +## Vulnerability submission format ```markdown # Security Policy diff --git a/documents/en/develop/contributing/issue_template.md b/documents/en/develop/contributing/issue_template.md index 8785430b..61487a7d 100644 --- a/documents/en/develop/contributing/issue_template.md +++ b/documents/en/develop/contributing/issue_template.md @@ -122,7 +122,7 @@ Report a bug.+ + + + ++ + + + ++ + + + ++ ++ + + Add a title - To avoid causing more trouble, before reporting issue, first check if others have already reported same problem.Check if duplicate exists😊 +To avoid unnecessary trouble, please check if anyone else has already reported the same issue before filing an issue. Check if duplicate exists😊 We will not handle issues on gitee, please use github to publish.🙋♂️ Please only report 1 problem at a time.😀 🔡Clickmouse version diff --git a/documents/public/imgs/en/mergeSteps.png b/documents/public/imgs/en/mergeSteps.png index 7b653391..226a017f 100644 Binary files a/documents/public/imgs/en/mergeSteps.png and b/documents/public/imgs/en/mergeSteps.png differ diff --git a/imgs/readme/mergeSteps-en.png b/imgs/readme/mergeSteps-en.png new file mode 100644 index 00000000..226a017f Binary files /dev/null and b/imgs/readme/mergeSteps-en.png differ diff --git a/requirements.txt b/requirements.txt index 011efece..548bddb4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,4 @@ pytz>=2014 nuitka>=2.8 psutil>=1 pytest>=5 -uiml >=0.1 \ No newline at end of file +uiml>=0.2 \ No newline at end of file