Skip to content

Commit 3ef44d6

Browse files
committed
feat: 添加GitHub PAT支持并优化设置界面
- 实现GitHub PAT加密存储和验证功能 - 在通用设置中添加PAT输入和测试界面 - 更新管理器自动使用PAT进行GitHub API调用 - 优化设置界面样式和布局 - 修复多个UI组件的圆角样式问题 - 清理无用测试文件并更新文档 - Launcher更新
1 parent 6ddc3d5 commit 3ef44d6

File tree

13 files changed

+358
-316
lines changed

13 files changed

+358
-316
lines changed

.DS_Store

2 KB
Binary file not shown.

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
uses: softprops/action-gh-release@v2
5555
with:
5656
tag_name: 2.0.0B5
57-
name: 2.0.0 Beta 4
57+
name: 2.0.0 Beta 5
5858
body: ${{ github.event.head_commit.message }}
5959
prerelease: true
6060
files: |

Converter.py

Lines changed: 189 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616
QSpacerItem,
1717
QGridLayout,
1818
QSizePolicy,
19-
QGroupBox
19+
QGroupBox,
20+
QDialog
2021
)
2122
from PySide6.QtGui import QIcon, QPainter, QPixmap, QPalette
22-
from PySide6.QtCore import QSize, Qt, QSettings
23+
from PySide6.QtCore import QSize, Qt, QSettings, QPropertyAnimation, QEasingCurve, QTimer
2324
import multiprocessing
2425
from qfluentwidgets import Theme, setTheme
2526
# Keep for freeze_support, but remove direct Process usage
@@ -252,6 +253,146 @@ def show_settings(self):
252253
self._settings_dialog = settings_dialog # Save dialog reference
253254
settings_dialog.show() # Use show() instead of exec() to keep dialog non-modal
254255

