mirror of
https://github.com/KenanZhu/AutoLibrary.git
synced 2026-06-18 23:43:02 +08:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f3d68c40cb | |||
| 0ceff677e4 | |||
| 6f6b415bff | |||
| 735f31830d | |||
| 7be5afeae1 | |||
| 3d6978c9c2 | |||
| db7a868598 | |||
| f1e0334ce3 | |||
| b9411261ea | |||
| fa737711d4 | |||
| 79e2128fca | |||
| 128c8e7a83 | |||
| 6474f6e3bb | |||
| ba60a5d884 | |||
| 4d8f8130dc | |||
| eba99cab9f | |||
| aa7a806ff7 | |||
| bb180f8c8e | |||
| 107ed41b58 | |||
| 43b87db4eb | |||
| ae23f65e5a | |||
| a7b9c340ae | |||
| 96d733d2ed | |||
| 65cb951ada | |||
| 94ce3433a3 |
@@ -1,48 +1,58 @@
|
|||||||
name: Build and Release
|
name: Build
|
||||||
|
|
||||||
|
# This workflow compiles the application for Windows platform using PyInstaller, and
|
||||||
|
# archives the built artifacts as 'AutoLibrary.<tag_name>-windows-x86_64.zip'.
|
||||||
|
#
|
||||||
|
# It is triggered when called by the release workflow.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_call:
|
||||||
tags:
|
inputs:
|
||||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
version:
|
||||||
|
description: 'Version number'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
tag_name:
|
||||||
|
description: 'Tag name'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
outputs:
|
||||||
|
version:
|
||||||
|
description: 'The version number'
|
||||||
|
value: ${{ jobs.build-windows.outputs.version }}
|
||||||
|
tag_name:
|
||||||
|
description: 'The tag name'
|
||||||
|
value: ${{ jobs.build-windows.outputs.tag_name }}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Build Windows
|
||||||
|
#
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-version-info:
|
build-windows:
|
||||||
uses: ./.github/workflows/update-version-info.yml
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
with:
|
|
||||||
tag_name: ${{ github.ref_name }}
|
|
||||||
ref: ${{ github.ref }}
|
|
||||||
|
|
||||||
commit-and-move-tag:
|
|
||||||
needs: update-version-info
|
|
||||||
if: ${{ needs.update-version-info.outputs.has_changes == 'true' }}
|
|
||||||
uses: ./.github/workflows/commit-and-move-tag.yml
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
with:
|
|
||||||
tag_name: ${{ needs.update-version-info.outputs.tag_name }}
|
|
||||||
version: ${{ needs.update-version-info.outputs.version }}
|
|
||||||
file_path: src/gui/ALVersionInfo.py
|
|
||||||
|
|
||||||
build-and-release:
|
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs: [update-version-info, commit-and-move-tag]
|
outputs:
|
||||||
if: always() && needs.update-version-info.result == 'success'
|
version: ${{ steps.get_version.outputs.VERSION }}
|
||||||
permissions:
|
tag_name: ${{ steps.get_version.outputs.TAG_NAME }}
|
||||||
contents: write
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code with updated version info
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Get version info from previous job
|
# here we download the build version of ALVersionInfo.py from artifacts
|
||||||
id: get_tag
|
# and replace the committed version
|
||||||
|
- name: Download build version of ALVersionInfo.py
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: updated-version-info-for-build
|
||||||
|
path: src/gui/
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: get_version
|
||||||
run: |
|
run: |
|
||||||
$tagName = "${{ needs.update-version-info.outputs.tag_name }}"
|
$version = "${{ inputs.version }}"
|
||||||
$version = "${{ needs.update-version-info.outputs.version }}"
|
$tagName = "${{ inputs.tag_name }}"
|
||||||
|
|
||||||
echo "TAG_NAME=$tagName" >> $env:GITHUB_OUTPUT
|
echo "TAG_NAME=$tagName" >> $env:GITHUB_OUTPUT
|
||||||
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
|
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
|
||||||
@@ -50,17 +60,17 @@ jobs:
|
|||||||
Write-Host "✓ Version: $version"
|
Write-Host "✓ Version: $version"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Verify ALVersionInfo.py was updated
|
- name: Verify 'ALVersionInfo.py' was updated
|
||||||
run: |
|
run: |
|
||||||
$versionInfoFile = "src/gui/ALVersionInfo.py"
|
$versionInfoFile = "src/gui/ALVersionInfo.py"
|
||||||
Write-Host "Verifying $versionInfoFile content:"
|
Write-Host "Verifying $versionInfoFile content:"
|
||||||
Write-Host "================================"
|
Write-Host "=================================="
|
||||||
Get-Content $versionInfoFile | Write-Host
|
Get-Content $versionInfoFile | Write-Host
|
||||||
Write-Host "================================"
|
Write-Host "=================================="
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.13'
|
python-version: '3.13'
|
||||||
|
|
||||||
@@ -69,7 +79,7 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install -r requirement.txt
|
pip install -r requirement.txt
|
||||||
|
|
||||||
- name: Fix ddddocr compatibility and copy model files
|
- name: Solve ddddocr compatibility and copy model files
|
||||||
run: |
|
run: |
|
||||||
$ddddocrPath = python -c "import ddddocr, os; print(os.path.dirname(ddddocr.__file__))"
|
$ddddocrPath = python -c "import ddddocr, os; print(os.path.dirname(ddddocr.__file__))"
|
||||||
Write-Host "ddddocr package location: $ddddocrPath"
|
Write-Host "ddddocr package location: $ddddocrPath"
|
||||||
@@ -121,9 +131,9 @@ jobs:
|
|||||||
./compile_rc.bat
|
./compile_rc.bat
|
||||||
shell: cmd
|
shell: cmd
|
||||||
|
|
||||||
- name: Generate Main.spec dynamically
|
- name: Generate 'Main.spec'
|
||||||
run: |
|
run: |
|
||||||
$version = "${{ steps.get_tag.outputs.VERSION }}"
|
$version = "${{ steps.get_version.outputs.VERSION }}"
|
||||||
$exeName = "AutoLibrary-$version"
|
$exeName = "AutoLibrary-$version"
|
||||||
|
|
||||||
Write-Host "Generating Main.spec for version: $version"
|
Write-Host "Generating Main.spec for version: $version"
|
||||||
@@ -173,28 +183,29 @@ jobs:
|
|||||||
" icon=['src\\gui\\icons\\AutoLibrary_32x32.ico'],"
|
" icon=['src\\gui\\icons\\AutoLibrary_32x32.ico'],"
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
|
|
||||||
$specLines | Out-File -FilePath "Main.spec" -Encoding UTF8
|
$specLines | Out-File -FilePath "Main.spec" -Encoding UTF8
|
||||||
|
|
||||||
Write-Host "✓ Main.spec generated successfully"
|
Write-Host "✓ Main.spec generated successfully"
|
||||||
Write-Host "`n=== Generated Main.spec ==="
|
Write-Host "`nGenerated Main.spec ============"
|
||||||
Get-Content "Main.spec" | Write-Host
|
Get-Content "Main.spec" | Write-Host
|
||||||
Write-Host "==========================`n"
|
Write-Host "==================================`n"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Build with PyInstaller
|
- name: Build with PyInstaller
|
||||||
run: |
|
run: |
|
||||||
pyinstaller Main.spec
|
pyinstaller Main.spec
|
||||||
|
|
||||||
- name: Create Release Archive
|
- name: Zip windows release
|
||||||
|
id: zip_release
|
||||||
run: |
|
run: |
|
||||||
$tagName = "${{ steps.get_tag.outputs.TAG_NAME }}"
|
$tagName = "${{ steps.get_version.outputs.TAG_NAME }}"
|
||||||
$version = "${{ steps.get_tag.outputs.VERSION }}"
|
$version = "${{ steps.get_version.outputs.VERSION }}"
|
||||||
$exeName = "AutoLibrary-$version.exe"
|
$exeName = "AutoLibrary-$version.exe"
|
||||||
$zipName = "AutoLibrary.$tagName-windows-x86_64.zip"
|
$zipName = "AutoLibrary.$tagName-windows-x86_64.zip"
|
||||||
|
|
||||||
Write-Host "Looking for executable: dist/$exeName"
|
echo "ZIP_PATH=$zipName" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
|
Write-Host "Looking for executable: dist/$exeName"
|
||||||
if (Test-Path "dist/$exeName") {
|
if (Test-Path "dist/$exeName") {
|
||||||
Compress-Archive -Path "dist/$exeName" -DestinationPath $zipName
|
Compress-Archive -Path "dist/$exeName" -DestinationPath $zipName
|
||||||
Write-Host "✓ Created release archive: $zipName"
|
Write-Host "✓ Created release archive: $zipName"
|
||||||
@@ -206,31 +217,9 @@ jobs:
|
|||||||
}
|
}
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Create Release
|
- name: Archive artifacts
|
||||||
id: create_release
|
uses: actions/upload-artifact@v4
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ steps.get_tag.outputs.TAG_NAME }}
|
name: AutoLibrary.${{ steps.get_version.outputs.TAG_NAME }}-windows-x86_64
|
||||||
name: AutoLibrary ${{ steps.get_tag.outputs.TAG_NAME }}
|
path: |
|
||||||
files: |
|
${{ steps.zip_release.outputs.ZIP_PATH }}
|
||||||
AutoLibrary.${{ steps.get_tag.outputs.TAG_NAME }}-windows-x86_64.zip
|
|
||||||
draft: false
|
|
||||||
prerelease: false
|
|
||||||
generate_release_notes: true
|
|
||||||
body: |
|
|
||||||
### 下载获取
|
|
||||||
- **Windows x86_64**: `AutoLibrary.${{ steps.get_tag.outputs.TAG_NAME }}-windows-x86_64.zip`
|
|
||||||
|
|
||||||
### 如何使用
|
|
||||||
1. 下载 `AutoLibrary.${{ steps.get_tag.outputs.TAG_NAME }}-windows-x86_64.zip` 文件
|
|
||||||
2. 解压到任意目录
|
|
||||||
3. 下载对应浏览器的驱动文件
|
|
||||||
4. 运行 `AutoLibrary-${{ steps.get_tag.outputs.VERSION }}.exe` (首次运行会初始化配置文件)
|
|
||||||
5. 按照提示操作即可
|
|
||||||
|
|
||||||
更多详情请访问 [AutoLibrary 网站](http://autolibrary.cv) 和查看 [帮助手册](https://autolibrary.cv/docs/manual_lists.html)
|
|
||||||
|
|
||||||
---
|
|
||||||
**完整更新日志见下方自动生成的 Release Notes**
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
name: Commit and Move Tag
|
name: Commit Release
|
||||||
|
|
||||||
|
# This workflow commits version changes in 'ALVersionInfo.py' (get from artifacts) and
|
||||||
|
# moves the release tag to this new release commit.
|
||||||
|
#
|
||||||
|
# It is triggered when called by the release workflow.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
@@ -18,10 +23,10 @@ on:
|
|||||||
outputs:
|
outputs:
|
||||||
new_commit_sha:
|
new_commit_sha:
|
||||||
description: 'The new commit SHA after moving the tag'
|
description: 'The new commit SHA after moving the tag'
|
||||||
value: ${{ jobs.commit-and-move-tag.outputs.new_commit_sha }}
|
value: ${{ jobs.commit-release.outputs.new_commit_sha }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
commit-and-move-tag:
|
commit-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -35,10 +40,12 @@ jobs:
|
|||||||
ref: main
|
ref: main
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Download modified file
|
# here we download the commit version of ALVersionInfo.py from artifacts
|
||||||
|
# and replace the original file with it.
|
||||||
|
- name: Download commit version of ALVersionInfo.py
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: updated-version-info
|
name: updated-version-info-for-commit
|
||||||
path: downloaded-file/
|
path: downloaded-file/
|
||||||
|
|
||||||
- name: Replace file with updated version
|
- name: Replace file with updated version
|
||||||
@@ -52,9 +59,9 @@ jobs:
|
|||||||
|
|
||||||
echo "✓ File replaced: $FILE_PATH"
|
echo "✓ File replaced: $FILE_PATH"
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Updated file content ==="
|
echo "Updated file content ==================="
|
||||||
cat "$FILE_PATH"
|
cat "$FILE_PATH"
|
||||||
echo "============================"
|
echo "========================================"
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
id: commit_changes
|
id: commit_changes
|
||||||
@@ -85,7 +92,7 @@ jobs:
|
|||||||
git push origin HEAD:${MAIN_BRANCH}
|
git push origin HEAD:${MAIN_BRANCH}
|
||||||
echo "✓ Changes pushed to ${MAIN_BRANCH}"
|
echo "✓ Changes pushed to ${MAIN_BRANCH}"
|
||||||
|
|
||||||
- name: Move tag to new commit
|
- name: Move tag to new release commit
|
||||||
run: |
|
run: |
|
||||||
TAG_NAME="${{ inputs.tag_name }}"
|
TAG_NAME="${{ inputs.tag_name }}"
|
||||||
|
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
# This workflow automates the complete release process for AutoLibrary application
|
||||||
|
# It is triggered when a new version tag (vX.Y.Z) is pushed to the repository
|
||||||
|
#
|
||||||
|
# Workflow Steps:
|
||||||
|
# START >
|
||||||
|
|
||||||
|
# 1. Update Version:
|
||||||
|
# Updates version information in 'ALVersionInfo.py' with build metadata and archives
|
||||||
|
# the updated version file as an artifact.
|
||||||
|
#
|
||||||
|
# for more information, please refer to the comment in the workflow 'update-version.yml'
|
||||||
|
|
||||||
|
# 2. Commit Release:
|
||||||
|
# Commits version changes and moves the release tag to this new release commit.
|
||||||
|
|
||||||
|
# 3. Build:
|
||||||
|
# Compiles the application for Windows platform using PyInstaller, and
|
||||||
|
# archives the built artifacts as 'AutoLibrary.<tag_name>-windows-x86_64.zip'.
|
||||||
|
|
||||||
|
# 4. Release:
|
||||||
|
# Creates GitHub release with generated artifacts and release notes
|
||||||
|
|
||||||
|
# < END
|
||||||
|
#
|
||||||
|
# The workflow ensures version consistency between source code, built artifacts, and GitHub releases
|
||||||
|
# while maintaining proper commit history and tag management.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
#
|
||||||
|
# Start :
|
||||||
|
# virtual job that indacates the start of the release process
|
||||||
|
#
|
||||||
|
|
||||||
|
start:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Start release
|
||||||
|
run: |
|
||||||
|
echo "✓ Starting release"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Update version :
|
||||||
|
# this job updates the version in the file 'ALVersionInfo.py'
|
||||||
|
#
|
||||||
|
|
||||||
|
update-version:
|
||||||
|
needs:
|
||||||
|
- start
|
||||||
|
uses: ./.github/workflows/update-version.yml
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref_name }}
|
||||||
|
ref: ${{ github.ref }}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Commit release :
|
||||||
|
# this job commits the updated version file and move the release
|
||||||
|
# tag to this new commit
|
||||||
|
#
|
||||||
|
|
||||||
|
commit-release:
|
||||||
|
needs:
|
||||||
|
- update-version
|
||||||
|
if: ${{ needs.update-version.outputs.has_changes == 'true' }}
|
||||||
|
uses: ./.github/workflows/commit-release.yml
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
with:
|
||||||
|
tag_name: ${{ needs.update-version.outputs.tag_name }}
|
||||||
|
version: ${{ needs.update-version.outputs.version }}
|
||||||
|
file_path: src/gui/ALVersionInfo.py
|
||||||
|
|
||||||
|
#
|
||||||
|
# Build :
|
||||||
|
# this job builds the application artifacts and archives them
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs:
|
||||||
|
- update-version
|
||||||
|
- commit-release
|
||||||
|
if: always() && needs.update-version.result == 'success' && needs.commit-release.result == 'success'
|
||||||
|
uses: ./.github/workflows/build.yml
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
with:
|
||||||
|
version: ${{ needs.update-version.outputs.version }}
|
||||||
|
tag_name: ${{ needs.update-version.outputs.tag_name }}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Release :
|
||||||
|
# this job creates a GitHub release and uploads the archive files
|
||||||
|
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
if: always() && needs.build.result == 'success'
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: AutoLibrary.${{ needs.build.outputs.tag_name }}-windows-x86_64
|
||||||
|
path: artifacts/
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
id: create_release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ needs.build.outputs.tag_name }}
|
||||||
|
name: AutoLibrary ${{ needs.build.outputs.tag_name }}
|
||||||
|
files: |
|
||||||
|
artifacts/AutoLibrary.${{ needs.build.outputs.tag_name }}-windows-x86_64.zip
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
generate_release_notes: true
|
||||||
|
body: |
|
||||||
|
### 下载获取
|
||||||
|
- **Windows x86_64**: `AutoLibrary.${{ needs.build.outputs.tag_name }}-windows-x86_64.zip`
|
||||||
|
|
||||||
|
### 如何使用
|
||||||
|
1. 下载 `AutoLibrary.${{ needs.build.outputs.tag_name }}-windows-x86_64.zip` 文件
|
||||||
|
2. 解压到任意目录
|
||||||
|
3. 下载对应浏览器的驱动文件
|
||||||
|
4. 运行 `AutoLibrary-${{ needs.build.outputs.version }}.exe` (首次运行会初始化配置文件)
|
||||||
|
5. 按照提示操作即可
|
||||||
|
|
||||||
|
更多详情请访问 [AutoLibrary 网站](http://autolibrary.cv) 和查看 [帮助手册](https://autolibrary.cv/docs/manual_lists.html)
|
||||||
|
|
||||||
|
---
|
||||||
|
**完整更新日志见下方自动生成的 Release Notes**
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# End :
|
||||||
|
# virtual job that indacates the end of the release process
|
||||||
|
#
|
||||||
|
|
||||||
|
end:
|
||||||
|
needs:
|
||||||
|
- release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: End release
|
||||||
|
run: |
|
||||||
|
echo "✓ Ending release"
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
name: Update Version Info
|
name: Update Version
|
||||||
|
|
||||||
|
# This workflow updates version information in 'ALVersionInfo.py' with build metadata.
|
||||||
|
# In progress, it will generate two version files, the first one is locate in 'src/gui/ALVersionInfo.py',
|
||||||
|
# and the second one is locate in 'src/gui/temp/ALVersionInfo.py'. The first one is use
|
||||||
|
# in the release process, it only update the version and tag name. The commit and build infomation
|
||||||
|
# is 'local' or 'null'. All of them will finally archive as artifacts.
|
||||||
|
#
|
||||||
|
# It is triggered when called by the release workflow.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
tag_name:
|
tag_name:
|
||||||
description: 'Tag name (e.g., v1.0.0)'
|
description: 'Tag name'
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
ref:
|
ref:
|
||||||
@@ -14,16 +22,16 @@ on:
|
|||||||
outputs:
|
outputs:
|
||||||
tag_name:
|
tag_name:
|
||||||
description: 'The tag name'
|
description: 'The tag name'
|
||||||
value: ${{ jobs.update-version-info.outputs.tag_name }}
|
value: ${{ jobs.update-version.outputs.tag_name }}
|
||||||
version:
|
version:
|
||||||
description: 'The version number'
|
description: 'The version number'
|
||||||
value: ${{ jobs.update-version-info.outputs.version }}
|
value: ${{ jobs.update-version.outputs.version }}
|
||||||
has_changes:
|
has_changes:
|
||||||
description: 'Whether ALVersionInfo.py was modified'
|
description: 'Whether ALVersionInfo.py was modified'
|
||||||
value: ${{ jobs.update-version-info.outputs.has_changes }}
|
value: ${{ jobs.update-version.outputs.has_changes }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-version-info:
|
update-version:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -59,17 +67,23 @@ jobs:
|
|||||||
echo "✓ Commit SHA: $COMMIT_SHA"
|
echo "✓ Commit SHA: $COMMIT_SHA"
|
||||||
echo "✓ Commit Date: $COMMIT_DATE"
|
echo "✓ Commit Date: $COMMIT_DATE"
|
||||||
|
|
||||||
|
- name: Create 'temp' directory
|
||||||
|
run: |
|
||||||
|
echo "Creating temp directory..."
|
||||||
|
mkdir -p "src/gui/temp"
|
||||||
|
echo "✓ temp directory created successfully"
|
||||||
|
|
||||||
- name: Update ALVersionInfo.py with version info
|
- name: Update ALVersionInfo.py with version info
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.get_version.outputs.VERSION }}"
|
VERSION="${{ steps.get_version.outputs.VERSION }}"
|
||||||
TAG_NAME="${{ steps.get_version.outputs.TAG_NAME }}"
|
TAG_NAME="${{ steps.get_version.outputs.TAG_NAME }}"
|
||||||
COMMIT_SHA="${{ steps.get_version.outputs.COMMIT_SHA }}"
|
COMMIT_SHA="${{ steps.get_version.outputs.COMMIT_SHA }}"
|
||||||
COMMIT_DATE="${{ steps.get_version.outputs.COMMIT_DATE }}"
|
COMMIT_DATE="${{ steps.get_version.outputs.COMMIT_DATE }}"
|
||||||
APP_INFO_FILE="src/gui/ALVersionInfo.py"
|
VER_INFO_BUILDFILE="src/gui/temp/ALVersionInfo.py"
|
||||||
|
VER_INFO_COMMITFILE="src/gui/ALVersionInfo.py"
|
||||||
BUILD_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
BUILD_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
||||||
|
|
||||||
echo "Updating $APP_INFO_FILE with build information..."
|
echo "Updating ALVersionInfo.py files with build information..."
|
||||||
|
|
||||||
{
|
{
|
||||||
echo '# -*- coding: utf-8 -*-'
|
echo '# -*- coding: utf-8 -*-'
|
||||||
echo ''
|
echo ''
|
||||||
@@ -87,13 +101,39 @@ jobs:
|
|||||||
echo "AL_COMMIT_DATE = \"${COMMIT_DATE}\" # time zone : UTC"
|
echo "AL_COMMIT_DATE = \"${COMMIT_DATE}\" # time zone : UTC"
|
||||||
echo "AL_BUILD_DATE = \"${BUILD_DATE}\" # time zone : UTC"
|
echo "AL_BUILD_DATE = \"${BUILD_DATE}\" # time zone : UTC"
|
||||||
echo 'AL_VERSION_FULL = f"{AL_VERSION} ({AL_COMMIT_SHA})"'
|
echo 'AL_VERSION_FULL = f"{AL_VERSION} ({AL_COMMIT_SHA})"'
|
||||||
} > "$APP_INFO_FILE"
|
} > "$VER_INFO_BUILDFILE"
|
||||||
|
|
||||||
echo "✓ ALVersionInfo.py updated successfully"
|
echo "Updating ALVersionInfo.py for release commit..."
|
||||||
|
{
|
||||||
|
echo '# -*- coding: utf-8 -*-'
|
||||||
|
echo ''
|
||||||
|
echo '"""'
|
||||||
|
echo ' The contents of this file will automatically be updated by the'
|
||||||
|
echo ' workflow process. Do not edit manually.'
|
||||||
|
echo ''
|
||||||
|
echo ' This file is auto-generated during the workflow process.'
|
||||||
|
echo " Last updated: ${BUILD_DATE}"
|
||||||
|
echo '"""'
|
||||||
|
echo ''
|
||||||
|
echo "AL_VERSION = \"${VERSION}\""
|
||||||
|
echo "AL_TAG = \"${TAG_NAME}\""
|
||||||
|
echo "AL_COMMIT_SHA = \"local\""
|
||||||
|
echo "AL_COMMIT_DATE = \"null\" # time zone : UTC"
|
||||||
|
echo "AL_BUILD_DATE = \"null\" # time zone : UTC"
|
||||||
|
echo 'AL_VERSION_FULL = f"{AL_VERSION} ({AL_COMMIT_SHA})"'
|
||||||
|
} > "$VER_INFO_COMMITFILE"
|
||||||
|
|
||||||
|
echo "✓ ALVersionInfo.py files updated successfully"
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Updated ALVersionInfo.py ==="
|
echo "Build version file location: $VER_INFO_BUILDFILE"
|
||||||
cat "$APP_INFO_FILE"
|
echo "Commit version file location: $VER_INFO_COMMITFILE"
|
||||||
echo "=========================="
|
echo ""
|
||||||
|
echo "Build version ALVersionInfo.py content ="
|
||||||
|
cat "$VER_INFO_BUILDFILE"
|
||||||
|
echo ""
|
||||||
|
echo "Commit version ALVersionInfo.py content "
|
||||||
|
cat "$VER_INFO_COMMITFILE"
|
||||||
|
echo "========================================"
|
||||||
|
|
||||||
- name: Check if ALVersionInfo.py was modified
|
- name: Check if ALVersionInfo.py was modified
|
||||||
id: check_changes
|
id: check_changes
|
||||||
@@ -106,10 +146,18 @@ jobs:
|
|||||||
echo "✓ ALVersionInfo.py has been modified"
|
echo "✓ ALVersionInfo.py has been modified"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Upload modified ALVersionInfo.py
|
- name: Upload modified ALVersionInfo.py ready for build
|
||||||
if: steps.check_changes.outputs.has_changes == 'true'
|
if: steps.check_changes.outputs.has_changes == 'true'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: updated-version-info
|
name: updated-version-info-for-build
|
||||||
|
path: src/gui/temp/ALVersionInfo.py
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
- name: Upload modified ALVersionInfo.py ready for commit
|
||||||
|
if: steps.check_changes.outputs.has_changes == 'true'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: updated-version-info-for-commit
|
||||||
path: src/gui/ALVersionInfo.py
|
path: src/gui/ALVersionInfo.py
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2025 KenanZhu
|
Copyright 2025 - 2026 KenanZhu
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
|
|
||||||

