Zu QtPds gewechselt

This commit is contained in:
2025-05-31 21:27:58 +02:00
parent 36372992c4
commit ce339a31ba
+108 -109
View File
@@ -1,11 +1,12 @@
import glob import glob
import os import os
# import time import time
import pymupdf # PyMuPDF from PySide6.QtCore import Qt, QSize
from PySide6.QtCore import Qt
from PySide6.QtGui import QCursor, QPixmap, QImage, QPainter, QAction from PySide6.QtGui import QCursor, QPixmap, QImage, QPainter, QAction
from PySide6.QtWidgets import QLabel, QMainWindow, QApplication, QStyleFactory 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 from ui.MainWinddow_ui import Ui_MainWindow
@@ -14,7 +15,8 @@ class MainWindow(QMainWindow):
def __init__(self, parent=None): def __init__(self, parent=None):
""" """
Konstruktor für die MainWindow-Klasse. Konstruktor für die MainWindow-Klasse.
Verwendet PySide6.QtPdf für optimale Performance.
Args: Args:
parent: Übergeordnetes Widget, falls vorhanden parent: Übergeordnetes Widget, falls vorhanden
""" """
@@ -28,7 +30,7 @@ class MainWindow(QMainWindow):
self.thumbnail_to_page = {} self.thumbnail_to_page = {}
# PDF-Dokumente für späteres On-Demand-Rendering speichern # 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 # Aktueller Zoom-Faktor
self.current_zoom = 100 # 100% self.current_zoom = 100 # 100%
@@ -156,10 +158,22 @@ class MainWindow(QMainWindow):
continue continue
try: try:
# Alle drei PDF-Dateien öffnen und speichern # Alle drei PDF-Dateien öffnen mit QtPdf
diff_doc = pymupdf.open(diff_pdf_path) diff_doc = QPdfDocument()
ref_doc = pymupdf.open(ref_pdf_path) ref_doc = QPdfDocument()
new_doc = pymupdf.open(new_pdf_path) 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 # PDF-Dokumente für später speichern
self.pdf_documents[pdf_filename] = { self.pdf_documents[pdf_filename] = {
@@ -169,22 +183,29 @@ class MainWindow(QMainWindow):
} }
print(f"PDFs geladen: {pdf_filename}") print(f"PDFs geladen: {pdf_filename}")
print(f" diff: {len(diff_doc)} Seiten") print(f" diff: {diff_doc.pageCount()} Seiten")
print(f" ref: {len(ref_doc)} Seiten") print(f" ref: {ref_doc.pageCount()} Seiten")
print(f" new: {len(new_doc)} Seiten") print(f" new: {new_doc.pageCount()} Seiten")
# Nehme die maximale Seitenzahl aller drei PDFs (diff_doc bestimmt die Anzahl) # Nehme die Seitenzahl der diff-PDF als Basis
max_pages = len(diff_doc) max_pages = diff_doc.pageCount()
# Performance-Test: Messe Thumbnail-Erstellung
start_time = time.time()
# Erstelle nur Thumbnails (keine Vollbilder) # Erstelle nur Thumbnails (keine Vollbilder)
for page_num in range(max_pages): for page_num in range(max_pages):
# Nur diff-Seite für Thumbnail rendern # Nur diff-Seite für Thumbnail rendern
diff_page = diff_doc[page_num] page_size = diff_doc.pagePointSize(page_num)
matrix = pymupdf.Matrix(1.0, 1.0) # Normale Auflösung für Thumbnails
diff_pix = diff_page.get_pixmap(matrix=matrix) # Skalierung für Thumbnail (entspricht ca. Matrix(1.0, 1.0) in PyMuPDF)
diff_img_data = diff_pix.tobytes("png") scale_factor = 200.0 / page_size.width() # 200 Pixel Breite für Thumbnail
diff_qimg = QImage.fromData(diff_img_data)
diff_pixmap = QPixmap.fromImage(diff_qimg) # 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 erstellen und zur linken Spalte hinzufügen
thumbnail = QLabel() thumbnail = QLabel()
@@ -210,6 +231,9 @@ class MainWindow(QMainWindow):
print(f"Thumbnail für Seite {page_num + 1} erstellt") 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 # Setze die erste PDF als aktuelle PDF
if self.current_pdf is None: if self.current_pdf is None:
self.current_pdf = pdf_filename self.current_pdf = pdf_filename
@@ -248,52 +272,51 @@ class MainWindow(QMainWindow):
print(f"PDF-Dokument {pdf_filename} nicht gefunden") print(f"PDF-Dokument {pdf_filename} nicht gefunden")
return return
start_time = time.time()
try: try:
docs = self.pdf_documents[pdf_filename] docs = self.pdf_documents[pdf_filename]
# Diff-Seite laden (bestimmt die Abmessungen) # Diff-Seite laden (bestimmt die Abmessungen)
diff_page = docs['diff'][page_num] diff_doc = docs['diff']
page_size = diff_doc.pagePointSize(page_num)
# Seiten in hoher Auflösung rendern
matrix = pymupdf.Matrix(2.0, 2.0) # 2x Vergrößerung für bessere Qualität # 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-Seite rendern (immer vorhanden)
diff_pix = diff_page.get_pixmap(matrix=matrix) diff_image = diff_doc.render(page_num, render_size)
diff_img_data = diff_pix.tobytes("png") diff_pixmap = QPixmap.fromImage(diff_image)
diff_qimg = QImage.fromData(diff_img_data)
diff_pixmap = QPixmap.fromImage(diff_qimg)
# 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_width = diff_pixmap.width()
diff_height = diff_pixmap.height() diff_height = diff_pixmap.height()
# Ref-Seite prüfen und rendern oder weiße Seite erstellen # Ref-Seite prüfen und rendern oder weiße Seite erstellen
if page_num < len(docs['ref']): ref_doc = docs['ref']
ref_page = docs['ref'][page_num] if page_num < ref_doc.pageCount():
ref_pix = ref_page.get_pixmap(matrix=matrix) ref_image = ref_doc.render(page_num, render_size)
ref_img_data = ref_pix.tobytes("png") ref_pixmap = QPixmap.fromImage(ref_image)
ref_qimg = QImage.fromData(ref_img_data)
ref_pixmap = QPixmap.fromImage(ref_qimg)
print(f"Ref-Seite {page_num + 1} gerendert") print(f"Ref-Seite {page_num + 1} gerendert")
else: else:
# Erstelle weiße Seite mit gleichen Abmessungen wie Diff-Seite # Erstelle weiße Seite mit gleichen Abmessungen wie Diff-Seite
ref_pixmap = QPixmap(diff_width, diff_height) ref_pixmap = QPixmap(diff_width, diff_height)
ref_pixmap.fill(Qt.GlobalColor.white) 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 # New-Seite prüfen und rendern oder weiße Seite erstellen
if page_num < len(docs['new']): new_doc = docs['new']
new_page = docs['new'][page_num] if page_num < new_doc.pageCount():
new_pix = new_page.get_pixmap(matrix=matrix) new_image = new_doc.render(page_num, render_size)
new_img_data = new_pix.tobytes("png") new_pixmap = QPixmap.fromImage(new_image)
new_qimg = QImage.fromData(new_img_data)
new_pixmap = QPixmap.fromImage(new_qimg)
print(f"New-Seite {page_num + 1} gerendert") print(f"New-Seite {page_num + 1} gerendert")
else: else:
# Erstelle weiße Seite mit gleichen Abmessungen wie Diff-Seite # Erstelle weiße Seite mit gleichen Abmessungen wie Diff-Seite
new_pixmap = QPixmap(diff_width, diff_height) new_pixmap = QPixmap(diff_width, diff_height)
new_pixmap.fill(Qt.GlobalColor.white) 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 # Cache die gerenderten Pixmaps für schnelle Alpha/Zoom-Operationen
self.current_rendered_pixmaps = { self.current_rendered_pixmaps = {
@@ -309,7 +332,8 @@ class MainWindow(QMainWindow):
# Zeige das Bild mit aktuellem Alpha- und Zoom-Wert an # Zeige das Bild mit aktuellem Alpha- und Zoom-Wert an
self.update_current_display() 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: except Exception as e:
print(f"Fehler beim Rendern der Seite {page_num + 1}: {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.setPixmap(layered_pixmap)
self.fullsize_label.setAlignment(Qt.AlignmentFlag.AlignHCenter) 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): def create_layered_pixmap(self, ref_pixmap, diff_pixmap, new_pixmap, alpha_value):
""" """
Erstellt ein übergelagertes Pixmap basierend auf dem Alpha-Wert. Erstellt ein übergelagertes Pixmap basierend auf dem Alpha-Wert.
@@ -432,6 +417,42 @@ class MainWindow(QMainWindow):
painter.end() painter.end()
return result 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): def on_button_clicked(self):
"""Wird ausgeführt, wenn der Button geklickt wird.""" """Wird ausgeführt, wenn der Button geklickt wird."""
print("Button wurde geklickt!") print("Button wurde geklickt!")
@@ -469,26 +490,14 @@ class MainWindow(QMainWindow):
self.update_current_display() self.update_current_display()
def on_fullsize_mouse_press(self, event, fullsize_label): def on_fullsize_mouse_press(self, event, fullsize_label):
""" """Wird ausgeführt, wenn die Maustaste auf einem großen Bild gedrückt wird."""
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
"""
if event.button() == Qt.MouseButton.LeftButton: if event.button() == Qt.MouseButton.LeftButton:
self.is_dragging = True self.is_dragging = True
self.last_drag_position = event.globalPosition().toPoint() self.last_drag_position = event.globalPosition().toPoint()
fullsize_label.setCursor(QCursor(Qt.CursorShape.ClosedHandCursor)) fullsize_label.setCursor(QCursor(Qt.CursorShape.ClosedHandCursor))
def on_fullsize_mouse_move(self, event, fullsize_label): def on_fullsize_mouse_move(self, event, fullsize_label):
""" """Wird ausgeführt, wenn die Maus über einem großen Bild bewegt wird."""
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
"""
if self.is_dragging and self.last_drag_position is not None: if self.is_dragging and self.last_drag_position is not None:
current_pos = event.globalPosition().toPoint() current_pos = event.globalPosition().toPoint()
delta = current_pos - self.last_drag_position delta = current_pos - self.last_drag_position
@@ -509,13 +518,7 @@ class MainWindow(QMainWindow):
self.last_drag_position = current_pos self.last_drag_position = current_pos
def on_fullsize_mouse_release(self, event, fullsize_label): def on_fullsize_mouse_release(self, event, fullsize_label):
""" """Wird ausgeführt, wenn die Maustaste auf einem großen Bild losgelassen wird."""
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
"""
if event.button() == Qt.MouseButton.LeftButton: if event.button() == Qt.MouseButton.LeftButton:
self.is_dragging = False self.is_dragging = False
self.last_drag_position = None self.last_drag_position = None
@@ -523,9 +526,5 @@ class MainWindow(QMainWindow):
def closeEvent(self, event): def closeEvent(self, event):
"""Wird beim Schließen der Anwendung aufgerufen.""" """Wird beim Schließen der Anwendung aufgerufen."""
# PDF-Dokumente schließen # PDF-Dokumente schließen ist bei QtPdf automatisch durch Garbage Collection
for pdf_filename, docs in self.pdf_documents.items():
docs['diff'].close()
docs['ref'].close()
docs['new'].close()
super().closeEvent(event) super().closeEvent(event)