2 months ago

Python-Tk: Ein Neofetch-GUI selbst schreiben Teil 11



Der 11. Teil einer Einführung in die Python3-Bibliothek Tkinter und der lange Weg zum perfekten GUI.

Zwischen diesem und dem letzten Teil ist leider etwas mehr als eine Woche vergangen. Das liegt daran, dass der Code immer komplexer wird. Heute soll es um das Erkennen des Desktop Environments gehen. Genau deswegen hat sich alles auch etwas verzögert, denn denken dauert manchmal.

Wo ist das Problem?

Was da so schwer sein soll, ist schnell erklärt. Geht einfach mal im Kopf durch wie viel Desktop-Umgebungen ihr kennt und dann nehmt eine Suchmaschine zur Hand und guckt wie viele es tatsächlich gibt.

Auf Anhieb fällt mir ein: Gnome, KDE, Mate, Cinnamon, Xfce, PIXEL(Raspberry Pi), LXDE, LXQT

All diese Desktops sind auf eine andere Art und Weise im System registriert. Schauen wir doch mal bei Neofetch auf Github vorbei. Genauer: Ab Zeile 1771 finden wir die Funktion get_de(). Ab der Zeile 1811 wird definiert, wie Linux-Desktops ausgelesen werden sollen.

Um mehr über unser System zu erfahren, haben wir schon das Modul os.environment kennengelernt. Suchen wir hier nach dem Begriff "Desktop" so finden wir drei Einträge: 

XDG_CURRENT_DESKTOP, XDG_SESSION_DESKTOP, DESKTOP_SESSION

