From 9ea3c32c64db11a202bb68fd313564cce3d20a7e Mon Sep 17 00:00:00 2001 From: Danil Iarosh Date: Thu, 19 Mar 2026 16:15:18 +0300 Subject: [PATCH] fix: keep resource folders external alongside EXE instead of embedding --- BUILD_EXE.md | 17 ++++++++++++++--- DEVELOPMENT.md | 10 ++++++---- README.md | 6 ++++-- app/services/commands_service.py | 4 ++-- app/services/input_mappings_service.py | 4 ++-- app/services/light_modes_service.py | 5 ++--- build_exe.ps1 | 25 ++++++++++++++++++++----- plate_config_app.spec | 3 --- 8 files changed, 50 insertions(+), 24 deletions(-) diff --git a/BUILD_EXE.md b/BUILD_EXE.md index b9800d3..ddac023 100644 --- a/BUILD_EXE.md +++ b/BUILD_EXE.md @@ -32,6 +32,7 @@ python -m pip install -r requirements-build.txt Скрипт автоматически: - очищает `build/`, `dist/`, `__pycache__/` - запускает PyInstaller по `plate_config_app.spec` +- копирует папки `light_modes/`, `input_mappings/`, `commands/` в `dist/` - выводит путь и размер готового EXE ### 3. Только очистка (без сборки) @@ -59,9 +60,19 @@ python -m pip install -r requirements-build.txt | Ресурс | Описание | |----------------------|---------------------------------| -| `light_modes/` | Файлы режимов подсветки (.lm) | -| `input_mappings/` | Маппинги входов (.mapping) | -| `commands/` | Наборы команд (.csv) | | `qtawesome/` | Шрифты и charmap иконок | Hidden imports: `qtawesome`, `Crypto.Cipher._raw_aes`, `Crypto.Util._raw_api` + +## Ресурсы рядом с EXE + +Скрипт `build_exe.ps1` после сборки копирует папки в `dist/`: + +| Папка | Описание | +|--------------------|---------------------------------| +| `light_modes/` | Файлы режимов подсветки (.lm) | +| `input_mappings/` | Маппинги входов (.mapping) | +| `commands/` | Наборы команд (.csv) | + +Эти файлы можно редактировать без пересборки — достаточно изменить их +рядом с EXE и перезапустить приложение. diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index bd71d99..1dca3a8 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -172,9 +172,10 @@ health-check) вынесены в `QThread`-подклассы. Связь с UI Создайте `.mapping` файл в `input_mappings/` в формате TOML. Структура описана в `app/services/input_mappings_service.py`. -После добавления файлов они подхватятся при следующем запуске из исходников. -Для EXE-сборки необходимо пересобрать приложение (папки автоматически -включаются через `plate_config_app.spec`). +После добавления файлов они подхватятся при следующем запуске. +При сборке EXE папки автоматически копируются рядом с исполняемым файлом +скриптом `build_exe.ps1`. Пересборка для обновления конфигураций **не нужна** — +достаточно отредактировать файлы рядом с EXE и перезапустить приложение. --- @@ -189,4 +190,5 @@ health-check) вынесены в `QThread`-подклассы. Связь с UI .\build_exe.ps1 -InstallBuildDeps ``` -Результат: `dist\plate_config_app.exe`. +Результат: `dist\plate_config_app.exe` + папки `dist\light_modes\`, +`dist\input_mappings\`, `dist\commands\`. diff --git a/README.md b/README.md index 4db8eb1..449646e 100644 --- a/README.md +++ b/README.md @@ -111,8 +111,10 @@ | `input_mappings/` | `.mapping` | Маппинги входов по играм (формат TOML) | | `commands/` | `.csv` | Наборы команд платы (разделитель `;`) | -Эти файлы встраиваются внутрь EXE при сборке. Для обновления конфигураций -необходима пересборка. +Эти файлы располагаются рядом с EXE (или в корне проекта при запуске из +исходников). При сборке они автоматически копируются в папку `dist/` рядом с +EXE. Можно редактировать файлы конфигураций без пересборки — достаточно +изменить нужный файл и перезапустить приложение. --- diff --git a/app/services/commands_service.py b/app/services/commands_service.py index 2590c36..34969db 100644 --- a/app/services/commands_service.py +++ b/app/services/commands_service.py @@ -32,11 +32,11 @@ class CommandSet: def _get_commands_dir() -> Path: """Определить путь к папке commands/. - При запуске из .exe (PyInstaller onefile) — в распакованном каталоге _MEIPASS. + При запуске из .exe (PyInstaller) — рядом с exe-файлом. При запуске из исходников — рядом с корнем проекта. """ if getattr(sys, "frozen", False): - base = Path(getattr(sys, "_MEIPASS", Path(sys.executable).parent)) + base = Path(sys.executable).parent else: base = Path(__file__).resolve().parent.parent.parent return base / "commands" diff --git a/app/services/input_mappings_service.py b/app/services/input_mappings_service.py index ed5dd5e..4e7fbcb 100644 --- a/app/services/input_mappings_service.py +++ b/app/services/input_mappings_service.py @@ -41,11 +41,11 @@ class InputMapping: def _get_input_mappings_dir() -> Path: """Определить путь к папке input_mappings/. - При запуске из .exe (PyInstaller onefile) — в распакованном каталоге _MEIPASS. + При запуске из .exe (PyInstaller) — рядом с exe-файлом. При запуске из исходников — рядом с корнем проекта. """ if getattr(sys, "frozen", False): - base = Path(getattr(sys, "_MEIPASS", Path(sys.executable).parent)) + base = Path(sys.executable).parent else: base = Path(__file__).resolve().parent.parent.parent return base / "input_mappings" diff --git a/app/services/light_modes_service.py b/app/services/light_modes_service.py index 77d450f..ebbd48c 100644 --- a/app/services/light_modes_service.py +++ b/app/services/light_modes_service.py @@ -34,13 +34,12 @@ class LightMode: def _get_light_modes_dir() -> Path: """Определить путь к папке light_modes/. - При запуске из .exe (PyInstaller onefile) — в распакованном каталоге _MEIPASS. + При запуске из .exe (PyInstaller) — рядом с exe-файлом. При запуске из исходников — рядом с корнем проекта. """ if getattr(sys, "frozen", False): - base = Path(getattr(sys, "_MEIPASS", Path(sys.executable).parent)) + base = Path(sys.executable).parent else: - # Разработка: корень проекта (где лежит main.py) base = Path(__file__).resolve().parent.parent.parent return base / "light_modes" diff --git a/build_exe.ps1 b/build_exe.ps1 index 752c281..b26307b 100644 --- a/build_exe.ps1 +++ b/build_exe.ps1 @@ -58,10 +58,25 @@ if ($LASTEXITCODE -ne 0) { } $exe = Join-Path $projectRoot "dist\plate_config_app.exe" -if (Test-Path $exe) { - $size = [math]::Round((Get-Item $exe).Length / 1MB, 1) - Write-Host "" - Write-Host "Сборка завершена: $exe ($size MB)" -} else { +if (-not (Test-Path $exe)) { throw "EXE не найден после сборки." } + +# ── Копирование ресурсных папок рядом с EXE ─────────────────────── +Write-Host "" +Write-Host "=== Копирование ресурсов ===" +$resourceFolders = @("light_modes", "input_mappings", "commands") +foreach ($folder in $resourceFolders) { + $src = Join-Path $projectRoot $folder + $dst = Join-Path $projectRoot "dist\$folder" + if (Test-Path $src) { + Copy-Item $src -Destination $dst -Recurse -Force + Write-Host " Скопировано: $folder" + } else { + Write-Host " Пропущено (не найдено): $folder" -ForegroundColor Yellow + } +} + +$size = [math]::Round((Get-Item $exe).Length / 1MB, 1) +Write-Host "" +Write-Host "Сборка завершена: $exe ($size MB)" diff --git a/plate_config_app.spec b/plate_config_app.spec index 6c134cc..43e91e8 100644 --- a/plate_config_app.spec +++ b/plate_config_app.spec @@ -11,9 +11,6 @@ a = Analysis( pathex=[], binaries=[], datas=[ - ('light_modes', 'light_modes'), - ('input_mappings', 'input_mappings'), - ('commands', 'commands'), (_qta_dir, 'qtawesome'), ], hiddenimports=[