From c2826695c7330334f316d1f510cb1ee4afc32f01 Mon Sep 17 00:00:00 2001 From: Vitali Graf Date: Tue, 27 May 2025 18:31:45 +0200 Subject: [PATCH] Vergleich der PDF --- src/MainWindow.py | 271 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 206 insertions(+), 65 deletions(-) diff --git a/src/MainWindow.py b/src/MainWindow.py index 8cb4035..60ccad0 100644 --- a/src/MainWindow.py +++ b/src/MainWindow.py @@ -3,7 +3,7 @@ import os import pymupdf # PyMuPDF from PySide6.QtCore import Qt -from PySide6.QtGui import QCursor, QPixmap, QImage +from PySide6.QtGui import QCursor, QPixmap, QImage, QPainter from PySide6.QtWidgets import QLabel, QMainWindow from ui.MainWinddow_ui import Ui_MainWindow @@ -26,8 +26,10 @@ class MainWindow(QMainWindow): # Dict zum Speichern der Beziehung zwischen Thumbnails und großen Bildern self.thumbnail_to_full_image = {} - # Dict zum Speichern der Original-Pixmaps für Zoom-Funktion - self.original_pixmaps = {} + # 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) # Aktueller Zoom-Faktor self.current_zoom = 100 # 100% @@ -45,75 +47,132 @@ class MainWindow(QMainWindow): self._connect_signals() def _load_images(self): - """Lädt PDF-Seiten aus der Ntbackup.pdf-Datei.""" + """Lädt PDF-Seiten aus den drei Unterordnern ref, diff und new.""" # 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.original_pixmaps = {} + self.ref_pixmaps = {} + self.diff_pixmaps = {} + self.new_pixmaps = {} - # Pfad zur PDF-Datei + # Basis-Pfad zu den PDF-Ordnern base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - pdf_path = os.path.join(base_dir, "src", "ui", "res", "pdf", "Ntbackup.pdf") + pdf_base_dir = os.path.join(base_dir, "src", "ui", "res", "pdf") + + diff_dir = os.path.join(pdf_base_dir, "diff") + ref_dir = os.path.join(pdf_base_dir, "ref") + new_dir = os.path.join(pdf_base_dir, "new") - if not os.path.exists(pdf_path): - print(f"PDF-Datei nicht gefunden: {pdf_path}") + # Prüfe ob diff-Ordner existiert und PDF-Dateien enthält + if not os.path.exists(diff_dir): + print(f"Diff-Ordner nicht gefunden: {diff_dir}") return - try: - # PDF-Datei öffnen - pdf_document = pymupdf.open(pdf_path) - print(f"PDF geladen: {pdf_path} mit {len(pdf_document)} Seiten") + # Finde alle PDF-Dateien im diff-Ordner + diff_pdfs = glob.glob(os.path.join(diff_dir, "*.pdf")) + if not diff_pdfs: + print("Keine PDF-Dateien im diff-Ordner gefunden") + return - # Für jede Seite der PDF: - for page_num in range(len(pdf_document)): - page = pdf_document[page_num] - - # Seite in hoher Auflösung rendern - matrix = pymupdf.Matrix(2.0, 2.0) # 2x Vergrößerung für bessere Qualität - pix = page.get_pixmap(matrix=matrix) - - # PyMuPDF Pixmap zu QImage konvertieren - img_data = pix.tobytes("png") - qimg = QImage.fromData(img_data) - - # QImage zu QPixmap konvertieren - original_pixmap = QPixmap.fromImage(qimg) + print(f"Gefundene PDF-Dateien im diff-Ordner: {diff_pdfs}") - # Thumbnail erstellen und zur linken Spalte hinzufügen - thumbnail = QLabel() - thumbnail.setObjectName(f"thumbnail_page_{page_num + 1}") - thumbnail.setPixmap(original_pixmap.scaledToWidth(200, Qt.TransformationMode.SmoothTransformation)) - thumbnail.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) - thumbnail.setMouseTracking(True) - self.ui.verticalLayout_2.addWidget(thumbnail) + # Für jede PDF-Datei im diff-Ordner + for diff_pdf_path in diff_pdfs: + pdf_filename = os.path.basename(diff_pdf_path) + ref_pdf_path = os.path.join(ref_dir, pdf_filename) + new_pdf_path = os.path.join(new_dir, pdf_filename) - # Vollbild-Version erstellen und zur rechten Spalte hinzufügen - fullsize = QLabel() - fullsize.setObjectName(f"fullsize_page_{page_num + 1}") - fullsize.setPixmap(original_pixmap) - fullsize.setAlignment(Qt.AlignmentFlag.AlignHCenter) # Horizontale Zentrierung - self.ui.verticalLayout_3.addWidget(fullsize) + # Prüfe ob gleichnamige PDFs in ref und new existieren + if not os.path.exists(ref_pdf_path): + print(f"Referenz-PDF nicht gefunden: {ref_pdf_path}") + continue + if not os.path.exists(new_pdf_path): + print(f"New-PDF nicht gefunden: {new_pdf_path}") + continue - # Beziehungen speichern - self.thumbnail_to_full_image[thumbnail] = fullsize - self.original_pixmaps[fullsize] = original_pixmap + try: + # Alle drei PDF-Dateien öffnen + diff_doc = pymupdf.open(diff_pdf_path) + ref_doc = pymupdf.open(ref_pdf_path) + new_doc = pymupdf.open(new_pdf_path) - # 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"PDFs geladen: {pdf_filename}") + print(f" diff: {len(diff_doc)} Seiten") + print(f" ref: {len(ref_doc)} Seiten") + print(f" new: {len(new_doc)} Seiten") - # PDF-Dokument schließen - pdf_document.close() + # Nehme die minimale Seitenzahl aller drei PDFs + max_pages = min(len(diff_doc), len(ref_doc), len(new_doc)) - except Exception as e: - print(f"Fehler beim Laden der PDF: {e}") + # Für jede Seite + for page_num in range(max_pages): + # Seiten aus allen drei PDFs laden + 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 + 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 = QLabel() + thumbnail.setObjectName(f"thumbnail_{pdf_filename}_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) + + # 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) + + # 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 + + # 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) + + # PDF-Dokumente schließen + diff_doc.close() + ref_doc.close() + new_doc.close() + + except Exception as e: + print(f"Fehler beim Laden der PDFs: {e}") + + # Initiale Darstellung mit aktuellem Alpha-Wert + self.update_layered_images() def _clear_layout(self, layout): """Entfernt alle Widgets aus einem Layout.""" @@ -131,6 +190,97 @@ class MainWindow(QMainWindow): # Zoom-Slider verbinden self.ui.zoom.valueChanged.connect(self.apply_zoom) + + # Alpha-Slider verbinden + self.ui.alpha.valueChanged.connect(self.on_alpha_changed) + + def on_alpha_changed(self, alpha_value): + """ + Wird ausgeführt, wenn der Alpha-Slider geändert wird. + + Args: + 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) + + 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: 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 + + # 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 on_button_clicked(self): """Wird ausgeführt, wenn der Button geklickt wird.""" @@ -162,17 +312,8 @@ class MainWindow(QMainWindow): self.current_zoom = zoom_value print(f"Zoom geändert auf {zoom_value}%") - # Wende den Zoom auf alle Bilder an - for fullsize_label, original_pixmap in self.original_pixmaps.items(): - # Berechne die neue Größe basierend auf dem Zoom-Faktor - new_width = int(original_pixmap.width() * zoom_value / 100) - - # Skaliere das Bild und setze es ins Label - scaled_pixmap = original_pixmap.scaledToWidth(new_width, Qt.TransformationMode.SmoothTransformation) - fullsize_label.setPixmap(scaled_pixmap) - - # Stelle sicher, dass die horizontale Zentrierung beibehalten wird - fullsize_label.setAlignment(Qt.AlignmentFlag.AlignHCenter) + # Aktualisiere alle überlagerten Bilder mit neuem Zoom + self.update_layered_images() def on_fullsize_mouse_press(self, event, fullsize_label): """