generated from dopt-python/py311
Dev #7
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import zoneinfo
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
@@ -25,3 +26,6 @@ class Config:
|
|||||||
PATH_LOGGING: Path = BASE_PATH / os.getenv("DOPT_PATH_LOGGING", "data/d-opt.log")
|
PATH_LOGGING: Path = BASE_PATH / os.getenv("DOPT_PATH_LOGGING", "data/d-opt.log")
|
||||||
LOG_FILENAME: str = "dopt.log"
|
LOG_FILENAME: str = "dopt.log"
|
||||||
ALEMBIC_PATH: Path = BASE_PATH / os.getenv("DOPT_ALEMBIC_BASE", "python/alembic")
|
ALEMBIC_PATH: Path = BASE_PATH / os.getenv("DOPT_ALEMBIC_BASE", "python/alembic")
|
||||||
|
|
||||||
|
|
||||||
|
TIMEZONE_CEST: Final[zoneinfo.ZoneInfo] = zoneinfo.ZoneInfo("Europe/Berlin")
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ class FormFieldType(enum.StrEnum):
|
|||||||
TEXT = enum.auto()
|
TEXT = enum.auto()
|
||||||
LONGTEXT = enum.auto()
|
LONGTEXT = enum.auto()
|
||||||
DATE = enum.auto()
|
DATE = enum.auto()
|
||||||
|
DATE_FLEXIBLE = enum.auto()
|
||||||
DATETIME = enum.auto()
|
DATETIME = enum.auto()
|
||||||
|
DATETIME_FLEXIBLE = enum.auto()
|
||||||
DROPDOWN = enum.auto()
|
DROPDOWN = enum.auto()
|
||||||
EXTENDED_DROPDOWN = enum.auto()
|
EXTENDED_DROPDOWN = enum.auto()
|
||||||
DYNAMIC_LIST = enum.auto()
|
DYNAMIC_LIST = enum.auto()
|
||||||
|
|||||||
+455
-6
@@ -15,14 +15,17 @@ from pprint import pformat
|
|||||||
from typing import TYPE_CHECKING, Any, Final, Protocol, TypeAlias, TypedDict, TypeVar, cast
|
from typing import TYPE_CHECKING, Any, Final, Protocol, TypeAlias, TypedDict, TypeVar, cast
|
||||||
from typing_extensions import override
|
from typing_extensions import override
|
||||||
|
|
||||||
|
from dopt_basics import datetime as dopt_dt
|
||||||
from pydantic import (
|
from pydantic import (
|
||||||
ValidationError,
|
ValidationError,
|
||||||
)
|
)
|
||||||
from PySide6.QtCore import (
|
from PySide6.QtCore import (
|
||||||
QDate,
|
QDate,
|
||||||
|
QDateTime,
|
||||||
QEvent,
|
QEvent,
|
||||||
QObject,
|
QObject,
|
||||||
Qt,
|
Qt,
|
||||||
|
QTime,
|
||||||
QTimer,
|
QTimer,
|
||||||
QtMsgType,
|
QtMsgType,
|
||||||
Signal,
|
Signal,
|
||||||
@@ -33,6 +36,7 @@ from PySide6.QtWidgets import (
|
|||||||
QAbstractItemView,
|
QAbstractItemView,
|
||||||
QApplication,
|
QApplication,
|
||||||
QButtonGroup,
|
QButtonGroup,
|
||||||
|
QCalendarWidget,
|
||||||
QComboBox,
|
QComboBox,
|
||||||
QCompleter,
|
QCompleter,
|
||||||
QDateEdit,
|
QDateEdit,
|
||||||
@@ -61,6 +65,7 @@ from PySide6.QtWidgets import (
|
|||||||
|
|
||||||
import wce_crm.constants
|
import wce_crm.constants
|
||||||
from wce_crm.backend import backend
|
from wce_crm.backend import backend
|
||||||
|
from wce_crm.constants import TIMEZONE_CEST
|
||||||
from wce_crm.data_models import (
|
from wce_crm.data_models import (
|
||||||
COLUMN_SEP,
|
COLUMN_SEP,
|
||||||
FlatBaseModel,
|
FlatBaseModel,
|
||||||
@@ -406,6 +411,54 @@ def _build_ui_recursively(
|
|||||||
else:
|
else:
|
||||||
parent_layout.addRow(field.label, widget)
|
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:
|
case FormFieldType.DROPDOWN:
|
||||||
widget = QComboBox()
|
widget = QComboBox()
|
||||||
widget.addItem(DROPDOWN_DEFAULT, None)
|
widget.addItem(DROPDOWN_DEFAULT, None)
|
||||||
@@ -576,6 +629,24 @@ def reset_form(
|
|||||||
widget.setDate(set_date)
|
widget.setDate(set_date)
|
||||||
else:
|
else:
|
||||||
widget.setDate(QDate.currentDate())
|
widget.setDate(QDate.currentDate())
|
||||||
|
elif isinstance(widget, FlexibleDateInput):
|
||||||
|
widget.reset()
|
||||||
|
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)
|
||||||
|
elif isinstance(widget, FlexibleDateTimeInput):
|
||||||
|
widget.reset()
|
||||||
|
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)
|
||||||
elif isinstance(widget, QComboBox):
|
elif isinstance(widget, QComboBox):
|
||||||
if form_field.fill_value:
|
if form_field.fill_value:
|
||||||
widget.setCurrentText(form_field.fill_value)
|
widget.setCurrentText(form_field.fill_value)
|
||||||
@@ -699,6 +770,10 @@ def get_form_data(
|
|||||||
elif isinstance(widget, QDateEdit):
|
elif isinstance(widget, QDateEdit):
|
||||||
qt_date = widget.date()
|
qt_date = widget.date()
|
||||||
value = qt_date.toPython()
|
value = qt_date.toPython()
|
||||||
|
elif isinstance(widget, FlexibleDateInput):
|
||||||
|
value = widget.get_pydate()
|
||||||
|
elif isinstance(widget, FlexibleDateTimeInput):
|
||||||
|
value = widget.get_pydatetime()
|
||||||
elif isinstance(widget, QComboBox):
|
elif isinstance(widget, QComboBox):
|
||||||
value = widget.currentData()
|
value = widget.currentData()
|
||||||
elif isinstance(
|
elif isinstance(
|
||||||
@@ -754,7 +829,18 @@ def set_widget_value(
|
|||||||
if not set_date.isValid():
|
if not set_date.isValid():
|
||||||
raise ValueError(f"Could not parse date field value >{value}<")
|
raise ValueError(f"Could not parse date field value >{value}<")
|
||||||
widget.setDate(set_date)
|
widget.setDate(set_date)
|
||||||
|
elif isinstance(widget, FlexibleDateInput):
|
||||||
|
assert isinstance(value, datetime.datetime)
|
||||||
|
try:
|
||||||
|
widget.set_pydate(value, raise_error=True)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(f"Could not parse date field value >{value}<")
|
||||||
|
elif isinstance(widget, FlexibleDateTimeInput):
|
||||||
|
assert isinstance(value, datetime.datetime)
|
||||||
|
try:
|
||||||
|
widget.set_pydatetime(value, raise_error=True)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(f"Could not parse datetime field value >{value}<")
|
||||||
elif isinstance(widget, QComboBox):
|
elif isinstance(widget, QComboBox):
|
||||||
index = widget.findData(value)
|
index = widget.findData(value)
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
@@ -847,6 +933,16 @@ def validate_form_data(
|
|||||||
if widget.currentData() is not None:
|
if widget.currentData() is not None:
|
||||||
continue
|
continue
|
||||||
error_post = True
|
error_post = True
|
||||||
|
elif isinstance(widget, FlexibleDateInput):
|
||||||
|
date = widget.get_date()
|
||||||
|
if date and date.isValid():
|
||||||
|
continue
|
||||||
|
error_post = True
|
||||||
|
elif isinstance(widget, FlexibleDateTimeInput):
|
||||||
|
date = widget.get_datetime()
|
||||||
|
if date and date.isValid():
|
||||||
|
continue
|
||||||
|
error_post = True
|
||||||
elif isinstance(
|
elif isinstance(
|
||||||
widget,
|
widget,
|
||||||
(
|
(
|
||||||
@@ -1477,6 +1573,355 @@ class AutoForm(QWidget):
|
|||||||
set_form_data(self.widget_registry, data)
|
set_form_data(self.widget_registry, data)
|
||||||
|
|
||||||
|
|
||||||
|
class FlexibleDateInput(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._read_only = False
|
||||||
|
|
||||||
|
layout = QHBoxLayout(self)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
# simple text field allows for easy editing and deletion
|
||||||
|
self.line_edit = QLineEdit()
|
||||||
|
self.line_edit.setPlaceholderText("TT.MM.JJJJ")
|
||||||
|
self.line_edit.setInputMask("99.99.9999;_") # force date structure
|
||||||
|
|
||||||
|
self.button = QPushButton("📅")
|
||||||
|
self.button.setFixedWidth(30)
|
||||||
|
|
||||||
|
layout.addWidget(self.line_edit)
|
||||||
|
layout.addWidget(self.button)
|
||||||
|
|
||||||
|
self.calendar = QCalendarWidget()
|
||||||
|
self.calendar.setWindowFlags(Qt.WindowType.Popup) # calendar as free-floating window
|
||||||
|
self.calendar.setFirstDayOfWeek(Qt.DayOfWeek.Monday)
|
||||||
|
self.calendar.setGridVisible(True)
|
||||||
|
self.calendar.setStyleSheet("""
|
||||||
|
QCalendarWidget QAbstractItemView {
|
||||||
|
selection-background-color: #2980b9;
|
||||||
|
selection-color: white;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
self.button.clicked.connect(self.show_calendar)
|
||||||
|
self.calendar.activated.connect(self.set_date)
|
||||||
|
# signal to validate
|
||||||
|
self.line_edit.editingFinished.connect(self.validate_input)
|
||||||
|
self.line_edit.installEventFilter(self)
|
||||||
|
|
||||||
|
self.setReadOnly(False)
|
||||||
|
|
||||||
|
def eventFilter(
|
||||||
|
self,
|
||||||
|
watched: QObject,
|
||||||
|
event: QEvent,
|
||||||
|
):
|
||||||
|
# press enter: direct validation
|
||||||
|
if watched == self.line_edit:
|
||||||
|
if event.type() == QEvent.Type.KeyPress:
|
||||||
|
if event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter): # type: ignore
|
||||||
|
self.validate_input()
|
||||||
|
return True # mark event as processed
|
||||||
|
elif event.type() == QEvent.Type.FocusOut:
|
||||||
|
self.validate_input()
|
||||||
|
# no return to not block focus change
|
||||||
|
return super().eventFilter(watched, event)
|
||||||
|
|
||||||
|
def show_calendar(self) -> None:
|
||||||
|
if self._read_only:
|
||||||
|
return
|
||||||
|
pos = self.line_edit.mapToGlobal(self.line_edit.rect().bottomLeft())
|
||||||
|
self.calendar.move(pos)
|
||||||
|
self.calendar.show()
|
||||||
|
|
||||||
|
def isReadOnly(self) -> bool:
|
||||||
|
return self._read_only
|
||||||
|
|
||||||
|
def setReadOnly(
|
||||||
|
self,
|
||||||
|
read_only: bool,
|
||||||
|
):
|
||||||
|
self._read_only = read_only
|
||||||
|
self.line_edit.setReadOnly(read_only)
|
||||||
|
self.button.setEnabled(not read_only)
|
||||||
|
|
||||||
|
current_text = self.line_edit.text()
|
||||||
|
|
||||||
|
if read_only:
|
||||||
|
self.line_edit.setInputMask("")
|
||||||
|
self.line_edit.setPlaceholderText("kein Datum eingetragen")
|
||||||
|
pure = (
|
||||||
|
current_text.replace(".", "")
|
||||||
|
.replace(":", "")
|
||||||
|
.replace(" ", "")
|
||||||
|
.replace("_", "")
|
||||||
|
.strip()
|
||||||
|
)
|
||||||
|
cleaned_date = current_text if pure else ""
|
||||||
|
self.line_edit.setText(cleaned_date)
|
||||||
|
self.line_edit.setProperty("styleClass", "stempel")
|
||||||
|
else:
|
||||||
|
self.line_edit.setInputMask("99.99.9999;_")
|
||||||
|
self.line_edit.setPlaceholderText("TT.MM.JJJJ")
|
||||||
|
|
||||||
|
def validate_input(self) -> None:
|
||||||
|
logger_gui.debug("[Widget -- FlexibleDateInput] Trigger input validation")
|
||||||
|
if self._read_only:
|
||||||
|
return
|
||||||
|
|
||||||
|
focused_widget = QApplication.focusWidget()
|
||||||
|
if focused_widget == self.button or focused_widget == self.calendar:
|
||||||
|
return
|
||||||
|
|
||||||
|
text = self.line_edit.text()
|
||||||
|
# field empty
|
||||||
|
if not text.replace(".", "").replace("_", "").strip():
|
||||||
|
self.line_edit.setStyleSheet("")
|
||||||
|
return
|
||||||
|
|
||||||
|
# if user wrote input
|
||||||
|
date = QDate.fromString(text, "dd.MM.yyyy")
|
||||||
|
|
||||||
|
if not date.isValid():
|
||||||
|
self.line_edit.setStyleSheet("border: 1px solid red; background-color: #FFEEEE;")
|
||||||
|
self.line_edit.selectAll()
|
||||||
|
# optional: keep focus in field to directly correct entry
|
||||||
|
# self.line_edit.setFocus()
|
||||||
|
else:
|
||||||
|
self.line_edit.setStyleSheet("")
|
||||||
|
|
||||||
|
def set_date(
|
||||||
|
self,
|
||||||
|
date: QDate,
|
||||||
|
) -> None:
|
||||||
|
if self._read_only:
|
||||||
|
return
|
||||||
|
|
||||||
|
if date and date.isValid():
|
||||||
|
self.line_edit.setText(date.toString("dd.MM.yyyy"))
|
||||||
|
else:
|
||||||
|
self.line_edit.clear()
|
||||||
|
|
||||||
|
self.line_edit.setText(date.toString("dd.MM.yyyy"))
|
||||||
|
self.calendar.hide()
|
||||||
|
|
||||||
|
def set_pydate(
|
||||||
|
self,
|
||||||
|
date: datetime.datetime,
|
||||||
|
raise_error: bool = False,
|
||||||
|
) -> None:
|
||||||
|
date_to_set = QDate(date.year, date.month, date.day)
|
||||||
|
if raise_error and not date_to_set.isValid():
|
||||||
|
raise ValueError("Could not parse date object correctly")
|
||||||
|
|
||||||
|
self.set_date(date_to_set)
|
||||||
|
|
||||||
|
def get_date(self) -> QDate:
|
||||||
|
# valid or invalid QDate
|
||||||
|
return QDate.fromString(self.line_edit.text(), "dd.MM.yyyy")
|
||||||
|
|
||||||
|
def get_pydate(self) -> datetime.date:
|
||||||
|
# valid or invalid QDate
|
||||||
|
date = self.get_date()
|
||||||
|
py_date = cast(datetime.date, date.toPython())
|
||||||
|
|
||||||
|
return py_date
|
||||||
|
|
||||||
|
def reset(self) -> None:
|
||||||
|
self.calendar.hide()
|
||||||
|
self.line_edit.clear()
|
||||||
|
|
||||||
|
|
||||||
|
class FlexibleDateTimeInput(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._read_only = False
|
||||||
|
|
||||||
|
layout = QHBoxLayout(self)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
# Simple text field allows for easy editing and deletion (with Time)
|
||||||
|
self.line_edit = QLineEdit()
|
||||||
|
self.line_edit.setPlaceholderText("TT.MM.JJJJ hh:mm")
|
||||||
|
self.line_edit.setInputMask("99.99.9999 99:99;_") # force datetime structure
|
||||||
|
|
||||||
|
self.button = QPushButton("📅")
|
||||||
|
self.button.setFixedWidth(30)
|
||||||
|
|
||||||
|
layout.addWidget(self.line_edit)
|
||||||
|
layout.addWidget(self.button)
|
||||||
|
|
||||||
|
self.calendar = QCalendarWidget()
|
||||||
|
self.calendar.setWindowFlags(Qt.WindowType.Popup) # calendar as free-floating window
|
||||||
|
self.calendar.setFirstDayOfWeek(Qt.DayOfWeek.Monday)
|
||||||
|
self.calendar.setGridVisible(True)
|
||||||
|
self.calendar.setStyleSheet("""
|
||||||
|
QCalendarWidget QAbstractItemView {
|
||||||
|
selection-background-color: #2980b9;
|
||||||
|
selection-color: white;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
self.button.clicked.connect(self.show_calendar)
|
||||||
|
self.calendar.activated.connect(self.set_date_from_calendar)
|
||||||
|
|
||||||
|
# signal to validate
|
||||||
|
self.line_edit.editingFinished.connect(self.validate_input)
|
||||||
|
self.line_edit.installEventFilter(self)
|
||||||
|
|
||||||
|
self.setReadOnly(False)
|
||||||
|
|
||||||
|
def eventFilter(
|
||||||
|
self,
|
||||||
|
watched: QObject,
|
||||||
|
event: QEvent,
|
||||||
|
):
|
||||||
|
# press enter: direct validation
|
||||||
|
if watched == self.line_edit:
|
||||||
|
if event.type() == QEvent.Type.KeyPress:
|
||||||
|
if event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter): # type: ignore
|
||||||
|
self.validate_input()
|
||||||
|
return True # mark event as processed
|
||||||
|
elif event.type() == QEvent.Type.FocusOut:
|
||||||
|
self.validate_input()
|
||||||
|
# no return to not block focus change
|
||||||
|
return super().eventFilter(watched, event)
|
||||||
|
|
||||||
|
def show_calendar(self) -> None:
|
||||||
|
if self._read_only:
|
||||||
|
return
|
||||||
|
pos = self.line_edit.mapToGlobal(self.line_edit.rect().bottomLeft())
|
||||||
|
self.calendar.move(pos)
|
||||||
|
self.calendar.show()
|
||||||
|
|
||||||
|
def isReadOnly(self) -> bool:
|
||||||
|
return self._read_only
|
||||||
|
|
||||||
|
def setReadOnly(
|
||||||
|
self,
|
||||||
|
read_only: bool,
|
||||||
|
):
|
||||||
|
self._read_only = read_only
|
||||||
|
self.line_edit.setReadOnly(read_only)
|
||||||
|
self.button.setEnabled(not read_only)
|
||||||
|
|
||||||
|
current_text = self.line_edit.text()
|
||||||
|
|
||||||
|
if read_only:
|
||||||
|
self.line_edit.setInputMask("")
|
||||||
|
self.line_edit.setPlaceholderText("kein Zeitstempel eingetragen")
|
||||||
|
pure = (
|
||||||
|
current_text.replace(".", "")
|
||||||
|
.replace(":", "")
|
||||||
|
.replace(" ", "")
|
||||||
|
.replace("_", "")
|
||||||
|
.strip()
|
||||||
|
)
|
||||||
|
cleaned_datetime = current_text if pure else ""
|
||||||
|
self.line_edit.setText(cleaned_datetime)
|
||||||
|
self.line_edit.setProperty("styleClass", "stempel")
|
||||||
|
else:
|
||||||
|
self.line_edit.setInputMask("99.99.9999 99:99;_")
|
||||||
|
self.line_edit.setPlaceholderText("TT.MM.JJJJ hh:mm")
|
||||||
|
|
||||||
|
def validate_input(self) -> None:
|
||||||
|
logger_gui.debug("[Widget -- FlexibleDateTimeInput] Trigger input validation")
|
||||||
|
if self._read_only:
|
||||||
|
return
|
||||||
|
|
||||||
|
focused_widget = QApplication.focusWidget()
|
||||||
|
if focused_widget == self.button or focused_widget == self.calendar:
|
||||||
|
return
|
||||||
|
|
||||||
|
text = self.line_edit.text()
|
||||||
|
# field empty (ignoring dots, colons, spaces, underscores)
|
||||||
|
pure_text = (
|
||||||
|
text.replace(".", "").replace(":", "").replace(" ", "").replace("_", "").strip()
|
||||||
|
)
|
||||||
|
if not pure_text:
|
||||||
|
self.line_edit.setStyleSheet("")
|
||||||
|
return
|
||||||
|
|
||||||
|
# check logic for QDateTime
|
||||||
|
dt = QDateTime.fromString(text, "dd.MM.yyyy hh:mm")
|
||||||
|
|
||||||
|
if not dt.isValid():
|
||||||
|
self.line_edit.setStyleSheet("border: 1px solid red; background-color: #FFEEEE;")
|
||||||
|
self.line_edit.selectAll()
|
||||||
|
else:
|
||||||
|
self.line_edit.setStyleSheet("")
|
||||||
|
|
||||||
|
def set_date_from_calendar(
|
||||||
|
self,
|
||||||
|
date: QDate,
|
||||||
|
) -> None:
|
||||||
|
if self._read_only:
|
||||||
|
return
|
||||||
|
|
||||||
|
# save time user already provided it
|
||||||
|
current_text = self.line_edit.text()
|
||||||
|
time_part = "00:00"
|
||||||
|
|
||||||
|
# extract time portion (last 5 characters of "dd.MM.yyyy hh:mm")
|
||||||
|
if len(current_text) >= 16:
|
||||||
|
potential_time = current_text[11:16]
|
||||||
|
if "_" not in potential_time: # only if time was fully provided
|
||||||
|
time_part = potential_time
|
||||||
|
|
||||||
|
if date and date.isValid():
|
||||||
|
formatted_date = date.toString("dd.MM.yyyy")
|
||||||
|
self.line_edit.setText(f"{formatted_date} {time_part}")
|
||||||
|
self.line_edit.setStyleSheet("")
|
||||||
|
else:
|
||||||
|
self.line_edit.clear()
|
||||||
|
|
||||||
|
self.calendar.hide()
|
||||||
|
|
||||||
|
def set_datetime(
|
||||||
|
self,
|
||||||
|
dt: QDateTime,
|
||||||
|
) -> None:
|
||||||
|
if dt and dt.isValid():
|
||||||
|
self.line_edit.setText(dt.toString("dd.MM.yyyy hh:mm"))
|
||||||
|
self.line_edit.setStyleSheet("")
|
||||||
|
else:
|
||||||
|
self.line_edit.clear()
|
||||||
|
|
||||||
|
def set_pydatetime(
|
||||||
|
self,
|
||||||
|
dt: datetime.datetime,
|
||||||
|
raise_error: bool = False,
|
||||||
|
) -> None:
|
||||||
|
dt_to_set = QDateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, 0)
|
||||||
|
if raise_error and not dt_to_set.isValid():
|
||||||
|
raise ValueError("Could not parse datetime object correctly")
|
||||||
|
|
||||||
|
self.set_datetime(dt_to_set)
|
||||||
|
|
||||||
|
def get_datetime(self) -> QDateTime:
|
||||||
|
# valid or invalid QDateTime
|
||||||
|
return QDateTime.fromString(self.line_edit.text(), "dd.MM.yyyy hh:mm")
|
||||||
|
|
||||||
|
def get_pydatetime(self) -> datetime.datetime:
|
||||||
|
"""returns current value of the field as UTC datetime
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
datetime.datetime
|
||||||
|
UTC datetime object
|
||||||
|
"""
|
||||||
|
# valid or invalid datetime
|
||||||
|
dt = self.get_datetime()
|
||||||
|
py_dt = cast(datetime.datetime, dt.toPython())
|
||||||
|
py_dt = py_dt.replace(tzinfo=TIMEZONE_CEST)
|
||||||
|
|
||||||
|
return py_dt.astimezone(datetime.UTC)
|
||||||
|
|
||||||
|
def reset(self) -> None:
|
||||||
|
self.calendar.hide()
|
||||||
|
self.line_edit.clear()
|
||||||
|
|
||||||
|
|
||||||
class DynamicListWidget(QWidget):
|
class DynamicListWidget(QWidget):
|
||||||
"""
|
"""
|
||||||
a widget, which can generate and manage an arbitrary number of sub forms.
|
a widget, which can generate and manage an arbitrary number of sub forms.
|
||||||
@@ -3206,7 +3651,13 @@ class Page_Consulting_ConsultingSession(QWidget):
|
|||||||
self.user_field = QLineEdit(
|
self.user_field = QLineEdit(
|
||||||
placeholderText="wird über aktiven Nutzer bei Anlage gesetzt"
|
placeholderText="wird über aktiven Nutzer bei Anlage gesetzt"
|
||||||
)
|
)
|
||||||
self.timestamp = QLineEdit() # TODO change to custom date field
|
self.user_field.setReadOnly(True)
|
||||||
|
self.user_field.setProperty("styleClass", "stempel")
|
||||||
|
# ?? date or datetime?
|
||||||
|
# self.timestamp = FlexibleDateInput()
|
||||||
|
# self.timestamp.set_pydate(datetime.datetime.now())
|
||||||
|
self.timestamp = FlexibleDateTimeInput()
|
||||||
|
self.timestamp.set_pydatetime(datetime.datetime.now())
|
||||||
|
|
||||||
self.contact = QLineEdit(placeholderText="Freitext")
|
self.contact = QLineEdit(placeholderText="Freitext")
|
||||||
self.contact_type = QLineEdit(placeholderText="sollte Dropdown sein") # Dropdown
|
self.contact_type = QLineEdit(placeholderText="sollte Dropdown sein") # Dropdown
|
||||||
@@ -3260,7 +3711,7 @@ class Page_Consulting_ConsultingSession(QWidget):
|
|||||||
"[Page Consulting -- Consulting Session]: State = %s", self.STATE
|
"[Page Consulting -- Consulting Session]: State = %s", self.STATE
|
||||||
)
|
)
|
||||||
set_widget_value(self.user_field, self.STATE.user_name)
|
set_widget_value(self.user_field, self.STATE.user_name)
|
||||||
set_widget_value(self.timestamp, self.STATE.timestamp)
|
set_widget_value(self.timestamp, self.STATE.timestamp.astimezone(TIMEZONE_CEST))
|
||||||
set_widget_value(self.contact, self.STATE.partner)
|
set_widget_value(self.contact, self.STATE.partner)
|
||||||
set_widget_value(self.contact_type, self.STATE.interaction_scheme)
|
set_widget_value(self.contact_type, self.STATE.interaction_scheme)
|
||||||
set_widget_value(self.crm_link, self.STATE.topic)
|
set_widget_value(self.crm_link, self.STATE.topic)
|
||||||
@@ -3269,9 +3720,7 @@ class Page_Consulting_ConsultingSession(QWidget):
|
|||||||
|
|
||||||
def _sync_gui_to_state(self) -> None:
|
def _sync_gui_to_state(self) -> None:
|
||||||
# TODO: get timestamp from custom field self.STATE.timestamp
|
# TODO: get timestamp from custom field self.STATE.timestamp
|
||||||
self.STATE.timestamp = datetime.datetime.strptime(
|
self.STATE.timestamp = self.timestamp.get_pydatetime()
|
||||||
self.timestamp.text(), "%d.%m.%Y %H:%M:%S"
|
|
||||||
)
|
|
||||||
self.STATE.partner = self.contact.text()
|
self.STATE.partner = self.contact.text()
|
||||||
self.STATE.interaction_scheme = self.contact_type.text()
|
self.STATE.interaction_scheme = self.contact_type.text()
|
||||||
self.STATE.topic = self.crm_link.text() # TODO: change to correct link
|
self.STATE.topic = self.crm_link.text() # TODO: change to correct link
|
||||||
|
|||||||
Reference in New Issue
Block a user