2025-05-22 19:10:39 +02:00
|
|
|
import glob
|
2025-05-23 11:09:47 +02:00
|
|
|
import os
|
|
|
|
|
|
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-23 20:38:19 +02:00
|
|
|
from PySide6.QtGui import QCursor, QPixmap, QImage
|
2025-05-23 11:09:47 +02:00
|
|
|
from PySide6.QtWidgets import QLabel, QMainWindow
|
|
|
|
|
|
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-22 19:10:39 +02:00
|
|
|
# Dict zum Speichern der Beziehung zwischen Thumbnails und großen Bildern
|
|
|
|
|
self.thumbnail_to_full_image = {}
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 21:05:22 +02:00
|
|
|
# Dict zum Speichern der Original-Pixmaps für Zoom-Funktion
|
|
|
|
|
self.original_pixmaps = {}
|
2025-05-23 11:09:47 +02:00
|
|
|
|
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-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-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-21 20:26:03 +02:00
|
|
|
def _load_images(self):
|
2025-05-23 20:38:19 +02:00
|
|
|
"""Lädt PDF-Seiten aus der Ntbackup.pdf-Datei."""
|
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
|
|
|
|
|
self.thumbnail_to_full_image = {}
|
|
|
|
|
self.original_pixmaps = {}
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-23 20:38:19 +02:00
|
|
|
# Pfad zur PDF-Datei
|
2025-05-21 20:26:03 +02:00
|
|
|
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
2025-05-23 20:38:19 +02:00
|
|
|
pdf_path = os.path.join(base_dir, "src", "ui", "res", "pdf", "Ntbackup.pdf")
|
|
|
|
|
|
|
|
|
|
if not os.path.exists(pdf_path):
|
|
|
|
|
print(f"PDF-Datei nicht gefunden: {pdf_path}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# PDF-Datei öffnen
|
|
|
|
|
pdf_document = pymupdf.open(pdf_path)
|
|
|
|
|
print(f"PDF geladen: {pdf_path} mit {len(pdf_document)} Seiten")
|
|
|
|
|
|
|
|
|
|
# Für jede Seite der PDF:
|
|
|
|
|
for page_num in range(len(pdf_document)):
|
|
|
|
|
page = pdf_document[page_num]
|
|
|
|
|
|
|
|
|
|
# Seite in hoher Auflösung rendern
|
|
|
|
|
matrix = pymupdf.Matrix(2.0, 2.0) # 2x Vergrößerung für bessere Qualität
|
|
|
|
|
pix = page.get_pixmap(matrix=matrix)
|
|
|
|
|
|
|
|
|
|
# PyMuPDF Pixmap zu QImage konvertieren
|
|
|
|
|
img_data = pix.tobytes("png")
|
|
|
|
|
qimg = QImage.fromData(img_data)
|
|
|
|
|
|
|
|
|
|
# QImage zu QPixmap konvertieren
|
|
|
|
|
original_pixmap = QPixmap.fromImage(qimg)
|
|
|
|
|
|
|
|
|
|
# Thumbnail erstellen und zur linken Spalte hinzufügen
|
|
|
|
|
thumbnail = QLabel()
|
|
|
|
|
thumbnail.setObjectName(f"thumbnail_page_{page_num + 1}")
|
|
|
|
|
thumbnail.setPixmap(original_pixmap.scaledToWidth(200, Qt.TransformationMode.SmoothTransformation))
|
|
|
|
|
thumbnail.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
|
|
|
|
|
thumbnail.setMouseTracking(True)
|
|
|
|
|
self.ui.verticalLayout_2.addWidget(thumbnail)
|
|
|
|
|
|
|
|
|
|
# Vollbild-Version erstellen und zur rechten Spalte hinzufügen
|
|
|
|
|
fullsize = QLabel()
|
|
|
|
|
fullsize.setObjectName(f"fullsize_page_{page_num + 1}")
|
|
|
|
|
fullsize.setPixmap(original_pixmap)
|
|
|
|
|
fullsize.setAlignment(Qt.AlignmentFlag.AlignHCenter) # Horizontale Zentrierung
|
|
|
|
|
self.ui.verticalLayout_3.addWidget(fullsize)
|
|
|
|
|
|
|
|
|
|
# Beziehungen speichern
|
|
|
|
|
self.thumbnail_to_full_image[thumbnail] = fullsize
|
|
|
|
|
self.original_pixmaps[fullsize] = original_pixmap
|
|
|
|
|
|
|
|
|
|
# Click-Event für das Thumbnail einrichten
|
|
|
|
|
thumbnail.mousePressEvent = lambda event, t=thumbnail: self.on_thumbnail_clicked(event, t)
|
2025-05-23 21:26:50 +02:00
|
|
|
|
|
|
|
|
# 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)
|
2025-05-23 20:38:19 +02:00
|
|
|
|
|
|
|
|
# PDF-Dokument schließen
|
|
|
|
|
pdf_document.close()
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Fehler beim Laden der PDF: {e}")
|
2025-05-23 11:09:47 +02:00
|
|
|
|
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-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!")
|
|
|
|
|
# Hier kann die gewünschte Aktion für den Button definiert werden
|
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
|
|
|
|
|
"""
|
|
|
|
|
print(f"Thumbnail {thumbnail.objectName()} wurde angeklickt: {event}")
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 19:10:39 +02:00
|
|
|
# Zum entsprechenden Vollbild scrollen
|
|
|
|
|
full_image = self.thumbnail_to_full_image.get(thumbnail)
|
|
|
|
|
if full_image:
|
|
|
|
|
self.ui.scrollArea_2.ensureWidgetVisible(full_image)
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 21:05:22 +02:00
|
|
|
def apply_zoom(self, zoom_value):
|
|
|
|
|
"""
|
|
|
|
|
Wendet den Zoom-Faktor auf alle Bilder im rechten Panel an.
|
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-22 21:05:22 +02:00
|
|
|
# Wende den Zoom auf alle Bilder an
|
|
|
|
|
for fullsize_label, original_pixmap in self.original_pixmaps.items():
|
|
|
|
|
# Berechne die neue Größe basierend auf dem Zoom-Faktor
|
|
|
|
|
new_width = int(original_pixmap.width() * zoom_value / 100)
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 21:05:22 +02:00
|
|
|
# Skaliere das Bild und setze es ins Label
|
2025-05-23 19:43:08 +02:00
|
|
|
scaled_pixmap = original_pixmap.scaledToWidth(new_width, Qt.TransformationMode.SmoothTransformation)
|
2025-05-22 21:05:22 +02:00
|
|
|
fullsize_label.setPixmap(scaled_pixmap)
|
2025-05-23 11:09:47 +02:00
|
|
|
|
2025-05-22 21:22:25 +02:00
|
|
|
# Stelle sicher, dass die horizontale Zentrierung beibehalten wird
|
|
|
|
|
fullsize_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
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.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
event: Das Maus-Event
|
|
|
|
|
fullsize_label: Das große Bild-Label
|
|
|
|
|
"""
|
|
|
|
|
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))
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
"""
|
|
|
|
|
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):
|
|
|
|
|
"""
|
|
|
|
|
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:
|
|
|
|
|
self.is_dragging = False
|
|
|
|
|
self.last_drag_position = None
|
|
|
|
|
fullsize_label.setCursor(QCursor(Qt.CursorShape.ArrowCursor))
|