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
sollCINNAMON
zurückgegeben werden - Ist dieser Wer
ubuntu_GNOME
sollGNOME
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()
Sehr schöne Reihe auch sehr gut erklärt, das einzige was mir so stört, das kein Link zu der ersten Teil hier zu finden ist. Oder vielleicht ein Art Listing, von den Python Tutorials, was schon erschienen ist. Denke das würden sich, natürlich ich auch, drüber freuen ;D
Mein Fehler, wird im Laufe des Tages gefixed.
Erst mal vielen Dank für dieses tolle Anleitungen. Ich hätte 2 Anmerkungen:
Hi, bei neofetch steht ja auch nicht "USER:". Ich hallte mich erstmal an den Aufbau von Neofetch und wenn alle Probleme ausgebügelt sind, kommen wir sowieso in die Phase, dass wir dem Programm einen eigenen Look geben. Das wird sich das dann auch ändern.
Das hört sich super an ;D Finde so ein Tutorial besser, als ein Video. Einiges kann man vielleicht besser mit Video zeigen, aber solche Sachen finde ich gehören schritlich dargestellt ;D Weiss nicht, wieviel Arbeit ein Video macht, aber der Haken ist halt, das auch das drumherum stimmen muss, also heisst sound, der Ablauf etc. Einige sind dann zu leise, der andere hat einen Sound wie aus einem Telefon.
Daher nochmals ein gannnzzz grossses Danke an dich und auch an das Team von GNU/Linux.ch ;D
Im Tag für Tkinter ist ein Typo drin. Wäre super, wenn man den korrigieren würde, dann wären die anderen Artikel aus der Serie auch einfacher zu finden.