Nur eine Seite wird gerendert

This commit is contained in:
2025-05-29 16:30:01 +02:00
parent 7426d9c50d
commit 4955d62e99
+141 -110
View File
@@ -23,17 +23,22 @@ class MainWindow(QMainWindow):
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# Dict zum Speichern der Beziehung zwischen Thumbnails und großen Bildern
self.thumbnail_to_full_image = {}
# Dict zum Speichern der Beziehung zwischen Thumbnails und Seitennummern
self.thumbnail_to_page = {}
# Dicts zum Speichern der Original-Pixmaps für alle drei Ebenen
self.ref_pixmaps = {} # Unterste Ebene (ref)
self.diff_pixmaps = {} # Mittlere Ebene (diff)
self.new_pixmaps = {} # Oberste Ebene (new)
# PDF-Dokumente für späteres On-Demand-Rendering speichern
self.pdf_documents = {} # {pdf_filename: {'diff': doc, 'ref': doc, 'new': doc}}
# Aktueller Zoom-Faktor
self.current_zoom = 100 # 100%
# Aktuell angezeigte Seite
self.current_page = 0
self.current_pdf = None
# Label für die Vollansicht (nur ein einziges Label)
self.fullsize_label = None
# Variablen für Drag-to-Scroll (Anti-Jitter für 4K/DPI-Skalierung)
self.is_dragging = False
self.last_drag_position = None
@@ -101,16 +106,14 @@ class MainWindow(QMainWindow):
print(f"Fehler beim Wechseln des Themes: {e}")
def _load_images(self):
"""Lädt PDF-Seiten aus den drei Unterordnern ref, diff und new."""
"""Lädt PDF-Thumbnails und bereitet On-Demand-Rendering vor."""
# 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_full_image = {}
self.ref_pixmaps = {}
self.diff_pixmaps = {}
self.new_pixmaps = {}
self.thumbnail_to_page = {}
self.pdf_documents = {}
# Basis-Pfad zu den PDF-Ordnern
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -148,11 +151,18 @@ class MainWindow(QMainWindow):
continue
try:
# Alle drei PDF-Dateien öffnen
# Alle drei PDF-Dateien öffnen und speichern
diff_doc = pymupdf.open(diff_pdf_path)
ref_doc = pymupdf.open(ref_pdf_path)
new_doc = pymupdf.open(new_pdf_path)
# PDF-Dokumente für später speichern
self.pdf_documents[pdf_filename] = {
'diff': diff_doc,
'ref': ref_doc,
'new': new_doc
}
print(f"PDFs geladen: {pdf_filename}")
print(f" diff: {len(diff_doc)} Seiten")
print(f" ref: {len(ref_doc)} Seiten")
@@ -161,35 +171,17 @@ class MainWindow(QMainWindow):
# Nehme die minimale Seitenzahl aller drei PDFs
max_pages = min(len(diff_doc), len(ref_doc), len(new_doc))
# Für jede Seite
# Erstelle nur Thumbnails (keine Vollbilder)
for page_num in range(max_pages):
# Seiten aus allen drei PDFs laden
# Nur diff-Seite für Thumbnail rendern
diff_page = diff_doc[page_num]
ref_page = ref_doc[page_num]
new_page = new_doc[page_num]
# Seiten in hoher Auflösung rendern
matrix = pymupdf.Matrix(2.0, 2.0) # 2x Vergrößerung für bessere Qualität
# Diff-Seite für Thumbnail verwenden
matrix = pymupdf.Matrix(1.0, 1.0) # Normale Auflösung für Thumbnails
diff_pix = diff_page.get_pixmap(matrix=matrix)
diff_img_data = diff_pix.tobytes("png")
diff_qimg = QImage.fromData(diff_img_data)
diff_pixmap = QPixmap.fromImage(diff_qimg)
# Ref-Seite für unterste Ebene
ref_pix = ref_page.get_pixmap(matrix=matrix)
ref_img_data = ref_pix.tobytes("png")
ref_qimg = QImage.fromData(ref_img_data)
ref_pixmap = QPixmap.fromImage(ref_qimg)
# New-Seite für oberste Ebene
new_pix = new_page.get_pixmap(matrix=matrix)
new_img_data = new_pix.tobytes("png")
new_qimg = QImage.fromData(new_img_data)
new_pixmap = QPixmap.fromImage(new_qimg)
# Thumbnail erstellen (aus diff-PDF) und zur linken Spalte hinzufügen
# Thumbnail erstellen und zur linken Spalte hinzufügen
thumbnail = QLabel()
thumbnail.setObjectName(f"thumbnail_{pdf_filename}_page_{page_num + 1}")
thumbnail.setPixmap(diff_pixmap.scaledToWidth(200, Qt.TransformationMode.SmoothTransformation))
@@ -197,48 +189,107 @@ class MainWindow(QMainWindow):
thumbnail.setMouseTracking(True)
self.ui.verticalLayout_2.addWidget(thumbnail)
# Seitennumer für Thumbnail anzeigen
thumbnail_label = QLabel(f"Seite: {page_num + 1}")
thumbnail_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.ui.verticalLayout_2.addWidget(thumbnail_label)
# 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)
# Vollbild-Version erstellen (überlagerte Ebenen) und zur rechten Spalte hinzufügen
fullsize = QLabel()
fullsize.setObjectName(f"fullsize_{pdf_filename}_page_{page_num + 1}")
fullsize.setAlignment(Qt.AlignmentFlag.AlignHCenter) # Horizontale Zentrierung
self.ui.verticalLayout_3.addWidget(fullsize)
# Seitennumer für Vollansich anzeigen
fullsize_label = QLabel(f"Seite: {page_num + 1}")
fullsize_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.ui.verticalLayout_3.addWidget(fullsize_label)
# Beziehungen speichern
self.thumbnail_to_full_image[thumbnail] = fullsize
self.ref_pixmaps[fullsize] = ref_pixmap
self.diff_pixmaps[fullsize] = diff_pixmap
self.new_pixmaps[fullsize] = new_pixmap
# Beziehung zwischen Thumbnail und Seitennummer speichern
self.thumbnail_to_page[thumbnail] = {
'pdf_filename': pdf_filename,
'page_num': page_num
}
# Click-Event für das Thumbnail einrichten
thumbnail.mousePressEvent = lambda event, t=thumbnail: self.on_thumbnail_clicked(event, t)
# Drag-to-Scroll Events für große Bilder einrichten
fullsize.mousePressEvent = lambda event, f=fullsize: self.on_fullsize_mouse_press(event, f)
fullsize.mouseMoveEvent = lambda event, f=fullsize: self.on_fullsize_mouse_move(event, f)
fullsize.mouseReleaseEvent = lambda event, f=fullsize: self.on_fullsize_mouse_release(event, f)
print(f"Thumbnail für Seite {page_num + 1} erstellt")
print(f"Seite {page_num + 1} gerendert.")
# PDF-Dokumente schließen
diff_doc.close()
ref_doc.close()
new_doc.close()
# Setze die erste PDF als aktuelle PDF
if self.current_pdf is None:
self.current_pdf = pdf_filename
except Exception as e:
print(f"Fehler beim Laden der PDFs: {e}")
# Initiale Darstellung mit aktuellem Alpha-Wert
self.update_layered_images()
# Erstelle das eine Vollbild-Label für die rechte Spalte
self.fullsize_label = QLabel()
self.fullsize_label.setObjectName("fullsize_current_page")
self.fullsize_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
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)
# Zeige die erste Seite initial an
if self.current_pdf:
self.render_and_display_page(self.current_pdf, 0)
def render_and_display_page(self, pdf_filename, page_num):
"""
Rendert und zeigt eine spezifische Seite in der Vollansicht an.
Args:
pdf_filename: Name der PDF-Datei
page_num: Seitennummer (0-basiert)
"""
print(f"Rendere Seite {page_num + 1} von {pdf_filename}")
if pdf_filename not in self.pdf_documents:
print(f"PDF-Dokument {pdf_filename} nicht gefunden")
return
try:
docs = self.pdf_documents[pdf_filename]
# Seiten aus allen drei PDFs laden
diff_page = docs['diff'][page_num]
ref_page = docs['ref'][page_num]
new_page = docs['new'][page_num]
# Seiten in hoher Auflösung rendern
matrix = pymupdf.Matrix(2.0, 2.0) # 2x Vergrößerung für bessere Qualität
# Alle drei Ebenen rendern
diff_pix = diff_page.get_pixmap(matrix=matrix)
diff_img_data = diff_pix.tobytes("png")
diff_qimg = QImage.fromData(diff_img_data)
diff_pixmap = QPixmap.fromImage(diff_qimg)
ref_pix = ref_page.get_pixmap(matrix=matrix)
ref_img_data = ref_pix.tobytes("png")
ref_qimg = QImage.fromData(ref_img_data)
ref_pixmap = QPixmap.fromImage(ref_qimg)
new_pix = new_page.get_pixmap(matrix=matrix)
new_img_data = new_pix.tobytes("png")
new_qimg = QImage.fromData(new_img_data)
new_pixmap = QPixmap.fromImage(new_qimg)
# Erstelle das überlagerte Bild
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)
# Aktualisiere aktuelle Seite
self.current_page = page_num
self.current_pdf = pdf_filename
print(f"Seite {page_num + 1} erfolgreich angezeigt")
except Exception as e:
print(f"Fehler beim Rendern der Seite {page_num + 1}: {e}")
def _clear_layout(self, layout):
"""Entfernt alle Widgets aus einem Layout."""
@@ -268,30 +319,9 @@ class MainWindow(QMainWindow):
alpha_value: Der neue Alpha-Wert (-100 bis 100)
"""
print(f"Alpha geändert auf {alpha_value}")
self.update_layered_images()
def update_layered_images(self):
"""Aktualisiert alle überlagerten Bilder basierend auf dem aktuellen Alpha-Wert."""
alpha_value = self.ui.alpha.value()
for fullsize_label in self.ref_pixmaps.keys():
# Hole die Original-Pixmaps für alle drei Ebenen
ref_pixmap = self.ref_pixmaps[fullsize_label]
diff_pixmap = self.diff_pixmaps[fullsize_label]
new_pixmap = self.new_pixmaps[fullsize_label]
# Erstelle das überlagerte Bild
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
fullsize_label.setPixmap(layered_pixmap)
fullsize_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
# Nur die aktuell angezeigte Seite neu rendern
if self.current_pdf:
self.render_and_display_page(self.current_pdf, self.current_page)
def create_layered_pixmap(self, ref_pixmap, diff_pixmap, new_pixmap, alpha_value):
"""
@@ -319,15 +349,11 @@ class MainWindow(QMainWindow):
if alpha_value <= 0:
# Alpha von -100 bis 0: Übergang von ref zu diff
# ref_opacity: 1.0 bei -100, 0.0 bei 0
# diff_opacity: 0.0 bei -100, 1.0 bei 0
ref_opacity = 1.0 - (alpha_value + 100) / 100.0
diff_opacity = (alpha_value + 100) / 100.0
new_opacity = 0.0
else:
# Alpha von 0 bis 100: Übergang von diff zu new
# diff_opacity: 1.0 bei 0, 0.0 bei 100
# new_opacity: 0.0 bei 0, 1.0 bei 100
ref_opacity = 0.0
diff_opacity = 1.0 - alpha_value / 100.0
new_opacity = alpha_value / 100.0
@@ -351,7 +377,6 @@ class MainWindow(QMainWindow):
def on_button_clicked(self):
"""Wird ausgeführt, wenn der Button geklickt wird."""
print("Button wurde geklickt!")
# Hier kann die gewünschte Aktion für den Button definiert werden
def on_thumbnail_clicked(self, event, thumbnail):
"""
@@ -361,16 +386,19 @@ class MainWindow(QMainWindow):
event: Das Maus-Event
thumbnail: Das geklickte Thumbnail-Label
"""
print(f"Thumbnail {thumbnail.objectName()} wurde angeklickt: {event}")
page_info = self.thumbnail_to_page.get(thumbnail)
if page_info:
pdf_filename = page_info['pdf_filename']
page_num = page_info['page_num']
# Zum entsprechenden Vollbild scrollen
full_image = self.thumbnail_to_full_image.get(thumbnail)
if full_image:
self.ui.scrollArea_2.ensureWidgetVisible(full_image)
print(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 alle Bilder im rechten Panel an.
Wendet den Zoom-Faktor auf das aktuelle Bild an.
Args:
zoom_value: Der neue Zoom-Wert (in Prozent)
@@ -378,8 +406,9 @@ class MainWindow(QMainWindow):
self.current_zoom = zoom_value
print(f"Zoom geändert auf {zoom_value}%")
# Aktualisiere alle überlagerten Bilder mit neuem Zoom
self.update_layered_images()
# Nur die aktuell angezeigte Seite neu rendern
if self.current_pdf:
self.render_and_display_page(self.current_pdf, self.current_page)
def on_fullsize_mouse_press(self, event, fullsize_label):
"""
@@ -391,7 +420,6 @@ class MainWindow(QMainWindow):
"""
if event.button() == Qt.MouseButton.LeftButton:
self.is_dragging = True
# Verwende globale Position für bessere 4K/DPI Kompatibilität
self.last_drag_position = event.globalPosition().toPoint()
fullsize_label.setCursor(QCursor(Qt.CursorShape.ClosedHandCursor))
@@ -404,28 +432,22 @@ class MainWindow(QMainWindow):
fullsize_label: Das große Bild-Label
"""
if self.is_dragging and self.last_drag_position is not None:
# Verwende globale Position für bessere 4K/DPI Kompatibilität
current_pos = event.globalPosition().toPoint()
delta = current_pos - self.last_drag_position
# Prüfe ob die Bewegung groß genug ist (Anti-Jitter)
if abs(delta.x()) >= self.drag_threshold or abs(delta.y()) >= self.drag_threshold:
# Hole die aktuellen Scroll-Balken
v_scrollbar = self.ui.scrollArea_2.verticalScrollBar()
h_scrollbar = self.ui.scrollArea_2.horizontalScrollBar()
# Berechne neue Scroll-Positionen mit reduzierter Empfindlichkeit
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
# Setze die neuen Scroll-Positionen
v_scrollbar.setValue(new_v_value)
h_scrollbar.setValue(new_h_value)
# Aktualisiere die letzte Position nur bei erfolgreichem Scroll
self.last_drag_position = current_pos
def on_fullsize_mouse_release(self, event, fullsize_label):
@@ -440,3 +462,12 @@ class MainWindow(QMainWindow):
self.is_dragging = False
self.last_drag_position = None
fullsize_label.setCursor(QCursor(Qt.CursorShape.ArrowCursor))
def closeEvent(self, event):
"""Wird beim Schließen der Anwendung aufgerufen."""
# PDF-Dokumente schließen
for pdf_filename, docs in self.pdf_documents.items():
docs['diff'].close()
docs['ref'].close()
docs['new'].close()
super().closeEvent(event)