From 586d628d77c5b4027c6435e3bdb946e02807f5bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 02:29:56 +0800 Subject: [PATCH 01/11] =?UTF-8?q?[=E4=BF=AE=E5=A4=8D]=20=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=20ctypes=20=E6=9B=BF=E4=BB=A3=20pywin32=20=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=20Nuitka=20=E6=89=93=E5=8C=85=E9=97=AA=E9=80=80=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 ctypes 替代 pywin32 进行 Windows API 调用 - 移除 pywin32 依赖,避免打包后缺少依赖导致闪退 - 更新 about_dialog.py 版权信息,移除 pywin32 声明 - 添加平台判断确保 Windows API 只在 Windows 系统执行 --- core/app_mode.py | 10 +++++++++- dialogs/tone_analysis_dialog.py | 15 ++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/core/app_mode.py b/core/app_mode.py index 6347b16..7142c7c 100644 --- a/core/app_mode.py +++ b/core/app_mode.py @@ -95,7 +95,15 @@ def detect_mode() -> AppMode: _logger.debug(f"exe_path={exe_path}") _logger.debug(f"TEMP={temp}") - # 在临时目录运行 = 安装程序运行中 + # Nuitka 单文件模式:使用 __compiled__ 标记检测 + # Nuitka 单文件运行时也会解压到临时目录,但不能误判为安装程序模式 + if is_nuitka: + # Nuitka 单文件模式下,配置目录应该使用用户主目录(与安装版相同) + _mode_cache = AppMode.INSTALLED + _logger.info(f"运行模式判定: 安装版(Nuitka 单文件)") + return _mode_cache + + # 在临时目录运行 = 安装程序运行中(仅适用于非 Nuitka 环境) if temp and str(exe_path).lower().startswith(temp): _mode_cache = AppMode.INSTALLER _logger.info(f"运行模式判定: 安装程序模式") diff --git a/dialogs/tone_analysis_dialog.py b/dialogs/tone_analysis_dialog.py index 7dc01b3..5417ed3 100644 --- a/dialogs/tone_analysis_dialog.py +++ b/dialogs/tone_analysis_dialog.py @@ -1,11 +1,11 @@ """明度分析对话框""" +import ctypes import math +import sys from typing import List, Optional import numpy as np -import win32con -import win32gui from PySide6.QtCharts import ( QBarSeries, QBarSet, QChart, QChartView, QLineSeries, QPieSeries, QValueAxis @@ -656,9 +656,14 @@ class ToneAnalysisDialog(BaseFramelessDialog): self.titleBar.setDoubleClickEnabled(True) # 恢复Win32最大化按钮样式(FramelessDialog初始化时禁用了) - hWnd = int(self.winId()) - style = win32gui.GetWindowLong(hWnd, win32con.GWL_STYLE) - win32gui.SetWindowLong(hWnd, win32con.GWL_STYLE, style | win32con.WS_MAXIMIZEBOX) + if sys.platform == 'win32': + hWnd = int(self.winId()) + # 使用ctypes替代pywin32 + GWL_STYLE = -16 + WS_MAXIMIZEBOX = 0x00010000 + user32 = ctypes.windll.user32 + style = user32.GetWindowLongW(hWnd, GWL_STYLE) + user32.SetWindowLongW(hWnd, GWL_STYLE, style | WS_MAXIMIZEBOX) self._setup_title_bar() self._setup_ui() -- Gitee From b9c78c3d64b27e42bc721c272b86600ce3984198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 02:32:06 +0800 Subject: [PATCH 02/11] =?UTF-8?q?[=E5=86=85=E5=AE=B9=E8=B0=83=E6=95=B4]=20?= =?UTF-8?q?=E5=90=AF=E7=94=A8fix/packaging-dependencies=E5=88=86=E6=94=AF?= =?UTF-8?q?=E7=9A=84=E8=87=AA=E5=8A=A8=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 53f3eb8..afe27a6 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -2,7 +2,7 @@ name: Build Windows App with Nuitka on: push: - branches: [ main, feature/tone-analysis ] + branches: [ main, fix/packaging-dependencies ] pull_request: branches: [ main ] -- Gitee From 73330c97e6392dcc0e2da0ddf55a1643da55ff47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 03:15:52 +0800 Subject: [PATCH 03/11] =?UTF-8?q?[=E4=BF=AE=E5=A4=8D]=20=E5=9B=9E=E6=BB=9A?= =?UTF-8?q?=20app=5Fmode.py=20=E4=BF=AE=E6=94=B9=E5=B9=B6=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E8=B0=83=E8=AF=95=E4=BF=A1=E6=81=AF=E5=AE=9A=E4=BD=8D?= =?UTF-8?q?=E9=97=AA=E9=80=80=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/app_mode.py | 10 +------- main.py | 60 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/core/app_mode.py b/core/app_mode.py index 7142c7c..6347b16 100644 --- a/core/app_mode.py +++ b/core/app_mode.py @@ -95,15 +95,7 @@ def detect_mode() -> AppMode: _logger.debug(f"exe_path={exe_path}") _logger.debug(f"TEMP={temp}") - # Nuitka 单文件模式:使用 __compiled__ 标记检测 - # Nuitka 单文件运行时也会解压到临时目录,但不能误判为安装程序模式 - if is_nuitka: - # Nuitka 单文件模式下,配置目录应该使用用户主目录(与安装版相同) - _mode_cache = AppMode.INSTALLED - _logger.info(f"运行模式判定: 安装版(Nuitka 单文件)") - return _mode_cache - - # 在临时目录运行 = 安装程序运行中(仅适用于非 Nuitka 环境) + # 在临时目录运行 = 安装程序运行中 if temp and str(exe_path).lower().startswith(temp): _mode_cache = AppMode.INSTALLER _logger.info(f"运行模式判定: 安装程序模式") diff --git a/main.py b/main.py index e5be5df..fe88509 100644 --- a/main.py +++ b/main.py @@ -128,15 +128,23 @@ def main(): logger = get_logger("main") logger.info("应用程序启动") - logger.info(f"运行环境: frozen={getattr(sys, 'frozen', False)}, _MEIPASS={getattr(sys, '_MEIPASS', None)}") + logger.info(f"运行环境: frozen={getattr(sys, 'frozen', False)}, _MEIPASS={getattr(sys, '_MEIPASS', None)}, __compiled__={'__compiled__' in globals()}") + + # 记录关键路径信息 + logger.info(f"sys.argv[0]={sys.argv[0]}") + logger.info(f"sys.executable={sys.executable}") + logger.info(f"sys.path[0]={sys.path[0] if sys.path else 'empty'}") # 设置全局异常处理器 setup_global_exception_handler(logger) # 立即显示启动画面(在其他模块导入前) + logger.info("开始创建启动画面...") splash = _create_splash_screen() + logger.info(f"启动画面创建完成: {splash is not None}") try: + logger.info("进入主try块...") # 临时重定向 stdout 以屏蔽 QFluentWidgets 的推广提示 from io import StringIO _old_stdout = sys.stdout @@ -166,12 +174,15 @@ def main(): # 导入项目模块 logger.info("开始导入项目模块...") + logger.info("导入 core 模块...") from core import get_config_manager logger.info("core 模块导入完成") + logger.info("导入 utils 模块...") from utils import fix_windows_taskbar_icon_for_window, load_icon_universal, get_locale_manager, force_window_to_front logger.info("utils 模块导入完成") + logger.info("导入 ui 模块...") from ui import MainWindow logger.info("ui 模块导入完成") @@ -183,17 +194,29 @@ def main(): # 加载配置 logger.info("加载配置...") - config_manager = get_config_manager() - config_manager.load() + try: + config_manager = get_config_manager() + logger.info("获取配置管理器完成") + config_manager.load() + logger.info("配置加载完成") + except Exception as e: + logger.error(f"配置加载失败: {e}", exc_info=True) + raise # 初始化语言管理器并加载用户语言配置 logger.info("初始化语言管理器...") - locale_manager = get_locale_manager() - language_setting = config_manager.get('settings.language', 'ZW_JT') - locale_manager.load_language(language_setting) - logger.info(f"语言设置: {language_setting}") + try: + locale_manager = get_locale_manager() + logger.info("获取语言管理器完成") + language_setting = config_manager.get('settings.language', 'ZW_JT') + locale_manager.load_language(language_setting) + logger.info(f"语言设置: {language_setting}") + except Exception as e: + logger.error(f"语言管理器初始化失败: {e}", exc_info=True) + raise # 设置主题 + logger.info("设置主题...") theme_setting = config_manager.get('settings.theme', 'auto') if theme_setting == 'light': setTheme(Theme.LIGHT) @@ -202,9 +225,15 @@ def main(): else: setTheme(Theme.AUTO) setThemeColor('#0078d4') + logger.info(f"主题设置完成: {theme_setting}") logger.info("创建主窗口...") - window = MainWindow() + try: + window = MainWindow() + logger.info("主窗口创建完成") + except Exception as e: + logger.error(f"主窗口创建失败: {e}", exc_info=True) + raise # 初始化标题栏主题按钮状态 if hasattr(window, 'titleBar') and hasattr(window.titleBar, 'init_theme'): @@ -226,6 +255,7 @@ def main(): force_window_to_front(window) QTimer.singleShot(100, _on_window_shown) + logger.info("启动画面关闭定时器已设置") # 计算并输出启动时间 startup_time = (time.perf_counter() - _startup_start_time) * 1000 @@ -233,13 +263,25 @@ def main(): logger.info("进入主事件循环...") try: - sys.exit(app.exec()) + exit_code = app.exec() + logger.info(f"主事件循环结束,退出码: {exit_code}") + sys.exit(exit_code) except KeyboardInterrupt: logger.info("程序被用户中断 (Ctrl+C)") sys.exit(0) + except Exception as e: + logger.critical(f"主事件循环异常: {e}", exc_info=True) + raise except Exception as e: logger.critical(f"程序启动失败: {str(e)}", exc_info=True) + # 尝试记录更多调试信息 + try: + import traceback + tb_str = traceback.format_exc() + logger.critical(f"异常堆栈:\n{tb_str}") + except: + pass raise -- Gitee From a59adc74d78701f9f1d199ef230c2a06545eedb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 03:46:34 +0800 Subject: [PATCH 04/11] =?UTF-8?q?[=E8=B0=83=E8=AF=95]=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E8=B0=83=E8=AF=95=E6=97=A5=E5=BF=97=E5=AE=9A?= =?UTF-8?q?=E4=BD=8D=E9=97=AA=E9=80=80=E9=97=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-windows-debug.yml | 67 +++++++++++++++++++++++ main.py | 37 +++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 .github/workflows/build-windows-debug.yml diff --git a/.github/workflows/build-windows-debug.yml b/.github/workflows/build-windows-debug.yml new file mode 100644 index 0000000..37a5aed --- /dev/null +++ b/.github/workflows/build-windows-debug.yml @@ -0,0 +1,67 @@ +name: Build Windows App with Nuitka (Debug) + +on: + push: + branches: [ fix/packaging-dependencies ] + workflow_dispatch: + +jobs: + build-windows-debug: + runs-on: windows-latest + + env: + TZ: Asia/Shanghai + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.13' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install nuitka[onefile] + pip install -r requirements.txt + + - name: Build Windows EXE with Nuitka (Debug Mode) + shell: cmd + env: + NUITKA_CACHE_DIR: "%LOCALAPPDATA%\Nuitka" + run: | + python -m nuitka ^ + --standalone ^ + --onefile ^ + --assume-yes-for-downloads ^ + --windows-console-mode=force ^ + --windows-icon-from-ico="logo/Color Card_logo.ico" ^ + --include-data-files=version.py=version.py ^ + --include-data-dir=color_data=color_data ^ + --include-data-dir=core=core ^ + --include-data-dir=dialogs=dialogs ^ + --include-data-dir=file=file ^ + --include-data-dir=locales=locales ^ + --include-data-dir=logo=logo ^ + --include-data-dir=scenes_data=scenes_data ^ + --include-data-dir=ui=ui ^ + --include-data-dir=utils=utils ^ + --enable-plugin=pyside6 ^ + --jobs=4 ^ + --output-filename="Color Card Debug" ^ + main.py + + - name: Check build output + shell: pwsh + run: | + Write-Host "Checking build directory..." + Get-ChildItem -Path "*.exe" -ErrorAction SilentlyContinue + + - name: Upload debug artifact + uses: actions/upload-artifact@v4 + with: + name: ColorCard-Windows-Debug + path: "Color Card Debug.exe" diff --git a/main.py b/main.py index fe88509..d124e84 100644 --- a/main.py +++ b/main.py @@ -3,10 +3,38 @@ import ctypes import os import sys import time +from pathlib import Path # 记录启动开始时间 _startup_start_time = time.perf_counter() +# 紧急调试日志(在日志系统初始化前使用) +_debug_log_path = Path.home() / ".color_card" / "logs" / "debug_startup.log" + +def _emergency_log(msg): + """紧急日志,不依赖日志系统""" + try: + _debug_log_path.parent.mkdir(parents=True, exist_ok=True) + with open(_debug_log_path, "a", encoding="utf-8") as f: + timestamp = time.strftime("%Y-%m-%d %H:%M:%S") + f.write(f"[{timestamp}] {msg}\n") + f.flush() + except Exception as e: + # 如果写入失败,尝试写入临时目录 + try: + temp_log = Path(os.environ.get("TEMP", "/tmp")) / "color_card_debug.log" + with open(temp_log, "a", encoding="utf-8") as f: + timestamp = time.strftime("%Y-%m-%d %H:%M:%S") + f.write(f"[{timestamp}] {msg} (original error: {e})\n") + f.flush() + except: + pass + +_emergency_log("=== 程序启动 ===") +_emergency_log(f"sys.frozen={getattr(sys, 'frozen', False)}") +_emergency_log(f"sys.argv[0]={sys.argv[0]}") +_emergency_log(f"__compiled__={'__compiled__' in globals()}") + def set_app_user_model_id(): """设置 Windows AppUserModelID @@ -46,12 +74,16 @@ def setup_global_exception_handler(logger): # 立即调用(在导入 PySide6 之前) +_emergency_log("调用 set_app_user_model_id...") set_app_user_model_id() +_emergency_log("set_app_user_model_id 完成") # 只导入启动画面必需的模块 +_emergency_log("开始导入 PySide6...") from PySide6.QtCore import Qt, QTimer from PySide6.QtGui import QColor, QPixmap from PySide6.QtWidgets import QApplication, QSplashScreen +_emergency_log("PySide6 导入完成") def _get_base_path() -> str: @@ -112,6 +144,7 @@ def _create_splash_screen(): def main(): + _emergency_log("进入 main() 函数") QApplication.setHighDpiScaleFactorRoundingPolicy( Qt.HighDpiScaleFactorRoundingPolicy.PassThrough ) @@ -119,12 +152,16 @@ def main(): # 增加 Qt 图像分配限制,支持超大图片(1GB) os.environ['QT_IMAGEIO_MAXALLOC'] = '1024' + _emergency_log("创建 QApplication...") app = QApplication(sys.argv) + _emergency_log("QApplication 创建完成") # 初始化日志系统 + _emergency_log("开始初始化日志系统...") from core import get_logger_manager, get_logger logger_manager = get_logger_manager() logger_manager.initialize() + _emergency_log("日志系统初始化完成") logger = get_logger("main") logger.info("应用程序启动") -- Gitee From 3cc4c469b6cf26e4d6beeef86d141dc862ef35b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 03:55:45 +0800 Subject: [PATCH 05/11] =?UTF-8?q?[=E7=BB=B4=E6=8A=A4]=20=E9=94=81=E5=AE=9A?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E5=BA=93=E7=89=88=E6=9C=AC=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E7=A8=B3=E5=AE=9A=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 requirements.txt 中的版本约束从 >= 改为 == - PySide6: 6.10.2 - PySide6-Fluent-Widgets: 1.11.1 - PySideSix-Frameless-Window: 0.8.0 - Pillow: 12.1.1 - requests: 2.32.5 - numpy: 2.4.2 --- requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index bbbc0af..b8d2c8d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ -PySide6~=6.10.2 -PySide6-Fluent-Widgets>=1.11.1 -PySideSix-Frameless-Window>=0.8.0 -Pillow>=12.1.1 -requests>=2.32.5 -numpy>=2.4.2 +PySide6==6.10.2 +PySide6-Fluent-Widgets==1.11.1 +PySideSix-Frameless-Window==0.8.0 +Pillow==12.1.1 +requests==2.32.5 +numpy==2.4.2 -- Gitee From 380a0383aed080ec72388b5c171944cf2f80b489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 04:14:03 +0800 Subject: [PATCH 06/11] =?UTF-8?q?[=E4=BF=AE=E5=A4=8D]=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20histogram=5Fservice.py=20=E7=BC=BA=E5=B0=91=20Any=20?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=AF=BC=E5=85=A5=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E9=97=AA=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/histogram_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/histogram_service.py b/core/histogram_service.py index accb089..7b7e4f9 100644 --- a/core/histogram_service.py +++ b/core/histogram_service.py @@ -1,5 +1,5 @@ # 标准库导入 -from typing import Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple # 第三方库导入 from PySide6.QtCore import QObject, QThread, Signal, QTimer, Qt -- Gitee From 36231fd3b8a9030762f9b3d93f13c66ec44d5416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 04:25:30 +0800 Subject: [PATCH 07/11] =?UTF-8?q?Revert=20"[=E7=BB=B4=E6=8A=A4]=20?= =?UTF-8?q?=E9=94=81=E5=AE=9A=E4=BE=9D=E8=B5=96=E5=BA=93=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=A1=AE=E4=BF=9D=E6=9E=84=E5=BB=BA=E7=A8=B3=E5=AE=9A=E6=80=A7?= =?UTF-8?q?"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 3cc4c469b6cf26e4d6beeef86d141dc862ef35b9. --- requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index b8d2c8d..bbbc0af 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ -PySide6==6.10.2 -PySide6-Fluent-Widgets==1.11.1 -PySideSix-Frameless-Window==0.8.0 -Pillow==12.1.1 -requests==2.32.5 -numpy==2.4.2 +PySide6~=6.10.2 +PySide6-Fluent-Widgets>=1.11.1 +PySideSix-Frameless-Window>=0.8.0 +Pillow>=12.1.1 +requests>=2.32.5 +numpy>=2.4.2 -- Gitee From b594e11d937f6db64bd814ba50bf3cf34ff799d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 04:25:44 +0800 Subject: [PATCH 08/11] =?UTF-8?q?Revert=20"[=E8=B0=83=E8=AF=95]=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=AF=E5=8A=A8=E8=B0=83=E8=AF=95=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=AE=9A=E4=BD=8D=E9=97=AA=E9=80=80=E9=97=AE"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a59adc74d78701f9f1d199ef230c2a06545eedb5. --- .github/workflows/build-windows-debug.yml | 67 ----------------------- main.py | 37 ------------- 2 files changed, 104 deletions(-) delete mode 100644 .github/workflows/build-windows-debug.yml diff --git a/.github/workflows/build-windows-debug.yml b/.github/workflows/build-windows-debug.yml deleted file mode 100644 index 37a5aed..0000000 --- a/.github/workflows/build-windows-debug.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Build Windows App with Nuitka (Debug) - -on: - push: - branches: [ fix/packaging-dependencies ] - workflow_dispatch: - -jobs: - build-windows-debug: - runs-on: windows-latest - - env: - TZ: Asia/Shanghai - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.13' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install nuitka[onefile] - pip install -r requirements.txt - - - name: Build Windows EXE with Nuitka (Debug Mode) - shell: cmd - env: - NUITKA_CACHE_DIR: "%LOCALAPPDATA%\Nuitka" - run: | - python -m nuitka ^ - --standalone ^ - --onefile ^ - --assume-yes-for-downloads ^ - --windows-console-mode=force ^ - --windows-icon-from-ico="logo/Color Card_logo.ico" ^ - --include-data-files=version.py=version.py ^ - --include-data-dir=color_data=color_data ^ - --include-data-dir=core=core ^ - --include-data-dir=dialogs=dialogs ^ - --include-data-dir=file=file ^ - --include-data-dir=locales=locales ^ - --include-data-dir=logo=logo ^ - --include-data-dir=scenes_data=scenes_data ^ - --include-data-dir=ui=ui ^ - --include-data-dir=utils=utils ^ - --enable-plugin=pyside6 ^ - --jobs=4 ^ - --output-filename="Color Card Debug" ^ - main.py - - - name: Check build output - shell: pwsh - run: | - Write-Host "Checking build directory..." - Get-ChildItem -Path "*.exe" -ErrorAction SilentlyContinue - - - name: Upload debug artifact - uses: actions/upload-artifact@v4 - with: - name: ColorCard-Windows-Debug - path: "Color Card Debug.exe" diff --git a/main.py b/main.py index d124e84..fe88509 100644 --- a/main.py +++ b/main.py @@ -3,38 +3,10 @@ import ctypes import os import sys import time -from pathlib import Path # 记录启动开始时间 _startup_start_time = time.perf_counter() -# 紧急调试日志(在日志系统初始化前使用) -_debug_log_path = Path.home() / ".color_card" / "logs" / "debug_startup.log" - -def _emergency_log(msg): - """紧急日志,不依赖日志系统""" - try: - _debug_log_path.parent.mkdir(parents=True, exist_ok=True) - with open(_debug_log_path, "a", encoding="utf-8") as f: - timestamp = time.strftime("%Y-%m-%d %H:%M:%S") - f.write(f"[{timestamp}] {msg}\n") - f.flush() - except Exception as e: - # 如果写入失败,尝试写入临时目录 - try: - temp_log = Path(os.environ.get("TEMP", "/tmp")) / "color_card_debug.log" - with open(temp_log, "a", encoding="utf-8") as f: - timestamp = time.strftime("%Y-%m-%d %H:%M:%S") - f.write(f"[{timestamp}] {msg} (original error: {e})\n") - f.flush() - except: - pass - -_emergency_log("=== 程序启动 ===") -_emergency_log(f"sys.frozen={getattr(sys, 'frozen', False)}") -_emergency_log(f"sys.argv[0]={sys.argv[0]}") -_emergency_log(f"__compiled__={'__compiled__' in globals()}") - def set_app_user_model_id(): """设置 Windows AppUserModelID @@ -74,16 +46,12 @@ def setup_global_exception_handler(logger): # 立即调用(在导入 PySide6 之前) -_emergency_log("调用 set_app_user_model_id...") set_app_user_model_id() -_emergency_log("set_app_user_model_id 完成") # 只导入启动画面必需的模块 -_emergency_log("开始导入 PySide6...") from PySide6.QtCore import Qt, QTimer from PySide6.QtGui import QColor, QPixmap from PySide6.QtWidgets import QApplication, QSplashScreen -_emergency_log("PySide6 导入完成") def _get_base_path() -> str: @@ -144,7 +112,6 @@ def _create_splash_screen(): def main(): - _emergency_log("进入 main() 函数") QApplication.setHighDpiScaleFactorRoundingPolicy( Qt.HighDpiScaleFactorRoundingPolicy.PassThrough ) @@ -152,16 +119,12 @@ def main(): # 增加 Qt 图像分配限制,支持超大图片(1GB) os.environ['QT_IMAGEIO_MAXALLOC'] = '1024' - _emergency_log("创建 QApplication...") app = QApplication(sys.argv) - _emergency_log("QApplication 创建完成") # 初始化日志系统 - _emergency_log("开始初始化日志系统...") from core import get_logger_manager, get_logger logger_manager = get_logger_manager() logger_manager.initialize() - _emergency_log("日志系统初始化完成") logger = get_logger("main") logger.info("应用程序启动") -- Gitee From 5c327e7f6d2c3f6b855f88d1d836145db850b35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 04:27:08 +0800 Subject: [PATCH 09/11] =?UTF-8?q?Revert=20"[=E4=BF=AE=E5=A4=8D]=20?= =?UTF-8?q?=E5=9B=9E=E6=BB=9A=20app=5Fmode.py=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=B9=B6=E5=A2=9E=E5=8A=A0=E8=B0=83=E8=AF=95=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=AE=9A=E4=BD=8D=E9=97=AA=E9=80=80=E9=97=AE=E9=A2=98"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 73330c97e6392dcc0e2da0ddf55a1643da55ff47. --- core/app_mode.py | 10 +++++++- main.py | 60 ++++++++---------------------------------------- 2 files changed, 18 insertions(+), 52 deletions(-) diff --git a/core/app_mode.py b/core/app_mode.py index 6347b16..7142c7c 100644 --- a/core/app_mode.py +++ b/core/app_mode.py @@ -95,7 +95,15 @@ def detect_mode() -> AppMode: _logger.debug(f"exe_path={exe_path}") _logger.debug(f"TEMP={temp}") - # 在临时目录运行 = 安装程序运行中 + # Nuitka 单文件模式:使用 __compiled__ 标记检测 + # Nuitka 单文件运行时也会解压到临时目录,但不能误判为安装程序模式 + if is_nuitka: + # Nuitka 单文件模式下,配置目录应该使用用户主目录(与安装版相同) + _mode_cache = AppMode.INSTALLED + _logger.info(f"运行模式判定: 安装版(Nuitka 单文件)") + return _mode_cache + + # 在临时目录运行 = 安装程序运行中(仅适用于非 Nuitka 环境) if temp and str(exe_path).lower().startswith(temp): _mode_cache = AppMode.INSTALLER _logger.info(f"运行模式判定: 安装程序模式") diff --git a/main.py b/main.py index fe88509..e5be5df 100644 --- a/main.py +++ b/main.py @@ -128,23 +128,15 @@ def main(): logger = get_logger("main") logger.info("应用程序启动") - logger.info(f"运行环境: frozen={getattr(sys, 'frozen', False)}, _MEIPASS={getattr(sys, '_MEIPASS', None)}, __compiled__={'__compiled__' in globals()}") - - # 记录关键路径信息 - logger.info(f"sys.argv[0]={sys.argv[0]}") - logger.info(f"sys.executable={sys.executable}") - logger.info(f"sys.path[0]={sys.path[0] if sys.path else 'empty'}") + logger.info(f"运行环境: frozen={getattr(sys, 'frozen', False)}, _MEIPASS={getattr(sys, '_MEIPASS', None)}") # 设置全局异常处理器 setup_global_exception_handler(logger) # 立即显示启动画面(在其他模块导入前) - logger.info("开始创建启动画面...") splash = _create_splash_screen() - logger.info(f"启动画面创建完成: {splash is not None}") try: - logger.info("进入主try块...") # 临时重定向 stdout 以屏蔽 QFluentWidgets 的推广提示 from io import StringIO _old_stdout = sys.stdout @@ -174,15 +166,12 @@ def main(): # 导入项目模块 logger.info("开始导入项目模块...") - logger.info("导入 core 模块...") from core import get_config_manager logger.info("core 模块导入完成") - logger.info("导入 utils 模块...") from utils import fix_windows_taskbar_icon_for_window, load_icon_universal, get_locale_manager, force_window_to_front logger.info("utils 模块导入完成") - logger.info("导入 ui 模块...") from ui import MainWindow logger.info("ui 模块导入完成") @@ -194,29 +183,17 @@ def main(): # 加载配置 logger.info("加载配置...") - try: - config_manager = get_config_manager() - logger.info("获取配置管理器完成") - config_manager.load() - logger.info("配置加载完成") - except Exception as e: - logger.error(f"配置加载失败: {e}", exc_info=True) - raise + config_manager = get_config_manager() + config_manager.load() # 初始化语言管理器并加载用户语言配置 logger.info("初始化语言管理器...") - try: - locale_manager = get_locale_manager() - logger.info("获取语言管理器完成") - language_setting = config_manager.get('settings.language', 'ZW_JT') - locale_manager.load_language(language_setting) - logger.info(f"语言设置: {language_setting}") - except Exception as e: - logger.error(f"语言管理器初始化失败: {e}", exc_info=True) - raise + locale_manager = get_locale_manager() + language_setting = config_manager.get('settings.language', 'ZW_JT') + locale_manager.load_language(language_setting) + logger.info(f"语言设置: {language_setting}") # 设置主题 - logger.info("设置主题...") theme_setting = config_manager.get('settings.theme', 'auto') if theme_setting == 'light': setTheme(Theme.LIGHT) @@ -225,15 +202,9 @@ def main(): else: setTheme(Theme.AUTO) setThemeColor('#0078d4') - logger.info(f"主题设置完成: {theme_setting}") logger.info("创建主窗口...") - try: - window = MainWindow() - logger.info("主窗口创建完成") - except Exception as e: - logger.error(f"主窗口创建失败: {e}", exc_info=True) - raise + window = MainWindow() # 初始化标题栏主题按钮状态 if hasattr(window, 'titleBar') and hasattr(window.titleBar, 'init_theme'): @@ -255,7 +226,6 @@ def main(): force_window_to_front(window) QTimer.singleShot(100, _on_window_shown) - logger.info("启动画面关闭定时器已设置") # 计算并输出启动时间 startup_time = (time.perf_counter() - _startup_start_time) * 1000 @@ -263,25 +233,13 @@ def main(): logger.info("进入主事件循环...") try: - exit_code = app.exec() - logger.info(f"主事件循环结束,退出码: {exit_code}") - sys.exit(exit_code) + sys.exit(app.exec()) except KeyboardInterrupt: logger.info("程序被用户中断 (Ctrl+C)") sys.exit(0) - except Exception as e: - logger.critical(f"主事件循环异常: {e}", exc_info=True) - raise except Exception as e: logger.critical(f"程序启动失败: {str(e)}", exc_info=True) - # 尝试记录更多调试信息 - try: - import traceback - tb_str = traceback.format_exc() - logger.critical(f"异常堆栈:\n{tb_str}") - except: - pass raise -- Gitee From 0a47f586612207f634ac03491def9ecebebc3d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 04:27:30 +0800 Subject: [PATCH 10/11] =?UTF-8?q?Revert=20"[=E4=BF=AE=E5=A4=8D]=20?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20ctypes=20=E6=9B=BF=E4=BB=A3=20pywin32=20?= =?UTF-8?q?=E8=A7=A3=E5=86=B3=20Nuitka=20=E6=89=93=E5=8C=85=E9=97=AA?= =?UTF-8?q?=E9=80=80=E9=97=AE=E9=A2=98"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 586d628d77c5b4027c6435e3bdb946e02807f5bb. --- core/app_mode.py | 10 +--------- dialogs/tone_analysis_dialog.py | 15 +++++---------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/core/app_mode.py b/core/app_mode.py index 7142c7c..6347b16 100644 --- a/core/app_mode.py +++ b/core/app_mode.py @@ -95,15 +95,7 @@ def detect_mode() -> AppMode: _logger.debug(f"exe_path={exe_path}") _logger.debug(f"TEMP={temp}") - # Nuitka 单文件模式:使用 __compiled__ 标记检测 - # Nuitka 单文件运行时也会解压到临时目录,但不能误判为安装程序模式 - if is_nuitka: - # Nuitka 单文件模式下,配置目录应该使用用户主目录(与安装版相同) - _mode_cache = AppMode.INSTALLED - _logger.info(f"运行模式判定: 安装版(Nuitka 单文件)") - return _mode_cache - - # 在临时目录运行 = 安装程序运行中(仅适用于非 Nuitka 环境) + # 在临时目录运行 = 安装程序运行中 if temp and str(exe_path).lower().startswith(temp): _mode_cache = AppMode.INSTALLER _logger.info(f"运行模式判定: 安装程序模式") diff --git a/dialogs/tone_analysis_dialog.py b/dialogs/tone_analysis_dialog.py index 5417ed3..7dc01b3 100644 --- a/dialogs/tone_analysis_dialog.py +++ b/dialogs/tone_analysis_dialog.py @@ -1,11 +1,11 @@ """明度分析对话框""" -import ctypes import math -import sys from typing import List, Optional import numpy as np +import win32con +import win32gui from PySide6.QtCharts import ( QBarSeries, QBarSet, QChart, QChartView, QLineSeries, QPieSeries, QValueAxis @@ -656,14 +656,9 @@ class ToneAnalysisDialog(BaseFramelessDialog): self.titleBar.setDoubleClickEnabled(True) # 恢复Win32最大化按钮样式(FramelessDialog初始化时禁用了) - if sys.platform == 'win32': - hWnd = int(self.winId()) - # 使用ctypes替代pywin32 - GWL_STYLE = -16 - WS_MAXIMIZEBOX = 0x00010000 - user32 = ctypes.windll.user32 - style = user32.GetWindowLongW(hWnd, GWL_STYLE) - user32.SetWindowLongW(hWnd, GWL_STYLE, style | WS_MAXIMIZEBOX) + hWnd = int(self.winId()) + style = win32gui.GetWindowLong(hWnd, win32con.GWL_STYLE) + win32gui.SetWindowLong(hWnd, win32con.GWL_STYLE, style | win32con.WS_MAXIMIZEBOX) self._setup_title_bar() self._setup_ui() -- Gitee From 4a095d407dd5fa0f517a77214fa4e46ee8051d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=B1=B1=E5=85=AC=E4=BB=94?= Date: Sun, 26 Apr 2026 13:59:13 +0800 Subject: [PATCH 11/11] =?UTF-8?q?[=E5=86=85=E5=AE=B9=E8=B0=83=E6=95=B4]=20?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- version.txt | 4 ++-- version_info.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/version.txt b/version.txt index 00be7b7..edc3c82 100644 --- a/version.txt +++ b/version.txt @@ -1,6 +1,6 @@ 1.9.0 -2026.4.26.1 -1.9.0.4 +2026.4.26.2 +1.9.0.5 浮晓 HXiao Studio © 2026 浮晓 HXiao Studio 取色卡 - Color Card \ No newline at end of file diff --git a/version_info.txt b/version_info.txt index 860c6cc..9501627 100644 --- a/version_info.txt +++ b/version_info.txt @@ -1,7 +1,7 @@ VSVersionInfo( ffi=FixedFileInfo( - filevers=(2026,4,26,1), - prodvers=(1,9,0,4), + filevers=(2026,4,26,2), + prodvers=(1,9,0,5), mask=0x3f, flags=0x0, OS=0x4, -- Gitee