Skip to content

Commit 6ddc3d5

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

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+28041
-1048
lines changed

.github/.DS_Store

-6 KB
Binary file not shown.

.github/workflows/build.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ jobs:
2929
if: github.event_name == 'push'
3030
uses: softprops/action-gh-release@v2
3131
with:
32-
tag_name: 2.0.0B4
32+
tag_name: 2.0.0B5
3333
name: 2.0.0 Beta 4
3434
body: ${{ github.event.head_commit.message }}
3535
prerelease: true
3636
files: |
3737
./dist/Converter_arm64_darwin.zip
3838
build_intel:
3939
name: Build Intel
40-
runs-on: self-hosted
40+
runs-on: macos-13
4141
if: github.repository_owner == 'pyquick'
4242
permissions:
4343
contents: write
@@ -53,7 +53,7 @@ jobs:
5353
if: github.event_name == 'push'
5454
uses: softprops/action-gh-release@v2
5555
with:
56-
tag_name: 2.0.0B4
56+
tag_name: 2.0.0B5
5757
name: 2.0.0 Beta 4
5858
body: ${{ github.event.head_commit.message }}
5959
prerelease: true

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
__pycache__/
33
*.py[codz]
44
*$py.class
5-
5+
.DS_Store
66
# C extensions
77
*.so
88

