diff --git a/src/ui/MainWinddow_ui.py b/src/ui/MainWinddow_ui.py index 152e184..e6c8cca 100644 --- a/src/ui/MainWinddow_ui.py +++ b/src/ui/MainWinddow_ui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'MainWinddow.ui' ## -## Created by: Qt User Interface Compiler version 6.9.0 +## Created by: Qt User Interface Compiler version 6.9.1 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ diff --git a/src/ui/MainWindow.py b/src/ui/MainWindow.py index 6b96710..99d7d6a 100644 --- a/src/ui/MainWindow.py +++ b/src/ui/MainWindow.py @@ -10,7 +10,9 @@ from PySide6.QtPdfWidgets import QPdfView from ui.MainWinddow_ui import Ui_MainWindow from ui.AppSettings import AppSettingsDlg -from conf import app_settings +from ui.PdfProject import PdfProjectDlg +from conf import app_settings, PdfProject +from pathlib import Path class MainWindow(QMainWindow): @@ -243,17 +245,18 @@ class MainWindow(QMainWindow): except Exception as e: print(f"Fehler beim Laden der PDFs: {e}") - # 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) - self.fullsize_label.setCursor(QCursor(Qt.CursorShape.OpenHandCursor)) - self.ui.verticalLayout_3.addWidget(self.fullsize_label) + # 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) + # 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: @@ -349,6 +352,10 @@ class MainWindow(QMainWindow): 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'] @@ -442,6 +449,7 @@ class MainWindow(QMainWindow): 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): @@ -471,6 +479,76 @@ class MainWindow(QMainWindow): 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']}") + + 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!") diff --git a/src/ui/PdfProject.py b/src/ui/PdfProject.py new file mode 100644 index 0000000..803a2d1 --- /dev/null +++ b/src/ui/PdfProject.py @@ -0,0 +1,292 @@ +import os + +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QDialog, QFileDialog, QMessageBox + +from conf import app_settings +from ui.PdfProject_ui import Ui_projectDlg + + +class PdfProjectDlg(QDialog): + def __init__(self, parent=None, project_data=None): + """ + Konstruktor für den PDF-Projekt-Dialog. + + Args: + parent: Übergeordnetes Widget + project_data: Bestehende Projektdaten zum Bearbeiten (optional) + """ + super().__init__(parent) + + # UI einrichten + self.ui = Ui_projectDlg() + self.ui.setupUi(self) + + # Projektdaten speichern + self.project_data = project_data or {} + + # Dialog-Eigenschaften setzen + self.setModal(True) + self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint) + + # Signale verbinden + self._connect_signals() + + # ComboBoxen initialisieren + self._init_combo_boxes() + + # Bestehende Projektdaten laden, falls vorhanden + if self.project_data: + self._load_project_data() + + def _connect_signals(self): + """Verbindet die Signale mit den entsprechenden Slots.""" + # Browse-Button für Projekt-Ordner + self.ui.pushButton.clicked.connect(self.browse_project_dir) + + # OK/Cancel Buttons sind bereits in der UI-Datei verbunden + # self.ui.buttonBox.accepted.connect(self.accept) + # self.ui.buttonBox.rejected.connect(self.reject) + + # Überschreibe accept() für Validierung + self.ui.buttonBox.accepted.disconnect() + self.ui.buttonBox.accepted.connect(self.validate_and_accept) + + def _init_combo_boxes(self): + """Initialisiert die ComboBoxen mit Werten aus app_settings.""" + # XSL-Ordner ComboBox + self.ui.cB_XslDir.clear() + for xsl_dir in app_settings.xsl_dirs: + self.ui.cB_XslDir.addItem(xsl_dir.name, xsl_dir.id) + if not app_settings.xsl_dirs: + self.ui.cB_XslDir.addItem("Keine XSL-Ordner konfiguriert", -1) + + # Java VM ComboBox + self.ui.cB_JavaVm.clear() + for java_vm in app_settings.java_vms: + display_text = f"Java VM {java_vm.version}" + self.ui.cB_JavaVm.addItem(display_text, java_vm.id) + if not app_settings.java_vms: + self.ui.cB_JavaVm.addItem("Keine Java VMs konfiguriert", -1) + + # Saxon Jar ComboBox + self.ui.cB_SaxonJar.clear() + for saxon_jar in app_settings.saxon_jars: + display_text = f"Saxon {saxon_jar.version}" + self.ui.cB_SaxonJar.addItem(display_text, saxon_jar.id) + if not app_settings.saxon_jars: + self.ui.cB_SaxonJar.addItem("Keine Saxon JARs konfiguriert", -1) + + # Apache FOP ComboBox + self.ui.cB_ApacheFop.clear() + for apache_fop in app_settings.apache_fops: + display_text = f"Apache FOP {apache_fop.version}" + self.ui.cB_ApacheFop.addItem(display_text, apache_fop.id) + if not app_settings.apache_fops: + self.ui.cB_ApacheFop.addItem("Keine Apache FOP-Instanzen konfiguriert", -1) + + # diff-pdf ComboBox + self.ui.comboBox.clear() + for diff_pdf in app_settings.diff_pdfs: + display_text = f"diff-pdf {diff_pdf.version}" + self.ui.comboBox.addItem(display_text, diff_pdf.id) + if not app_settings.diff_pdfs: + self.ui.comboBox.addItem("Keine diff-pdf-Instanzen konfiguriert", -1) + + def _load_project_data(self): + """Lädt bestehende Projektdaten in die Eingabefelder.""" + # Projekt-Name + if 'name' in self.project_data: + self.ui.lineProjectName.setText(self.project_data['name']) + + # Projekt-Ordner + if 'project_dir' in self.project_data: + self.ui.lineProjectDir.setText(str(self.project_data['project_dir'])) + elif 'directory' in self.project_data: + self.ui.lineProjectDir.setText(self.project_data['directory']) + + # ComboBox-Werte basierend auf IDs + if 'xsl_dir_id' in self.project_data: + self._select_combo_by_data(self.ui.cB_XslDir, self.project_data['xsl_dir_id']) + + if 'java_vm_id' in self.project_data: + self._select_combo_by_data(self.ui.cB_JavaVm, self.project_data['java_vm_id']) + + if 'saxon_jar_id' in self.project_data: + self._select_combo_by_data(self.ui.cB_SaxonJar, self.project_data['saxon_jar_id']) + + if 'apache_fop_id' in self.project_data: + self._select_combo_by_data(self.ui.cB_ApacheFop, self.project_data['apache_fop_id']) + + if 'diff_pdf_id' in self.project_data: + self._select_combo_by_data(self.ui.comboBox, self.project_data['diff_pdf_id']) + + def _select_combo_by_data(self, combo_box, data_value): + """ + Wählt einen ComboBox-Eintrag basierend auf dem data-Wert aus. + + Args: + combo_box: Die ComboBox + data_value: Der zu suchende data-Wert + """ + for i in range(combo_box.count()): + if combo_box.itemData(i) == data_value: + combo_box.setCurrentIndex(i) + break + + def browse_project_dir(self): + """Öffnet einen Dialog zum Auswählen des Projekt-Ordners.""" + current_dir = self.ui.lineProjectDir.text() + if not current_dir or not os.path.exists(current_dir): + current_dir = os.path.expanduser("~") + + selected_dir = QFileDialog.getExistingDirectory( + self, + "Projekt-Ordner auswählen", + current_dir, + QFileDialog.Option.ShowDirsOnly | QFileDialog.Option.DontResolveSymlinks + ) + + if selected_dir: + self.ui.lineProjectDir.setText(selected_dir) + + # Automatisch Projekt-Name generieren, wenn leer + if not self.ui.lineProjectName.text(): + project_name = os.path.basename(selected_dir) + self.ui.lineProjectName.setText(project_name) + + def validate_and_accept(self): + """Validiert die Eingaben und akzeptiert den Dialog.""" + # Projekt-Name prüfen + project_name = self.ui.lineProjectName.text().strip() + if not project_name: + QMessageBox.warning( + self, + "Ungültige Eingabe", + "Bitte geben Sie einen Projekt-Namen ein." + ) + self.ui.lineProjectName.setFocus() + return + + # Projekt-Ordner prüfen + project_dir = self.ui.lineProjectDir.text().strip() + if not project_dir: + QMessageBox.warning( + self, + "Ungültige Eingabe", + "Bitte wählen Sie einen Projekt-Ordner aus." + ) + self.ui.pushButton.setFocus() + return + + # Prüfen, ob der Ordner existiert + if not os.path.exists(project_dir): + reply = QMessageBox.question( + self, + "Ordner nicht gefunden", + f"Der Ordner '{project_dir}' existiert nicht.\n" + "Möchten Sie ihn erstellen?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.No + ) + + if reply == QMessageBox.StandardButton.Yes: + try: + os.makedirs(project_dir, exist_ok=True) + except OSError as e: + QMessageBox.critical( + self, + "Fehler", + f"Der Ordner konnte nicht erstellt werden:\n{str(e)}" + ) + return + else: + return + + # Prüfen, ob der Ordner beschreibbar ist + if not os.access(project_dir, os.W_OK): + QMessageBox.warning( + self, + "Ungültiger Ordner", + f"Der Ordner '{project_dir}' ist nicht beschreibbar.\n" + "Bitte wählen Sie einen anderen Ordner aus." + ) + return + + # Alle Validierungen bestanden + self.accept() + + def get_project_data(self): + """ + Gibt die eingegebenen Projektdaten zurück. + + Returns: + dict: Dictionary mit allen Projektdaten + """ + return { + 'name': self.ui.lineProjectName.text().strip(), + 'project_dir': self.ui.lineProjectDir.text().strip(), + 'xsl_dir_id': self.ui.cB_XslDir.currentData(), + 'java_vm_id': self.ui.cB_JavaVm.currentData(), + 'saxon_jar_id': self.ui.cB_SaxonJar.currentData(), + 'apache_fop_id': self.ui.cB_ApacheFop.currentData(), + 'diff_pdf_id': self.ui.comboBox.currentData() + } + + def set_project_data(self, project_data): + """ + Setzt die Projektdaten in den Dialog. + + Args: + project_data: Dictionary mit Projektdaten + """ + self.project_data = project_data + self._load_project_data() + + +# Convenience-Funktionen für einfache Verwendung +def create_project_dialog(parent=None): + """ + Erstellt einen neuen Projekt-Dialog für ein neues Projekt. + + Args: + parent: Übergeordnetes Widget + + Returns: + PdfProjectDlg: Der Dialog + """ + return PdfProjectDlg(parent) + + +def edit_project_dialog(parent=None, project_data=None): + """ + Erstellt einen Projekt-Dialog zum Bearbeiten eines bestehenden Projekts. + + Args: + parent: Übergeordnetes Widget + project_data: Bestehende Projektdaten + + Returns: + PdfProjectDlg: Der Dialog + """ + return PdfProjectDlg(parent, project_data) + + +def show_project_dialog(parent=None, project_data=None): + """ + Zeigt einen Projekt-Dialog an und gibt die Ergebnisse zurück. + + Args: + parent: Übergeordnetes Widget + project_data: Bestehende Projektdaten (optional) + + Returns: + tuple: (accepted: bool, project_data: dict) + """ + dialog = PdfProjectDlg(parent, project_data) + result = dialog.exec() + + if result == QDialog.DialogCode.Accepted: + return True, dialog.get_project_data() + else: + return False, None diff --git a/src/ui/PdfProject.ui b/src/ui/PdfProject.ui new file mode 100644 index 0000000..5e3cb7a --- /dev/null +++ b/src/ui/PdfProject.ui @@ -0,0 +1,171 @@ + + + projectDlg + + + + 0 + 0 + 608 + 269 + + + + PDF-Projekt + + + + + + + + + Name: + + + + + + + + + + Projekt-Ordner: + + + + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Durchsuchen ... + + + + + + + + + + XSL-Ordner: + + + + + + + + + + Java VM: + + + + + + + + + + Saxon Jar: + + + + + + + + + + Apache FOP: + + + + + + + + + + diff-pdf: + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + + + + + + + buttonBox + accepted() + projectDlg + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + projectDlg + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/ui/PdfProject_ui.py b/src/ui/PdfProject_ui.py new file mode 100644 index 0000000..a3ad886 --- /dev/null +++ b/src/ui/PdfProject_ui.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'PdfProject.ui' +## +## Created by: Qt User Interface Compiler version 6.9.1 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QAbstractButton, QApplication, QComboBox, QDialog, + QDialogButtonBox, QFormLayout, QFrame, QHBoxLayout, + QLabel, QLineEdit, QPushButton, QSizePolicy, + QVBoxLayout, QWidget) + +class Ui_projectDlg(object): + def setupUi(self, projectDlg): + if not projectDlg.objectName(): + projectDlg.setObjectName(u"projectDlg") + projectDlg.resize(608, 269) + self.verticalLayout = QVBoxLayout(projectDlg) + self.verticalLayout.setObjectName(u"verticalLayout") + self.widget = QWidget(projectDlg) + self.widget.setObjectName(u"widget") + self.formLayout = QFormLayout(self.widget) + self.formLayout.setObjectName(u"formLayout") + self.label = QLabel(self.widget) + self.label.setObjectName(u"label") + + self.formLayout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label) + + self.lineProjectName = QLineEdit(self.widget) + self.lineProjectName.setObjectName(u"lineProjectName") + + self.formLayout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.lineProjectName) + + self.label_2 = QLabel(self.widget) + self.label_2.setObjectName(u"label_2") + + self.formLayout.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_2) + + self.frame = QFrame(self.widget) + self.frame.setObjectName(u"frame") + self.frame.setFrameShape(QFrame.Shape.StyledPanel) + self.frame.setFrameShadow(QFrame.Shadow.Raised) + self.horizontalLayout = QHBoxLayout(self.frame) + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.lineProjectDir = QLineEdit(self.frame) + self.lineProjectDir.setObjectName(u"lineProjectDir") + + self.horizontalLayout.addWidget(self.lineProjectDir) + + self.pushButton = QPushButton(self.frame) + self.pushButton.setObjectName(u"pushButton") + + self.horizontalLayout.addWidget(self.pushButton) + + + self.formLayout.setWidget(1, QFormLayout.ItemRole.FieldRole, self.frame) + + self.label_3 = QLabel(self.widget) + self.label_3.setObjectName(u"label_3") + + self.formLayout.setWidget(2, QFormLayout.ItemRole.LabelRole, self.label_3) + + self.cB_XslDir = QComboBox(self.widget) + self.cB_XslDir.setObjectName(u"cB_XslDir") + + self.formLayout.setWidget(2, QFormLayout.ItemRole.FieldRole, self.cB_XslDir) + + self.label_4 = QLabel(self.widget) + self.label_4.setObjectName(u"label_4") + + self.formLayout.setWidget(3, QFormLayout.ItemRole.LabelRole, self.label_4) + + self.cB_JavaVm = QComboBox(self.widget) + self.cB_JavaVm.setObjectName(u"cB_JavaVm") + + self.formLayout.setWidget(3, QFormLayout.ItemRole.FieldRole, self.cB_JavaVm) + + self.label_5 = QLabel(self.widget) + self.label_5.setObjectName(u"label_5") + + self.formLayout.setWidget(4, QFormLayout.ItemRole.LabelRole, self.label_5) + + self.cB_SaxonJar = QComboBox(self.widget) + self.cB_SaxonJar.setObjectName(u"cB_SaxonJar") + + self.formLayout.setWidget(4, QFormLayout.ItemRole.FieldRole, self.cB_SaxonJar) + + self.label_6 = QLabel(self.widget) + self.label_6.setObjectName(u"label_6") + + self.formLayout.setWidget(5, QFormLayout.ItemRole.LabelRole, self.label_6) + + self.cB_ApacheFop = QComboBox(self.widget) + self.cB_ApacheFop.setObjectName(u"cB_ApacheFop") + + self.formLayout.setWidget(5, QFormLayout.ItemRole.FieldRole, self.cB_ApacheFop) + + self.label_7 = QLabel(self.widget) + self.label_7.setObjectName(u"label_7") + + self.formLayout.setWidget(6, QFormLayout.ItemRole.LabelRole, self.label_7) + + self.comboBox = QComboBox(self.widget) + self.comboBox.setObjectName(u"comboBox") + + self.formLayout.setWidget(6, QFormLayout.ItemRole.FieldRole, self.comboBox) + + + self.verticalLayout.addWidget(self.widget) + + self.buttonBox = QDialogButtonBox(projectDlg) + self.buttonBox.setObjectName(u"buttonBox") + self.buttonBox.setOrientation(Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok) + + self.verticalLayout.addWidget(self.buttonBox) + + + self.retranslateUi(projectDlg) + self.buttonBox.accepted.connect(projectDlg.accept) + self.buttonBox.rejected.connect(projectDlg.reject) + + QMetaObject.connectSlotsByName(projectDlg) + # setupUi + + def retranslateUi(self, projectDlg): + projectDlg.setWindowTitle(QCoreApplication.translate("projectDlg", u"PDF-Projekt", None)) + self.label.setText(QCoreApplication.translate("projectDlg", u"Name:", None)) + self.label_2.setText(QCoreApplication.translate("projectDlg", u"Projekt-Ordner:", None)) + self.pushButton.setText(QCoreApplication.translate("projectDlg", u"Durchsuchen ...", None)) + self.label_3.setText(QCoreApplication.translate("projectDlg", u"XSL-Ordner:", None)) + self.label_4.setText(QCoreApplication.translate("projectDlg", u"Java VM:", None)) + self.label_5.setText(QCoreApplication.translate("projectDlg", u"Saxon Jar:", None)) + self.label_6.setText(QCoreApplication.translate("projectDlg", u"Apache FOP:", None)) + self.label_7.setText(QCoreApplication.translate("projectDlg", u"diff-pdf:", None)) + # retranslateUi +