From 2c0ce2b0164ab26d308c639c750e3d5d32ad6378 Mon Sep 17 00:00:00 2001 From: Daniel Nagel Date: Tue, 27 Jan 2026 08:37:25 +0000 Subject: [PATCH] =?UTF-8?q?UI=20Fenster=20Gr=C3=B6=C3=9Fe=20Fehler=20Fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/database.cpython-312.pyc | Bin 3214 -> 3244 bytes database.py | 3 +- gui/__pycache__/main_gui.cpython-312.pyc | Bin 6437 -> 6977 bytes gui/main_gui.py | 192 ++++++++++++----------- 4 files changed, 105 insertions(+), 90 deletions(-) diff --git a/__pycache__/database.cpython-312.pyc b/__pycache__/database.cpython-312.pyc index b2e32f731b3e2eaa363a1b5473b8087ca6da452e..6c928f992813fd553823762dcc28134fcc1daeca 100644 GIT binary patch delta 112 zcmeB^TqDVQnwOW00SLq@Dl)q^@}6O2Tsrv{V}+m&mjV(<&QICw%GA%q#U{e>g@KKg z^Mlaldn}bqj9Qx`*jbqv%{Rw!dN46MOkYSa|YX?(GtajN+d>7`cI@6epwVXEAO@ J^&&x_5&-Cc9kKub diff --git a/database.py b/database.py index 22928a7..38e5e5e 100644 --- a/database.py +++ b/database.py @@ -10,7 +10,8 @@ def initialize_db(): cursor.execute(''' CREATE TABLE IF NOT EXISTS ats ( id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT UNIQUE + name TEXT UNIQUE, + code TEXT UNIQUE ) ''') diff --git a/gui/__pycache__/main_gui.cpython-312.pyc b/gui/__pycache__/main_gui.cpython-312.pyc index 0764cc4cfd68b2c1f9d98005343fd06688ab6849..ee1f4d87cc4a328e75832ad75d775b0bb39b2b73 100644 GIT binary patch delta 3028 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 NN<|j{5#EBb{SQHH3K0MR diff --git a/gui/main_gui.py b/gui/main_gui.py index 1efa9a8..cfa350c 100644 --- a/gui/main_gui.py +++ b/gui/main_gui.py @@ -2,95 +2,109 @@ from nicegui import ui from datetime import datetime import database - def build_ui(): - ui.colors(primary='#e30013') #Firetruck Red - with ui.card().classes("w-full h-25").style("background-color: #e30013"): - dark = ui.dark_mode(True) + ui.colors(primary='#e30013') + + # 1. HAUPT-CONTAINER: + # Spalte die exakt so hoch ist wie der Bildschirm (h-screen). + # 'items-stretch' sorgt dafür, dass die Kinder (Header, Mitte, Footer) die volle Breite nutzen. + with ui.column().classes('w-full h-screen no-wrap gap-0 items-stretch'): + + # --------------------------------------------------------- + # BEREICH OBEN (Header) - Feste Höhe (h-25) + # --------------------------------------------------------- + with ui.card().classes("h-25 rounded-none").style("background-color: #e30013"): + # Hinweis: rounded-none entfernt die abgerundeten Ecken, damit es nahtlos aussieht + ui.image('gui/logo.png').classes('absolute top-4 right-4 w-55') + + # --------------------------------------------------------- + # BEREICH MITTE (Inhalt) - Variable Höhe (flex-1) + # --------------------------------------------------------- + # 'flex-1' ist hier entscheidend. Es sagt: "Nimm allen Platz zwischen Header und Footer". + # 'overflow-hidden' verhindert, dass das ganze Fenster scrollt -> wir wollen nur die Tabelle scrollen. + with ui.row().classes("w-full flex-1 p-4 gap-4 no-wrap overflow-hidden"): + + # --- Linke Karte: Eingabemaske --- + # 'overflow-y-auto': Falls das Formular zu lang für kleine Bildschirme wird, bekommt nur diese Karte einen Scrollbalken. + with ui.card().classes('overflow-y-auto'): + + traeger = database.get_ats_names() + input_name = ui.select(label="ATS Träger Name", options=traeger, with_input=True).classes('w-full') + + input_location = ui.input(label='Einsatzort', placeholder='Adresse, oder Beschreibung').classes("w-full") + + with ui.row().classes('gap-10'): + today = datetime.now().strftime("%Y-%m-%d") + with ui.input('Datum', value=today).classes('flex-1') as date_input: + with date_input.add_slot('append'): + ui.icon('event').classes('cursor-pointer') \ + .on('click', lambda: menu.open()) + with ui.menu() as menu: + input_date = ui.date().bind_value(date_input) + + input_time = ui.number(label="Dauer (Min)", value=0).classes('flex-1') + + with ui.row().classes("gap-10 w-full items-center"): + input_type = ui.toggle(["Einsatz", "Übung"]).classes('flex-1 center') + input_device = ui.select(["ATS Gerät 1", "ATS Gerät 2"], label="ATS Gerät auswählen").classes('flex-1') + + def InputDataToTable(): + name = input_name.value + location = input_location.value + date = input_date.value + time = input_time.value + etype = input_type.value + device = input_device.value + # Hinweis: database Aufruf ist hier korrekt, sofern importiert + database.add_data_to_einsaetze(name, location, date, time, etype, device) + + einsaetze_table.rows = database.get_einsaetze() + einsaetze_table.update() + ui.notify("Eintrag in Datenbank erstellt.") + + def ClearForm(): + input_name.value = "" + input_location.value = "" + input_date.value = today + input_time.value = 0 + input_type.value = "Einsatz" # Achtung: Value sollte zum Toggle passen (String, nicht 0) + input_device.value = None # Select resetten + ui.notify("Gelöscht.") + + ui.separator() + ui.space() # Drückt die Buttons nach unten (innerhalb der Formular-Karte) + with ui.row().classes('w-full'): + ui.button(text="Leeren", on_click=ClearForm) + ui.space() + ui.button(text="Eintragen", on_click=InputDataToTable) + + # --- Rechte Karte: Tabelle --- + # 'flex-1': Diese Karte nimmt die restliche Breite der Reihe ein. + # 'h-full': Diese Karte nutzt die volle Höhe des mittleren Bereichs. + with ui.card().classes('flex-1 h-full p-0'): + collums = [ + {'name': 'date', 'label': 'Datum', 'field': 'date', 'sortable': True, 'align': 'left'}, + {'name': 'name', 'label': 'Name', 'field': 'name', 'sortable': True, 'align': 'left'}, + {'name': 'location', 'label': 'Ort', 'field': 'location', 'align': 'left'}, + {'name': 'time', 'label': 'Dauer', 'field': 'time'}, + {'name': 'etype', 'label': 'Art', 'field': 'etype'}, + {'name': 'device', 'label': 'Gerät', 'field': 'device'}, + ] + # Die Tabelle selbst bekommt auch h-full, damit sie die Karte ausfüllt + einsaetze_table = ui.table( + columns = collums, + rows = database.get_einsaetze(), + row_key = "id" + ).classes("w-full h-full") + + # --------------------------------------------------------- + # BEREICH UNTEN (Footer) - Automatische Höhe + # --------------------------------------------------------- + # Hier landet dein Switch. Durch 'flex-1' im mittleren Bereich wird dieser Teil + # immer an den unteren Bildschirmrand geschoben. - ui.image('gui/logo.png').classes('absolute top-4 right-4 w-55') - with ui.row().classes("w-full"): - with ui.card(): - # ATS Träger Dropdown Menü - traeger = database.get_ats_names() - input_name = ui.select(label="ATS Träger Name", options=traeger, with_input=True).classes('w-full') - # Einsatzname - input_location = ui.input(label='Einsatzort', placeholder='Adresse, oder Beschreibung').classes("w-full") + with ui.row().classes("p-2 pb-6 items-center"): + dark = ui.dark_mode(True) + # Der Switch ist nun schön in einer Leiste unten + ui.switch('Dark mode').bind_value(dark) - with ui.row().classes('gap-10'): - # --- Der DATUMS-PICKER (Pop-Up) --- - today = datetime.now().strftime("%Y-%m-%d") - - with ui.input('Datum', value=today).classes('flex-1') as date_input: - # Das Icon im Eingabefeld (zum Klicken) - with date_input.add_slot('append'): - ui.icon('event').classes('cursor-pointer') \ - .on('click', lambda: menu.open()) # Öffnet das Menü - # Das Pop-Up Menü mit dem Kalender - with ui.menu() as menu: - input_date = ui.date().bind_value(date_input) - - # --- Zeit Eingabe in Minuten --- - input_time = ui.number(label="Dauer (Min)", value=0).classes('flex-1') - - with ui.row().classes("gap-10 w-full items-center"): - input_type = ui.toggle(["Einsatz", "Übung"]).classes('flex-1 center') - input_device = ui.select(["ATS Gerät 1", "ATS Gerät 2"], label="ATS Gerät auswählen").classes('flex-1') - - def InputDataToTable(): - name = input_name.value - location = input_location.value - date = input_date.value - time = input_time.value - etype = input_type.value - device = input_device.value - database.add_data_to_einsaetze(name, location, date, time, etype, device) - print (name, location, date, time) - - einsaetze_table.rows = database.get_einsaetze() - einsaetze_table.update() - - ui.notify("Eintrag in Datenbank erstellt.") - - def ClearForm(): - input_name.value = "" - input_location.value = "" - input_date.value = today - input_time.value = 0 - input_type.value = 0 - input_device.value = 0 - ui.notify("Gelöscht.") - - ui.separator() - ui.space() - with ui.row().classes('w-full'): - ui.button(text="Leeren", on_click=ClearForm) - ui.space() - ui.button(text="Eintragen", on_click=InputDataToTable) - - - with ui.card().classes('flex-1 h-[85vh] p-0'): - collums = [ - {'name': 'date', 'label': 'Datum', 'field': 'date', 'sortable': True, 'align': 'left'}, - {'name': 'name', 'label': 'Name', 'field': 'name', 'sortable': True, 'align': 'left'}, - {'name': 'location', 'label': 'Ort', 'field': 'location', 'align': 'left'}, - {'name': 'time', 'label': 'Dauer', 'field': 'time'}, - {'name': 'etype', 'label': 'Art', 'field': 'etype'}, - {'name': 'device', 'label': 'Gerät', 'field': 'device'}, - ] - einsaetze_table = ui.table( - columns = collums, - rows = database.get_einsaetze(), - row_key = "id" - ).classes("w-full h-full") - - ui.switch('Dark mode').bind_value(dark) - - - - - - - - -