Files
xsl-validator/src/ui/MainWindow.py
T

693 lines
28 KiB
Python
Raw Normal View History

import glob
2025-05-23 11:09:47 +02:00
import os
2025-05-31 21:27:58 +02:00
import time
2025-05-23 11:09:47 +02:00
2025-05-31 21:27:58 +02:00
from PySide6.QtCore import Qt, QSize
2025-06-22 11:58:57 +02:00
from PySide6.QtGui import QCursor, QPixmap, QPainter, QAction
from PySide6.QtWidgets import QLabel, QMainWindow, QApplication, QStyleFactory, QMenu
2025-05-31 21:27:58 +02:00
from PySide6.QtPdf import QPdfDocument
2025-05-23 11:09:47 +02:00
2025-05-21 20:26:03 +02:00
from ui.MainWinddow_ui import Ui_MainWindow
from ui.AppSettings import AppSettingsDlg
2025-06-16 20:30:56 +02:00
from ui.PdfProject import PdfProjectDlg
from conf import app_settings, PdfProject
from pathlib import Path
2025-05-20 11:24:07 +02:00
class MainWindow(QMainWindow):
def __init__(self, parent=None):
"""
Konstruktor für die MainWindow-Klasse.
2025-05-31 21:27:58 +02:00
Verwendet PySide6.QtPdf für optimale Performance.
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
2025-05-31 21:27:58 +02:00
self.pdf_documents = {} # {pdf_filename: {'diff': QPdfDocument, 'ref': QPdfDocument, 'new': QPdfDocument}}
2025-05-29 16:30:01 +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-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-06-22 11:58:57 +02:00
# Vorhandene Projekte-Menü initialisieren
self._setup_projects_menu()
#
if (theme := app_settings.theme):
self.change_theme(theme)
else:
self.change_theme('Fusion')
2025-05-27 20:48:21 +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)
2025-06-22 11:58:57 +02:00
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}")
# Hier könnte die Logik zum Laden des Projekts implementiert werden
# Zum Beispiel:
# - PDF-Dateien aus dem Projekt-Ordner laden
# - Projekt-spezifische Einstellungen anwenden
# - UI entsprechend aktualisieren
# Für jetzt nur eine Meldung ausgeben
print(f"Projekt '{project.name}' wurde ausgewählt")
# TODO: Implementiere Projekt-Lade-Logik
2025-05-27 20:48:21 +02:00
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}")
app_settings.theme = theme_name
app_settings.save()
2025-05-27 20:48:21 +02:00
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."""
# 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-06-09 17:17:53 +02:00
pdf_base_dir = os.path.join(base_dir, "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-31 21:27:58 +02:00
# 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
2025-05-27 18:31:45 +02:00
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}")
2025-05-31 21:27:58 +02:00
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()
2025-05-27 18:31:45 +02:00
2025-05-31 21:27:58 +02:00
# Performance-Test: Messe Thumbnail-Erstellung
start_time = time.time()
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-31 21:27:58 +02:00
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)
2025-05-27 18:31:45 +02:00
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-31 21:27:58 +02:00
thumbnail_time = time.time() - start_time
print(f"Performance: {max_pages} Thumbnails in {thumbnail_time:.3f}s")
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-06-16 20:30:56 +02:00
# 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)
2025-05-29 16:30:01 +02:00
2025-06-16 20:30:56 +02:00
# 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)
2025-05-29 16:30:01 +02:00
# 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
2025-05-31 21:27:58 +02:00
start_time = time.time()
2025-05-29 16:30:01 +02:00
try:
docs = self.pdf_documents[pdf_filename]
2025-05-29 21:21:18 +02:00
# Diff-Seite laden (bestimmt die Abmessungen)
2025-05-31 21:27:58 +02:00
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))
2025-05-29 16:30:01 +02:00
2025-05-29 21:21:18 +02:00
# Diff-Seite rendern (immer vorhanden)
2025-05-31 21:27:58 +02:00
diff_image = diff_doc.render(page_num, render_size)
diff_pixmap = QPixmap.fromImage(diff_image)
2025-05-29 21:21:18 +02:00
2025-05-31 21:27:58 +02:00
# Ermittle die Abmessungen für weiße Seiten
2025-05-29 21:21:18 +02:00
diff_width = diff_pixmap.width()
diff_height = diff_pixmap.height()
# Ref-Seite prüfen und rendern oder weiße Seite erstellen
2025-05-31 21:27:58 +02:00
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)
2025-05-29 21:21:18 +02:00
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)
2025-05-31 21:27:58 +02:00
print(f"Weiße Ref-Seite {page_num + 1} erstellt")
2025-05-29 21:21:18 +02:00
# New-Seite prüfen und rendern oder weiße Seite erstellen
2025-05-31 21:27:58 +02:00
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)
2025-05-29 21:21:18 +02:00
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)
2025-05-31 21:27:58 +02:00
print(f"Weiße New-Seite {page_num + 1} erstellt")
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()
2025-05-31 21:27:58 +02:00
render_time = time.time() - start_time
print(f"Performance: Seite {page_num + 1} gerendert in {render_time:.3f}s")
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
2025-06-16 20:30:56 +02:00
if self.fullsize_label is None:
print("Fullsize-Label ist nicht verfügbar")
return
2025-05-29 19:03:19 +02:00
# 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-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
2025-06-15 19:52:24 +02:00
ref_opacity = 1.0 # - (alpha_value + 100) / 100.0
2025-05-27 18:31:45 +02:00
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
2025-06-15 19:52:24 +02:00
diff_opacity = 1.0 # - alpha_value / 100.0
2025-05-27 18:31:45 +02:00
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-31 21:27:58 +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()
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)
2025-05-31 21:27:58 +02:00
# 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
2025-06-16 20:30:56 +02:00
self.ui.actionNeu.triggered.connect(self.open_new_project_dialog)
self.ui.actionEinstellungen.triggered.connect(self.open_settings_dialog)
2025-05-31 21:27:58 +02:00
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}")
2025-06-16 20:30:56 +02:00
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']}")
2025-06-22 11:58:57 +02:00
# Aktualisiere das Projekte-Menü
self._setup_projects_menu()
2025-06-16 20:30:56 +02:00
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'
# Projekt-Datei erstellen, wenn nicht vorhanden
if not project_yaml_path.exists():
project_yaml_path.touch()
print(f"project.yaml erstellt: {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!")
2025-05-23 11:09:47 +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
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):
2025-05-31 21:27:58 +02:00
"""Wird ausgeführt, wenn die Maustaste auf einem großen Bild gedrückt wird."""
2025-05-23 21:26:50 +02:00
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):
2025-05-31 21:27:58 +02:00
"""Wird ausgeführt, wenn die Maus über einem großen Bild bewegt wird."""
2025-05-23 21:26:50 +02:00
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):
2025-05-31 21:27:58 +02:00
"""Wird ausgeführt, wenn die Maustaste auf einem großen Bild losgelassen wird."""
2025-05-23 21:26:50 +02:00
if event.button() == Qt.MouseButton.LeftButton:
self.is_dragging = False
self.last_drag_position = None
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."""
2025-05-31 21:27:58 +02:00
# PDF-Dokumente schließen ist bei QtPdf automatisch durch Garbage Collection
2025-05-29 16:30:01 +02:00
super().closeEvent(event)