diff --git a/CHANGELOG b/CHANGELOG index 759b966f..3a8679da 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +2024-05-31: [BUGFIX] Fix `GlobalMenu` crash after reloading config 2024-05-29: [BUGFIX] Fix bug with border decorations when using both standard and decorated borders 2024-05-22: [RELEASE] v0.26.0 release - compatible with qtile 0.26.0 2024-05-19: [FEATURE] Add modified `Plasma` layout to include border highlighting diff --git a/qtile_extras/resources/dbusmenu/__init__.py b/qtile_extras/resources/dbusmenu/__init__.py index 073fa675..d8301aa5 100644 --- a/qtile_extras/resources/dbusmenu/__init__.py +++ b/qtile_extras/resources/dbusmenu/__init__.py @@ -76,7 +76,7 @@ def __init__( def __repr__(self): """Custom repr to help debugging.""" - txt = f"'{self.label.replace('_','')}'" if self.label else self.item_type + txt = f"'{self.label.replace('_', '')}'" if self.label else self.item_type if self.children_display == "submenu": txt += "*" return f"" @@ -119,6 +119,7 @@ def __init__( self.path = path self.bus = bus self._menus: dict[int, dict[str, int | list[DBusMenuItem]]] = {} + self._interface = None self.no_cache_menus = no_cache_menus self.layout_callbacks = [] self.display_menu_callback = display_menu_callback @@ -186,6 +187,9 @@ async def _get_menu(self, root): Method to retrieve the menu layout from the DBus interface. """ + if self._interface is None: + return None, None + needs_update = True # Alert the app that we're about to draw a menu @@ -237,7 +241,10 @@ def parse_menu(self, root, callback, task): update_needed, returned_menu = task.result() menu = [] - if update_needed == self.MENU_UPDATED: + if update_needed is None: + return + + elif update_needed == self.MENU_UPDATED: # Remember the menu revision ID so we know whether to update or not revision, layout = returned_menu @@ -285,3 +292,6 @@ async def click(self, id): # Ugly hack: delete all stored menus if the menu has been clicked # This will force a reload when the menu is next generated. self._menus = {} + + def stop(self): + self._interface.off_layout_updated(self._layout_updated) diff --git a/qtile_extras/widget/globalmenu.py b/qtile_extras/widget/globalmenu.py index b13ec862..d5c323ac 100644 --- a/qtile_extras/widget/globalmenu.py +++ b/qtile_extras/widget/globalmenu.py @@ -19,11 +19,11 @@ # SOFTWARE. from __future__ import annotations -import asyncio from typing import TYPE_CHECKING from libqtile import hook from libqtile.backend.base.window import Internal +from libqtile.utils import create_task from libqtile.widget import base from qtile_extras.resources.dbusmenu import DBusMenu @@ -85,12 +85,16 @@ def client_updated(self, wid): del self.app_menus[wid] if wid == self.current_wid: - asyncio.create_task(self.get_window_menu(wid)) + create_task(self.get_window_menu(wid)) def set_hooks(self): hook.subscribe.focus_change(self.hook_response) hook.subscribe.client_killed(self.client_killed) + def clear_hooks(self): + hook.unsubscribe.focus_change(self.hook_response) + hook.unsubscribe.client_killed(self.client_killed) + def hook_response(self, *args, startup=False): if not startup and self.bar.screen != self.qtile.current_screen: self.clear() @@ -98,7 +102,7 @@ def hook_response(self, *args, startup=False): self.current_wid = self.qtile.current_window.wid if self.qtile.current_window else None if self.current_wid: - asyncio.create_task(self.get_window_menu(self.current_wid)) + create_task(self.get_window_menu(self.current_wid)) elif not startup: self.clear() @@ -228,4 +232,8 @@ def finalize(self): registrar.finalize() for item in self.items: item.finalize() + for menu in self.app_menus.values(): + menu.stop() + self.app_menus.clear() + self.clear_hooks() base._TextBox.finalize(self)