UI/UX pass: readability, feedback, safer actions, DRK branding

Items 1–12 of the interface review:

- Larger UI font (UI_FONT_SIZE) across widgets, menus, dialogs, Treeview rows.
- DRK-red header bar; window title shows entry count; runtime-drawn red-cross
  icon (also written to data/icon.png for the launcher).
- Status bar colour-coded by severity; transient messages auto-clear after 8 s,
  queue-count messages stay.
- Tab 1: more form spacing, digits-only PLZ, explicit "Auswahl entfernen" +
  Delete key for queue rows, confirm on "Warteschlange leeren", modal on
  duplicate save.
- Tab 2: raw lat/lon columns replaced by one "Karte" column (checkmark / red
  "fehlt"); ▲/▼ arrow on the active sort column; hint line always shows the
  saved-entry count.
- "Karte öffnen" with no coordinates shows a dialog, not just a status line.
- Window size/position remembered between sessions (data/window.json).

Verified by building and driving the window under Homebrew Python 3.12 / Tk 9;
non-GUI smoke tests still pass. Docs updated (changelog, overview, improvements,
data-model, setup, dev-notes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-07 18:51:17 +02:00
parent 37c9642877
commit caecc60c7b
9 changed files with 349 additions and 103 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ code) and plots them on an interactive map.
- **Storage:** one CSV file, `data/orte.csv`
- **Map:** generated on demand as `data/karte.html` with [Folium](https://python-visualization.github.io/folium/) (Leaflet), opened in the default browser
- **Geocoding:** OpenStreetMap Nominatim via [`geopy`](https://geopy.readthedocs.io/), rate-limited to 1 request/second on a background thread
- **UI language:** German
- **UI language:** German; DRK-red header, enlarged font, colour-coded status bar, window position remembered
- **Persistence:** atomic CSV writes + rotating backups in `data/backups/`; log at `data/app.log`
- **Tests:** non-GUI smoke checks only (not yet a committed `tests/` suite)
- **Version control:** git initialized 2026-09-07
+28 -2
View File
@@ -2,6 +2,32 @@
Notable changes to the app. Newest first.
## 2026-09-07 — UI/UX pass
A round of usability improvements aimed at the non-technical user
(see [improvements.md](improvements.md#uiux-review-2026-09-07)).
- **Larger UI font** (`UI_FONT_SIZE = 11`) applied to all default fonts, menus,
dialogs and the Treeview (taller rows).
- **DRK-red header bar** with the app name; window title shows the total count
("… · 142 Einsätze"); simple red-cross window icon drawn at runtime (also
written to `data/icon.png` for the launcher).
- **Status bar** is now colour-coded by severity (grey/green/orange/red), and
transient messages auto-clear after 8 s back to a neutral state; queue-count
messages stay put.
- **Tab 1:** more spacing in the form; PLZ field accepts digits only;
"+ Hinzufügen" is a solid button; explicit **"Auswahl entfernen"** button and
<kbd>Delete</kbd> key for queue rows; **"Warteschlange leeren"** now asks for
confirmation; **duplicate save shows a modal** (list + "trotzdem speichern?")
instead of a transient hint.
- **Tab 2:** the raw `lat`/`lon` columns are replaced by a single **"Karte"**
column (`✓` / red `fehlt`); active sort column shows a ▲/▼ arrow; the hint
line always shows the saved-entry count.
- **"Karte öffnen"** with no coordinates now shows a dialog, not just a status
line.
- **Window size & position** are remembered between sessions
(`data/window.json`).
## 2026-09-07 — data safety, robustness, easier input
Addressed the top three findings from [improvements.md](improvements.md).
@@ -43,5 +69,5 @@ Addressed the top three findings from [improvements.md](improvements.md).
### Verification
Non-GUI smoke tests (helpers, `DataStore` backup/atomic/recovery, geocoder
resilience) pass. **The GUI could not be run on the development Mac** — see
[dev-notes.md](dev-notes.md).
resilience) pass. The GUI now *can* be built and driven on the dev Mac via a
Homebrew Python with Tk 9 — see [dev-notes.md](dev-notes.md).
+11
View File
@@ -59,3 +59,14 @@ closed.
Rotating log file (512 KB × 3 generations). Git-ignored. Records saves, map
generation, geocoding failures, and uncaught exceptions.
## `data/window.json`
`{"geometry": "<w>x<h>+<x>+<y>"}` the window size and position, saved on close
and restored on start. Git-ignored, per-machine. Safe to delete (window opens at
its default size).
## `data/icon.png`
The red-cross window icon, drawn at first run and written here so the `.desktop`
launcher can point at it. Git-ignored (regenerated). Delete to force a redraw.
+20 -12
View File
@@ -3,23 +3,31 @@
Running list so the same mistakes aren't repeated. Add to it whenever reality
contradicts an assumption.
## The GUI does not run on the development Mac (2026-09-07)
## The macOS system Python can't run the GUI; use a Homebrew Python (2026-09-07)
The macOS **CommandLineTools** Python 3.9 (`/Library/Developer/CommandLineTools/
.../python3.9`) ships **Tk 8.5.9**. `ttkbootstrap` 1.10.1 requires **Tk 8.6+**.
Symptoms: `TclError: couldn't recognize image data` (window icon) and
`TclError: unknown option "-style"` on `ttk.Scrollbar`.
Symptoms when trying to launch `App()` there:
- `_tkinter.TclError: couldn't recognize image data` (ttkbootstrap's window icon
is a PNG; Tk 8.5 can't decode it), and
- `_tkinter.TclError: unknown option "-style"` on `ttk.Scrollbar`.
**Fix for local GUI testing:** `brew install python-tk@3.12` (pulls `tcl-tk`,
gives Python 3.12 + **Tk 9.0**), then:
Consequences:
- `app.py` **cannot be smoke-tested through the GUI on this machine.** Logic is
covered by a non-GUI script (constructs `DataStore`, exercises helpers and the
worker with monkey-patched paths).
- The **Linux Mint target is unaffected** — its system Tk is 8.6.
- If GUI testing on macOS is ever needed, install a Homebrew
`python-tk@3.x` / `tcl-tk` combo, or use `pyenv` with `--with-tcltk`.
```bash
/opt/homebrew/bin/python3.12 -m venv venv
venv/bin/pip install -r requirements.txt
venv/bin/python app.py
```
`ttkbootstrap` 1.10.1 runs fine on Tk 9.0 in practice (verified: window builds,
Treeview sort, DateEntry, LabelFrame, custom styles all work).
- The **Linux Mint target** uses its own `python3-tk` (Tk 8.6+) and is
unaffected either way.
- `screencapture` from a non-GUI shell fails ("could not create image from
display") without Screen-Recording permission — automated screenshots of the
running app aren't available here; verify by building the widget tree and
driving it programmatically instead.
## `pandas` reads everything as strings by design
+24 -1
View File
@@ -188,9 +188,32 @@ tile pack — larger effort).
---
## UI/UX review (2026-09-07)
Second review, focused on the interface. Items 112 implemented; 13 deferred.
| # | Change | Status |
|---|--------|--------|
| 1 | Larger base font + taller Treeview rows (`UI_FONT_SIZE`) | ✅ |
| 2 | Colour-coded status bar + 8 s auto-clear to a neutral state | ✅ |
| 3 | "Warteschlange leeren" asks for confirmation; more form spacing | ✅ |
| 4 | ▲/▼ arrow on the active sort column in Tab 2 | ✅ |
| 5 | Dropped the now-redundant `(JJJJ-MM-TT)` hint | ✅ |
| 6 | Tab 2: single **"Karte"** column (`✓` / red `fehlt`) instead of raw lat/lon | ✅ |
| 7 | Explicit "Auswahl entfernen" button + <kbd>Delete</kbd> key for queue rows | ✅ |
| 8 | Duplicate save shows a modal with the list, not a transient hint | ✅ |
| 9 | Persistent entry count (window title + Tab 2 hint line) | ✅ |
| 10 | "Karte öffnen" with no coordinates shows a dialog | ✅ |
| 11 | Remember window size & position (`data/window.json`) | ✅ |
| 12 | DRK-red header bar + runtime-drawn red-cross icon (`data/icon.png`) | ✅ |
| 13 | One-click "Hinzufügen & sofort speichern" | ⬜ deferred |
Full custom DRK-red *theme* (recolouring `primary` etc.) was **not** done — the
red header bar gives the branding without fighting ttkbootstrap's theme system.
## Suggested order of remaining work
1. Initial git commit (item 4), then decide on committing `orte.csv`.
2. Pinned deps + `run.sh` / `.desktop` launcher in the repo (item 5).
3. Stable `id` column (item 7) and a modal duplicate prompt (item 8).
3. Stable `id` column (item 7 of the first review).
4. Formalize tests (item 10); offline map (item 9) only if offline use becomes real.
+23 -12
View File
@@ -1,6 +1,11 @@
# Overview what the app does
The app is a single window with a status bar at the bottom and two tabs.
The app is a single window: a **DRK-red header bar**, two tabs, and a
**colour-coded status bar** at the bottom (grey = neutral, green = success,
orange = warning, red = error; transient messages fade back to neutral after
~8 s). The window title shows the total entry count, and the window remembers
its size and position between sessions (`data/window.json`). The UI font is
enlarged for readability (`UI_FONT_SIZE`).
## Tab 1 "Neuer Eintrag" (new entry)
@@ -12,28 +17,34 @@ then save the whole batch at once.
`TT.MM.JJJJ` and **validated** an invalid date is rejected with a dialog),
city (autocomplete combobox), postal code (optional). Pressing `Return` in any
field, or the **+ Hinzufügen** button, adds the row to the queue.
The PLZ field only accepts digits.
2. **Queue table** shows date, city, PLZ, and a live coordinate column that
updates from `⏳ wird gesucht…` to either `lat / lon` or `⚠ nicht gefunden`
as the background geocoder works through the queue.
- Left-click the `✕` cell to remove a row.
- Select a row and press <kbd>Delete</kbd>, use the **"Auswahl entfernen"**
button, or click the `✕` cell.
- Right-click a row for *Löschen* / *Koordinaten erneut suchen*.
3. **Action bar**
- **Alle speichern** appends every queued row to `orte.csv`. If a row looks
like a duplicate of an existing entry (same date + city + PLZ) a hint is
shown in the status bar, but the row is **still saved**.
- **Leeren** discards the queue without saving.
- **Alle speichern** appends every queued row to `orte.csv`. If any row
looks like a duplicate (same date + city + PLZ), a **modal** lists them and
asks whether to save anyway.
- **Warteschlange leeren** discards the queue (asks for confirmation).
- **Karte öffnen** regenerates `karte.html` and opens it in the browser.
## Tab 2 "Einträge verwalten" (manage entries)
A table view of everything in `orte.csv`.
- **Columns:** Datum, Ort, PLZ, and **Karte** a status column showing `✓` when
the row has coordinates or a red `fehlt` when it doesn't (the whole row is red
too). The raw lat/lon numbers live in the edit dialog, not this table.
- **Search box** live filter across date, city, and PLZ (substring match).
- **Sortable columns** click a header to sort; clicking again reverses.
Default sort is by date, newest first.
- **Sortable columns** click a header to sort; clicking again reverses. The
active column shows a ▲/▼ arrow. Default sort is by date, newest first.
Sorting by **Karte** groups the rows without coordinates together.
- **"Nur ohne Koordinaten"** toggle filters to entries that have no
coordinates yet. Such rows are also shown in **red** in the full list, and the
hint line reports how many there are.
coordinates yet. The hint line always shows the total count, plus how many are
missing coordinates.
- **Edit** double-click a row (or *Bearbeiten*) opens a modal dialog to change
date (calendar picker, validated) / city / PLZ. Two ways to fix coordinates:
a "Koordinaten automatisch neu suchen" toggle re-runs geocoding after saving,
@@ -58,8 +69,8 @@ Generated by `generate_map()`:
(`5 + count / max_count * 20` px), colour is DRK red (`#CC0000`).
- Popup shows the city and the visit count; tooltip shows the city.
- Rows with missing/blank coordinates are silently excluded.
- If there are no usable coordinates at all, a `ValueError` is raised and shown
in the status bar.
- If there are no usable coordinates at all, a dialog says so (and the status
bar shows a warning).
> The generated HTML pulls Leaflet, jQuery and Bootstrap from CDNs, so the map
> needs an internet connection to render even though the data is local.
+5 -4
View File
@@ -73,14 +73,15 @@ Type=Application
Name=DRK Blutspende Arbeitsorte
Comment=Einsatzorte protokollieren und auf der Karte anzeigen
Exec=/home/USER/Apps/DRK_Blutspende_Orte/run.sh
Icon=/home/USER/Apps/DRK_Blutspende_Orte/icon.png
Icon=/home/USER/Apps/DRK_Blutspende_Orte/data/icon.png
Terminal=false
Categories=Utility;
```
Replace `USER` with the real username. Add any PNG as `icon.png` (the DRK logo
works well). The entry then shows up in the Mint menu and can be pinned to the
panel or the desktop.
Replace `USER` with the real username. The app writes a simple red-cross
`data/icon.png` on first run; drop in the real DRK logo at that path if you have
one. The entry then shows up in the Mint menu and can be pinned to the panel or
the desktop.
## Python / Tk version