256+
class AnimatedAppDialog(QDialog):
257+
def __init__(self, parent=None, app_type=""):
258+
super().__init__(parent)
259+
self.app_type = app_type
260+
self.setWindowFlags(Qt.WindowType.Dialog | Qt.WindowType.FramelessWindowHint)
261+
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
262+
self.setModal(False) # Non-modal
263+
264+
# Animation for showing the dialog
265+
self.animation = QPropertyAnimation(self, b"windowOpacity")
266+
self.animation.setDuration(250) # Duration in milliseconds
267+
self.animation.setStartValue(0.0)
268+
self.animation.setEndValue(1.0)
269+
self.animation.setEasingCurve(QEasingCurve.Type.OutCubic)
270+
271+
# Animation for closing the dialog
272+
self.close_animation = QPropertyAnimation(self, b"windowOpacity")
273+
self.close_animation.setDuration(200)
274+
self.close_animation.setStartValue(1.0)
275+
self.close_animation.setEndValue(0.0)
276+
self.close_animation.setEasingCurve(QEasingCurve.Type.OutCubic)
277+
self.close_animation.finished.connect(self._finish_close)
278+
279+
self._should_close = False
280+
281+
def showEvent(self, event):
282+
# Start animation when the dialog is shown
283+
self.animation.start()
284+
super().showEvent(event)
285+
# Start the external app after animation
286+
QTimer.singleShot(300, self.start_external_app)
287+
288+
def closeEvent(self, event):
289+
if not self._should_close:
290+
event.ignore()
291+
self.close_animation.start()
292+
else:
293+
super().closeEvent(event)
294+
295+
def _finish_close(self):
296+
self._should_close = True
297+
self.close()
298+
299+
def start_external_app(self):
300+
"""Start the external app in a separate process"""
301+
try:
302+
if self.app_type == "image":
303+
multiprocessing.Process(target=run_image).start()
304+
elif self.app_type == "zip":
305+
multiprocessing.Process(target=run_zip).start()
306+
except Exception as e:
307+
print(f"Error starting {self.app_type} app: {e}")
308+
finally:
309+
# Close the animation dialog after starting the external app
310+
QTimer.singleShot(1000, self.close)
311+
312+
class ImageAppDialog(AnimatedAppDialog):
313+
def __init__(self, parent=None):
314+
super().__init__(parent, "image")
315+
self.setFixedSize(400, 200)
316+
self.center_on_screen()
317+
318+
layout = QVBoxLayout(self)
319+
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
320+
321+
# Title
322+
title = QLabel("Image Converter")
323+
title.setStyleSheet("""
324+
font-size: 24px;
325+
font-weight: bold;
326+
color: #333;
327+
margin-bottom: 10px;
328+
""")
329+
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
330+
layout.addWidget(title)
331+
332+
# Subtitle
333+
subtitle = QLabel("正在启动应用...")
334+
subtitle.setStyleSheet("""
335+
font-size: 14px;
336+
color: #666;
337+
margin-bottom: 20px;
338+
""")
339+
subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter)
340+
layout.addWidget(subtitle)
341+
342+
# Loading indicator
343+
from qfluentwidgets import IndeterminateProgressBar
344+
progress = IndeterminateProgressBar()
345+
layout.addWidget(progress)
346+
347+
def center_on_screen(self):
348+
screen = QApplication.primaryScreen().geometry()
349+
self.move(
350+
screen.center().x() - self.width() // 2,
351+
screen.center().y() - self.height() // 2
352+
)
353+
354+
class ZipAppDialog(AnimatedAppDialog):
355+
def __init__(self, parent=None):
356+
super().__init__(parent, "zip")
357+
self.setFixedSize(400, 200)
358+
self.center_on_screen()
359+
360+
layout = QVBoxLayout(self)
361+
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
362+
363+
# Title
364+
title = QLabel("Archive Manager")
365+
title.setStyleSheet("""
366+
font-size: 24px;
367+
font-weight: bold;
368+
color: #333;
369+
margin-bottom: 10px;
370+
""")
371+
title.setAlignment(Qt.AlignmentFlag.AlignCenter)
372+
layout.addWidget(title)
373+
374+
# Subtitle
375+
subtitle = QLabel("正在启动应用...")
376+
subtitle.setStyleSheet("""
377+
font-size: 14px;
378+
color: #666;
379+
margin-bottom: 20px;
380+
""")
381+
subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter)
382+
layout.addWidget(subtitle)
383+
384+
# Loading indicator
385+
from qfluentwidgets import IndeterminateProgressBar
386+
progress = IndeterminateProgressBar()
387+
layout.addWidget(progress)
388+
389+
def center_on_screen(self):
390+
screen = QApplication.primaryScreen().geometry()
391+
self.move(
392+
screen.center().x() - self.width() // 2,
393+
screen.center().y() - self.height() // 2
394+
)
395+
255396
def run_zip():
256397
from arc_gui import ZipAppRunner
257398
app_runner = ZipAppRunner()
@@ -260,10 +401,53 @@ def run_image():
260401
from image_converter import ICNSConverterApp
261402
app_runner = ICNSConverterApp()
262403
app_runner.MainLoop()
263-
def run_zip_app():
264-
multiprocessing.Process(target=run_zip).start()
265404
def run_image_app():
266-
multiprocessing.Process(target=run_image).start()
405+
"""Run the image converter app with animation"""
406+
try:
407+
# Get the main window instance
408+
app = QApplication.instance()
409+
main_window = None
410+
for widget in app.topLevelWidgets():
411+
if isinstance(widget, IconButtonsWindow):
412+
main_window = widget
413+
break
414+
415+
if main_window:
416+
# Create and show the animation dialog
417+
dialog = ImageAppDialog(main_window)
418+
dialog.show()
419+
else:
420+
# Fallback to multiprocessing if no main window found
421+
multiprocessing.Process(target=run_image).start()
422+
423+
except Exception as e:
424+
print(f"Error running image app: {e}")
425+
# Fallback to multiprocessing
426+
multiprocessing.Process(target=run_image).start()
427+
428+
def run_zip_app():
429+
"""Run the archive manager app with animation"""
430+
try:
431+
# Get the main window instance
432+
app = QApplication.instance()
433+
main_window = None
434+
for widget in app.topLevelWidgets():
435+
if isinstance(widget, IconButtonsWindow):
436+
main_window = widget
437+
break
438+
439+
if main_window:
440+
# Create and show the animation dialog
441+
dialog = ZipAppDialog(main_window)
442+
dialog.show()
443+
else:
444+
# Fallback to multiprocessing if no main window found
445+
multiprocessing.Process(target=run_zip).start()
446+
447+
except Exception as e:
448+
print(f"Error running zip app: {e}")
449+
# Fallback to multiprocessing
450+
multiprocessing.Process(target=run_zip).start()
267451

268452

269453

TODO.md

