generated from dopt-python/py311
further prototyping, added first DB interactions
This commit is contained in:
142
prototypes/db_access.py
Normal file
142
prototypes/db_access.py
Normal file
@@ -0,0 +1,142 @@
|
||||
# %%
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
|
||||
import polars as pl
|
||||
import sqlalchemy as sql
|
||||
|
||||
from wce_crm import db
|
||||
|
||||
importlib.reload(db)
|
||||
|
||||
# %%
|
||||
PTH_DATA_DB = Path.cwd().parent / "data/db"
|
||||
assert PTH_DATA_DB.exists()
|
||||
assert PTH_DATA_DB.is_dir()
|
||||
# %%
|
||||
DB_KL = PTH_DATA_DB / "wce_kontaktliste.db"
|
||||
DB_CRM = PTH_DATA_DB / "wce_crm.db"
|
||||
assert DB_KL.exists()
|
||||
assert DB_CRM.exists()
|
||||
# %%
|
||||
engine = sql.create_engine(f"sqlite:///{DB_CRM}")
|
||||
# %%
|
||||
db.df_crm_master
|
||||
# %%
|
||||
stmt = sql.select(db.ext_crm_master)
|
||||
str(stmt.compile(engine))
|
||||
df = pl.read_database(stmt, engine, schema_overrides=db.ext_crm_master_schema)
|
||||
# df = pl.concat([df, df[:2]])
|
||||
# %%
|
||||
df.select("ma_unternehmensname").is_duplicated().sum()
|
||||
# %%
|
||||
q = df.lazy()
|
||||
counter = pl.int_range(0, pl.len()).over(pl.col.ma_unternehmensname)
|
||||
q = q.with_columns(
|
||||
ma_unternehmensname_dedupl=pl.when(counter == 0)
|
||||
.then(pl.col.ma_unternehmensname)
|
||||
.otherwise(pl.format("{} ({})", pl.col.ma_unternehmensname, counter))
|
||||
)
|
||||
df = q.collect()
|
||||
df.select("ma_unternehmensname_dedupl").is_duplicated().sum()
|
||||
|
||||
# %%
|
||||
# mapping dedupl text to idx
|
||||
df.head()
|
||||
|
||||
# dict(zip(df["ma_unternehmensname_dedupl"], df["ma_id"]))
|
||||
|
||||
# %%
|
||||
sub = df[0]
|
||||
sub
|
||||
# sub.with_columns(
|
||||
# # pl.when(pl.col(pl.Boolean)).then(pl.lit("Ja")).otherwise(pl.lit("Nein"))
|
||||
# pl.when(pl.col(pl.Boolean)).then(pl.lit("Ja")).otherwise(pl.lit("Nein")).name.keep()
|
||||
# )
|
||||
# %%
|
||||
q = (
|
||||
sub.lazy()
|
||||
.with_columns(
|
||||
pl.col(pl.Datetime).dt.to_string("%d.%m.%Y"),
|
||||
pl.col(pl.Date).dt.to_string("%d.%m.%Y"),
|
||||
pl.when(pl.col(pl.Boolean)).then(pl.lit("Ja")).otherwise(pl.lit("Nein")).name.keep(),
|
||||
)
|
||||
.with_columns(pl.all().cast(pl.String))
|
||||
)
|
||||
sub = q.collect()
|
||||
sub
|
||||
# %%
|
||||
df.row(0, named=True)
|
||||
|
||||
# %%
|
||||
db.df_crm_master.estimated_size("mb")
|
||||
|
||||
|
||||
# %%
|
||||
# // CRM Nutzer
|
||||
stmt = sql.select(db.ext_crm_nutzer).limit(20)
|
||||
str(stmt.compile(engine))
|
||||
df = pl.read_database(stmt, engine, schema_overrides=db.ext_crm_nutzer_schema)
|
||||
# %%
|
||||
stmt = sql.text("""SELECT ma_unternehmensname, ma_ersteintrag_datum, ma_aktualisierung_datum
|
||||
FROM Master
|
||||
WHERE ma_ersteintrag_datum LIKE '%ff'
|
||||
LIMIT 10;""")
|
||||
|
||||
with engine.connect() as con:
|
||||
res = con.execute(stmt)
|
||||
|
||||
print(res.fetchall())
|
||||
|
||||
# %%
|
||||
# ----------------------------------------------------------------
|
||||
engine = sql.create_engine(f"sqlite:///{DB_KL}")
|
||||
stmt = sql.select(db.ext_kl_unternehmen.c.u_firmenname).limit(20)
|
||||
|
||||
with engine.connect() as con:
|
||||
res = con.execute(stmt)
|
||||
|
||||
res.scalars().all()
|
||||
# %%
|
||||
for _ in res.mappings():
|
||||
print(_)
|
||||
# %%
|
||||
# %%
|
||||
stmt = sql.select(db.ext_kl_unternehmen)
|
||||
df = pl.read_database(stmt, engine, schema_overrides=db.ext_kl_unternehmen_schema)
|
||||
# %%
|
||||
df
|
||||
# %%
|
||||
df.estimated_size("mb")
|
||||
# %%
|
||||
df.height
|
||||
# %%
|
||||
db.df_kontaktliste
|
||||
# %%
|
||||
sub = db.df_kontaktliste.select(["u_id", "u_firmenname"]).lazy()
|
||||
# %%
|
||||
counter = pl.int_range(0, pl.len()).over(pl.col.u_firmenname)
|
||||
sub = sub.with_columns(
|
||||
t=pl.when(counter == 0)
|
||||
.then(pl.col.u_firmenname)
|
||||
.otherwise(pl.format("{} ({})", pl.col.u_firmenname, counter))
|
||||
)
|
||||
# %%
|
||||
sub.collect()
|
||||
|
||||
# %%
|
||||
# 1. Create a sample DataFrame
|
||||
df = pl.DataFrame({"text_col": ["TEST", "APPLE", "TEST", "TEST", "BANANA", "APPLE"]})
|
||||
|
||||
# 2. Define the window function to count occurrences
|
||||
# This generates a sequence [0, 1, 2...] for each unique string
|
||||
counter = pl.int_range(0, pl.len()).over("text_col")
|
||||
|
||||
# 3. Apply the conditional formatting
|
||||
df = df.with_columns(
|
||||
updated_col=pl.when(counter == 0)
|
||||
.then(pl.col("text_col")) # Keep original for the first occurrence
|
||||
.otherwise(pl.format("{} ({})", pl.col("text_col"), counter)) # Format duplicates
|
||||
)
|
||||
# %%
|
||||
df
|
||||
@@ -1,24 +1,30 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses as dc
|
||||
import enum
|
||||
import sys
|
||||
import time
|
||||
from collections.abc import Sequence
|
||||
|
||||
from PySide6.QtCore import Qt, Signal # Signal ist wichtig!
|
||||
from PySide6.QtCore import QDate, Qt, QTimer, Signal # Signal ist wichtig!
|
||||
from PySide6.QtGui import QAction
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QComboBox,
|
||||
QCompleter,
|
||||
QDateEdit,
|
||||
QDialog,
|
||||
QDialogButtonBox,
|
||||
QFormLayout,
|
||||
QFrame,
|
||||
QGridLayout,
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QListWidget,
|
||||
QMainWindow,
|
||||
QMessageBox,
|
||||
QPlainTextEdit,
|
||||
QPushButton,
|
||||
QScrollArea,
|
||||
@@ -28,6 +34,22 @@ from PySide6.QtWidgets import (
|
||||
QWidget,
|
||||
)
|
||||
|
||||
from wce_crm.backend import initial_recording as be_init_rec
|
||||
|
||||
QSS = """
|
||||
*[styleClass="stempel"] {
|
||||
background-color: #f1f5f9;
|
||||
color: #333D4B;
|
||||
border: 1px dashed #cbd5e1;
|
||||
border-radius: 4px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
*[styleClass="stempel"]:focus {
|
||||
border: 1px dashed #cbd5e1;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
@dc.dataclass(slots=True)
|
||||
class Address:
|
||||
@@ -134,16 +156,24 @@ class AddressForm_Search(QWidget):
|
||||
super().__init__()
|
||||
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
form_layout = QFormLayout()
|
||||
form_layout.setSpacing(15)
|
||||
form_layout.setSpacing(10)
|
||||
|
||||
# title
|
||||
title = QLabel("--- Suche Unternehmen ---")
|
||||
title.setStyleSheet("font-size: 14px; font-style: italic;") # font-weight: bold;
|
||||
main_layout.addWidget(title)
|
||||
|
||||
self.search_input = QLineEdit(placeholderText="Tippen zum Suchen...")
|
||||
form_layout.addRow("Suche:", self.search_input)
|
||||
search_data = [addr.name for addr in ADDRESSES]
|
||||
self.SEARCH_MAP = {addr.name: addr for addr in ADDRESSES}
|
||||
self.completer = QCompleter(search_data)
|
||||
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
|
||||
self.completer.setFilterMode(Qt.MatchContains)
|
||||
# search_data = [addr.name for addr in ADDRESSES]
|
||||
# self.SEARCH_MAP = {addr.name: addr for addr in ADDRESSES}
|
||||
self.SEARCH_MAP = be_init_rec.comp_search_choice_mapping()
|
||||
self.search_data = tuple(self.SEARCH_MAP.keys())
|
||||
self.completer = QCompleter(self.search_data)
|
||||
self.completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
|
||||
self.completer.setFilterMode(Qt.MatchFlag.MatchContains)
|
||||
self.search_input.setCompleter(self.completer)
|
||||
self.completer.activated.connect(self.search_result_selected)
|
||||
|
||||
@@ -207,15 +237,23 @@ class AddressForm_Search(QWidget):
|
||||
}
|
||||
""")
|
||||
|
||||
def fill_out(self, address: Address):
|
||||
addr_ = address.export()
|
||||
def fill_out(self, comp_info: be_init_rec.CompanyInfo):
|
||||
# addr_ = address.export()
|
||||
|
||||
for field, value in zip(self.autofilled_fields, addr_.values()):
|
||||
field.setText(value)
|
||||
# for field, value in zip(self.autofilled_fields, addr_.values()):
|
||||
# field.setText(value)
|
||||
self.company_input.setText(comp_info["ma_unternehmensname"])
|
||||
self.street_input.setText(comp_info["ma_strasse"])
|
||||
self.number_input.setText(comp_info["ma_hausnummer"])
|
||||
self.zip_input.setText(comp_info["ma_plz"])
|
||||
self.city_input.setText(comp_info["ma_ort"])
|
||||
|
||||
def search_result_selected(self, name):
|
||||
address = self.SEARCH_MAP[name]
|
||||
self.fill_out(address)
|
||||
|
||||
comp_info = be_init_rec.comp_search_get_info(
|
||||
ma_id=self.SEARCH_MAP[name],
|
||||
)
|
||||
self.fill_out(comp_info)
|
||||
|
||||
|
||||
class DropdownSearch(QWidget):
|
||||
@@ -242,11 +280,11 @@ class DropdownSearch(QWidget):
|
||||
|
||||
# --- WICHTIGE EINSTELLUNGEN ---
|
||||
# Ignoriert Groß-/Kleinschreibung (sehr wichtig für eine gute Suche!)
|
||||
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
|
||||
self.completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
|
||||
|
||||
# 'MatchContains' sorgt dafür, dass "pha" auch "Projekt Alpha" findet.
|
||||
# Standard ist 'MatchStartsWith' (findet nur Worte am Anfang).
|
||||
self.completer.setFilterMode(Qt.MatchContains)
|
||||
self.completer.setFilterMode(Qt.MatchFlag.MatchContains)
|
||||
|
||||
# 4. Den Completer an das Eingabefeld binden
|
||||
self.search_input.setCompleter(self.completer)
|
||||
@@ -261,78 +299,405 @@ class DropdownSearch(QWidget):
|
||||
# Hier könntest du z.B. deine Detail-Seite für dieses Projekt öffnen
|
||||
|
||||
|
||||
class FormFieldType(enum.StrEnum):
|
||||
TEXT = enum.auto()
|
||||
LONGTEXT = enum.auto()
|
||||
DATE = enum.auto()
|
||||
DATETIME = enum.auto()
|
||||
|
||||
|
||||
@dc.dataclass(slots=True)
|
||||
class FormField:
|
||||
key: str
|
||||
label: str
|
||||
type: FormFieldType
|
||||
required: bool
|
||||
placeholder: str | None = None
|
||||
fill_value: str | None = None
|
||||
readonly: bool = False
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.label = self.label.strip()
|
||||
if not self.label.endswith(":"):
|
||||
self.label += ":"
|
||||
if self.required:
|
||||
self.label += "*"
|
||||
|
||||
|
||||
@dc.dataclass(slots=True)
|
||||
class FormFieldGroup:
|
||||
key: str
|
||||
label: str
|
||||
fields: Sequence[FormField]
|
||||
|
||||
|
||||
FORM_FIELD_DEF = [
|
||||
FormField("name", "Projektname", FormFieldType.TEXT, True, "Bitte füllen..."),
|
||||
FormField("descr", "Beschreibung", FormFieldType.LONGTEXT, False),
|
||||
FormField(
|
||||
"ext_data",
|
||||
"Externe Daten",
|
||||
FormFieldType.TEXT,
|
||||
True,
|
||||
fill_value="Lorem ipsum und so weiter...",
|
||||
readonly=True,
|
||||
),
|
||||
FormField("init_date", "Auftragsdatum", FormFieldType.DATE, True),
|
||||
FormField("start_date", "Startdatum", FormFieldType.DATE, True),
|
||||
FormField(
|
||||
"ms_date1",
|
||||
"MS Datum 1",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
"",
|
||||
fill_value="26.07.2026",
|
||||
readonly=True,
|
||||
),
|
||||
FormField(
|
||||
"ms_date2",
|
||||
"MS Datum 2",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
"",
|
||||
fill_value="30.08.2026",
|
||||
readonly=False,
|
||||
),
|
||||
FormField(
|
||||
"important:notes",
|
||||
"Wichtige Notizen",
|
||||
FormFieldType.LONGTEXT,
|
||||
True,
|
||||
"Text eingeben...",
|
||||
),
|
||||
]
|
||||
|
||||
FORM_FIELD_DEF2 = [
|
||||
FormField("name", "Projektname", FormFieldType.TEXT, True, "Bitte füllen..."),
|
||||
FormField("descr", "Beschreibung", FormFieldType.LONGTEXT, False),
|
||||
FormField(
|
||||
"ext_data",
|
||||
"Externe Daten",
|
||||
FormFieldType.TEXT,
|
||||
True,
|
||||
fill_value="Lorem ipsum und so weiter...",
|
||||
readonly=True,
|
||||
),
|
||||
FormField("init_date", "Auftragsdatum", FormFieldType.DATE, True),
|
||||
FormField("start_date", "Startdatum", FormFieldType.DATE, True),
|
||||
FormField(
|
||||
"ms_date1",
|
||||
"MS Datum 1",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
"",
|
||||
fill_value="26.07.2026",
|
||||
readonly=True,
|
||||
),
|
||||
FormField(
|
||||
"ms_date2",
|
||||
"MS Datum 2",
|
||||
FormFieldType.DATE,
|
||||
False,
|
||||
"",
|
||||
fill_value="30.08.2026",
|
||||
readonly=False,
|
||||
),
|
||||
FormField(
|
||||
"important:notes",
|
||||
"Wichtige Notizen",
|
||||
FormFieldType.LONGTEXT,
|
||||
True,
|
||||
"Text eingeben...",
|
||||
),
|
||||
]
|
||||
|
||||
FORM_FIELD_GROUPS = [
|
||||
FormFieldGroup("group1", "Test-1", FORM_FIELD_DEF),
|
||||
FormFieldGroup("group2", "Test-2", FORM_FIELD_DEF2),
|
||||
]
|
||||
|
||||
|
||||
class MyForm(QWidget):
|
||||
def __init__(self):
|
||||
def __init__(
|
||||
self,
|
||||
form_field_groups: Sequence[FormFieldGroup],
|
||||
add_buttons: bool = True,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
# --- LAYOUT ---
|
||||
self.main_layout = QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.form_field_groups = form_field_groups
|
||||
|
||||
# Das Herzstück: Das Form-Layout
|
||||
# Es kümmert sich automatisch darum, dass alle Labels links
|
||||
# und alle Felder rechts perfekt bündig untereinander stehen.
|
||||
self.form_layout = QFormLayout(self)
|
||||
self.form_layout.setSpacing(15) # Abstand zwischen den Zeilen
|
||||
for fg in form_field_groups:
|
||||
widget = MyFormPart(fg.fields, fg.label, add_buttons=False)
|
||||
self.main_layout.addWidget(widget)
|
||||
|
||||
# Definition deiner Felder
|
||||
# 'key' ist der Name, unter dem du die Daten später abrufst
|
||||
# 'label' ist der Text, der angezeigt wird
|
||||
# 'type' bestimmt, welches Widget erstellt wird
|
||||
self.field_definitions = [
|
||||
{"key": "name", "label": "Projektname:", "type": "text"},
|
||||
{"key": "date", "label": "Datum:", "type": "date", "value": "22.04.2026"},
|
||||
{
|
||||
"key": "status",
|
||||
"label": "Status:",
|
||||
"type": "text",
|
||||
"placeholder": "z.B. Aktiv",
|
||||
},
|
||||
{"key": "desc", "label": "Beschreibung:", "type": "longtext"},
|
||||
{"key": "notes", "label": "Interne Notizen:", "type": "longtext"},
|
||||
]
|
||||
# buttons
|
||||
# self.add_buttons = add_buttons
|
||||
# if self.add_buttons:
|
||||
# self.layout_btn = QHBoxLayout()
|
||||
# self.main_layout.addLayout(self.layout_btn)
|
||||
# self.save_btn_txt_enabled = "Speichern (Strg + S)"
|
||||
# self.save_btn_txt_disabled = "Wird gespeichert..."
|
||||
# self.save_btn = QPushButton(self.save_btn_txt_enabled)
|
||||
# self.save_btn.setShortcut("Ctrl+S")
|
||||
# self.save_btn.setFixedHeight(50)
|
||||
# self.save_btn.setSizePolicy(
|
||||
# QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
|
||||
# )
|
||||
# self.save_btn.clicked.connect(self.on_save_clicked)
|
||||
# self.layout_btn.addWidget(self.save_btn)
|
||||
# self.reset_btn = QPushButton("Zurücksetzen (Strg + Z)")
|
||||
# self.reset_btn.setShortcut("Ctrl+Z")
|
||||
# self.reset_btn.setFixedHeight(50)
|
||||
# self.reset_btn.setSizePolicy(
|
||||
# QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
|
||||
# )
|
||||
# self.reset_btn.clicked.connect(self.reset_form)
|
||||
# self.layout_btn.addWidget(self.reset_btn)
|
||||
|
||||
# Dictionary, um die erstellten Widgets zu speichern (für späteren Zugriff)
|
||||
self.widgets = {}
|
||||
|
||||
# Automatischer Aufbau des Formulars
|
||||
class MyFormPart(QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
field_definitons: Sequence[FormField],
|
||||
group_name: str | None = None,
|
||||
add_buttons: bool = False,
|
||||
):
|
||||
super().__init__()
|
||||
self.setStyleSheet("""
|
||||
QGroupBox {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
border: 1px solid #cbd5e1; /* Heller, moderner Rahmen */
|
||||
border-radius: 8px; /* Abgerundete Ecken */
|
||||
margin-top: 15px; /* Platz für die Überschrift schaffen */
|
||||
padding-top: 15px; /* Abstand zwischen Rahmen und erstem Feld */
|
||||
}
|
||||
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top left; /* Überschrift oben links */
|
||||
padding: 0 5px; /* Etwas Luft links und rechts vom Text */
|
||||
color: #334155; /* Dunkelgraue Schrift */
|
||||
}
|
||||
""")
|
||||
|
||||
# --- LAYOUT ---
|
||||
self.main_layout = QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.form_layout = QFormLayout()
|
||||
self.group_box: QGroupBox | None = None
|
||||
self.group_name = group_name
|
||||
|
||||
if self.group_name:
|
||||
self.group_box = QGroupBox(group_name)
|
||||
self.main_layout.addWidget(self.group_box)
|
||||
self.group_box.setLayout(self.form_layout)
|
||||
else:
|
||||
self.main_layout.addLayout(self.form_layout)
|
||||
|
||||
self.form_layout.setSpacing(10) # Abstand zwischen den Zeilen
|
||||
|
||||
self.field_definitions = field_definitons
|
||||
self.widgets: dict[str, QWidget] = {}
|
||||
# automatic build
|
||||
self.create_form_fields()
|
||||
|
||||
def create_form_fields(self):
|
||||
# buttons
|
||||
self.add_buttons = add_buttons
|
||||
if self.add_buttons:
|
||||
self.layout_btn = QHBoxLayout()
|
||||
self.main_layout.addLayout(self.layout_btn)
|
||||
self.save_btn_txt_enabled = "Speichern (Strg + S)"
|
||||
self.save_btn_txt_disabled = "Wird gespeichert..."
|
||||
self.save_btn = QPushButton(self.save_btn_txt_enabled)
|
||||
self.save_btn.setShortcut("Ctrl+S")
|
||||
self.save_btn.setFixedHeight(50)
|
||||
self.save_btn.setSizePolicy(
|
||||
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
|
||||
)
|
||||
self.save_btn.clicked.connect(self.on_save_clicked)
|
||||
self.layout_btn.addWidget(self.save_btn)
|
||||
self.reset_btn = QPushButton("Zurücksetzen (Strg + Z)")
|
||||
self.reset_btn.setShortcut("Ctrl+Z")
|
||||
self.reset_btn.setFixedHeight(50)
|
||||
self.reset_btn.setSizePolicy(
|
||||
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed
|
||||
)
|
||||
self.reset_btn.clicked.connect(self.reset_form)
|
||||
self.layout_btn.addWidget(self.reset_btn)
|
||||
|
||||
def create_form_fields(self) -> None:
|
||||
for field in self.field_definitions:
|
||||
widget = None
|
||||
|
||||
# Entscheidung: Welches Widget wird benötigt?
|
||||
if field["type"] == "text":
|
||||
widget = QLineEdit()
|
||||
if "placeholder" in field:
|
||||
widget.setPlaceholderText(field["placeholder"])
|
||||
if "value" in field:
|
||||
widget.setText(field["value"])
|
||||
widget.setReadOnly(True) # Falls es ein Fixwert ist
|
||||
match field.type:
|
||||
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")
|
||||
|
||||
elif field["type"] == "longtext":
|
||||
widget = QPlainTextEdit()
|
||||
widget.setMaximumHeight(80) # Kompakte Höhe für Formulare
|
||||
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")
|
||||
|
||||
elif field["type"] == "date":
|
||||
widget = QLineEdit() # Oder QDateEdit
|
||||
widget.setText(field.get("value", ""))
|
||||
widget.setReadOnly(True)
|
||||
widget.setStyleSheet("background-color: #f1f5f9; border: 1px dashed #cbd5e1;")
|
||||
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)
|
||||
|
||||
case _:
|
||||
raise NotImplementedError(f"Not supported field type: {field.type.value}")
|
||||
|
||||
if widget:
|
||||
# Widget im Dictionary speichern, um später darauf zuzugreifen
|
||||
self.widgets[field["key"]] = widget
|
||||
# Dem Form-Layout hinzufügen (Label links, Widget rechts)
|
||||
self.form_layout.addRow(field["label"], widget)
|
||||
self.widgets[field.key] = widget
|
||||
self.form_layout.addRow(field.label, widget)
|
||||
|
||||
def get_form_data(self):
|
||||
def get_form_data(self) -> ...:
|
||||
"""Liest alle Felder automatisch aus"""
|
||||
data = {}
|
||||
for key, widget in self.widgets.items():
|
||||
if isinstance(widget, QLineEdit):
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
data[key] = widget.text()
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
data[key] = widget.toPlainText()
|
||||
|
||||
return data
|
||||
|
||||
def _disable_save(self) -> None:
|
||||
self.save_btn.setEnabled(False)
|
||||
self.save_btn.setText(self.save_btn_txt_disabled)
|
||||
|
||||
def _enable_save(
|
||||
self,
|
||||
timeout: int = 3000,
|
||||
) -> None:
|
||||
QTimer.singleShot(timeout, lambda: self.save_btn.setEnabled(True))
|
||||
QTimer.singleShot(timeout + 1, lambda: self.save_btn.setShortcut("Ctrl+S"))
|
||||
self.save_btn.setText(self.save_btn_txt_enabled)
|
||||
|
||||
def on_save_clicked(self) -> None:
|
||||
self._disable_save()
|
||||
errors = [] # Hier sammeln wir die Namen der fehlenden Felder
|
||||
|
||||
# time.sleep(0.5)
|
||||
|
||||
# Wir gehen unsere Feld-Definitionen durch
|
||||
for field in self.field_definitions:
|
||||
widget = self.widgets[field.key]
|
||||
|
||||
# 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("")
|
||||
|
||||
# 2. Ist es überhaupt ein Pflichtfeld?
|
||||
if not field.required:
|
||||
continue
|
||||
|
||||
is_empty = False
|
||||
if isinstance(widget, (QLineEdit, QDateEdit)):
|
||||
if not widget.text().strip():
|
||||
is_empty = True
|
||||
|
||||
elif isinstance(widget, QPlainTextEdit):
|
||||
if not widget.toPlainText().strip():
|
||||
is_empty = True
|
||||
|
||||
if not is_empty:
|
||||
continue
|
||||
|
||||
errors.append(
|
||||
field.label.replace("*", "").replace(":", "")
|
||||
) # Sternchen für die Fehlermeldung entfernen
|
||||
|
||||
# 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:
|
||||
# Es gibt Fehler! Speichern abbrechen und Pop-up anzeigen.
|
||||
error_text = "Bitte fülle die folgenden Pflichtfelder aus:\n\n- " + "\n- ".join(
|
||||
errors
|
||||
)
|
||||
QMessageBox.warning(self, "Fehlende Angaben", error_text)
|
||||
self._enable_save()
|
||||
return
|
||||
|
||||
# Wenn wir hier ankommen, ist die Liste 'errors' leer. Alles ist korrekt ausgefüllt!
|
||||
# time.sleep(0.5)
|
||||
print("Erfolg! Alle Daten sind valide.")
|
||||
self.reset_form()
|
||||
self._enable_save()
|
||||
|
||||
def reset_form(self) -> None:
|
||||
for field in self.field_definitions:
|
||||
widget = self.widgets[field.key]
|
||||
|
||||
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())
|
||||
|
||||
widget.setStyleSheet("")
|
||||
|
||||
|
||||
class ClickableCell(QFrame):
|
||||
# Wir definieren ein Signal, das ein Dictionary (die Daten) mitschickt
|
||||
@@ -355,11 +720,11 @@ class ClickableCell(QFrame):
|
||||
layout = QVBoxLayout(self)
|
||||
label = QLabel(text)
|
||||
label.setWordWrap(True)
|
||||
label.setAlignment(Qt.AlignCenter)
|
||||
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
layout.addWidget(label)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.LeftButton:
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
# Wenn geklickt wird, senden wir die Daten aus
|
||||
self.clicked.emit(self.data_record)
|
||||
|
||||
@@ -369,7 +734,7 @@ class HeaderCell(QLabel):
|
||||
super().__init__(text)
|
||||
|
||||
# Textausrichtung zentrieren
|
||||
self.setAlignment(Qt.AlignCenter)
|
||||
self.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
# Styling: Fetter Text, grauer Hintergrund (entspricht slate-200), leicht abgerundet
|
||||
self.setStyleSheet("""
|
||||
@@ -407,7 +772,9 @@ class NewEntryDialog(QDialog):
|
||||
layout.addRow("Datum:", self.input_date)
|
||||
|
||||
# Standard-Buttons (OK und Abbrechen)
|
||||
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
buttons = QDialogButtonBox(
|
||||
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
||||
)
|
||||
buttons.accepted.connect(self.accept) # Schließt Dialog und meldet "Erfolg"
|
||||
buttons.rejected.connect(self.reject) # Schließt Dialog und meldet "Abbruch"
|
||||
layout.addWidget(buttons)
|
||||
@@ -431,7 +798,7 @@ class DetailView(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||
layout.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft)
|
||||
|
||||
# Zurück-Button
|
||||
back_btn = QPushButton("← Zurück zur Tabelle")
|
||||
@@ -470,7 +837,7 @@ class NewEntrySelect_view(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||
layout.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft)
|
||||
|
||||
# Zurück-Button
|
||||
back_btn = QPushButton("← Zurück zur Übersicht")
|
||||
@@ -524,38 +891,65 @@ class SearchFormPage(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# Hauptlayout der Seite
|
||||
main_layout = QVBoxLayout(self)
|
||||
outer_layout = QHBoxLayout(self)
|
||||
vert_layout = QVBoxLayout()
|
||||
# main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
outer_layout.addStretch(1)
|
||||
outer_layout.addLayout(vert_layout, stretch=100)
|
||||
# outer_layout.addWidget(scroll_area, stretch=100)
|
||||
outer_layout.addStretch(1)
|
||||
# Optional: Damit der Container oben am Rand klebt
|
||||
outer_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
# --- 1. HEADER ---
|
||||
header_layout = QVBoxLayout()
|
||||
upper_button_group_v = QVBoxLayout()
|
||||
upper_button_group = QHBoxLayout()
|
||||
upper_button_group.addLayout(upper_button_group_v)
|
||||
upper_button_group.addStretch()
|
||||
# header_layout = QGridLayout()
|
||||
# header_layout.setColumnStretch(0, 1)
|
||||
# header_layout.setColumnStretch(1, 1)
|
||||
# --- HEADER ---
|
||||
header_container = QWidget()
|
||||
header_container.setMinimumWidth(700)
|
||||
header_container.setMaximumWidth(1000)
|
||||
header_container.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
header_layout = QVBoxLayout(header_container)
|
||||
header_layout.setContentsMargins(0, 0, 0, 10)
|
||||
|
||||
back_btn_main = QPushButton("← Zurück zur Übersicht")
|
||||
back_btn_main.clicked.connect(lambda: self.back_main_requested.emit())
|
||||
back_btn_main.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
back_btn_main.setMinimumWidth(200)
|
||||
back_btn_main.setMaximumWidth(200)
|
||||
back_btn_step = QPushButton("← Zurück")
|
||||
back_btn_step.clicked.connect(lambda: self.back_requested.emit())
|
||||
back_btn_step.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
back_btn_step.setMinimumWidth(200)
|
||||
back_btn_step.setMaximumWidth(200)
|
||||
|
||||
title = QLabel("Grunderfassung Unternehmen")
|
||||
title.setStyleSheet("font-size: 20px; font-weight: bold;")
|
||||
|
||||
upper_button_group_v.addWidget(back_btn_step)
|
||||
upper_button_group_v.addWidget(back_btn_main)
|
||||
|
||||
header_layout.addLayout(upper_button_group)
|
||||
header_layout.addSpacing(15)
|
||||
# header_layout.addWidget(back_btn_step)
|
||||
header_layout.setSpacing(5)
|
||||
header_layout.addWidget(back_btn_step)
|
||||
header_layout.addWidget(back_btn_main)
|
||||
header_layout.addWidget(title)
|
||||
# header_layout.addStretch() # Drückt den Titel nach links
|
||||
main_layout.addLayout(header_layout)
|
||||
vert_layout.addWidget(header_container)
|
||||
|
||||
# --- HAUPTINHALT ---
|
||||
container = QWidget()
|
||||
# SCROLL-BEREICH
|
||||
scroll_area = QScrollArea()
|
||||
scroll_area.setWidgetResizable(
|
||||
True
|
||||
) # WICHTIG: Erlaubt dem Grid im Inneren, sich an die Breite anzupassen
|
||||
scroll_area.setMinimumWidth(700)
|
||||
scroll_area.setMaximumWidth(1000)
|
||||
scroll_area.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
# Optional: Rahmen der ScrollArea entfernen, damit es "flacher" und moderner aussieht
|
||||
scroll_area.setFrameShape(QFrame.Shape.NoFrame)
|
||||
scroll_area.setWidget(container)
|
||||
|
||||
# vert_layout.addSpacing(20)
|
||||
vert_layout.addWidget(scroll_area)
|
||||
|
||||
# --- KOPF Metadaten ---
|
||||
container_layout = QVBoxLayout(container)
|
||||
container_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# --- KOPF Unternehmen ---
|
||||
inf_block_1 = QHBoxLayout()
|
||||
inhalte = [
|
||||
"Fall-Nr.:",
|
||||
@@ -565,7 +959,7 @@ class SearchFormPage(QWidget):
|
||||
]
|
||||
for entry in inhalte:
|
||||
label = QLabel(entry)
|
||||
label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
label.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
field = QLineEdit(placeholderText="...")
|
||||
field.setText("22.04.2026")
|
||||
field.setReadOnly(True)
|
||||
@@ -582,107 +976,47 @@ class SearchFormPage(QWidget):
|
||||
border: 1px #cbd5e1;
|
||||
}
|
||||
""")
|
||||
field.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
field.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
inf_block_1.addWidget(label)
|
||||
inf_block_1.addWidget(field)
|
||||
|
||||
inf_block_1.addStretch()
|
||||
main_layout.addLayout(inf_block_1)
|
||||
container_layout.addLayout(inf_block_1)
|
||||
|
||||
# --- NOTIZEN Unternehmen ---
|
||||
# eventuell später verknüpft
|
||||
inf_block_2 = QHBoxLayout()
|
||||
label = QLabel("Notizen:")
|
||||
label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
label.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
inf_block_2.addWidget(label, alignment=Qt.AlignmentFlag.AlignTop)
|
||||
inf_block_2.addWidget(QPlainTextEdit(placeholderText="Notizen ergänzen..."))
|
||||
|
||||
main_layout.addLayout(inf_block_2)
|
||||
main_layout.addSpacing(10)
|
||||
container_layout.addLayout(inf_block_2)
|
||||
container_layout.addSpacing(10)
|
||||
|
||||
# --- Suche mit Namen
|
||||
# inf_block_3 = (
|
||||
# QVBoxLayout()
|
||||
# ) # Horizontal, damit Suchen und Filtern nebeneinander stehen
|
||||
# name_input = QLineEdit(placeholderText="Tippe zum Suchen...")
|
||||
# name_input.setMinimumWidth(150)
|
||||
# name_input.setMaximumWidth(600)
|
||||
# inf_block_3_1 = QHBoxLayout()
|
||||
# inf_block_3_1.addWidget(QLabel("Name"))
|
||||
# inf_block_3_1.addWidget(name_input, stretch=100)
|
||||
# inf_block_3_1.addStretch()
|
||||
# --- Test Formularlayout ---
|
||||
container_layout.addSpacing(30)
|
||||
title = QLabel("--- Automatische Form ---")
|
||||
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(MyForm(FORM_FIELD_GROUPS))
|
||||
|
||||
# inf_block_3_2 = QHBoxLayout()
|
||||
# inf_block_3_3 = QHBoxLayout()
|
||||
# demo_data = {
|
||||
# "name": "Test UG",
|
||||
# "Straße": "Teststraße",
|
||||
# "Hausnummer": "12",
|
||||
# "PLZ": "09111",
|
||||
# "Ort": "Chemnitz",
|
||||
# }
|
||||
|
||||
# current_block = inf_block_3_2
|
||||
# for entry in ("Straße", "Hausnummer", "PLZ", "Ort"):
|
||||
# if entry == "PLZ":
|
||||
# current_block = inf_block_3_3
|
||||
|
||||
# label = QLabel(entry)
|
||||
# label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
# field = QLineEdit()
|
||||
# field.setText(demo_data[entry])
|
||||
# field.setReadOnly(True)
|
||||
# field.setStyleSheet("""
|
||||
# QLineEdit {
|
||||
# background-color: #f1f5f9; /* Helles System-Grau */
|
||||
# color: #333D4B; /* Etwas blassere Schrift */
|
||||
# border: 1px dashed #cbd5e1; /* Ein gestrichelter Rand wirkt oft wie ein "Stempel" */
|
||||
# border-radius: 4px;
|
||||
# padding: 5px;
|
||||
# }
|
||||
# /* Wenn das Feld fokussiert wird, keinen blauen Rand anzeigen */
|
||||
# QLineEdit:focus {
|
||||
# border: 1px dashed #cbd5e1;
|
||||
# }
|
||||
# """)
|
||||
# if entry in ("Hausnummer", "PLZ"):
|
||||
# field.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
# field.setMinimumWidth(50)
|
||||
# field.setMaximumWidth(50)
|
||||
# else:
|
||||
# field.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
# field.setMinimumWidth(100)
|
||||
# field.setMaximumWidth(300)
|
||||
# current_block.addWidget(label)
|
||||
# current_block.addWidget(field, stretch=100)
|
||||
|
||||
# inf_block_3_2.addStretch()
|
||||
# inf_block_3_3.addStretch()
|
||||
|
||||
# inf_block_3.addLayout(inf_block_3_1)
|
||||
# inf_block_3.addLayout(inf_block_3_2)
|
||||
# inf_block_3.addLayout(inf_block_3_3)
|
||||
# main_layout.addLayout(inf_block_3)
|
||||
|
||||
main_layout.addSpacing(30)
|
||||
|
||||
main_layout.addWidget(MyForm())
|
||||
|
||||
main_layout.addSpacing(30)
|
||||
container_layout.addSpacing(30)
|
||||
|
||||
# --- SUCHE MIT NAMEN ---
|
||||
# addr = Address("Test UG", "Teststraße", 202, "09111", "Chemnitz")
|
||||
# addr_widget = AddressForm()
|
||||
# addr_widget.fill_out(addr)
|
||||
addr_widget = AddressForm_Search()
|
||||
main_layout.addWidget(addr_widget)
|
||||
container_layout.addWidget(addr_widget)
|
||||
|
||||
main_layout.addSpacing(30)
|
||||
container_layout.addSpacing(30)
|
||||
|
||||
main_layout.addWidget(DropdownSearch())
|
||||
# container_layout.addWidget(DropdownSearch())
|
||||
|
||||
main_layout.addSpacing(30)
|
||||
container_layout.addSpacing(30)
|
||||
|
||||
# --- 2. SUCH-FORMULAR ---
|
||||
# --- SUCH-FORMULAR ---
|
||||
form_layout = (
|
||||
QHBoxLayout()
|
||||
) # Horizontal, damit Suchen und Filtern nebeneinander stehen
|
||||
@@ -702,12 +1036,12 @@ class SearchFormPage(QWidget):
|
||||
form_layout.addWidget(QLabel("Status:"))
|
||||
form_layout.addWidget(self.status_filter, stretch=1)
|
||||
|
||||
main_layout.addLayout(form_layout)
|
||||
container_layout.addLayout(form_layout)
|
||||
|
||||
# --- 3. ERGEBNIS-BEREICH ---
|
||||
# Für den Anfang ein einfaches Listen-Widget
|
||||
self.results_list = QListWidget()
|
||||
main_layout.addWidget(
|
||||
container_layout.addWidget(
|
||||
self.results_list, stretch=100
|
||||
) # Nimmt den restlichen Platz ein
|
||||
|
||||
@@ -740,7 +1074,7 @@ class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Master")
|
||||
self.resize(1800, 200)
|
||||
self.resize(1800, 1000)
|
||||
|
||||
# --- 1. DAS MENÜ ERSTELLEN ---
|
||||
self.create_menu()
|
||||
@@ -801,9 +1135,9 @@ class MainWindow(QMainWindow):
|
||||
scroll_area.setMaximumWidth(
|
||||
1500
|
||||
) # Die Breiten-Begrenzung wandert nun auf die ScrollArea
|
||||
scroll_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
scroll_area.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
# Optional: Rahmen der ScrollArea entfernen, damit es "flacher" und moderner aussieht
|
||||
scroll_area.setFrameShape(QFrame.NoFrame)
|
||||
scroll_area.setFrameShape(QFrame.Shape.NoFrame)
|
||||
scroll_area.setWidget(container)
|
||||
|
||||
vert_layout.addWidget(new_btn)
|
||||
@@ -817,7 +1151,7 @@ class MainWindow(QMainWindow):
|
||||
# outer_layout.addWidget(scroll_area, stretch=100)
|
||||
outer_layout.addStretch(1)
|
||||
# Optional: Damit der Container oben am Rand klebt
|
||||
outer_layout.setAlignment(Qt.AlignTop)
|
||||
outer_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
# Wir geben dem Container ein vertikales Layout
|
||||
container_layout = QVBoxLayout(container)
|
||||
@@ -961,6 +1295,7 @@ class MainWindow(QMainWindow):
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
app.setStyleSheet(QSS)
|
||||
window = MainWindow()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
@@ -1,28 +1,45 @@
|
||||
# %%
|
||||
import dataclasses as dc
|
||||
import enum
|
||||
|
||||
from PySide6.QtCore import QDate, Qt
|
||||
|
||||
|
||||
class FormFieldType(enum.StrEnum):
|
||||
TEXT = enum.auto()
|
||||
LONGTEXT = enum.auto()
|
||||
DATE = enum.auto()
|
||||
DATETIME = enum.auto()
|
||||
|
||||
|
||||
# %%
|
||||
@dc.dataclass(slots=True)
|
||||
class Address:
|
||||
street: str
|
||||
number: int
|
||||
postal_code: str
|
||||
city: str
|
||||
class FormField:
|
||||
key: str
|
||||
label: str
|
||||
type: FormFieldType
|
||||
required: bool
|
||||
|
||||
def export(self):
|
||||
data = {}
|
||||
for f in dc.fields(self):
|
||||
val = getattr(self, f.name)
|
||||
if f.type is int:
|
||||
val = str(val)
|
||||
data[f.name] = val
|
||||
|
||||
return data
|
||||
def __post_init__(self) -> None:
|
||||
self.label = self.label.strip()
|
||||
if not self.label.endswith(":"):
|
||||
self.label += ":"
|
||||
if self.required:
|
||||
self.label += "*"
|
||||
|
||||
|
||||
# %%
|
||||
addr = Address("Teststraße", 202, "09111", "Chemnitz")
|
||||
FormField("name", "Projektbeschreibung", FormFieldType.LONGTEXT, required=True)
|
||||
# %%
|
||||
FormField("name", "Projektbeschreibung:", FormFieldType.LONGTEXT, required=True)
|
||||
|
||||
# %%
|
||||
FormField("name", "Projektbeschreibung", FormFieldType.LONGTEXT, required=False)
|
||||
# %%
|
||||
FormField("name", "Projektbeschreibung:", FormFieldType.LONGTEXT, required=False)
|
||||
# %%
|
||||
addr.export()
|
||||
# %%
|
||||
set_date = QDate.fromString("26.07.2026", "dd.MM.yyyy")
|
||||
|
||||
# %%
|
||||
Qt.Tet
|
||||
|
||||
Reference in New Issue
Block a user