2025-05-22 19:10:39 +02:00
|
|
|
import glob
|
2025-05-23 11:09:47 +02:00
|
|
|
import os
|
2025-05-29 19:06:43 +02:00
|
|
|
# import time
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-23 20:38:19 +02:00
|
|
|
import pymupdf # PyMuPDF
|
2025-05-22 19:10:39 +02:00
|
|
|
from PySide6.QtCore import Qt
|
2025-05-28 19:30:37 +02:00
|
|
|
from PySide6.QtGui import QCursor, QPixmap, QImage, QPainter, QAction
|
2025-05-27 20:48:21 +02:00
|
|
|
from PySide6.QtWidgets import QLabel, QMainWindow, QApplication, QStyleFactory
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-21 20:26:03 +02:00
|
|
|
from ui.MainWinddow_ui import Ui_MainWindow
|
2025-05-20 11:24:07 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class MainWindow(QMainWindow):
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
|
|
"""
|
|
|
|
|
Konstruktor für die MainWindow-Klasse.
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-20 11:24:07 +02:00
|
|
|
Args:
|
|
|
|
|
parent: Übergeordnetes Widget, falls vorhanden
|
|
|
|
|
"""
|
|
|
|
|
super().__init__(parent)
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-20 11:24:07 +02:00
|
|
|
# UI einrichten
|
|
|
|
|
self.ui = Ui_MainWindow()
|
|
|
|
|
self.ui.setupUi(self)
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# Dict zum Speichern der Beziehung zwischen Thumbnails und Seitennummern
|
|
|
|
|
self.thumbnail_to_page = {}
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# PDF-Dokumente für späteres On-Demand-Rendering speichern
|
|
|
|
|
self.pdf_documents = {} # {pdf_filename: {'diff': doc, 'ref': doc, 'new': doc}}
|
|
|
|
|
|
2025-05-22 21:05:22 +02:00
|
|
|
# Aktueller Zoom-Faktor
|
|
|
|
|
self.current_zoom = 100 # 100%
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# Aktuell angezeigte Seite
|
|
|
|
|
self.current_page = 0
|
|
|
|
|
self.current_pdf = None
|
|
|
|
|
|
2025-05-29 19:03:19 +02:00
|
|
|
# Cache für die aktuell gerenderten Pixmaps (Performance-Optimierung)
|
|
|
|
|
self.current_rendered_pixmaps = None
|
|
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# Label für die Vollansicht (nur ein einziges Label)
|
|
|
|
|
self.fullsize_label = None
|
|
|
|
|
|
2025-05-23 21:26:50 +02:00
|
|
|
# Variablen für Drag-to-Scroll (Anti-Jitter für 4K/DPI-Skalierung)
|
|
|
|
|
self.is_dragging = False
|
|
|
|
|
self.last_drag_position = None
|
|
|
|
|
self.drag_threshold = 3 # Mindestbewegung in Pixeln vor dem Scrollen
|
|
|
|
|
self.scroll_sensitivity = 0.7 # Reduzierte Empfindlichkeit für sanfteres Scrollen
|
|
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
# Theme-Menü initialisieren
|
|
|
|
|
self._setup_theme_menu()
|
|
|
|
|
|
2025-05-22 19:10:39 +02:00
|
|
|
# Bilder laden
|
2025-05-21 20:26:03 +02:00
|
|
|
self._load_images()
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-20 11:24:07 +02:00
|
|
|
# Signale und Slots verbinden
|
|
|
|
|
self._connect_signals()
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
def _setup_theme_menu(self):
|
|
|
|
|
"""Initialisiert das Theme-Menü mit verfügbaren Themes."""
|
|
|
|
|
# Hole alle verfügbaren Themes
|
|
|
|
|
available_themes = QStyleFactory.keys()
|
|
|
|
|
current_theme = QApplication.style().objectName()
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
print(f"Verfügbare Themes: {available_themes}")
|
|
|
|
|
print(f"Aktuelles Theme: {current_theme}")
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
# Füge Theme-Aktionen zum Menü hinzu
|
|
|
|
|
for theme_name in available_themes:
|
|
|
|
|
action = QAction(theme_name, self)
|
|
|
|
|
action.setCheckable(True)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
# Markiere das aktuelle Theme
|
|
|
|
|
if theme_name.lower() == current_theme.lower():
|
|
|
|
|
action.setChecked(True)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
# Verbinde die Aktion mit der Theme-Wechsel-Funktion
|
|
|
|
|
action.triggered.connect(lambda checked, theme=theme_name: self.change_theme(theme))
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
# Füge die Aktion zum Theme-Menü hinzu
|
|
|
|
|
self.ui.menuThema.addAction(action)
|
|
|
|
|
|
|
|
|
|
def change_theme(self, theme_name):
|
|
|
|
|
"""
|
|
|
|
|
Wechselt das Theme der Anwendung.
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
Args:
|
|
|
|
|
theme_name: Name des zu verwendenden Themes
|
|
|
|
|
"""
|
|
|
|
|
print(f"Wechsle zu Theme: {theme_name}")
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
try:
|
|
|
|
|
# Erstelle den neuen Style
|
|
|
|
|
style = QStyleFactory.create(theme_name)
|
|
|
|
|
if style:
|
|
|
|
|
# Wende den neuen Style auf die Anwendung an
|
|
|
|
|
QApplication.setStyle(style)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
# Aktualisiere die Checkmarks im Menü
|
|
|
|
|
for action in self.ui.menuThema.actions():
|
|
|
|
|
action.setChecked(action.text() == theme_name)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
print(f"Theme erfolgreich gewechselt zu: {theme_name}")
|
|
|
|
|
else:
|
|
|
|
|
print(f"Fehler: Theme '{theme_name}' konnte nicht erstellt werden")
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 20:48:21 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Fehler beim Wechseln des Themes: {e}")
|
|
|
|
|
|
2025-05-21 20:26:03 +02:00
|
|
|
def _load_images(self):
|
2025-05-29 16:30:01 +02:00
|
|
|
"""Lädt PDF-Thumbnails und bereitet On-Demand-Rendering vor."""
|
2025-05-22 19:10:39 +02:00
|
|
|
# Entferne bestehende Widgets aus den Layouts
|
|
|
|
|
self._clear_layout(self.ui.verticalLayout_2)
|
|
|
|
|
self._clear_layout(self.ui.verticalLayout_3)
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 21:05:22 +02:00
|
|
|
# Dicts zurücksetzen
|
2025-05-29 16:30:01 +02:00
|
|
|
self.thumbnail_to_page = {}
|
|
|
|
|
self.pdf_documents = {}
|
2025-05-29 19:03:19 +02:00
|
|
|
self.current_rendered_pixmaps = None
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
# Basis-Pfad zu den PDF-Ordnern
|
2025-05-21 20:26:03 +02:00
|
|
|
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
2025-05-27 18:31:45 +02:00
|
|
|
pdf_base_dir = os.path.join(base_dir, "src", "ui", "res", "pdf")
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
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")
|
|
|
|
|
|
|
|
|
|
# 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
|
2025-05-23 20:38:19 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
# 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")
|
2025-05-23 20:38:19 +02:00
|
|
|
return
|
|
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
print(f"Gefundene PDF-Dateien im diff-Ordner: {diff_pdfs}")
|
|
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
|
|
|
|
|
# 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
|
|
|
|
|
|
|
|
|
|
try:
|
2025-05-29 16:30:01 +02:00
|
|
|
# Alle drei PDF-Dateien öffnen und speichern
|
2025-05-27 18:31:45 +02:00
|
|
|
diff_doc = pymupdf.open(diff_pdf_path)
|
|
|
|
|
ref_doc = pymupdf.open(ref_pdf_path)
|
|
|
|
|
new_doc = pymupdf.open(new_pdf_path)
|
|
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# PDF-Dokumente für später speichern
|
|
|
|
|
self.pdf_documents[pdf_filename] = {
|
|
|
|
|
'diff': diff_doc,
|
|
|
|
|
'ref': ref_doc,
|
|
|
|
|
'new': new_doc
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
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")
|
|
|
|
|
|
2025-05-29 21:21:18 +02:00
|
|
|
# Nehme die maximale Seitenzahl aller drei PDFs (diff_doc bestimmt die Anzahl)
|
|
|
|
|
max_pages = len(diff_doc)
|
2025-05-27 18:31:45 +02:00
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# Erstelle nur Thumbnails (keine Vollbilder)
|
2025-05-27 18:31:45 +02:00
|
|
|
for page_num in range(max_pages):
|
2025-05-29 16:30:01 +02:00
|
|
|
# Nur diff-Seite für Thumbnail rendern
|
2025-05-27 18:31:45 +02:00
|
|
|
diff_page = diff_doc[page_num]
|
2025-05-29 16:30:01 +02:00
|
|
|
matrix = pymupdf.Matrix(1.0, 1.0) # Normale Auflösung für Thumbnails
|
2025-05-27 18:31:45 +02:00
|
|
|
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)
|
|
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# Thumbnail erstellen und zur linken Spalte hinzufügen
|
2025-05-27 18:31:45 +02:00
|
|
|
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)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# 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)
|
2025-05-27 18:31:45 +02:00
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# Beziehung zwischen Thumbnail und Seitennummer speichern
|
|
|
|
|
self.thumbnail_to_page[thumbnail] = {
|
|
|
|
|
'pdf_filename': pdf_filename,
|
|
|
|
|
'page_num': page_num
|
|
|
|
|
}
|
2025-05-27 18:31:45 +02:00
|
|
|
|
|
|
|
|
# Click-Event für das Thumbnail einrichten
|
|
|
|
|
thumbnail.mousePressEvent = lambda event, t=thumbnail: self.on_thumbnail_clicked(event, t)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
print(f"Thumbnail für Seite {page_num + 1} erstellt")
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# Setze die erste PDF als aktuelle PDF
|
|
|
|
|
if self.current_pdf is None:
|
|
|
|
|
self.current_pdf = pdf_filename
|
2025-05-27 18:31:45 +02:00
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Fehler beim Laden der PDFs: {e}")
|
|
|
|
|
|
2025-05-29 16:30:01 +02:00
|
|
|
# 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)
|
2025-05-29 20:14:37 +02:00
|
|
|
self.fullsize_label.setCursor(QCursor(Qt.CursorShape.OpenHandCursor))
|
2025-05-29 16:30:01 +02:00
|
|
|
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.
|
2025-05-29 19:03:19 +02:00
|
|
|
Cached die gerenderten Pixmaps für bessere Performance.
|
2025-05-29 16:30:01 +02:00
|
|
|
|
|
|
|
|
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]
|
|
|
|
|
|
2025-05-29 21:21:18 +02:00
|
|
|
# Diff-Seite laden (bestimmt die Abmessungen)
|
2025-05-29 16:30:01 +02:00
|
|
|
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
|
|
|
|
|
|
2025-05-29 21:21:18 +02:00
|
|
|
# Diff-Seite rendern (immer vorhanden)
|
2025-05-29 16:30:01 +02:00
|
|
|
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)
|
2025-05-29 21:21:18 +02:00
|
|
|
|
|
|
|
|
# Ermittle die Abmessungen der Diff-Seite 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)
|
|
|
|
|
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)")
|
|
|
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
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)")
|
2025-05-29 16:30:01 +02:00
|
|
|
|
2025-05-29 19:03:19 +02:00
|
|
|
# Cache die gerenderten Pixmaps für schnelle Alpha/Zoom-Operationen
|
|
|
|
|
self.current_rendered_pixmaps = {
|
|
|
|
|
'ref': ref_pixmap,
|
|
|
|
|
'diff': diff_pixmap,
|
|
|
|
|
'new': new_pixmap
|
|
|
|
|
}
|
2025-05-29 16:30:01 +02:00
|
|
|
|
|
|
|
|
# Aktualisiere aktuelle Seite
|
|
|
|
|
self.current_page = page_num
|
|
|
|
|
self.current_pdf = pdf_filename
|
|
|
|
|
|
2025-05-29 19:03:19 +02:00
|
|
|
# Zeige das Bild mit aktuellem Alpha- und Zoom-Wert an
|
|
|
|
|
self.update_current_display()
|
|
|
|
|
|
|
|
|
|
print(f"Seite {page_num + 1} erfolgreich gerendert und gecacht")
|
2025-05-29 16:30:01 +02:00
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Fehler beim Rendern der Seite {page_num + 1}: {e}")
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-29 19:03:19 +02:00
|
|
|
def update_current_display(self):
|
|
|
|
|
"""
|
|
|
|
|
Aktualisiert die Anzeige der aktuellen Seite basierend auf gecachten Pixmaps.
|
|
|
|
|
Verwendet für Alpha- und Zoom-Änderungen ohne erneutes PDF-Rendering.
|
|
|
|
|
"""
|
|
|
|
|
if not self.current_rendered_pixmaps:
|
|
|
|
|
print("Keine gerenderten Pixmaps verfügbar")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Hole die gecachten Pixmaps
|
|
|
|
|
ref_pixmap = self.current_rendered_pixmaps['ref']
|
|
|
|
|
diff_pixmap = self.current_rendered_pixmaps['diff']
|
|
|
|
|
new_pixmap = self.current_rendered_pixmaps['new']
|
|
|
|
|
|
|
|
|
|
# Erstelle das überlagerte Bild mit aktuellem Alpha-Wert
|
|
|
|
|
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)
|
|
|
|
|
|
2025-05-22 19:10:39 +02:00
|
|
|
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()
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-20 11:24:07 +02:00
|
|
|
def _connect_signals(self):
|
|
|
|
|
"""Verbindet Signale mit den entsprechenden Slots."""
|
2025-05-22 19:10:39 +02:00
|
|
|
# Button-Klicks verbinden
|
2025-05-22 21:05:22 +02:00
|
|
|
self.ui.pushButton.clicked.connect(self.on_button_clicked)
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 21:05:22 +02:00
|
|
|
# Zoom-Slider verbinden
|
|
|
|
|
self.ui.zoom.valueChanged.connect(self.apply_zoom)
|
2025-05-30 14:26:38 +02:00
|
|
|
self.ui.zoom.mouseDoubleClickEvent = lambda event: self.ui.zoom.setValue(100)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
# Alpha-Slider verbinden
|
|
|
|
|
self.ui.alpha.valueChanged.connect(self.on_alpha_changed)
|
2025-05-30 14:26:38 +02:00
|
|
|
self.ui.alpha.mouseDoubleClickEvent = lambda event: self.ui.alpha.setValue(0)
|
2025-05-27 18:31:45 +02:00
|
|
|
|
|
|
|
|
def on_alpha_changed(self, alpha_value):
|
|
|
|
|
"""
|
|
|
|
|
Wird ausgeführt, wenn der Alpha-Slider geändert wird.
|
2025-05-29 19:03:19 +02:00
|
|
|
Optimiert: Verwendet gecachte Pixmaps statt erneutes PDF-Rendering.
|
2025-05-29 16:30:01 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
Args:
|
|
|
|
|
alpha_value: Der neue Alpha-Wert (-100 bis 100)
|
|
|
|
|
"""
|
|
|
|
|
print(f"Alpha geändert auf {alpha_value}")
|
2025-05-29 19:06:43 +02:00
|
|
|
# start = time.time()
|
2025-05-29 19:03:19 +02:00
|
|
|
|
|
|
|
|
# Verwende gecachte Pixmaps für schnelle Alpha-Änderungen
|
|
|
|
|
self.update_current_display()
|
|
|
|
|
|
2025-05-29 19:06:43 +02:00
|
|
|
# dauer = time.time() - start
|
|
|
|
|
# print(f"Dauer: {dauer}")
|
2025-05-27 18:31:45 +02:00
|
|
|
|
|
|
|
|
def create_layered_pixmap(self, ref_pixmap, diff_pixmap, new_pixmap, alpha_value):
|
|
|
|
|
"""
|
|
|
|
|
Erstellt ein übergelagertes Pixmap basierend auf dem Alpha-Wert.
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
Args:
|
|
|
|
|
ref_pixmap: Unterste Ebene (ref)
|
|
|
|
|
diff_pixmap: Mittlere Ebene (diff)
|
|
|
|
|
new_pixmap: Oberste Ebene (new)
|
|
|
|
|
alpha_value: Alpha-Wert (-100 bis 100)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
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())
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
# Erstelle ein leeres Pixmap für das Ergebnis
|
|
|
|
|
result = QPixmap(max_width, max_height)
|
|
|
|
|
result.fill(Qt.GlobalColor.white)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
painter = QPainter(result)
|
|
|
|
|
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
if alpha_value <= 0:
|
|
|
|
|
# Alpha von -100 bis 0: Übergang von ref zu diff
|
|
|
|
|
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
|
|
|
|
|
ref_opacity = 0.0
|
|
|
|
|
diff_opacity = 1.0 - alpha_value / 100.0
|
|
|
|
|
new_opacity = alpha_value / 100.0
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
# Zeichne die Ebenen mit entsprechender Transparenz
|
|
|
|
|
if ref_opacity > 0:
|
|
|
|
|
painter.setOpacity(ref_opacity)
|
|
|
|
|
painter.drawPixmap(0, 0, ref_pixmap)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
if diff_opacity > 0:
|
|
|
|
|
painter.setOpacity(diff_opacity)
|
|
|
|
|
painter.drawPixmap(0, 0, diff_pixmap)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
if new_opacity > 0:
|
|
|
|
|
painter.setOpacity(new_opacity)
|
|
|
|
|
painter.drawPixmap(0, 0, new_pixmap)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-27 18:31:45 +02:00
|
|
|
painter.end()
|
|
|
|
|
return result
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 19:10:39 +02:00
|
|
|
def on_button_clicked(self):
|
|
|
|
|
"""Wird ausgeführt, wenn der Button geklickt wird."""
|
|
|
|
|
print("Button wurde geklickt!")
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 19:10:39 +02:00
|
|
|
def on_thumbnail_clicked(self, event, thumbnail):
|
|
|
|
|
"""
|
|
|
|
|
Wird ausgeführt, wenn ein Thumbnail angeklickt wird.
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 19:10:39 +02:00
|
|
|
Args:
|
|
|
|
|
event: Das Maus-Event
|
|
|
|
|
thumbnail: Das geklickte Thumbnail-Label
|
|
|
|
|
"""
|
2025-05-29 16:30:01 +02:00
|
|
|
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)
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 21:05:22 +02:00
|
|
|
def apply_zoom(self, zoom_value):
|
|
|
|
|
"""
|
2025-05-29 16:30:01 +02:00
|
|
|
Wendet den Zoom-Faktor auf das aktuelle Bild an.
|
2025-05-29 19:03:19 +02:00
|
|
|
Optimiert: Verwendet gecachte Pixmaps statt erneutes PDF-Rendering.
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 21:05:22 +02:00
|
|
|
Args:
|
|
|
|
|
zoom_value: Der neue Zoom-Wert (in Prozent)
|
|
|
|
|
"""
|
|
|
|
|
self.current_zoom = zoom_value
|
|
|
|
|
print(f"Zoom geändert auf {zoom_value}%")
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-29 19:03:19 +02:00
|
|
|
# Verwende gecachte Pixmaps für schnelle Zoom-Änderungen
|
|
|
|
|
self.update_current_display()
|
2025-05-23 21:26:50 +02:00
|
|
|
|
|
|
|
|
def on_fullsize_mouse_press(self, event, fullsize_label):
|
|
|
|
|
"""
|
|
|
|
|
Wird ausgeführt, wenn die Maustaste auf einem großen Bild gedrückt wird.
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-23 21:26:50 +02:00
|
|
|
Args:
|
|
|
|
|
event: Das Maus-Event
|
|
|
|
|
fullsize_label: Das große Bild-Label
|
|
|
|
|
"""
|
|
|
|
|
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.
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-23 21:26:50 +02:00
|
|
|
Args:
|
|
|
|
|
event: Das Maus-Event
|
|
|
|
|
fullsize_label: Das große Bild-Label
|
|
|
|
|
"""
|
|
|
|
|
if self.is_dragging and self.last_drag_position is not None:
|
|
|
|
|
current_pos = event.globalPosition().toPoint()
|
|
|
|
|
delta = current_pos - self.last_drag_position
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-23 21:26:50 +02:00
|
|
|
if abs(delta.x()) >= self.drag_threshold or abs(delta.y()) >= self.drag_threshold:
|
|
|
|
|
v_scrollbar = self.ui.scrollArea_2.verticalScrollBar()
|
|
|
|
|
h_scrollbar = self.ui.scrollArea_2.horizontalScrollBar()
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-23 21:26:50 +02:00
|
|
|
scroll_delta_y = int(-delta.y() * self.scroll_sensitivity)
|
|
|
|
|
scroll_delta_x = int(-delta.x() * self.scroll_sensitivity)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-23 21:26:50 +02:00
|
|
|
new_v_value = v_scrollbar.value() + scroll_delta_y
|
|
|
|
|
new_h_value = h_scrollbar.value() + scroll_delta_x
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-23 21:26:50 +02:00
|
|
|
v_scrollbar.setValue(new_v_value)
|
|
|
|
|
h_scrollbar.setValue(new_h_value)
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-23 21:26:50 +02:00
|
|
|
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.
|
2025-05-28 19:30:37 +02:00
|
|
|
|
2025-05-23 21:26:50 +02:00
|
|
|
Args:
|
|
|
|
|
event: Das Maus-Event
|
|
|
|
|
fullsize_label: Das große Bild-Label
|
|
|
|
|
"""
|
|
|
|
|
if event.button() == Qt.MouseButton.LeftButton:
|
|
|
|
|
self.is_dragging = False
|
|
|
|
|
self.last_drag_position = None
|
2025-05-29 20:14:37 +02:00
|
|
|
fullsize_label.setCursor(QCursor(Qt.CursorShape.OpenHandCursor))
|
2025-05-29 16:30:01 +02:00
|
|
|
|
|
|
|
|
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)
|