Feat: Hilfe-Menü mit Info-Dialog und Lizenz-Parser hinzugefügt (v1.1.0)

Neues Menü "Hilfe > Info" zeigt Programmversion, Python-Version und alle
Drittanbieter-Bibliotheken mit installierten Versionen und Lizenzinfos an.
Der license_parser liest THIRD_PARTY_LICENSES.txt als Datenquelle und
ergänzt tatsächlich installierte Versionen via importlib.metadata.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 16:54:21 +01:00
parent c69c163b34
commit a8b4fac085
7 changed files with 357 additions and 4 deletions
+148
View File
@@ -0,0 +1,148 @@
"""
Info-Dialog für DocuMentor.
Zeigt Programmversion, Python-Version und alle Drittanbieter-Bibliotheken
mit installierten Versionen und Lizenzinformationen an.
"""
import logging
import sys
from importlib.metadata import PackageNotFoundError, version
from pathlib import Path
from PySide6.QtCore import Qt
from PySide6.QtGui import QFont
from PySide6.QtWidgets import (
QDialog,
QDialogButtonBox,
QFrame,
QHBoxLayout,
QHeaderView,
QLabel,
QTableWidget,
QTableWidgetItem,
QVBoxLayout,
)
from license_parser import parse_license_file
logger = logging.getLogger(__name__)
class AboutDialog(QDialog):
"""Info-Dialog mit Versionsinformationen und Drittanbieter-Lizenzen."""
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Über DocuMentor")
self.resize(950, 620)
self.setSizeGripEnabled(True)
self.setModal(True)
self._setup_ui()
self._populate_data()
def _get_app_version(self) -> str:
"""Ermittelt die Programmversion."""
try:
return version("DocuMentor")
except PackageNotFoundError:
# Fallback: pyproject.toml parsen (Entwicklungsmodus ohne Installation)
try:
import tomllib
pyproject = Path(__file__).parent.parent.parent / "pyproject.toml"
with open(pyproject, "rb") as f:
return tomllib.load(f)["project"]["version"]
except Exception:
return "unbekannt"
def _setup_ui(self):
"""Erstellt das Dialog-Layout programmatisch."""
layout = QVBoxLayout(self)
layout.setSpacing(8)
# App-Name
self.app_name_label = QLabel("DocuMentor")
font = QFont()
font.setPointSize(18)
font.setBold(True)
self.app_name_label.setFont(font)
self.app_name_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.app_name_label)
# Beschreibung
self.description_label = QLabel("Professionelle XSL-Transformations-Verwaltung und PDF-Generierung")
self.description_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.description_label.setWordWrap(True)
layout.addWidget(self.description_label)
# Version + Python-Version
self.version_label = QLabel()
self.version_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.version_label)
# Lizenz
self.license_label = QLabel("Lizenz: MIT")
self.license_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(self.license_label)
# Separator
separator = QFrame()
separator.setFrameShape(QFrame.Shape.HLine)
separator.setFrameShadow(QFrame.Shadow.Sunken)
layout.addWidget(separator)
# Überschrift für Tabelle
header_layout = QHBoxLayout()
table_header = QLabel("Drittanbieter-Bibliotheken:")
header_font = QFont()
header_font.setBold(True)
table_header.setFont(header_font)
header_layout.addWidget(table_header)
header_layout.addStretch()
layout.addLayout(header_layout)
# Dependency-Tabelle
self.table = QTableWidget()
self.table.setColumnCount(6)
self.table.setHorizontalHeaderLabels(["Name", "Lizenz", "Installiert", "Webseite", "Copyright", "Kategorie"])
self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
self.table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
self.table.setAlternatingRowColors(True)
self.table.setSortingEnabled(True)
self.table.verticalHeader().setVisible(False)
# Spaltenbreiten
header = self.table.horizontalHeader()
header.resizeSection(0, 170) # Name
header.resizeSection(1, 180) # Lizenz
header.resizeSection(2, 80) # Installiert
header.resizeSection(5, 140) # Kategorie
header.setSectionResizeMode(3, QHeaderView.ResizeMode.Stretch) # Webseite
header.setSectionResizeMode(4, QHeaderView.ResizeMode.Stretch) # Copyright
layout.addWidget(self.table)
# Schließen-Button
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close)
button_box.rejected.connect(self.reject)
button_box.setCenterButtons(True)
layout.addWidget(button_box)
def _populate_data(self):
"""Befüllt den Dialog mit Versions- und Lizenzinformationen."""
# Version setzen
app_version = self._get_app_version()
python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
self.version_label.setText(f"Version: {app_version} | Python: {python_version}")
# Lizenzdaten laden und Tabelle befüllen
parsed = parse_license_file()
self.table.setRowCount(len(parsed.entries))
for row, entry in enumerate(parsed.entries):
self.table.setItem(row, 0, QTableWidgetItem(entry.name))
self.table.setItem(row, 1, QTableWidgetItem(entry.license))
self.table.setItem(row, 2, QTableWidgetItem(entry.installed_version or ""))
self.table.setItem(row, 3, QTableWidgetItem(entry.website))
self.table.setItem(row, 4, QTableWidgetItem(entry.copyright))
self.table.setItem(row, 5, QTableWidgetItem(entry.category))
+15
View File
@@ -400,6 +400,14 @@ class MainWindow(
self.ui.view_ref_pdf.clicked.connect(self._on_view_ref_pdf_clicked)
self.ui.view_new_pdf.clicked.connect(self._on_view_new_pdf_clicked)
# Hilfe-Menü programmatisch erstellen
self.menu_hilfe = QMenu("Hilfe", self)
self.action_info = QAction("Info ...", self)
self.action_info.setIcon(_QIcon(_QIcon.fromTheme("help-about")))
self.action_info.triggered.connect(self._show_about_dialog)
self.menu_hilfe.addAction(self.action_info)
self.ui.menubar.addMenu(self.menu_hilfe)
def open_settings_dialog(self):
"""Öffnet den Einstellungen-Dialog."""
try:
@@ -411,6 +419,13 @@ class MainWindow(
except Exception as e:
logger.error(f"Fehler beim Öffnen des Einstellungen-Dialogs: {e}")
def _show_about_dialog(self):
"""Öffnet den Info-Dialog mit Versionsinformationen und Drittanbieter-Lizenzen."""
from ui.AboutDialog import AboutDialog
dialog = AboutDialog(self)
dialog.exec()
def _show_xsl_dependency_dialog(self):
"""Öffnet den XSL-Abhängigkeitsgraph-Dialog."""
try: