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:
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._qga_socket: Path = state_dir / "qga.sock"
self._qmp: QEMUMonitorProtocol | None = None
@contextmanager
def qmp(self) -> Generator[QEMUMonitorProtocol, None, None]:
def qmp_ctx(self) -> Generator[QEMUMonitorProtocol, None, None]:
if self._qmp is None:
log.debug(f"qmp_socket: {self._qmp_socket}")
rpath = self._qmp_socket.resolve()
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.connect()
try:

View File

@ -43,7 +43,7 @@ def wait_vm_up(state_dir: Path) -> None:
timeout: float = 300
while True:
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():
break
sleep(0.1)
@ -56,7 +56,7 @@ def wait_vm_down(state_dir: Path) -> None:
timeout: float = 300
while socket_file.exists():
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)
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
```
To find available icons execute:
```bash
gtk4-icon-browser
```

View File

@ -72,6 +72,24 @@ class MainApplication(Adw.Application):
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:
log.debug("Shutting down")
@ -84,9 +102,8 @@ class MainApplication(Adw.Application):
assert self.window is not None
if self.window.is_visible():
self.window.hide()
return
self.window.present()
else:
self.window.present()
def dummy_menu_entry(self) -> None:
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()}")
try:
with self.machine.vm.qmp() as qmp:
with self.machine.vm.qmp_ctx() as qmp:
qmp.command("system_powerdown")
except ClanError as e:
log.debug(e)

View File

@ -172,7 +172,7 @@ class BaseImplementation:
self.application = application
self.menu_items: dict[int, Any] = {}
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.create_menu()
@ -213,16 +213,12 @@ class BaseImplementation:
def create_menu(self) -> None:
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(
"default", self.application.dummy_menu_entry
)
# self.create_item()
self.create_item()
self.create_item("_Quit", self.application.dummy_menu_entry)
# self.create_item("_Quit", self.application.on_shutdown)
def update_window_visibility(self) -> None:
if self.application.window is None:
@ -237,14 +233,6 @@ class BaseImplementation:
self.update_menu()
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_menu()
@ -266,9 +254,9 @@ class BaseImplementation:
# icon_name = "disconnect"
# 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
pass
@ -410,6 +398,7 @@ class StatusNotifierImplementation(BaseImplementation):
):
method = self.methods[method_name]
result = method.callback(*parameters.unpack())
out_arg_types = "".join(method.out_args)
return_value = None
@ -570,6 +559,11 @@ class StatusNotifierImplementation(BaseImplementation):
)
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(
bus_name="org.kde.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
system-wide icons."""
self.custom_icons = False
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
# icon_path = self.application.get_application_icon_path()
return ""
def set_icon_name(self, icon_name):
if self.custom_icons:
# 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
def set_icon(self, icon_path) -> None:
self.tray_icon.properties["IconName"].value = icon_path
self.tray_icon.emit_signal("NewIcon")
if not self.is_visible:
@ -1064,7 +1032,7 @@ class Win32Implementation(BaseImplementation):
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)
def show_notification(self, title, message):