clan-vm-manager: Added clan icon to trayicon

This commit is contained in:
Luis Hebendanz 2024-02-16 12:25:06 +07:00
parent 7fe38a9a80
commit 4f7f34f9b4
7 changed files with 49 additions and 56 deletions

View File

@ -18,17 +18,21 @@ log = logging.getLogger(__name__)
class VMAttr: class VMAttr:
def __init__(self, state_dir: Path) -> None: def __init__(self, state_dir: Path) -> None:
# These sockets here are just symlinks to the real sockets which
# are created by the run.py file. The reason being that we run into
# file path length issues on Linux. If no qemu process is running
# the symlink will be dangling.
self._qmp_socket: Path = state_dir / "qmp.sock" self._qmp_socket: Path = state_dir / "qmp.sock"
self._qga_socket: Path = state_dir / "qga.sock" self._qga_socket: Path = state_dir / "qga.sock"
self._qmp: QEMUMonitorProtocol | None = None self._qmp: QEMUMonitorProtocol | None = None
@contextmanager @contextmanager
def qmp(self) -> Generator[QEMUMonitorProtocol, None, None]: def qmp_ctx(self) -> Generator[QEMUMonitorProtocol, None, None]:
if self._qmp is None: if self._qmp is None:
log.debug(f"qmp_socket: {self._qmp_socket}") log.debug(f"qmp_socket: {self._qmp_socket}")
rpath = self._qmp_socket.resolve() rpath = self._qmp_socket.resolve()
if not rpath.exists(): if not rpath.exists():
raise ClanError(f"qmp socket {rpath} does not exist") raise ClanError(f"qmp socket {rpath} does not exist. Is the VM running?")
self._qmp = QEMUMonitorProtocol(str(rpath)) self._qmp = QEMUMonitorProtocol(str(rpath))
self._qmp.connect() self._qmp.connect()
try: try:

View File

@ -43,7 +43,7 @@ def wait_vm_up(state_dir: Path) -> None:
timeout: float = 300 timeout: float = 300
while True: while True:
if timeout <= 0: if timeout <= 0:
raise TimeoutError(f"qga socket {socket_file} not found") raise TimeoutError(f"qga socket {socket_file} not found. Is the VM running?")
if socket_file.exists(): if socket_file.exists():
break break
sleep(0.1) sleep(0.1)
@ -56,7 +56,7 @@ def wait_vm_down(state_dir: Path) -> None:
timeout: float = 300 timeout: float = 300
while socket_file.exists(): while socket_file.exists():
if timeout <= 0: if timeout <= 0:
raise TimeoutError(f"qga socket {socket_file} still exists") raise TimeoutError(f"qga socket {socket_file} still exists. Is the VM down?")
sleep(0.1) sleep(0.1)
timeout -= 0.1 timeout -= 0.1

View File

@ -11,6 +11,10 @@ GTK4 has a demo application showing all widgets. You can run it by executing:
gtk4-widget-factory gtk4-widget-factory
``` ```
To find available icons execute:
```bash
gtk4-icon-browser
```

View File

@ -72,6 +72,24 @@ class MainApplication(Adw.Application):
return 0 return 0
def get_application_icon_path(self) -> None:
self.icon_name = "lol.clan.vm.manager"
if not self.icon_name:
return None
icon_theme = Gtk.IconTheme.get_for_display(
self.get_active_window().get_display()
)
# Use the correct method to look up an icon
icon_lookup_flags = 16
icon = icon_theme.lookup_icon(
self.icon_name, 128, 1.0, Gtk.TextDirection.NONE, icon_lookup_flags
)
if icon:
return icon.get_file().get_path()
return None
def on_shutdown(self, app: Gtk.Application) -> None: def on_shutdown(self, app: Gtk.Application) -> None:
log.debug("Shutting down") log.debug("Shutting down")
@ -84,9 +102,8 @@ class MainApplication(Adw.Application):
assert self.window is not None assert self.window is not None
if self.window.is_visible(): if self.window.is_visible():
self.window.hide() self.window.hide()
return else:
self.window.present()
self.window.present()
def dummy_menu_entry(self) -> None: def dummy_menu_entry(self) -> None:
log.info("Dummy menu entry called") log.info("Dummy menu entry called")

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -253,7 +253,7 @@ class VM(GObject.Object):
log.info(f"Stopping VM {self.get_id()}") log.info(f"Stopping VM {self.get_id()}")
try: try:
with self.machine.vm.qmp() as qmp: with self.machine.vm.qmp_ctx() as qmp:
qmp.command("system_powerdown") qmp.command("system_powerdown")
except ClanError as e: except ClanError as e:
log.debug(e) log.debug(e)

View File

