Edit on GitHub

sysmon_pytk.desktop_menu

Installs an entry in the desktop menu system.

You can enable it in Settings. This only works in Linux/X11.

Originally from https://github.com/Akuli/porcupine/blob/main/porcupine/plugins/desktop_menu.py

  1# SPDX-FileCopyrightText: © 2017-2024 Akuli
  2# SPDX-FileCopyrightText: © 2024 Stacey Adams
  3# SPDX-License-Identifier: MIT
  4
  5"""
  6Installs an entry in the desktop menu system.
  7
  8You can enable it in Settings. This only works in Linux/X11.
  9
 10Originally from https://github.com/Akuli/porcupine/blob/main/porcupine/plugins/desktop_menu.py
 11"""
 12
 13from __future__ import annotations
 14
 15import os
 16import shlex
 17import shutil
 18import subprocess  # nosec B404
 19from pathlib import Path
 20from typing import TYPE_CHECKING
 21
 22import platformdirs
 23
 24from .app_locale import get_translator
 25from .file_utils import get_full_path
 26from .modals import messagebox
 27
 28if TYPE_CHECKING:
 29    from tkinter import Tk, Toplevel
 30
 31_ = get_translator()
 32
 33XDG_DESKTOP_MENU = "xdg-desktop-menu"
 34"""Program to execute to create/remove the desktop menu entry."""
 35
 36DESKTOP_FILE_NAME = "sysmon.desktop"
 37"""Filename of the desktop menu entry to create."""
 38
 39EXECUTABLE = "sysmon"
 40"""Executable to run fro the desktop menu entry."""
 41
 42
 43def install_desktop_file(parent: Tk | Toplevel) -> bool:
 44    """
 45    Install a desktop menu entry on Linux/X11.
 46
 47    Parameters
 48    ----------
 49    parent : Tk | Toplevel
 50        The parent window that is calling this function. Needed for error reporting.
 51    """
 52    if parent.tk.call("tk", "windowingsystem") != "x11":
 53        return False
 54    location = shutil.which(EXECUTABLE)
 55    if location is None:
 56        messagebox.show_error(
 57            parent, _("Error creating desktop menu item"),
 58            _("System Monitor (sysmon) must be installed in a virtual "
 59                "environment in order to create a desktop menu entry.")
 60        )
 61        return False
 62
 63    venv = os.environ.get("VIRTUAL_ENV")
 64    if venv:
 65        activate_path = Path(venv) / "bin" / "activate"
 66        if not activate_path.is_file():
 67            raise FileExistsError(activate_path)
 68        # Must activate the venv, otherwise various things don't work
 69        # (e.g. os.environ.get("VIRTUAL_ENV") in this plugin)
 70        # %F is a list of filenames that are already quoted, so we cannot place that inside quotes.
 71        # Instead we need https://unix.stackexchange.com/a/144519
 72        bash_command = f'source {shlex.quote(str(activate_path))} && {EXECUTABLE} "$@"'
 73        exec_line = f"Exec=bash -c {shlex.quote(bash_command)} bash %F\n"
 74    else:
 75        exec_line = f"Exec={location}"
 76
 77    launcher_path = platformdirs.user_config_path("sysmon_pytk") / DESKTOP_FILE_NAME
 78
 79    with launcher_path.open("w") as file:
 80        file.write("[Desktop Entry]\n")
 81        file.write("Name=" + _("System Monitor") + "\n")
 82        file.write("GenericName=System Monitor\n")
 83        file.write(f"{exec_line}\n")
 84        file.write("Terminal=false\n")
 85        file.write("Type=Application\n")
 86        file.write("Categories=Monitor;System;\n")
 87        file.write(f"Icon={(Path(get_full_path('.')) / 'images' / 'icon-lg.png').absolute()}\n")
 88
 89    subprocess.call(
 90        [XDG_DESKTOP_MENU, "install", "--mode", "user", "--novendor", launcher_path]
 91    )  # nosec B603
 92    launcher_path.unlink()
 93    return True
 94
 95
 96def uninstall_desktop_file() -> None:
 97    """
 98    Uninstall the desktop menu entry that was previously installed.
 99    """
100    subprocess.call(
101        [XDG_DESKTOP_MENU, "uninstall", "--mode", "user", DESKTOP_FILE_NAME]
102    )  # nosec B603
XDG_DESKTOP_MENU = 'xdg-desktop-menu'

Program to execute to create/remove the desktop menu entry.

DESKTOP_FILE_NAME = 'sysmon.desktop'

Filename of the desktop menu entry to create.

EXECUTABLE = 'sysmon'

Executable to run fro the desktop menu entry.

def install_desktop_file(parent: tkinter.Tk | tkinter.Toplevel) -> bool:
44def install_desktop_file(parent: Tk | Toplevel) -> bool:
45    """
46    Install a desktop menu entry on Linux/X11.
47
48    Parameters
49    ----------
50    parent : Tk | Toplevel
51        The parent window that is calling this function. Needed for error reporting.
52    """
53    if parent.tk.call("tk", "windowingsystem") != "x11":
54        return False
55    location = shutil.which(EXECUTABLE)
56    if location is None:
57        messagebox.show_error(
58            parent, _("Error creating desktop menu item"),
59            _("System Monitor (sysmon) must be installed in a virtual "
60                "environment in order to create a desktop menu entry.")
61        )
62        return False
63
64    venv = os.environ.get("VIRTUAL_ENV")
65    if venv:
66        activate_path = Path(venv) / "bin" / "activate"
67        if not activate_path.is_file():
68            raise FileExistsError(activate_path)
69        # Must activate the venv, otherwise various things don't work
70        # (e.g. os.environ.get("VIRTUAL_ENV") in this plugin)
71        # %F is a list of filenames that are already quoted, so we cannot place that inside quotes.
72        # Instead we need https://unix.stackexchange.com/a/144519
73        bash_command = f'source {shlex.quote(str(activate_path))} && {EXECUTABLE} "$@"'
74        exec_line = f"Exec=bash -c {shlex.quote(bash_command)} bash %F\n"
75    else:
76        exec_line = f"Exec={location}"
77
78    launcher_path = platformdirs.user_config_path("sysmon_pytk") / DESKTOP_FILE_NAME
79
80    with launcher_path.open("w") as file:
81        file.write("[Desktop Entry]\n")
82        file.write("Name=" + _("System Monitor") + "\n")
83        file.write("GenericName=System Monitor\n")
84        file.write(f"{exec_line}\n")
85        file.write("Terminal=false\n")
86        file.write("Type=Application\n")
87        file.write("Categories=Monitor;System;\n")
88        file.write(f"Icon={(Path(get_full_path('.')) / 'images' / 'icon-lg.png').absolute()}\n")
89
90    subprocess.call(
91        [XDG_DESKTOP_MENU, "install", "--mode", "user", "--novendor", launcher_path]
92    )  # nosec B603
93    launcher_path.unlink()
94    return True

Install a desktop menu entry on Linux/X11.

Parameters
  • parent (Tk | Toplevel): The parent window that is calling this function. Needed for error reporting.
def uninstall_desktop_file() -> None:
 97def uninstall_desktop_file() -> None:
 98    """
 99    Uninstall the desktop menu entry that was previously installed.
100    """
101    subprocess.call(
102        [XDG_DESKTOP_MENU, "uninstall", "--mode", "user", DESKTOP_FILE_NAME]
103    )  # nosec B603

Uninstall the desktop menu entry that was previously installed.