diff --git a/src/MainWindow.py b/src/MainWindow.py index 5fa3288..aa7326a 100644 --- a/src/MainWindow.py +++ b/src/MainWindow.py @@ -1,11 +1,12 @@ import glob import os -# import time +import time -import pymupdf # PyMuPDF -from PySide6.QtCore import Qt +from PySide6.QtCore import Qt, QSize from PySide6.QtGui import QCursor, QPixmap, QImage, QPainter, QAction from PySide6.QtWidgets import QLabel, QMainWindow, QApplication, QStyleFactory +from PySide6.QtPdf import QPdfDocument +from PySide6.QtPdfWidgets import QPdfView from ui.MainWinddow_ui import Ui_MainWindow @@ -14,7 +15,8 @@ class MainWindow(QMainWindow): def __init__(self, parent=None): """ Konstruktor für die MainWindow-Klasse. - + Verwendet PySide6.QtPdf für optimale Performance. + Args: parent: Übergeordnetes Widget, falls vorhanden """ @@ -28,7 +30,7 @@ class MainWindow(QMainWindow): 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}} + self.pdf_documents = {} # {pdf_filename: {'diff': QPdfDocument, 'ref': QPdfDocument, 'new': QPdfDocument}} # Aktueller Zoom-Faktor self.current_zoom = 100 # 100% @@ -156,10 +158,22 @@ class MainWindow(QMainWindow): continue try: - # 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) + # Alle drei PDF-Dateien öffnen mit QtPdf + diff_doc = QPdfDocument() + ref_doc = QPdfDocument() + new_doc = QPdfDocument() + + # PDF-Dateien laden + diff_doc.load(diff_pdf_path) + ref_doc.load(ref_pdf_path) + new_doc.load(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): + print(f"Fehler beim Laden der PDFs für {pdf_filename}") + continue # PDF-Dokumente für später speichern self.pdf_documents[pdf_filename] = { @@ -169,22 +183,29 @@ class MainWindow(QMainWindow): } 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") + print(f" diff: {diff_doc.pageCount()} Seiten") + print(f" ref: {ref_doc.pageCount()} Seiten") + print(f" new: {new_doc.pageCount()} Seiten") - # Nehme die maximale Seitenzahl aller drei PDFs (diff_doc bestimmt die Anzahl) - max_pages = len(diff_doc) + # Nehme die Seitenzahl der diff-PDF als Basis + max_pages = diff_doc.pageCount() + + # Performance-Test: Messe Thumbnail-Erstellung + start_time = time.time() # Erstelle nur Thumbnails (keine Vollbilder) for page_num in range(max_pages): # Nur diff-Seite für Thumbnail rendern - diff_page = diff_doc[page_num] - 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) + page_size = diff_doc.pagePointSize(page_num) + + # Skalierung für Thumbnail (entspricht ca. Matrix(1.0, 1.0) in PyMuPDF) + scale_factor = 200.0 / page_size.width() # 200 Pixel Breite für Thumbnail + + # 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() @@ -210,6 +231,9 @@ class MainWindow(QMainWindow): print(f"Thumbnail für Seite {page_num + 1} erstellt") + thumbnail_time = time.time() - start_time + print(f"Performance: {max_pages} Thumbnails in {thumbnail_time:.3f}s") + # Setze die erste PDF als aktuelle PDF if self.current_pdf is None: self.current_pdf = pdf_filename @@ -248,52 +272,51 @@ class MainWindow(QMainWindow): print(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_page = docs['diff'][page_num] - - # Seiten in hoher Auflösung rendern - matrix = pymupdf.Matrix(2.0, 2.0) # 2x Vergrößerung für bessere Qualität + 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_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) + diff_image = diff_doc.render(page_num, render_size) + diff_pixmap = QPixmap.fromImage(diff_image) - # Ermittle die Abmessungen der Diff-Seite für weiße Seiten + # 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 - if page_num < len(docs['ref']): - ref_page = docs['ref'][page_num] - 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) + 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) print(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) - print(f"Weiße Ref-Seite {page_num + 1} erstellt (Seite existiert nicht)") + print(f"Weiße Ref-Seite {page_num + 1} erstellt") # New-Seite prüfen und rendern oder weiße Seite erstellen - if page_num < len(docs['new']): - new_page = docs['new'][page_num] - 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) + 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) print(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) - print(f"Weiße New-Seite {page_num + 1} erstellt (Seite existiert nicht)") + print(f"Weiße New-Seite {page_num + 1} erstellt") # Cache die gerenderten Pixmaps für schnelle Alpha/Zoom-Operationen self.current_rendered_pixmaps = { @@ -309,7 +332,8 @@ class MainWindow(QMainWindow): # Zeige das Bild mit aktuellem Alpha- und Zoom-Wert an self.update_current_display() - print(f"Seite {page_num + 1} erfolgreich gerendert und gecacht") + render_time = time.time() - start_time + print(f"Performance: Seite {page_num + 1} gerendert in {render_time:.3f}s") except Exception as e: print(f"Fehler beim Rendern der Seite {page_num + 1}: {e}") @@ -342,45 +366,6 @@ class MainWindow(QMainWindow): self.fullsize_label.setPixmap(layered_pixmap) self.fullsize_label.setAlignment(Qt.AlignmentFlag.AlignHCenter) - 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 _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) - self.ui.zoom.mouseDoubleClickEvent = lambda event: self.ui.zoom.setValue(100) - - # Alpha-Slider verbinden - self.ui.alpha.valueChanged.connect(self.on_alpha_changed) - self.ui.alpha.mouseDoubleClickEvent = lambda event: self.ui.alpha.setValue(0) - - 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) - """ - print(f"Alpha geändert auf {alpha_value}") - # start = time.time() - - # Verwende gecachte Pixmaps für schnelle Alpha-Änderungen - self.update_current_display() - - # dauer = time.time() - start - # print(f"Dauer: {dauer}") - def create_layered_pixmap(self, ref_pixmap, diff_pixmap, new_pixmap, alpha_value): """ Erstellt ein übergelagertes Pixmap basierend auf dem Alpha-Wert. @@ -432,6 +417,42 @@ class MainWindow(QMainWindow): 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 _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) + + # 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. + Optimiert: Verwendet gecachte Pixmaps statt erneutes PDF-Rendering. + + Args: + alpha_value: Der neue Alpha-Wert (-100 bis 100) + """ + print(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 + print(f"Alpha-Update in {alpha_time:.6f}s") + def on_button_clicked(self): """Wird ausgeführt, wenn der Button geklickt wird.""" print("Button wurde geklickt!") @@ -469,26 +490,14 @@ class MainWindow(QMainWindow): 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. - - Args: - event: Das Maus-Event - fullsize_label: Das große Bild-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. - - Args: - event: Das Maus-Event - fullsize_label: Das große Bild-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 @@ -509,13 +518,7 @@ class MainWindow(QMainWindow): 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. - - Args: - event: Das Maus-Event - fullsize_label: Das große Bild-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 @@ -523,9 +526,5 @@ class MainWindow(QMainWindow): 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() + # PDF-Dokumente schließen ist bei QtPdf automatisch durch Garbage Collection super().closeEvent(event)