Lines changed: 63 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,63 @@
1-
# TODO List
2-
3-
## New Features:
4-
1. ✅ GitHub PAT (Personal Access Token) 支持
5-
- ✅ 添加PAT输入界面到通用设置
6-
- ✅ 实现PAT加密存储
7-
- ✅ 在更新管理器中使用PAT进行GitHub API调用
8-
- ✅ 添加PAT安全机制
9-
10-
2. xxx
11-
12-
## Bug Fixes:
13-
1. xxx
14-
15-
## Improvements:
16-
1. xxx
17-
18-
---
19-
20-
## Alpha/Deepdev版本通道过滤 - ✅ 已完成
21-
22-
### 目标
23-
实现对Alpha和Deepdev版本的特殊处理,确保内部版本只显示对应的更新通道,提升用户体验和版本管理的准确性。
24-
25-
### 具体任务
26-
1. **版本类型检测** - ✅ 已完成
27-
- 在UpdateManager中增强版本解析功能
28-
- 识别版本号中的Alpha(A)和Deepdev(D)标识
29-
- 将Alpha/D版本标记为内部版本
30-
31-
2. **通道过滤逻辑** - ✅ 已完成
32-
- Alpha版本仅显示"Alpha"和"Stable"通道
33-
- Deepdev版本仅显示"Deepdev"和"Stable"通道
34-
- 普通版本显示所有5个通道(Beta, Alpha, Deepdev, RC, Stable)
35-
36-
3. **参数映射调整** - ✅ 已完成
37-
- 内部版本使用简化索引映射(0: Alpha, 1: Stable)
38-
- 普通版本保持原有映射(0: Beta, 1: Alpha, 2: Deepdev, 3: RC, 4: Stable)
39-
40-
### 技术实现
41-
- **版本解析增强**: 在`UpdateManager._parse_version()`中增加对Alpha和Deepdev标签的识别
42-
- **内部版本判断**: 添加`is_internal_version()`方法判断版本类型
43-
- **通道过滤**: 在UI层根据版本类型过滤显示的更新通道
44-
- **参数映射**: 根据版本类型调整`prerelease_types`数组的索引映射
45-
46-
### 测试结果
47-
- ✅ 版本检测功能正确识别Alpha/D版本为内部版本
48-
- ✅ 通道过滤逻辑对Alpha/D版本仅显示对应通道
49-
- ✅ 参数映射逻辑正确处理不同版本类型的索引选择
50-
- ✅ 真实环境测试验证功能正确性
51-
52-
### 完成状态
53-
- 功能实现: ✅ 完成
54-
- 单元测试: ✅ 通过
55-
- 集成测试: ✅ 通过
56-
- 文档更新: ✅ 完成
57-
58-
---
59-
60-
## 之前的任务记录...
61-
62-
### 设置合并 - ✅ 已完成
63-
将图像转换器设置合并到通用设置中,简化设置界面结构。
64-
65-
### Update Command Script Not Found - ✅ 已修复
66-
修复了更新命令脚本找不到的问题,确保更新功能正常运行。
67-
68-
### Pre-release版本支持 - ✅ 已完成
69-
实现了对预发布版本的支持,包括版本解析、更新检查和用户界面显示。
70-
71-
### 更新通道UI优化 - ✅ 已完成
72-
优化了更新通道的用户界面,提供更好的用户体验和版本选择功能。
73-
74-
### Beta版本更新检查问题分析 - ✅ 已完成
75-
分析并解决了Beta版本更新检查中的问题,确保更新功能的稳定性和准确性。
1+
# PNG/ICNS Converter Application
2+
3+
## Overview
4+
A modern desktop application for converting PNG images to ICNS format (macOS icons) with additional archive management functionality.
5+
6+
## Features
7+
- PNG to ICNS conversion
8+
- Archive management (ZIP handling)
9+
- Modern UI with QFluentWidgets
10+
- Theme support (Light/Dark/Auto)
11+
- Settings management
12+
- Debug logging support
13+
- **NEW: Animated app launching with fade in/out effects**
14+
15+
## Project Structure
16+
- `Converter.py` - Main application entry point and UI
17+
- `image_converter.py` - PNG to ICNS conversion functionality
18+
- `arc_gui.py` - Archive management functionality
19+
- `support/` - Supporting modules (themes, settings, debug logging)
20+
- `test/` - Test files
21+
22+
## Requirements
23+
- Python 3.13
24+
- PySide6
25+
- QFluentWidgets
26+
- Pillow (PIL)
27+
28+
## Usage
29+
```bash
30+
pip3.13 install -r requirements.txt
31+
python3 Converter.py
32+
```
33+
34+
## Recent Updates
35+
36+
### 动态效果实现总结
37+
38+
#### New Features:
39+
1. 实现了AnimatedAppDialog基类,提供淡入淡出动画效果
40+
2. 创建了ImageAppDialog类,用于Image Converter应用的动画启动
41+
3. 创建了ZipAppDialog类,用于Archive Manager应用的动画启动
42+
4. 实现了多进程启动外部应用的功能
43+
5. 添加了加载指示器(IndeterminateProgressBar)提升用户体验
44+
45+
#### Bug Fixes:
46+
1. 修复了重复的ZipAppDialog类定义问题
47+
2. 修复了ImageAppDialog类缺失的问题
48+
3. 修复了run_image_app和run_zip_app函数中的主窗口检测逻辑
49+
4. 修复了AttributeError: 'IconButtonsWindow' object has no attribute 'run_image_app'错误
50+
51+
#### Improvements:
52+
1. 优化了动画对话框的UI布局,添加了标题和加载指示器
53+
2. 实现了主窗口检测机制,通过遍历QApplication.topLevelWidgets()查找IconButtonsWindow实例
54+
3. 添加了异常处理和回退机制,确保在主窗口未找到时仍能启动应用
55+
4. 优化了动画时序,对话框显示后300ms启动外部应用,1秒后自动关闭
56+
5. 统一了动画风格,使用QPropertyAnimation实现平滑的淡入淡出效果
57+
58+
#### 技术实现:
59+
- 使用QPropertyAnimation控制窗口透明度实现淡入淡出效果
60+
- 使用QTimer.singleShot实现延迟启动和自动关闭
61+
- 使用multiprocessing.Process在独立进程中运行外部应用
62+
- 使用IndeterminateProgressBar提供视觉反馈
63+
- 遵循PySide6编码规范,使用类型注解和文档字符串

0 commit comments

Comments
 (0)