Refactor: MainWindow in 7 Mixins aufgeteilt (80% Code-Reduktion)
MainWindow.py von 5025 auf 983 Zeilen reduziert durch Extraktion in: - TreeManagerMixin: Baumstruktur-Verwaltung (~1136 Zeilen) - PdfViewerMixin: PDF-Anzeige und Rendering - WorkerPoolMixin: Saxon/FOP Worker-Pool-Verwaltung - DatabaseMixin: PostgreSQL-Operationen - DragDropMixin: Drag-and-Drop für XML-Dateien - HashCalculationMixin: blake2b Hash-Berechnung - TransformationMixin: XSL-Transformationen Zusätzlich Thread-Klassen in threads.py ausgelagert. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,547 @@
|
||||
"""
|
||||
PdfViewerMixin - Mixin für PDF-Viewer-Operationen.
|
||||
|
||||
Dieses Mixin enthält alle Methoden für die PDF-Anzeige und -Vergleich im MainWindow:
|
||||
- PDF-Rendering und -Anzeige
|
||||
- Alpha-Blending und Zoom
|
||||
- Thumbnail-Navigation
|
||||
- Drag-to-Scroll
|
||||
- PDF-Dokument-Management
|
||||
"""
|
||||
|
||||
import gc
|
||||
import logging
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtCore import Qt, QSize, QUrl
|
||||
from PySide6.QtGui import QCursor, QPixmap, QPainter, QDesktopServices
|
||||
from PySide6.QtWidgets import QLabel, QMessageBox
|
||||
from PySide6.QtPdf import QPdfDocument
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PdfViewerMixin:
|
||||
"""
|
||||
Mixin-Klasse für PDF-Viewer-Operationen.
|
||||
|
||||
Dieses Mixin erwartet, dass die verwendende Klasse folgende Attribute hat:
|
||||
- self.ui: UI-Objekt mit Layouts, Slidern, etc.
|
||||
- self.project: Aktuelles Projekt
|
||||
- self.pdf_documents: Dict für PDF-Dokumente
|
||||
- self.current_rendered_pixmaps: Cache für gerenderte Pixmaps
|
||||
- self.fullsize_label: QLabel für Vollbild-Anzeige
|
||||
- self.thumbnail_to_page: Dict für Thumbnail-zu-Seite-Mapping
|
||||
- self.current_zoom: Aktueller Zoom-Faktor
|
||||
- self.current_page: Aktuelle Seitennummer
|
||||
- self.current_pdf: Aktueller PDF-Dateiname
|
||||
- self.is_dragging: Drag-Status
|
||||
- self.last_drag_position: Letzte Drag-Position
|
||||
- self.drag_threshold: Mindestbewegung für Drag
|
||||
- self.scroll_sensitivity: Scroll-Empfindlichkeit
|
||||
"""
|
||||
|
||||
def render_and_display_page(self, pdf_filename, page_num):
|
||||
"""
|
||||
Rendert und zeigt eine spezifische Seite in der Vollansicht an.
|
||||
Cached die gerenderten Pixmaps für bessere Performance.
|
||||
|
||||
Args:
|
||||
pdf_filename: Name der PDF-Datei
|
||||
page_num: Seitennummer (0-basiert)
|
||||
"""
|
||||
logger.debug(f"Rendere Seite {page_num + 1} von {pdf_filename}")
|
||||
|
||||
if pdf_filename not in self.pdf_documents:
|
||||
logger.warning(f"PDF-Dokument {pdf_filename} nicht gefunden")
|
||||
return
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
docs = self.pdf_documents[pdf_filename]
|
||||
|
||||
# Diff-Seite laden (bestimmt die Abmessungen)
|
||||
diff_doc = docs["diff"]
|
||||
page_size = diff_doc.pagePointSize(page_num)
|
||||
|
||||
# Hohe Auflösung für Vollbild (entspricht ca. Matrix(2.0, 2.0) in PyMuPDF)
|
||||
scale_factor = 2.0
|
||||
render_size = QSize(int(page_size.width() * scale_factor), int(page_size.height() * scale_factor))
|
||||
|
||||
# Diff-Seite rendern (immer vorhanden)
|
||||
diff_image = diff_doc.render(page_num, render_size)
|
||||
diff_pixmap = QPixmap.fromImage(diff_image)
|
||||
|
||||
# Ermittle die Abmessungen für weiße Seiten
|
||||
diff_width = diff_pixmap.width()
|
||||
diff_height = diff_pixmap.height()
|
||||
|
||||
# Ref-Seite prüfen und rendern oder weiße Seite erstellen
|
||||
ref_doc = docs["ref"]
|
||||
if page_num < ref_doc.pageCount():
|
||||
ref_image = ref_doc.render(page_num, render_size)
|
||||
ref_pixmap = QPixmap.fromImage(ref_image)
|
||||
logger.debug(f"Ref-Seite {page_num + 1} gerendert")
|
||||
else:
|
||||
# Erstelle weiße Seite mit gleichen Abmessungen wie Diff-Seite
|
||||
ref_pixmap = QPixmap(diff_width, diff_height)
|
||||
ref_pixmap.fill(Qt.GlobalColor.white)
|
||||
logger.debug(f"Weiße Ref-Seite {page_num + 1} erstellt")
|
||||
|
||||
# New-Seite prüfen und rendern oder weiße Seite erstellen
|
||||
new_doc = docs["new"]
|
||||
if page_num < new_doc.pageCount():
|
||||
new_image = new_doc.render(page_num, render_size)
|
||||
new_pixmap = QPixmap.fromImage(new_image)
|
||||
logger.debug(f"New-Seite {page_num + 1} gerendert")
|
||||
else:
|
||||
# Erstelle weiße Seite mit gleichen Abmessungen wie Diff-Seite
|
||||
new_pixmap = QPixmap(diff_width, diff_height)
|
||||
new_pixmap.fill(Qt.GlobalColor.white)
|
||||
logger.debug(f"Weiße New-Seite {page_num + 1} erstellt")
|
||||
|
||||
# Cache die gerenderten Pixmaps für schnelle Alpha/Zoom-Operationen
|
||||
self.current_rendered_pixmaps = {"ref": ref_pixmap, "diff": diff_pixmap, "new": new_pixmap}
|
||||
|
||||
# Aktualisiere aktuelle Seite
|
||||
self.current_page = page_num
|
||||
self.current_pdf = pdf_filename
|
||||
|
||||
# Zeige das Bild mit aktuellem Alpha- und Zoom-Wert an
|
||||
self.update_current_display()
|
||||
|
||||
render_time = time.time() - start_time
|
||||
logger.debug(f"Performance: Seite {page_num + 1} gerendert in {render_time:.3f}s")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Rendern der Seite {page_num + 1}: {e}", exc_info=True)
|
||||
|
||||
def update_current_display(self):
|
||||
"""
|
||||
Aktualisiert die Anzeige der aktuellen Seite basierend auf gecachten Pixmaps.
|
||||
Verwendet für Alpha- und Zoom-Änderungen ohne erneutes PDF-Rendering.
|
||||
"""
|
||||
if not self.current_rendered_pixmaps:
|
||||
logger.warning("Keine gerenderten Pixmaps verfügbar")
|
||||
return
|
||||
|
||||
if self.fullsize_label is None:
|
||||
logger.warning("Fullsize-Label ist nicht verfügbar")
|
||||
return
|
||||
|
||||
try:
|
||||
# Hole die gecachten Pixmaps
|
||||
ref_pixmap = self.current_rendered_pixmaps["ref"]
|
||||
diff_pixmap = self.current_rendered_pixmaps["diff"]
|
||||
new_pixmap = self.current_rendered_pixmaps["new"]
|
||||
|
||||
# Erstelle das überlagerte Bild mit aktuellem Alpha-Wert
|
||||
alpha_value = self.ui.alpha.value()
|
||||
layered_pixmap = self.create_layered_pixmap(ref_pixmap, diff_pixmap, new_pixmap, alpha_value)
|
||||
|
||||
# Wende aktuellen Zoom an
|
||||
zoom_factor = self.current_zoom / 100.0
|
||||
if zoom_factor != 1.0:
|
||||
new_width = int(layered_pixmap.width() * zoom_factor)
|
||||
layered_pixmap = layered_pixmap.scaledToWidth(new_width, Qt.TransformationMode.SmoothTransformation)
|
||||
|
||||
# Setze das überlagerte Bild
|
||||
self.fullsize_label.setPixmap(layered_pixmap)
|
||||
self.fullsize_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
except RuntimeError as e:
|
||||
# C++-Objekt wurde bereits gelöscht
|
||||
logger.warning(f"Fullsize-Label wurde bereits gelöscht: {e}")
|
||||
self.fullsize_label = None
|
||||
|
||||
def create_layered_pixmap(self, ref_pixmap, diff_pixmap, new_pixmap, alpha_value):
|
||||
"""
|
||||
Erstellt ein übergelagertes Pixmap basierend auf dem Alpha-Wert.
|
||||
|
||||
Args:
|
||||
ref_pixmap: Unterste Ebene (ref)
|
||||
diff_pixmap: Mittlere Ebene (diff)
|
||||
new_pixmap: Oberste Ebene (new)
|
||||
alpha_value: Alpha-Wert (-100 bis 100)
|
||||
|
||||
Returns:
|
||||
QPixmap: Das überlagerte Bild
|
||||
"""
|
||||
# Verwende die Größe des größten Bildes
|
||||
max_width = max(ref_pixmap.width(), diff_pixmap.width(), new_pixmap.width())
|
||||
max_height = max(ref_pixmap.height(), diff_pixmap.height(), new_pixmap.height())
|
||||
|
||||
# Erstelle ein leeres Pixmap für das Ergebnis
|
||||
result = QPixmap(max_width, max_height)
|
||||
result.fill(Qt.GlobalColor.white)
|
||||
|
||||
painter = QPainter(result)
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
|
||||
if alpha_value <= 0:
|
||||
# Alpha von -100 bis 0: Übergang von ref zu diff
|
||||
ref_opacity = abs(alpha_value) / 100
|
||||
diff_opacity = 1.0 - abs(alpha_value) / 100.0
|
||||
new_opacity = 0.0
|
||||
else:
|
||||
ref_opacity = 0.0
|
||||
diff_opacity = 1.0 - alpha_value / 100.0
|
||||
new_opacity = alpha_value / 100.0
|
||||
|
||||
# Zeichne die Ebenen mit entsprechender Transparenz
|
||||
if ref_opacity > 0:
|
||||
painter.setOpacity(ref_opacity)
|
||||
painter.drawPixmap(0, 0, ref_pixmap)
|
||||
|
||||
if diff_opacity > 0:
|
||||
painter.setOpacity(diff_opacity)
|
||||
painter.drawPixmap(0, 0, diff_pixmap)
|
||||
|
||||
if new_opacity > 0:
|
||||
painter.setOpacity(new_opacity)
|
||||
painter.drawPixmap(0, 0, new_pixmap)
|
||||
|
||||
painter.end()
|
||||
return result
|
||||
|
||||
def _clear_layout(self, layout):
|
||||
"""Entfernt alle Widgets aus einem Layout."""
|
||||
if layout is not None:
|
||||
while layout.count():
|
||||
item = layout.takeAt(0)
|
||||
widget = item.widget()
|
||||
if widget is not None:
|
||||
widget.deleteLater()
|
||||
|
||||
def on_alpha_changed(self, alpha_value):
|
||||
"""
|
||||
Wird ausgeführt, wenn der Alpha-Slider geändert wird.
|
||||
Optimiert: Verwendet gecachte Pixmaps statt erneutes PDF-Rendering.
|
||||
|
||||
Args:
|
||||
alpha_value: Der neue Alpha-Wert (-100 bis 100)
|
||||
"""
|
||||
logger.debug(f"Alpha geändert auf {alpha_value}")
|
||||
|
||||
start_time = time.time()
|
||||
# Verwende gecachte Pixmaps für schnelle Alpha-Änderungen
|
||||
self.update_current_display()
|
||||
alpha_time = time.time() - start_time
|
||||
logger.debug(f"Alpha-Update in {alpha_time:.6f}s")
|
||||
|
||||
def on_thumbnail_clicked(self, event, thumbnail):
|
||||
"""
|
||||
Wird ausgeführt, wenn ein Thumbnail angeklickt wird.
|
||||
|
||||
Args:
|
||||
event: Das Maus-Event
|
||||
thumbnail: Das geklickte Thumbnail-Label
|
||||
"""
|
||||
page_info = self.thumbnail_to_page.get(thumbnail)
|
||||
if page_info:
|
||||
pdf_filename = page_info["pdf_filename"]
|
||||
page_num = page_info["page_num"]
|
||||
|
||||
logger.debug(f"Thumbnail für Seite {page_num + 1} von {pdf_filename} wurde angeklickt")
|
||||
|
||||
# Rendere und zeige die gewählte Seite an
|
||||
self.render_and_display_page(pdf_filename, page_num)
|
||||
|
||||
def apply_zoom(self, zoom_value):
|
||||
"""
|
||||
Wendet den Zoom-Faktor auf das aktuelle Bild an.
|
||||
Optimiert: Verwendet gecachte Pixmaps statt erneutes PDF-Rendering.
|
||||
|
||||
Args:
|
||||
zoom_value: Der neue Zoom-Wert (in Prozent)
|
||||
"""
|
||||
self.current_zoom = zoom_value
|
||||
logger.debug(f"Zoom geändert auf {zoom_value}%")
|
||||
|
||||
# Verwende gecachte Pixmaps für schnelle Zoom-Änderungen
|
||||
self.update_current_display()
|
||||
|
||||
def on_fullsize_mouse_press(self, event, fullsize_label):
|
||||
"""Wird ausgeführt, wenn die Maustaste auf einem großen Bild gedrückt wird."""
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
self.is_dragging = True
|
||||
self.last_drag_position = event.globalPosition().toPoint()
|
||||
fullsize_label.setCursor(QCursor(Qt.CursorShape.ClosedHandCursor))
|
||||
|
||||
def on_fullsize_mouse_move(self, event, fullsize_label):
|
||||
"""Wird ausgeführt, wenn die Maus über einem großen Bild bewegt wird."""
|
||||
if self.is_dragging and self.last_drag_position is not None:
|
||||
current_pos = event.globalPosition().toPoint()
|
||||
delta = current_pos - self.last_drag_position
|
||||
|
||||
if abs(delta.x()) >= self.drag_threshold or abs(delta.y()) >= self.drag_threshold:
|
||||
v_scrollbar = self.ui.scrollArea_2.verticalScrollBar()
|
||||
h_scrollbar = self.ui.scrollArea_2.horizontalScrollBar()
|
||||
|
||||
scroll_delta_y = int(-delta.y() * self.scroll_sensitivity)
|
||||
scroll_delta_x = int(-delta.x() * self.scroll_sensitivity)
|
||||
|
||||
new_v_value = v_scrollbar.value() + scroll_delta_y
|
||||
new_h_value = h_scrollbar.value() + scroll_delta_x
|
||||
|
||||
v_scrollbar.setValue(new_v_value)
|
||||
h_scrollbar.setValue(new_h_value)
|
||||
|
||||
self.last_drag_position = current_pos
|
||||
|
||||
def on_fullsize_mouse_release(self, event, fullsize_label):
|
||||
"""Wird ausgeführt, wenn die Maustaste auf einem großen Bild losgelassen wird."""
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
self.is_dragging = False
|
||||
self.last_drag_position = None
|
||||
fullsize_label.setCursor(QCursor(Qt.CursorShape.OpenHandCursor))
|
||||
|
||||
def _load_pdf_for_comparison(self, xml_file_path: Path, xsl_id_str: str):
|
||||
"""
|
||||
Lädt die PDFs (diff, ref, new) einer Transformation in den Vergleichs-Viewer.
|
||||
|
||||
Args:
|
||||
xml_file_path: Pfad zur XML-Datei (relativ)
|
||||
xsl_id_str: XSL-ID als String (z.B. "2002_1_128")
|
||||
"""
|
||||
try:
|
||||
if not self.project:
|
||||
QMessageBox.warning(self, "Fehler", "Kein Projekt geöffnet")
|
||||
return
|
||||
|
||||
# Ermittle PDF-Dateinamen basierend auf XML und XSL-ID
|
||||
xml_stem = xml_file_path.stem
|
||||
pdf_basename = f"{xml_stem}_xsl_{xsl_id_str}.pdf"
|
||||
|
||||
# Pfade zu den drei PDFs
|
||||
diff_dir = self.project.project_dir / "diff"
|
||||
ref_dir = self.project.project_dir / "ref"
|
||||
new_dir = self.project.project_dir / "new"
|
||||
|
||||
diff_pdf_path = diff_dir / pdf_basename
|
||||
ref_pdf_path = ref_dir / pdf_basename
|
||||
new_pdf_path = new_dir / pdf_basename
|
||||
|
||||
# Prüfe ob PDFs existieren
|
||||
if not diff_pdf_path.exists():
|
||||
QMessageBox.information(self, "Keine Diff-PDF", f"Diff-PDF nicht gefunden:\n{pdf_basename}")
|
||||
return
|
||||
|
||||
if not ref_pdf_path.exists() or not new_pdf_path.exists():
|
||||
QMessageBox.warning(
|
||||
self,
|
||||
"Fehlende PDFs",
|
||||
f"Ref-PDF oder New-PDF nicht gefunden:\n{pdf_basename}\n\nNur Diff-PDF vorhanden.",
|
||||
)
|
||||
return
|
||||
|
||||
logger.info(f"Lade PDFs für Vergleich: {pdf_basename}")
|
||||
|
||||
# Entferne bestehende Widgets aus den Layouts
|
||||
self._clear_layout(self.ui.verticalLayout_2)
|
||||
self._clear_layout(self.ui.verticalLayout_3)
|
||||
|
||||
# Dicts zurücksetzen
|
||||
self.thumbnail_to_page = {}
|
||||
self.pdf_documents = {}
|
||||
self.current_rendered_pixmaps = None
|
||||
self.fullsize_label = None # Label wurde durch _clear_layout gelöscht
|
||||
|
||||
# Alle drei PDF-Dateien öffnen mit QtPdf
|
||||
diff_doc = QPdfDocument()
|
||||
ref_doc = QPdfDocument()
|
||||
new_doc = QPdfDocument()
|
||||
|
||||
# PDF-Dateien laden
|
||||
diff_doc.load(str(diff_pdf_path))
|
||||
ref_doc.load(str(ref_pdf_path))
|
||||
new_doc.load(str(new_pdf_path))
|
||||
|
||||
# Warten bis PDFs geladen sind
|
||||
if (
|
||||
diff_doc.status() != QPdfDocument.Status.Ready
|
||||
or ref_doc.status() != QPdfDocument.Status.Ready
|
||||
or new_doc.status() != QPdfDocument.Status.Ready
|
||||
):
|
||||
QMessageBox.critical(self, "Fehler", f"Fehler beim Laden der PDFs:\n{pdf_basename}")
|
||||
return
|
||||
|
||||
# PDF-Dokumente speichern
|
||||
self.pdf_documents[pdf_basename] = {"diff": diff_doc, "ref": ref_doc, "new": new_doc}
|
||||
|
||||
# PDF-Pfade für System-Viewer speichern
|
||||
self.current_ref_pdf_path = ref_pdf_path
|
||||
self.current_new_pdf_path = new_pdf_path
|
||||
|
||||
# Buttons zum Öffnen der PDFs im System-Viewer aktivieren
|
||||
self.ui.view_ref_pdf.setEnabled(True)
|
||||
self.ui.view_new_pdf.setEnabled(True)
|
||||
|
||||
# Slider aktivieren
|
||||
self.ui.alpha.setEnabled(True)
|
||||
self.ui.zoom.setEnabled(True)
|
||||
|
||||
logger.info(f"PDFs geladen: {pdf_basename}")
|
||||
logger.info(f" diff: {diff_doc.pageCount()} Seiten")
|
||||
logger.info(f" ref: {ref_doc.pageCount()} Seiten")
|
||||
logger.info(f" new: {new_doc.pageCount()} Seiten")
|
||||
|
||||
# Nehme die Seitenzahl der diff-PDF als Basis
|
||||
max_pages = diff_doc.pageCount()
|
||||
|
||||
# Erstelle Thumbnails für alle Seiten
|
||||
for page_num in range(max_pages):
|
||||
# Nur diff-Seite für Thumbnail rendern
|
||||
page_size = diff_doc.pagePointSize(page_num)
|
||||
|
||||
# Skalierung für Thumbnail
|
||||
scale_factor = 200.0 / page_size.width() # 200 Pixel Breite
|
||||
|
||||
# Seite rendern
|
||||
page_image = diff_doc.render(
|
||||
page_num,
|
||||
QSize(int(page_size.width() * scale_factor), int(page_size.height() * scale_factor)),
|
||||
)
|
||||
|
||||
diff_pixmap = QPixmap.fromImage(page_image)
|
||||
|
||||
# Thumbnail erstellen und zur linken Spalte hinzufügen
|
||||
thumbnail = QLabel()
|
||||
thumbnail.setObjectName(f"thumbnail_{pdf_basename}_page_{page_num + 1}")
|
||||
thumbnail.setPixmap(diff_pixmap.scaledToWidth(200, Qt.TransformationMode.SmoothTransformation))
|
||||
thumbnail.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
|
||||
thumbnail.setMouseTracking(True)
|
||||
self.ui.verticalLayout_2.addWidget(thumbnail)
|
||||
|
||||
# Seitennummer für Thumbnail anzeigen
|
||||
thumbnail_info = QLabel(f"Seite {page_num + 1}")
|
||||
thumbnail_info.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.ui.verticalLayout_2.addWidget(thumbnail_info)
|
||||
|
||||
# Beziehung zwischen Thumbnail und Seitennummer speichern
|
||||
self.thumbnail_to_page[thumbnail] = {"pdf_filename": pdf_basename, "page_num": page_num}
|
||||
|
||||
# Click-Event für das Thumbnail einrichten
|
||||
thumbnail.mousePressEvent = lambda event, t=thumbnail: self.on_thumbnail_clicked(event, t)
|
||||
|
||||
# Erstelle das Vollbild-Label für die rechte Spalte (falls noch nicht vorhanden)
|
||||
if self.fullsize_label is None:
|
||||
self.fullsize_label = QLabel()
|
||||
self.fullsize_label.setObjectName("fullsize_current_page")
|
||||
self.fullsize_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.fullsize_label.setCursor(QCursor(Qt.CursorShape.OpenHandCursor))
|
||||
self.ui.verticalLayout_3.addWidget(self.fullsize_label)
|
||||
|
||||
# Drag-to-Scroll Events für das große Bild einrichten
|
||||
self.fullsize_label.mousePressEvent = lambda event: self.on_fullsize_mouse_press(
|
||||
event, self.fullsize_label
|
||||
)
|
||||
self.fullsize_label.mouseMoveEvent = lambda event: self.on_fullsize_mouse_move(
|
||||
event, self.fullsize_label
|
||||
)
|
||||
self.fullsize_label.mouseReleaseEvent = lambda event: self.on_fullsize_mouse_release(
|
||||
event, self.fullsize_label
|
||||
)
|
||||
|
||||
# Setze die aktuelle PDF
|
||||
self.current_pdf = pdf_basename
|
||||
|
||||
# Speichere Diff-PDF-Informationen für Accept Changes
|
||||
self.current_diff_xml_path = xml_file_path
|
||||
self.current_diff_xsl_id = xsl_id_str
|
||||
|
||||
# Aktiviere Accept-Changes-Button
|
||||
self.ui.accept_changes.setEnabled(True)
|
||||
|
||||
# Zeige die erste Seite initial an
|
||||
self.render_and_display_page(pdf_basename, 0)
|
||||
|
||||
logger.info(f"PDF-Vergleich geladen: {pdf_basename}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Laden der PDFs für Vergleich: {e}")
|
||||
QMessageBox.critical(self, "Fehler", f"Konnte PDFs nicht laden:\n{str(e)}")
|
||||
|
||||
def _close_all_pdf_documents(self):
|
||||
"""Schließt alle geöffneten PDF-Dokumente explizit (wichtig für Windows)."""
|
||||
if self.pdf_documents:
|
||||
for pdf_basename, docs in self.pdf_documents.items():
|
||||
for doc_type, doc in docs.items():
|
||||
if doc:
|
||||
doc.close()
|
||||
logger.debug(f"PDF-Dokument geschlossen: {pdf_basename} ({doc_type})")
|
||||
|
||||
# Lösche alle Referenzen
|
||||
self.pdf_documents.clear()
|
||||
|
||||
# Lösche gerenderte Pixmaps
|
||||
self.current_rendered_pixmaps = None
|
||||
|
||||
# Erzwinge Garbage Collection um Dateihandles freizugeben (wichtig für Windows)
|
||||
gc.collect()
|
||||
|
||||
logger.info("Alle PDF-Dokumente geschlossen und Referenzen freigegeben")
|
||||
|
||||
def _clear_pdf_viewer(self):
|
||||
"""Leert den PDF-Viewer und alle Thumbnails."""
|
||||
# Schließe alle PDF-Dokumente explizit (wichtig für Windows)
|
||||
self._close_all_pdf_documents()
|
||||
|
||||
# Entferne Widgets aus Layouts
|
||||
self._clear_layout(self.ui.verticalLayout_2)
|
||||
self._clear_layout(self.ui.verticalLayout_3)
|
||||
|
||||
# Zurücksetzen der Datenstrukturen
|
||||
self.thumbnail_to_page = {}
|
||||
self.pdf_documents = {}
|
||||
self.current_rendered_pixmaps = None
|
||||
self.fullsize_label = None
|
||||
self.current_pdf = None
|
||||
self.current_diff_xml_path = None
|
||||
self.current_diff_xsl_id = None
|
||||
|
||||
# PDF-Pfade zurücksetzen und Buttons deaktivieren
|
||||
self.current_ref_pdf_path = None
|
||||
self.current_new_pdf_path = None
|
||||
self.ui.view_ref_pdf.setEnabled(False)
|
||||
self.ui.view_new_pdf.setEnabled(False)
|
||||
|
||||
# Slider deaktivieren
|
||||
self.ui.alpha.setEnabled(False)
|
||||
self.ui.zoom.setEnabled(False)
|
||||
|
||||
logger.info("PDF-Viewer geleert")
|
||||
|
||||
def _on_view_ref_pdf_clicked(self):
|
||||
"""
|
||||
Handler für view_ref_pdf Button.
|
||||
Öffnet die Referenz-PDF im systemseitig installierten PDF-Viewer.
|
||||
"""
|
||||
if not self.current_ref_pdf_path or not self.current_ref_pdf_path.exists():
|
||||
QMessageBox.warning(self, "Fehler", "Referenz-PDF nicht gefunden")
|
||||
logger.warning("Referenz-PDF nicht verfügbar")
|
||||
return
|
||||
|
||||
logger.info(f"Öffne Referenz-PDF im System-Viewer: {self.current_ref_pdf_path}")
|
||||
url = QUrl.fromLocalFile(str(self.current_ref_pdf_path))
|
||||
if not QDesktopServices.openUrl(url):
|
||||
QMessageBox.critical(self, "Fehler", f"Konnte Referenz-PDF nicht öffnen:\n{self.current_ref_pdf_path}")
|
||||
logger.error(f"Fehler beim Öffnen der Referenz-PDF: {self.current_ref_pdf_path}")
|
||||
|
||||
def _on_view_new_pdf_clicked(self):
|
||||
"""
|
||||
Handler für view_new_pdf Button.
|
||||
Öffnet die neue PDF im systemseitig installierten PDF-Viewer.
|
||||
"""
|
||||
if not self.current_new_pdf_path or not self.current_new_pdf_path.exists():
|
||||
QMessageBox.warning(self, "Fehler", "Neue PDF nicht gefunden")
|
||||
logger.warning("Neue PDF nicht verfügbar")
|
||||
return
|
||||
|
||||
logger.info(f"Öffne neue PDF im System-Viewer: {self.current_new_pdf_path}")
|
||||
url = QUrl.fromLocalFile(str(self.current_new_pdf_path))
|
||||
if not QDesktopServices.openUrl(url):
|
||||
QMessageBox.critical(self, "Fehler", f"Konnte neue PDF nicht öffnen:\n{self.current_new_pdf_path}")
|
||||
logger.error(f"Fehler beim Öffnen der neuen PDF: {self.current_new_pdf_path}")
|
||||
Reference in New Issue
Block a user