Add PyInstaller packaging (--onedir) as alternative to pyw launch

Avoids the cmd.exe flash from double-clicking run_desktop.bat and
gives a proper double-click .exe like VersaGUI.exe. Must be deployed
locally (%LOCALAPPDATA%) -- Windows silently refuses to load the
_internal DLLs when the .exe sits on the Z: network share, no error
shown. build_and_deploy.ps1 handles build + local copy in one step.
This commit is contained in:
cjjohn 2026-08-05 08:04:38 +02:00
parent 6227903ebd
commit 50658107d5
4 changed files with 99 additions and 0 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
__pycache__/ __pycache__/
*.pyc *.pyc
*.log *.log
build/
dist/

View file

@ -100,6 +100,33 @@ liegt in PATH zuerst eine Hermes-Agent-venv (`...\hermes\hermes-agent\venv\
Scripts\pythonw.exe`) ohne pyserial. `pyw`/`py` (Python Launcher) lösen Scripts\pythonw.exe`) ohne pyserial. `pyw`/`py` (Python Launcher) lösen
zuverlässig zur echten Python313-Installation auf. zuverlässig zur echten Python313-Installation auf.
## Gepackte .exe (PyInstaller) — kein Terminal-Flash, kein Python-Setup nötig
`build_and_deploy.ps1` baut `desktop_viewer.py` mit PyInstaller
(`--windowed --onedir`) und kopiert das Ergebnis lokal nach
`%LOCALAPPDATA%\VersaPadViewer\VersaPadViewer.exe`. Desktop-Verknüpfung
"VersaPad Viewer (Fenster)" zeigt dorthin.
**`--onedir`, nicht `--onefile`** — Single-File-PyInstaller-Bundles lösen
öfter AV-Fehlalarme aus (sehen strukturell wie ein Packer aus); `--onedir`
(Ordner mit `.exe` + `_internal`) ist unauffälliger. Trotzdem unsigniert,
also grundsätzlich nie über Smart App Control/AV in Stein gemeißelt sicher.
**Kritischer Stolperstein:** Die gebaute `.exe` läuft NICHT vom Netzlaufwerk
aus (`Z:\Git\...`, SMB-Share auf cj-ki) — Windows blockiert das Nachladen
der `_internal`-DLLs von einem Netzwerkpfad, der Prozess startet dann
einfach nie, ganz ohne Fehlermeldung/Crash-Log/AV-Benachrichtigung (sah
zuerst wie ein AV-Block aus, war aber keiner — sauber verifiziert indem
dieselbe `.exe` 1:1 lokal kopiert sofort lief). Bauen darf auf Z: passieren,
das fertige Bundle muss vor dem Start nach C: kopiert werden — genau das
macht `build_and_deploy.ps1`.
Bei erneutem Bauen: `.\build_and_deploy.ps1` in PowerShell ausführen
(lokal, nicht über die sandboxed Bash-Tool-Umgebung — die hat eine eigene
Dateisystem-Sicht, die nicht zuverlässig mit dem echten Windows-Dateisystem
übereinstimmt; Kopieroperationen für Dinge, die der User/andere Prozesse
sehen sollen, gehören in PowerShell, nicht in Bash).
## Dateien/Pfade (hardcoded, hal9001-spezifisch) ## Dateien/Pfade (hardcoded, hal9001-spezifisch)
- Einzel-JSONs (Read-only-Modus): `~\OneDrive\Desktop\versapad_config{1,2,3}.json` - Einzel-JSONs (Read-only-Modus): `~\OneDrive\Desktop\versapad_config{1,2,3}.json`

44
VersaPadViewer.spec Normal file
View file

@ -0,0 +1,44 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['desktop_viewer.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='VersaPadViewer',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='VersaPadViewer',
)

26
build_and_deploy.ps1 Normal file
View file

@ -0,0 +1,26 @@
# Baut VersaPadViewer.exe (PyInstaller --onedir) und kopiert das Ergebnis
# nach lokal (C:\Users\<user>\AppData\Local\VersaPadViewer).
#
# WICHTIG: Muss lokal laufen, NICHT direkt vom Netzlaufwerk (Z:\Git\...) --
# Windows blockiert das Nachladen der PyInstaller-_internal-DLLs von einem
# Netzwerkpfad (SMB-Share) ohne jede Fehlermeldung, der Prozess startet
# einfach nie. Deshalb bauen wir hier (Quelle darf auf Z: liegen) und
# kopieren das fertige Bundle danach nach C:.
$ErrorActionPreference = "Stop"
$projectDir = $PSScriptRoot
$localDir = "$env:LOCALAPPDATA\VersaPadViewer"
Push-Location $projectDir
try {
py -m PyInstaller --windowed --onedir --name VersaPadViewer desktop_viewer.py --noconfirm
} finally {
Pop-Location
}
if (Test-Path $localDir) {
Remove-Item $localDir -Recurse -Force
}
Copy-Item "$projectDir\dist\VersaPadViewer" -Destination $localDir -Recurse
Write-Host "Fertig: $localDir\VersaPadViewer.exe"