From 4955d62e997c4a56671a3686077373958c462091 Mon Sep 17 00:00:00 2001 From: Vitali Graf Date: Thu, 29 May 2025 16:30:01 +0200 Subject: [PATCH] Nur eine Seite wird gerendert --- src/MainWindow.py | 257 ++++++++++++++++++++++++++-------------------- 1 file changed, 144 insertions(+), 113 deletions(-) diff --git a/src/MainWindow.py b/src/MainWindow.py index d5a1fc6..e8f2db2 100644 --- a/src/MainWindow.py +++ b/src/MainWindow.py @@ -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 = {} - - # 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) + # Dict zum Speichern der Beziehung zwischen Thumbnails und Seitennummern + self.thumbnail_to_page = {} + # 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.""" @@ -263,35 +314,14 @@ class MainWindow(QMainWindow): 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) + # 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}") - - # Zum entsprechenden Vollbild scrollen - full_image = self.thumbnail_to_full_image.get(thumbnail) - if full_image: - self.ui.scrollArea_2.ensureWidgetVisible(full_image) + page_info = self.thumbnail_to_page.get(thumbnail) + if page_info: + pdf_filename = page_info['pdf_filename'] + page_num = page_info['page_num'] + + 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)