extended loading

This commit is contained in:
2026-05-20 10:09:05 +02:00
parent e46e494500
commit e97999713e

View File

@@ -16,7 +16,16 @@ from typing import Annotated, Any, Protocol, TypeAlias, TypedDict
import babel import babel
from pydantic import BaseModel, ConfigDict, EmailStr, Field, ValidationError, field_validator from pydantic import BaseModel, ConfigDict, EmailStr, Field, ValidationError, field_validator
from PySide6.QtCore import QDate, QModelIndex, QStringListModel, Qt, QTimer, Signal from PySide6.QtCore import (
QDate,
QEvent,
QModelIndex,
QObject,
QStringListModel,
Qt,
QTimer,
Signal,
)
from PySide6.QtGui import QAction, QStandardItem, QStandardItemModel from PySide6.QtGui import QAction, QStandardItem, QStandardItemModel
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QApplication, QApplication,
@@ -193,8 +202,12 @@ def pprint_registry(widget_registry: WidgetRegistry) -> None:
class CustomForm(Protocol): class CustomForm(Protocol):
def get_form_data(self) -> dict[str, Any]: ... def get_form_data(self) -> dict[str, Any]: ...
def set_form_data(self, data: Any) -> None: ...
def reset_form(self) -> None: ... def reset_form(self) -> None: ...
def validate_form_data(self) -> list[str]: ...
def _build_ui_recursively( def _build_ui_recursively(
schema: Sequence[FormField], schema: Sequence[FormField],
@@ -202,6 +215,7 @@ def _build_ui_recursively(
widget_registry: WidgetRegistry, widget_registry: WidgetRegistry,
prefix: str = "", prefix: str = "",
) -> None: ) -> None:
no_scroll_filter = NoScrollFilter(parent_layout)
for field in schema: for field in schema:
full_key = f"{prefix}.{field.key}" if prefix else field.key full_key = f"{prefix}.{field.key}" if prefix else field.key
widget: QWidget widget: QWidget
@@ -232,6 +246,7 @@ def _build_ui_recursively(
"widget": widget, "widget": widget,
"form_field": field, "form_field": field,
} }
widget.installEventFilter(no_scroll_filter)
if field.tooltip: if field.tooltip:
tooltip_layout = _add_tooltip(widget, field.tooltip) tooltip_layout = _add_tooltip(widget, field.tooltip)
@@ -252,6 +267,7 @@ def _build_ui_recursively(
"widget": widget, "widget": widget,
"form_field": field, "form_field": field,
} }
widget.installEventFilter(no_scroll_filter)
if field.tooltip: if field.tooltip:
tooltip_layout = _add_tooltip(widget, field.tooltip) tooltip_layout = _add_tooltip(widget, field.tooltip)
@@ -293,6 +309,7 @@ def _build_ui_recursively(
"widget": widget, "widget": widget,
"form_field": field, "form_field": field,
} }
widget.installEventFilter(no_scroll_filter)
if field.tooltip: if field.tooltip:
tooltip_layout = _add_tooltip(widget, field.tooltip) tooltip_layout = _add_tooltip(widget, field.tooltip)
@@ -321,6 +338,7 @@ def _build_ui_recursively(
"widget": widget, "widget": widget,
"form_field": field, "form_field": field,
} }
widget.installEventFilter(no_scroll_filter)
if field.tooltip: if field.tooltip:
tooltip_layout = _add_tooltip(widget, field.tooltip) tooltip_layout = _add_tooltip(widget, field.tooltip)
@@ -359,6 +377,7 @@ def _build_ui_recursively(
"widget": widget, "widget": widget,
"form_field": field, "form_field": field,
} }
widget.installEventFilter(no_scroll_filter)
if field.tooltip: if field.tooltip:
tooltip_layout = _add_tooltip(widget, field.tooltip) tooltip_layout = _add_tooltip(widget, field.tooltip)
@@ -618,19 +637,11 @@ class Grunderfassung_Unternehmen(BaseModel):
Projektrelevanz: Grunderfassung_Projektrelevanz | None = None Projektrelevanz: Grunderfassung_Projektrelevanz | None = None
Kontaktperson: Grunderfassung_Kontaktperson | None = None Kontaktperson: Grunderfassung_Kontaktperson | None = None
Stammdaten: Grunderfassung_Stammdaten | None = None Stammdaten: Grunderfassung_Stammdaten | None = None
WeitereInfos: Grunderfassung_WeitereInfos | None = None
Schulbildung: list[Grunderfassung_Schulbildung] | None = None Schulbildung: list[Grunderfassung_Schulbildung] | None = None
HoehereBildung: list[Grunderfassung_HoehereBildung] | None = None
Arbeitserfahrung: list[Grunderfassung_Arbeitserfahrung] | None = None
class Grunderfassung_Schulbildung(BaseModel): Sprachkenntnisse: list[Grunderfassung_Sprachen] | None = None
model_config = ConfigDict(str_strip_whitespace=True)
SB_abschluss: str | None
SB_abschlussgrad: str | None
SB_schule: str | None
SB_ort: str | None
SB_land: str | None
SB_abschlussjahr: str | None
SB_bemerkungsfeld: str | None
class Grunderfassung_Projektrelevanz(BaseModel): class Grunderfassung_Projektrelevanz(BaseModel):
@@ -719,6 +730,65 @@ class Grunderfassung_Stammdaten_AnzahlKinder(BaseModel):
alter: list[ValidAge | None] | None = None alter: list[ValidAge | None] | None = None
class Grunderfassung_WeitereInfos(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
WI_deutsch_sprache: str | None
WI_aufenthaltstitel: str | None
WI_gueltigkeit_aufenthaltstitel: datetime.date | None
WI_arbeitsstatus: str | None
WI_meldung_institution: str | None
class Grunderfassung_Schulbildung(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
SB_abschluss: str | None
SB_abschlussgrad: str | None
SB_schule: str | None
SB_ort: str | None
SB_land: str | None
SB_abschlussjahr: str | None
SB_bemerkungsfeld: str | None
class Grunderfassung_HoehereBildung(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
HB_anerkennung: str | None
HB_abschlussgrad: str | None
HB_abschlussgrad_dokument: str | None
HB_organisation: str | None
HB_beruf: str | None
HB_land: str | None
HB_ort: str | None
HB_abschlussjahr: str | None
HB_bemerkungsfeld: str | None
class Grunderfassung_Arbeitserfahrung(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
AE_branche: str | None
AE_bezeichnung: str | None
AE_funktion: str | None
AE_unternehmen: str | None
AE_land: str | None
AE_zeitspanne: str | None
AE_beschaeftigungsart: str | None
AE_bemerkungsfeld: str | None
class Grunderfassung_Sprachen(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
SP_sprache: str | None
SP_niveau: str | None
SP_nachweis: str | None
SP_art_nachweis: str | None
SP_datum_nachweis: datetime.date | None
class CompanyForm_Search(QWidget): class CompanyForm_Search(QWidget):
company_selected = Signal(int) company_selected = Signal(int)
@@ -1032,21 +1102,21 @@ class WidgetRegistryEntry(TypedDict):
WidgetRegistry: TypeAlias = dict[str, WidgetRegistryEntry] WidgetRegistry: TypeAlias = dict[str, WidgetRegistryEntry]
def search_widget_by_key( def search_widgets_by_key(
widget_registry: WidgetRegistry, widget_registry: WidgetRegistry,
key_part: str, key_part: str,
) -> QWidget | None: ) -> list[WidgetRegistryEntry]:
""" """
needed for custom logic of auto-built forms, needed for custom logic of auto-built forms,
search for specific keys and obtain the widget to assign search for specific keys and obtain the widget to assign
special logic or callbacks to them special logic or callbacks to them
""" """
hit: QWidget | None = None hits: list[WidgetRegistryEntry] = []
for key, entry in widget_registry.items(): for key, entry in widget_registry.items():
if key_part in key: if key_part in key:
hit = entry["widget"] hits.append(entry)
return hit return hits
FORM_FIELDS_CONTACT_PERSON = [ FORM_FIELDS_CONTACT_PERSON = [
@@ -1258,14 +1328,6 @@ FORM_FIELDS_MASTER_DATA = [
required=False, required=False,
tooltip="* Wichtig zu erfragen aufgrund Lebensunterhaltssicherung", tooltip="* Wichtig zu erfragen aufgrund Lebensunterhaltssicherung",
), ),
# FormField(
# "Anzahl Kinder",
# FormFieldType.DYNAMIC_DROPDOWN,
# key="Stammdaten_anzahl_kinder",
# required=False,
# options=[(str(x), None) for x in range(11)],
# tooltip="* Wichtig zu erfragen aufgrund Lebensunterhaltssicherung",
# ),
FormField( FormField(
"Anzahl Kinder", "Anzahl Kinder",
FormFieldType.DYNAMIC_DROPDOWN, FormFieldType.DYNAMIC_DROPDOWN,
@@ -1293,6 +1355,7 @@ FORM_FIELDS_ADDITIONAL_DATA = [
"Deutsch als Kommunikationssprache", "Deutsch als Kommunikationssprache",
FormFieldType.DROPDOWN, FormFieldType.DROPDOWN,
required=False, required=False,
key="WI_deutsch_sprache",
options=[ options=[
("nein", None), ("nein", None),
("ja, als Muttersprache", None), ("ja, als Muttersprache", None),
@@ -1303,6 +1366,7 @@ FORM_FIELDS_ADDITIONAL_DATA = [
"Aufenthaltstitel", "Aufenthaltstitel",
FormFieldType.DROPDOWN, FormFieldType.DROPDOWN,
required=False, required=False,
key="WI_aufenthaltstitel",
options=[ options=[
("anerkannter Flüchtling §§ 22 - 26 AufenthG", None), ("anerkannter Flüchtling §§ 22 - 26 AufenthG", None),
("Aufenthaltsgestattung §55 AufenthG", None), ("Aufenthaltsgestattung §55 AufenthG", None),
@@ -1325,11 +1389,13 @@ FORM_FIELDS_ADDITIONAL_DATA = [
"Gültigkeit Aufenthaltsstatus", "Gültigkeit Aufenthaltsstatus",
FormFieldType.DATE, FormFieldType.DATE,
required=False, required=False,
key="WI_gueltigkeit_aufenthaltstitel",
), ),
FormField( FormField(
"Arbeitsstatus aktuell", "Arbeitsstatus aktuell",
FormFieldType.DROPDOWN, FormFieldType.DROPDOWN,
required=False, required=False,
key="WI_arbeitsstatus",
options=[ options=[
("Arbeitslos", None), ("Arbeitslos", None),
("Ausbildung/Qualifizierung Inland", None), ("Ausbildung/Qualifizierung Inland", None),
@@ -1345,6 +1411,7 @@ FORM_FIELDS_ADDITIONAL_DATA = [
"Gemeldet bei Institutionen ", "Gemeldet bei Institutionen ",
FormFieldType.DROPDOWN, FormFieldType.DROPDOWN,
required=False, required=False,
key="WI_meldung_institution",
options=[ options=[
("bei keiner", None), ("bei keiner", None),
("Jobcenter mit Leistungsbezug", None), ("Jobcenter mit Leistungsbezug", None),
@@ -1399,11 +1466,13 @@ FORM_FIELDS_HIGHER_EDUCATION = [
"Anerkennung", "Anerkennung",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="HB_anerkennung",
), ),
FormField( FormField(
"Abschlussgrad", "Abschlussgrad",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="HB_abschlussgrad",
tooltip=( tooltip=(
"bitte den Titel eingeben z.B. Doktor, Diplom oder " "bitte den Titel eingeben z.B. Doktor, Diplom oder "
"Betriebswirt (Fachschulabschluss)" "Betriebswirt (Fachschulabschluss)"
@@ -1413,16 +1482,19 @@ FORM_FIELDS_HIGHER_EDUCATION = [
"Abschlussgrad laut Dokument", "Abschlussgrad laut Dokument",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="HB_abschlussgrad_dokument",
), ),
FormField( FormField(
"Hochschule / Ausbildungsbetrieb / Berufsschule", "Hochschule / Ausbildungsbetrieb / Berufsschule",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="HB_organisation",
), ),
FormField( FormField(
"Beruf / Fachrichtung", "Beruf / Fachrichtung",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="HB_beruf",
tooltip=( tooltip=(
"bitte spezifizieren z.B. Allgemeinmedizin, Ingenieur Maschinenbau, " "bitte spezifizieren z.B. Allgemeinmedizin, Ingenieur Maschinenbau, "
"technischer Betriebswirt Datenverarbeitung" "technischer Betriebswirt Datenverarbeitung"
@@ -1431,7 +1503,7 @@ FORM_FIELDS_HIGHER_EDUCATION = [
FormField( FormField(
"Land", "Land",
FormFieldType.EXTENDED_DROPDOWN, FormFieldType.EXTENDED_DROPDOWN,
key="country", key="HB_land",
required=False, required=False,
placeholder="Suche...", placeholder="Suche...",
options=COUNTRY_LIST.for_dropdown, options=COUNTRY_LIST.for_dropdown,
@@ -1440,16 +1512,19 @@ FORM_FIELDS_HIGHER_EDUCATION = [
"Ort", "Ort",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="HB_ort",
), ),
FormField( FormField(
"Abschlussjahr", "Abschlussjahr",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="HB_abschlussjahr",
), ),
FormField( FormField(
"Bemerkungsfeld", "Bemerkungsfeld",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="HB_bemerkungsfeld",
tooltip="z.B. Promotionen oder den Studiengang angeben", tooltip="z.B. Promotionen oder den Studiengang angeben",
), ),
] ]
@@ -1459,17 +1534,20 @@ FORM_FIELDS_WORK_EXPERIENCE = [
"Branche", "Branche",
FormFieldType.DROPDOWN, FormFieldType.DROPDOWN,
required=False, required=False,
key="AE_branche",
options=[("DROPDOWN-LISTE AN ANDERER STELLE DEFINIERT", None)], options=[("DROPDOWN-LISTE AN ANDERER STELLE DEFINIERT", None)],
), ),
FormField( FormField(
"Berufsbezeichnung/Tätigkeit", "Berufsbezeichnung/Tätigkeit",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="AE_bezeichnung",
), ),
FormField( FormField(
"Funktion", "Funktion",
FormFieldType.DROPDOWN, FormFieldType.DROPDOWN,
required=False, required=False,
key="AE_funktion",
options=[ options=[
("Auszubildender", None), ("Auszubildender", None),
("Fachkraft", None), ("Fachkraft", None),
@@ -1487,11 +1565,12 @@ FORM_FIELDS_WORK_EXPERIENCE = [
"Unternehmen", "Unternehmen",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="AE_unternehmen",
), ),
FormField( FormField(
"Land", "Land",
FormFieldType.EXTENDED_DROPDOWN, FormFieldType.EXTENDED_DROPDOWN,
key="country", key="AE_land",
required=False, required=False,
placeholder="Suche...", placeholder="Suche...",
options=COUNTRY_LIST.for_dropdown, options=COUNTRY_LIST.for_dropdown,
@@ -1500,11 +1579,13 @@ FORM_FIELDS_WORK_EXPERIENCE = [
"Zeitspanne (von ... bis ...)", "Zeitspanne (von ... bis ...)",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="AE_zeitspanne",
), ),
FormField( FormField(
"Beschäftsigungsart", "Beschäftsigungsart",
FormFieldType.DROPDOWN, FormFieldType.DROPDOWN,
required=False, required=False,
key="AE_beschaeftigungsart",
options=[ options=[
("Vollzeit", None), ("Vollzeit", None),
("Teilzeit", None), ("Teilzeit", None),
@@ -1516,6 +1597,7 @@ FORM_FIELDS_WORK_EXPERIENCE = [
"Bemerkungsfeld", "Bemerkungsfeld",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="AE_bemerkungsfeld",
), ),
] ]
@@ -1524,11 +1606,13 @@ FORM_FIELDS_LANGUAGES = [
"Sprache", "Sprache",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="SP_sprache",
), ),
FormField( FormField(
"Niveau", "Niveau",
FormFieldType.DROPDOWN, FormFieldType.DROPDOWN,
required=False, required=False,
key="SP_niveau",
options=[ options=[
("A1", None), ("A1", None),
("A2", None), ("A2", None),
@@ -1542,6 +1626,7 @@ FORM_FIELDS_LANGUAGES = [
"Nachweis", "Nachweis",
FormFieldType.DROPDOWN, FormFieldType.DROPDOWN,
required=False, required=False,
key="SP_nachweis",
options=[ options=[
("vorhanden", None), ("vorhanden", None),
("nicht vorhanden", None), ("nicht vorhanden", None),
@@ -1551,93 +1636,74 @@ FORM_FIELDS_LANGUAGES = [
"Art des Nachweises (NUR WENN VORHANDEN)", "Art des Nachweises (NUR WENN VORHANDEN)",
FormFieldType.TEXT, FormFieldType.TEXT,
required=False, required=False,
key="SP_art_nachweis",
), ),
FormField( FormField(
"Datum des Nachweises (NUR WENN VORHANDEN)", "Datum des Nachweises (NUR WENN VORHANDEN)",
FormFieldType.DATE, FormFieldType.DATE,
required=False, required=False,
key="SP_datum_nachweis",
), ),
] ]
FORM_FIELDS = [ FORM_FIELDS = [
# FormField( FormField(
# "Status && Projektrelevanz", "Status && Projektrelevanz",
# FormFieldType.GROUP, FormFieldType.GROUP,
# key="Projektrelevanz", key="Projektrelevanz",
# children=[ children=[
# FormField( FormField(
# "Projektrelevanz", "Projektrelevanz",
# FormFieldType.DROPDOWN, FormFieldType.DROPDOWN,
# key="Projektrelevanz_relevanz", key="Projektrelevanz_relevanz",
# required=True, required=True,
# options=[("ja", None), ("nein", None)], options=[("ja", None), ("nein", None)],
# ), ),
# ], ],
# ), ),
# FormField( FormField(
# "Daten Kontaktperson", "Daten Kontaktperson",
# FormFieldType.GROUP, FormFieldType.GROUP,
# key="Kontaktperson", key="Kontaktperson",
# children=FORM_FIELDS_CONTACT_PERSON, children=FORM_FIELDS_CONTACT_PERSON,
# ), ),
FormField( FormField(
"Stammdaten", "Stammdaten",
FormFieldType.GROUP, FormFieldType.GROUP,
key="Stammdaten", key="Stammdaten",
children=FORM_FIELDS_MASTER_DATA, children=FORM_FIELDS_MASTER_DATA,
), ),
FormField(
"Weitere Informationen",
FormFieldType.GROUP,
key="WeitereInfos",
children=FORM_FIELDS_ADDITIONAL_DATA,
),
FormField( FormField(
"Schulbildung", "Schulbildung",
FormFieldType.DYNAMIC_LIST, FormFieldType.DYNAMIC_LIST,
children=FORM_FIELDS_SCHOOL, children=FORM_FIELDS_SCHOOL,
key="Schulbildung", key="Schulbildung",
), ),
# FormField( FormField(
# "Test Länderauswahl", "Studium/Ausbildung",
# FormFieldType.GROUP, FormFieldType.DYNAMIC_LIST,
# key="countries", children=FORM_FIELDS_HIGHER_EDUCATION,
# children=[ key="HoehereBildung",
# FormField( ),
# "Länderauswahl", FormField(
# FormFieldType.EXTENDED_DROPDOWN, "Arbeitserfahrung",
# key="country", FormFieldType.DYNAMIC_LIST,
# required=True, children=FORM_FIELDS_WORK_EXPERIENCE,
# placeholder="Suche...", key="Arbeitserfahrung",
# options=COUNTRY_LIST.for_dropdown, ),
# ), FormField(
# ], "Sprachkenntnisse",
# ), FormFieldType.DYNAMIC_LIST,
# FormField( children=FORM_FIELDS_LANGUAGES,
# "Anzahl Kinder (dynamischer Dropdown)", key="Sprachkenntnisse",
# FormFieldType.DYNAMIC_DROPDOWN, ),
# required=False,
# options=[(str(x), None) for x in range(11)],
# tooltip="* Wichtig zu erfragen aufgrund Lebensunterhaltssicherung",
# key="DynamicDropdown",
# children=[
# FormField(
# "Anzahl Kinder",
# FormFieldType.DROPDOWN,
# required=False,
# options=[(str(x), None) for x in range(11)],
# tooltip="* Wichtig zu erfragen aufgrund Lebensunterhaltssicherung",
# key="MainDropdown",
# children=[
# FormField("Alter Kind", FormFieldType.TEXT),
# ],
# ),
# ],
# ),
# 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),
] ]
@@ -1678,6 +1744,8 @@ class AutoForm(QWidget):
image: none; image: none;
} }
""") """)
# --- special funtionality ---
self.no_scroll_filter = NoScrollFilter(self)
# --- LAYOUT --- # --- LAYOUT ---
self.main_layout = QVBoxLayout(self) self.main_layout = QVBoxLayout(self)
@@ -1740,11 +1808,11 @@ class AutoForm(QWidget):
self.save_btn.setText(self.save_btn_txt_enabled) self.save_btn.setText(self.save_btn_txt_enabled)
def on_load_clicked(self) -> None: def on_load_clicked(self) -> None:
# TODO change logic to database backend
print(">>>> LOAD CLICKED") print(">>>> LOAD CLICKED")
loaded_data = load_pydantic_model_dict() loaded_data = load_pydantic_model_dict()
# print("Loaded dictionary:") # print("Loaded dictionary:")
# pprint(loaded) # pprint(loaded)
# print(">>>> Widget registry:") # print(">>>> Widget registry:")
# pprint_registry(self.widget_registry) # pprint_registry(self.widget_registry)
@@ -1755,48 +1823,11 @@ class AutoForm(QWidget):
key_path = key.split(".") key_path = key.split(".")
value = _get_nested(loaded_data, key_path) value = _get_nested(loaded_data, key_path)
# print(f"Key: {key}") set_widget_value(widget, value)
# print(f"Value: {value}")
# Wenn kein Wert da ist (z.B. weil er optional ist), überspringen wir das Feld
if value is None:
continue
elif value is True:
value = "ja"
elif value is False:
value = "nein"
# 2. Das Widget entsprechend seinem Typ füllen
if isinstance(widget, QLineEdit):
widget.setText(str(value)) # str() zur Sicherheit, falls es ein int ist
elif isinstance(widget, QComboBox):
# MEGA WICHTIG: Wir suchen nicht den Text, sondern die unsichtbaren Daten!
# (z.B. suchen wir nach dem ISO-Code 'DE' oder der ID 1042)
index = widget.findData(value)
if index >= 0:
widget.setCurrentIndex(index)
elif isinstance(widget, (DynamicListWidget, DynamicDropdownWidget)):
print("\n-----------------\n\nCustom widget, skip...")
print(f"Key: {key}")
print("Widget type: ", type(widget).__name__)
print("current value or data format: ")
pprint(value)
if hasattr(widget, "set_form_data"):
widget.set_form_data(value) # type: ignore
continue
# elif hasattr(widget, "set_data"):
# # Wenn es eins unserer Custom-Widgets ist (Dropdown-Liste, Dynamische Liste)
# # übergeben wir die Liste einfach an das Widget selbst!
# widget.set_data(wert)
def on_save_clicked(self) -> None: def on_save_clicked(self) -> None:
self._disable_save() self._disable_save()
errors = validate_form_data(self.widget_registry) errors = self.validate_form_data()
if errors: if errors:
# errors: abort saving and show pop up window # errors: abort saving and show pop up window
@@ -1813,54 +1844,47 @@ class AutoForm(QWidget):
print("Erfolg! Alle Daten sind valide.") print("Erfolg! Alle Daten sind valide.")
print("Get form data call...") print("Get form data call...")
form_data = self.get_form_data() form_data = self.get_form_data()
# post_proc_1 = form_data["Stammdaten"]["Stammdaten_anzahl_kinder"]
# Stammdaten_anzahl_kinder = post_proc_1[0]["Stammdaten_anzahl_kinder-[0]"][
# "'Stammdaten_anzahl_kinder'"
# ]
# Stammdaten_alter_kinder: list[str] = []
# if len(post_proc_1) > 1:
# for i in range(1, len(post_proc_1)):
# content = post_proc_1[i]
print("------------>>>>>>>>> Get form data") print("------------>>>>>>>>> Get form data")
pprint(form_data) pprint(form_data)
print("------------>>>>>>>>> Call Pydantic") print("\n------------>>>>>>>>> Call Pydantic")
try: try:
validated_data = Grunderfassung_Unternehmen(**form_data) validated_data = Grunderfassung_Unternehmen(**form_data)
# validated_data = Grunderfassung_Stammdaten(**form_data["Stammdaten"])
pprint(validated_data.model_dump()) pprint(validated_data.model_dump())
except ValidationError as e: except ValidationError as e:
# 4. Fehler abfangen und in der GUI anzeigen! # catch errors and show them in GUI
fehler_texte = [] fehler_texte = []
# Pydantic liefert eine detaillierte Fehlerliste # Pydantic detailed error list
for error in e.errors(): for error in e.errors():
print(error) print(error)
# error['loc'][0] enthält den Namen des Feldes (z.B. 'email') # error['loc'][0] = name of field
fehlerhaftes_feld = str(error["loc"][0]) fehlerhaftes_feld = str(error["loc"][0])
grund = error["msg"] grund = error["msg"]
fehler_texte.append(f"- {fehlerhaftes_feld}: {grund}") fehler_texte.append(f"- {fehlerhaftes_feld}: {grund}")
# Wir machen das betroffene Widget in PySide rot! # formatting red
# if fehlerhaftes_feld in self.widgets: # if fehlerhaftes_feld in self.widgets:
# self.widgets[fehlerhaftes_feld].setStyleSheet( # self.widgets[fehlerhaftes_feld].setStyleSheet(
# "border: 1px solid red; background: #fef2f2;" # "border: 1px solid red; background: #fef2f2;"
# ) # )
# Dem Nutzer gesammelt mitteilen, was schiefgelaufen ist # tell user what went wrong
QMessageBox.warning(self, "Eingabefehler", "\n".join(fehler_texte)) QMessageBox.warning(self, "Eingabefehler", "\n".join(fehler_texte))
else: else:
# !! keep this code must be called again # !! this code is only called if the 'try' block was successful
self.reset_form() self.reset_form()
save_pydantic_model_dict(validated_data) save_pydantic_model_dict(validated_data)
finally: finally:
# always re-enable save, even if error occurred
self._enable_save() self._enable_save()
# ------------------------------------------------------------ # ------------------------------------------------------------
def validate_form_data(self) -> list[str]:
return validate_form_data(self.widget_registry)
def reset_form(self) -> None: def reset_form(self) -> None:
reset_form(self.widget_registry) reset_form(self.widget_registry)
@@ -1870,6 +1894,54 @@ class AutoForm(QWidget):
return raw_data return raw_data
def set_widget_value(
widget: QWidget,
value: Any,
) -> None:
if value is None:
return
elif value is True:
value = "ja"
elif value is False:
value = "nein"
if isinstance(widget, QLineEdit):
widget.setText(str(value))
elif isinstance(widget, QPlainTextEdit):
widget.setPlainText(str(value))
elif isinstance(widget, QDateEdit):
assert isinstance(value, datetime.date)
set_date = QDate(value.year, value.month, value.day)
if not set_date.isValid():
raise ValueError(f"Could not parse date field value >{value}<")
widget.setDate(set_date)
elif isinstance(widget, QComboBox):
index = widget.findData(value)
if index >= 0:
widget.setCurrentIndex(index)
elif isinstance(widget, DynamicListWidget):
# print("\n-----------------\n\nCustom widget...")
# print(f"Key: {key}")
# print("Widget type: ", type(widget).__name__)
# print("current value or data format: ")
# pprint(value)
assert isinstance(value, list)
widget.set_form_data(value)
elif isinstance(widget, DynamicDropdownWidget):
# print("\n-----------------\n\nCustom widget, skip...")
# print(f"Key: {key}")
# print("Widget type: ", type(widget).__name__)
# print("current value or data format: ")
# pprint(value)
assert isinstance(value, dict)
widget.set_form_data(value)
@dc.dataclass(slots=True) @dc.dataclass(slots=True)
class SubForm: class SubForm:
entry_box: QWidget entry_box: QWidget
@@ -1996,15 +2068,14 @@ class DynamicListWidget(QWidget):
# pprint_registry(self.widget_registry) # pprint_registry(self.widget_registry)
def reset_form(self) -> None: def reset_form(self) -> None:
reset_form(self.widget_registry) while self.sub_forms:
self.remove_entry(self.sub_forms[0])
self.add_entry()
def validate_form_data(self) -> list[str]: def validate_form_data(self) -> list[str]:
return validate_form_data(self.widget_registry) return validate_form_data(self.widget_registry)
def load_form_data(self) -> None:
# TODO add way to load data when initialised (probably with click)
...
def get_form_data(self): def get_form_data(self):
raw_data = get_form_data(self.widget_registry) raw_data = get_form_data(self.widget_registry)
@@ -2018,19 +2089,23 @@ class DynamicListWidget(QWidget):
while self.sub_forms: while self.sub_forms:
self.remove_entry(self.sub_forms[0]) self.remove_entry(self.sub_forms[0])
# empyt default row # empty default row
if not data: if not data:
self.add_entry() self.add_entry()
return return
# TODO rework for sub_data in data:
for entry in data:
self.add_entry() self.add_entry()
current_sub_form = self.sub_forms[-1]
registry_entries = search_widgets_by_key(
self.widget_registry, f"-[{current_sub_form.index}]"
)
for reg_entry in registry_entries:
widget = reg_entry["widget"]
field_def = reg_entry["form_field"]
# get all relevant widgets and fill in the values value = sub_data[field_def.key]
# current_sub_form = self.sub_forms[-1] set_widget_value(widget, value)
# fill in values
class DynamicDropdownWidget(QWidget): class DynamicDropdownWidget(QWidget):
@@ -2163,10 +2238,6 @@ class DynamicDropdownWidget(QWidget):
def validate_form_data(self) -> list[str]: def validate_form_data(self) -> list[str]:
return validate_form_data(self.widget_registry) return validate_form_data(self.widget_registry)
def load_form_data(self) -> None:
# TODO add way to load data when initialised (probably with click)
...
def get_form_data(self): def get_form_data(self):
raw_data = get_form_data(self.widget_registry) raw_data = get_form_data(self.widget_registry)
# each sub form has its own numbered key: We need to get rid of these # each sub form has its own numbered key: We need to get rid of these
@@ -2202,32 +2273,21 @@ class DynamicDropdownWidget(QWidget):
while self.sub_forms: while self.sub_forms:
self._remove_row() self._remove_row()
# empty default line
# !! only dynamic list widget
# if not daten_liste:
# self._add_row()
# return
# fill in value of combobox field # fill in value of combobox field
entries = tuple(self.widget_registry.values()) entries = tuple(self.widget_registry.values())
assert len(entries) == 1 assert len(entries) == 1
widget = entries[0]["widget"] widget = entries[0]["widget"]
assert isinstance(widget, QComboBox) assert isinstance(widget, QComboBox)
value = data["anzahl"] value = data[self.combobox_field.key]
index = widget.findData(value) set_widget_value(widget, value)
if index >= 0:
widget.setCurrentIndex(index)
data_list: list[int | None] | None = data["alter"] data_list: list[int | None] | None = data[self.assigned_form_field.key]
if not data_list: if not data_list:
return return
# now there are as many new sub forms as the saved value for the dropdown # now there are as many new sub forms as the saved value for the dropdown
# assert this # assert this
assert len(data_list) == len(self.sub_forms) assert len(data_list) == len(self.sub_forms)
print(">>>>>>> New Registry of dynamic dropdown:")
pprint_registry(self.widget_registry)
sub_form_index: int = 0 sub_form_index: int = 0
for key, entry in self.widget_registry.items(): for key, entry in self.widget_registry.items():
if "-[0]" in key: if "-[0]" in key:
@@ -2235,13 +2295,28 @@ class DynamicDropdownWidget(QWidget):
continue continue
widget = entry["widget"] widget = entry["widget"]
value = data_list[sub_form_index] value = data_list[sub_form_index]
if value is None: set_widget_value(widget, value)
value = ""
if isinstance(widget, QLineEdit):
widget.setText(str(value))
sub_form_index += 1 sub_form_index += 1
class NoScrollFilter(QObject):
"""disables scrolling in fields which are not in focus"""
def eventFilter(
self,
obj: QObject,
event: QEvent,
) -> bool:
if event.type() == QEvent.Type.Wheel:
# ignored Qt gives event to parent (ScrollArea)
event.ignore()
# event was handled
return True
# propagate other events
return super().eventFilter(obj, event)
class ClickableCell(QFrame): class ClickableCell(QFrame):
"""cell in the table on the startup screen""" """cell in the table on the startup screen"""