mirror of
https://github.com/KenanZhu/AutoLibrary.git
synced 2026-06-18 07:23:03 +08:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3963b3f2e6 | |||
| f2a05809bd | |||
| b55a0c06a5 | |||
| 2496c4e367 | |||
| de30559af1 | |||
| e1c2efc8c0 | |||
| 26a70cdceb | |||
| ce14be2555 | |||
| eda16f01f1 | |||
| 22f806bfb0 | |||
| d26852eaaf | |||
| 2ffe620532 | |||
| fe42d3cd98 | |||
| 0795939aa3 | |||
| 8b6baf9b6a | |||
| 7098d7075f | |||
| be3942ea2f | |||
| 7e3a089e21 |
+14
-14
@@ -94,13 +94,13 @@ jobs:
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not (Test-Path "model")) {
|
||||
New-Item -ItemType Directory -Path "model" | Out-Null
|
||||
Write-Host "✓ Created model directory"
|
||||
if (-not (Test-Path "models")) {
|
||||
New-Item -ItemType Directory -Path "models" | Out-Null
|
||||
Write-Host "✓ Created models directory"
|
||||
}
|
||||
|
||||
$onnxSource = Join-Path $ddddocrPath "common.onnx"
|
||||
$onnxDest = "model/common.onnx"
|
||||
$onnxDest = "models/common.onnx"
|
||||
if (Test-Path $onnxSource) {
|
||||
Copy-Item $onnxSource $onnxDest -Force
|
||||
Write-Host "✓ Copied ONNX model from: $onnxSource"
|
||||
@@ -119,18 +119,18 @@ jobs:
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
- name: Compile Qt UI files
|
||||
run: |
|
||||
cd src/gui/batchs
|
||||
./compile_ui.bat
|
||||
shell: cmd
|
||||
|
||||
- name: Compile Qt Resource files
|
||||
run: |
|
||||
cd src/gui/batchs
|
||||
cd batchs
|
||||
./compile_rc.bat
|
||||
shell: cmd
|
||||
|
||||
- name: Compile Qt UI files
|
||||
run: |
|
||||
cd batchs
|
||||
./compile_ui.bat
|
||||
shell: cmd
|
||||
|
||||
- name: Generate 'Main.spec'
|
||||
run: |
|
||||
$version = "${{ steps.get_version.outputs.VERSION }}"
|
||||
@@ -148,8 +148,8 @@ jobs:
|
||||
" pathex=[],"
|
||||
" binaries=[],"
|
||||
" datas=["
|
||||
" ('model\\common.onnx', 'ddddocr'),"
|
||||
" ('src\\gui\\icons\\AutoLibrary_32x32.ico', 'gui\\icons'),"
|
||||
" ('models\\common.onnx', 'ddddocr'),"
|
||||
" ('src\\gui\\resources\\icons\\AutoLibrary_32x32.ico', 'gui\\resources\\icons'),"
|
||||
" ],"
|
||||
" hiddenimports=[],"
|
||||
" hookspath=[],"
|
||||
@@ -180,7 +180,7 @@ jobs:
|
||||
" target_arch=None,"
|
||||
" codesign_identity=None,"
|
||||
" entitlements_file=None,"
|
||||
" icon=['src\\gui\\icons\\AutoLibrary_32x32.ico'],"
|
||||
" icon=['src\\gui\\resources\\icons\\AutoLibrary_32x32.ico'],"
|
||||
")"
|
||||
)
|
||||
$specLines | Out-File -FilePath "Main.spec" -Encoding UTF8
|
||||
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
4. 运行 `AutoLibrary-${{ needs.build.outputs.version }}.exe` (首次运行会初始化配置文件)
|
||||
5. 按照提示操作即可
|
||||
|
||||
更多详情请访问 [AutoLibrary 网站](http://autolibrary.cv) 和查看 [帮助手册](https://autolibrary.cv/docs/manual_lists.html)
|
||||
更多详情请访问 [AutoLibrary 网站](http://www.autolibrary.top) 和查看 [帮助手册](https://www.autolibrary.top/manuals)
|
||||
|
||||
---
|
||||
**完整更新日志见下方自动生成的 Release Notes**
|
||||
|
||||
+12
-12
@@ -6,16 +6,16 @@
|
||||
__pycache__/
|
||||
build/
|
||||
dist/
|
||||
model/*.onnx
|
||||
driver/*.exe
|
||||
src/gui/configs/*.json
|
||||
src/gui/translators/qtbase_zh_CN.qm
|
||||
src/gui/AutoLibraryResources.py
|
||||
src/gui/AutoLibraryResource.py
|
||||
src/gui/Ui_ALMainWindow.py
|
||||
src/gui/Ui_ALConfigWidget.py
|
||||
src/gui/Ui_ALTimerTaskWidget.py
|
||||
src/gui/Ui_ALAddTimerTaskDialog.py
|
||||
src/gui/Ui_ALAboutDialog.py
|
||||
|
||||
Main.spec
|
||||
models/*.*
|
||||
drivers/*.*
|
||||
!models/*.md
|
||||
!drivers/*.md
|
||||
!templates/*.md
|
||||
!templates/configs/*.md
|
||||
|
||||
src/gui/resources/ui/Ui_*.py
|
||||
src/gui/resources/translators/qtbase_zh_CN.qm
|
||||
src/gui/resources/ALResource.py
|
||||
|
||||
Main.spec
|
||||
@@ -3,6 +3,7 @@ chcp 65001 >nul
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
cd /d "%~dp0.."
|
||||
cd src/gui/resources
|
||||
|
||||
echo [AutoLibrary compile] 检查翻译文件...
|
||||
if exist translators (
|
||||
@@ -18,12 +19,11 @@ if exist translators (
|
||||
|
||||
pyside6-lrelease "%%f"
|
||||
if !errorlevel! equ 0 (
|
||||
echo [AutoLibrary compile] 翻译文件 "%%f" ✓ 编译成功,输出文件: "!qm_filename!"
|
||||
echo [AutoLibrary compile] 翻译文件 "%%f" 编译成功,输出文件: "!qm_filename!"
|
||||
) else (
|
||||
echo [AutoLibrary compile] 翻译文件 "%%f" ✗ 编译失败
|
||||
echo [AutoLibrary compile] 翻译文件 "%%f" 编译失败
|
||||
)
|
||||
)
|
||||
echo.
|
||||
) else (
|
||||
echo [AutoLibrary compile] 未找到任何 .ts 翻译文件
|
||||
)
|
||||
@@ -52,11 +52,10 @@ for %%f in (*.qrc) do (
|
||||
|
||||
pyside6-rcc "%%f" -o "!output_file!"
|
||||
if !errorlevel! equ 0 (
|
||||
echo [AutoLibrary compile] 文件 "%%f" ✓ 编译成功,输出文件: "!output_file!"
|
||||
echo [AutoLibrary compile] 文件 "%%f" 编译成功,输出文件: "!output_file!"
|
||||
) else (
|
||||
echo [AutoLibrary compile] 文件 "%%f" ✗ 编译失败
|
||||
echo [AutoLibrary compile] 文件 "%%f" 编译失败
|
||||
)
|
||||
echo.
|
||||
)
|
||||
|
||||
echo [AutoLibrary compile] 所有操作完成。
|
||||
Regular → Executable
+7
-11
@@ -1,8 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
cd "$PARENT_DIR"
|
||||
PRJECT_DIR="$SCRIPT_DIR/.."
|
||||
|
||||
cd "$PRJECT_DIR/src/gui/resources"
|
||||
|
||||
echo "[AutoLibrary compile] 检查翻译文件..."
|
||||
if [ -d "translators" ]; then
|
||||
@@ -10,7 +11,6 @@ if [ -d "translators" ]; then
|
||||
ts_files=(*.ts)
|
||||
ts_count=${#ts_files[@]}
|
||||
|
||||
# 如果第一个元素是"*.ts"(表示没有匹配),则数量为0
|
||||
if [ "$ts_count" -eq 1 ] && [ "${ts_files[0]}" = "*.ts" ]; then
|
||||
ts_count=0
|
||||
fi
|
||||
@@ -23,12 +23,11 @@ if [ -d "translators" ]; then
|
||||
echo "[AutoLibrary compile] 正在编译翻译文件: \"$file\" -> \"$qm_file\""
|
||||
|
||||
if pyside6-lrelease "$file"; then
|
||||
echo "[AutoLibrary compile] 翻译文件 \"$file\" ✓ 编译成功,输出文件: \"$qm_file\""
|
||||
echo "[AutoLibrary compile] 翻译文件 \"$file\" 编译成功,输出文件: \"$qm_file\""
|
||||
else
|
||||
echo "[AutoLibrary compile] 翻译文件 \"$file\" ✗ 编译失败"
|
||||
echo "[AutoLibrary compile] 翻译文件 \"$file\" 编译失败"
|
||||
fi
|
||||
done
|
||||
echo
|
||||
else
|
||||
echo "[AutoLibrary compile] 未找到任何 .ts 翻译文件"
|
||||
fi
|
||||
@@ -36,7 +35,6 @@ if [ -d "translators" ]; then
|
||||
else
|
||||
echo "[AutoLibrary compile] 未找到 translators 目录"
|
||||
fi
|
||||
echo
|
||||
|
||||
file_count=$(ls *.qrc 2>/dev/null | wc -l)
|
||||
|
||||
@@ -46,7 +44,6 @@ if [ $file_count -eq 0 ]; then
|
||||
fi
|
||||
|
||||
echo "[AutoLibrary compile] 找到 $file_count 个 .qrc 文件,开始编译..."
|
||||
echo
|
||||
|
||||
for file in *.qrc; do
|
||||
base_name=$(basename "$file" .qrc)
|
||||
@@ -54,11 +51,10 @@ for file in *.qrc; do
|
||||
echo "[AutoLibrary compile] 正在编译: \"$file\" -> \"$output_file\""
|
||||
|
||||
if pyside6-rcc "$file" -o "$output_file"; then
|
||||
echo "[AutoLibrary compile] 文件 \"$file\" ✓ 编译成功,输出文件: \"$output_file\""
|
||||
echo "[AutoLibrary compile] 文件 \"$file\" 编译成功,输出文件: \"$output_file\""
|
||||
else
|
||||
echo "[AutoLibrary compile] 文件 \"$file\" ✗ 编译失败"
|
||||
echo "[AutoLibrary compile] 文件 \"$file\" 编译失败"
|
||||
fi
|
||||
echo
|
||||
done
|
||||
|
||||
echo "[AutoLibrary compile] 所有操作完成。"
|
||||
@@ -3,6 +3,7 @@ chcp 65001 >nul
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
cd /d "%~dp0.."
|
||||
cd src/gui/resources/ui
|
||||
|
||||
set count=0
|
||||
for %%f in (*.ui) do set /a count+=1
|
||||
@@ -23,11 +24,10 @@ for %%f in (*.ui) do (
|
||||
|
||||
pyside6-uic "%%f" -o "!output_file!"
|
||||
if !errorlevel! equ 0 (
|
||||
echo [AutoLibrary compile] 文件 "%%f" ✓ 编译成功,输出文件: "!output_file!"
|
||||
echo [AutoLibrary compile] 文件 "%%f" 编译成功,输出文件: "!output_file!"
|
||||
) else (
|
||||
echo [AutoLibrary compile] 文件 "%%f" ✗ 编译失败
|
||||
echo [AutoLibrary compile] 文件 "%%f" 编译失败
|
||||
)
|
||||
echo.
|
||||
)
|
||||
|
||||
echo [AutoLibrary compile] 所有操作完成。
|
||||
Regular → Executable
+5
-6
@@ -1,8 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
cd "$PARENT_DIR"
|
||||
PRJECT_DIR="$SCRIPT_DIR/.."
|
||||
|
||||
cd "$PRJECT_DIR/src/gui/resources/ui"
|
||||
|
||||
file_count=$(ls *.ui 2>/dev/null | wc -l)
|
||||
|
||||
@@ -12,7 +13,6 @@ if [ $file_count -eq 0 ]; then
|
||||
fi
|
||||
|
||||
echo "[AutoLibrary compile] 找到 $file_count 个 .ui 文件,开始编译..."
|
||||
echo
|
||||
|
||||
for file in *.ui; do
|
||||
base_name=$(basename "$file" .ui)
|
||||
@@ -20,11 +20,10 @@ for file in *.ui; do
|
||||
echo "[AutoLibrary compile] 正在编译: \"$file\" -> \"$output_file\""
|
||||
|
||||
if pyside6-uic "$file" -o "$output_file"; then
|
||||
echo "[AutoLibrary compile] 文件 \"$file\" ✓ 编译成功,输出文件: \"$output_file\""
|
||||
echo "[AutoLibrary compile] 文件 \"$file\" 编译成功,输出文件: \"$output_file\""
|
||||
else
|
||||
echo "[AutoLibrary compile] 文件 \"$file\" ✗ 编译失败"
|
||||
echo "[AutoLibrary compile] 文件 \"$file\" 编译失败"
|
||||
fi
|
||||
echo
|
||||
done
|
||||
|
||||
echo "[AutoLibrary compile] 所有操作完成。"
|
||||
@@ -0,0 +1 @@
|
||||
This folder is used to store the batch scripts.
|
||||
@@ -1 +0,0 @@
|
||||
For more infomation, please visit our website: https://www.autolibrary.cv
|
||||
@@ -1 +0,0 @@
|
||||
This folder is used to store the browser driver using by selenium.
|
||||
@@ -0,0 +1 @@
|
||||
This folder is used to store the browser drivers using by selenium.
|
||||
@@ -0,0 +1,3 @@
|
||||
This folder is used to store the manuals.
|
||||
|
||||
Our manuals are available at https://www.autolibrary.top/manuals
|
||||
@@ -1 +0,0 @@
|
||||
This folder is used to store the model using by ddddocr.
|
||||
@@ -0,0 +1 @@
|
||||
This folder is used to store the models using by ddddocr.
|
||||
@@ -2,7 +2,7 @@
|
||||
# AutoLibrary
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
[](https://github.com/KenanZhu/AutoLibrary)
|
||||

|
||||
@@ -10,7 +10,7 @@
|
||||
[](https://github.com/KenanZhu/AutoLibrary/releases)
|
||||

|
||||
|
||||
了解更多请访问 [_AutoLibrary 网站_](http://autolibrary.cv)
|
||||
了解更多请访问 [_AutoLibrary 网站_](http://www.autolibrary.top)
|
||||
|
||||
---
|
||||
|
||||
@@ -22,7 +22,42 @@
|
||||
4. 批量操作 - 支持同时预约多个用户,可以指定当前需要跳过的用户,并将用户分成多个组
|
||||
5. 定时任务 - 使用内置定时任务管理,添加定时任务,指定时间后按当前预约信息自动运行
|
||||
|
||||
*1,2,3 的具体操作方法和注意事项请访问我们的 [帮助手册](https://autolibrary.cv/docs/manual_lists.html)*
|
||||
*1,2,3 的具体操作方法和注意事项请访问我们的 [帮助手册](https://www.autolibrary.top/manuals)*
|
||||
|
||||
### 如何使用
|
||||
|
||||
1. 下载最新版本的 [AutoLibrary 压缩包](https://github.com/KenanZhu/AutoLibrary/releases)。
|
||||
2. 解压下载的文件到任意目录。
|
||||
3. 下载对应浏览器的驱动文件,并在配置界面的运行配置选项卡对应位置选择你下载好的浏览器驱动
|
||||
4. 运行 `AutoLibrary.exe` 文件。
|
||||
5. 按照提示操作即可。
|
||||
|
||||
*注意 1*: 关于浏览器驱动的下载和其它相关问题,请参考我们的 [帮助手册](https://www.autolibrary.top/manuals) 中对应软件版本的内容。
|
||||
|
||||
#### 平台支持 & 编译步骤
|
||||
|
||||
本工具目前仅支持 Windows 平台,由于使用 PySide6 库开发,理论上是可以自行编译并在 Linux 和 macOS 上运行,这里提供简单的编译步骤:
|
||||
|
||||
1. 确保系统安装了 Python 3.13 版本 (推荐,过低或高版本会导致兼容问题)。
|
||||
2. 安装 pyside6 selenium ddddocr 库,命令为 `pip install pyside6 selenium ddddocr`。
|
||||
3. 在 `batchs` 目录下运行 `compile_ui.bat` (linux 和 macOS 系统使用 `compile_ui.sh`) 文件来编译 Qt 的 UI 文件。
|
||||
4. 在上一步相同目录内运行 `compile_rc.bat` (linux 和 macOS 系统使用 `compile_rc.sh`) 文件来编译 Qt 的资源文件。
|
||||
5. 待上述步骤完成后,运行 `src/Main.py` 文件即可。
|
||||
|
||||
*注意 1*:如果 python 使用的是虚拟环境,请在虚拟环境安装依赖后,在激活的虚拟环境终端中使用 `cd src/gui/batchs` 命令切换到 `batchs` 目录下,再运行编译脚本。否则会提示缺少必要的 Qt PySide 依赖库。
|
||||
|
||||
*注意 2*:由于 ddddocr 的代码版本问题,其中 `__init__.py` 文件中的函数 `def classification(self, img: bytes):` 中的 `image.resize` 方法传入了不符合当前 pillow 版本的 `resample` 参数 `Image.ANTIALIAS`,该重采样常量已经在 10.0.0 版中删除 [1](@ref)。请将 `image.resize` 方法中的参数替换为 `resample=Image.Resampling.LANCZOS`,具体函数如下:
|
||||
```python
|
||||
def classification(self, img: bytes):
|
||||
image = Image.open(io.BytesIO(img))
|
||||
image = image.resize((int(image.size[0]*(64/image.size[1])), 64), Image.ANTIALIAS).convert('L')
|
||||
^^^^^
|
||||
请将上述参数替换为 `Image.Resampling.LANCZOS`
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
[1](@ref):[pillow 中已经删除或已经弃用的常量](https://pillow.ac.cn/en/stable/deprecations.html#constants)
|
||||
|
||||
### 注意事项
|
||||
|
||||
@@ -43,41 +78,6 @@
|
||||
|
||||
定时任务会在指定的时间自动运行,运行时会根据当前预约信息进行操作。一般情况下不建议设置两个运行开始时间比较接近的定时任务,否则后一个任务会等待前一个任务完成后才会运行,按照队列的顺序执行。
|
||||
|
||||
### 如何使用
|
||||
|
||||
1. 下载最新版本的 [AutoLibrary 压缩包](https://github.com/KenanZhu/AutoLibrary/releases)。
|
||||
2. 解压下载的文件到任意目录。
|
||||
3. 下载对应浏览器的驱动文件,并在配置界面的运行配置选项卡对应位置选择你下载好的浏览器驱动
|
||||
4. 运行 `AutoLibrary.exe` 文件。
|
||||
5. 按照提示操作即可。
|
||||
|
||||
*注意 1*: 关于浏览器驱动的下载和其它相关问题,请参考我们的 [帮助手册](https://autolibrary.cv/docs/manual_lists.html) 中对应软件版本的内容。
|
||||
|
||||
#### 平台支持 & 编译步骤
|
||||
|
||||
本工具目前仅支持 Windows 平台,由于使用 PySide6 库开发,理论上是可以自行编译并在 Linux 和 macOS 上运行,这里提供简单的编译步骤:
|
||||
|
||||
1. 确保系统安装了 Python 3.13 版本 (推荐,过低或高版本会导致兼容问题)。
|
||||
2. 安装 pyside6 selenium ddddocr 库,命令为 `pip install pyside6 selenium ddddocr`。
|
||||
3. 在 `src/gui/batchs` 目录下运行 `compile_ui.bat` (linux 和 macOS 系统使用 `compile_ui.sh`) 文件来编译 Qt 的 UI 文件。
|
||||
4. 在上一步相同目录内运行 `compile_rc.bat` (linux 和 macOS 系统使用 `compile_rc.sh`) 文件来编译 Qt 的资源文件。
|
||||
5. 待上述步骤完成后,运行 `src/Main.py` 文件即可。
|
||||
|
||||
*注意 1*:如果 python 使用的是虚拟环境,请在虚拟环境安装依赖后,在激活的虚拟环境终端中使用 `cd src/gui/batchs` 命令切换到 `batchs` 目录下,再运行编译脚本。否则会提示缺少必要的 Qt PySide 依赖库。
|
||||
|
||||
*注意 2*:由于 ddddocr 的代码版本问题,其中 `__init__.py` 文件中的函数 `def classification(self, img: bytes):` 中的 `image.resize` 方法传入了不符合当前 pillow 版本的 `resample` 参数 `Image.ANTIALIAS`,该重采样常量已经在 10.0.0 版中删除 [1](@ref)。请将 `image.resize` 方法中的参数替换为 `resample=Image.Resampling.LANCZOS`,具体函数如下:
|
||||
```python
|
||||
def classification(self, img: bytes):
|
||||
image = Image.open(io.BytesIO(img))
|
||||
image = image.resize((int(image.size[0]*(64/image.size[1])), 64), Image.ANTIALIAS).convert('L')
|
||||
^^^^^
|
||||
请将上述参数替换为 `Image.Resampling.LANCZOS`
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
[1](@ref):[pillow 中已经删除或已经弃用的常量](https://pillow.ac.cn/en/stable/deprecations.html#constants)
|
||||
|
||||
### Q&A
|
||||
|
||||
#### 为什么开发这个工具?
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ from PySide6.QtCore import QTranslator
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
from gui.ALMainWindow import ALMainWindow
|
||||
from gui import AutoLibraryResource
|
||||
from gui.resources import ALResource
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
+2
-14
@@ -7,8 +7,8 @@ This software is provided "as is", without any warranty of any kind.
|
||||
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||
See the LICENSE file for details.
|
||||
"""
|
||||
import time
|
||||
import queue
|
||||
import datetime
|
||||
|
||||
|
||||
class MsgBase:
|
||||
@@ -53,7 +53,7 @@ class MsgBase:
|
||||
msg: str
|
||||
):
|
||||
|
||||
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
self._output_queue.put(f"{timestamp}-[{self._class_name:<15}] : {msg}")
|
||||
|
||||
|
||||
@@ -67,15 +67,3 @@ class MsgBase:
|
||||
return msg
|
||||
except queue.Empty:
|
||||
return None
|
||||
|
||||
|
||||
def _inputMsg(
|
||||
self,
|
||||
timeout: float = 1.0
|
||||
) -> bool:
|
||||
|
||||
try:
|
||||
self._input_queue.get(timeout=timeout)
|
||||
return True
|
||||
except queue.Empty:
|
||||
return False
|
||||
|
||||
@@ -7,11 +7,10 @@ This software is provided "as is", without any warranty of any kind.
|
||||
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||
See the LICENSE file for details.
|
||||
"""
|
||||
import sys
|
||||
import platform
|
||||
|
||||
from PySide6.QtGui import (
|
||||
QIcon
|
||||
QIcon, QFont
|
||||
)
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QApplication
|
||||
@@ -23,9 +22,9 @@ from PySide6.QtCore import (
|
||||
from gui.ALVersionInfo import (
|
||||
AL_VERSION, AL_COMMIT_SHA, AL_COMMIT_DATE, AL_BUILD_DATE
|
||||
)
|
||||
from gui.Ui_ALAboutDialog import Ui_ALAboutDialog
|
||||
from gui.resources.ui.Ui_ALAboutDialog import Ui_ALAboutDialog
|
||||
|
||||
from gui import AutoLibraryResource
|
||||
from gui.resources import ALResource
|
||||
|
||||
|
||||
class ALAboutDialog(QDialog, Ui_ALAboutDialog):
|
||||
@@ -47,8 +46,11 @@ class ALAboutDialog(QDialog, Ui_ALAboutDialog):
|
||||
|
||||
self.LogoIconLabel.setPixmap(QIcon(":/res/icon/icons/AutoLibrary_32x32.ico").pixmap(48, 48))
|
||||
info_text = self.generateAboutText()
|
||||
self.AboutInfoEdit.setHtml(info_text)
|
||||
self.AboutInfoEdit.setTextInteractionFlags(Qt.TextBrowserInteraction)
|
||||
self.AboutInfoBrowser.setHtml(info_text)
|
||||
browser_font = self.AboutInfoBrowser.font()
|
||||
browser_font.setFamily("Courier New")
|
||||
self.AboutInfoBrowser.setFont(browser_font)
|
||||
self.AboutInfoBrowser.setTextInteractionFlags(Qt.TextBrowserInteraction)
|
||||
|
||||
|
||||
def connectSignals(
|
||||
@@ -81,7 +83,7 @@ System architecture: {os_info['architecture']}<br>
|
||||
<h4>Project Information:</h4>
|
||||
License: MIT License<br>
|
||||
Project repository: <a href="https://www.github.com/KenanZhu/AutoLibrary" style="text-decoration: none;">https://www.github.com/KenanZhu/AutoLibrary</a><br>
|
||||
Project website: <a href="https://www.autolibrary.cv/" style="text-decoration: none;">https://www.autolibrary.cv/</a><br>
|
||||
Project website: <a href="https://www.autolibrary.top" style="text-decoration: none;">https://www.autolibrary.top</a><br>
|
||||
|
||||
<h4>Author Information:</h4>
|
||||
Developer: KenanZhu<br>
|
||||
@@ -138,7 +140,7 @@ GitHub: <a href="https://www.github.com/KenanZhu" style="text-decoration: none;"
|
||||
self
|
||||
):
|
||||
|
||||
about_text = self.AboutInfoEdit.toPlainText()
|
||||
about_text = self.AboutInfoBrowser.toPlainText()
|
||||
clipboard = QApplication.clipboard()
|
||||
clipboard.setText(about_text)
|
||||
original_text = self.CopyButton.text()
|
||||
|
||||
+32
-53
@@ -14,18 +14,17 @@ from PySide6.QtCore import (
|
||||
Qt, Signal, Slot, QTime, QDate, QDir, QFileInfo
|
||||
)
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget, QLineEdit, QMessageBox, QFileDialog,
|
||||
QDialog, QWidget, QLineEdit, QMessageBox, QFileDialog,
|
||||
QTreeWidgetItem, QMenu, QInputDialog
|
||||
)
|
||||
from PySide6.QtGui import (
|
||||
QCloseEvent, QAction
|
||||
)
|
||||
|
||||
from gui.Ui_ALConfigWidget import Ui_ALConfigWidget
|
||||
from gui.ALSeatMapWidget import ALSeatMapWidget
|
||||
from gui.ALSeatMapTable import seats_maps
|
||||
from gui.ALUserTreeWidget import TreeItemType
|
||||
from gui.ALUserTreeWidget import ALUserTreeWidget
|
||||
from gui.resources.ui.Ui_ALConfigWidget import Ui_ALConfigWidget
|
||||
from gui.ALSeatMapSelectDialog import ALSeatMapSelectDialog
|
||||
from gui.ALSeatMapTable import ALSeatMapTable
|
||||
from gui.ALUserTreeWidget import ALUserTreeWidget, ALUserTreeItemType
|
||||
|
||||
from utils.ConfigReader import ConfigReader
|
||||
from utils.ConfigWriter import ConfigWriter
|
||||
@@ -47,7 +46,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
super().__init__(parent)
|
||||
self.__config_paths = config_paths
|
||||
self.__config_data = {"run": {}, "user": {}}
|
||||
self.__seat_map_widget = None
|
||||
|
||||
self.setupUi(self)
|
||||
self.modifyUi()
|
||||
@@ -196,7 +194,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
which: str
|
||||
) -> bool:
|
||||
|
||||
msg = ""
|
||||
msg = "" # no use for now
|
||||
is_success = True
|
||||
if which == "run":
|
||||
run_config_path = self.__config_paths[which]
|
||||
@@ -224,12 +222,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
self.__config_data[which] = self.loadUserConfig(user_config_path)
|
||||
if self.__config_data[which] is None:
|
||||
is_success = False
|
||||
if msg:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"提示 - AutoLibrary",
|
||||
f"配置文件初始化完成: \n{msg}"
|
||||
)
|
||||
return is_success
|
||||
|
||||
|
||||
@@ -625,12 +617,12 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
try:
|
||||
if "groups" in user_config_data:
|
||||
for group_config in user_config_data["groups"]:
|
||||
group_item = QTreeWidgetItem(self.UserTreeWidget, TreeItemType.GROUP.value)
|
||||
group_item = QTreeWidgetItem(self.UserTreeWidget, ALUserTreeItemType.GROUP.value)
|
||||
group_item.setText(0, group_config["name"])
|
||||
group_item.setFlags(group_item.flags() | Qt.ItemIsEditable)
|
||||
group_item.setCheckState(1, Qt.Checked if group_config.get("enabled", True) else Qt.Unchecked)
|
||||
for user_config in group_config["users"]:
|
||||
user_item = QTreeWidgetItem(group_item, TreeItemType.USER.value)
|
||||
user_item = QTreeWidgetItem(group_item, ALUserTreeItemType.USER.value)
|
||||
user_item.setText(0, user_config["username"])
|
||||
user_item.setText(1, "" if user_config.get("enabled", True) else "跳过")
|
||||
user_item.setData(0, Qt.UserRole, user_config)
|
||||
@@ -647,7 +639,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
) -> QTreeWidgetItem:
|
||||
|
||||
self.UserTreeWidget.itemChanged.disconnect(self.onUserTreeWidgetItemChanged)
|
||||
group_item = QTreeWidgetItem(self.UserTreeWidget, TreeItemType.GROUP.value)
|
||||
group_item = QTreeWidgetItem(self.UserTreeWidget, ALUserTreeItemType.GROUP.value)
|
||||
if not group_name:
|
||||
group_name = f"新分组-{self.UserTreeWidget.topLevelItemCount()}"
|
||||
group_item.setText(0, group_name)
|
||||
@@ -667,7 +659,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
current_item = self.UserTreeWidget.currentItem()
|
||||
if current_item is None:
|
||||
group_item = self.addGroup()
|
||||
if group_item.type() == TreeItemType.USER.value:
|
||||
if group_item.type() == ALUserTreeItemType.USER.value:
|
||||
group_item = group_item.parent()
|
||||
if group_item.checkState(1) == Qt.CheckState.Unchecked:
|
||||
return None
|
||||
@@ -701,7 +693,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
}
|
||||
}
|
||||
self.UserTreeWidget.itemChanged.disconnect(self.onUserTreeWidgetItemChanged)
|
||||
user_item = QTreeWidgetItem(group_item, TreeItemType.USER.value)
|
||||
user_item = QTreeWidgetItem(group_item, ALUserTreeItemType.USER.value)
|
||||
user_item.setText(0, new_user["username"])
|
||||
user_item.setText(1, "")
|
||||
user_item.setData(0, Qt.UserRole, new_user)
|
||||
@@ -720,7 +712,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
|
||||
if user_item is None:
|
||||
return
|
||||
if user_item.type() != TreeItemType.USER.value:
|
||||
if user_item.type() != ALUserTreeItemType.USER.value:
|
||||
return
|
||||
parent_item = user_item.parent()
|
||||
index = parent_item.indexOfChild(user_item)
|
||||
@@ -736,7 +728,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
|
||||
if group_item is None:
|
||||
return
|
||||
if group_item.type() != TreeItemType.GROUP.value:
|
||||
if group_item.type() != ALUserTreeItemType.GROUP.value:
|
||||
return
|
||||
index = self.UserTreeWidget.indexOfTopLevelItem(group_item)
|
||||
self.UserTreeWidget.takeTopLevelItem(index)
|
||||
@@ -761,7 +753,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
if not ok or not new_name:
|
||||
return
|
||||
item.setText(0, new_name)
|
||||
if item.type() == TreeItemType.GROUP.value:
|
||||
if item.type() == ALUserTreeItemType.GROUP.value:
|
||||
item.setText(0, new_name)
|
||||
else:
|
||||
user = item.data(0, Qt.UserRole)
|
||||
@@ -792,20 +784,6 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
self.RoomComboBox.addItems(self.__floor_room_map[floor])
|
||||
self.RoomComboBox.setCurrentIndex(0)
|
||||
|
||||
@Slot()
|
||||
def onSeatMapWidgetClosed(
|
||||
self,
|
||||
selected_seats: list[str]
|
||||
):
|
||||
|
||||
self.__seat_map_widget.seatMapWidgetClosed.disconnect(self.onSeatMapWidgetClosed)
|
||||
self.__seat_map_widget.deleteLater()
|
||||
self.__seat_map_widget = None
|
||||
if len(selected_seats) == 0:
|
||||
self.SeatIDEdit.clear() # no selected seat, we clear the edit
|
||||
return
|
||||
self.SeatIDEdit.setText(",".join(selected_seats))
|
||||
|
||||
@Slot()
|
||||
def onSelectSeatsButtonClicked(
|
||||
self
|
||||
@@ -815,18 +793,19 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
room = self.RoomComboBox.currentText()
|
||||
floor_idx = self.__floor_rmap[floor]
|
||||
room_idx = self.__room_rmap[room]
|
||||
if self.__seat_map_widget is None:
|
||||
self.__seat_map_widget = ALSeatMapWidget(
|
||||
self,
|
||||
floor,
|
||||
room,
|
||||
seats_maps[floor_idx][room_idx]
|
||||
)
|
||||
self.__seat_map_widget.seatMapWidgetClosed.connect(self.onSeatMapWidgetClosed)
|
||||
self.__seat_map_widget.show()
|
||||
self.__seat_map_widget.raise_()
|
||||
self.__seat_map_widget.activateWindow()
|
||||
self.__seat_map_widget.selectSeats(self.SeatIDEdit.text().split(","))
|
||||
dialog = ALSeatMapSelectDialog(
|
||||
self,
|
||||
floor,
|
||||
room,
|
||||
ALSeatMapTable[floor_idx][room_idx]
|
||||
)
|
||||
dialog.selectSeats(self.SeatIDEdit.text().split(","))
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
selected_seats = dialog.getSelectedSeats()
|
||||
if len(selected_seats) == 0:
|
||||
self.SeatIDEdit.clear()
|
||||
return
|
||||
self.SeatIDEdit.setText(",".join(dialog.getSelectedSeats()))
|
||||
|
||||
@Slot()
|
||||
def onUserTreeWidgetCurrentItemChanged(
|
||||
@@ -838,7 +817,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
# cant effectively update the data of each user, due to the
|
||||
# possiblity of frequency edit. we just let the QListWidget
|
||||
# help us.
|
||||
if previous and previous.type() == TreeItemType.USER.value:
|
||||
if previous and previous.type() == ALUserTreeItemType.USER.value:
|
||||
user = self.collectUserFromUserInfoWidget()
|
||||
if user:
|
||||
self.UsernameEdit.textEdited.disconnect()
|
||||
@@ -849,7 +828,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
if current is None:
|
||||
self.initilizeUserInfoWidget()
|
||||
return
|
||||
if current.type() == TreeItemType.USER.value:
|
||||
if current.type() == ALUserTreeItemType.USER.value:
|
||||
user = current.data(0, Qt.UserRole)
|
||||
if user:
|
||||
self.setUserToWidget(user)
|
||||
@@ -868,7 +847,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
return
|
||||
if column != 1:
|
||||
return
|
||||
if item.type() == TreeItemType.GROUP.value:
|
||||
if item.type() == ALUserTreeItemType.GROUP.value:
|
||||
is_checked = item.checkState(1) == Qt.CheckState.Checked
|
||||
for i in range(item.childCount()):
|
||||
child = item.child(i)
|
||||
@@ -933,7 +912,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
menu = QMenu(self.UserTreeWidget)
|
||||
if current_item is None:
|
||||
self.showTreeMenu(menu)
|
||||
elif current_item.type() == TreeItemType.GROUP.value:
|
||||
elif current_item.type() == ALUserTreeItemType.GROUP.value:
|
||||
self.showGroupMenu(menu, current_item)
|
||||
else:
|
||||
self.showUserMenu(menu, current_item)
|
||||
@@ -1115,7 +1094,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||
):
|
||||
|
||||
current_item = self.UserTreeWidget.currentItem()
|
||||
if current_item and current_item.type() == TreeItemType.USER.value:
|
||||
if current_item and current_item.type() == ALUserTreeItemType.USER.value:
|
||||
self.UserTreeWidget.setCurrentItem(None)
|
||||
if self.saveConfigs(
|
||||
self.__config_paths["run"],
|
||||
|
||||
+22
-39
@@ -21,16 +21,18 @@ from PySide6.QtGui import (
|
||||
QTextCursor, QCloseEvent, QFont, QIcon, QDesktopServices
|
||||
)
|
||||
|
||||
from gui.Ui_ALMainWindow import Ui_ALMainWindow
|
||||
from base.MsgBase import MsgBase
|
||||
|
||||
from gui.resources.ui.Ui_ALMainWindow import Ui_ALMainWindow
|
||||
from gui.ALConfigWidget import ALConfigWidget
|
||||
from gui.ALTimerTaskWidget import ALTimerTaskWidget
|
||||
from gui.ALTimerTaskManageWidget import ALTimerTaskManageWidget
|
||||
from gui.ALAboutDialog import ALAboutDialog
|
||||
from gui.ALMainWorkers import TimerTaskWorker, AutoLibWorker
|
||||
|
||||
from gui import AutoLibraryResource
|
||||
from gui.resources import ALResource
|
||||
|
||||
|
||||
class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow):
|
||||
|
||||
timerTaskIsRunning = Signal(dict)
|
||||
timerTaskIsExecuted = Signal(dict)
|
||||
@@ -40,10 +42,8 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
self
|
||||
):
|
||||
|
||||
super().__init__()
|
||||
self.__class_name = self.__class__.__name__
|
||||
self.__input_queue = queue.Queue()
|
||||
self.__output_queue = queue.Queue()
|
||||
MsgBase.__init__(self, queue.Queue(), queue.Queue())
|
||||
QMainWindow.__init__(self)
|
||||
self.__timer_task_queue = queue.Queue()
|
||||
script_path = sys.executable
|
||||
script_dir = QFileInfo(script_path).absoluteDir()
|
||||
@@ -77,12 +77,12 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
self.AboutAction.triggered.connect(self.onAboutActionTriggered)
|
||||
|
||||
# initialize timer task widget, but not show it
|
||||
self.__alTimerTaskWidget = ALTimerTaskWidget(self, self.__config_paths["timer_task"])
|
||||
self.__alTimerTaskWidget = ALTimerTaskManageWidget(self, self.__config_paths["timer_task"])
|
||||
self.timerTaskIsRunning.connect(self.__alTimerTaskWidget.onTimerTaskIsRunning)
|
||||
self.timerTaskIsExecuted.connect(self.__alTimerTaskWidget.onTimerTaskIsExecuted)
|
||||
self.timerTaskIsError.connect(self.__alTimerTaskWidget.onTimerTaskIsError)
|
||||
self.__alTimerTaskWidget.timerTaskIsReady.connect(self.onTimerTaskIsReady)
|
||||
self.__alTimerTaskWidget.timerTaskWidgetClosed.connect(self.onTimerTaskWidgetClosed)
|
||||
self.__alTimerTaskWidget.timerTaskManageWidgetClosed.connect(self.onTimerTaskWidgetClosed)
|
||||
self.__alTimerTaskWidget.setWindowFlags(Qt.WindowType.Window|Qt.WindowType.WindowCloseButtonHint)
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
self
|
||||
):
|
||||
|
||||
url = QUrl("https://www.autolibrary.cv/docs/manual_lists.html")
|
||||
url = QUrl("https://www.autolibrary.top/manuals")
|
||||
QDesktopServices.openUrl(url)
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
self.timerTaskIsRunning.emit(timer_task)
|
||||
self.__timer_task_timer.stop()
|
||||
self.__is_running_timer_task = True
|
||||
self.setControlButtons(True, True, False)
|
||||
self.setControlButtons(None, True, False)
|
||||
if not timer_task["silent"]:
|
||||
self.TrayIcon.showMessage(
|
||||
"定时任务 - AutoLibrary",
|
||||
@@ -237,8 +237,8 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
self.showNormal()
|
||||
self.__current_timer_task_thread = TimerTaskWorker(
|
||||
timer_task,
|
||||
self.__input_queue,
|
||||
self.__output_queue,
|
||||
self._input_queue,
|
||||
self._output_queue,
|
||||
self.__config_paths
|
||||
)
|
||||
self.__current_timer_task_thread.TimerTaskWorkerIsFinished.connect(self.onTimerTaskFinished)
|
||||
@@ -263,23 +263,6 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
if start_button_enabled is not None:
|
||||
self.StartButton.setEnabled(start_button_enabled)
|
||||
|
||||
@Slot()
|
||||
def showMsg(
|
||||
self,
|
||||
msg: str
|
||||
):
|
||||
|
||||
self.__output_queue.put(f"[{self.__class_name:<15}] >>> : {msg}")
|
||||
|
||||
@Slot()
|
||||
def showTrace(
|
||||
self,
|
||||
msg: str
|
||||
):
|
||||
|
||||
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
self.__output_queue.put(f"{timestamp}-[{self.__class_name:<15}] : {msg}")
|
||||
|
||||
@Slot()
|
||||
def pollMsgQueue(
|
||||
self
|
||||
@@ -287,7 +270,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
|
||||
try:
|
||||
while True:
|
||||
msg = self.__output_queue.get_nowait()
|
||||
msg = self._output_queue.get_nowait()
|
||||
self.appendToTextEdit(msg)
|
||||
except queue.Empty:
|
||||
pass
|
||||
@@ -341,7 +324,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
QSystemTrayIcon.MessageIcon.Information,
|
||||
1000
|
||||
)
|
||||
self.showTrace(
|
||||
self._showTrace(
|
||||
f"定时任务 {timer_task['name']} 执行{'失败' if is_error else '完成'}, uuid: {timer_task['task_uuid']}"
|
||||
)
|
||||
if not is_error:
|
||||
@@ -383,8 +366,8 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
self.setControlButtons(None, True, False)
|
||||
if self.__auto_lib_thread is None:
|
||||
self.__auto_lib_thread = AutoLibWorker(
|
||||
self.__input_queue,
|
||||
self.__output_queue,
|
||||
self._input_queue,
|
||||
self._output_queue,
|
||||
self.__config_paths
|
||||
)
|
||||
self.__auto_lib_thread.AutoLibWorkerIsFinished.connect(self.onStopButtonClicked)
|
||||
@@ -397,9 +380,9 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
):
|
||||
|
||||
if self.__auto_lib_thread:
|
||||
self.showTrace("正在停止操作......")
|
||||
self._showTrace("正在停止操作......")
|
||||
self.__auto_lib_thread.wait(2000)
|
||||
self.showTrace("操作已停止")
|
||||
self._showTrace("操作已停止")
|
||||
self.__auto_lib_thread.AutoLibWorkerIsFinished.disconnect(self.onStopButtonClicked)
|
||||
self.__auto_lib_thread.AutoLibWorkerFinishedWithError.disconnect(self.onStopButtonClicked)
|
||||
self.__auto_lib_thread.deleteLater()
|
||||
@@ -414,6 +397,6 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||
msg = self.MessageEdit.text().strip()
|
||||
if not msg:
|
||||
return
|
||||
self.showMsg(msg)
|
||||
self.__input_queue.put(msg) # put message to input queue
|
||||
self._showMsg(msg)
|
||||
self._input_queue.put(msg) # put message to input queue
|
||||
self.MessageEdit.clear()
|
||||
@@ -20,7 +20,7 @@ from operators.AutoLib import AutoLib
|
||||
from utils.ConfigReader import ConfigReader
|
||||
|
||||
|
||||
class AutoLibWorker(QThread, MsgBase):
|
||||
class AutoLibWorker(MsgBase, QThread):
|
||||
|
||||
AutoLibWorkerIsFinished = Signal()
|
||||
AutoLibWorkerFinishedWithError = Signal()
|
||||
@@ -32,8 +32,8 @@ class AutoLibWorker(QThread, MsgBase):
|
||||
config_paths: dict
|
||||
):
|
||||
|
||||
super().__init__(input_queue=input_queue, output_queue=output_queue)
|
||||
|
||||
MsgBase.__init__(self, input_queue, output_queue)
|
||||
QThread.__init__(self)
|
||||
self.__config_paths = config_paths
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (c) 2025 - 2026 KenanZhu.
|
||||
All rights reserved.
|
||||
|
||||
This software is provided "as is", without any warranty of any kind.
|
||||
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||
See the LICENSE file for details.
|
||||
"""
|
||||
from PySide6.QtCore import (
|
||||
Qt, Slot, Signal
|
||||
)
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QLabel, QHBoxLayout, QVBoxLayout,
|
||||
QPushButton,
|
||||
)
|
||||
from PySide6.QtGui import (
|
||||
QCloseEvent
|
||||
)
|
||||
from gui.ALSeatMapView import ALSeatMapView
|
||||
|
||||
|
||||
class ALSeatMapSelectDialog(QDialog):
|
||||
|
||||
seatMapSelectDialogClosed = Signal(list)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: QDialog = None,
|
||||
floor: str = "",
|
||||
room: str = "",
|
||||
seats_data: str = ""
|
||||
):
|
||||
|
||||
super().__init__(parent)
|
||||
self.__floor = floor
|
||||
self.__room = room
|
||||
self.__seats_data = seats_data
|
||||
self.__confirmed = False
|
||||
|
||||
self.setupUi()
|
||||
self.connectSignals()
|
||||
|
||||
|
||||
def setupUi(
|
||||
self
|
||||
):
|
||||
|
||||
self.setModal(True)
|
||||
self.setMinimumSize(800, 600)
|
||||
self.resize(800, 600)
|
||||
self.setWindowTitle(f"选择楼层座位 - AutoLibrary")
|
||||
|
||||
self.SeatMapWidgetMainLayout = QVBoxLayout(self)
|
||||
self.SeatMapWidgetMainLayout.setContentsMargins(5, 5, 5, 5)
|
||||
self.SeatMapWidgetMainLayout.setSpacing(5)
|
||||
self.TitleLabel = QLabel(f"楼层座位分布图: {self.__floor}-{self.__room}")
|
||||
self.TitleLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.TitleLabel.setStyleSheet("font-size: 16px; font-weight: bold; margin: 10px;")
|
||||
self.SeatMapWidgetMainLayout.addWidget(self.TitleLabel)
|
||||
|
||||
self.SeatMapGraphicsView = ALSeatMapView(None, self.__seats_data)
|
||||
self.SeatMapWidgetMainLayout.addWidget(self.SeatMapGraphicsView)
|
||||
|
||||
self.TipsLabel = QLabel(
|
||||
" 点击座位进行选择/取消选择, 最多选择1个座位 \n"
|
||||
" [操作方法: Ctrl+鼠标滚轮缩放 | 滚轮/拖拽/方向键 移动]"
|
||||
)
|
||||
self.TipsLabel.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||
self.TipsLabel.setStyleSheet("color: #666; margin: 5px;")
|
||||
self.SeatMapWidgetMainLayout.addWidget(self.TipsLabel)
|
||||
|
||||
self.ConfirmButton = QPushButton("确认")
|
||||
self.ConfirmButton.setFixedSize(80, 25)
|
||||
self.ConfirmButton.setAutoDefault(True)
|
||||
self.ConfirmButton.setDefault(True)
|
||||
self.CancelButton = QPushButton("取消")
|
||||
self.CancelButton.setFixedSize(80, 25)
|
||||
self.SeatMapWidgetControlLayout = QHBoxLayout()
|
||||
self.SeatMapWidgetControlLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.SeatMapWidgetControlLayout.setSpacing(5)
|
||||
self.SeatMapWidgetControlLayout.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
self.SeatMapWidgetControlLayout.addWidget(self.CancelButton)
|
||||
self.SeatMapWidgetControlLayout.addWidget(self.ConfirmButton)
|
||||
self.SeatMapWidgetMainLayout.addLayout(self.SeatMapWidgetControlLayout)
|
||||
|
||||
|
||||
def connectSignals(
|
||||
self
|
||||
):
|
||||
|
||||
self.ConfirmButton.clicked.connect(self.onConfirmButtonClicked)
|
||||
self.CancelButton.clicked.connect(self.onCancelButtonClicked)
|
||||
|
||||
|
||||
def showEvent(
|
||||
self,
|
||||
event
|
||||
):
|
||||
|
||||
result = super().showEvent(event)
|
||||
|
||||
screen_rect = self.screen().geometry()
|
||||
target_pos = self.parent().geometry().center()
|
||||
target_pos.setX(target_pos.x() - self.width()//2)
|
||||
target_pos.setY(target_pos.y() - self.height()//2)
|
||||
if target_pos.x() < 0:
|
||||
target_pos.setX(0)
|
||||
if target_pos.x() + self.width() > screen_rect.width():
|
||||
target_pos.setX(screen_rect.width() - self.width())
|
||||
if target_pos.y() < 0:
|
||||
target_pos.setY(0)
|
||||
if target_pos.y() + self.height() > screen_rect.height():
|
||||
target_pos.setY(screen_rect.height() - self.height())
|
||||
self.move(target_pos)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def closeEvent(
|
||||
self,
|
||||
event: QCloseEvent
|
||||
):
|
||||
|
||||
if not self.__confirmed:
|
||||
self.clearSelections()
|
||||
self.reject()
|
||||
else:
|
||||
self.accept()
|
||||
self.seatMapSelectDialogClosed.emit(self.getSelectedSeats())
|
||||
super().closeEvent(event)
|
||||
|
||||
|
||||
def selectSeat(
|
||||
self,
|
||||
seat_number: str
|
||||
):
|
||||
|
||||
self.SeatMapGraphicsView.selectSeat(seat_number)
|
||||
|
||||
|
||||
def selectSeats(
|
||||
self,
|
||||
seat_numbers: list[str]
|
||||
) -> bool:
|
||||
|
||||
return self.SeatMapGraphicsView.selectSeats(seat_numbers)
|
||||
|
||||
|
||||
def getSelectedSeats(
|
||||
self
|
||||
) -> list[str]:
|
||||
|
||||
return self.SeatMapGraphicsView.getSelectedSeats()
|
||||
|
||||
|
||||
def clearSelections(
|
||||
self
|
||||
):
|
||||
|
||||
self.SeatMapGraphicsView.clearSelections()
|
||||
|
||||
@Slot()
|
||||
def onConfirmButtonClicked(
|
||||
self
|
||||
):
|
||||
|
||||
self.__confirmed = True
|
||||
self.accept()
|
||||
|
||||
@Slot()
|
||||
def onCancelButtonClicked(
|
||||
self
|
||||
):
|
||||
|
||||
self.__confirmed = False
|
||||
self.reject()
|
||||
@@ -1,4 +1,4 @@
|
||||
seats_maps = {
|
||||
ALSeatMapTable = {
|
||||
"2": {
|
||||
"1": """
|
||||
,,,,,,,,,,,039A,039B,,040A,040B,,041A,041B,,042A,042B,,043A,043B,,044A,044B,,,,,,,,,
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (c) 2025 - 2026 KenanZhu.
|
||||
All rights reserved.
|
||||
|
||||
This software is provided "as is", without any warranty of any kind.
|
||||
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||
See the LICENSE file for details.
|
||||
"""
|
||||
from PySide6.QtCore import (
|
||||
Qt, Slot, QEvent
|
||||
)
|
||||
from PySide6.QtWidgets import (
|
||||
QFrame, QWidget,
|
||||
QGridLayout, QGraphicsView, QGraphicsScene, QGraphicsItem
|
||||
)
|
||||
from PySide6.QtGui import (
|
||||
QPainter, QWheelEvent
|
||||
)
|
||||
from gui.ALSeatFrame import ALSeatFrame
|
||||
|
||||
|
||||
class ALSeatMapView(QGraphicsView):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: QWidget = None,
|
||||
seats_data: dict = {},
|
||||
):
|
||||
super().__init__(parent)
|
||||
self.__seats_data = seats_data
|
||||
self.__selected_seats = []
|
||||
self.__seat_frames = {}
|
||||
|
||||
self.setupUi()
|
||||
|
||||
@staticmethod
|
||||
def formatSeatNumber(
|
||||
seat_number: str
|
||||
) -> str:
|
||||
|
||||
if seat_number and not seat_number[-1].isdigit():
|
||||
digits = seat_number[:-1]
|
||||
letter = seat_number[-1]
|
||||
return digits.zfill(3) + letter
|
||||
return seat_number.zfill(3)
|
||||
|
||||
|
||||
def eventFilter(
|
||||
self,
|
||||
watched,
|
||||
event
|
||||
):
|
||||
|
||||
if (watched is self.viewport() and
|
||||
event.type() == QEvent.Type.Wheel and
|
||||
event.modifiers() == Qt.KeyboardModifier.ControlModifier
|
||||
):
|
||||
self.zoomGraphicsView(event)
|
||||
return True
|
||||
return super().eventFilter(watched, event)
|
||||
|
||||
|
||||
def zoomGraphicsView(
|
||||
self,
|
||||
event: QWheelEvent
|
||||
):
|
||||
|
||||
delta = event.angleDelta().y()
|
||||
min_scale = 0.1
|
||||
max_scale = 4.0
|
||||
current_scale = self.transform().m11()
|
||||
zoom_factor = 1.2 if delta > 0 else 1/1.2
|
||||
target_scale = current_scale*zoom_factor
|
||||
if target_scale < min_scale and delta < 0:
|
||||
return
|
||||
if target_scale > max_scale and delta > 0:
|
||||
return
|
||||
self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
|
||||
self.scale(zoom_factor, zoom_factor)
|
||||
|
||||
|
||||
def setupUi(
|
||||
self
|
||||
):
|
||||
|
||||
self.SeatMapGraphicsScene = QGraphicsScene(self)
|
||||
self.setScene(self.SeatMapGraphicsScene)
|
||||
self.setRenderHint(QPainter.RenderHint.LosslessImageRendering)
|
||||
self.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.viewport().installEventFilter(self)
|
||||
|
||||
self.SeatsContainerWidget = QWidget()
|
||||
self.SeatsContainerLayout = QGridLayout(self.SeatsContainerWidget)
|
||||
self.setupSeatMap()
|
||||
|
||||
self.ContainerProxy = self.SeatMapGraphicsScene.addWidget(self.SeatsContainerWidget)
|
||||
self.ContainerProxy.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable, False)
|
||||
|
||||
|
||||
def setupSeatMap(
|
||||
self
|
||||
):
|
||||
|
||||
rows = self.__seats_data.strip().split("\n")
|
||||
for row_idx, row in enumerate(rows):
|
||||
col_idx = 0
|
||||
seats_number = [seat.strip() for seat in row.split(",")]
|
||||
for seat_number in seats_number:
|
||||
if seat_number:
|
||||
seat_widget = ALSeatFrame(seat_number)
|
||||
seat_widget.clicked.connect(self.onSeatClicked)
|
||||
self.SeatsContainerLayout.addWidget(seat_widget, row_idx, col_idx)
|
||||
self.__seat_frames[seat_number] = seat_widget
|
||||
else:
|
||||
spacer = QFrame()
|
||||
spacer.setFixedSize(20, 30)
|
||||
spacer.setStyleSheet("background-color: transparent; border: none;")
|
||||
self.SeatsContainerLayout.addWidget(spacer, row_idx, col_idx)
|
||||
col_idx += 1
|
||||
self.SeatsContainerLayout.setSpacing(20)
|
||||
self.SeatsContainerLayout.setContentsMargins(20, 20, 20, 20)
|
||||
self.SeatsContainerWidget.adjustSize()
|
||||
|
||||
|
||||
def selectSeat(
|
||||
self,
|
||||
seat_number: str
|
||||
):
|
||||
|
||||
if len(self.__selected_seats) >= 1:
|
||||
return
|
||||
seat_number = self.formatSeatNumber(seat_number)
|
||||
if seat_number not in self.__seat_frames:
|
||||
return
|
||||
widget = self.__seat_frames[seat_number]
|
||||
if widget.isSelected():
|
||||
return
|
||||
widget.toggleSelection()
|
||||
self.__selected_seats.append(seat_number)
|
||||
|
||||
|
||||
def selectSeats(
|
||||
self,
|
||||
selected_seats: list
|
||||
):
|
||||
|
||||
self.clearSelections()
|
||||
for seat_number in selected_seats:
|
||||
self.selectSeat(seat_number)
|
||||
|
||||
|
||||
def getSelectedSeats(
|
||||
self
|
||||
) -> list[str]:
|
||||
|
||||
return self.__selected_seats
|
||||
|
||||
|
||||
def clearSelections(
|
||||
self
|
||||
):
|
||||
|
||||
seats_to_clear = self.__selected_seats.copy()
|
||||
for seat_number in seats_to_clear:
|
||||
if seat_number not in self.__seat_frames:
|
||||
continue
|
||||
widget = self.__seat_frames[seat_number]
|
||||
if widget.isSelected():
|
||||
widget.toggleSelection()
|
||||
self.__selected_seats = []
|
||||
|
||||
@Slot(str)
|
||||
def onSeatClicked(
|
||||
self,
|
||||
seat_number: str
|
||||
):
|
||||
|
||||
if seat_number in self.__selected_seats:
|
||||
self.__selected_seats.remove(seat_number)
|
||||
else:
|
||||
if len(self.__selected_seats) < 1:
|
||||
self.__selected_seats.append(seat_number)
|
||||
else:
|
||||
self.__seat_frames[seat_number].toggleSelection()
|
||||
@@ -1,286 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (c) 2025 - 2026 KenanZhu.
|
||||
All rights reserved.
|
||||
|
||||
This software is provided "as is", without any warranty of any kind.
|
||||
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||
See the LICENSE file for details.
|
||||
"""
|
||||
from PySide6.QtCore import (
|
||||
Qt, Slot, Signal, QEvent
|
||||
)
|
||||
from PySide6.QtWidgets import (
|
||||
QFrame, QWidget, QLabel, QHBoxLayout, QVBoxLayout,
|
||||
QGridLayout, QGraphicsView, QGraphicsScene, QGraphicsItem,
|
||||
QPushButton,
|
||||
)
|
||||
from PySide6.QtGui import (
|
||||
QPainter, QWheelEvent, QCloseEvent
|
||||
)
|
||||
from gui.ALSeatFrame import ALSeatFrame
|
||||
|
||||
|
||||
class ALSeatMapWidget(QWidget):
|
||||
|
||||
seatMapWidgetClosed = Signal(list)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: QWidget = None,
|
||||
floor: str = "",
|
||||
room: str = "",
|
||||
seats_data: dict = {},
|
||||
):
|
||||
|
||||
super().__init__(parent)
|
||||
self.__floor = floor
|
||||
self.__room = room
|
||||
self.__seats_data = seats_data
|
||||
self.__selected_seats = []
|
||||
self.__seat_frames = {}
|
||||
self.__confirmed = False
|
||||
|
||||
self.setupUi()
|
||||
self.connectSignals()
|
||||
|
||||
@staticmethod
|
||||
def formatSeatNumber(
|
||||
seat_number: str
|
||||
) -> str:
|
||||
|
||||
if seat_number and not seat_number[-1].isdigit():
|
||||
digits = seat_number[:-1]
|
||||
letter = seat_number[-1]
|
||||
return digits.zfill(3) + letter
|
||||
return seat_number.zfill(3)
|
||||
|
||||
|
||||
def setupUi(
|
||||
self
|
||||
):
|
||||
|
||||
self.setWindowFlags(Qt.WindowType.Window)
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setMinimumSize(800, 600)
|
||||
self.resize(800, 600)
|
||||
self.setWindowTitle(f"选择楼层座位 - AutoLibrary")
|
||||
|
||||
self.SeatMapWidgetMainLayout = QVBoxLayout(self)
|
||||
self.SeatMapWidgetMainLayout.setContentsMargins(5, 5, 5, 5)
|
||||
self.SeatMapWidgetMainLayout.setSpacing(5)
|
||||
self.TitleLabel = QLabel(f"楼层座位分布图: {self.__floor}-{self.__room}")
|
||||
self.TitleLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.TitleLabel.setStyleSheet("font-size: 16px; font-weight: bold; margin: 10px;")
|
||||
self.SeatMapWidgetMainLayout.addWidget(self.TitleLabel)
|
||||
|
||||
self.SeatMapGraphicsView = QGraphicsView(self)
|
||||
self.SeatMapGraphicsScene = QGraphicsScene(self)
|
||||
self.SeatMapGraphicsView.setScene(self.SeatMapGraphicsScene)
|
||||
self.SeatMapGraphicsView.setRenderHint(QPainter.RenderHint.LosslessImageRendering)
|
||||
self.SeatMapGraphicsView.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
|
||||
self.SeatMapGraphicsView.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.SeatMapGraphicsView.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.SeatMapGraphicsView.viewport().installEventFilter(self)
|
||||
|
||||
self.SeatsContainerWidget = QWidget()
|
||||
self.SeatsContainerLayout = QGridLayout(self.SeatsContainerWidget)
|
||||
self.createSeatMap()
|
||||
|
||||
self.ContainerProxy = self.SeatMapGraphicsScene.addWidget(self.SeatsContainerWidget)
|
||||
self.ContainerProxy.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable, False)
|
||||
self.SeatMapWidgetMainLayout.addWidget(self.SeatMapGraphicsView)
|
||||
|
||||
self.TipsLabel = QLabel(
|
||||
" 点击座位进行选择/取消选择, 最多选择1个座位 \n"
|
||||
" [操作方法: Ctrl+鼠标滚轮缩放 | 滚轮/拖拽/方向键 移动]"
|
||||
)
|
||||
self.TipsLabel.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||
self.TipsLabel.setStyleSheet("color: #666; margin: 5px;")
|
||||
self.SeatMapWidgetMainLayout.addWidget(self.TipsLabel)
|
||||
|
||||
self.ConfirmButton = QPushButton("确认")
|
||||
self.ConfirmButton.setFixedSize(80, 25)
|
||||
self.ConfirmButton.setAutoDefault(True)
|
||||
self.ConfirmButton.setDefault(True)
|
||||
self.CancelButton = QPushButton("取消")
|
||||
self.CancelButton.setFixedSize(80, 25)
|
||||
self.SeatMapWidgetControlLayout = QHBoxLayout()
|
||||
self.SeatMapWidgetControlLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.SeatMapWidgetControlLayout.setSpacing(5)
|
||||
self.SeatMapWidgetControlLayout.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
self.SeatMapWidgetControlLayout.addWidget(self.CancelButton)
|
||||
self.SeatMapWidgetControlLayout.addWidget(self.ConfirmButton)
|
||||
self.SeatMapWidgetMainLayout.addLayout(self.SeatMapWidgetControlLayout)
|
||||
|
||||
|
||||
def connectSignals(
|
||||
self
|
||||
):
|
||||
|
||||
self.ConfirmButton.clicked.connect(self.onConfirmButtonClicked)
|
||||
self.CancelButton.clicked.connect(self.onCancelButtonClicked)
|
||||
|
||||
|
||||
def showEvent(
|
||||
self,
|
||||
event
|
||||
):
|
||||
|
||||
result = super().showEvent(event)
|
||||
|
||||
screen_rect = self.screen().geometry()
|
||||
target_pos = self.parent().geometry().center()
|
||||
target_pos.setX(target_pos.x() - self.width()//2)
|
||||
target_pos.setY(target_pos.y() - self.height()//2)
|
||||
if target_pos.x() < 0:
|
||||
target_pos.setX(0)
|
||||
if target_pos.x() + self.width() > screen_rect.width():
|
||||
target_pos.setX(screen_rect.width() - self.width())
|
||||
if target_pos.y() < 0:
|
||||
target_pos.setY(0)
|
||||
if target_pos.y() + self.height() > screen_rect.height():
|
||||
target_pos.setY(screen_rect.height() - self.height())
|
||||
self.move(target_pos)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def closeEvent(
|
||||
self,
|
||||
event: QCloseEvent
|
||||
):
|
||||
|
||||
if not self.__confirmed:
|
||||
self.clearSelections()
|
||||
self.seatMapWidgetClosed.emit(self.__selected_seats)
|
||||
super().closeEvent(event)
|
||||
|
||||
|
||||
def eventFilter(
|
||||
self,
|
||||
watched,
|
||||
event
|
||||
):
|
||||
|
||||
if (watched is self.SeatMapGraphicsView.viewport() and
|
||||
event.type() == QEvent.Type.Wheel and
|
||||
event.modifiers() == Qt.KeyboardModifier.ControlModifier
|
||||
):
|
||||
self.zoomGraphicsView(event)
|
||||
return True
|
||||
return super().eventFilter(watched, event)
|
||||
|
||||
|
||||
def zoomGraphicsView(
|
||||
self,
|
||||
event: QWheelEvent
|
||||
):
|
||||
|
||||
delta = event.angleDelta().y()
|
||||
zoom_factor = 1.2 if delta > 0 else 1/1.2
|
||||
self.SeatMapGraphicsView.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
|
||||
self.SeatMapGraphicsView.scale(zoom_factor, zoom_factor)
|
||||
|
||||
|
||||
def createSeatMap(
|
||||
self
|
||||
):
|
||||
|
||||
rows = self.__seats_data.strip().split("\n")
|
||||
for row_idx, row in enumerate(rows):
|
||||
col_idx = 0
|
||||
seats_number = [seat.strip() for seat in row.split(",")]
|
||||
for seat_number in seats_number:
|
||||
if seat_number:
|
||||
seat_widget = ALSeatFrame(seat_number)
|
||||
seat_widget.clicked.connect(self.onSeatClicked)
|
||||
self.SeatsContainerLayout.addWidget(seat_widget, row_idx, col_idx)
|
||||
self.__seat_frames[seat_number] = seat_widget
|
||||
else:
|
||||
spacer = QFrame()
|
||||
spacer.setFixedSize(20, 30)
|
||||
spacer.setStyleSheet("background-color: transparent; border: none;")
|
||||
self.SeatsContainerLayout.addWidget(spacer, row_idx, col_idx)
|
||||
col_idx += 1
|
||||
self.SeatsContainerLayout.setSpacing(20)
|
||||
self.SeatsContainerLayout.setContentsMargins(20, 20, 20, 20)
|
||||
self.SeatsContainerWidget.adjustSize()
|
||||
|
||||
|
||||
def selectSeat(
|
||||
self,
|
||||
seat_number: str
|
||||
):
|
||||
|
||||
if len(self.__selected_seats) >= 1:
|
||||
return
|
||||
seat_number = self.formatSeatNumber(seat_number)
|
||||
if seat_number not in self.__seat_frames:
|
||||
return
|
||||
widget = self.__seat_frames[seat_number]
|
||||
if widget.isSelected():
|
||||
return
|
||||
widget.toggleSelection()
|
||||
self.__selected_seats.append(seat_number)
|
||||
|
||||
|
||||
def selectSeats(
|
||||
self,
|
||||
selected_seats: list
|
||||
):
|
||||
|
||||
self.clearSelections()
|
||||
for seat_number in selected_seats:
|
||||
self.selectSeat(seat_number)
|
||||
|
||||
|
||||
def getSelectedSeats(
|
||||
self
|
||||
) -> list[str]:
|
||||
|
||||
return self.__selected_seats
|
||||
|
||||
|
||||
def clearSelections(
|
||||
self
|
||||
):
|
||||
|
||||
seats_to_clear = self.__selected_seats.copy()
|
||||
for seat_number in seats_to_clear:
|
||||
if seat_number not in self.__seat_frames:
|
||||
continue
|
||||
widget = self.__seat_frames[seat_number]
|
||||
if widget.isSelected():
|
||||
widget.toggleSelection()
|
||||
self.__selected_seats = []
|
||||
|
||||
@Slot(str)
|
||||
def onSeatClicked(
|
||||
self,
|
||||
seat_number: str
|
||||
):
|
||||
|
||||
if seat_number in self.__selected_seats:
|
||||
self.__selected_seats.remove(seat_number)
|
||||
else:
|
||||
if len(self.__selected_seats) < 1:
|
||||
self.__selected_seats.append(seat_number)
|
||||
else:
|
||||
self.__seat_frames[seat_number].toggleSelection()
|
||||
|
||||
@Slot()
|
||||
def onConfirmButtonClicked(
|
||||
self
|
||||
):
|
||||
|
||||
self.__confirmed = True
|
||||
self.close()
|
||||
|
||||
@Slot()
|
||||
def onCancelButtonClicked(
|
||||
self
|
||||
):
|
||||
|
||||
self.__confirmed = False
|
||||
self.close()
|
||||
@@ -20,10 +20,10 @@ from PySide6.QtWidgets import (
|
||||
QHBoxLayout, QGridLayout, QDateTimeEdit
|
||||
)
|
||||
|
||||
from gui.Ui_ALAddTimerTaskDialog import Ui_ALAddTimerTaskDialog
|
||||
from gui.resources.ui.Ui_ALTimerTaskAddDialog import Ui_ALTimerTaskAddDialog
|
||||
|
||||
|
||||
class TimerTaskStatus(Enum):
|
||||
class ALTimerTaskStatus(Enum):
|
||||
|
||||
PENDING = "等待中"
|
||||
READY = "已就绪"
|
||||
@@ -33,7 +33,7 @@ class TimerTaskStatus(Enum):
|
||||
OUTDATED = "已过期"
|
||||
|
||||
|
||||
class ALAddTimerTaskWidget(QDialog, Ui_ALAddTimerTaskDialog):
|
||||
class ALTimerTaskAddDialog(QDialog, Ui_ALTimerTaskAddDialog):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -128,7 +128,7 @@ class ALAddTimerTaskWidget(QDialog, Ui_ALAddTimerTaskDialog):
|
||||
"execute_time": execute_time,
|
||||
"silent": silent,
|
||||
"add_time": added_time,
|
||||
"status": TimerTaskStatus.PENDING,
|
||||
"status": ALTimerTaskStatus.PENDING,
|
||||
"executed": False
|
||||
}
|
||||
|
||||
@@ -24,21 +24,14 @@ from PySide6.QtGui import (
|
||||
QCloseEvent
|
||||
)
|
||||
|
||||
from gui.Ui_ALTimerTaskWidget import Ui_ALTimerTaskWidget
|
||||
from gui.ALAddTimerTaskDialog import ALAddTimerTaskWidget, TimerTaskStatus
|
||||
from gui.resources.ui.Ui_ALTimerTaskManageWidget import Ui_ALTimerTaskManageWidget
|
||||
from gui.ALTimerTaskAddDialog import ALTimerTaskAddDialog, ALTimerTaskStatus
|
||||
|
||||
from utils.ConfigReader import ConfigReader
|
||||
from utils.ConfigWriter import ConfigWriter
|
||||
|
||||
|
||||
class SortPolicy(Enum):
|
||||
|
||||
BY_NAME = "按名称"
|
||||
BY_ADD_TIME = "按添加时间"
|
||||
BY_EXECUTE_TIME = "按执行时间"
|
||||
|
||||
|
||||
class TimerTaskItemWidget(QWidget):
|
||||
class ALTimerTaskItemWidget(QWidget):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -79,22 +72,22 @@ class TimerTaskItemWidget(QWidget):
|
||||
self.ItemWidgetLayout.addStretch()
|
||||
|
||||
match self.__timer_task["status"]:
|
||||
case TimerTaskStatus.PENDING:
|
||||
case ALTimerTaskStatus.PENDING:
|
||||
TaskStatusText = "等待中"
|
||||
TaskStatusColor = "#FF9800"
|
||||
case TimerTaskStatus.READY:
|
||||
case ALTimerTaskStatus.READY:
|
||||
TaskStatusText = "已就绪"
|
||||
TaskStatusColor = "#316BFF"
|
||||
case TimerTaskStatus.RUNNING:
|
||||
case ALTimerTaskStatus.RUNNING:
|
||||
TaskStatusText = "执行中"
|
||||
TaskStatusColor = "#2294FF"
|
||||
case TimerTaskStatus.EXECUTED:
|
||||
case ALTimerTaskStatus.EXECUTED:
|
||||
TaskStatusText = "已执行"
|
||||
TaskStatusColor = "#4CAF50"
|
||||
case TimerTaskStatus.ERROR:
|
||||
case ALTimerTaskStatus.ERROR:
|
||||
TaskStatusText = "执行失败"
|
||||
TaskStatusColor = "#DC0000"
|
||||
case TimerTaskStatus.OUTDATED:
|
||||
case ALTimerTaskStatus.OUTDATED:
|
||||
TaskStatusText = "已过期"
|
||||
TaskStatusColor = "#DC0000"
|
||||
TaskStatusLabel = QLabel(TaskStatusText)
|
||||
@@ -128,17 +121,23 @@ class TimerTaskItemWidget(QWidget):
|
||||
self.DeleteButton = QPushButton("删除")
|
||||
self.DeleteButton.setFixedSize(80, 25)
|
||||
self.ItemWidgetLayout.addWidget(self.DeleteButton)
|
||||
if self.__timer_task["status"] == TimerTaskStatus.READY\
|
||||
or self.__timer_task["status"] == TimerTaskStatus.RUNNING:
|
||||
if self.__timer_task["status"] == ALTimerTaskStatus.READY\
|
||||
or self.__timer_task["status"] == ALTimerTaskStatus.RUNNING:
|
||||
self.DeleteButton.setEnabled(False)
|
||||
self.setFixedHeight(55)
|
||||
|
||||
|
||||
class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
class ALTimerTaskManageWidget(QWidget, Ui_ALTimerTaskManageWidget):
|
||||
|
||||
class SortPolicy(Enum):
|
||||
|
||||
BY_NAME = "按名称"
|
||||
BY_ADD_TIME = "按添加时间"
|
||||
BY_EXECUTE_TIME = "按执行时间"
|
||||
|
||||
timerTasksChanged = Signal()
|
||||
timerTaskIsReady = Signal(dict)
|
||||
timerTaskWidgetClosed = Signal()
|
||||
timerTasksChanged = Signal()
|
||||
timerTaskManageWidgetClosed = Signal()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -147,10 +146,9 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
):
|
||||
|
||||
super().__init__(parent)
|
||||
|
||||
self.__timer_tasks = []
|
||||
self.__check_timer = None
|
||||
self.__sort_policy = SortPolicy.BY_EXECUTE_TIME
|
||||
self.__sort_policy = self.SortPolicy.BY_EXECUTE_TIME
|
||||
self.__sort_order = Qt.SortOrder.AscendingOrder
|
||||
self.__timer_tasks_config_path = timer_tasks_config_path
|
||||
|
||||
@@ -158,7 +156,7 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
self.connectSignals()
|
||||
self.setupTimer()
|
||||
if not self.initializeTimerTasks():
|
||||
return
|
||||
raise Exception("定时任务配置文件初始化失败 !")
|
||||
|
||||
|
||||
def connectSignals(
|
||||
@@ -192,11 +190,6 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
return True
|
||||
timer_tasks = []
|
||||
if self.saveTimerTasks(self.__timer_tasks_config_path, copy.deepcopy(timer_tasks)):
|
||||
QMessageBox.information(
|
||||
self,
|
||||
"信息 - AutoLibrary",
|
||||
f"定时任务配置文件初始化完成: \n{self.__timer_tasks_config_path}"
|
||||
)
|
||||
self.__timer_tasks = timer_tasks
|
||||
self.updateTimerTaskList()
|
||||
return True
|
||||
@@ -216,16 +209,10 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
for task in timer_tasks["timer_tasks"]:
|
||||
task["add_time"] = datetime.strptime(task["add_time"], "%Y-%m-%d %H:%M:%S")
|
||||
task["execute_time"] = datetime.strptime(task["execute_time"], "%Y-%m-%d %H:%M:%S")
|
||||
task["status"] = TimerTaskStatus(task["status"])
|
||||
task["status"] = ALTimerTaskStatus(task["status"])
|
||||
return timer_tasks["timer_tasks"]
|
||||
raise Exception("定时任务配置文件格式错误")
|
||||
except Exception as e:
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"警告 - AutoLibrary",
|
||||
f"加载定时任务配置发生错误 ! : {e}\n"\
|
||||
f"文件路径: {timer_tasks_config_path}"
|
||||
)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
@@ -287,7 +274,7 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
):
|
||||
|
||||
self.hide()
|
||||
self.timerTaskWidgetClosed.emit()
|
||||
self.timerTaskManageWidgetClosed.emit()
|
||||
event.ignore()
|
||||
|
||||
|
||||
@@ -297,17 +284,17 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
order: Qt.SortOrder = Qt.SortOrder.AscendingOrder
|
||||
):
|
||||
|
||||
if policy == SortPolicy.BY_NAME:
|
||||
if policy == self.SortPolicy.BY_NAME:
|
||||
self.__timer_tasks.sort(
|
||||
key = lambda x: x["name"],
|
||||
reverse = order is Qt.SortOrder.DescendingOrder
|
||||
)
|
||||
elif policy == SortPolicy.BY_ADD_TIME:
|
||||
elif policy == self.SortPolicy.BY_ADD_TIME:
|
||||
self.__timer_tasks.sort(
|
||||
key = lambda x: x["add_time"],
|
||||
reverse = order is Qt.SortOrder.DescendingOrder
|
||||
)
|
||||
elif policy == SortPolicy.BY_EXECUTE_TIME:
|
||||
elif policy == self.SortPolicy.BY_EXECUTE_TIME:
|
||||
self.__timer_tasks.sort(
|
||||
key = lambda x: x["execute_time"],
|
||||
reverse = order is Qt.SortOrder.DescendingOrder
|
||||
@@ -324,15 +311,15 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
invalid = 0
|
||||
total = len(self.__timer_tasks)
|
||||
for timer_task in self.__timer_tasks:
|
||||
if timer_task["status"] == TimerTaskStatus.PENDING:
|
||||
if timer_task["status"] == ALTimerTaskStatus.PENDING:
|
||||
pending += 1
|
||||
elif timer_task["status"] == TimerTaskStatus.READY\
|
||||
or timer_task["status"] == TimerTaskStatus.RUNNING:
|
||||
elif timer_task["status"] == ALTimerTaskStatus.READY\
|
||||
or timer_task["status"] == ALTimerTaskStatus.RUNNING:
|
||||
in_queue += 1
|
||||
elif timer_task["status"] == TimerTaskStatus.EXECUTED:
|
||||
elif timer_task["status"] == ALTimerTaskStatus.EXECUTED:
|
||||
executed += 1
|
||||
elif timer_task["status"] == TimerTaskStatus.ERROR\
|
||||
or timer_task["status"] == TimerTaskStatus.OUTDATED:
|
||||
elif timer_task["status"] == ALTimerTaskStatus.ERROR\
|
||||
or timer_task["status"] == ALTimerTaskStatus.OUTDATED:
|
||||
invalid += 1
|
||||
self.TotalTaskLabel.setText(f"总任务:{total}")
|
||||
self.PendingTaskLabel.setText(f"待执行:{pending}")
|
||||
@@ -350,7 +337,7 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
for timer_task in self.__timer_tasks:
|
||||
item = QListWidgetItem()
|
||||
item.setData(Qt.UserRole, timer_task)
|
||||
widget = TimerTaskItemWidget(self, timer_task)
|
||||
widget = ALTimerTaskItemWidget(self, timer_task)
|
||||
widget.DeleteButton.clicked.connect(
|
||||
lambda _, uuid = timer_task["task_uuid"]: self.deleteTask(uuid)
|
||||
)
|
||||
@@ -363,7 +350,7 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
self
|
||||
):
|
||||
|
||||
dialog = ALAddTimerTaskWidget(self)
|
||||
dialog = ALTimerTaskAddDialog(self)
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
timer_task = dialog.getTimerTask()
|
||||
self.__timer_tasks.append(timer_task)
|
||||
@@ -398,8 +385,8 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
return
|
||||
in_queue_tasks = [
|
||||
x for x in self.__timer_tasks
|
||||
if x["status"] == TimerTaskStatus.READY
|
||||
or x["status"] == TimerTaskStatus.RUNNING
|
||||
if x["status"] == ALTimerTaskStatus.READY
|
||||
or x["status"] == ALTimerTaskStatus.RUNNING
|
||||
]
|
||||
in_queue_count = len(in_queue_tasks)
|
||||
if in_queue_count > 0:
|
||||
@@ -422,13 +409,13 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
for timer_task in self.__timer_tasks:
|
||||
if timer_task["execute_time"] > now:
|
||||
continue
|
||||
if timer_task["status"] is not TimerTaskStatus.PENDING:
|
||||
if timer_task["status"] is not ALTimerTaskStatus.PENDING:
|
||||
continue
|
||||
if timer_task["execute_time"] <= now + timedelta(seconds = -5):
|
||||
timer_task["status"] = TimerTaskStatus.OUTDATED
|
||||
timer_task["status"] = ALTimerTaskStatus.OUTDATED
|
||||
need_update = True
|
||||
else:
|
||||
timer_task["status"] = TimerTaskStatus.READY
|
||||
timer_task["status"] = ALTimerTaskStatus.READY
|
||||
self.timerTaskIsReady.emit(timer_task)
|
||||
need_update = True
|
||||
if need_update:
|
||||
@@ -441,9 +428,9 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
):
|
||||
|
||||
mapping = {
|
||||
0: SortPolicy.BY_NAME,
|
||||
1: SortPolicy.BY_ADD_TIME,
|
||||
2: SortPolicy.BY_EXECUTE_TIME
|
||||
0: self.SortPolicy.BY_NAME,
|
||||
1: self.SortPolicy.BY_ADD_TIME,
|
||||
2: self.SortPolicy.BY_EXECUTE_TIME
|
||||
}
|
||||
self.__sort_policy = mapping[policy]
|
||||
self.updateTimerTaskList()
|
||||
@@ -479,7 +466,7 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
|
||||
for task in self.__timer_tasks:
|
||||
if task["task_uuid"] == timer_task["task_uuid"]:
|
||||
task["status"] = TimerTaskStatus.RUNNING
|
||||
task["status"] = ALTimerTaskStatus.RUNNING
|
||||
self.timerTasksChanged.emit()
|
||||
|
||||
|
||||
@@ -491,7 +478,7 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
|
||||
for task in self.__timer_tasks:
|
||||
if task["task_uuid"] == timer_task["task_uuid"]:
|
||||
task["status"] = TimerTaskStatus.EXECUTED
|
||||
task["status"] = ALTimerTaskStatus.EXECUTED
|
||||
self.timerTasksChanged.emit()
|
||||
|
||||
@Slot(dict)
|
||||
@@ -502,5 +489,5 @@ class ALTimerTaskWidget(QWidget, Ui_ALTimerTaskWidget):
|
||||
|
||||
for task in self.__timer_tasks:
|
||||
if task["task_uuid"] == timer_task["task_uuid"]:
|
||||
task["status"] = TimerTaskStatus.ERROR
|
||||
task["status"] = ALTimerTaskStatus.ERROR
|
||||
self.timerTasksChanged.emit()
|
||||
@@ -21,7 +21,7 @@ from PySide6.QtGui import (
|
||||
)
|
||||
|
||||
|
||||
class TreeItemType(Enum):
|
||||
class ALUserTreeItemType(Enum):
|
||||
|
||||
GROUP = 0
|
||||
USER = 1
|
||||
@@ -111,15 +111,15 @@ class ALUserTreeWidget(QTreeWidget):
|
||||
if source_item is None:
|
||||
event.ignore()
|
||||
return
|
||||
if source_item.type() == TreeItemType.GROUP.value:
|
||||
if source_item.type() == ALUserTreeItemType.GROUP.value:
|
||||
if target_item is not None:
|
||||
event.ignore()
|
||||
return
|
||||
elif source_item.type() == TreeItemType.USER.value:
|
||||
elif source_item.type() == ALUserTreeItemType.USER.value:
|
||||
if target_item is None:
|
||||
event.ignore()
|
||||
return
|
||||
if target_item.type() != TreeItemType.GROUP.value:
|
||||
if target_item.type() != ALUserTreeItemType.GROUP.value:
|
||||
event.ignore()
|
||||
return
|
||||
if target_item.checkState(1) == Qt.CheckState.Unchecked:
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
workflow process. Do not edit manually.
|
||||
|
||||
This file is auto-generated during the workflow process.
|
||||
Last updated: 2026-01-17 18:18:12 UTC
|
||||
Last updated: 2026-02-16 07:04:48 UTC
|
||||
"""
|
||||
|
||||
AL_VERSION = "1.0.4"
|
||||
AL_TAG = "v1.0.4"
|
||||
AL_VERSION = "1.0.5"
|
||||
AL_TAG = "v1.0.5"
|
||||
AL_COMMIT_SHA = "local"
|
||||
AL_COMMIT_DATE = "null" # time zone : UTC
|
||||
AL_BUILD_DATE = "null" # time zone : UTC
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
this folder is used to store the batch scripts.
|
||||
@@ -1 +0,0 @@
|
||||
this folder is used to store the config files.
|
||||
|
Before Width: | Height: | Size: 785 KiB After Width: | Height: | Size: 785 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
@@ -129,21 +129,24 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="AboutInfoEdit">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Courier New</family>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
<widget class="QTextBrowser" name="AboutInfoBrowser">
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Shadow::Plain</enum>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="lineWrapMode">
|
||||
<enum>QTextEdit::LineWrapMode::NoWrap</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextInteractionFlag::TextBrowserInteraction</set>
|
||||
<property name="openLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -1356,7 +1356,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>详情请参阅 <a href="https://www.autolibrary.cv/docs/manual_lists.html"><span style=" text-decoration: underline; color:#69fcff;">用户手册</span></a></p></body></html></string>
|
||||
<string><html><head/><body><p>详情请参阅 <a href="https://www.autolibrary.top/manuals"><span style=" text-decoration: underline; color:#69fcff;">用户手册</span></a></p></body></html></string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p><br/></p></body></html></string>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ALAddTimerTaskDialog</class>
|
||||
<widget class="QDialog" name="ALAddTimerTaskDialog">
|
||||
<class>ALTimerTaskAddDialog</class>
|
||||
<widget class="QDialog" name="ALTimerTaskAddDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ALTimerTaskWidget</class>
|
||||
<widget class="QWidget" name="ALTimerTaskWidget">
|
||||
<class>ALTimerTaskManageWidget</class>
|
||||
<widget class="QWidget" name="ALTimerTaskManageWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@@ -23,7 +23,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>定时任务 - AutoLibrary</string>
|
||||
<string>定时任务管理 - AutoLibrary</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="ALTimerTaskWidgetLayout">
|
||||
<property name="spacing">
|
||||
@@ -11,6 +11,7 @@ import os
|
||||
import queue
|
||||
|
||||
from selenium import webdriver
|
||||
from selenium.common.exceptions import TimeoutException
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
@@ -41,10 +42,11 @@ class AutoLib(MsgBase):
|
||||
self.__user_config = None
|
||||
self.__driver = None
|
||||
if not self.__initBrowserDriver():
|
||||
raise Exception("浏览器驱动初始化失败")
|
||||
raise Exception("浏览器驱动初始化失败 !")
|
||||
else:
|
||||
if not self.__initDriverUrl():
|
||||
raise Exception("浏览器驱动URL初始化失败")
|
||||
self.close()
|
||||
raise Exception("浏览器驱动URL初始化失败 !")
|
||||
self.__initLibOperators()
|
||||
|
||||
|
||||
@@ -64,7 +66,8 @@ class AutoLib(MsgBase):
|
||||
case "firefox":
|
||||
driver_options = webdriver.FirefoxOptions()
|
||||
case _:
|
||||
raise Exception(f"不支持的浏览器驱动类型: {self.__driver_type} !")
|
||||
self._showTrace(f"不支持的浏览器驱动类型: {self.__driver_type} !")
|
||||
return False
|
||||
|
||||
if not web_driver_config:
|
||||
self._showTrace("未配置浏览器驱动参数 !")
|
||||
@@ -107,7 +110,8 @@ class AutoLib(MsgBase):
|
||||
# init browser driver
|
||||
self.__driver_path = web_driver_config.get("driver_path")
|
||||
if not self.__driver_path:
|
||||
raise Exception(f"未配置浏览器驱动路径 !")
|
||||
self._showTrace("未配置浏览器驱动路径 !")
|
||||
return False
|
||||
self.__driver_path = os.path.abspath(self.__driver_path)
|
||||
try:
|
||||
service = None
|
||||
@@ -122,7 +126,8 @@ class AutoLib(MsgBase):
|
||||
self._showTrace(f"Firefox 浏览器驱动初始化略慢, 请耐心等待...")
|
||||
service = FirefoxService(executable_path=self.__driver_path)
|
||||
self.__driver = webdriver.Firefox(service=service, options=driver_options)
|
||||
case _:
|
||||
case _: # actually will not happen, beacuse we have checked it at the initlization
|
||||
# of 'driver_options'
|
||||
raise Exception(f"不支持的浏览器驱动类型: {self.__driver_type}")
|
||||
self.__driver.implicitly_wait(1)
|
||||
self.__driver.execute_script(
|
||||
@@ -183,10 +188,16 @@ class AutoLib(MsgBase):
|
||||
|
||||
lib_config = self.__run_config.get("library", None)
|
||||
if not lib_config:
|
||||
self._showError("未配置图书馆参数 !")
|
||||
self._showTrace("未配置图书馆参数 !")
|
||||
return False
|
||||
url = lib_config.get("host_url") + lib_config.get("login_url")
|
||||
self.__driver.get(url)
|
||||
self.__driver.set_page_load_timeout(5)
|
||||
try:
|
||||
self.__driver.get(url)
|
||||
except TimeoutException:
|
||||
self.__driver.execute_script("window.stop();")
|
||||
self._showTrace(f"图书馆登录页面加载超时 ! 请检查网络环境是否正常")
|
||||
return False
|
||||
if not self.__waitResponseLoad():
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -52,13 +52,13 @@ class ConfigReader:
|
||||
with open(self.__config_path, 'r', encoding='utf-8') as file:
|
||||
self.__config_data = json.load(file)
|
||||
except FileNotFoundError as e:
|
||||
raise Exception(f"Config file not found: {self.__config_path}") from e
|
||||
raise Exception(f"配置文件不存在: {self.__config_path}") from e
|
||||
except PermissionError as e:
|
||||
raise Exception(f"Without enough permission to read config file: {self.__config_path}") from e
|
||||
raise Exception(f"没有足够的权限读取配置文件: {self.__config_path}") from e
|
||||
except json.JSONDecodeError as e:
|
||||
raise Exception(f"JSON decode error in config file: {self.__config_path}") from e
|
||||
raise Exception(f"JSON 解析错误: {self.__config_path}") from e
|
||||
except Exception as e:
|
||||
raise Exception(f"Unknown error occurred while reading config file: {e}") from e
|
||||
raise Exception(f"读取配置文件时未知错误: {e}") from e
|
||||
|
||||
|
||||
def getConfigs(
|
||||
|
||||
@@ -58,13 +58,13 @@ class ConfigWriter:
|
||||
with open(self.__config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(self.__config_data, f, indent=4, sort_keys=False)
|
||||
except PermissionError as e:
|
||||
raise Exception(f"Without enough permission to write config file: {self.__config_path}") from e
|
||||
raise Exception(f"没有足够的权限写入配置文件: {self.__config_path}") from e
|
||||
except IOError as e:
|
||||
raise Exception(f"IO error occurred while writing config file: {self.__config_path}") from e
|
||||
raise Exception(f"写入配置文件时发生 IO 错误: {self.__config_path}") from e
|
||||
except TypeError as e:
|
||||
raise Exception(f"Config data contains invalid type that can not be JSON serialized: {e}") from e
|
||||
raise Exception(f"配置数据包含无法 JSON 序列化的类型: {e}") from e
|
||||
except Exception as e:
|
||||
raise Exception(f"Unknown error occurred while writing config file: {e}") from e
|
||||
raise Exception(f"写入配置文件时未知错误: {e}") from e
|
||||
|
||||
|
||||
def setConfigs(
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
This folder is used to store the template config files.
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"library": {
|
||||
"host_url": "http://10.1.20.7",
|
||||
"login_url": "/login"
|
||||
},
|
||||
"login": {
|
||||
"auto_captcha": true,
|
||||
"max_attempt": 3
|
||||
},
|
||||
"web_driver": {
|
||||
"driver_type": "edge",
|
||||
"driver_path": "",
|
||||
"headless": false
|
||||
},
|
||||
"mode": {
|
||||
"run_mode": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"groups": []
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
This folder is used to store the template files.
|
||||
|
||||
Directory structure:
|
||||
|
||||
templates
|
||||
|─── configs // template config files
|
||||
Reference in New Issue
Block a user