|

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

|

|
||||||

|
[](https://github.com/KenanZhu/AutoLibrary/actions/workflows/release.yml)
|
||||||

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

|

|
||||||
|
|
||||||
了解更多请访问 [_AutoLibrary 网站_](http://autolibrary.cv)
|
了解更多请访问 [_AutoLibrary 网站_](http://autolibrary.cv)
|
||||||
|
|
||||||
@@ -23,9 +24,9 @@
|
|||||||
|
|
||||||
*1,2,3 的具体操作方法和注意事项请访问我们的 [帮助手册](https://autolibrary.cv/docs/manual_lists.html)*
|
*1,2,3 的具体操作方法和注意事项请访问我们的 [帮助手册](https://autolibrary.cv/docs/manual_lists.html)*
|
||||||
|
|
||||||
### 特点
|
### 注意事项
|
||||||
|
|
||||||
#### 关于预约等操作的注意事项
|
#### 关于预约等操作
|
||||||
|
|
||||||
工具会自动处理登录过程的验证码识别过程,正常情况下单次识别准确率在 90% 以上,如遇验证码识别错误,大概率是校园网网络环境不佳导致的。
|
工具会自动处理登录过程的验证码识别过程,正常情况下单次识别准确率在 90% 以上,如遇验证码识别错误,大概率是校园网网络环境不佳导致的。
|
||||||
|
|
||||||
@@ -34,11 +35,11 @@
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> 工具仅作为正常的预约,签到和续约的图书馆辅助工具,请勿干扰图书馆的正常运行(如故意预约多个座位,或同时预约大量的用户等,对此影响图书馆正常运行本工具概不负责,请在善用工具方便自己的情况下尽量不用影响其他同学的使用)。
|
> 工具仅作为正常的预约,签到和续约的图书馆辅助工具,请勿干扰图书馆的正常运行(如故意预约多个座位,或同时预约大量的用户等,对此影响图书馆正常运行本工具概不负责,请在善用工具方便自己的情况下尽量不用影响其他同学的使用)。
|
||||||
|
|
||||||
#### 关于批量操作的注意事项
|
#### 关于批量操作
|
||||||
|
|
||||||
批量操作时,建议将需要操作的用户分成多个组,每个组的用户数量不要超过 4 人(即一整张桌子的数量),否则会影响操作效率,大量用户同时预约会一定程度上增加图书馆服务器的压力,影响正常使用。根据需要在用户管理界面中可以勾选本次操作是否跳过该用户,以提高运行效率。
|
批量操作时,建议将需要操作的用户分成多个组,每个组的用户数量不要超过 4 人(即一整张桌子的数量),否则会影响操作效率,大量用户同时预约会一定程度上增加图书馆服务器的压力,影响正常使用。根据需要在用户管理界面中可以勾选本次操作是否跳过该用户,以提高运行效率。
|
||||||
|
|
||||||
#### 关于定时任务的注意事项
|
#### 关于定时任务
|
||||||
|
|
||||||
定时任务会在指定的时间自动运行,运行时会根据当前预约信息进行操作。一般情况下不建议设置两个运行开始时间比较接近的定时任务,否则后一个任务会等待前一个任务完成后才会运行,按照队列的顺序执行。
|
定时任务会在指定的时间自动运行,运行时会根据当前预约信息进行操作。一般情况下不建议设置两个运行开始时间比较接近的定时任务,否则后一个任务会等待前一个任务完成后才会运行,按照队列的顺序执行。
|
||||||
|
|
||||||
@@ -97,7 +98,7 @@ def classification(self, img: bytes):
|
|||||||
|
|
||||||
#### 后续会有哪些功能?
|
#### 后续会有哪些功能?
|
||||||
|
|
||||||
当前 v1.0.0 版本的功能对于正常使用已经足够,不过后续会着重考虑完善 2-4 人预约时的使用体验,暂时有以下构想:
|
当前版本的功能对于正常使用已经足够,不过后续会着重考虑完善 2-4 人预约时的使用体验,暂时有以下构想:
|
||||||
|
|
||||||
1. 2-4 人一起预约时,往往会偏向于预约并排或对面的整个空座位,这时候工具会按照一定策略查询搜索符合条件的座位,并预约并排或对面的整个座位,而不是各自独立预约。
|
1. 2-4 人一起预约时,往往会偏向于预约并排或对面的整个空座位,这时候工具会按照一定策略查询搜索符合条件的座位,并预约并排或对面的整个座位,而不是各自独立预约。
|
||||||
2. 预约时会考虑到组内用户的预约时间是否冲突,若冲突则会提示用户是否继续预约,若用户选择继续预约,则会按需要调整预约时间,再进行预约。
|
2. 预约时会考虑到组内用户的预约时间是否冲突,若冲突则会提示用户是否继续预约,若用户选择继续预约,则会按需要调整预约时间,再进行预约。
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -13,6 +13,13 @@ from base.MsgBase import MsgBase
|
|||||||
|
|
||||||
|
|
||||||
class LibOperator(MsgBase):
|
class LibOperator(MsgBase):
|
||||||
|
"""
|
||||||
|
Base abstract class for library operation.
|
||||||
|
|
||||||
|
This class provides the foundation for library-related operations, inheriting
|
||||||
|
message handling and tracing abilities from MsgBase. It serves as an abstract
|
||||||
|
base class that must be subclassed to implement specific library functionality.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|||||||
+17
-1
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -12,6 +12,22 @@ import queue
|
|||||||
|
|
||||||
|
|
||||||
class MsgBase:
|
class MsgBase:
|
||||||
|
"""
|
||||||
|
Base class for message and trace abilities (thread-safe).
|
||||||
|
|
||||||
|
This class provides the foundation for message handling and tracing
|
||||||
|
abilities based on the provided input and output queues. It enables
|
||||||
|
thread-safe communication between components using queue-based messaging.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input_queue (queue.Queue): The input queue for receiving messages.
|
||||||
|
output_queue (queue.Queue): The output queue for sending messages.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
This class must be initialized with input and output queues. The queue
|
||||||
|
provider (the caller of this class or its subclasses) must explicitly
|
||||||
|
implement queue polling to retrieve and process messages.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -60,7 +60,7 @@ class ALAboutDialog(QDialog, Ui_ALAboutDialog):
|
|||||||
|
|
||||||
def generateAboutText(
|
def generateAboutText(
|
||||||
self
|
self
|
||||||
):
|
) -> str:
|
||||||
|
|
||||||
os_info = self.getOSInfo()
|
os_info = self.getOSInfo()
|
||||||
about_text = f"""
|
about_text = f"""
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="frame">
|
<widget class="QFrame" name="AboutInfoSpaceFrame">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>56</width>
|
<width>56</width>
|
||||||
|
|||||||
@@ -1,31 +1,24 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
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.
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
See the LICENSE file for details.
|
See the LICENSE file for details.
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import uuid
|
import uuid
|
||||||
import queue
|
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from PySide6.QtCore import (
|
from PySide6.QtCore import (
|
||||||
Qt, Signal, Slot, QDateTime
|
Slot, QDateTime
|
||||||
)
|
)
|
||||||
from PySide6.QtWidgets import (
|
from PySide6.QtWidgets import (
|
||||||
QLabel, QDialog, QWidget, QSpinBox, QVBoxLayout,
|
QLabel, QDialog, QWidget, QSpinBox,
|
||||||
QHBoxLayout, QGridLayout, QDateTimeEdit
|
QHBoxLayout, QGridLayout, QDateTimeEdit
|
||||||
)
|
)
|
||||||
from PySide6.QtGui import (
|
|
||||||
QCloseEvent
|
|
||||||
)
|
|
||||||
|
|
||||||
from gui.Ui_ALAddTimerTaskDialog import Ui_ALAddTimerTaskDialog
|
from gui.Ui_ALAddTimerTaskDialog import Ui_ALAddTimerTaskDialog
|
||||||
|
|
||||||
|
|||||||
@@ -86,16 +86,16 @@
|
|||||||
<number>5</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="topMargin">
|
<property name="topMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="TimerTypeSelectLayout">
|
<layout class="QHBoxLayout" name="TimerTypeSelectLayout">
|
||||||
@@ -135,16 +135,16 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="TaskConfigLayout">
|
<layout class="QGridLayout" name="TaskConfigLayout">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="topMargin">
|
<property name="topMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>5</number>
|
<number>5</number>
|
||||||
|
|||||||
+13
-10
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -33,7 +33,7 @@ from utils.ConfigWriter import ConfigWriter
|
|||||||
|
|
||||||
class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
||||||
|
|
||||||
configWidgetCloseSingal = Signal(dict)
|
configWidgetIsClosed = Signal(dict)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -45,12 +45,11 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.setupUi(self)
|
|
||||||
self.__config_paths = config_paths
|
self.__config_paths = config_paths
|
||||||
self.__config_data = {"run": {}, "user": {}}
|
self.__config_data = {"run": {}, "user": {}}
|
||||||
self.__seat_map_widget = None
|
self.__seat_map_widget = None
|
||||||
|
|
||||||
|
self.setupUi(self)
|
||||||
self.modifyUi()
|
self.modifyUi()
|
||||||
self.connectSignals()
|
self.connectSignals()
|
||||||
self.initlizeFloorRoomMap()
|
self.initlizeFloorRoomMap()
|
||||||
@@ -127,7 +126,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
event: QCloseEvent
|
event: QCloseEvent
|
||||||
):
|
):
|
||||||
|
|
||||||
self.configWidgetCloseSingal.emit(self.__config_paths)
|
self.configWidgetIsClosed.emit(self.__config_paths)
|
||||||
super().closeEvent(event)
|
super().closeEvent(event)
|
||||||
|
|
||||||
|
|
||||||
@@ -143,12 +142,12 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
}
|
}
|
||||||
self.__room_map = {
|
self.__room_map = {
|
||||||
"1": "二层内环",
|
"1": "二层内环",
|
||||||
"2": "二层外环",
|
"2": "二层西区",
|
||||||
"3": "三层内环",
|
"3": "三层内环",
|
||||||
"4": "三层外环",
|
"4": "三层外环",
|
||||||
"5": "四层内环",
|
"5": "四层内环",
|
||||||
"6": "四层外环",
|
"6": "四层外环",
|
||||||
"7": "四层期刊区",
|
"7": "四层期刊",
|
||||||
"8": "五层考研"
|
"8": "五层考研"
|
||||||
}
|
}
|
||||||
self.__floor_rmap = {
|
self.__floor_rmap = {
|
||||||
@@ -158,9 +157,9 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
v: k for k, v in self.__room_map.items()
|
v: k for k, v in self.__room_map.items()
|
||||||
}
|
}
|
||||||
self.__floor_room_map = {
|
self.__floor_room_map = {
|
||||||
"二层": ["二层内环", "二层外环"],
|
"二层": ["二层内环", "二层西区"],
|
||||||
"三层": ["三层内环", "三层外环"],
|
"三层": ["三层内环", "三层外环"],
|
||||||
"四层": ["四层内环", "四层外环", "四层期刊区"],
|
"四层": ["四层内环", "四层外环", "四层期刊"],
|
||||||
"五层": ["五层考研"]
|
"五层": ["五层考研"]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +263,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
},
|
},
|
||||||
"web_driver": {
|
"web_driver": {
|
||||||
"driver_type": "edge",
|
"driver_type": "edge",
|
||||||
"driver_path": "msedgedriver.exe",
|
"driver_path": "",
|
||||||
"headless": False
|
"headless": False
|
||||||
},
|
},
|
||||||
"mode": {
|
"mode": {
|
||||||
@@ -334,7 +333,10 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
self.AutoCaptchaCheckBox.setChecked(run_config["login"]["auto_captcha"])
|
self.AutoCaptchaCheckBox.setChecked(run_config["login"]["auto_captcha"])
|
||||||
self.LoginAttemptSpinBox.setValue(run_config["login"]["max_attempt"])
|
self.LoginAttemptSpinBox.setValue(run_config["login"]["max_attempt"])
|
||||||
self.BrowserTypeComboBox.setCurrentText(run_config["web_driver"]["driver_type"])
|
self.BrowserTypeComboBox.setCurrentText(run_config["web_driver"]["driver_type"])
|
||||||
|
if run_config["web_driver"]["driver_path"]:
|
||||||
driver_path = os.path.abspath(run_config["web_driver"]["driver_path"])
|
driver_path = os.path.abspath(run_config["web_driver"]["driver_path"])
|
||||||
|
else:
|
||||||
|
driver_path = ""
|
||||||
self.BrowseBrowserDriverEdit.setText(QDir.toNativeSeparators(driver_path))
|
self.BrowseBrowserDriverEdit.setText(QDir.toNativeSeparators(driver_path))
|
||||||
self.HeadlessCheckBox.setChecked(run_config["web_driver"]["headless"])
|
self.HeadlessCheckBox.setChecked(run_config["web_driver"]["headless"])
|
||||||
run_mode = run_config["mode"]["run_mode"]
|
run_mode = run_config["mode"]["run_mode"]
|
||||||
@@ -800,6 +802,7 @@ class ALConfigWidget(QWidget, Ui_ALConfigWidget):
|
|||||||
self.__seat_map_widget.deleteLater()
|
self.__seat_map_widget.deleteLater()
|
||||||
self.__seat_map_widget = None
|
self.__seat_map_widget = None
|
||||||
if len(selected_seats) == 0:
|
if len(selected_seats) == 0:
|
||||||
|
self.SeatIDEdit.clear() # no selected seat, we clear the edit
|
||||||
return
|
return
|
||||||
self.SeatIDEdit.setText(",".join(selected_seats))
|
self.SeatIDEdit.setText(",".join(selected_seats))
|
||||||
|
|
||||||
|
|||||||
+64
-18
@@ -63,7 +63,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="UserConfigWidget">
|
<widget class="QWidget" name="UserConfigWidget">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>用户设置</string>
|
<string>用户配置</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QHBoxLayout" name="UserConfigWidgetLayout">
|
<layout class="QHBoxLayout" name="UserConfigWidgetLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
@@ -94,19 +94,19 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="UserListLayout">
|
<layout class="QVBoxLayout" name="UserListLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="topMargin">
|
<property name="topMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>5</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTreeWidget" name="UserTreeWidget">
|
<widget class="QTreeWidget" name="UserTreeWidget">
|
||||||
@@ -178,6 +178,9 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="UserListControlLayout">
|
<layout class="QHBoxLayout" name="UserListControlLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="DelUserButton">
|
<widget class="QPushButton" name="DelUserButton">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
@@ -309,7 +312,7 @@
|
|||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<layout class="QHBoxLayout" name="PasswordLayout">
|
<layout class="QHBoxLayout" name="PasswordLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="PasswordEdit">
|
<widget class="QLineEdit" name="PasswordEdit">
|
||||||
@@ -546,7 +549,7 @@
|
|||||||
<item row="4" column="4">
|
<item row="4" column="4">
|
||||||
<layout class="QHBoxLayout" name="SeatIDLayout">
|
<layout class="QHBoxLayout" name="SeatIDLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="SeatIDEdit">
|
<widget class="QLineEdit" name="SeatIDEdit">
|
||||||
@@ -703,7 +706,7 @@
|
|||||||
<item row="10" column="4">
|
<item row="10" column="4">
|
||||||
<layout class="QHBoxLayout" name="EndTimeDiffLayout">
|
<layout class="QHBoxLayout" name="EndTimeDiffLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="MaxEndTimeDiffSpinBox">
|
<widget class="QSpinBox" name="MaxEndTimeDiffSpinBox">
|
||||||
@@ -899,13 +902,13 @@
|
|||||||
<item row="7" column="4">
|
<item row="7" column="4">
|
||||||
<layout class="QHBoxLayout" name="BeginTimeDiffLayout">
|
<layout class="QHBoxLayout" name="BeginTimeDiffLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="MaxBeginTimeDiffSpinBox">
|
<widget class="QSpinBox" name="MaxBeginTimeDiffSpinBox">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>65</width>
|
<width>55</width>
|
||||||
<height>25</height>
|
<height>25</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@@ -1049,10 +1052,23 @@
|
|||||||
<item row="15" column="4">
|
<item row="15" column="4">
|
||||||
<layout class="QHBoxLayout" name="RenewTimeDiffLayout">
|
<layout class="QHBoxLayout" name="RenewTimeDiffLayout">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="MaxRenewTimeDiffSpinBox"/>
|
<widget class="QSpinBox" name="MaxRenewTimeDiffSpinBox">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>55</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="PreferLateRenewTimeCheckBox">
|
<widget class="QCheckBox" name="PreferLateRenewTimeCheckBox">
|
||||||
@@ -1079,7 +1095,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="15" column="1">
|
<item row="15" column="1">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="MaxRenewTimeDiffLabel">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>80</width>
|
<width>80</width>
|
||||||
@@ -1107,7 +1123,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="RunConfigWidget">
|
<widget class="QWidget" name="RunConfigWidget">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>运行设置</string>
|
<string>运行配置</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout" name="SystemConfigWidgetLayout">
|
<layout class="QGridLayout" name="SystemConfigWidgetLayout">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
@@ -1140,10 +1156,10 @@
|
|||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>3</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="horizontalSpacing">
|
<property name="bottomMargin">
|
||||||
<number>3</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="verticalSpacing">
|
<property name="spacing">
|
||||||
<number>5</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="0" colspan="2">
|
<item row="0" column="0" colspan="2">
|
||||||
@@ -1636,6 +1652,21 @@
|
|||||||
<string>当前配置</string>
|
<string>当前配置</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="CurrentConfigLayout">
|
<layout class="QGridLayout" name="CurrentConfigLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLineEdit" name="CurrentRunConfigEdit">
|
<widget class="QLineEdit" name="CurrentRunConfigEdit">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
@@ -1730,7 +1761,7 @@
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>;;;</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -1762,6 +1793,21 @@
|
|||||||
<string>导出路径</string>
|
<string>导出路径</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="ExportConfigLayout">
|
<layout class="QGridLayout" name="ExportConfigLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLineEdit" name="ExportUserConfigEdit">
|
<widget class="QLineEdit" name="ExportUserConfigEdit">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
|
|||||||
+10
-15
@@ -1,13 +1,12 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
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.
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
See the LICENSE file for details.
|
See the LICENSE file for details.
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import queue
|
import queue
|
||||||
@@ -30,9 +29,6 @@ from gui.ALMainWorkers import TimerTaskWorker, AutoLibWorker
|
|||||||
|
|
||||||
from gui import AutoLibraryResource
|
from gui import AutoLibraryResource
|
||||||
|
|
||||||
from utils.ConfigReader import ConfigReader
|
|
||||||
from utils.ConfigWriter import ConfigWriter
|
|
||||||
|
|
||||||
|
|
||||||
class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
||||||
|
|
||||||
@@ -46,8 +42,6 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
|||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.__class_name = self.__class__.__name__
|
self.__class_name = self.__class__.__name__
|
||||||
|
|
||||||
self.setupUi(self)
|
|
||||||
self.__input_queue = queue.Queue()
|
self.__input_queue = queue.Queue()
|
||||||
self.__output_queue = queue.Queue()
|
self.__output_queue = queue.Queue()
|
||||||
self.__timer_task_queue = queue.Queue()
|
self.__timer_task_queue = queue.Queue()
|
||||||
@@ -64,6 +58,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
|||||||
self.__current_timer_task_thread = None
|
self.__current_timer_task_thread = None
|
||||||
self.__is_running_timer_task = False
|
self.__is_running_timer_task = False
|
||||||
|
|
||||||
|
self.setupUi(self)
|
||||||
self.modifyUi()
|
self.modifyUi()
|
||||||
self.setupTray()
|
self.setupTray()
|
||||||
self.connectSignals()
|
self.connectSignals()
|
||||||
@@ -246,7 +241,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
|||||||
self.__output_queue,
|
self.__output_queue,
|
||||||
self.__config_paths
|
self.__config_paths
|
||||||
)
|
)
|
||||||
self.__current_timer_task_thread.finishedSignal_TimerWorker.connect(self.onTimerTaskFinished)
|
self.__current_timer_task_thread.TimerTaskWorkerIsFinished.connect(self.onTimerTaskFinished)
|
||||||
self.__current_timer_task_thread.start()
|
self.__current_timer_task_thread.start()
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
self.__is_running_timer_task = False
|
self.__is_running_timer_task = False
|
||||||
@@ -311,7 +306,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
|||||||
):
|
):
|
||||||
|
|
||||||
if self.__alConfigWidget:
|
if self.__alConfigWidget:
|
||||||
self.__alConfigWidget.configWidgetCloseSingal.disconnect(self.onConfigWidgetClosed)
|
self.__alConfigWidget.configWidgetIsClosed.disconnect(self.onConfigWidgetClosed)
|
||||||
self.__alConfigWidget.deleteLater()
|
self.__alConfigWidget.deleteLater()
|
||||||
self.__alConfigWidget = None
|
self.__alConfigWidget = None
|
||||||
self.setControlButtons(True, None, None)
|
self.setControlButtons(True, None, None)
|
||||||
@@ -333,7 +328,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
|||||||
):
|
):
|
||||||
|
|
||||||
self.__current_timer_task_thread.wait(1000)
|
self.__current_timer_task_thread.wait(1000)
|
||||||
self.__current_timer_task_thread.finishedSignal_TimerWorker.disconnect(self.onTimerTaskFinished)
|
self.__current_timer_task_thread.TimerTaskWorkerIsFinished.disconnect(self.onTimerTaskFinished)
|
||||||
self.__current_timer_task_thread.deleteLater()
|
self.__current_timer_task_thread.deleteLater()
|
||||||
self.__current_timer_task_thread = None
|
self.__current_timer_task_thread = None
|
||||||
self.setControlButtons(None, False, True)
|
self.setControlButtons(None, False, True)
|
||||||
@@ -374,7 +369,7 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
|||||||
self,
|
self,
|
||||||
self.__config_paths
|
self.__config_paths
|
||||||
)
|
)
|
||||||
self.__alConfigWidget.configWidgetCloseSingal.connect(self.onConfigWidgetClosed)
|
self.__alConfigWidget.configWidgetIsClosed.connect(self.onConfigWidgetClosed)
|
||||||
self.__alConfigWidget.show()
|
self.__alConfigWidget.show()
|
||||||
self.__alConfigWidget.raise_()
|
self.__alConfigWidget.raise_()
|
||||||
self.__alConfigWidget.activateWindow()
|
self.__alConfigWidget.activateWindow()
|
||||||
@@ -392,8 +387,8 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
|||||||
self.__output_queue,
|
self.__output_queue,
|
||||||
self.__config_paths
|
self.__config_paths
|
||||||
)
|
)
|
||||||
self.__auto_lib_thread.finishedSignal.connect(self.onStopButtonClicked)
|
self.__auto_lib_thread.AutoLibWorkerIsFinished.connect(self.onStopButtonClicked)
|
||||||
self.__auto_lib_thread.finishedWithErrorSignal.connect(self.onStopButtonClicked)
|
self.__auto_lib_thread.AutoLibWorkerFinishedWithError.connect(self.onStopButtonClicked)
|
||||||
self.__auto_lib_thread.start()
|
self.__auto_lib_thread.start()
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
@@ -405,8 +400,8 @@ class ALMainWindow(QMainWindow, Ui_ALMainWindow):
|
|||||||
self.showTrace("正在停止操作......")
|
self.showTrace("正在停止操作......")
|
||||||
self.__auto_lib_thread.wait(2000)
|
self.__auto_lib_thread.wait(2000)
|
||||||
self.showTrace("操作已停止")
|
self.showTrace("操作已停止")
|
||||||
self.__auto_lib_thread.finishedSignal.disconnect(self.onStopButtonClicked)
|
self.__auto_lib_thread.AutoLibWorkerIsFinished.disconnect(self.onStopButtonClicked)
|
||||||
self.__auto_lib_thread.finishedWithErrorSignal.disconnect(self.onStopButtonClicked)
|
self.__auto_lib_thread.AutoLibWorkerFinishedWithError.disconnect(self.onStopButtonClicked)
|
||||||
self.__auto_lib_thread.deleteLater()
|
self.__auto_lib_thread.deleteLater()
|
||||||
self.__auto_lib_thread = None
|
self.__auto_lib_thread = None
|
||||||
self.setControlButtons(None, False, True)
|
self.setControlButtons(None, False, True)
|
||||||
|
|||||||
@@ -34,13 +34,13 @@
|
|||||||
<number>5</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>3</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="topMargin">
|
<property name="topMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>3</number>
|
<number>5</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
@@ -156,11 +156,9 @@
|
|||||||
<string><html><head/><body><p><br/></p></body></html></string>
|
<string><html><head/><body><p><br/></p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="styleSheet">
|
<property name="styleSheet">
|
||||||
<string notr="true">background-color: rgb(10, 170, 10);
|
<string notr="true">background-color: #0AAA0A;
|
||||||
font: 12pt "Microsoft YaHei UI";
|
color: #FFFFFF;
|
||||||
color: rgb(255, 255, 255);
|
font: 700 9pt;</string>
|
||||||
font: 9pt "Segoe UI";
|
|
||||||
font: 700 9pt "Microsoft YaHei UI";</string>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>启动脚本</string>
|
<string>启动脚本</string>
|
||||||
|
|||||||
+14
-18
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -22,8 +22,8 @@ from utils.ConfigReader import ConfigReader
|
|||||||
|
|
||||||
class AutoLibWorker(QThread, MsgBase):
|
class AutoLibWorker(QThread, MsgBase):
|
||||||
|
|
||||||
finishedSignal = Signal()
|
AutoLibWorkerIsFinished = Signal()
|
||||||
finishedWithErrorSignal = Signal()
|
AutoLibWorkerFinishedWithError = Signal()
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -69,15 +69,11 @@ class AutoLibWorker(QThread, MsgBase):
|
|||||||
self._showTrace(
|
self._showTrace(
|
||||||
f"正在加载配置文件, 运行配置文件路径: {self.__config_paths["run"]}"
|
f"正在加载配置文件, 运行配置文件路径: {self.__config_paths["run"]}"
|
||||||
)
|
)
|
||||||
self.__run_config = ConfigReader(
|
self.__run_config = ConfigReader(self.__config_paths["run"]).getConfigs()
|
||||||
self.__config_paths["run"]
|
|
||||||
).getConfigs()
|
|
||||||
self._showTrace(
|
self._showTrace(
|
||||||
f"正在加载配置文件, 用户配置文件路径: {self.__config_paths["user"]}"
|
f"正在加载配置文件, 用户配置文件路径: {self.__config_paths["user"]}"
|
||||||
)
|
)
|
||||||
self.__user_config = ConfigReader(
|
self.__user_config = ConfigReader(self.__config_paths["user"]).getConfigs()
|
||||||
self.__config_paths["user"]
|
|
||||||
).getConfigs()
|
|
||||||
if self.__run_config is None or self.__user_config is None:
|
if self.__run_config is None or self.__user_config is None:
|
||||||
self._showTrace("配置文件加载失败, 请检查配置文件是否正确")
|
self._showTrace("配置文件加载失败, 请检查配置文件是否正确")
|
||||||
self._showTrace("配置文件加载失败, 请检查配置文件是否正确")
|
self._showTrace("配置文件加载失败, 请检查配置文件是否正确")
|
||||||
@@ -120,17 +116,17 @@ class AutoLibWorker(QThread, MsgBase):
|
|||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._showTrace(f"AutoLibrary 运行时发生异常 : {e}")
|
self._showTrace(f"AutoLibrary 运行时发生异常 : {e}")
|
||||||
self.finishedWithErrorSignal.emit()
|
self.AutoLibWorkerFinishedWithError.emit()
|
||||||
return
|
return
|
||||||
if auto_lib:
|
if auto_lib:
|
||||||
auto_lib.close()
|
auto_lib.close()
|
||||||
self._showTrace("AutoLibrary 运行结束")
|
self._showTrace("AutoLibrary 运行结束")
|
||||||
self.finishedSignal.emit()
|
self.AutoLibWorkerIsFinished.emit()
|
||||||
|
|
||||||
|
|
||||||
class TimerTaskWorker(AutoLibWorker):
|
class TimerTaskWorker(AutoLibWorker):
|
||||||
|
|
||||||
finishedSignal_TimerWorker = Signal(bool, dict)
|
TimerTaskWorkerIsFinished = Signal(bool, dict)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -143,8 +139,8 @@ class TimerTaskWorker(AutoLibWorker):
|
|||||||
super().__init__(input_queue, output_queue, config_paths)
|
super().__init__(input_queue, output_queue, config_paths)
|
||||||
|
|
||||||
self.__timer_task = timer_task
|
self.__timer_task = timer_task
|
||||||
self.finishedSignal.connect(self.onTimerTaskIsFinished)
|
self.AutoLibWorkerIsFinished.connect(self.onTimerTaskIsFinished)
|
||||||
self.finishedWithErrorSignal.connect(self.onTimerTaskIsError)
|
self.AutoLibWorkerFinishedWithError.connect(self.onTimerTaskIsError)
|
||||||
|
|
||||||
def run(
|
def run(
|
||||||
self
|
self
|
||||||
@@ -153,18 +149,18 @@ class TimerTaskWorker(AutoLibWorker):
|
|||||||
self._showTrace(f"定时任务 {self.__timer_task['name']} 开始运行")
|
self._showTrace(f"定时任务 {self.__timer_task['name']} 开始运行")
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
@Slot(dict)
|
@Slot()
|
||||||
def onTimerTaskIsError(
|
def onTimerTaskIsError(
|
||||||
self
|
self
|
||||||
):
|
):
|
||||||
|
|
||||||
self._showTrace(f"定时任务 {self.__timer_task['name']} 运行时发生异常")
|
self._showTrace(f"定时任务 {self.__timer_task['name']} 运行时发生异常")
|
||||||
self.finishedSignal_TimerWorker.emit(True, self.__timer_task)
|
self.TimerTaskWorkerIsFinished.emit(True, self.__timer_task)
|
||||||
|
|
||||||
@Slot(dict)
|
@Slot()
|
||||||
def onTimerTaskIsFinished(
|
def onTimerTaskIsFinished(
|
||||||
self
|
self
|
||||||
):
|
):
|
||||||
|
|
||||||
self._showTrace(f"定时任务 {self.__timer_task['name']} 运行结束")
|
self._showTrace(f"定时任务 {self.__timer_task['name']} 运行结束")
|
||||||
self.finishedSignal_TimerWorker.emit(False, self.__timer_task)
|
self.TimerTaskWorkerIsFinished.emit(False, self.__timer_task)
|
||||||
|
|||||||
+14
-12
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -28,6 +28,7 @@ class ALSeatFrame(QFrame):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.__seat_number = seat_number
|
self.__seat_number = seat_number
|
||||||
self.__is_selected = False
|
self.__is_selected = False
|
||||||
|
|
||||||
self.setupUi()
|
self.setupUi()
|
||||||
|
|
||||||
def setupUi(
|
def setupUi(
|
||||||
@@ -39,18 +40,19 @@ class ALSeatFrame(QFrame):
|
|||||||
self.setLineWidth(2)
|
self.setLineWidth(2)
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
QFrame {
|
QFrame {
|
||||||
background-color: #4196EB;
|
background-color: #2294FF;
|
||||||
border: 2px solid #4196EB;
|
border: 2px solid #2294FF;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
QLabel {
|
QLabel {
|
||||||
color: #F0F0F0;
|
color: #FFFFFF;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
self.label = QLabel(self.__seat_number, self)
|
self.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||||
self.label.setAlignment(Qt.AlignCenter)
|
self.Label = QLabel(self.__seat_number, self)
|
||||||
self.label.setGeometry(0, 0, 60, 40)
|
self.Label.setAlignment(Qt.AlignCenter)
|
||||||
|
self.Label.setGeometry(0, 0, 60, 40)
|
||||||
|
|
||||||
def mousePressEvent(
|
def mousePressEvent(
|
||||||
self,
|
self,
|
||||||
@@ -76,24 +78,24 @@ class ALSeatFrame(QFrame):
|
|||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
QFrame {
|
QFrame {
|
||||||
background-color: #4CAF50;
|
background-color: #4CAF50;
|
||||||
border: 2px solid #388E3C;
|
border: 2px solid #4CAF50;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
QLabel {
|
QLabel {
|
||||||
color: #F0F0F0;
|
color: #FFFFFF;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
else:
|
else:
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
QFrame {
|
QFrame {
|
||||||
background-color: #4196EB;
|
background-color: #2294FF;
|
||||||
border: 2px solid #4196EB;
|
border: 2px solid #2294FF;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
QLabel {
|
QLabel {
|
||||||
color: #F0F0F0;
|
color: #FFFFFF;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -34,12 +34,13 @@ class ALSeatMapWidget(QWidget):
|
|||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.__floor = floor
|
self.__floor = floor
|
||||||
self.__room = room
|
self.__room = room
|
||||||
self.__seats_data = seats_data
|
self.__seats_data = seats_data
|
||||||
self.__selected_seats = []
|
self.__selected_seats = []
|
||||||
self.__seat_frames = {}
|
self.__seat_frames = {}
|
||||||
|
self.__confirmed = False
|
||||||
|
|
||||||
self.setupUi()
|
self.setupUi()
|
||||||
self.connectSignals()
|
self.connectSignals()
|
||||||
|
|
||||||
@@ -66,6 +67,8 @@ class ALSeatMapWidget(QWidget):
|
|||||||
self.setWindowTitle(f"选择楼层座位 - AutoLibrary")
|
self.setWindowTitle(f"选择楼层座位 - AutoLibrary")
|
||||||
|
|
||||||
self.SeatMapWidgetMainLayout = QVBoxLayout(self)
|
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 = QLabel(f"楼层座位分布图: {self.__floor}-{self.__room}")
|
||||||
self.TitleLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.TitleLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.TitleLabel.setStyleSheet("font-size: 16px; font-weight: bold; margin: 10px;")
|
self.TitleLabel.setStyleSheet("font-size: 16px; font-weight: bold; margin: 10px;")
|
||||||
@@ -98,9 +101,13 @@ class ALSeatMapWidget(QWidget):
|
|||||||
|
|
||||||
self.ConfirmButton = QPushButton("确认")
|
self.ConfirmButton = QPushButton("确认")
|
||||||
self.ConfirmButton.setFixedSize(80, 25)
|
self.ConfirmButton.setFixedSize(80, 25)
|
||||||
|
self.ConfirmButton.setAutoDefault(True)
|
||||||
|
self.ConfirmButton.setDefault(True)
|
||||||
self.CancelButton = QPushButton("取消")
|
self.CancelButton = QPushButton("取消")
|
||||||
self.CancelButton.setFixedSize(80, 25)
|
self.CancelButton.setFixedSize(80, 25)
|
||||||
self.SeatMapWidgetControlLayout = QHBoxLayout()
|
self.SeatMapWidgetControlLayout = QHBoxLayout()
|
||||||
|
self.SeatMapWidgetControlLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.SeatMapWidgetControlLayout.setSpacing(5)
|
||||||
self.SeatMapWidgetControlLayout.setAlignment(Qt.AlignmentFlag.AlignRight)
|
self.SeatMapWidgetControlLayout.setAlignment(Qt.AlignmentFlag.AlignRight)
|
||||||
self.SeatMapWidgetControlLayout.addWidget(self.CancelButton)
|
self.SeatMapWidgetControlLayout.addWidget(self.CancelButton)
|
||||||
self.SeatMapWidgetControlLayout.addWidget(self.ConfirmButton)
|
self.SeatMapWidgetControlLayout.addWidget(self.ConfirmButton)
|
||||||
@@ -144,6 +151,8 @@ class ALSeatMapWidget(QWidget):
|
|||||||
event: QCloseEvent
|
event: QCloseEvent
|
||||||
):
|
):
|
||||||
|
|
||||||
|
if not self.__confirmed:
|
||||||
|
self.clearSelections()
|
||||||
self.seatMapWidgetClosed.emit(self.__selected_seats)
|
self.seatMapWidgetClosed.emit(self.__selected_seats)
|
||||||
super().closeEvent(event)
|
super().closeEvent(event)
|
||||||
|
|
||||||
@@ -265,6 +274,7 @@ class ALSeatMapWidget(QWidget):
|
|||||||
self
|
self
|
||||||
):
|
):
|
||||||
|
|
||||||
|
self.__confirmed = True
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
@@ -272,5 +282,5 @@ class ALSeatMapWidget(QWidget):
|
|||||||
self
|
self
|
||||||
):
|
):
|
||||||
|
|
||||||
self.clearSelections()
|
self.__confirmed = False
|
||||||
self.close()
|
self.close()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -8,10 +8,7 @@ You may use, modify, and distribute this file under the terms of the MIT License
|
|||||||
See the LICENSE file for details.
|
See the LICENSE file for details.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import copy
|
import copy
|
||||||
import queue
|
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@@ -24,7 +21,7 @@ from PySide6.QtWidgets import (
|
|||||||
QHBoxLayout, QVBoxLayout, QLabel, QPushButton
|
QHBoxLayout, QVBoxLayout, QLabel, QPushButton
|
||||||
)
|
)
|
||||||
from PySide6.QtGui import (
|
from PySide6.QtGui import (
|
||||||
QCloseEvent, QScreen
|
QCloseEvent
|
||||||
)
|
)
|
||||||
|
|
||||||
from gui.Ui_ALTimerTaskWidget import Ui_ALTimerTaskWidget
|
from gui.Ui_ALTimerTaskWidget import Ui_ALTimerTaskWidget
|
||||||
@@ -50,8 +47,8 @@ class TimerTaskItemWidget(QWidget):
|
|||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.__timer_task = timer_task
|
self.__timer_task = timer_task
|
||||||
|
|
||||||
self.modifyUi()
|
self.modifyUi()
|
||||||
|
|
||||||
|
|
||||||
@@ -74,7 +71,7 @@ class TimerTaskItemWidget(QWidget):
|
|||||||
|
|
||||||
ExecuteTimeStr = self.__timer_task["execute_time"].strftime("%Y-%m-%d %H:%M:%S")
|
ExecuteTimeStr = self.__timer_task["execute_time"].strftime("%Y-%m-%d %H:%M:%S")
|
||||||
ExecuteTimeLabel = QLabel(f"执行时间: {ExecuteTimeStr}")
|
ExecuteTimeLabel = QLabel(f"执行时间: {ExecuteTimeStr}")
|
||||||
ExecuteTimeLabel.setStyleSheet("color: gray;")
|
ExecuteTimeLabel.setStyleSheet("color: #969696;")
|
||||||
ExecuteTimeLabel.setFixedHeight(20)
|
ExecuteTimeLabel.setFixedHeight(20)
|
||||||
self.TaskInfoLayout.addWidget(ExecuteTimeLabel)
|
self.TaskInfoLayout.addWidget(ExecuteTimeLabel)
|
||||||
|
|
||||||
@@ -96,15 +93,15 @@ class TimerTaskItemWidget(QWidget):
|
|||||||
TaskStatusColor = "#4CAF50"
|
TaskStatusColor = "#4CAF50"
|
||||||
case TimerTaskStatus.ERROR:
|
case TimerTaskStatus.ERROR:
|
||||||
TaskStatusText = "执行失败"
|
TaskStatusText = "执行失败"
|
||||||
TaskStatusColor = "#FF5722"
|
TaskStatusColor = "#DC0000"
|
||||||
case TimerTaskStatus.OUTDATED:
|
case TimerTaskStatus.OUTDATED:
|
||||||
TaskStatusText = "已过期"
|
TaskStatusText = "已过期"
|
||||||
TaskStatusColor = "#FF5722"
|
TaskStatusColor = "#DC0000"
|
||||||
TaskStatusLabel = QLabel(TaskStatusText)
|
TaskStatusLabel = QLabel(TaskStatusText)
|
||||||
TaskStatusLabel.setStyleSheet(f"""
|
TaskStatusLabel.setStyleSheet(f"""
|
||||||
QLabel {{
|
QLabel {{
|
||||||
background-color: {TaskStatusColor};
|
background-color: {TaskStatusColor};
|
||||||
color: white;
|
color: #FFFFFF;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}}
|
}}
|
||||||
@@ -119,7 +116,7 @@ class TimerTaskItemWidget(QWidget):
|
|||||||
TaskModeLabel.setStyleSheet(f"""
|
TaskModeLabel.setStyleSheet(f"""
|
||||||
QLabel {{
|
QLabel {{
|
||||||
background-color: {TaskModeColor};
|
background-color: {TaskModeColor};
|
||||||
color: white;
|
color: #FFFFFF;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="styleSheet">
|
<property name="styleSheet">
|
||||||
<string notr="true">QLabel {
|
<string notr="true">QLabel {
|
||||||
color: #FF5722
|
color: #DC0000
|
||||||
}</string>
|
}</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
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.
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
See the LICENSE file for details.
|
See the LICENSE file for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from PySide6.QtCore import (
|
from PySide6.QtCore import (
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
workflow process. Do not edit manually.
|
workflow process. Do not edit manually.
|
||||||
|
|
||||||
This file is auto-generated during the workflow process.
|
This file is auto-generated during the workflow process.
|
||||||
Last updated: 2026-01-02 16:38:53 UTC
|
Last updated: 2026-01-17 18:18:12 UTC
|
||||||
"""
|
"""
|
||||||
|
|
||||||
AL_VERSION = "1.0.1"
|
AL_VERSION = "1.0.4"
|
||||||
AL_TAG = "v1.0.1"
|
AL_TAG = "v1.0.4"
|
||||||
AL_COMMIT_SHA = "924db3b"
|
AL_COMMIT_SHA = "local"
|
||||||
AL_COMMIT_DATE = "2026-01-02 16:35:16 UTC" # time zone : UTC
|
AL_COMMIT_DATE = "null" # time zone : UTC
|
||||||
AL_BUILD_DATE = "2026-01-02 16:38:53 UTC" # time zone : UTC
|
AL_BUILD_DATE = "null" # time zone : UTC
|
||||||
AL_VERSION_FULL = f"{AL_VERSION} ({AL_COMMIT_SHA})"
|
AL_VERSION_FULL = f"{AL_VERSION} ({AL_COMMIT_SHA})"
|
||||||
|
|||||||
+53
-32
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -14,7 +14,9 @@ from selenium import webdriver
|
|||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
from selenium.webdriver.edge.service import Service
|
from selenium.webdriver.edge.service import Service as EdgeService
|
||||||
|
from selenium.webdriver.chrome.service import Service as ChromeService
|
||||||
|
from selenium.webdriver.firefox.service import Service as FirefoxService
|
||||||
|
|
||||||
from base.MsgBase import MsgBase
|
from base.MsgBase import MsgBase
|
||||||
from operators.LibChecker import LibChecker
|
from operators.LibChecker import LibChecker
|
||||||
@@ -24,8 +26,6 @@ from operators.LibReserve import LibReserve
|
|||||||
from operators.LibCheckin import LibCheckin
|
from operators.LibCheckin import LibCheckin
|
||||||
from operators.LibRenew import LibRenew
|
from operators.LibRenew import LibRenew
|
||||||
|
|
||||||
from utils.ConfigReader import ConfigReader
|
|
||||||
|
|
||||||
|
|
||||||
class AutoLib(MsgBase):
|
class AutoLib(MsgBase):
|
||||||
|
|
||||||
@@ -53,54 +53,75 @@ class AutoLib(MsgBase):
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
self._showTrace("正在初始化浏览器驱动......")
|
self._showTrace("正在初始化浏览器驱动......")
|
||||||
edge_options = webdriver.EdgeOptions()
|
|
||||||
|
|
||||||
web_driver_config = self.__run_config.get("web_driver", None)
|
web_driver_config = self.__run_config.get("web_driver", None)
|
||||||
|
self.__driver_type = web_driver_config.get("driver_type")
|
||||||
|
match self.__driver_type.lower():
|
||||||
|
case "edge":
|
||||||
|
driver_options = webdriver.EdgeOptions()
|
||||||
|
case "chrome":
|
||||||
|
driver_options = webdriver.ChromeOptions()
|
||||||
|
case "firefox":
|
||||||
|
driver_options = webdriver.FirefoxOptions()
|
||||||
|
case _:
|
||||||
|
raise Exception(f"不支持的浏览器驱动类型: {self.__driver_type} !")
|
||||||
|
|
||||||
if not web_driver_config:
|
if not web_driver_config:
|
||||||
self._showTrace("未配置浏览器驱动参数 !")
|
self._showTrace("未配置浏览器驱动参数 !")
|
||||||
return False
|
return False
|
||||||
if web_driver_config.get("headless"):
|
if web_driver_config.get("headless"):
|
||||||
edge_options.add_argument("--headless")
|
driver_options.add_argument("--headless")
|
||||||
edge_options.add_argument("--disable-gpu")
|
driver_options.add_argument("--disable-gpu")
|
||||||
edge_options.add_argument("--no-sandbox")
|
driver_options.add_argument("--no-sandbox")
|
||||||
edge_options.add_argument("--disable-dev-shm-usage")
|
driver_options.add_argument("--disable-dev-shm-usage")
|
||||||
|
|
||||||
# must be 1920x1080, otherwise the page will cause some elements not accessible
|
# must be 1920x1080, otherwise the page will cause some elements not accessible
|
||||||
edge_options.add_argument("--window-size=1920,1080")
|
driver_options.add_argument("--window-size=1920,1080")
|
||||||
edge_options.add_argument("--remote-allow-origins=*")
|
|
||||||
|
|
||||||
# omit ssl errors and verbose log level
|
# omit ssl errors and verbose log level
|
||||||
edge_options.add_argument("--ignore-certificate-errors")
|
driver_options.add_argument("--ignore-certificate-errors")
|
||||||
edge_options.add_argument("--ignore-ssl-errors")
|
driver_options.add_argument("--ignore-ssl-errors")
|
||||||
edge_options.add_argument("--log-level=OFF")
|
driver_options.add_argument("--log-level=OFF")
|
||||||
edge_options.add_argument("--silent")
|
driver_options.add_argument("--silent")
|
||||||
|
|
||||||
edge_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
# set options for chrome and edge
|
||||||
edge_options.add_experimental_option("useAutomationExtension", False)
|
if self.__driver_type.lower() in ["edge", "chrome"]:
|
||||||
edge_options.add_argument("--disable-blink-features=AutomationControlled")
|
driver_options.add_argument("--remote-allow-origins=*")
|
||||||
edge_options.add_argument(
|
driver_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
||||||
"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "\
|
driver_options.add_experimental_option("useAutomationExtension", False)
|
||||||
|
driver_options.add_argument("--disable-blink-features=AutomationControlled")
|
||||||
|
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "\
|
||||||
"AppleWebKit/537.36 (KHTML, like Gecko) "\
|
"AppleWebKit/537.36 (KHTML, like Gecko) "\
|
||||||
"Chrome/120.0.0.0 "\
|
"Chrome/120.0.0.0 "\
|
||||||
"Safari/537.36 "\
|
"Safari/537.36"
|
||||||
"Edg/120.0.0.0"
|
if self.__driver_type.lower() == "edge":
|
||||||
)
|
user_agent += " Edg/120.0.0.0"
|
||||||
|
# set options for firefox
|
||||||
|
elif self.__driver_type.lower() == "firefox":
|
||||||
|
driver_options.set_preference("dom.webdriver.enabled", False)
|
||||||
|
driver_options.set_preference("useAutomationExtension", False)
|
||||||
|
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) "\
|
||||||
|
"Gecko/20100101 Firefox/120.0"
|
||||||
|
driver_options.add_argument(f"user-agent={user_agent}")
|
||||||
|
|
||||||
# init browser driver
|
# init browser driver
|
||||||
self.__driver_path = web_driver_config.get("driver_path")
|
self.__driver_path = web_driver_config.get("driver_path")
|
||||||
self.__driver_type = web_driver_config.get("driver_type")
|
if not self.__driver_path:
|
||||||
|
raise Exception(f"未配置浏览器驱动路径 !")
|
||||||
self.__driver_path = os.path.abspath(self.__driver_path)
|
self.__driver_path = os.path.abspath(self.__driver_path)
|
||||||
try:
|
try:
|
||||||
service = None
|
service = None
|
||||||
if self.__driver_path:
|
|
||||||
service = Service(executable_path=self.__driver_path)
|
|
||||||
match self.__driver_type.lower():
|
match self.__driver_type.lower():
|
||||||
case "edge":
|
case "edge":
|
||||||
self.__driver = webdriver.Edge(service=service, options=edge_options)
|
service = EdgeService(executable_path=self.__driver_path)
|
||||||
|
self.__driver = webdriver.Edge(service=service, options=driver_options)
|
||||||
case "chrome":
|
case "chrome":
|
||||||
self.__driver = webdriver.Chrome(service=service, options=edge_options)
|
service = ChromeService(executable_path=self.__driver_path)
|
||||||
|
self.__driver = webdriver.Chrome(service=service, options=driver_options)
|
||||||
case "firefox":
|
case "firefox":
|
||||||
self.__driver = webdriver.Firefox(service=service, options=edge_options)
|
self._showTrace(f"Firefox 浏览器驱动初始化略慢, 请耐心等待...")
|
||||||
|
service = FirefoxService(executable_path=self.__driver_path)
|
||||||
|
self.__driver = webdriver.Firefox(service=service, options=driver_options)
|
||||||
case _:
|
case _:
|
||||||
raise Exception(f"不支持的浏览器驱动类型: {self.__driver_type}")
|
raise Exception(f"不支持的浏览器驱动类型: {self.__driver_type}")
|
||||||
self.__driver.implicitly_wait(1)
|
self.__driver.implicitly_wait(1)
|
||||||
@@ -191,9 +212,7 @@ class AutoLib(MsgBase):
|
|||||||
login_config.get("auto_captcha", True),
|
login_config.get("auto_captcha", True),
|
||||||
):
|
):
|
||||||
return 1
|
return 1
|
||||||
"""
|
# Here, we collect the run mode from the run config.
|
||||||
Here, we collect the run mode from the run config.
|
|
||||||
"""
|
|
||||||
run_mode = run_mode_config.get("run_mode", 0)
|
run_mode = run_mode_config.get("run_mode", 0)
|
||||||
run_mode = {
|
run_mode = {
|
||||||
"auto_reserve": run_mode&0x1,
|
"auto_reserve": run_mode&0x1,
|
||||||
@@ -294,6 +313,8 @@ class AutoLib(MsgBase):
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
if self.__driver:
|
if self.__driver:
|
||||||
|
if self.__driver_type.lower() == "firefox":
|
||||||
|
self._showTrace(f"Firefox 浏览器驱动关闭略慢, 请耐心等待...")
|
||||||
self.__driver.quit()
|
self.__driver.quit()
|
||||||
self.__driver = None
|
self.__driver = None
|
||||||
self._showTrace(f"浏览器驱动已关闭")
|
self._showTrace(f"浏览器驱动已关闭")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -13,6 +13,7 @@ import queue
|
|||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.chrome.webdriver import WebDriver
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ class LibChecker(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver: any
|
driver: WebDriver
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
@@ -336,7 +337,7 @@ class LibChecker(LibOperator):
|
|||||||
def postRenewCheck(
|
def postRenewCheck(
|
||||||
self,
|
self,
|
||||||
record: dict
|
record: dict
|
||||||
):
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if the renew operation is successful
|
Check if the renew operation is successful
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
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.
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
See the LICENSE file for details.
|
See the LICENSE file for details.
|
||||||
"""
|
"""
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
import queue
|
import queue
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.chrome.webdriver import WebDriver
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ class LibCheckin(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver: any
|
driver: WebDriver
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -13,6 +13,7 @@ import queue
|
|||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.chrome.webdriver import WebDriver
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ class LibCheckout(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver: any
|
driver: WebDriver
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
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.
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
See the LICENSE file for details.
|
See the LICENSE file for details.
|
||||||
"""
|
"""
|
||||||
import time
|
|
||||||
import queue
|
import queue
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
import ddddocr
|
import ddddocr
|
||||||
|
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.chrome.webdriver import WebDriver
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ class LibLogin(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver: any
|
driver: WebDriver
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -10,8 +10,7 @@ See the LICENSE file for details.
|
|||||||
import queue
|
import queue
|
||||||
|
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.chrome.webdriver import WebDriver
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
|
||||||
|
|
||||||
from base.LibOperator import LibOperator
|
from base.LibOperator import LibOperator
|
||||||
|
|
||||||
@@ -22,7 +21,7 @@ class LibLogout(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver: any
|
driver: WebDriver
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
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.
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
See the LICENSE file for details.
|
See the LICENSE file for details.
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import queue
|
import queue
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.chrome.webdriver import WebDriver
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
@@ -25,7 +23,7 @@ class LibRenew(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver: any
|
driver: WebDriver
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
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.
|
You may use, modify, and distribute this file under the terms of the MIT License.
|
||||||
See the LICENSE file for details.
|
See the LICENSE file for details.
|
||||||
"""
|
"""
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
import queue
|
import queue
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
|
from selenium.webdriver.chrome.webdriver import WebDriver
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ class LibReserve(LibOperator):
|
|||||||
self,
|
self,
|
||||||
input_queue: queue.Queue,
|
input_queue: queue.Queue,
|
||||||
output_queue: queue.Queue,
|
output_queue: queue.Queue,
|
||||||
driver: any
|
driver: WebDriver
|
||||||
):
|
):
|
||||||
|
|
||||||
super().__init__(input_queue, output_queue)
|
super().__init__(input_queue, output_queue)
|
||||||
@@ -40,12 +40,12 @@ class LibReserve(LibOperator):
|
|||||||
}
|
}
|
||||||
self.__room_map = {
|
self.__room_map = {
|
||||||
"1": "二层内环",
|
"1": "二层内环",
|
||||||
"2": "二层外环",
|
"2": "二层西区",
|
||||||
"3": "三层内环",
|
"3": "三层内环",
|
||||||
"4": "三层外环",
|
"4": "三层外环",
|
||||||
"5": "四层内环",
|
"5": "四层内环",
|
||||||
"6": "四层外环",
|
"6": "四层外环",
|
||||||
"7": "四层期刊区",
|
"7": "四层期刊",
|
||||||
"8": "五层考研"
|
"8": "五层考研"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,6 @@
|
|||||||
- LibReserve: Library operator for reserving seat.
|
- LibReserve: Library operator for reserving seat.
|
||||||
- LibCheckin: Library operator for checking in seat.
|
- LibCheckin: Library operator for checking in seat.
|
||||||
- LibCheckout: Library operator for checking out seat.
|
- LibCheckout: Library operator for checking out seat.
|
||||||
|
- LibChecker: Library operator for checking record status.
|
||||||
- LibRenew: Library operator for renewing seat.
|
- LibRenew: Library operator for renewing seat.
|
||||||
"""
|
"""
|
||||||
+45
-19
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -8,63 +8,89 @@ You may use, modify, and distribute this file under the terms of the MIT License
|
|||||||
See the LICENSE file for details.
|
See the LICENSE file for details.
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
class ConfigReader:
|
class ConfigReader:
|
||||||
|
"""
|
||||||
|
Config reader class.
|
||||||
|
|
||||||
|
This class is used to read config file in JSON format.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path (str): The path of config file.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> print(open("config.json", "r", encoding="utf-8").read())
|
||||||
|
{
|
||||||
|
"key1": {
|
||||||
|
"key2": "value1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>>> config_reader = ConfigReader("config.json")
|
||||||
|
>>> config_reader.get("key1/key2")
|
||||||
|
"value1"
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config_path: str
|
config_path: str
|
||||||
):
|
):
|
||||||
|
|
||||||
self._config_path = config_path
|
self.__config_path = config_path
|
||||||
self._config_data = {}
|
self.__config_data = None
|
||||||
if not self.__readConfig():
|
self.__readConfig()
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def __readConfig(
|
def __readConfig(
|
||||||
self
|
self
|
||||||
) -> bool:
|
):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(self._config_path, 'r', encoding='utf-8') as file:
|
with open(self.__config_path, 'r', encoding='utf-8') as file:
|
||||||
self._config_data = json.load(file)
|
self.__config_data = json.load(file)
|
||||||
return True
|
except FileNotFoundError as e:
|
||||||
|
raise Exception(f"Config file not found: {self.__config_path}") from e
|
||||||
|
except PermissionError as e:
|
||||||
|
raise Exception(f"Without enough permission to read config file: {self.__config_path}") from e
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
raise Exception(f"JSON decode error in config file: {self.__config_path}") from e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error reading config file: {e}")
|
raise Exception(f"Unknown error occurred while reading config file: {e}") from e
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def getConfigs(
|
def getConfigs(
|
||||||
self
|
self
|
||||||
) -> dict:
|
) -> dict:
|
||||||
|
|
||||||
return self._config_data.copy()
|
return self.__config_data.copy()
|
||||||
|
|
||||||
|
|
||||||
def getConfig(
|
def getConfig(
|
||||||
self,
|
self,
|
||||||
key: str
|
key: str
|
||||||
) -> dict:
|
) -> Any:
|
||||||
|
|
||||||
return self._config_data.get(key, {})
|
config = self.__config_data.get(key, {})
|
||||||
|
return copy.deepcopy(config)
|
||||||
|
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
self,
|
self,
|
||||||
key: str,
|
key: str,
|
||||||
default: any = None
|
default: Any = None
|
||||||
) -> any:
|
) -> Any:
|
||||||
|
|
||||||
keys = key.split('/')
|
keys = key.split('/')
|
||||||
current = self._config_data
|
current = self.__config_data
|
||||||
for k in keys:
|
for k in keys:
|
||||||
if isinstance(current, dict) and k in current:
|
if isinstance(current, dict) and k in current:
|
||||||
current = current[k]
|
current = current[k]
|
||||||
else:
|
else:
|
||||||
return default
|
return default
|
||||||
return current
|
return copy.deepcopy(current)
|
||||||
|
|
||||||
|
|
||||||
def hasConfig(
|
def hasConfig(
|
||||||
@@ -86,4 +112,4 @@ class ConfigReader:
|
|||||||
self
|
self
|
||||||
) -> str:
|
) -> str:
|
||||||
|
|
||||||
return self._config_path
|
return self.__config_path
|
||||||
|
|||||||
+41
-12
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2025 KenanZhu.
|
Copyright (c) 2025 - 2026 KenanZhu.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is provided "as is", without any warranty of any kind.
|
This software is provided "as is", without any warranty of any kind.
|
||||||
@@ -9,8 +9,35 @@ See the LICENSE file for details.
|
|||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
class ConfigWriter:
|
class ConfigWriter:
|
||||||
|
"""
|
||||||
|
Config writer class.
|
||||||
|
|
||||||
|
This class is used to write config file in JSON format.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path (str): The path of config file.
|
||||||
|
config_data (dict): The config data to be written.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> config_data = {
|
||||||
|
... "key1": {
|
||||||
|
... "key2": "value1"
|
||||||
|
... }
|
||||||
|
... }
|
||||||
|
>>> config_writer = ConfigWriter("config.json", config_data)
|
||||||
|
>>> config_writer.set("key1/key2", "value1")
|
||||||
|
True
|
||||||
|
>>> print(open("config.json", "r", encoding="utf-8").read())
|
||||||
|
{
|
||||||
|
"key1": {
|
||||||
|
"key2": "value1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -19,23 +46,25 @@ class ConfigWriter:
|
|||||||
):
|
):
|
||||||
|
|
||||||
self.__config_path = config_path
|
self.__config_path = config_path
|
||||||
self.__config_data = config_data if config_data is not None else {}
|
self.__config_data = config_data.copy() if config_data is not None else {}
|
||||||
if config_data is None:
|
self.__writeConfig()
|
||||||
return None
|
|
||||||
if not self.__writeConfig():
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def __writeConfig(
|
def __writeConfig(
|
||||||
self
|
self
|
||||||
) -> bool:
|
):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(self.__config_path, "w") as f:
|
with open(self.__config_path, "w", encoding="utf-8") as f:
|
||||||
json.dump(self.__config_data, f, indent=4, sort_keys=False)
|
json.dump(self.__config_data, f, indent=4, sort_keys=False)
|
||||||
return True
|
except PermissionError as e:
|
||||||
except:
|
raise Exception(f"Without enough permission to write config file: {self.__config_path}") from e
|
||||||
return False
|
except IOError as e:
|
||||||
|
raise Exception(f"IO error occurred while writing config file: {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
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"Unknown error occurred while writing config file: {e}") from e
|
||||||
|
|
||||||
|
|
||||||
def setConfigs(
|
def setConfigs(
|
||||||
@@ -60,7 +89,7 @@ class ConfigWriter:
|
|||||||
def set(
|
def set(
|
||||||
self,
|
self,
|
||||||
key: str,
|
key: str,
|
||||||
value: dict
|
value: Any
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
|
||||||
keys = key.replace("\\", "/").split("/")
|
keys = key.replace("\\", "/").split("/")
|
||||||
|
|||||||
Reference in New Issue
Block a user