Feature: Aktions-Menü mit Batch-Transformationen und UI-Bereinigung

- Neues Aktions-Menü mit Transformations- und Datenbankfunktionen
- Menü wird beim Projekt-Laden automatisch aktiviert
- Neue Aktion: Alle XML-Dateien transformieren (inkrementell)
- Neue Aktion: Alle XML-Dateien neu transformieren (force)
- Neue Aktion: Aus Datenbank laden (ersetzt Button)
- Entfernte obsolete Buttons (pushButton, pushButton_2, pB_lade_aus_fn2)
- UI-Bereinigung: Button-Frame unterhalb TreeWidget entfernt
- Batch-Transformationen sammeln rekursiv alle XML/XSL-Paare
- Bestätigungsdialoge mit Job-Anzahl und Warnungen
- Deutsche Log-Meldungen und Fehlertexte
This commit is contained in:
2026-01-25 15:23:32 +01:00
parent 2858d46ef1
commit 3bdc0a0daa
4 changed files with 206 additions and 126 deletions
+44 -75
View File
@@ -95,76 +95,6 @@
</column>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="pushButton">
<property name="layoutDirection">
<enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>nur geänderte generieren</string>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::MediaPlaybackStart"/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string>Alle generieren</string>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::MediaSeekForward"/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pB_lade_aus_fn2">
<property name="text">
<string>lade aus FN2</string>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::GoDown"/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QScrollArea" name="scrollArea">
@@ -188,8 +118,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>68</width>
<height>728</height>
<width>54</width>
<height>716</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@@ -416,8 +346,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>726</width>
<height>697</height>
<width>881</width>
<height>684</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
@@ -449,7 +379,7 @@
<x>0</x>
<y>0</y>
<width>1263</width>
<height>22</height>
<height>33</height>
</rect>
</property>
<widget class="QMenu" name="menuProjekt">
@@ -469,7 +399,20 @@
<string>Thema</string>
</property>
</widget>
<widget class="QMenu" name="menuAktion">
<property name="enabled">
<bool>false</bool>
</property>
<property name="title">
<string>Aktion</string>
</property>
<addaction name="actionAlle_XML_Dateien_transformieren"/>
<addaction name="actionAlle_XML_Dateien_neu_transformieren_force"/>
<addaction name="separator"/>
<addaction name="actionAus_Datenbank_laden"/>
</widget>
<addaction name="menuProjekt"/>
<addaction name="menuAktion"/>
<addaction name="menuThema"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
@@ -507,10 +450,36 @@
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="folder-open"/>
</property>
<property name="text">
<string>Vorhandene Projekte</string>
</property>
</action>
<action name="actionAlle_XML_Dateien_transformieren">
<property name="text">
<string>Alle XML-Dateien transformieren</string>
</property>
</action>
<action name="actionAlle_XML_Dateien_neu_transformieren_force">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::ViewRefresh"/>
</property>
<property name="text">
<string>Alle XML-Dateien neu transformieren (force)</string>
</property>
</action>
<action name="actionFN2">
<property name="text">
<string>FN2</string>
</property>
</action>
<action name="actionAus_Datenbank_laden">
<property name="text">
<string>Aus Datenbank laden</string>
</property>
</action>
</widget>
<resources/>
<connections>
+29 -43
View File
@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'MainWinddow.ui'
##
## Created by: Qt User Interface Compiler version 6.9.2
## Created by: Qt User Interface Compiler version 6.10.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@@ -42,6 +42,18 @@ class Ui_MainWindow(object):
self.actionVorhandene_Projekte = QAction(MainWindow)
self.actionVorhandene_Projekte.setObjectName(u"actionVorhandene_Projekte")
self.actionVorhandene_Projekte.setEnabled(False)
icon3 = QIcon(QIcon.fromTheme(u"folder-open"))
self.actionVorhandene_Projekte.setIcon(icon3)
self.actionAlle_XML_Dateien_transformieren = QAction(MainWindow)
self.actionAlle_XML_Dateien_transformieren.setObjectName(u"actionAlle_XML_Dateien_transformieren")
self.actionAlle_XML_Dateien_neu_transformieren_force = QAction(MainWindow)
self.actionAlle_XML_Dateien_neu_transformieren_force.setObjectName(u"actionAlle_XML_Dateien_neu_transformieren_force")
icon4 = QIcon(QIcon.fromTheme(QIcon.ThemeIcon.ViewRefresh))
self.actionAlle_XML_Dateien_neu_transformieren_force.setIcon(icon4)
self.actionFN2 = QAction(MainWindow)
self.actionFN2.setObjectName(u"actionFN2")
self.actionAus_Datenbank_laden = QAction(MainWindow)
self.actionAus_Datenbank_laden.setObjectName(u"actionAus_Datenbank_laden")
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.horizontalLayout = QHBoxLayout(self.centralwidget)
@@ -83,42 +95,6 @@ class Ui_MainWindow(object):
self.verticalLayout.addWidget(self.treeWidget)
self.frame_2 = QFrame(self.frame)
self.frame_2.setObjectName(u"frame_2")
self.frame_2.setFrameShadow(QFrame.Shadow.Raised)
self.horizontalLayout_2 = QHBoxLayout(self.frame_2)
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.pushButton = QPushButton(self.frame_2)
self.pushButton.setObjectName(u"pushButton")
self.pushButton.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
icon3 = QIcon(QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackStart))
self.pushButton.setIcon(icon3)
self.horizontalLayout_2.addWidget(self.pushButton)
self.pushButton_2 = QPushButton(self.frame_2)
self.pushButton_2.setObjectName(u"pushButton_2")
self.pushButton_2.setAutoFillBackground(False)
icon4 = QIcon(QIcon.fromTheme(QIcon.ThemeIcon.MediaSeekForward))
self.pushButton_2.setIcon(icon4)
self.horizontalLayout_2.addWidget(self.pushButton_2)
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout_2.addItem(self.horizontalSpacer)
self.pB_lade_aus_fn2 = QPushButton(self.frame_2)
self.pB_lade_aus_fn2.setObjectName(u"pB_lade_aus_fn2")
icon5 = QIcon(QIcon.fromTheme(QIcon.ThemeIcon.GoDown))
self.pB_lade_aus_fn2.setIcon(icon5)
self.horizontalLayout_2.addWidget(self.pB_lade_aus_fn2)
self.verticalLayout.addWidget(self.frame_2)
self.splitter.addWidget(self.frame)
self.scrollArea = QScrollArea(self.splitter)
self.scrollArea.setObjectName(u"scrollArea")
@@ -132,7 +108,7 @@ class Ui_MainWindow(object):
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents")
self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 68, 728))
self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 54, 716))
self.verticalLayout_2 = QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.label = QLabel(self.scrollAreaWidgetContents)
@@ -234,7 +210,7 @@ class Ui_MainWindow(object):
self.scrollArea_2.setWidgetResizable(True)
self.scrollAreaWidgetContents_2 = QWidget()
self.scrollAreaWidgetContents_2.setObjectName(u"scrollAreaWidgetContents_2")
self.scrollAreaWidgetContents_2.setGeometry(QRect(0, 0, 726, 697))
self.scrollAreaWidgetContents_2.setGeometry(QRect(0, 0, 881, 684))
self.verticalLayout_3 = QVBoxLayout(self.scrollAreaWidgetContents_2)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
@@ -249,17 +225,21 @@ class Ui_MainWindow(object):
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 1263, 22))
self.menubar.setGeometry(QRect(0, 0, 1263, 33))
self.menuProjekt = QMenu(self.menubar)
self.menuProjekt.setObjectName(u"menuProjekt")
self.menuThema = QMenu(self.menubar)
self.menuThema.setObjectName(u"menuThema")
self.menuAktion = QMenu(self.menubar)
self.menuAktion.setObjectName(u"menuAktion")
self.menuAktion.setEnabled(False)
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menubar.addAction(self.menuProjekt.menuAction())
self.menubar.addAction(self.menuAktion.menuAction())
self.menubar.addAction(self.menuThema.menuAction())
self.menuProjekt.addAction(self.actionNeu)
self.menuProjekt.addSeparator()
@@ -268,6 +248,10 @@ class Ui_MainWindow(object):
self.menuProjekt.addAction(self.actionEinstellungen)
self.menuProjekt.addSeparator()
self.menuProjekt.addAction(self.actionBeenden)
self.menuAktion.addAction(self.actionAlle_XML_Dateien_transformieren)
self.menuAktion.addAction(self.actionAlle_XML_Dateien_neu_transformieren_force)
self.menuAktion.addSeparator()
self.menuAktion.addAction(self.actionAus_Datenbank_laden)
self.retranslateUi(MainWindow)
self.actionBeenden.triggered.connect(MainWindow.close)
@@ -287,9 +271,10 @@ class Ui_MainWindow(object):
self.actionEinstellungen.setShortcut(QCoreApplication.translate("MainWindow", u"Ctrl+S", None))
#endif // QT_CONFIG(shortcut)
self.actionVorhandene_Projekte.setText(QCoreApplication.translate("MainWindow", u"Vorhandene Projekte", None))
self.pushButton.setText(QCoreApplication.translate("MainWindow", u"nur ge\u00e4nderte generieren", None))
self.pushButton_2.setText(QCoreApplication.translate("MainWindow", u"Alle generieren", None))
self.pB_lade_aus_fn2.setText(QCoreApplication.translate("MainWindow", u"lade aus FN2", None))
self.actionAlle_XML_Dateien_transformieren.setText(QCoreApplication.translate("MainWindow", u"Alle XML-Dateien transformieren", None))
self.actionAlle_XML_Dateien_neu_transformieren_force.setText(QCoreApplication.translate("MainWindow", u"Alle XML-Dateien neu transformieren (force)", None))
self.actionFN2.setText(QCoreApplication.translate("MainWindow", u"FN2", None))
self.actionAus_Datenbank_laden.setText(QCoreApplication.translate("MainWindow", u"Aus Datenbank laden", None))
self.label.setText("")
self.label_2.setText("")
self.view_ref_pdf.setText(QCoreApplication.translate("MainWindow", u"Vorher (Referenz)", None))
@@ -304,5 +289,6 @@ class Ui_MainWindow(object):
self.accept_changes.setText(QCoreApplication.translate("MainWindow", u"\u2705 \u00c4nderungen \u00fcbernehmen", None))
self.menuProjekt.setTitle(QCoreApplication.translate("MainWindow", u"Projekt", None))
self.menuThema.setTitle(QCoreApplication.translate("MainWindow", u"Thema", None))
self.menuAktion.setTitle(QCoreApplication.translate("MainWindow", u"Aktion", None))
# retranslateUi
+8 -8
View File
@@ -284,6 +284,10 @@ class MainWindow(
# Worker-Pools werden jetzt erst beim Start der Transformation initialisiert (lazy loading)
# Aktiviere das Aktions-Menü, da ein Projekt geladen wurde
self.ui.menuAktion.setEnabled(True)
logger.info("Aktions-Menü aktiviert nach Projekt-Laden")
except Exception as e:
logger.error(f"Fehler beim Laden des Projekts '{project.name}': {e}")
# Fallback: Erstelle Standard-Einstellungen
@@ -326,8 +330,6 @@ class MainWindow(
def _connect_signals(self):
"""Verbindet Signale mit den entsprechenden Slots."""
# Button-Klicks verbinden
self.ui.pushButton.clicked.connect(self.on_button_clicked)
# Zoom-Slider verbinden
self.ui.zoom.valueChanged.connect(self.apply_zoom)
@@ -340,6 +342,8 @@ class MainWindow(
# Menü-Aktionen verbinden
self.ui.actionNeu.triggered.connect(self.open_new_project_dialog)
self.ui.actionEinstellungen.triggered.connect(self.open_settings_dialog)
self.ui.actionAlle_XML_Dateien_transformieren.triggered.connect(self._transform_all_xml_files)
self.ui.actionAlle_XML_Dateien_neu_transformieren_force.triggered.connect(self._transform_all_xml_files_force)
# Worker-Pool-Metriken Menüeintrag (programmatisch hinzufügen)
from PySide6.QtGui import QAction
@@ -360,8 +364,8 @@ class MainWindow(
else:
self.ui.menuProjekt.addAction(self.action_worker_metrics)
# Button "lade aus FN2" verbinden
self.ui.pB_lade_aus_fn2.clicked.connect(self.on_load_from_fn2_clicked)
# Menü-Aktion "Aus Datenbank laden" verbinden (macht das Gleiche wie Button)
self.ui.actionAus_Datenbank_laden.triggered.connect(self.on_load_from_fn2_clicked)
# Button "Accept Changes" verbinden
self.ui.accept_changes.clicked.connect(self._on_accept_changes_clicked)
@@ -462,10 +466,6 @@ class MainWindow(
logger.error(f"Fehler beim Erstellen der Projekt-Struktur: {e}")
raise
def on_button_clicked(self):
"""Wird ausgeführt, wenn der Button geklickt wird."""
logger.debug("Button wurde geklickt!")
def _update_diff_icons_for_existing_pdfs(self):
"""
Durchläuft alle XML-Items und setzt Icons für bereits existierende Diff-PDFs.
+125
View File
@@ -740,3 +740,128 @@ class TransformationMixin:
"Abgeschlossen mit Fehlern",
f"{successful_count} von {total_count} Transformationen erfolgreich\n{failed_count} fehlgeschlagen\n\nGesamtdauer: {duration_str}",
)
def _transform_all_xml_files(self):
"""
Transformiert ALLE XML-Dateien in allen TreeNodes des TreeWidgets.
Nur Dateien, die nicht up-to-date sind, werden transformiert.
"""
try:
if not self.project or not self.pdf_project:
QMessageBox.warning(self, "Fehler", "Kein Projekt geladen")
return
# Sammle alle XSL/XML-Paare aus allen Root-Nodes
all_jobs = []
root = self.ui.treeWidget.invisibleRootItem()
for i in range(root.childCount()):
root_item = root.child(i)
root_node = root_item.data(0, Qt.ItemDataRole.UserRole)
if isinstance(root_node, TreeNode):
# Sammle alle XSL/XML-Paare rekursiv
xsl_xml_pairs = self._collect_all_xsl_xml_pairs_recursive(root_node, root_item)
# Erstelle TransformationJobs
for xsl_file_obj, xml_file_obj, xsl_file_item in xsl_xml_pairs:
job = self._create_transformation_job(xsl_file_obj, xml_file_obj, xsl_file_item)
if job:
all_jobs.append(job)
elif isinstance(root_node, XslFile):
# Direkt XslFile als Root-Element
for xml_file_obj in root_node.xmls:
job = self._create_transformation_job(root_node, xml_file_obj, root_item)
if job:
all_jobs.append(job)
if not all_jobs:
QMessageBox.information(self, "Info", "Keine XML-Dateien zum Transformieren gefunden")
return
# Frage Benutzer um Bestätigung
reply = QMessageBox.question(
self,
"Alle XML-Dateien transformieren",
f"Möchten Sie wirklich alle {len(all_jobs)} XML-Dateien transformieren?\n\n"
f"Nur Dateien mit Änderungen werden verarbeitet.",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.Yes,
)
if reply != QMessageBox.StandardButton.Yes:
logger.info("Transformation abgebrochen durch Benutzer")
return
logger.info(f"Starte Transformation für alle {len(all_jobs)} XML-Dateien (nicht-force)")
# Starte Transformation in separatem Thread
self._start_transformation(all_jobs, force=False)
except Exception as e:
logger.error(f"Fehler beim Transformieren aller XML-Dateien: {e}")
QMessageBox.critical(self, "Fehler", f"Fehler beim Starten der Transformation: {str(e)}")
def _transform_all_xml_files_force(self):
"""
Transformiert ALLE XML-Dateien in allen TreeNodes des TreeWidgets (force).
Alle Dateien werden unabhängig vom Änderungsstatus neu transformiert.
"""
try:
if not self.project or not self.pdf_project:
QMessageBox.warning(self, "Fehler", "Kein Projekt geladen")
return
# Sammle alle XSL/XML-Paare aus allen Root-Nodes
all_jobs = []
root = self.ui.treeWidget.invisibleRootItem()
for i in range(root.childCount()):
root_item = root.child(i)
root_node = root_item.data(0, Qt.ItemDataRole.UserRole)
if isinstance(root_node, TreeNode):
# Sammle alle XSL/XML-Paare rekursiv
xsl_xml_pairs = self._collect_all_xsl_xml_pairs_recursive(root_node, root_item)
# Erstelle TransformationJobs
for xsl_file_obj, xml_file_obj, xsl_file_item in xsl_xml_pairs:
job = self._create_transformation_job(xsl_file_obj, xml_file_obj, xsl_file_item)
if job:
all_jobs.append(job)
elif isinstance(root_node, XslFile):
# Direkt XslFile als Root-Element
for xml_file_obj in root_node.xmls:
job = self._create_transformation_job(root_node, xml_file_obj, root_item)
if job:
all_jobs.append(job)
if not all_jobs:
QMessageBox.information(self, "Info", "Keine XML-Dateien zum Transformieren gefunden")
return
# Frage Benutzer um Bestätigung (mit Warnung wegen force)
reply = QMessageBox.question(
self,
"Alle XML-Dateien neu transformieren (force)",
f"Möchten Sie wirklich ALLE {len(all_jobs)} XML-Dateien NEU transformieren?\n\n"
f"⚠ WARNUNG: Im Force-Modus werden alle Dateien unabhängig von ihrem Status neu verarbeitet!\n"
f"Dies kann längere Zeit in Anspruch nehmen.",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
if reply != QMessageBox.StandardButton.Yes:
logger.info("Force-Transformation abgebrochen durch Benutzer")
return
logger.info(f"Starte Force-Transformation für alle {len(all_jobs)} XML-Dateien")
# Starte Transformation in separatem Thread (mit force=True)
self._start_transformation(all_jobs, force=True)
except Exception as e:
logger.error(f"Fehler beim Force-Transformieren aller XML-Dateien: {e}")
QMessageBox.critical(self, "Fehler", f"Fehler beim Starten der Force-Transformation: {str(e)}")