~$ python3 Python 3.12.3 (main, Jul 31 2024, 17:43:48) [GCC 13.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import os >>> print(os.environ)

Output

environ({'SHELL': '/bin/bash', 'SESSION_MANAGER': 'local/test-desktop-pi5:@/tmp/.ICE-unix/3553,unix/test-desktop-pi5:/tmp/.ICE-unix/3553', 'QT_ACCESSIBILITY': '1', 'COLORTERM': 'truecolor', 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/etc/xdg', 'XDG_MENU_PREFIX': 'gnome-', 'GNOME_DESKTOP_SESSION_ID': 'this-is-deprecated', 'LANGUAGE': 'de_DE:en', 'LC_ADDRESS': 'de_DE.UTF-8', 'GNOME_SHELL_SESSION_MODE': 'ubuntu', 'LC_NAME': 'de_DE.UTF-8', 'SSH_AUTH_SOCK': '/run/user/1002/keyring/ssh', 'MEMORY_PRESSURE_WRITE': 'c29tZSAyMDAwMDAgMjAwMDAwMAA=', 'XMODIFIERS': '@im=ibus', 'DESKTOP_SESSION': 'ubuntu', 'LC_MONETARY': 'de_DE.UTF-8', 'GTK_MODULES': 'gail:atk-bridge', 'DBUS_STARTER_BUS_TYPE': 'session', 'PWD': '/home/test', 'LOGNAME': 'test', 'XDG_SESSION_DESKTOP': 'ubuntu', 'XDG_SESSION_TYPE': 'wayland', 'SYSTEMD_EXEC_PID': '3553', 'XAUTHORITY': '/run/user/1002/.mutter-Xwaylandauth.WWECT2', 'HOME': '/home/test', 'USERNAME': 'test', 'IM_CONFIG_PHASE': '1', 'LC_PAPER': 'de_DE.UTF-8', 'LANG': 'de_DE.UTF-8', 'XDG_CURRENT_DESKTOP': 'ubuntu:GNOME', 'MEMORY_PRESSURE_WATCH': '/sys/fs/cgroup/user.slice/user-1002.slice/user@1002.service/app.slice/app-gnome\\x2dsession\\x2dmanager.slice/gnome-session-manager@ubuntu.service/memory.pressure', 'VTE_VERSION': '7600', 'WAYLAND_DISPLAY': 'wayland-0', 'INVOCATION_ID': '3d44dbaaba694451b24777b735796027', 'QTWEBENGINE_DICTIONARIES_PATH': '/usr/share/hunspell-bdic/', 'MANAGERPID': '3301', 'GNOME_SETUP_DISPLAY': ':1', 'LESSCLOSE': '/usr/bin/lesspipe %s %s', 'XDG_SESSION_CLASS': 'user', 'TERM': 'xterm-256color', 'LC_IDENTIFICATION': 'de_DE.UTF-8', 'LESSOPEN': '| /usr/bin/lesspipe %s', 'USER': 'test', 'DISPLAY': ':0', 'SHLVL': '1', 'GSM_SKIP_SSH_AGENT_WORKAROUND': 'true', 'LC_TELEPHONE': 'de_DE.UTF-8', 'QT_IM_MODULE': 'ibus', 'LC_MEASUREMENT': 'de_DE.UTF-8', 'DBUS_STARTER_ADDRESS': 'unix:path=/run/user/1002/bus,guid=c22ff578dbe9a3215dd7d07166d8266e', 'PAPERSIZE': 'a4', 'TILIX_ID': 'bdc945c3-3564-4ece-9789-2086791869a9', 'XDG_RUNTIME_DIR': '/run/user/1002', 'DEBUGINFOD_URLS': 'https://debuginfod.ubuntu.com ', 'LC_TIME': 'de_DE.UTF-8', 'JOURNAL_STREAM': '8:21961'

Anmerkung: Daten und Zahlen wurden, für die Privatsphäre, verfälscht.

Alle Desktop-Umgebung auf diese Keys zu checken würde sehr lange dauern, weshalb ich mich nur auf eine Auswahl beschränke. Es zeichnet sich folgendes Bild

Cinnamon Mate XFCE KDE GNOME UBUNTU
XDG_CURRENT_DESKTOP x-cinnamon MATE XFCE KDE GNOME ubuntu:GNOME
XDG_SESSION_DESKTOP cinnamon mate lghtdm-x-session KDE gnome ubuntu
DESKTOP_SESSION cinnamon mate lghtdm-x-session plasma gnome ubuntu

Die richte Wahl wäre hier XDG_CURRENT_DESKTOP, welches Auskunft über den derzeitig benutzen Desktop gibt. Bemerkenswert wären hier zum einen, dass Value x-cinnamon, welches zusätzlich darauf hinweist, dass wir hier die X11 Sitzung laufen haben. Zum anderen wäre da noch ubuntu:GNOME, was uns zeigt, dass Ubuntu-Desktop in der Registrierung von Vannilla-Gnome abweicht.

Wie gehen wir mit diesen Informationen um?

Zunächst einmal bauen wir eine Funktion:

import os def get_desktop_environment(): xdg_current_desktop = os.environ.get("XDG_CURRENT_DESKTOP") return xdg_current_desktop print(get_desktop_environment())

Output:

>>> %Run get_de.py ubuntu:GNOME

Dass es sich bei dem OS um Ubuntu handelt und wird schon über das Label OS: ausgelesen, und aus unserer Tabelle wissen wir, dass Cinnamon mit dem Zusatz x- daher kommt. Wie schon erwähnt, hat jeder Desktop seine Eigenheiten und deswegen werde ich hier nur exemplarisch zeigen, wie man den Output anpasst. Somit könnt Ihr bei Abweichungen auch selbst aktiv werden.

import os def get_desktop_environment(): xdg_current_desktop = os.environ.get("XDG_CURRENT_DESKTOP") if xdg_current_desktop == "x-cinnamon": return "CINNAMON" elif xdg_current_desktop == "ubuntu:GNOME": return "GNOME" else: return xdg_current_desktop print(get_desktop_environment())

In main.py implementieren

def get_desktop_environment(): xdg_current_desktop = os.environ.get("XDG_CURRENT_DESKTOP") if xdg_current_desktop == "x-cinnamon": return "CINNAMON" elif xdg_current_desktop == "ubuntu:GNOME": return "GNOME" else: return xdg_current_desktop

Logik

  • xdg_current_desktop gibt einen Wert aus
  • Ist dieser Wer x-cinnamon soll CINNAMON zurückgegeben werden
  • Ist dieser Wer ubuntu_GNOME soll GNOME zurückgegeben werden
  • Trift nichts zu, wird der Wert unverfälscht zurückgegeben

Wie gewohnt erstellen wir ein Label:

# Label mit Text DE: de_label = tk.Label(stat_frame,text=f"DE: {get_desktop_environment()}",background="#FFFFFF",font=("Sans",14)) de_label.pack(anchor=tk.NW)

Hier sei nochmal erwähnt, dass dieser Code natürlich fehleranfällig ist. Ich habe 2 Stunden auf DistroSea verbracht, um diese Eigenheiten zu erfassen. Alle Desktops zu checken, würde als Einzelperson sehr lange dauern. Sollte euer Desktop-Ergebens abweichen könnt ihr gerne den Output in die Kommentare posten und ich passe den Code wie bei Cinnamon und Ubuntu-Gnome an.

Der ganze Code

## Teil 11 ### import tkinter as tk from PIL import Image, ImageTk import os import socket import distro import platform import psutil import datetime import subprocess def get_desktop_environment(): xdg_current_desktop = os.environ.get("XDG_CURRENT_DESKTOP") if xdg_current_desktop == "x-cinnamon": return "CINNAMON" elif xdg_current_desktop == "ubuntu:GNOME": return "GNOME" else: return xdg_current_desktop # Ließt den Window-Manager aus def get_window_manager_name(): try: result = subprocess.run( ["wmctrl", "-m"], capture_output=True, text=True, check=True ) output_lines = result.stdout.strip().split("\n") for line in output_lines: if line.startswith("Name: "): window_manager_name = line.split("Name: ")[1] if window_manager_name == "GNOME Shell": return "Mutter" return window_manager_name except subprocess.CalledProcessError as e: print(f"Error running wmctrl: {e}") # Macht die RAM-Größe lesbar def get_size(bytes, suffix="B"): """ Scale bytes to its proper format e.g: 1253656 => '1.20MB' 1253656678 => '1.17GB' """ factor = 1024 for unit in ["", "K", "M", "G", "T", "P"]: if bytes < factor: return f"{bytes:.2f}{unit}{suffix}" bytes /= factor # Findet die Auflösung heraus def get_screen_size(): screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() return f"{screen_width}x{screen_height}" def get_sys_uptime(): # System-Startzeit ermitteln boot_time_timestamp = psutil.boot_time() boot_time = datetime.datetime.fromtimestamp(boot_time_timestamp) # Aktuelle Zeit now = datetime.datetime.now() # Uptime berechnen uptime = now - boot_time # Uptime in Stunden und Minuten umrechnen uptime_hours, remainder = divmod(uptime.total_seconds(), 3600) uptime_minutes = remainder // 60 # Weist an das diese Funktion Stunden und Minuten ausgeben soll return f"{int(uptime_hours)} h , {int(uptime_minutes)} m" # Setzt das korrekte Logo für die Distro def get_distro_logo(): if distro_id == "debian": distro_icon.configure(image=debian_logo) elif distro_id == "arch": distro_icon.configure(image=arch_logo) elif distro_id == "mint": distro_icon.configure(image=mint_logo) elif distro_id == "ubuntu": distro_icon.configure(image=ubuntu_logo) elif distro_id == "opensuse": distro_icon.configure(image=osuse_logo) elif distro_id == "fedora": distro_icon.configure(image=fedora_logo) else: distro_icon.configure(image=distro_logo) # Vars für die Labels # Ließt den User aus user = os.environ["USER"] # Ließt den Host aus hostname = socket.gethostname() # Ließt den Pretty Name aus os_release_pretty = distro.name(pretty=True) # Ließt den Kernel aus kernel_release = platform.release() # Basis um den RAM auszulesen svmem = psutil.virtual_memory() # Basis um CPU-Werte auszulesen cpu_freq = psutil.cpu_freq() # Ließt Anzahl der CPU-Kerne aus cpu_core_count = psutil.cpu_count(logical=False) # Gibt die aktuelle Shell aus active_shell = os.environ["SHELL"] # Gibt die Distro-ID aus distro_id = distro.id() # Erstelle das Hauptfenster root = tk.Tk() root.title("Neofetch-Tk") root.geometry("800x500") root["background"]="#FFFFFF" # Weiß # Distro Logos distro_logo = tk.PhotoImage(file="images/test.png") arch_logo = tk.PhotoImage(file="images/arch_logo_350.png") debian_logo = tk.PhotoImage(file="images/debian_logo_350.png") mint_logo = tk.PhotoImage(file="images/mint_logo_350.png") suse_logo = tk.PhotoImage(file="images/osuse_logo_350.png") ubuntu_logo = tk.PhotoImage(file="images/ubuntu_logo_350.png") fedora_logo = tk.PhotoImage(file="images/fedora_logo_350.png") # Einen Frame Zeichen logo_frame = tk.Frame(root,background="#FFFFFF") logo_frame.pack(fill="both",expand=False,side='left',padx=10,pady=10) # Distro-Logo-Label distro_icon = tk.Label(logo_frame,text="DISTRO LOGO",image=distro_logo,background="#FFFFFF") distro_icon.pack(anchor=tk.NW) # Einen Frame Zeichen stat_frame = tk.Frame(root,background="#FFFFFF") stat_frame.pack(fill="both",expand=True,side='left',padx=10,pady=10) # Label mit Text USER@HOST user_host_label = tk.Label(stat_frame,text=f"{user}@{hostname}",background="#FFFFFF",font=("Sans",14)) user_host_label.pack(anchor=tk.NW) # Label mit Text OS: os_label = tk.Label(stat_frame,text=f"OS: {os_release_pretty}",background="#FFFFFF",font=("Sans",14)) os_label.pack(anchor=tk.NW) # Label mit Text Host: host_label = tk.Label(stat_frame,text=f"Host: {hostname}",background="#FFFFFF",font=("Sans",14)) host_label.pack(anchor=tk.NW) # Label mit Text Kernel: kernel_label = tk.Label(stat_frame,text=f"Kernel: {kernel_release}",background="#FFFFFF",font=("Sans",14)) kernel_label.pack(anchor=tk.NW) # Label mit Text Uptime: uptime_label = tk.Label(stat_frame,text=f"Uptime: {get_sys_uptime()}",background="#FFFFFF",font=("Sans",14)) uptime_label.pack(anchor=tk.NW) # Label mit Text Shell: shell_label = tk.Label(stat_frame,text=f"Shell: {active_shell}",background="#FFFFFF",font=("Sans",14)) shell_label.pack(anchor=tk.NW) # Label mit Text Resolution: res_label = tk.Label(stat_frame,text=f"Resolution: {get_screen_size()}",background="#FFFFFF",font=("Sans",14)) res_label.pack(anchor=tk.NW) # Label mit Text DE: de_label = tk.Label(stat_frame,text=f"DE: {get_desktop_environment()}",background="#FFFFFF",font=("Sans",14)) de_label.pack(anchor=tk.NW) # Label mit Text Window-Manager: wm_label = tk.Label(stat_frame,text=f"WM: {get_window_manager_name()}",background="#FFFFFF",font=("Sans",14)) wm_label.pack(anchor=tk.NW) # Label mit Text CPU: cpu_label = tk.Label(stat_frame,text=f"CPU: ({cpu_core_count}) @ {cpu_freq.max:.2f} Mhz",background="#FFFFFF",font=("Sans",14)) cpu_label.pack(anchor=tk.NW) # Label mit Text Memory: mem_label = tk.Label(stat_frame,text=f"Memory: {(get_size(svmem.used))}/{get_size(svmem.total)}",background="#FFFFFF",font=("Sans",14)) mem_label.pack(anchor=tk.NW) # Führt get_distro_logo aus get_distro_logo() # Starte die Hauptschleife root.mainloop()


GNU/Linux.ch ist ein Community-Projekt. Bei uns kannst du nicht nur mitlesen, sondern auch selbst aktiv werden. Wir freuen uns, wenn du mit uns über die Artikel in unseren Chat-Gruppen oder im Fediverse diskutierst. Auch du selbst kannst Autor werden. Reiche uns deinen Artikelvorschlag über das Formular auf unserer Webseite ein.
Gesamten Artikel lesen

© Varient 2024. All rights are reserved