@ -172,7 +172,7 @@ class BaseImplementation:
self.application = application self.application = application
self.menu_items: dict[int, Any] = {} self.menu_items: dict[int, Any] = {}
self.menu_item_id: int = 1 self.menu_item_id: int = 1
self.activate_callback: Callable = application.on_window_hide_unhide self.activate_callback: Callable = lambda a, b: self.update_window_visibility
self.is_visible: bool = True self.is_visible: bool = True
self.create_menu() self.create_menu()
@ -213,16 +213,12 @@ class BaseImplementation:
def create_menu(self) -> None: def create_menu(self) -> None:
self.show_hide_item = self.create_item( self.show_hide_item = self.create_item(
"default", self.application.dummy_menu_entry "default", self.application.on_window_hide_unhide
) )
self.connect_disconnect_item = self.create_item( # self.create_item()
"default", self.application.dummy_menu_entry
)
self.create_item() # self.create_item("_Quit", self.application.on_shutdown)
self.create_item("_Quit", self.application.dummy_menu_entry)
def update_window_visibility(self) -> None: def update_window_visibility(self) -> None:
if self.application.window is None: if self.application.window is None:
@ -237,14 +233,6 @@ class BaseImplementation:
self.update_menu() self.update_menu()
def update_user_status(self) -> None: def update_user_status(self) -> None:
sensitive = core.users.login_status != slskmessages.UserStatus.OFFLINE
label = "_Disconnect" if sensitive else "_Connect"
# self.set_item_sensitive(self.away_item, sensitive)
self.set_item_text(self.connect_disconnect_item, label)
# self.set_item_toggled(self.away_item, core.users.login_status == slskmessages.UserStatus.AWAY)
self.update_icon() self.update_icon()
self.update_menu() self.update_menu()
@ -266,9 +254,9 @@ class BaseImplementation:
# icon_name = "disconnect" # icon_name = "disconnect"
# icon_name = f"{pynicotine.__application_id__}-{icon_name}" # icon_name = f"{pynicotine.__application_id__}-{icon_name}"
# self.set_icon_name(icon_name) # self.set_icon(icon_name)
def set_icon_name(self, icon_name: str) -> None: def set_icon(self, icon_name: str) -> None:
# Implemented in subclasses # Implemented in subclasses
pass pass
@ -410,6 +398,7 @@ class StatusNotifierImplementation(BaseImplementation):
): ):
method = self.methods[method_name] method = self.methods[method_name]
result = method.callback(*parameters.unpack()) result = method.callback(*parameters.unpack())
out_arg_types = "".join(method.out_args) out_arg_types = "".join(method.out_args)
return_value = None return_value = None
@ -570,6 +559,11 @@ class StatusNotifierImplementation(BaseImplementation):
) )
self.tray_icon.register() self.tray_icon.register()
from .assets import loc
icon_path = str(loc / "clan_white_notext.png")
self.set_icon(icon_path)
self.bus.call_sync( self.bus.call_sync(
bus_name="org.kde.StatusNotifierWatcher", bus_name="org.kde.StatusNotifierWatcher",
object_path="/StatusNotifierWatcher", object_path="/StatusNotifierWatcher",
@ -617,38 +611,12 @@ class StatusNotifierImplementation(BaseImplementation):
"""Returns an icon path to use for tray icons, or None to fall back to """Returns an icon path to use for tray icons, or None to fall back to
system-wide icons.""" system-wide icons."""
self.custom_icons = False # icon_path = self.application.get_application_icon_path()
custom_icon_path = os.path.join(config.data_folder_path, ".nicotine-icon-theme")
if hasattr(sys, "real_prefix") or sys.base_prefix != sys.prefix:
# Virtual environment
local_icon_path = os.path.join(
sys.prefix, "share", "icons", "hicolor", "scalable", "apps"
)
else:
# Git folder
local_icon_path = os.path.join(
GTK_GUI_FOLDER_PATH, "icons", "hicolor", "scalable", "apps"
)
for icon_name in ("away", "connect", "disconnect", "msg"):
# Check if custom icons exist
if self.check_icon_path(icon_name, custom_icon_path):
self.custom_icons = True
return custom_icon_path
# Check if local icons exist
if self.check_icon_path(icon_name, local_icon_path):
return local_icon_path
return "" return ""
def set_icon_name(self, icon_name): def set_icon(self, icon_path) -> None:
if self.custom_icons: self.tray_icon.properties["IconName"].value = icon_path
# Use alternative icon names to enforce custom icons, since system-wide icons take precedence
icon_name = icon_name.replace(pynicotine.__application_id__, "nplus-tray")
self.tray_icon.properties["IconName"].value = icon_name
self.tray_icon.emit_signal("NewIcon") self.tray_icon.emit_signal("NewIcon")
if not self.is_visible: if not self.is_visible:
@ -1064,7 +1032,7 @@ class Win32Implementation(BaseImplementation):
self._menu, item_id, False, byref(item_info) self._menu, item_id, False, byref(item_info)
) )
def set_icon_name(self, icon_name): def set_icon(self, icon_name):
self._update_notify_icon(icon_name=icon_name) self._update_notify_icon(icon_name=icon_name)
def show_notification(self, title, message): def show_notification(self, title, message):