Repository navigation
Fix generic taskbar/Activities icon on Linux: ship and install the .desktop launchers / fixes #1667 - #1710
Fix generic taskbar/Activities icon on Linux: ship and install the .desktop launchers / fixes #1667#1710asikarastallion wants to merge 10 commits into
Conversation
1d2f237 to
6dc1e35
Compare
Previous reviewAutomated review note — AI-generated (Claude), validated against the live diff. Please sanity-check before acting. Reviewed at head Good news first, because it's the part most likely to be silently wrong and it isn't: The blocker: the command your ReadMe tells users to run doesn't exist in a pip install. And even if it were importable, The fix is small: move Two things that affect what this actually achieves. The Mission Editor icon won't appear in the GNOME taskbar regardless: console, map and mission editor all run as Worth knowing: no CI ran on this PR at all. Minor: the installer hardcodes |
|
Thanks — the packaging blocker was exactly right, and I reproduced it before changing anything. Building the previous head ( Pushed as four commits on top of 1. Packaging —
|
| script | WM_CLASS |
StartupWMClass |
|---|---|---|
mavproxy.py |
"mavproxy.py", "Mavproxy" |
mavproxy.py |
MAVExplorer.py |
"MAVExplorer.py", "Mavexplorer" |
MAVExplorer.py |
mavpicviewer.py |
"mavpicviewer.py", "Mavpicviewer" |
mavpicviewer.py |
3. Icon size — c8b4c8e
mavpicviewer.png padded symmetrically to a real 256×256 (artwork unchanged; re-encoding also took it from 124KB to 104KB). The installer now reads each PNG's IHDR and skips anything that isn't 256×256, so a wrongly-sized icon can't silently land in a directory claiming that size again.
4. matplotlib — c8e1ba9
Good catch. missionEditorFrame.py already defers its own matplotlib import in height_profile_pushed() and says why in a comment, so the module-scope icon import was working against the file's own intent. Moved inside the existing try:. Confirmed 'matplotlib' in sys.modules is False after importing the module.
On the forked children
Confirmed independently — a multiprocessing.Process child forked from a script named mavproxy.py reports the identical WM_CLASS(STRING) = "mavproxy.py", "Mavproxy". So GNOME does group console, map and mission editor under mavproxy.desktop and paints Icon=mavproxy for all of them.
I've retitled the PR accordingly, and the ReadMe now states this explicitly rather than implying the per-window SetIcon() drives the taskbar. The SetIcon() calls stay because _NET_WM_ICON is still used elsewhere (window lists, other desktops), but they are not what fixes #1667 — as you suggested, the ~/.icon → ~/.local/share/icons/hicolor/256x256/apps correction, plus actually installing the launcher, is.
Minor points
$XDG_DATA_HOMEis honoured, with relative values ignored per the XDG basedir spec.- Platform guard: returns non-zero on
win32/cygwin/darwininstead of scattering files. - The script now returns real exit codes rather than degrading quietly.
flake8
Added AP_FLAKE8_CLEAN to install_desktop_icons.py; scripts/run_flake8.py MAVProxy picks it up and passes with a plain pip install flake8, matching the workflow. I left the marker off icon.py and missionEditorFrame.py — both carry substantial pre-existing violations (405 in missionEditorFrame.py), so marking them would be a separate cleanup. CI still appears not to be approved to run on this fork branch.
One thing I did not touch
desktop-file-validate reports pre-existing Exec= quoting errors in all three launchers, including the untouched mavproxy.desktop (reserved ;, ' and ~ outside quotes). It predates this PR and fixing it changes launch behaviour, so it felt out of scope — happy to do it here or separately if you'd prefer.
The PR description above still describes the original shortcuts/… layout; say the word and I'll refresh it to match.
AI assistance disclosure, as before: Claude Code helped investigate and prepare these changes; I reviewed the diff and ran the verification above on my own machine.
Previous reviewAutomated review note — AI-generated (Claude), validated against the live diff. Please sanity-check before acting. Full report: https://uav.tridgell.net/DevCallReviews/followups/2026_09_04_2335/devcall_pr_reviews.html#prMAVProxy-1710 Re-reviewed at head I re-ran the same test that produced the blockers rather than reading
Two things still open, neither yours to fixNo CI has run on this PR at all. The A cross-check also flagged that Cosmetic: |
|
Two follow-ups since your re-review at
Head is now CI still reports |
Previous review (2026-09-04)Automated review note — AI-generated (Claude), validated against the live diff. Please sanity-check before acting. Full report: https://uav.tridgell.net/DevCallReviews/2026_09_05/devcall_pr_reviews.html#prMAVProxy-1710 Re-reviewed at head The entire delta since the last review is one mode bit: every blob under A cross-check pass added a useful qualification: it has no effect on helper-based installation, because The two things noted last round as open but not yours to fix are unchanged: no CI has ever run on this PR ( Worth a maintainer approving the workflow run before merge, since nothing here has ever been machine-checked. |
007bbb0 to
56547f3
Compare
Previous review (2026-09-17)Automated review note — AI-generated (Claude), validated against the live diff. Please sanity-check before acting. Full report: https://uav.tridgell.net/DevCallReviews/2026_09_17_AIReview/devcall_pr_reviews.html#prMAVProxy-1710 Re-reviewed at head Good news first: CI has now run, and it's green. That was the one blocking item from the last round. This head is a pure rebase — confirmed by blob SHA on all ten files and by Verdict: COMMENT. One thing worth a decision before merge. I got something wrong last round — please ignore itI claimed the launchers' The The one open questionISSUE — Same in the released PyPI wheel. The contrast confirms the diagnosis: The root cause is pre-existing and not yours — before this PR, Two ways out, both small: add Does it fix #1667? YesThe mechanism is confirmed, and I specifically re-checked the part most likely to have rotted: Python 3.14 changed the default Linux multiprocessing start method from Install paths, precisely: Notes
Everything you fixed in the two earlier rounds I re-verified empirically at this head rather than by reading the diff, and all of it holds: |
|
AP-Review identified that the newly packaged MAVPicViewer desktop launcher could not start from a wheel installation because Fixed in The desktop icon installer remains unchanged and still installs all three launchers and icons successfully with an isolated |
Previous review (2026-09-19)Automated review note — AI-generated (Claude), validated against the live diff. Please sanity-check before acting. Full report: followups/2026_09_20_0112 Re-reviewed at head Your fix landed — the one open issue from last round is resolved, and I verified it by building and installing rather than by reading RESOLVED — the MAVPicViewer packaging issueYou fixed it both ways round, which is what makes it work: Evidence — wheel contents at each head:
And the launcher itself, installed into a clean venv and run from I also checked the things your commit didn't explicitly name and they're all fine: One new thing, which only became visible because the fix landedISSUE — the menu entry still doesn't start after a plain
This is adjacent to your change rather than caused by it — those imports predate it. But it means the user story from the original finding still ends the same way: install, click MAVPicViewer in the app grid, terminal flashes a Worth noting NOTE — CI no longer reports at this head. Last round's headline was that the rebase had finally triggered the workflows and both jobs were green. The four notes from last round — unchanged, none blockingYour commit didn't touch any of these, and I re-verified rather than assuming:
|
|
Friendly ping: the current head (28b1986) has been ready since Sep 19, but the Windows Build and test Python cleanliness workflow runs are still awaiting maintainer approval. When convenient, could someone approve the workflow runs so CI can proceed? Thanks! |
|
@asikarastallion I've marked the workflows to run, but the fact that tridge force-pushed and then you force-pushed means I'm unlikely to merge this one myself. |
|
@asikarastallion apologies, needs rebase |
MissionEditorFrame never called SetIcon(), unlike the Console and Map windows which both use icon.SimpleIcon(...).get_ico(). This left the Mission Editor with no window icon at all. Add a "WP" icon variant (numbered waypoint route) to SimpleIcon and set it on MissionEditorFrame right after wx.Frame.__init__(), mirroring the existing try/except pattern used by wxconsole_ui.py. Related to ArduPilot#1667
GNOME's taskbar/Alt-Tab icon for a window comes from an installed .desktop file matching the window's WM_CLASS, not from the runtime SetIcon() call alone (see wxWidgets/wxWidgets#24034). MAVProxy shipped shortcuts/mavproxy.desktop but nothing installed it, and it had no StartupWMClass, so it never matched. - Add StartupWMClass=mavproxy.py to shortcuts/mavproxy.desktop (confirmed via `xprop WM_CLASS` against a running Console/Map window) - Add MAVProxy/tools/install_desktop_icons.py to copy the .desktop files and icons into ~/.local/share/{applications,icons/...} (pip install cannot do this reliably, especially into a venv) - Update shortcuts/ReadMe.md with corrected instructions Verified on Ubuntu 24.04 / GNOME (X11): before the fix, Console and Map showed a generic gear icon in Alt-Tab; after installing and restarting GNOME Shell (Alt+F2 -> r), both show the correct MAVProxy icon. Fixes ArduPilot#1667
E127 continuation line over-indented for visual indent
The install_desktop_icons helper and the files it installs were both absent from a pip install, so the feature was inert for exactly the users the ReadMe addresses. setup.py never listed MAVProxy.tools in packages, and shortcuts/ sat outside the MAVProxy package with no package_data glob or MANIFEST.in covering it: building a wheel from the previous head gave a MAVProxy/tools/ holding only graphs/*.xml, and no .desktop or .png anywhere in the archive. - Move shortcuts/ to MAVProxy/data/shortcuts/ and add data/shortcuts/* to package_data - Add MAVProxy.tools to packages, with an __init__.py - Resolve the shortcuts directory with importlib.resources.files() against the package rather than three dirnames up to the repo root. That is also correct under the zip_safe=True this setup declares, and matches how MAVExplorer.py already reaches MAVProxy.tools.graphs - Honour $XDG_DATA_HOME, ignoring a relative value as the XDG basedir spec requires, instead of hardcoding ~/.local/share - Refuse to run on win32/cygwin/darwin rather than scattering files across a system with no freedesktop icon theme - Return exit codes, and say so, when nothing could be installed - Check each icon's PNG header and skip anything that is not 256x256 rather than installing it into a directory that claims that size - Mark the file AP_FLAKE8_CLEAN so scripts/run_flake8.py checks it Verified by installing a wheel into a clean venv and running "python3 -m MAVProxy.tools.install_desktop_icons" from outside the checkout, so the source tree could not satisfy the import: the module resolves to site-packages and all six files install under $XDG_DATA_HOME. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nchers Only mavproxy.desktop carried the key, so the other two launchers still had nothing for GNOME Shell to match a running window against. The values are the WM_CLASS instance names wxWidgets derives from each script's name, probed against a real wxPython/GTK3 window opened by a script of that name: mavproxy.py "mavproxy.py", "Mavproxy" MAVExplorer.py "MAVExplorer.py", "Mavexplorer" mavpicviewer.py "mavpicviewer.py", "Mavpicviewer" gnome-shell matches StartupWMClass against res_class and then res_name, so it is the instance name that hits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
It was 245x256, but the installer puts it in the hicolor 256x256 directory, which by the icon theme spec holds icons of that size. Padded symmetrically with transparency, so the artwork is unchanged; re-encoding also took it from 124KB to 104KB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
icon.py imports matplotlib at module scope, so importing it from missionEditorFrame.py made the mission editor a hard consumer of a dependency it had deliberately kept optional - height_profile_pushed() already defers its own matplotlib import for exactly that reason, and says so in a comment. Move the import inside the try: that already wraps SetIcon(), so a missing matplotlib leaves the frame without a custom icon rather than failing to import at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
It was the only launcher at mode 100644 while mavproxy.desktop and mavexplorer.desktop are 100755. The installer chmods what it copies, so this only matters for the documented manual route of copying a .desktop file to the Desktop, where GNOME requires the executable bit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The desktop launcher added for MAVPicViewer exposed a pre-existing packaging issue: setup.py installed mavpicviewer.py as a script, but its sibling modules were not included in wheel/sdist installs. Package MAVProxy.tools.mavpicviewer and use package-qualified imports so the installed launcher can resolve its helper modules. This keeps the existing source/install workflows working while making the launcher usable from packaged MAVProxy installations.
28b1986 to
696d476
Compare
Previous review (2026-10-04)Automated review note — AI-generated (Claude), validated against the live diff. Please sanity-check before acting. Reviewed at head Re-reviewed at Still fixed: the MAVPicViewer packaging. The rebuilt wheel and sdist ship all six helper modules, and the installed launcher loads them from site-packages. Two small documentation points, worth doing in this PR:
Not for this PR, carried forward so it isn't lost: CI: the snapshot shows no checks for |
|
Automated review note — AI-generated (Claude+Codex), validated against the live diff. Please sanity-check before acting. Reviewed at head Re-reviewed at One small wording point on the new text:
Not for this PR, as before: Not tested: CI (the snapshot shows no checks for |
Summary
Fixes the generic "gear" taskbar/Activities icon reported in #1667.
On GNOME/GTK3 the taskbar icon for a running window does not come from
wx.Frame.SetIcon()at runtime. GNOME Shell matches the window'sWM_CLASSagainst theStartupWMClassof an installed.desktopfile and paints that file'sIcon=— see wxWidgets/wxWidgets#24034.MAVProxy had
.desktopfiles in the repo, but nothing installed them (not evenpip install), they carried noStartupWMClass, and the ReadMe pointed at~/.icon, which GTK never searches. So there was nothing for GNOME to match, and the windows fell back to a generic icon.Changes
Shipping and installing the launchers — the part that actually fixes #1667
Move
shortcuts/→MAVProxy/data/shortcuts/, so it lives inside the package, withdata/shortcuts/*added topackage_dataandMAVProxy.toolsadded topackages(plusMAVProxy/tools/__init__.py).Without this, neither the helper nor the files it installs existed in a pip install: a wheel built from the previous head had a
MAVProxy/tools/holding onlygraphs/*.xml, and no.desktopor.pnganywhere in the archive. The script degraded quietly rather than erroring, so the feature was simply inert for pip users — which is who the ReadMe addresses.MAVProxy/tools/install_desktop_icons.py— new opt-in helper:It copies the
.desktopfiles to$XDG_DATA_HOME/applicationsand the icons to$XDG_DATA_HOME/icons/hicolor/256x256/apps(the standard freedesktop icon theme location; the~/.iconpath the old ReadMe documented is never searched), then best-effort refreshes the desktop and icon caches. It resolves the packaged files withimportlib.resources.files('MAVProxy')rather than a path walked up from__file__, honours$XDG_DATA_HOME(ignoring a relative value, as the XDG basedir spec requires), refuses to run onwin32/cygwin/darwininstead of scattering files, returns real exit codes, and skips any icon whose PNG header is not 256×256.It is not run automatically by
pip install, since pip cannot reliably write to XDG user directories — especially into a venv, as in the original report.StartupWMClasson all three launchers. The values are theWM_CLASSinstance names wxWidgets derives from each script's name, probed against a live wxPython/GTK3 window opened by a script of that name:WM_CLASSStartupWMClassmavproxy.desktop"mavproxy.py", "Mavproxy"mavproxy.pymavexplorer.desktop"MAVExplorer.py", "Mavexplorer"MAVExplorer.pymavpicviewer.desktop"mavpicviewer.py", "Mavpicviewer"mavpicviewer.pyMAVProxy/data/shortcuts/ReadMe.md: corrected install instructions, points at the helper, documents why an installed.desktopfile is needed at all, and notes that GNOME Shell may need a restart (Alt+F2→ron X11, log out/in on Wayland) to pick up a newly-installed.desktopfile.MAVProxy/data/shortcuts/mavpicviewer.png: was 245×256 while being installed into the256x256theme directory. Padded symmetrically with transparency, so the artwork is unchanged; re-encoding also took it from 124KB to 104KB.Window icons
MAVProxy/modules/lib/icon.py: add a"WP"icon (dashed route with numbered waypoint markers) toSimpleIcon.MAVProxy/modules/mavproxy_misseditor/missionEditorFrame.py:MissionEditorFramenever calledSetIcon(), unlikeConsoleFrameand the Map window. It now sets the"WP"icon using the sametry/exceptpatternwxconsole_ui.pyuses, with theiconimport inside thetryso matplotlib stays optional — this file already defers its own matplotlib import inheight_profile_pushed()for that reason.What this does and does not achieve
MAVProxy's console, map and mission editor windows all run in
multiprocessing.Processchildren forked frommavproxy.py, so they inherit itsWM_CLASS— a forked child's window reports the identicalWM_CLASS(STRING) = "mavproxy.py", "Mavproxy". GNOME therefore groups all of them undermavproxy.desktopand paintsIcon=mavproxyfor every one.So the fix for the reported gear icon is installing the launcher with a matching
StartupWMClassand putting the icons in the theme directory GTK actually searches — not the per-windowSetIcon()calls. Those are still worth having, since_NET_WM_ICONis used elsewhere (window lists, other desktops such as KDE), but they are not what changes the GNOME taskbar. The ReadMe states this explicitly so a user reading it isn't misled.Verification
WM_CLASSvalues probed withxpropagainst live wxPython/GTK3 windows, and against amultiprocessing.Processchild to confirm the forked-window behaviour above.python3 -m MAVProxy.tools.install_desktop_iconsrun from outside the checkout, so the source tree could not satisfy the import: the module resolves tosite-packages, and all six files install under$XDG_DATA_HOME.scripts/run_flake8.py MAVProxypasses with a plainpip install flake8, matching the workflow.install_desktop_icons.pycarries anAP_FLAKE8_CLEANmarker so it is actually checked. The marker is deliberately not added toicon.pyormissionEditorFrame.py, which have substantial pre-existing violations (405 inmissionEditorFrame.py) — that would be a separate cleanup.Known, pre-existing, deliberately not touched here
desktop-file-validatefails on all three launchers'Exec=lines (reserved;,'and~outside quotes, an unescaped$, and%Fsplitting filenames containing spaces). Those lines are byte-identical to the ones at this PR's base — this PR only adds aStartupWMClassline to each file — and fixing them changes launch behaviour, so they belong in their own change. Likewise,importlib.resources.files()needs Python 3.9 whilesetup.pydeclares nopython_requires; the floor is not introduced here, sinceMAVProxy/tools/MAVExplorer.pyalready calls the same API.AI assistance disclosure: this contribution was developed with the assistance of Claude (Anthropic), including Claude Code — investigating the root cause, drafting the changes, and preparing this description. I reviewed the diff, understand the change, and ran the verification described above on my own machine.
Fixes #1667