From 0b6ed854dcf8cdfd9328cb9367166a7b9d901332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCller?= Date: Mon, 7 Sep 2026 22:41:10 +0200 Subject: [PATCH] Fix right-click context menus firing their first item on Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bound to with finally: menu.grab_release(), the menu lost its grab immediately and the trailing activated the entry under the cursor — right-clicking a history row jumped straight into the edit dialog and deleting was impossible. - RIGHT_CLICK constant: ( on macOS) - history + queue menus built once on self, not per click - drop the premature grab_release() Co-Authored-By: Claude Sonnet 5 --- app.py | 50 +++++++++++++++++++++++++++++------------------ docs/changelog.md | 9 +++++++++ docs/dev-notes.md | 14 +++++++++++++ docs/overview.md | 2 +- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/app.py b/app.py index b8b3b47..9449e9f 100755 --- a/app.py +++ b/app.py @@ -54,6 +54,10 @@ UI_FONT_SIZE = 11 #: DRK-Rot – für Akzente in der Oberfläche und die Kartenpunkte. DRK_RED = "#CC0000" +#: Rechtsklick-Event. ```` statt ````: sonst landet +#: unter X11 das Loslassen der Maustaste direkt im ersten Menüeintrag. +RIGHT_CLICK = "" if sys.platform == "darwin" else "" + #: Regionen, die bei der Geokodierung bevorzugt durchsucht werden (Reihenfolge zählt). GEOCODE_REGIONS = ["Baden-Württemberg", "Hessen"] @@ -1050,8 +1054,16 @@ class App(ttk.Window): self.tree.pack(side=LEFT, fill=BOTH, expand=True) sb.pack(side=RIGHT, fill=Y) + self._queue_menu = tk.Menu(self, tearoff=0) + self._queue_menu.add_command( + label="Entfernen", command=self._delete_selected_queue_rows + ) + self._queue_menu.add_command( + label="Koordinaten erneut suchen", command=self._retry_selected_geocode + ) + self.tree.bind("", self._on_tree_click) - self.tree.bind("", self._on_queue_right_click) + self.tree.bind(RIGHT_CLICK, self._on_queue_right_click) self.tree.bind("", lambda _e: self._delete_selected_queue_rows()) # Aktionsleiste @@ -1113,8 +1125,16 @@ class App(ttk.Window): self.hist_tree.tag_configure("missing", foreground=DRK_RED) + self._hist_menu = tk.Menu(self, tearoff=0) + self._hist_menu.add_command(label="Bearbeiten…", command=self._on_hist_edit) + self._hist_menu.add_command( + label="Koordinaten suchen", command=self._on_hist_geocode + ) + self._hist_menu.add_separator() + self._hist_menu.add_command(label="Löschen", command=self._on_hist_delete) + self.hist_tree.bind("", self._on_hist_double_click) - self.hist_tree.bind("", self._on_hist_right_click) + self.hist_tree.bind(RIGHT_CLICK, self._on_hist_right_click) self._sort_col = "date" self._sort_asc = False # neueste zuerst @@ -1212,15 +1232,8 @@ class App(ttk.Window): if not iid: return self.hist_tree.selection_set(iid) - menu = tk.Menu(self, tearoff=0) - menu.add_command(label="Bearbeiten", command=self._on_hist_edit) - menu.add_command(label="Koordinaten suchen", command=self._on_hist_geocode) - menu.add_separator() - menu.add_command(label="Löschen", command=self._on_hist_delete) - try: - menu.tk_popup(event.x_root, event.y_root) - finally: - menu.grab_release() + self.hist_tree.focus(iid) + self._hist_menu.tk_popup(event.x_root, event.y_root) def _on_hist_geocode(self) -> None: sel = self.hist_tree.selection() @@ -1406,14 +1419,13 @@ class App(ttk.Window): row_id = self.tree.identify_row(event.y) if not row_id: return - menu = tk.Menu(self, tearoff=0) - menu.add_command(label="Löschen", command=lambda: self._delete_queue_row(row_id)) - menu.add_command(label="Koordinaten erneut suchen", - command=lambda: self._retry_geocode(row_id)) - try: - menu.tk_popup(event.x_root, event.y_root) - finally: - menu.grab_release() + if row_id not in self.tree.selection(): + self.tree.selection_set(row_id) + self._queue_menu.tk_popup(event.x_root, event.y_root) + + def _retry_selected_geocode(self) -> None: + for row_id in self.tree.selection(): + self._retry_geocode(row_id) def _delete_queue_row(self, row_id: str) -> None: self._queue = [r for r in self._queue if r["_id"] != row_id] diff --git a/docs/changelog.md b/docs/changelog.md index 4b7bc31..13d23c3 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,15 @@ Notable changes to the app. Newest first. +## 2026-09-07 — fix: right-click context menu instantly triggered its first item + +On Linux the Tab 2 (and queue) right-click menu flashed and immediately ran +"Bearbeiten" / "Löschen", so entries couldn't be deleted. Cause: the menu was +bound to `` (press) and `finally: menu.grab_release()` dropped the +grab, so the following `` selected the item under the cursor. +Fix: bind `` (`` on macOS), build each menu +once, and drop the premature `grab_release()`. + ## 2026-09-07 — map tiles → Esri (OSM 403s local files) The switch to `tiles="OpenStreetMap"` worked on the dev machine but the target diff --git a/docs/dev-notes.md b/docs/dev-notes.md index 1e1513c..cf508cf 100644 --- a/docs/dev-notes.md +++ b/docs/dev-notes.md @@ -57,6 +57,20 @@ Lesson: a tile source that works from `curl` or an `http://` page can still fail from `file://`. Test the map by **opening the generated `karte.html` directly**, not just by checking the URL in the HTML. +## Tk popup menus: bind ``, not `` (2026-09-07) + +`widget.bind("", …)` + `menu.tk_popup(x, y)` + `finally: +menu.grab_release()` is the Windows idiom and **misfires on X11**: `tk_popup` +returns immediately, `grab_release()` drops the menu's grab, and the pending +`` then activates whatever entry is under the cursor (the first +one). Symptom here: right-click a history row → menu flashes → edit dialog opens. + +Rules for this codebase: +- bind the release event: `RIGHT_CLICK` constant (``, or + `` on macOS), +- build each context menu **once** (store on `self`), don't recreate per click, +- just call `menu.tk_popup(...)` — no `grab_release()`. + ## Nominatim query order matters `geocode_city` returns the **first** hit, trying PLZ-qualified first, then each diff --git a/docs/overview.md b/docs/overview.md index a57a20d..ceea0f8 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -26,7 +26,7 @@ then save the whole batch at once. as the background geocoder works through the queue. - Select a row and press Delete, use the **"Auswahl entfernen"** button, or click the `✕` cell. - - Right-click a row for *Löschen* / *Koordinaten erneut suchen*. + - Right-click a row for *Entfernen* / *Koordinaten erneut suchen*. 3. **Action bar** - **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