Converter.py

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from concurrent.futures import thread
4+
from importlib import reload
5+
import sys
6+
import os
7+
import threading
8+
import time
9+
from PySide6.QtWidgets import (
10+
QApplication,
11+
QWidget,
12+
QPushButton,
13+
QVBoxLayout,
14+
QHBoxLayout,
15+
QLabel,
16+
QSpacerItem,
17+
QGridLayout,
18+
QSizePolicy,
19+
QGroupBox
20+
)
21+
from PySide6.QtGui import QIcon, QPainter, QPixmap, QPalette
22+
from PySide6.QtCore import QSize, Qt, QSettings
23+
import multiprocessing
24+
from qfluentwidgets import Theme, setTheme
25+
# Keep for freeze_support, but remove direct Process usage
26+
from settings.update_settings_gui import UpdateDialog
27+
from settings.settings_gui import SettingsDialog
28+
from con import CON # Import CON instance for theme settings
29+
# Encoding settings have been moved to debug_logger for handling
30+
# --- Helper function to create placeholder icons ---
31+
# Since we cannot directly generate .icns files, we create PNG files as examples.
32+
# Please place the AppIcon.icns and zip.icns files in the same directory as this script.
33+
def create_placeholder_icon(path: str, color: str, text: str):
34+
"""Create a simple PNG placeholder icon if the icon file does not exist."""
35+
if not os.path.exists(path):
36+
pixmap = QPixmap(128, 128)
37+
pixmap.fill(color)
38+
painter = QPainter(pixmap)
39+
painter.setPen("white")
40+
font = painter.font()
41+
font.setPointSize(48)
42+
43+
painter.setFont(font)
44+
# Qt.AlignCenter is enum value 1
45+
painter.drawText(pixmap.rect(), Qt.AlignmentFlag.AlignCenter, text)
46+
painter.end()
47+
pixmap.save(path)
48+
print(f"Note: '{path}' not found. A placeholder icon has been created.")
49+
return True
50+
# If it's an .icns file, use it directly
51+
elif path.endswith(".icns") and os.path.exists(path):
52+
return True
53+
# If a non-.icns placeholder file exists, consider it successful
54+
elif not path.endswith(".icns") and os.path.exists(path):
55+
return True
56+
return False
57+
58+
class IconButtonsWindow(QWidget):
59+
60+
def _load_qss_file(self, filename):
61+
"""Load QSS content from external file"""
62+
qss_path = os.path.join(os.path.dirname(__file__), 'qss', filename)
63+
try:
64+
with open(qss_path, 'r', encoding='utf-8') as f:
65+
return f.read()
66+
except FileNotFoundError:
67+
print(f"Warning: QSS file not found: {qss_path}")
68+
return ""
69+
except Exception as e:
70+
print(f"Error loading QSS file {qss_path}: {e}")
71+
return ""
72+
73+
@property
74+
def LIGHT_QSS(self):
75+
"""Load light theme QSS from external file"""
76+
return self._load_qss_file('launcher_light.qss')
77+
78+
@property
79+
def DARK_QSS(self):
80+
"""Load dark theme QSS from external file"""
81+
return self._load_qss_file('launcher_dark.qss')
82+
83+
84+
def __init__(self, q_app: QApplication):
85+
super().__init__()
86+
self._q_app = q_app # Store QApplication instance
87+
self.setWindowTitle("Converter")
88+
# Load theme setting immediately
89+
self.settings = QSettings("MyCompany", "ConverterApp")
90+
self.theme_setting = self.settings.value("theme", 0, type=int)
91+
92+
self.path= os.path.dirname(os.path.abspath(__file__))
93+
# Define paths for icon files
94+
self.app_icon_path = os.path.join(self.path,"AppIcon.png")
95+
self.appd_icon_path = os.path.join(self.path,"AppIcond.png")
96+
self.zip_icon_path = os.path.join(self.path,"zip.png")
97+
self.zipd_icon_path = os.path.join(self.path,"zipd.png")
98+
99+
# Check if icon files exist and create placeholders if needed
100+
if not os.path.exists(self.app_icon_path):
101+
print("Note: AppIcon.png file not found. Will try to create a PNG placeholder icon.")
102+
create_placeholder_icon(self.app_icon_path, "dodgerblue", "App")
103+
if not os.path.exists(self.appd_icon_path):
104+
print("Note: AppIcond.png file not found. Will try to create a PNG placeholder icon.")
105+
create_placeholder_icon(self.appd_icon_path, "darkblue", "AppD") # Changed placeholder color for dark mode app icon
106+
107+
if not os.path.exists(self.zip_icon_path):
108+
print("Note: zip.png file not found. Will try to create a PNG placeholder icon.")
109+
create_placeholder_icon(self.zip_icon_path, "gray", "Zip")
110+
111+
if not os.path.exists(self.zipd_icon_path):
112+
print("Note: zipd.png file not found. Will try to create a PNG placeholder icon.")
113+
create_placeholder_icon(self.zipd_icon_path, "dimgray", "ZipD")
114+
115+
self.init_ui()
116+
# Apply theme based on settings or initial system detection
117+
self._apply_system_theme_from_settings()
118+
119+
def _apply_system_theme(self, is_dark_mode): # This method will now be primarily for paletteChanged signal
120+
# Only apply system theme if setting is System Default
121+
if self.settings.value("theme", 0, type=int) == 0:
122+
self._apply_theme(is_dark_mode)
123+
124+
def _apply_system_theme_from_settings(self):
125+
theme_setting = self.settings.value("theme", 0, type=int)
126+
if self._q_app:
127+
if theme_setting == 0: # System Default
128+
is_dark_mode = self._q_app.palette().color(QPalette.ColorRole.Window).lightnessF() < 0.5
129+
self._apply_theme(is_dark_mode)
130+
131+
132+
def _apply_theme(self, is_dark_mode):
133+
if is_dark_mode:
134+
self.setStyleSheet(self.DARK_QSS)
135+
if hasattr(self, 'button_zip'):
136+
self.button_zip.setIcon(QIcon(self.zipd_icon_path))
137+
if hasattr(self, 'button_app'):
138+
self.button_app.setIcon(QIcon(self.appd_icon_path))
139+
else:
140+
self.setStyleSheet(self.LIGHT_QSS)
141+
if hasattr(self, 'button_zip'):
142+
self.button_zip.setIcon(QIcon(self.zip_icon_path))
143+
if hasattr(self, 'button_app'):
144+
self.button_app.setIcon(QIcon(self.app_icon_path))
145+
146+
# Notify all sub-widgets to update theme
147+
self.update_sub_widgets_theme(is_dark_mode)
148+
149+
def update_sub_widgets_theme(self, is_dark_mode):
150+
"""Notify all sub-widgets to update theme"""
151+
# Update settings dialog theme (if already created)
152+
if hasattr(self, '_settings_dialog') and self._settings_dialog:
153+
self._settings_dialog.apply_theme(is_dark_mode)
154+
155+
def init_ui(self):
156+
# Create main layout
157+
main_layout = QVBoxLayout()
158+
159+
main_layout.setSpacing(25) # Increased spacing for better visual separation
160+
main_layout.setContentsMargins(40, 35, 40, 35) # Better margins
161+
162+
# Add title
163+
title_label = QLabel("Converter")
164+
title_label.setObjectName("title_label")
165+
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
166+
main_layout.addWidget(title_label)
167+
168+
# --- Image Converter Group ---
169+
image_group = QGroupBox("Image Converter")
170+
image_group.setObjectName("image_group")
171+
image_layout = QVBoxLayout(image_group)
172+
image_layout.setSpacing(10)
173+
image_layout.setContentsMargins(15, 15, 15, 15)
174+
175+
# Image Converter Button
176+
app_icon = QIcon(self.app_icon_path)
177+
self.button_app = QPushButton("Image Converter")
178+
self.button_app.setObjectName("button_app")
179+
self.button_app.setIcon(app_icon)
180+
self.button_app.setIconSize(QSize(40, 40)) # Consistent icon size
181+
self.button_app.setMinimumHeight(55) # Consistent height
182+
self.button_app.clicked.connect(run_image_app)
183+
184+
# Center the button
185+
app_button_layout = QHBoxLayout()
186+
app_button_layout.addStretch()
187+
app_button_layout.addWidget(self.button_app)
188+
app_button_layout.addStretch()
189+
image_layout.addLayout(app_button_layout)
190+
191+
# Description for Image Converter - moved below button
192+
image_desc = QLabel("Convert PNG images to ICNS format for macOS applications")
193+
image_desc.setObjectName("description_label")
194+
image_desc.setAlignment(Qt.AlignmentFlag.AlignCenter)
195+
image_desc.setWordWrap(True)
196+
image_layout.addWidget(image_desc)
197+
198+
main_layout.addWidget(image_group)
199+
200+
# --- Archive Converter Group ---
201+
archive_group = QGroupBox("Archive Converter")
202+
archive_group.setObjectName("archive_group")
203+
archive_layout = QVBoxLayout(archive_group)
204+
archive_layout.setSpacing(10)
205+
archive_layout.setContentsMargins(15, 15, 15, 15)
206+
207+
# Archive Converter Button
208+
zip_icon = QIcon(self.zip_icon_path)
209+
self.button_zip = QPushButton(" Archive Converter")
210+
self.button_zip.setObjectName("button_zip")
211+
self.button_zip.setIcon(zip_icon)
212+
self.button_zip.setIconSize(QSize(40, 40)) # Consistent icon size
213+
self.button_zip.setMinimumHeight(55) # Consistent height
214+
self.button_zip.clicked.connect(run_zip_app)
215+
216+
# Center the button
217+
zip_button_layout = QHBoxLayout()
218+
zip_button_layout.addStretch()
219+
zip_button_layout.addWidget(self.button_zip)
220+
zip_button_layout.addStretch()
221+
archive_layout.addLayout(zip_button_layout)
222+
223+
# Description for Archive Converter - moved below button
224+
archive_desc = QLabel("Create and extract ZIP, RAR, and 7Z archive files")
225+
archive_desc.setObjectName("description_label")
226+
archive_desc.setAlignment(Qt.AlignmentFlag.AlignCenter)
227+
archive_desc.setWordWrap(True)
228+
archive_layout.addWidget(archive_desc)
229+
230+
main_layout.addWidget(archive_group)
231+
232+
# Add vertical space
233+
main_layout.addSpacerItem(QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding))
234+
235+
# Settings button - positioned below zip button
236+
settings_button = QPushButton(QIcon.fromTheme("preferences-system"), " Settings")
237+
settings_button.setObjectName("settings_button")
238+
settings_button.setIconSize(QSize(20, 20))
239+
settings_button.clicked.connect(self.show_settings)
240+
241+
settings_button_layout = QHBoxLayout()
242+
settings_button_layout.addStretch()
243+
settings_button_layout.addWidget(settings_button)
244+
settings_button_layout.addStretch()
245+
main_layout.addLayout(settings_button_layout)
246+
247+
# Set the main layout for the window
248+
self.setLayout(main_layout)
249+
250+
def show_settings(self):
251+
settings_dialog = SettingsDialog(self)
252+
self._settings_dialog = settings_dialog # Save dialog reference
253+
settings_dialog.show() # Use show() instead of exec() to keep dialog non-modal
254+
255+
def run_zip():
256+
from arc_gui import ZipAppRunner
257+
app_runner = ZipAppRunner()
258+
app_runner.MainLoop()
259+
def run_image():
260+
from image_converter import ICNSConverterApp
261+
app_runner = ICNSConverterApp()
262+
app_runner.MainLoop()
263+
def run_zip_app():
264+
multiprocessing.Process(target=run_zip).start()
265+
def run_image_app():
266+
multiprocessing.Process(target=run_image).start()
267+
268+
269+
270+
if __name__ == "__main__":
271+
multiprocessing.freeze_support()
272+
app = QApplication(sys.argv)
273+
274+
# Initialize debug logger
275+
try:
276+
from support.debug_logger import debug_logger
277+
debug_logger.setup_logger()
278+
if debug_logger.is_debug_enabled():
279+
print("Debug mode enabled - logging to ~/.converter/log")
280+
except Exception as e:
281+
print(f"Failed to initialize debug logger: {e}")
282+
283+
from support.toggle import theme_manager
284+
theme_manager.start()
285+
setTheme(Theme.AUTO)
286+
window = IconButtonsWindow(q_app=app)
287+
window.show()
288+
# Connect to palette changes for real-time theme switching ONLY if setting is System Default
289+
app.paletteChanged.connect(lambda: window._apply_system_theme(app.palette().color(QPalette.ColorRole.Window).lightnessF() < 0.5))
290+
exit_code = app.exec()
291+
292+
# Cleanup debug logger
293+
try:
294+
from support.debug_logger import debug_logger
295+
debug_logger.restore_output()
296+
except:
297+
pass
298+
299+
theme_manager.stop()
300+
sys.exit(exit_code)

0 commit comments

Comments
 (0)