import glob import os import time from PySide6.QtCore import Qt, QSize from PySide6.QtGui import QCursor, QPixmap, QPainter, QAction from PySide6.QtWidgets import QLabel, QMainWindow, QApplication, QStyleFactory, QMenu from PySide6.QtPdf import QPdfDocument from ui.MainWinddow_ui import Ui_MainWindow from ui.AppSettings import AppSettingsDlg from ui.PdfProject import PdfProjectDlg from conf import app_settings, PdfProject, PdfProjectSettings from pathlib import Path 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 """ super().__init__(parent) # UI einrichten self.ui = Ui_MainWindow() self.ui.setupUi(self) # 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': QPdfDocument, 'ref': QPdfDocument, 'new': QPdfDocument}} # Aktueller Zoom-Faktor self.current_zoom = 100 # 100% # Aktuell angezeigte Seite self.current_page = 0 self.current_pdf = None # Cache für die aktuell gerenderten Pixmaps (Performance-Optimierung) self.current_rendered_pixmaps = 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 self.drag_threshold = 3 # Mindestbewegung in Pixeln vor dem Scrollen self.scroll_sensitivity = 0.7 # Reduzierte Empfindlichkeit für sanfteres Scrollen # Theme-Menü initialisieren self._setup_theme_menu() # Vorhandene Projekte-Menü initialisieren self._setup_projects_menu() # if (theme := app_settings.theme): self.change_theme(theme) else: self.change_theme('Fusion') # Bilder laden self._load_images() # Signale und Slots verbinden self._connect_signals() 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() print(f"Verfügbare Themes: {available_themes}") print(f"Aktuelles Theme: {current_theme}") # Füge Theme-Aktionen zum Menü hinzu for theme_name in available_themes: action = QAction(theme_name, self) action.setCheckable(True) # Markiere das aktuelle Theme if theme_name.lower() == current_theme.lower(): action.setChecked(True) # Verbinde die Aktion mit der Theme-Wechsel-Funktion action.triggered.connect(lambda checked, theme=theme_name: self.change_theme(theme)) # Füge die Aktion zum Theme-Menü hinzu self.ui.menuThema.addAction(action) def _setup_projects_menu(self): """Initialisiert das Vorhandene Projekte-Menü mit gespeicherten Projekten.""" # Prüfe ob Projekte vorhanden sind if not app_settings.pdf_projects: # Keine Projekte vorhanden - Menü deaktiviert lassen self.ui.actionVorhandene_Projekte.setEnabled(False) self.ui.actionVorhandene_Projekte.setText("Vorhandene Projekte (keine vorhanden)") return # Projekte vorhanden - Menü aktivieren und Untermenü erstellen self.ui.actionVorhandene_Projekte.setEnabled(True) self.ui.actionVorhandene_Projekte.setText("Vorhandene Projekte") # Erstelle ein Untermenü für die Projekte projects_menu = QMenu(self) # Füge jedes Projekt als Menü-Eintrag hinzu for project in app_settings.pdf_projects: project_action = QAction(project.name, self) project_action.setToolTip(f"Projekt-Ordner: {project.project_dir}") # Verbinde die Aktion mit der Projekt-Öffnen-Funktion project_action.triggered.connect( lambda checked, proj=project: self.open_existing_project(proj) ) projects_menu.addAction(project_action) # Setze das Untermenü für die Aktion self.ui.actionVorhandene_Projekte.setMenu(projects_menu) print(f"Projekte-Menü initialisiert mit {len(app_settings.pdf_projects)} Projekten") def open_existing_project(self, project: PdfProject): """ Öffnet ein vorhandenes Projekt. Args: project: Das zu öffnende PdfProject-Objekt """ print(f"Öffne Projekt: {project.name}") print(f"Projekt-Ordner: {project.project_dir}") try: # Prüfe ob project.yaml existiert und nicht leer ist project_yaml_path = Path(project.project_dir) / 'project.yaml' if project_yaml_path.exists() and project_yaml_path.stat().st_size > 0: # Versuche die Projekt-Einstellungen zu laden self.pdf_project = PdfProjectSettings.readSettings(project_dir=project.project_dir) print(f"Projekt-Einstellungen aus {project_yaml_path} geladen!") else: # Erstelle Standard-Projekt-Einstellungen wenn Datei leer oder nicht vorhanden print("project.yaml ist leer oder nicht vorhanden, erstelle Standard-Einstellungen") self.pdf_project = PdfProjectSettings() # Speichere die Standard-Einstellungen in die project.yaml self.pdf_project.writeSettings(project_dir=project.project_dir) print(f"Standard-Projekt-Einstellungen in {project_yaml_path} gespeichert") except Exception as e: print(f"Fehler beim Laden des Projekts '{project.name}': {e}") # Fallback: Erstelle Standard-Einstellungen try: self.pdf_project = PdfProjectSettings() print("Fallback: Standard-Projekt-Einstellungen erstellt") except Exception as fallback_error: print(f"Fehler beim Erstellen der Fallback-Einstellungen: {fallback_error}") def change_theme(self, theme_name): """ Wechselt das Theme der Anwendung. Args: theme_name: Name des zu verwendenden Themes """ print(f"Wechsle zu Theme: {theme_name}") try: # Erstelle den neuen Style style = QStyleFactory.create(theme_name) if style: # Wende den neuen Style auf die Anwendung an QApplication.setStyle(style) # Aktualisiere die Checkmarks im Menü for action in self.ui.menuThema.actions(): action.setChecked(action.text() == theme_name) print(f"Theme erfolgreich gewechselt zu: {theme_name}") app_settings.theme = theme_name app_settings.save() else: print(f"Fehler: Theme '{theme_name}' konnte nicht erstellt werden") except Exception as e: print(f"Fehler beim Wechseln des Themes: {e}") def _load_images(self): """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_page = {} self.pdf_documents = {} self.current_rendered_pixmaps = None # Basis-Pfad zu den PDF-Ordnern base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) pdf_base_dir = os.path.join(base_dir, "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") # 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 # 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 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: # 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] = { 'diff': diff_doc, 'ref': ref_doc, 'new': new_doc } print(f"PDFs geladen: {pdf_filename}") print(f" diff: {diff_doc.pageCount()} Seiten") print(f" ref: {ref_doc.pageCount()} Seiten") print(f" new: {new_doc.pageCount()} Seiten") # 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 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() 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) # 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) # 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) 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 except Exception as e: print(f"Fehler beim Laden der PDFs: {e}") # Erstelle das eine Vollbild-Label für die rechte Spalte (immer erstellen) if self.fullsize_label is None: self.fullsize_label = QLabel() self.fullsize_label.setObjectName("fullsize_current_page") self.fullsize_label.setAlignment(Qt.AlignmentFlag.AlignHCenter) self.fullsize_label.setCursor(QCursor(Qt.CursorShape.OpenHandCursor)) 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. Cached die gerenderten Pixmaps für bessere Performance. 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 start_time = time.time() try: docs = self.pdf_documents[pdf_filename] # Diff-Seite laden (bestimmt die Abmessungen) 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_image = diff_doc.render(page_num, render_size) diff_pixmap = QPixmap.fromImage(diff_image) # 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 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") # New-Seite prüfen und rendern oder weiße Seite erstellen 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") # Cache die gerenderten Pixmaps für schnelle Alpha/Zoom-Operationen self.current_rendered_pixmaps = { 'ref': ref_pixmap, 'diff': diff_pixmap, 'new': new_pixmap } # Aktualisiere aktuelle Seite self.current_page = page_num self.current_pdf = pdf_filename # Zeige das Bild mit aktuellem Alpha- und Zoom-Wert an self.update_current_display() 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}") 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 if self.fullsize_label is None: print("Fullsize-Label ist nicht 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) 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 # - (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 # 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 _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) # Menü-Aktionen verbinden self.ui.actionNeu.triggered.connect(self.open_new_project_dialog) self.ui.actionEinstellungen.triggered.connect(self.open_settings_dialog) 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 open_settings_dialog(self): """Öffnet den Einstellungen-Dialog.""" try: # Erstelle und zeige den Dialog dialog = AppSettingsDlg(self, app_settings) if dialog.exec() == AppSettingsDlg.DialogCode.Accepted: # Einstellungen wurden gespeichert, hier könnten weitere Aktionen folgen print("Einstellungen wurden gespeichert") except Exception as e: print(f"Fehler beim Öffnen des Einstellungen-Dialogs: {e}") def open_new_project_dialog(self): """Öffnet Pdf-Projekt-Dialog.""" try: # Erstelle und zeige den PdfProject-Dialog dialog = PdfProjectDlg(self) if dialog.exec() == PdfProjectDlg.DialogCode.Accepted: # Hole die Projektdaten aus dem Dialog project_data = dialog.get_project_data() # Erstelle neue ID für das Projekt new_id = max([p.id for p in app_settings.pdf_projects], default=0) + 1 # Erstelle PdfProject-Objekt new_project = PdfProject( id=new_id, name=project_data['name'], project_dir=Path(project_data['project_dir']), java_vm_id=project_data['java_vm_id'] if project_data['java_vm_id'] != -1 else 1, diff_pdf_id=project_data['diff_pdf_id'] if project_data['diff_pdf_id'] != -1 else 1, saxon_jar_id=project_data['saxon_jar_id'] if project_data['saxon_jar_id'] != -1 else 1, apache_fop_id=project_data['apache_fop_id'] if project_data['apache_fop_id'] != -1 else 1, xsl_dir_id=project_data['xsl_dir_id'] if project_data['xsl_dir_id'] != -1 else 1, default_xslt_params={} ) # Erstelle Projekt-Ordnerstruktur self._create_project_structure(new_project) # Füge das neue Projekt zu app_settings hinzu app_settings.pdf_projects.append(new_project) # Speichere app_settings app_settings.save() print(f"Neues PDF-Projekt '{project_data['name']}' wurde erstellt und gespeichert") print(f"Projekt-ID: {new_id}") print(f"Projekt-Ordner: {project_data['project_dir']}") # Aktualisiere das Projekte-Menü self._setup_projects_menu() except Exception as e: print(f"Fehler beim Erstellen des neuen Projekts: {e}") def _create_project_structure(self, project: PdfProject): """ Erstellt die Ordnerstruktur und project.yaml-Datei für ein neues Projekt. Args: project: Das PdfProject-Objekt """ try: project_dir = Path(project.project_dir) # Erstelle Unterordner subdirs = ['xml', 'new', 'diff', 'ref', 'tmp'] for subdir in subdirs: subdir_path = project_dir / subdir subdir_path.mkdir(parents=True, exist_ok=True) print(f"Ordner erstellt: {subdir_path}") project_yaml_path = project_dir / 'project.yaml' # Erstelle Standard-Projekt-Einstellungen und speichere sie if not project_yaml_path.exists(): # Erstelle Standard-PdfProjectSettings default_settings = PdfProjectSettings() # Speichere die Standard-Einstellungen in die project.yaml default_settings.writeSettings(project_dir=project_dir) print(f"project.yaml mit Standard-Einstellungen erstellt: {project_yaml_path}") else: print(f"project.yaml existiert bereits: {project_yaml_path}") except Exception as e: print(f"Fehler beim Erstellen der Projekt-Struktur: {e}") raise def on_button_clicked(self): """Wird ausgeführt, wenn der Button geklickt wird.""" print("Button wurde geklickt!") def on_thumbnail_clicked(self, event, thumbnail): """ Wird ausgeführt, wenn ein Thumbnail angeklickt wird. Args: event: Das Maus-Event thumbnail: Das geklickte Thumbnail-Label """ 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 das aktuelle Bild an. Optimiert: Verwendet gecachte Pixmaps statt erneutes PDF-Rendering. Args: zoom_value: Der neue Zoom-Wert (in Prozent) """ self.current_zoom = zoom_value print(f"Zoom geändert auf {zoom_value}%") # Verwende gecachte Pixmaps für schnelle Zoom-Änderungen 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.""" 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.""" if self.is_dragging and self.last_drag_position is not None: current_pos = event.globalPosition().toPoint() delta = current_pos - self.last_drag_position 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() 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 v_scrollbar.setValue(new_v_value) h_scrollbar.setValue(new_h_value) 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.""" if event.button() == Qt.MouseButton.LeftButton: self.is_dragging = False self.last_drag_position = None fullsize_label.setCursor(QCursor(Qt.CursorShape.OpenHandCursor)) def closeEvent(self, event): """Wird beim Schließen der Anwendung aufgerufen.""" # PDF-Dokumente schließen ist bei QtPdf automatisch durch Garbage Collection super().closeEvent(event)