Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf2db92040 | |||
| 19f7deec20 | |||
| 5f10b79906 |
@@ -4,7 +4,10 @@ PyInstaller Konfiguration für DocuMentor
|
||||
Erstellt eine eigenständige Windows-Executable ohne Python-Installation
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from importlib.metadata import PackageNotFoundError
|
||||
from importlib.metadata import version as pkg_version
|
||||
from pathlib import Path
|
||||
from PyInstaller.utils.hooks import collect_all
|
||||
|
||||
@@ -14,6 +17,22 @@ block_cipher = None
|
||||
project_root = Path(SPECPATH)
|
||||
src_path = project_root / 'src'
|
||||
|
||||
# Versions-Snapshot erzeugen und ins Bundle einbetten
|
||||
_packages_to_snapshot = [
|
||||
"PySide6", "pydantic", "pydantic-settings", "pydantic-yaml",
|
||||
"polars", "connectorx", "pyarrow", "psutil", "lxml",
|
||||
"ruff", "pyinstaller", "pillow",
|
||||
]
|
||||
_versions_snapshot: dict[str, str] = {}
|
||||
for _pkg in _packages_to_snapshot:
|
||||
try:
|
||||
_versions_snapshot[_pkg.lower()] = pkg_version(_pkg)
|
||||
except PackageNotFoundError:
|
||||
_versions_snapshot[_pkg.lower()] = ""
|
||||
|
||||
_versions_file = project_root / "versions.json"
|
||||
_versions_file.write_text(json.dumps(_versions_snapshot), encoding="utf-8")
|
||||
|
||||
# connectorx komplett sammeln (Python-Code, native .pyd und Metadaten)
|
||||
# PyInstaller erkennt connectorx nicht automatisch, da es zur Laufzeit
|
||||
# von polars per importlib.import_module() geladen wird
|
||||
@@ -36,6 +55,7 @@ a = Analysis(
|
||||
datas=ui_files + res_files + cx_datas + [
|
||||
(str(project_root / 'pyproject.toml'), '.'),
|
||||
(str(project_root / 'THIRD_PARTY_LICENSES.txt'), '.'),
|
||||
(str(_versions_file), '.'),
|
||||
(str(project_root / 'resources' / 'icon.ico'), 'resources'),
|
||||
],
|
||||
hiddenimports=[
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<!-- Paket-Definition (ersetzt Product in v4) -->
|
||||
<Package
|
||||
Name="DocuMentor"
|
||||
Version="1.6.4"
|
||||
Version="1.6.6"
|
||||
Manufacturer="Vitali Graf / Software- und Datenbankentwicklung"
|
||||
UpgradeCode="F498B66C-726D-44AA-95F4-CB4FBDCEF26E"
|
||||
Language="1031"
|
||||
|
||||
@@ -253,5 +253,5 @@ HINWEISE
|
||||
|
||||
================================================================================
|
||||
Stand: April 2026
|
||||
Erstellt für: DocuMentor v1.6.4
|
||||
Erstellt für: DocuMentor v1.6.6
|
||||
================================================================================
|
||||
|
||||
+13
-4
@@ -48,10 +48,19 @@ def build_msi():
|
||||
|
||||
# Schritt 2: MSI kompilieren mit WiX v6
|
||||
print("Schritt 2/2: Kompiliere MSI-Installer...")
|
||||
result = subprocess.run(
|
||||
["wix", "build", "DocuMentor.wxs", "ProductFiles.wxs", "-o", str(msi_output)],
|
||||
check=False,
|
||||
)
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["wix", "build", "DocuMentor.wxs", "ProductFiles.wxs", "-o", str(msi_output)],
|
||||
check=False,
|
||||
)
|
||||
except FileNotFoundError:
|
||||
print("\nFEHLER: 'wix' wurde nicht gefunden!")
|
||||
print("WiX v6 muss installiert sein. Installationsschritte:")
|
||||
print(" 1. .NET SDK installieren: https://dot.net")
|
||||
print(" 2. WiX als dotnet tool installieren:")
|
||||
print(" dotnet tool install --global wix --version 6.*")
|
||||
print(" 3. Neues Terminal öffnen (PATH aktualisieren)")
|
||||
sys.exit(1)
|
||||
|
||||
if result.returncode != 0:
|
||||
print("\nFEHLER: MSI-Kompilierung fehlgeschlagen!")
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
; Build-Befehl: iscc installer.iss
|
||||
|
||||
#define MyAppName "DocuMentor"
|
||||
#define MyAppVersion "1.6.4"
|
||||
#define MyAppVersion "1.6.6"
|
||||
#define MyAppPublisher "Ihr Name/Organisation"
|
||||
#define MyAppURL "https://github.com/yourusername/xsl-validator"
|
||||
#define MyAppExeName "DocuMentor.exe"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "DocuMentor"
|
||||
version = "1.6.4"
|
||||
version = "1.6.6"
|
||||
description = "Professionelle XSL-Transformations-Verwaltung und PDF-Generierung"
|
||||
readme = "README.md"
|
||||
license = {text = "MIT"}
|
||||
|
||||
+24
-1
@@ -2,8 +2,10 @@
|
||||
Parser für THIRD_PARTY_LICENSES.txt.
|
||||
|
||||
Extrahiert strukturierte Lizenzinformationen und ergänzt sie
|
||||
mit den tatsächlich installierten Paketversionen via importlib.metadata.
|
||||
mit den tatsächlich installierten Paketversionen via importlib.metadata
|
||||
oder (im PyInstaller-Bundle) aus der mitgebündelten versions.json.
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import sys
|
||||
@@ -71,8 +73,29 @@ def _normalize_package_name(display_name: str) -> str:
|
||||
return display_name.lower().split("(")[0].strip()
|
||||
|
||||
|
||||
_bundled_versions: dict[str, str] | None = None
|
||||
|
||||
|
||||
def _load_bundled_versions() -> dict[str, str]:
|
||||
"""Lädt den Versions-Snapshot aus der mitgebündelten versions.json (nur im PyInstaller-Bundle)."""
|
||||
global _bundled_versions
|
||||
if _bundled_versions is None:
|
||||
if hasattr(sys, "_MEIPASS"):
|
||||
versions_file = Path(sys._MEIPASS) / "versions.json" # type: ignore[attr-defined]
|
||||
if versions_file.exists():
|
||||
_bundled_versions = json.loads(versions_file.read_text(encoding="utf-8"))
|
||||
else:
|
||||
_bundled_versions = {}
|
||||
else:
|
||||
_bundled_versions = {}
|
||||
return _bundled_versions
|
||||
|
||||
|
||||
def _get_installed_version(package_name: str) -> str:
|
||||
"""Ermittelt die installierte Version eines Pakets."""
|
||||
bundled = _load_bundled_versions()
|
||||
if bundled:
|
||||
return bundled.get(package_name.lower(), "")
|
||||
try:
|
||||
return version(package_name)
|
||||
except PackageNotFoundError:
|
||||
|
||||
@@ -59,12 +59,31 @@ class TreeManagerMixin:
|
||||
# Aktiviere Kontextmenü für das TreeWidget
|
||||
self.ui.treeWidget.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||
self.ui.treeWidget.customContextMenuRequested.connect(self._show_tree_context_menu)
|
||||
self.ui.treeWidget.itemDoubleClicked.connect(self._on_tree_item_double_clicked)
|
||||
|
||||
# Verbinde Selection-Changed-Signal für automatisches Laden von Diff-PDFs
|
||||
self.ui.treeWidget.itemSelectionChanged.connect(self._on_tree_selection_changed)
|
||||
|
||||
logger.debug("Kontextmenü und Selection-Handler für TreeWidget eingerichtet")
|
||||
|
||||
def _on_tree_item_double_clicked(self, item, column):
|
||||
"""
|
||||
Behandelt Doppelklick auf ein TreeWidget-Item, indem die Bearbeiten-Aktion ausgeführt wird.
|
||||
|
||||
Args:
|
||||
item: Das angeklickte TreeWidgetItem
|
||||
column: Die Spalte des Doppelklicks
|
||||
"""
|
||||
if item.isDisabled():
|
||||
return
|
||||
node_type = self._get_node_type_from_item(item)
|
||||
if node_type == ItemType.TREE_NODE:
|
||||
self._edit_tree_node(item)
|
||||
elif node_type == ItemType.XSL_FILE:
|
||||
self._edit_xsl_file(item)
|
||||
elif node_type == ItemType.XML_FILE:
|
||||
self._edit_xml_file(item)
|
||||
|
||||
def _show_tree_context_menu(self, position):
|
||||
"""
|
||||
Zeigt das Kontextmenü für das TreeWidget an.
|
||||
@@ -299,6 +318,17 @@ class TreeManagerMixin:
|
||||
|
||||
if node_type == ItemType.TREE_NODE:
|
||||
# Kontextmenü für TreeNode
|
||||
action_edit = QAction("Bearbeiten", self)
|
||||
action_edit.setIcon(QIcon(QIcon.fromTheme(QIcon.ThemeIcon.DocumentProperties)))
|
||||
action_edit.triggered.connect(lambda: self._edit_tree_node(item))
|
||||
font = action_edit.font()
|
||||
font.setBold(True)
|
||||
action_edit.setFont(font)
|
||||
menu.addAction(action_edit)
|
||||
menu.setDefaultAction(action_edit)
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
action_add_child = QAction("Unterknoten hinzufügen", self)
|
||||
action_add_child.setIcon(QIcon(QIcon.fromTheme("folder-new")))
|
||||
action_add_child.triggered.connect(lambda: self._add_tree_node_child(item))
|
||||
@@ -341,11 +371,6 @@ class TreeManagerMixin:
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
action_edit = QAction("Bearbeiten", self)
|
||||
action_edit.setIcon(QIcon(QIcon.fromTheme(QIcon.ThemeIcon.DocumentProperties)))
|
||||
action_edit.triggered.connect(lambda: self._edit_tree_node(item))
|
||||
menu.addAction(action_edit)
|
||||
|
||||
action_delete = QAction("Löschen", self)
|
||||
action_delete.setIcon(QIcon(QIcon.fromTheme(QIcon.ThemeIcon.EditDelete)))
|
||||
action_delete.triggered.connect(lambda: self._delete_tree_node(item))
|
||||
@@ -353,6 +378,17 @@ class TreeManagerMixin:
|
||||
|
||||
elif node_type == ItemType.XSL_FILE:
|
||||
# Kontextmenü für XslFile
|
||||
action_edit = QAction("Bearbeiten", self)
|
||||
action_edit.setIcon(QIcon(QIcon.fromTheme(QIcon.ThemeIcon.DocumentProperties)))
|
||||
action_edit.triggered.connect(lambda: self._edit_xsl_file(item))
|
||||
font = action_edit.font()
|
||||
font.setBold(True)
|
||||
action_edit.setFont(font)
|
||||
menu.addAction(action_edit)
|
||||
menu.setDefaultAction(action_edit)
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
action_add_xml = QAction("XML-Datei hinzufügen", self)
|
||||
action_add_xml.setIcon(QIcon(QIcon.fromTheme("document-new")))
|
||||
action_add_xml.triggered.connect(lambda: self._add_xml_file_to_xsl(item))
|
||||
@@ -397,11 +433,6 @@ class TreeManagerMixin:
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
action_edit = QAction("Bearbeiten", self)
|
||||
action_edit.setIcon(QIcon(QIcon.fromTheme(QIcon.ThemeIcon.DocumentProperties)))
|
||||
action_edit.triggered.connect(lambda: self._edit_xsl_file(item))
|
||||
menu.addAction(action_edit)
|
||||
|
||||
action_delete = QAction("Löschen", self)
|
||||
action_delete.setIcon(QIcon(QIcon.fromTheme(QIcon.ThemeIcon.EditDelete)))
|
||||
action_delete.triggered.connect(lambda: self._delete_xsl_file(item))
|
||||
@@ -409,6 +440,17 @@ class TreeManagerMixin:
|
||||
|
||||
elif node_type == ItemType.XML_FILE:
|
||||
# Kontextmenü für XmlFile
|
||||
action_edit = QAction("Bearbeiten", self)
|
||||
action_edit.setIcon(QIcon(QIcon.fromTheme(QIcon.ThemeIcon.DocumentProperties)))
|
||||
action_edit.triggered.connect(lambda: self._edit_xml_file(item))
|
||||
font = action_edit.font()
|
||||
font.setBold(True)
|
||||
action_edit.setFont(font)
|
||||
menu.addAction(action_edit)
|
||||
menu.setDefaultAction(action_edit)
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
# Transformations-Aktionen
|
||||
action_transform = QAction("Transformieren", self)
|
||||
action_transform.setIcon(QIcon(QIcon.fromTheme("system-run")))
|
||||
@@ -422,11 +464,6 @@ class TreeManagerMixin:
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
action_edit = QAction("Bearbeiten", self)
|
||||
action_edit.setIcon(QIcon(QIcon.fromTheme(QIcon.ThemeIcon.DocumentProperties)))
|
||||
action_edit.triggered.connect(lambda: self._edit_xml_file(item))
|
||||
menu.addAction(action_edit)
|
||||
|
||||
action_delete = QAction("Löschen", self)
|
||||
action_delete.setIcon(QIcon(QIcon.fromTheme(QIcon.ThemeIcon.EditDelete)))
|
||||
action_delete.triggered.connect(lambda: self._delete_xml_file(item))
|
||||
|
||||
Reference in New Issue
Block a user