generated from dopt-python/py311
dataclass-driven form generation
This commit is contained in:
parent
c5aadd502d
commit
3dbc9ecfcb
@ -4,6 +4,7 @@ import dataclasses as dc
|
||||
import enum
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from collections.abc import Sequence
|
||||
|
||||
from PySide6.QtCore import QDate, Qt, QTimer, Signal # Signal ist wichtig!
|
||||
@ -169,6 +170,8 @@ class AddressForm_Search(QWidget):
|
||||
form_layout.addRow("Suche:", self.search_input)
|
||||
# search_data = [addr.name for addr in ADDRESSES]
|
||||
# self.SEARCH_MAP = {addr.name: addr for addr in ADDRESSES}
|
||||
# TODO Qt supports the addition of custom data like addItem + Qt.UserRole
|
||||
# TODO or setProperty
|
||||
self.SEARCH_MAP = be_init_rec.comp_search_choice_mapping()
|
||||
self.search_data = tuple(self.SEARCH_MAP.keys())
|
||||
self.completer = QCompleter(self.search_data)
|
||||
@ -304,48 +307,82 @@ class FormFieldType(enum.StrEnum):
|
||||
LONGTEXT = enum.auto()
|
||||
DATE = enum.auto()
|
||||
DATETIME = enum.auto()
|
||||
DROPDOWN = enum.auto()
|
||||
|
||||
|
||||
@dc.dataclass(slots=True)
|
||||
class FormField:
|
||||
key: str
|
||||
label: str
|
||||
type: FormFieldType
|
||||
required: bool
|
||||
placeholder: str | None = None
|
||||
fill_value: str | None = None
|
||||
readonly: bool = False
|
||||
options: Sequence[str] | None = None
|
||||
key: str = ""
|
||||
tooltip: str = ""
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if not self.key:
|
||||
self.key = str(uuid.uuid4())
|
||||
|
||||
self.label = self.label.strip()
|
||||
if not self.label.endswith(":"):
|
||||
self.label += ":"
|
||||
if self.required:
|
||||
self.label += "*"
|
||||
|
||||
if self.type is FormFieldType.DROPDOWN and self.options is None:
|
||||
raise ValueError("Invalid field definition: Dropdown requires options")
|
||||
|
||||
|
||||
@dc.dataclass(slots=True)
|
||||
class FormFieldGroup:
|
||||
key: str
|
||||
label: str
|
||||
label: str | None
|
||||
fields: Sequence[FormField]
|
||||
|
||||
|
||||
FORM_FIELD_DEF = [
|
||||
FormField("name", "Projektname", FormFieldType.TEXT, True, "Bitte füllen..."),
|
||||
FormField("descr", "Beschreibung", FormFieldType.LONGTEXT, False),
|
||||
FormField(
|
||||
"ext_data",
|
||||
"test",
|
||||
FormFieldType.DROPDOWN,
|
||||
required=False,
|
||||
options=["test1", "test2"],
|
||||
),
|
||||
FormField(
|
||||
"test",
|
||||
FormFieldType.DROPDOWN,
|
||||
required=False,
|
||||
options=["test1", "test2"],
|
||||
fill_value="test1",
|
||||
),
|
||||
FormField(
|
||||
"test",
|
||||
FormFieldType.DROPDOWN,
|
||||
required=False,
|
||||
options=["test1", "test2"],
|
||||
fill_value="test2",
|
||||
readonly=True,
|
||||
),
|
||||
FormField(
|
||||
"test",
|
||||
FormFieldType.DROPDOWN,
|
||||
required=False,
|
||||
options=["test1", "test2", "test3"],
|
||||
fill_value="test3",
|
||||
),
|
||||
FormField("Projektname", FormFieldType.TEXT, True, "Bitte füllen..."),
|
||||
FormField("Beschreibung", FormFieldType.LONGTEXT, False),
|
||||
FormField(
|
||||
"Externe Daten",
|
||||
FormFieldType.TEXT,
|
||||
True,
|
||||
fill_value="Lorem ipsum und so weiter...",
|
||||
readonly=True,
|
||||
),
|
||||
FormField("init_date", "Auftragsdatum", FormFieldType.DATE, True),
|
||||
FormField("start_date", "Startdatum", FormFieldType.DATE, True),
|
||||
FormField("Auftragsdatum", FormFieldType.DATE, True),
|
||||
FormField("Startdatum", FormFieldType.DATE, True),
|
||||
FormField(
|
||||
"ms_date1",
|
||||
"MS Datum 1",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
@ -354,7 +391,6 @@ FORM_FIELD_DEF = [
|
||||
readonly=True,
|
||||
),
|
||||
FormField(
|
||||
"ms_date2",
|
||||
"MS Datum 2",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
@ -363,7 +399,6 @@ FORM_FIELD_DEF = [
|
||||
readonly=False,
|
||||
),
|
||||
FormField(
|
||||
"important:notes",
|
||||
"Wichtige Notizen",
|
||||
FormFieldType.LONGTEXT,
|
||||
True,
|
||||
@ -372,20 +407,18 @@ FORM_FIELD_DEF = [
|
||||
]
|
||||
|
||||
FORM_FIELD_DEF2 = [
|
||||
FormField("name", "Projektname", FormFieldType.TEXT, True, "Bitte füllen..."),
|
||||
FormField("descr", "Beschreibung", FormFieldType.LONGTEXT, False),
|
||||
FormField("Projektname", FormFieldType.TEXT, True, "Bitte füllen..."),
|
||||
FormField("Beschreibung", FormFieldType.LONGTEXT, False),
|
||||
FormField(
|
||||
"ext_data",
|
||||
"Externe Daten",
|
||||
FormFieldType.TEXT,
|
||||
True,
|
||||
fill_value="Lorem ipsum und so weiter...",
|
||||
readonly=True,
|
||||
),
|
||||
FormField("init_date", "Auftragsdatum", FormFieldType.DATE, True),
|
||||
FormField("start_date", "Startdatum", FormFieldType.DATE, True),
|
||||
FormField("Auftragsdatum", FormFieldType.DATE, True),
|
||||
FormField("Startdatum", FormFieldType.DATE, True),
|
||||
FormField(
|
||||
"ms_date1",
|
||||
"MS Datum 1",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
@ -394,7 +427,6 @@ FORM_FIELD_DEF2 = [
|
||||
readonly=True,
|
||||
),
|
||||
FormField(
|
||||
"ms_date2",
|
||||
"MS Datum 2",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
@ -403,7 +435,6 @@ FORM_FIELD_DEF2 = [
|
||||
readonly=False,
|
||||
),
|
||||
FormField(
|
||||
"important:notes",
|
||||
"Wichtige Notizen",
|
||||
FormFieldType.LONGTEXT,
|
||||
True,
|
||||
@ -411,60 +442,89 @@ FORM_FIELD_DEF2 = [
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
FORM_FIELDS_CONTACT_PERSON = [
|
||||
FormField(
|
||||
"Name Unternehmen/Netzwerkpartner (pre-filled von Suche)",
|
||||
FormFieldType.TEXT,
|
||||
required=False,
|
||||
placeholder="Text wird nach gewähltem Unternehmen angezeigt",
|
||||
readonly=True,
|
||||
),
|
||||
FormField(
|
||||
"Titel",
|
||||
FormFieldType.TEXT,
|
||||
required=False,
|
||||
tooltip=(
|
||||
"* nur wenn anrufende Person oder kontaktaufnehmende Person "
|
||||
"nicht die zu beratende Person ist"
|
||||
),
|
||||
),
|
||||
FormField(
|
||||
"Anrede_Anschrift",
|
||||
FormFieldType.TEXT,
|
||||
required=True,
|
||||
),
|
||||
FormField(
|
||||
"Name",
|
||||
FormFieldType.TEXT,
|
||||
required=True,
|
||||
),
|
||||
FormField(
|
||||
"Vorname",
|
||||
FormFieldType.TEXT,
|
||||
required=False,
|
||||
),
|
||||
FormField(
|
||||
"Festnetznummer",
|
||||
FormFieldType.TEXT,
|
||||
required=False,
|
||||
),
|
||||
FormField(
|
||||
"Mobilfunknummer",
|
||||
FormFieldType.TEXT,
|
||||
required=False,
|
||||
),
|
||||
FormField(
|
||||
"E-Mail",
|
||||
FormFieldType.TEXT,
|
||||
required=False,
|
||||
),
|
||||
FormField(
|
||||
"Funktion/Beziehung zur beratenden Person",
|
||||
FormFieldType.TEXT,
|
||||
required=False,
|
||||
),
|
||||
FormField(
|
||||
"Adresse",
|
||||
FormFieldType.LONGTEXT,
|
||||
required=False,
|
||||
),
|
||||
]
|
||||
|
||||
FORM_FIELD_GROUPS = [
|
||||
FormFieldGroup("group1", "Test-1", FORM_FIELD_DEF),
|
||||
FormFieldGroup("group2", "Test-2", FORM_FIELD_DEF2),
|
||||
FormFieldGroup(
|
||||
"Status && Projektrelevanz",
|
||||
[
|
||||
FormField(
|
||||
"Projektrelevanz",
|
||||
FormFieldType.DROPDOWN,
|
||||
required=True,
|
||||
options=["ja", "nein"],
|
||||
),
|
||||
],
|
||||
),
|
||||
FormFieldGroup("Daten Kontaktperson", FORM_FIELDS_CONTACT_PERSON),
|
||||
# FormFieldGroup("Test-2", FORM_FIELD_DEF2),
|
||||
]
|
||||
|
||||
|
||||
class MyForm(QWidget):
|
||||
class AutoForm(QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
form_field_groups: Sequence[FormFieldGroup],
|
||||
add_buttons: bool = True,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
# --- LAYOUT ---
|
||||
self.main_layout = QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.form_field_groups = form_field_groups
|
||||
|
||||
for fg in form_field_groups:
|
||||
widget = MyFormPart(fg.fields, fg.label, add_buttons=False)
|
||||
self.main_layout.addWidget(widget)
|
||||
|
||||
# buttons
|
||||
# self.add_buttons = add_buttons
|
||||
# if self.add_buttons:
|
||||
# self.layout_btn = QHBoxLayout()
|
||||
# self.main_layout.addLayout(self.layout_btn)
|
||||
# self.save_btn_txt_enabled = "Speichern (Strg + S)"
|
||||
# self.save_btn_txt_disabled = "Wird gespeichert..."
|
||||
# self.save_btn = QPushButton(self.save_btn_txt_enabled)
|
||||
# self.save_btn.setShortcut("Ctrl+S")
|
||||
# self.save_btn.setFixedHeight(50)
|
||||
# self.save_btn.setSizePolicy(
|
||||
# QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
|
||||
# )
|
||||
# self.save_btn.clicked.connect(self.on_save_clicked)
|
||||
# self.layout_btn.addWidget(self.save_btn)
|
||||
# self.reset_btn = QPushButton("Zurücksetzen (Strg + Z)")
|
||||
# self.reset_btn.setShortcut("Ctrl+Z")
|
||||
# self.reset_btn.setFixedHeight(50)
|
||||
# self.reset_btn.setSizePolicy(
|
||||
# QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
|
||||
# )
|
||||
# self.reset_btn.clicked.connect(self.reset_form)
|
||||
# self.layout_btn.addWidget(self.reset_btn)
|
||||
|
||||
|
||||
class MyFormPart(QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
field_definitons: Sequence[FormField],
|
||||
group_name: str | None = None,
|
||||
add_buttons: bool = False,
|
||||
):
|
||||
super().__init__()
|
||||
self.setStyleSheet("""
|
||||
QGroupBox {
|
||||
@ -487,23 +547,15 @@ class MyFormPart(QWidget):
|
||||
# --- LAYOUT ---
|
||||
self.main_layout = QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.form_layout = QFormLayout()
|
||||
self.group_box: QGroupBox | None = None
|
||||
self.group_name = group_name
|
||||
self.form_field_groups = form_field_groups
|
||||
|
||||
if self.group_name:
|
||||
self.group_box = QGroupBox(group_name)
|
||||
self.main_layout.addWidget(self.group_box)
|
||||
self.group_box.setLayout(self.form_layout)
|
||||
else:
|
||||
self.main_layout.addLayout(self.form_layout)
|
||||
|
||||
self.form_layout.setSpacing(10) # Abstand zwischen den Zeilen
|
||||
|
||||
self.field_definitions = field_definitons
|
||||
self.widgets: dict[str, QWidget] = {}
|
||||
# automatic build
|
||||
self.create_form_fields()
|
||||
|
||||
for fg in form_field_groups:
|
||||
form_group = FormGroupWidget(fg.fields, fg.label)
|
||||
self.main_layout.addWidget(form_group)
|
||||
self._add_widgets(form_group.widgets)
|
||||
# self.widgets.update(form_group.widgets)
|
||||
|
||||
# buttons
|
||||
self.add_buttons = add_buttons
|
||||
@ -529,9 +581,197 @@ class MyFormPart(QWidget):
|
||||
self.reset_btn.clicked.connect(self.reset_form)
|
||||
self.layout_btn.addWidget(self.reset_btn)
|
||||
|
||||
def _add_widgets(
|
||||
self,
|
||||
widgets: dict[str, QWidget],
|
||||
) -> None:
|
||||
current_keys = set(self.widgets.keys())
|
||||
new_keys = set(widgets.keys())
|
||||
shared_keys = current_keys.intersection(new_keys)
|
||||
|
||||
if shared_keys:
|
||||
raise ValueError(f"Tried to add fields with already assigned keys: {shared_keys}")
|
||||
|
||||
self.widgets.update(widgets)
|
||||
|
||||
def _disable_save(self) -> None:
|
||||
self.save_btn.setEnabled(False)
|
||||
self.save_btn.setText(self.save_btn_txt_disabled)
|
||||
|
||||
def _enable_save(
|
||||
self,
|
||||
timeout: int = 3000,
|
||||
) -> None:
|
||||
QTimer.singleShot(timeout, lambda: self.save_btn.setEnabled(True))
|
||||
QTimer.singleShot(timeout + 1, lambda: self.save_btn.setShortcut("Ctrl+S"))
|
||||
self.save_btn.setText(self.save_btn_txt_enabled)
|
||||
|
||||
def on_save_clicked(self) -> None:
|
||||
self._disable_save()
|
||||
errors: list[str] = []
|
||||
|
||||
# time.sleep(0.5)
|
||||
|
||||
# Wir gehen unsere Feld-Definitionen durch
|
||||
for fg in self.form_field_groups:
|
||||
for field in fg.fields:
|
||||
widget = self.widgets[field.key]
|
||||
|
||||
# 1. Zuerst setzen wir das Design des Feldes wieder auf "Normal" zurück.
|
||||
# Falls der Nutzer den Fehler vorher schon korrigiert hat, muss der rote Rand weg!
|
||||
if not field.readonly:
|
||||
widget.setStyleSheet("")
|
||||
|
||||
# 2. Ist es überhaupt ein Pflichtfeld?
|
||||
if not field.required:
|
||||
continue
|
||||
|
||||
is_empty = False
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
if not widget.text().strip():
|
||||
is_empty = True
|
||||
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
if not widget.toPlainText().strip():
|
||||
is_empty = True
|
||||
|
||||
if not is_empty:
|
||||
continue
|
||||
|
||||
error = field.label.replace("*", "").replace(":", "")
|
||||
if fg.label:
|
||||
error = f"{fg.label}: {error}"
|
||||
errors.append(error)
|
||||
|
||||
# Optisches Feedback: Heller roter Hintergrund und roter Rand
|
||||
widget.setStyleSheet("""
|
||||
border: 1px solid #ef4444;
|
||||
background-color: #ffe9e9;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
""")
|
||||
|
||||
# --- ERGEBNIS AUSWERTEN ---
|
||||
if errors:
|
||||
# Es gibt Fehler! Speichern abbrechen und Pop-up anzeigen.
|
||||
error_text = "Bitte fülle die folgenden Pflichtfelder aus:\n\n- " + "\n- ".join(
|
||||
errors
|
||||
)
|
||||
QMessageBox.warning(self, "Fehlende Angaben", error_text)
|
||||
self._enable_save()
|
||||
return
|
||||
|
||||
# Wenn wir hier ankommen, ist die Liste 'errors' leer. Alles ist korrekt ausgefüllt!
|
||||
# time.sleep(0.5)
|
||||
print("Erfolg! Alle Daten sind valide.")
|
||||
self.reset_form()
|
||||
self._enable_save()
|
||||
|
||||
def reset_form(self) -> None:
|
||||
for fg in self.form_field_groups:
|
||||
for field in fg.fields:
|
||||
widget = self.widgets[field.key]
|
||||
|
||||
if field.readonly:
|
||||
continue
|
||||
|
||||
if isinstance(widget, QLineEdit):
|
||||
widget.clear()
|
||||
if field.fill_value:
|
||||
widget.setText(field.fill_value)
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
widget.clear()
|
||||
elif isinstance(widget, QDateEdit):
|
||||
if field.fill_value:
|
||||
set_date = QDate.fromString(field.fill_value, "dd.MM.yyyy")
|
||||
if not set_date.isValid():
|
||||
raise ValueError(
|
||||
f"Could not parse date field value >{field.fill_value}<"
|
||||
)
|
||||
widget.setDate(set_date)
|
||||
else:
|
||||
widget.setDate(QDate.currentDate())
|
||||
elif isinstance(widget, QComboBox):
|
||||
if field.fill_value:
|
||||
widget.setCurrentText(field.fill_value)
|
||||
|
||||
widget.setStyleSheet("")
|
||||
|
||||
def get_form_data(self) -> ...:
|
||||
raise NotImplementedError()
|
||||
|
||||
data = {}
|
||||
for key, widget in self.widgets.items():
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
data[key] = widget.text()
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
data[key] = widget.toPlainText()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class FormGroupWidget(QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
form_fields: Sequence[FormField],
|
||||
group_name: str | None = None,
|
||||
):
|
||||
super().__init__()
|
||||
self.setStyleSheet("""
|
||||
QGroupBox {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
border: 1px solid #cbd5e1; /* Heller, moderner Rahmen */
|
||||
border-radius: 8px; /* Abgerundete Ecken */
|
||||
margin-top: 15px; /* Platz für die Überschrift schaffen */
|
||||
padding-top: 15px; /* Abstand zwischen Rahmen und erstem Feld */
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top left; /* Überschrift oben links */
|
||||
padding: 0 5px; /* Etwas Luft links und rechts vom Text */
|
||||
color: #334155; /* Dunkelgraue Schrift */
|
||||
}
|
||||
QComboBox {
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 4px;
|
||||
padding: 5px;
|
||||
}
|
||||
QComboBox:disabled {
|
||||
background-color: #f1f5f9; /* Helles System-Grau */
|
||||
color: #333D4B; /* Gut lesbare, aber gedeckte Schrift */
|
||||
border: 1px dashed #cbd5e1; /* Gestrichelter Rand wie beim Datum */
|
||||
}
|
||||
QComboBox::drop-down:disabled {
|
||||
border: none;
|
||||
image: none;
|
||||
}
|
||||
""")
|
||||
|
||||
# --- LAYOUT ---
|
||||
self.main_layout = QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.form_layout = QFormLayout()
|
||||
self.group_box: QGroupBox | None = None
|
||||
self.group_name = group_name
|
||||
|
||||
if self.group_name:
|
||||
self.group_box = QGroupBox(group_name)
|
||||
self.main_layout.addWidget(self.group_box)
|
||||
self.group_box.setLayout(self.form_layout)
|
||||
else:
|
||||
self.main_layout.addLayout(self.form_layout)
|
||||
|
||||
self.form_layout.setSpacing(10) # Abstand zwischen den Zeilen
|
||||
|
||||
self.form_fields = form_fields
|
||||
self.widgets: dict[str, QWidget] = {}
|
||||
# automatic build
|
||||
self.create_form_fields()
|
||||
|
||||
def create_form_fields(self) -> None:
|
||||
for field in self.field_definitions:
|
||||
widget = None
|
||||
for field in self.form_fields:
|
||||
widget: QWidget | None = None
|
||||
|
||||
match field.type:
|
||||
case FormFieldType.TEXT:
|
||||
@ -583,120 +823,40 @@ class MyFormPart(QWidget):
|
||||
if field.fill_value:
|
||||
widget.setText(field.fill_value)
|
||||
|
||||
case FormFieldType.DROPDOWN:
|
||||
widget = QComboBox()
|
||||
assert field.options
|
||||
widget.addItems(field.options)
|
||||
if field.placeholder:
|
||||
widget.setPlaceholderText(field.placeholder)
|
||||
if field.fill_value:
|
||||
widget.setCurrentText(field.fill_value)
|
||||
if field.readonly:
|
||||
widget.setEnabled(False)
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
|
||||
case _:
|
||||
raise NotImplementedError(f"Not supported field type: {field.type.value}")
|
||||
|
||||
if widget:
|
||||
self.widgets[field.key] = widget
|
||||
self.widgets[field.key] = widget
|
||||
|
||||
if not field.tooltip:
|
||||
self.form_layout.addRow(field.label, widget)
|
||||
|
||||
def get_form_data(self) -> ...:
|
||||
"""Liest alle Felder automatisch aus"""
|
||||
data = {}
|
||||
for key, widget in self.widgets.items():
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
data[key] = widget.text()
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
data[key] = widget.toPlainText()
|
||||
|
||||
return data
|
||||
|
||||
def _disable_save(self) -> None:
|
||||
self.save_btn.setEnabled(False)
|
||||
self.save_btn.setText(self.save_btn_txt_disabled)
|
||||
|
||||
def _enable_save(
|
||||
self,
|
||||
timeout: int = 3000,
|
||||
) -> None:
|
||||
QTimer.singleShot(timeout, lambda: self.save_btn.setEnabled(True))
|
||||
QTimer.singleShot(timeout + 1, lambda: self.save_btn.setShortcut("Ctrl+S"))
|
||||
self.save_btn.setText(self.save_btn_txt_enabled)
|
||||
|
||||
def on_save_clicked(self) -> None:
|
||||
self._disable_save()
|
||||
errors = [] # Hier sammeln wir die Namen der fehlenden Felder
|
||||
|
||||
# time.sleep(0.5)
|
||||
|
||||
# Wir gehen unsere Feld-Definitionen durch
|
||||
for field in self.field_definitions:
|
||||
widget = self.widgets[field.key]
|
||||
|
||||
# 1. Zuerst setzen wir das Design des Feldes wieder auf "Normal" zurück.
|
||||
# Falls der Nutzer den Fehler vorher schon korrigiert hat, muss der rote Rand weg!
|
||||
if not field.readonly:
|
||||
widget.setStyleSheet("")
|
||||
|
||||
# 2. Ist es überhaupt ein Pflichtfeld?
|
||||
if not field.required:
|
||||
continue
|
||||
|
||||
is_empty = False
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
if not widget.text().strip():
|
||||
is_empty = True
|
||||
field_layout = QHBoxLayout()
|
||||
field_layout.setContentsMargins(0, 0, 0, 0)
|
||||
field_layout.setSpacing(5)
|
||||
info_btn = QPushButton("ℹ️")
|
||||
# info_btn = QPushButton("?")
|
||||
info_btn.setFixedSize(28, 27)
|
||||
info_btn.setFlat(True)
|
||||
info_btn.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
info_btn.setToolTip(field.tooltip)
|
||||
field_layout.addWidget(widget)
|
||||
field_layout.addWidget(info_btn)
|
||||
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
if not widget.toPlainText().strip():
|
||||
is_empty = True
|
||||
|
||||
if not is_empty:
|
||||
continue
|
||||
|
||||
errors.append(
|
||||
field.label.replace("*", "").replace(":", "")
|
||||
) # Sternchen für die Fehlermeldung entfernen
|
||||
|
||||
# Optisches Feedback: Heller roter Hintergrund und roter Rand
|
||||
widget.setStyleSheet("""
|
||||
border: 1px solid #ef4444;
|
||||
background-color: #ffe9e9;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
""")
|
||||
|
||||
# --- ERGEBNIS AUSWERTEN ---
|
||||
if errors:
|
||||
# Es gibt Fehler! Speichern abbrechen und Pop-up anzeigen.
|
||||
error_text = "Bitte fülle die folgenden Pflichtfelder aus:\n\n- " + "\n- ".join(
|
||||
errors
|
||||
)
|
||||
QMessageBox.warning(self, "Fehlende Angaben", error_text)
|
||||
self._enable_save()
|
||||
return
|
||||
|
||||
# Wenn wir hier ankommen, ist die Liste 'errors' leer. Alles ist korrekt ausgefüllt!
|
||||
# time.sleep(0.5)
|
||||
print("Erfolg! Alle Daten sind valide.")
|
||||
self.reset_form()
|
||||
self._enable_save()
|
||||
|
||||
def reset_form(self) -> None:
|
||||
for field in self.field_definitions:
|
||||
widget = self.widgets[field.key]
|
||||
|
||||
if field.readonly:
|
||||
continue
|
||||
|
||||
if isinstance(widget, QLineEdit):
|
||||
widget.clear()
|
||||
if field.fill_value:
|
||||
widget.setText(field.fill_value)
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
widget.clear()
|
||||
elif isinstance(widget, QDateEdit):
|
||||
if field.fill_value:
|
||||
set_date = QDate.fromString(field.fill_value, "dd.MM.yyyy")
|
||||
if not set_date.isValid():
|
||||
raise ValueError(
|
||||
f"Could not parse date field value >{field.fill_value}<"
|
||||
)
|
||||
widget.setDate(set_date)
|
||||
else:
|
||||
widget.setDate(QDate.currentDate())
|
||||
|
||||
widget.setStyleSheet("")
|
||||
self.form_layout.addRow(field.label, field_layout)
|
||||
|
||||
|
||||
class ClickableCell(QFrame):
|
||||
@ -999,7 +1159,7 @@ class SearchFormPage(QWidget):
|
||||
title.setStyleSheet("font-size: 14px; font-style: italic;") # font-weight: bold;
|
||||
container_layout.addWidget(title)
|
||||
# container_layout.addWidget(MyFormPart(FORM_FIELD_DEF, "Test-Gruppe"))
|
||||
container_layout.addWidget(MyForm(FORM_FIELD_GROUPS))
|
||||
container_layout.addWidget(AutoForm(FORM_FIELD_GROUPS))
|
||||
|
||||
container_layout.addSpacing(30)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user