Fix battery tray icon vanishing after boot

The wait loop polled QSystemTrayIcon.isSystemTrayAvailable(), but Qt5
caches that value from the moment QApplication is constructed. At login
the autostart wins the race against wf-panel-pi's tray, so Qt caches
"no tray" permanently, the loop never escapes, and the process hangs
until the 120s timeout and exits.

Add wait_for_tray(), which polls the session bus directly (dbus-send
NameHasOwner for org.kde.StatusNotifierWatcher) before QApplication is
constructed, so Qt reports the tray correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joachim Hummel
2026-05-16 23:16:39 +02:00
parent 500a939db4
commit a6cdad5082

View File

@@ -108,14 +108,48 @@ def make_battery_icon(percent, charging):
return QIcon(pix) return QIcon(pix)
def tray_host_ready():
"""True once a StatusNotifier tray host is registered on the session bus.
Queried over D-Bus directly instead of via
QSystemTrayIcon.isSystemTrayAvailable(): Qt caches that result from the
moment QApplication is constructed. At login the icon's autostart usually
wins the race against wf-panel-pi, so Qt caches "no tray" forever and the
icon never appears even after the panel's tray comes up. Checking the bus
directly dodges that cache -- we build QApplication only once the host is
actually present.
"""
try:
result = subprocess.run(
["dbus-send", "--session", "--print-reply",
"--dest=org.freedesktop.DBus", "/org/freedesktop/DBus",
"org.freedesktop.DBus.NameHasOwner",
"string:org.kde.StatusNotifierWatcher"],
capture_output=True, text=True, timeout=5)
except (subprocess.SubprocessError, OSError):
return False
return "boolean true" in result.stdout
def wait_for_tray(timeout=180):
"""Block until a tray host appears, so autostart survives a cold boot."""
deadline = time.monotonic() + timeout
while not tray_host_ready():
if time.monotonic() > deadline:
print(f"Tray host not available after {timeout}s", file=sys.stderr)
sys.exit(1)
time.sleep(2)
class BatteryTray: class BatteryTray:
def __init__(self): def __init__(self):
self.app = QApplication(sys.argv) self.app = QApplication(sys.argv)
self.app.setQuitOnLastWindowClosed(False) self.app.setQuitOnLastWindowClosed(False)
# At login the panel may not have started its system tray yet. # wait_for_tray() already confirmed a tray host on the bus, so Qt now
# Wait for it instead of giving up, so autostart works after a reboot. # reports the tray correctly. Keep a short re-check as a guard against
deadline = time.monotonic() + 120 # the host still finishing its own registration.
deadline = time.monotonic() + 60
while not QSystemTrayIcon.isSystemTrayAvailable(): while not QSystemTrayIcon.isSystemTrayAvailable():
if time.monotonic() > deadline: if time.monotonic() > deadline:
print("System tray not available after 120s", file=sys.stderr) print("System tray not available after 120s", file=sys.stderr)
@@ -208,6 +242,7 @@ class BatteryTray:
def main(): def main():
wait_for_tray()
bt = BatteryTray() bt = BatteryTray()
sys.exit(bt.run()) sys.exit(bt.run())