generated from dopt-python/py311
more generic build function for widgets defined by form fields
This commit is contained in:
+243
-277
@@ -35,6 +35,7 @@ from PySide6.QtGui import QAction, QFont
|
||||
from PySide6.QtWidgets import (
|
||||
QAbstractItemView,
|
||||
QApplication,
|
||||
QBoxLayout,
|
||||
QButtonGroup,
|
||||
QCalendarWidget,
|
||||
QComboBox,
|
||||
@@ -272,19 +273,244 @@ class CustomWidget(QWidget):
|
||||
def validate_form_data(self) -> list[str]: ...
|
||||
|
||||
|
||||
def _add_widget_to_layout(
|
||||
form_field: FormField,
|
||||
widget: QWidget,
|
||||
target_layout: QLayout,
|
||||
widget_only: bool = False,
|
||||
) -> None:
|
||||
|
||||
if isinstance(target_layout, QFormLayout):
|
||||
if widget_only:
|
||||
target_layout.addRow(widget)
|
||||
elif form_field.tooltip:
|
||||
tooltip_layout = _add_tooltip(widget, form_field.tooltip)
|
||||
target_layout.addRow(form_field.label, tooltip_layout)
|
||||
else:
|
||||
target_layout.addRow(form_field.label, widget)
|
||||
elif isinstance(target_layout, QBoxLayout):
|
||||
if widget_only:
|
||||
target_layout.addWidget(widget)
|
||||
elif form_field.tooltip:
|
||||
tooltip_layout = _add_tooltip(widget, form_field.tooltip)
|
||||
target_layout.addLayout(tooltip_layout)
|
||||
else:
|
||||
target_layout.addWidget(widget)
|
||||
|
||||
|
||||
def build_and_add_widget(
|
||||
form_field: FormField,
|
||||
parent_layout: QLayout,
|
||||
prefix: str = "",
|
||||
) -> tuple[str, QWidget]:
|
||||
no_scroll_filter = NoScrollFilter(parent_layout)
|
||||
if prefix and form_field.key:
|
||||
full_key = f"{prefix}{COLUMN_SEP}{form_field.key}" if prefix else form_field.key
|
||||
elif prefix and not form_field.key:
|
||||
full_key = f"{prefix}"
|
||||
else:
|
||||
full_key = form_field.key
|
||||
|
||||
widget: QWidget
|
||||
layout_widget_only: bool = False
|
||||
install_no_scroll_filter: bool = True
|
||||
|
||||
match form_field.type:
|
||||
case (
|
||||
FormFieldType.DYNAMIC_LIST
|
||||
| FormFieldType.DYNAMIC_DROPDOWN_NUMERIC
|
||||
| FormFieldType.DYNAMIC_DROPDOWN_OPTION
|
||||
| FormFieldType.CUSTOM
|
||||
):
|
||||
layout_widget_only = True
|
||||
install_no_scroll_filter = False
|
||||
|
||||
match form_field.type:
|
||||
case FormFieldType.GROUP:
|
||||
raise TypeError("FormField Groups are not supported for direct building")
|
||||
|
||||
case FormFieldType.TEXT | FormFieldType.TEXT_DATE | FormFieldType.TEXT_DATETIME:
|
||||
widget = QLineEdit()
|
||||
if form_field.placeholder:
|
||||
widget.setPlaceholderText(form_field.placeholder)
|
||||
if form_field.fill_value:
|
||||
widget.setText(form_field.fill_value)
|
||||
if form_field.readonly:
|
||||
widget.setReadOnly(True)
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
|
||||
case FormFieldType.TEXT_SEARCH:
|
||||
widget = QLineEdit()
|
||||
if form_field.placeholder:
|
||||
widget.setPlaceholderText(form_field.placeholder)
|
||||
else:
|
||||
widget.setPlaceholderText("Tippen zum Suchen...")
|
||||
|
||||
search_completer = QCompleter()
|
||||
search_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
|
||||
search_completer.setFilterMode(Qt.MatchFlag.MatchContains)
|
||||
widget.setCompleter(search_completer)
|
||||
|
||||
case FormFieldType.LONGTEXT:
|
||||
widget = QPlainTextEdit()
|
||||
widget.setMaximumHeight(80)
|
||||
if form_field.placeholder:
|
||||
widget.setPlaceholderText(form_field.placeholder)
|
||||
if form_field.readonly:
|
||||
widget.setReadOnly(True)
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
|
||||
case FormFieldType.DATE:
|
||||
widget = 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 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())
|
||||
|
||||
if form_field.readonly:
|
||||
widget = QLineEdit()
|
||||
widget.setReadOnly(True)
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
if form_field.fill_value:
|
||||
widget.setText(form_field.fill_value)
|
||||
|
||||
case FormFieldType.DATE_FLEXIBLE:
|
||||
widget = FlexibleDateInput()
|
||||
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.set_date(set_date)
|
||||
|
||||
if form_field.readonly:
|
||||
widget.setReadOnly(True)
|
||||
|
||||
case FormFieldType.DATETIME_FLEXIBLE:
|
||||
widget = FlexibleDateTimeInput()
|
||||
if form_field.fill_value:
|
||||
set_datetime = QDateTime.fromString(form_field.fill_value, "dd.MM.yyyy hh:mm")
|
||||
if not set_datetime.isValid():
|
||||
raise ValueError(
|
||||
f"Could not parse date field value >{form_field.fill_value}<"
|
||||
)
|
||||
widget.set_datetime(set_datetime)
|
||||
|
||||
if form_field.readonly:
|
||||
widget.setReadOnly(True)
|
||||
|
||||
case FormFieldType.DROPDOWN:
|
||||
widget = QComboBox()
|
||||
widget.addItem(DROPDOWN_DEFAULT, None)
|
||||
for option in form_field.dropdown_options:
|
||||
widget.addItem(option.label, option.data)
|
||||
|
||||
if form_field.placeholder:
|
||||
widget.setPlaceholderText(form_field.placeholder)
|
||||
if form_field.fill_value:
|
||||
widget.setCurrentText(form_field.fill_value)
|
||||
else:
|
||||
widget.setCurrentIndex(0)
|
||||
if form_field.readonly:
|
||||
widget.setEnabled(False)
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
|
||||
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)
|
||||
|
||||
widget.addItem(DROPDOWN_DEFAULT, None)
|
||||
for option in form_field.dropdown_options:
|
||||
widget.addItem(option.label, option.data)
|
||||
|
||||
if form_field.placeholder:
|
||||
line_edit = widget.lineEdit()
|
||||
assert line_edit
|
||||
line_edit.setPlaceholderText(form_field.placeholder)
|
||||
if form_field.fill_value:
|
||||
widget.setCurrentText(form_field.fill_value)
|
||||
else:
|
||||
widget.setCurrentIndex(-1)
|
||||
if form_field.readonly:
|
||||
widget.setEnabled(False)
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
|
||||
case FormFieldType.DYNAMIC_LIST:
|
||||
widget = DynamicListWidget(
|
||||
form_field.children,
|
||||
form_field.label,
|
||||
prefix=f"{full_key}",
|
||||
)
|
||||
|
||||
case FormFieldType.DYNAMIC_DROPDOWN_NUMERIC:
|
||||
widget = DynamicDropdownWidgetNumeric(
|
||||
form_field.children,
|
||||
form_field.label,
|
||||
prefix=f"{full_key}",
|
||||
)
|
||||
|
||||
case FormFieldType.DYNAMIC_DROPDOWN_OPTION:
|
||||
widget = DynamicDropdownWidgetOption(
|
||||
form_field.children,
|
||||
form_field.trigger_value,
|
||||
form_field.label,
|
||||
prefix=f"{full_key}",
|
||||
)
|
||||
|
||||
case FormFieldType.CUSTOM:
|
||||
widget_class = CUSTOM_WIDGETS[form_field.custom_widget]
|
||||
widget = widget_class(
|
||||
form_fields=form_field.children,
|
||||
label=form_field.label,
|
||||
prefix=f"{full_key}",
|
||||
)
|
||||
|
||||
case _:
|
||||
raise NotImplementedError(f"Not supported field type: {form_field.type.value}")
|
||||
|
||||
if install_no_scroll_filter:
|
||||
widget.installEventFilter(no_scroll_filter)
|
||||
|
||||
_add_widget_to_layout(form_field, widget, parent_layout, widget_only=layout_widget_only)
|
||||
|
||||
return full_key, widget
|
||||
|
||||
|
||||
def _build_ui_recursively(
|
||||
schema: Sequence[FormField],
|
||||
parent_layout: QFormLayout,
|
||||
widget_registry: WidgetRegistry,
|
||||
prefix: str = "",
|
||||
) -> list[str]:
|
||||
no_scroll_filter = NoScrollFilter(parent_layout)
|
||||
keys: list[str] = []
|
||||
for field in schema:
|
||||
if prefix and field.key:
|
||||
full_key = f"{prefix}{COLUMN_SEP}{field.key}" if prefix else field.key
|
||||
elif prefix and not field.key:
|
||||
full_key = f"{prefix}"
|
||||
full_key = prefix
|
||||
else:
|
||||
full_key = field.key
|
||||
|
||||
@@ -302,282 +528,12 @@ def _build_ui_recursively(
|
||||
)
|
||||
parent_layout.addRow(group_box)
|
||||
|
||||
case FormFieldType.TEXT | FormFieldType.TEXT_DATE | FormFieldType.TEXT_DATETIME:
|
||||
widget = QLineEdit()
|
||||
if field.placeholder:
|
||||
widget.setPlaceholderText(field.placeholder)
|
||||
if field.fill_value:
|
||||
widget.setText(field.fill_value)
|
||||
if field.readonly:
|
||||
widget.setReadOnly(True)
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
widget.installEventFilter(no_scroll_filter)
|
||||
|
||||
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.TEXT_SEARCH:
|
||||
widget = QLineEdit()
|
||||
if field.placeholder:
|
||||
widget.setPlaceholderText(field.placeholder)
|
||||
else:
|
||||
widget.setPlaceholderText("Tippen zum Suchen...")
|
||||
|
||||
search_completer = QCompleter()
|
||||
search_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
|
||||
search_completer.setFilterMode(Qt.MatchFlag.MatchContains)
|
||||
widget.setCompleter(search_completer)
|
||||
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
widget.installEventFilter(no_scroll_filter)
|
||||
|
||||
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.LONGTEXT:
|
||||
widget = QPlainTextEdit()
|
||||
widget.setMaximumHeight(80)
|
||||
if field.placeholder:
|
||||
widget.setPlaceholderText(field.placeholder)
|
||||
if field.readonly:
|
||||
widget.setReadOnly(True)
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
widget.installEventFilter(no_scroll_filter)
|
||||
|
||||
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.DATE:
|
||||
widget = 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)
|
||||
widget.setProperty("styleClass", "stempel")
|
||||
if field.fill_value:
|
||||
widget.setText(field.fill_value)
|
||||
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
widget.installEventFilter(no_scroll_filter)
|
||||
|
||||
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.DATE_FLEXIBLE:
|
||||
widget = FlexibleDateInput()
|
||||
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.set_date(set_date)
|
||||
|
||||
if field.readonly:
|
||||
widget.setReadOnly(True)
|
||||
|
||||
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.DATETIME_FLEXIBLE:
|
||||
widget = FlexibleDateTimeInput()
|
||||
if field.fill_value:
|
||||
set_datetime = QDateTime.fromString(field.fill_value, "dd.MM.yyyy hh:mm")
|
||||
if not set_datetime.isValid():
|
||||
raise ValueError(
|
||||
f"Could not parse date field value >{field.fill_value}<"
|
||||
)
|
||||
widget.set_datetime(set_datetime)
|
||||
|
||||
if field.readonly:
|
||||
widget.setReadOnly(True)
|
||||
|
||||
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.DROPDOWN:
|
||||
widget = QComboBox()
|
||||
widget.addItem(DROPDOWN_DEFAULT, None)
|
||||
for option in field.dropdown_options:
|
||||
widget.addItem(option.label, option.data)
|
||||
|
||||
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,
|
||||
}
|
||||
widget.installEventFilter(no_scroll_filter)
|
||||
|
||||
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.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)
|
||||
|
||||
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,
|
||||
}
|
||||
widget.installEventFilter(no_scroll_filter)
|
||||
|
||||
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,
|
||||
field.label,
|
||||
prefix=f"{full_key}",
|
||||
)
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
parent_layout.addRow(widget)
|
||||
|
||||
case FormFieldType.DYNAMIC_DROPDOWN_NUMERIC:
|
||||
widget = DynamicDropdownWidgetNumeric(
|
||||
field.children,
|
||||
field.label,
|
||||
prefix=f"{full_key}",
|
||||
)
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
parent_layout.addRow(widget)
|
||||
|
||||
case FormFieldType.DYNAMIC_DROPDOWN_OPTION:
|
||||
widget = DynamicDropdownWidgetOption(
|
||||
field.children,
|
||||
field.trigger_value,
|
||||
field.label,
|
||||
prefix=f"{full_key}",
|
||||
)
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
parent_layout.addRow(widget)
|
||||
|
||||
case FormFieldType.CUSTOM:
|
||||
widget_class = CUSTOM_WIDGETS[field.custom_widget]
|
||||
widget = widget_class(
|
||||
form_fields=field.children,
|
||||
label=field.label,
|
||||
prefix=f"{full_key}",
|
||||
)
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
parent_layout.addRow(widget)
|
||||
|
||||
case _:
|
||||
raise NotImplementedError(f"Not supported field type: {field.type.value}")
|
||||
|
||||
full_key, widget = build_and_add_widget(field, parent_layout, prefix=prefix)
|
||||
widget_registry[full_key] = {
|
||||
"widget": widget,
|
||||
"form_field": field,
|
||||
}
|
||||
keys.append(full_key)
|
||||
|
||||
return keys
|
||||
@@ -1589,6 +1545,11 @@ class FlexibleDateInput(QWidget):
|
||||
self.button = QPushButton("📅")
|
||||
self.button.setFixedWidth(30)
|
||||
|
||||
self.line_edit.setSizePolicy(
|
||||
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
|
||||
)
|
||||
self.button.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)
|
||||
|
||||
layout.addWidget(self.line_edit)
|
||||
layout.addWidget(self.button)
|
||||
|
||||
@@ -1748,6 +1709,11 @@ class FlexibleDateTimeInput(QWidget):
|
||||
self.button = QPushButton("📅")
|
||||
self.button.setFixedWidth(30)
|
||||
|
||||
self.line_edit.setSizePolicy(
|
||||
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
|
||||
)
|
||||
self.button.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)
|
||||
|
||||
layout.addWidget(self.line_edit)
|
||||
layout.addWidget(self.button)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user