generated from dopt-python/py311
basic recursive build functionality
This commit is contained in:
@@ -6,6 +6,8 @@ import sys
|
||||
import time
|
||||
import uuid
|
||||
from collections.abc import Sequence
|
||||
from pprint import pprint
|
||||
from typing import TypeAlias, TypedDict
|
||||
|
||||
from PySide6.QtCore import QDate, QModelIndex, QStringListModel, Qt, QTimer, Signal
|
||||
from PySide6.QtGui import QAction, QStandardItem, QStandardItemModel
|
||||
@@ -22,6 +24,7 @@ from PySide6.QtWidgets import (
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLayout,
|
||||
QLineEdit,
|
||||
QListWidget,
|
||||
QMainWindow,
|
||||
@@ -395,18 +398,22 @@ class ContactPersonForm_Search(QWidget):
|
||||
|
||||
|
||||
class FormFieldType(enum.StrEnum):
|
||||
GROUP = enum.auto()
|
||||
TEXT = enum.auto()
|
||||
LONGTEXT = enum.auto()
|
||||
DATE = enum.auto()
|
||||
DATETIME = enum.auto()
|
||||
DROPDOWN = enum.auto()
|
||||
DYNAMIC_LIST = enum.auto()
|
||||
|
||||
|
||||
@dc.dataclass(slots=True)
|
||||
class FormField:
|
||||
label: str
|
||||
type: FormFieldType
|
||||
required: bool
|
||||
children: Sequence[FormField] = dc.field(default_factory=list)
|
||||
parent: FormField | None = None
|
||||
required: bool = False
|
||||
placeholder: str | None = None
|
||||
fill_value: str | None = None
|
||||
readonly: bool = False
|
||||
@@ -419,7 +426,7 @@ class FormField:
|
||||
self.key = str(uuid.uuid4())
|
||||
|
||||
self.label = self.label.strip()
|
||||
if not self.label.endswith(":"):
|
||||
if not self.label.endswith(":") and self.type is not FormFieldType.GROUP:
|
||||
self.label += ":"
|
||||
if self.required:
|
||||
self.label += "*"
|
||||
@@ -427,6 +434,9 @@ class FormField:
|
||||
if self.type is FormFieldType.DROPDOWN and self.options is None:
|
||||
raise ValueError("Invalid field definition: Dropdown requires options")
|
||||
|
||||
for child in self.children:
|
||||
child.parent = self
|
||||
|
||||
|
||||
@dc.dataclass(slots=True)
|
||||
class FormFieldDynList:
|
||||
@@ -440,105 +450,119 @@ class FormFieldGroup:
|
||||
fields: Sequence[FormField] | FormFieldDynList
|
||||
|
||||
|
||||
FORM_FIELD_DEF = [
|
||||
FormField(
|
||||
"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("Auftragsdatum", FormFieldType.DATE, True),
|
||||
FormField("Startdatum", FormFieldType.DATE, True),
|
||||
FormField(
|
||||
"MS Datum 1",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
"",
|
||||
fill_value="26.07.2026",
|
||||
readonly=True,
|
||||
),
|
||||
FormField(
|
||||
"MS Datum 2",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
"",
|
||||
fill_value="30.08.2026",
|
||||
readonly=False,
|
||||
),
|
||||
FormField(
|
||||
"Wichtige Notizen",
|
||||
FormFieldType.LONGTEXT,
|
||||
True,
|
||||
"Text eingeben...",
|
||||
),
|
||||
]
|
||||
# @dc.dataclass(slots=True)
|
||||
# class FormFieldRegistry:
|
||||
# widget: QWidget
|
||||
# form_field: FormField
|
||||
|
||||
FORM_FIELD_DEF2 = [
|
||||
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("Auftragsdatum", FormFieldType.DATE, True),
|
||||
FormField("Startdatum", FormFieldType.DATE, True),
|
||||
FormField(
|
||||
"MS Datum 1",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
"",
|
||||
fill_value="26.07.2026",
|
||||
readonly=True,
|
||||
),
|
||||
FormField(
|
||||
"MS Datum 2",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
"",
|
||||
fill_value="30.08.2026",
|
||||
readonly=False,
|
||||
),
|
||||
FormField(
|
||||
"Wichtige Notizen",
|
||||
FormFieldType.LONGTEXT,
|
||||
True,
|
||||
"Text eingeben...",
|
||||
),
|
||||
]
|
||||
|
||||
class WidgetRegistryEntry(TypedDict):
|
||||
widget: QWidget
|
||||
form_field: FormField
|
||||
|
||||
|
||||
WidgetRegistry: TypeAlias = dict[str, WidgetRegistryEntry]
|
||||
|
||||
|
||||
# FORM_FIELD_DEF = [
|
||||
# FormField(
|
||||
# "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("Auftragsdatum", FormFieldType.DATE, True),
|
||||
# FormField("Startdatum", FormFieldType.DATE, True),
|
||||
# FormField(
|
||||
# "MS Datum 1",
|
||||
# FormFieldType.DATE,
|
||||
# False,
|
||||
# "",
|
||||
# fill_value="26.07.2026",
|
||||
# readonly=True,
|
||||
# ),
|
||||
# FormField(
|
||||
# "MS Datum 2",
|
||||
# FormFieldType.DATE,
|
||||
# False,
|
||||
# "",
|
||||
# fill_value="30.08.2026",
|
||||
# readonly=False,
|
||||
# ),
|
||||
# FormField(
|
||||
# "Wichtige Notizen",
|
||||
# FormFieldType.LONGTEXT,
|
||||
# True,
|
||||
# "Text eingeben...",
|
||||
# ),
|
||||
# ]
|
||||
|
||||
# FORM_FIELD_DEF2 = [
|
||||
# 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("Auftragsdatum", FormFieldType.DATE, True),
|
||||
# FormField("Startdatum", FormFieldType.DATE, True),
|
||||
# FormField(
|
||||
# "MS Datum 1",
|
||||
# FormFieldType.DATE,
|
||||
# False,
|
||||
# "",
|
||||
# fill_value="26.07.2026",
|
||||
# readonly=True,
|
||||
# ),
|
||||
# FormField(
|
||||
# "MS Datum 2",
|
||||
# FormFieldType.DATE,
|
||||
# False,
|
||||
# "",
|
||||
# fill_value="30.08.2026",
|
||||
# readonly=False,
|
||||
# ),
|
||||
# FormField(
|
||||
# "Wichtige Notizen",
|
||||
# FormFieldType.LONGTEXT,
|
||||
# True,
|
||||
# "Text eingeben...",
|
||||
# ),
|
||||
# ]
|
||||
|
||||
|
||||
FORM_FIELDS_CONTACT_PERSON = [
|
||||
@@ -991,13 +1015,27 @@ FORM_FIELDS_LANGUAGES = [
|
||||
),
|
||||
]
|
||||
|
||||
FORM_DYN_LIST = FormFieldDynList("Dynamische Liste", FORM_FIELDS_SCHOOL)
|
||||
# FORM_DYN_LIST = FormFieldDynList("Dynamische Liste", FORM_FIELDS_SCHOOL)
|
||||
|
||||
|
||||
FORM_FIELD_GROUPS = [
|
||||
FormFieldGroup(
|
||||
FORM_FIELDS = [
|
||||
# FormFieldGroup(
|
||||
# "Status && Projektrelevanz",
|
||||
# [
|
||||
# FormField(
|
||||
# "Projektrelevanz",
|
||||
# FormFieldType.DROPDOWN,
|
||||
# required=True,
|
||||
# options=["ja", "nein"],
|
||||
# fill_value="nein",
|
||||
# ),
|
||||
# ],
|
||||
# ),
|
||||
FormField(
|
||||
"Status && Projektrelevanz",
|
||||
[
|
||||
FormFieldType.GROUP,
|
||||
key="state_relevance",
|
||||
children=[
|
||||
FormField(
|
||||
"Projektrelevanz",
|
||||
FormFieldType.DROPDOWN,
|
||||
@@ -1007,14 +1045,20 @@ FORM_FIELD_GROUPS = [
|
||||
),
|
||||
],
|
||||
),
|
||||
FormFieldGroup("Daten Kontaktperson", FORM_FIELDS_CONTACT_PERSON),
|
||||
FormFieldGroup("Stammdaten (ERGÄNZUNG KINDERALTER DYNAMISCH)", FORM_FIELDS_MASTER_DATA),
|
||||
FormFieldGroup("weitere Informationen", FORM_FIELDS_ADDITIONAL_DATA),
|
||||
FormFieldGroup("Schule (MIT PLUS-ERWEITERUNG)", FORM_FIELDS_SCHOOL),
|
||||
FormFieldGroup("Studium/Ausbildung (MIT PLUS-ERWEITERUNG)", FORM_FIELDS_HIGHER_EDUCATION),
|
||||
FormFieldGroup("Arbeitserfahrung (MIT PLUS-ERWEITERUNG)", FORM_FIELDS_WORK_EXPERIENCE),
|
||||
FormFieldGroup("Sprachkenntnisse (MIT PLUS-ERWEITERUNG)", FORM_FIELDS_LANGUAGES),
|
||||
FormFieldGroup("Dynamische Liste", FORM_DYN_LIST),
|
||||
FormField(
|
||||
"Daten Kontaktperson",
|
||||
FormFieldType.GROUP,
|
||||
key="data_contact_person",
|
||||
children=FORM_FIELDS_CONTACT_PERSON,
|
||||
),
|
||||
# FormFieldGroup("Daten Kontaktperson", FORM_FIELDS_CONTACT_PERSON),
|
||||
# FormFieldGroup("Stammdaten (ERGÄNZUNG KINDERALTER DYNAMISCH)", FORM_FIELDS_MASTER_DATA),
|
||||
# FormFieldGroup("weitere Informationen", FORM_FIELDS_ADDITIONAL_DATA),
|
||||
# FormFieldGroup("Schule (MIT PLUS-ERWEITERUNG)", FORM_FIELDS_SCHOOL),
|
||||
# FormFieldGroup("Studium/Ausbildung (MIT PLUS-ERWEITERUNG)", FORM_FIELDS_HIGHER_EDUCATION),
|
||||
# FormFieldGroup("Arbeitserfahrung (MIT PLUS-ERWEITERUNG)", FORM_FIELDS_WORK_EXPERIENCE),
|
||||
# FormFieldGroup("Sprachkenntnisse (MIT PLUS-ERWEITERUNG)", FORM_FIELDS_LANGUAGES),
|
||||
# FormFieldGroup("Dynamische Liste", FORM_DYN_LIST),
|
||||
# FormFieldGroup("Test-2", FORM_FIELD_DEF2),
|
||||
]
|
||||
|
||||
@@ -1022,7 +1066,7 @@ FORM_FIELD_GROUPS = [
|
||||
class AutoForm(QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
form_field_groups: Sequence[FormFieldGroup],
|
||||
form_fields: Sequence[FormField],
|
||||
add_buttons: bool = True,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
@@ -1042,25 +1086,52 @@ class AutoForm(QWidget):
|
||||
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_field_groups = form_field_groups
|
||||
self.top_level_form_layout = QFormLayout()
|
||||
self.main_layout.addLayout(self.top_level_form_layout)
|
||||
self.top_level_form_layout.setSpacing(10)
|
||||
|
||||
self.widgets: dict[str, QWidget] = {}
|
||||
self.form_fields = form_fields
|
||||
|
||||
for fg in form_field_groups:
|
||||
_sub = fg.fields
|
||||
if isinstance(_sub, FormFieldDynList):
|
||||
widget = DynamicListWidget(_sub.fields, _sub.label)
|
||||
else:
|
||||
widget = FormGroupWidget(_sub, fg.label)
|
||||
self.main_layout.addWidget(widget)
|
||||
# TODO widget list is not updated with dynamic lists
|
||||
# self._add_widgets(widget.widgets)
|
||||
# self.widgets.update(form_group.widgets)
|
||||
self.widget_registry: WidgetRegistry = {}
|
||||
|
||||
self._build_ui_recursively(
|
||||
self.form_fields,
|
||||
self.top_level_form_layout,
|
||||
self.widget_registry,
|
||||
)
|
||||
|
||||
# TODO: REMOVE
|
||||
# for fg in form_field_groups:
|
||||
# _sub = fg.fields
|
||||
# if isinstance(_sub, FormFieldDynList):
|
||||
# widget = DynamicListWidget(_sub.fields, _sub.label)
|
||||
# else:
|
||||
# widget = FormGroupWidget(_sub, fg.label)
|
||||
# self.main_layout.addWidget(widget)
|
||||
# TODO widget list is not updated with dynamic lists
|
||||
# self._add_widgets(widget.widgets)
|
||||
# self.widgets.update(form_group.widgets)
|
||||
# print("------------->>> Widget Registry")
|
||||
# pprint(self.widget_registry)
|
||||
|
||||
# buttons
|
||||
self.add_buttons = add_buttons
|
||||
@@ -1086,18 +1157,174 @@ class AutoForm(QWidget):
|
||||
self.reset_btn.clicked.connect(self.reset_form)
|
||||
self.layout_btn.addWidget(self.reset_btn)
|
||||
|
||||
def _add_widgets(
|
||||
# def _add_widgets(
|
||||
# self,
|
||||
# widgets: dict[str, QWidget],
|
||||
# ) -> None:
|
||||
# current_keys = set(self.widget_registry.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.widget_registry.update(widgets)
|
||||
|
||||
def _build_ui_recursively(
|
||||
self,
|
||||
widgets: dict[str, QWidget],
|
||||
schema: Sequence[FormField],
|
||||
parent_layout: QFormLayout,
|
||||
widget_registry: WidgetRegistry,
|
||||
prefix: str = "",
|
||||
) -> None:
|
||||
current_keys = set(self.widgets.keys())
|
||||
new_keys = set(widgets.keys())
|
||||
shared_keys = current_keys.intersection(new_keys)
|
||||
for field in schema:
|
||||
full_key = f"{prefix}.{field.key}" if prefix else field.key
|
||||
|
||||
if shared_keys:
|
||||
raise ValueError(f"Tried to add fields with already assigned keys: {shared_keys}")
|
||||
widget: QWidget | None = None
|
||||
|
||||
self.widgets.update(widgets)
|
||||
match field.type:
|
||||
case FormFieldType.GROUP:
|
||||
group_box = QGroupBox(field.label)
|
||||
group_layout = QFormLayout(group_box)
|
||||
self._build_ui_recursively(
|
||||
schema=field.children,
|
||||
parent_layout=group_layout,
|
||||
widget_registry=widget_registry,
|
||||
prefix=f"{full_key}",
|
||||
)
|
||||
parent_layout.addRow(group_box)
|
||||
|
||||
case FormFieldType.TEXT:
|
||||
widget = QLineEdit()
|
||||
if field.placeholder:
|
||||
widget.setPlaceholderText(field.placeholder)
|
||||
if field.fill_value:
|
||||
widget.setText(field.fill_value)
|
||||
if field.readonly:
|
||||
widget.setReadOnly(True) # Falls es ein Fixwert ist
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
|
||||
if field.tooltip:
|
||||
tooltip_layout = self._add_tooltip(widget, field.tooltip)
|
||||
parent_layout.addRow(field.label, tooltip_layout)
|
||||
else:
|
||||
parent_layout.addRow(field.label, widget)
|
||||
|
||||
case FormFieldType.LONGTEXT:
|
||||
widget = QPlainTextEdit()
|
||||
widget.setMaximumHeight(80) # Kompakte Höhe für Formulare
|
||||
if field.placeholder:
|
||||
widget.setPlaceholderText(field.placeholder)
|
||||
if field.readonly:
|
||||
widget.setReadOnly(True) # Falls es ein Fixwert ist
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
|
||||
if field.tooltip:
|
||||
tooltip_layout = self._add_tooltip(widget, field.tooltip)
|
||||
parent_layout.addRow(field.label, tooltip_layout)
|
||||
else:
|
||||
parent_layout.addRow(field.label, widget)
|
||||
|
||||
case FormFieldType.DATE:
|
||||
widget = QDateEdit() # Oder QDateEdit
|
||||
widget.setCalendarPopup(True)
|
||||
cal = widget.calendarWidget()
|
||||
if cal:
|
||||
cal.setFirstDayOfWeek(Qt.DayOfWeek.Monday)
|
||||
cal.setGridVisible(True)
|
||||
cal.setStyleSheet("""
|
||||
QCalendarWidget QAbstractItemView {
|
||||
selection-background-color: #2980b9;
|
||||
selection-color: white;
|
||||
}
|
||||
""")
|
||||
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())
|
||||
|
||||
if field.readonly:
|
||||
widget = QLineEdit()
|
||||
widget.setReadOnly(True) # Falls es ein Fixwert ist
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
if field.fill_value:
|
||||
widget.setText(field.fill_value)
|
||||
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
|
||||
if field.tooltip:
|
||||
tooltip_layout = self._add_tooltip(widget, field.tooltip)
|
||||
parent_layout.addRow(field.label, tooltip_layout)
|
||||
else:
|
||||
parent_layout.addRow(field.label, widget)
|
||||
|
||||
case FormFieldType.DROPDOWN:
|
||||
widget = QComboBox()
|
||||
assert field.options
|
||||
widget.addItem("--- Bitte wählen ---")
|
||||
widget.addItems(field.options)
|
||||
if field.placeholder:
|
||||
widget.setPlaceholderText(field.placeholder)
|
||||
if field.fill_value:
|
||||
widget.setCurrentText(field.fill_value)
|
||||
else:
|
||||
widget.setCurrentIndex(0)
|
||||
if field.readonly:
|
||||
widget.setEnabled(False)
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
|
||||
if field.tooltip:
|
||||
tooltip_layout = self._add_tooltip(widget, field.tooltip)
|
||||
parent_layout.addRow(field.label, tooltip_layout)
|
||||
else:
|
||||
parent_layout.addRow(field.label, widget)
|
||||
|
||||
# case FormFieldType.DYNAMIC_LIST:
|
||||
# widget = DynamicListWidget(field.label)
|
||||
|
||||
case _:
|
||||
raise NotImplementedError(f"Not supported field type: {field.type.value}")
|
||||
|
||||
@staticmethod
|
||||
def _add_tooltip(
|
||||
widget: QWidget,
|
||||
tooltip: str,
|
||||
) -> QHBoxLayout:
|
||||
field_layout = QHBoxLayout()
|
||||
field_layout.setContentsMargins(0, 0, 0, 0)
|
||||
field_layout.setSpacing(5)
|
||||
info_btn = QPushButton("ℹ️")
|
||||
info_btn.setFixedSize(28, 27)
|
||||
info_btn.setFlat(True)
|
||||
info_btn.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
info_btn.setToolTip(tooltip)
|
||||
field_layout.addWidget(widget)
|
||||
field_layout.addWidget(info_btn)
|
||||
|
||||
return field_layout
|
||||
|
||||
def _disable_save(self) -> None:
|
||||
self.save_btn.setEnabled(False)
|
||||
@@ -1118,46 +1345,48 @@ class AutoForm(QWidget):
|
||||
# time.sleep(0.5)
|
||||
|
||||
# Wir gehen unsere Feld-Definitionen durch
|
||||
for fg in self.form_field_groups:
|
||||
if isinstance(fg.fields, DynamicListWidget):
|
||||
# for fg in self.form_fields:
|
||||
# if isinstance(fg.fields, DynamicListWidget):
|
||||
# continue
|
||||
|
||||
for key, registry_entry in self.widget_registry.items(): # type: ignore
|
||||
# widget = self.widget_registry[field.key]
|
||||
widget = registry_entry["widget"]
|
||||
form_field = registry_entry["form_field"]
|
||||
|
||||
# 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 form_field.readonly:
|
||||
widget.setStyleSheet("")
|
||||
|
||||
# 2. Ist es überhaupt ein Pflichtfeld?
|
||||
if not form_field.required:
|
||||
continue
|
||||
|
||||
for field in fg.fields: # type: ignore
|
||||
widget = self.widgets[field.key]
|
||||
is_empty = False
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
if not widget.text().strip():
|
||||
is_empty = True
|
||||
|
||||
# 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("")
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
if not widget.toPlainText().strip():
|
||||
is_empty = True
|
||||
|
||||
# 2. Ist es überhaupt ein Pflichtfeld?
|
||||
if not field.required:
|
||||
continue
|
||||
if not is_empty:
|
||||
continue
|
||||
|
||||
is_empty = False
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
if not widget.text().strip():
|
||||
is_empty = True
|
||||
error = form_field.label.replace("*", "").replace(":", "")
|
||||
if form_field.parent is not None:
|
||||
error = f"{form_field.parent.label}: {error}"
|
||||
errors.append(error)
|
||||
|
||||
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;
|
||||
""")
|
||||
# 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:
|
||||
@@ -1177,43 +1406,50 @@ class AutoForm(QWidget):
|
||||
self._enable_save()
|
||||
|
||||
def reset_form(self) -> None:
|
||||
for fg in self.form_field_groups:
|
||||
if isinstance(fg.fields, DynamicListWidget):
|
||||
for key, registry_entry in self.widget_registry.items(): # type: ignore
|
||||
# widget = self.widget_registry[field.key]
|
||||
widget = registry_entry["widget"]
|
||||
form_field = registry_entry["form_field"]
|
||||
|
||||
# for fg in self.form_fields:
|
||||
# if isinstance(fg.fields, DynamicListWidget):
|
||||
# continue
|
||||
|
||||
# for field in fg.fields: # type: ignore
|
||||
# widget = self.widget_registry[field.key]
|
||||
|
||||
if form_field.readonly:
|
||||
continue
|
||||
|
||||
for field in fg.fields: # type: ignore
|
||||
widget = self.widgets[field.key]
|
||||
if isinstance(widget, QLineEdit):
|
||||
widget.clear()
|
||||
if form_field.fill_value:
|
||||
widget.setText(form_field.fill_value)
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
widget.clear()
|
||||
elif isinstance(widget, QDateEdit):
|
||||
if form_field.fill_value:
|
||||
set_date = QDate.fromString(form_field.fill_value, "dd.MM.yyyy")
|
||||
if not set_date.isValid():
|
||||
raise ValueError(
|
||||
f"Could not parse date field value >{form_field.fill_value}<"
|
||||
)
|
||||
widget.setDate(set_date)
|
||||
else:
|
||||
widget.setDate(QDate.currentDate())
|
||||
elif isinstance(widget, QComboBox):
|
||||
if form_field.fill_value:
|
||||
widget.setCurrentText(form_field.fill_value)
|
||||
else:
|
||||
widget.setCurrentIndex(0)
|
||||
|
||||
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("")
|
||||
widget.setStyleSheet("")
|
||||
|
||||
def get_form_data(self) -> ...:
|
||||
raise NotImplementedError()
|
||||
|
||||
data = {}
|
||||
for key, widget in self.widgets.items():
|
||||
for key, widget in self.widget_registry.items():
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
data[key] = widget.text()
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
@@ -1839,7 +2075,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(AutoForm(FORM_FIELD_GROUPS))
|
||||
container_layout.addWidget(AutoForm(FORM_FIELDS))
|
||||
|
||||
container_layout.addSpacing(30)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user