diff --git a/pdm.lock b/pdm.lock index 5626db6..4ff5697 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev", "lint", "nb", "tests"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:09d959f153789f965fe2d45e1eb65842eb8d0b446b67cdec76891c6048192b0c" +content_hash = "sha256:ae1cefda69bbf1f63d7c5d8c8b19f5dae8525f42c8550e9a44b186d7f57fe7bc" [[metadata.targets]] requires_python = ">=3.11,<3.14" @@ -321,7 +321,7 @@ name = "babel" version = "2.18.0" requires_python = ">=3.8" summary = "Internationalization utilities" -groups = ["nb"] +groups = ["default", "nb"] dependencies = [ "pytz>=2015.7; python_version < \"3.9\"", ] diff --git a/prototypes/t_qt_2.py b/prototypes/t_qt_2.py index a33eac0..9620755 100644 --- a/prototypes/t_qt_2.py +++ b/prototypes/t_qt_2.py @@ -10,6 +10,7 @@ from collections.abc import Sequence from pprint import pprint from typing import Any, Protocol, TypeAlias, TypedDict +import babel from PySide6.QtCore import QDate, QModelIndex, QStringListModel, Qt, QTimer, Signal from PySide6.QtGui import QAction, QStandardItem, QStandardItemModel from PySide6.QtWidgets import ( @@ -55,6 +56,34 @@ QSS = """ } """ DROPDOWN_DEFAULT: str = "--- Bitte wählen ---" +DYNAMIC_LIST_KEY_PATTERN = re.compile(r"-\[(\d+)\]") + + +@dc.dataclass(slots=True) +class CountryList: + iso_to_country: dict[str, str] + for_dropdown: Sequence[tuple[str, str]] + + +def get_country_list_german() -> CountryList: + locale = babel.Locale("de", "DE") + countries: list[tuple[str, str]] = [] + iso_to_country: dict[str, str] = {} + + for iso_code, country_name in locale.territories.items(): + if len(iso_code) == 2 and not iso_code.isdigit(): + countries.append((country_name, iso_code)) + iso_to_country[iso_code] = country_name + + countries.sort(key=lambda x: x[0]) + + return CountryList( + iso_to_country=iso_to_country, + for_dropdown=tuple(countries), + ) + + +COUNTRY_LIST = get_country_list_german() class CompanyForm_Search(QWidget): @@ -166,7 +195,8 @@ class ContactPersonForm_Search(QWidget): title.setStyleSheet("font-size: 14px; font-style: italic;") # font-weight: bold; main_layout.addWidget(title) # --- SEARCH --- - self.search_input = QComboBox(placeholderText="Tippen zum Suchen...") + # self.search_input = QComboBox(placeholderText="Tippen zum Suchen...") + self.search_input = QComboBox() self.search_input.setEditable(True) self.search_input.setInsertPolicy(QComboBox.InsertPolicy.NoInsert) line_edit = self.search_input.lineEdit() @@ -270,6 +300,7 @@ class FormFieldType(enum.StrEnum): DATE = enum.auto() DATETIME = enum.auto() DROPDOWN = enum.auto() + EXTENDED_DROPDOWN = enum.auto() DYNAMIC_LIST = enum.auto() @@ -285,6 +316,8 @@ class DropdownOption: ) -> None: if _data is None: self.data = self.label + else: + self.data = _data @dc.dataclass(slots=True) @@ -315,9 +348,12 @@ class FormField: if self.required: self.label += "*" - if self.type is FormFieldType.DROPDOWN and not options: + if ( + self.type in (FormFieldType.DROPDOWN, FormFieldType.EXTENDED_DROPDOWN) + and not options + ): raise ValueError("Invalid field definition: Dropdown requires options") - elif self.type is FormFieldType.DROPDOWN: + elif self.type in (FormFieldType.DROPDOWN, FormFieldType.EXTENDED_DROPDOWN): self.dropdown_options = tuple(DropdownOption(op[0], op[1]) for op in options) if self.children: @@ -722,9 +758,11 @@ FORM_FIELDS_SCHOOL = [ ), FormField( "Land", - FormFieldType.DROPDOWN, + FormFieldType.EXTENDED_DROPDOWN, + key="country", required=False, - options=[("LÄNDERLISTE ERGÄNZEN", None)], + placeholder="Suche...", + options=COUNTRY_LIST.for_dropdown, ), FormField( "Abschlussjahr", @@ -774,9 +812,11 @@ FORM_FIELDS_HIGHER_EDUCATION = [ ), FormField( "Land", - FormFieldType.DROPDOWN, + FormFieldType.EXTENDED_DROPDOWN, + key="country", required=False, - options=[("LÄNDERLISTE ERGÄNZEN", None)], + placeholder="Suche...", + options=COUNTRY_LIST.for_dropdown, ), FormField( "Ort", @@ -832,9 +872,11 @@ FORM_FIELDS_WORK_EXPERIENCE = [ ), FormField( "Land", - FormFieldType.DROPDOWN, + FormFieldType.EXTENDED_DROPDOWN, + key="country", required=False, - options=[("LÄNDERLISTE ERGÄNZEN", None)], + placeholder="Suche...", + options=COUNTRY_LIST.for_dropdown, ), FormField( "Zeitspanne (von ... bis ...)", @@ -927,6 +969,21 @@ FORM_FIELDS = [ children=FORM_FIELDS_SCHOOL, key="Schulbildung", ), + FormField( + "Test Länderauswahl", + FormFieldType.GROUP, + key="countries", + children=[ + FormField( + "Länderauswahl", + FormFieldType.EXTENDED_DROPDOWN, + key="country", + required=True, + placeholder="Suche...", + options=COUNTRY_LIST.for_dropdown, + ), + ], + ), # FormFieldGroup("Daten Kontaktperson", FORM_FIELDS_CONTACT_PERSON), # FormFieldGroup("Stammdaten (ERGÄNZUNG KINDERALTER DYNAMISCH)", FORM_FIELDS_MASTER_DATA), # FormFieldGroup("weitere Informationen", FORM_FIELDS_ADDITIONAL_DATA), @@ -1077,6 +1134,44 @@ def _build_ui_recursively( else: parent_layout.addRow(field.label, widget) + case FormFieldType.EXTENDED_DROPDOWN: + widget = QComboBox() + widget.setEditable(True) + widget.setInsertPolicy(QComboBox.InsertPolicy.NoInsert) + completer = widget.completer() + assert completer + completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) + completer.setFilterMode(Qt.MatchFlag.MatchContains) + completer.setCompletionMode(QCompleter.CompletionMode.PopupCompletion) + + assert field.dropdown_options + widget.addItem(DROPDOWN_DEFAULT, None) + for option in field.dropdown_options: + widget.addItem(option.label, option.data) + + if field.placeholder: + line_edit = widget.lineEdit() + assert line_edit + line_edit.setPlaceholderText(field.placeholder) + if field.fill_value: + widget.setCurrentText(field.fill_value) + else: + widget.setCurrentIndex(-1) + if field.readonly: + widget.setEnabled(False) + widget.setProperty("styleClass", "stempel") + + widget_registry[full_key] = { + "widget": widget, + "form_field": field, + } + + if field.tooltip: + tooltip_layout = _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.children, @@ -1188,9 +1283,6 @@ def get_form_data( return raw_data -DYNAMIC_LIST_KEY_PATTERN = re.compile(r"-\[(\d+)\]") - - def validate_form_data( widget_registry: WidgetRegistry, ) -> list[str]: diff --git a/prototypes/tests.py b/prototypes/tests.py index c098eec..ea3f451 100644 --- a/prototypes/tests.py +++ b/prototypes/tests.py @@ -2,10 +2,90 @@ import dataclasses as dc import enum import re +from collections.abc import Sequence +import babel from PySide6.QtCore import QDate, Qt +# %% +@dc.dataclass(slots=True) +class CountryList: + iso_to_country: dict[str, str] + for_dropdown: Sequence[tuple[str, str]] + + +def get_country_list_german() -> CountryList: + locale = babel.Locale("de", "DE") + countries: list[tuple[str, str]] = [] + iso_to_country: dict[str, str] = {} + + for iso_code, country_name in locale.territories.items(): + if len(iso_code) == 2 and not iso_code.isdigit(): + countries.append((country_name, iso_code)) + iso_to_country[iso_code] = country_name + + countries.sort(key=lambda x: x[0]) + + return CountryList( + iso_to_country=iso_to_country, + for_dropdown=tuple(countries), + ) + + +# %% +laender_liste + +# %% +DYNAMIC_LIST_KEY_PATTERN = r"-\[(\d+)\]" +key = "Schulbildung-[12].7b8da0f7-7a0e-4f71-878a-85616099e849" + +matches = re.search(DYNAMIC_LIST_KEY_PATTERN, key) + +# %% +matches + +# %% +matches.group(1) + + +# %% +class COUNTRY(enum.IntEnum): + DE = 1 + FR = 2 + CM = 3 + + +class COUNTRY2(enum.Enum): + DE = 1 + FR = 2 + CM = 3 + + +def give_value(t): + print(f"Wert ist: {t}") + + +give_value(COUNTRY.DE) +give_value(COUNTRY2.DE) + +# %% +COUNTRY(10) +# %% +COUNTRY.DE +# %% +t_str = "asd.yxcxc.dfgjj.aasdsdsdsd.sdsdsdsd" +splitted = t_str.split(".") +part, rest = splitted[0], splitted[1:] + +part + +# %% +".".join([part] + rest) + +# %% + + class FormFieldType(enum.StrEnum): TEXT = enum.auto() LONGTEXT = enum.auto() @@ -28,29 +108,6 @@ class FormField: self.label += "*" -# %% -DYNAMIC_LIST_KEY_PATTERN = r"-\[(\d+)\]" -key = "Schulbildung-[12].7b8da0f7-7a0e-4f71-878a-85616099e849" - -matches = re.search(DYNAMIC_LIST_KEY_PATTERN, key) - -# %% -matches - -# %% -matches.group(1) - -# %% -t_str = "asd.yxcxc.dfgjj.aasdsdsdsd.sdsdsdsd" -splitted = t_str.split(".") -part, rest = splitted[0], splitted[1:] - -part - -# %% -".".join([part] + rest) - - # %% FormField("name", "Projektbeschreibung", FormFieldType.LONGTEXT, required=True) # %% diff --git a/pyproject.toml b/pyproject.toml index 326c772..a966567 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "GUI for CRM of NAFKA project with WCE" authors = [ {name = "d-opt GmbH, resp. Florian Förster", email = "f.foerster@d-opt.com"}, ] -dependencies = ["nicegui>=3.10.0", "pyside6>=6.11.0", "sqlalchemy>=2.0.49", "polars>=1.40.1", "dopt-basics>=0.2.4", "pydantic>=2.13.4"] +dependencies = ["nicegui>=3.10.0", "pyside6>=6.11.0", "sqlalchemy>=2.0.49", "polars>=1.40.1", "dopt-basics>=0.2.4", "pydantic>=2.13.4", "babel>=2.18.0"] requires-python = "<3.14,>=3.11" readme = "README.md" license = {text = "LicenseRef-Proprietary"}