Big thanks to Paul Liecke for these changes which will improve performance significantly. 1. **Scoped the event filter instead of installing it on the whole QApplication.** The original `app.installEventFilter(self)` meant a Python call for literally every event in the entire QGIS process — every paint, timer, tooltip, layout and repaint event of every widget, not just mouse movement on the canvas. On a busy session that's tens of thousands of C++→Python round-trips per second, and it's the main reason the whole application felt sluggish, not just the popup. I moved mouse handling off the event filter entirely and onto `QgsMapCanvas.xyCoordinates`, which reports map coordinates on every canvas move regardless of which map tool is active or holds the mouse grab. The event filter itself is now only installed on the canvas viewport (Leave/Wheel/MouseButtonPress) plus the QApplication instance for just KeyPress/KeyRelease — and for those the very first thing it does is a cheap `event.type()` comparison and returns `False` immediately for anything irrelevant. So the app-wide reach is kept only for the one thing that genuinely needs it (Shift/Escape still reaching the plugin after a pan/select operation grabs focus elsewhere), while all the actual work — hover detection, popup rendering — happens off far cheaper, targeted signals. 2. **Added a result fingerprint so the popup doesn't fully rebuild on every timer tick.** Previously `_do_query` called `set_content()` + `show_at()` every time the hover timer fired, even if the cursor was still sitting over the exact same feature. That meant rebuilding the HTML body, `setHtml()` parsing/layout, tearing down and recreating the footer widgets, resizing and repainting the drop shadow — every ~90ms while the mouse was merely resting. Now I compute a `(layer_id, feature_id)` signature per query; if it's unchanged and the popup is already visible, it just repositions instead of re-rendering. 3. **Cached the expensive per-field lookups.** `editorWidgetSetup()` and `fieldFormatterRegistry()` lookups for visible fields and value formatters were being redone for every field, on every single query. These essentially never change at runtime, so they're now cached per layer id (and per layer+field for formatters), invalidated on CRS change, layer removal, or tool reactivation. 4. **Added `setSubsetOfAttributes()` to the feature request** so the provider only fetches the fields that are actually shown in the popup, instead of pulling full attribute rows for every hit. 5. **Added a one-time warning when a layer has no spatial index.** Without one, the `filterRect` query degenerates into a full table scan on every mouse move — for anyone working with large shapefiles without a `.qix`, this is often the single biggest factor. Net effect: the plugin no longer touches anything outside the canvas/keyboard path it actually needs, and it stops doing full render work on every idle tick. I kept all your Qt5/Qt6 and QGIS 3.x/4.x compatibility shims untouched — tested on 3.44, and the affected code paths don't rely on anything removed in 4.x.
yes
mapian
2026-07-26T00:29:15.988026+00:00
3.22.0
4.99.0
None
no
Plugin Tags