Grüße,
ich würde sagen, legen sofort los und beginnen mit einer Bestandsaufnahme.
Die Tabelle zeigt, dass wir 8 von 16 Funktionen schon implementiert haben, wobei es 3 gibt, die noch Nacharbeitung benötigen.
Zu erledigen sind noch Distro Logo, Uptime, Packages, DE, WM, WM-Theme, Icons und Terminal.
Distro Logo | Nur Platzhalter | |
USER & HOST | Check | Sollte keine Probleme machen |
OS | Check | Sollte keine Probleme machen |
Host | Check | Muss angepasst werden |
Kernel | Check | Sollte keine Probleme machen |
Uptime | ||
Packages | ||
Shell | Check | Muss angepasst werden |
Resolution | Check | Sollte keine Probleme machen |
DE | ||
WM | ||
WM-Theme | ||
Icons | ||
Terminal | ||
CPU | Check | Sollte keine Probleme machen |
Memory | Check | Sollte keine Probleme machen |
Rückmeldungen
Mir ist natürlich von Anfang an bewusst gewesen, dass dieses Tutorial nicht alle Linux-Nutzer abholen kann. Das ist dem Grund geschuldet, dass ich hier teils Debian-Befehlen arbeite. Es besteht ja die Möglichkeit eine virtuelle Python Umgebung zu schaffen, die ohne die Bibliotheken einer bestimmten Distro funktioniert. Natürlich will ich niemanden ausschließen.
Die Debian-Basis ist nun mal am weitesten verbreitet und kein Python-Einsteiger-Tutorial der Welt fängt an mit der Erklärung, wie man den Python-Paket-Manager nutzt. Ich werde mir aber in der Sommerpause Gedanken machen, wie/wann ich diesen am besten einführe.
Uptime
Wir wollen also herausfinden, wie lange unser PC schon an ist. Da hier wieder Mathematik und tiefergehendes Verständnis nötig ist, legen wir ein Test-File an.
Wir importieren:
import psutil
import datetime
Nur machen wir den Zeitpunkt, an dem der PC gestartet ist. Von psutil
nutzen wir die Methode boot_time
.
import psutil
import datetime
unix_timestamp = psutil.boot_time()
print(unix_timestamp)
Das Ergebnis sieht bei mir so aus:
1720781358.0
psutil.boot_time()
gibt die Zeit als Unix-Zeitstempel zurück, zu der das System gestartet wurde. Um diese Zahl besser verständlich auszudrücken, nutzen wir datetime
.
boot_time = datetime.datetime.fromtimestamp(unix_timestamp)
Dieses Modul ist etwas verwirrend. Man nutzt die Methode frometimestamp
aus Methode datetime
des Moduls datetime
und geb unseren unix_timestamp
hinein.
Das Resultat
import psutil
import datetime
unix_timestamp = psutil.boot_time()
boot_time = datetime.datetime.fromtimestamp(unix_timestamp)
print(boot_time)
>>>
2024-07-12 12:49:18
Jetzt wissen wir genau, wann ich meinen PC angeschaltet habe. Nun müssen wir noch herausfinden welches Datum um wie viel Uhr es gerade in diesem Augenblick sind.
now = datetime.datetime.now()
Jetzt kommt ganz simple Subtraktion zum Einsatz
uptime = now - boot_time
[Jetzt
] - (MINUS
) [Wann wurde der PC angeschaltet
]
Der ganze Test-Code
import psutil
import datetime
unix_timestamp = psutil.boot_time()
boot_time = datetime.datetime.fromtimestamp(unix_timestamp)
now = datetime.datetime.now()
uptime = now - boot_time
print(f"Startzeit: {boot_time}")
print(f"Jetzt: {now}")
print(f"Uptime: {uptime}")
>>>
Startzeit: 2024-07-12 12:49:18
Jetzt: 2024-07-12 17:49:43.825505
Uptime: 5:00:25.825505
In Main einbinden
Wie auch im Test-Skript importieren wir datetime
import datetime
Da wir ja irgendwann Live-Daten auslesen wollen, habe ich die UPTIME
in eine Funktion gepackt und ein paar Änderungen vorgenommen, um die Lesbarkeit zu verbessern.
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
return f"{int(uptime_hours)} h , {int(uptime_minutes)} m"
Es wird mal wieder etwas komplexer. Der Wert aus uptime
wird hier in Stunden und Minuten umgewandelt. Was übrigbleibt, wird durch 60
geteilt. Wird die Funktion abgerufen, soll "X h
, X m
" ausgegeben werden.
uptime_hours, remainder = divmod(uptime.total_seconds(), 3600)
uptime_minutes = remainder // 60
return f"{int(uptime_hours)} h , {int(uptime_minutes)} m"
Logik
- Eine Stunde hat 3600 Sekunden.
divmod
Teil den Wert ausuptime._total_seconds
.- Was bleibt sind Stunde und ein Rest
- Der Rest wird durch 60 geteilt
- Ergibt der Rest eine oder mehrere ganze Minuten, landet in
uptime_minutes
- über
return
findet die Ausgabe statt - Da wir ganze Zahlen wollen, aber eine Kommazahl bekommen(
float
), nutzen wirint
um die Zahlen zu konvertieren - Das geht aber nur, weil wir durch
divmod
ganze Zahlen z. B. 10.0 zurückbekommen
Unter dem kernel_label
machen wir etwas Platz frei und schreiben wie gewohnt ein Label, dessen Text den Inhalt der Funktion wiedergibt.
uptime_label = tk.Label(stat_frame,text=f"Uptime: {get_sys_uptime()}")
uptime_label.pack(anchor=tk.NW)
Der ganze Code in Main
## Teil 8 ###
import tkinter as tk
from PIL import Image, ImageTk
import os
import socket
import distro
import platform
import psutil
import datetime
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
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
return f"{int(uptime_hours)} h , {int(uptime_minutes)} m"
user = os.environ["USER"]
hostname = socket.gethostname()
os_release_pretty = distro.name(pretty=True)
kernel_release = platform.release()
svmem = psutil.virtual_memory()
cpu_freq = psutil.cpu_freq()
cpu_core_count = psutil.cpu_count(logical=False)
active_shell = os.environ["SHELL"]
# Erstelle das Hauptfenster
root = tk.Tk()
root.title("Neofetch-Tk")
root.geometry("800x500")
distro_logo = tk.PhotoImage(file="images/test.png")
# Einen Frame Zeichen
logo_frame = tk.Frame(root,background="yellow")
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="yellow")
distro_icon.pack(anchor=tk.NW)
# Einen Frame Zeichen
stat_frame = tk.Frame(root,background="cyan")
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}")
user_host_label.pack(anchor=tk.NW)
# Label mit Text OS:
os_label = tk.Label(stat_frame,text=f"OS: {os_release_pretty}")
os_label.pack(anchor=tk.NW)
host_label = tk.Label(stat_frame,text=f"Host: {hostname}")
host_label.pack(anchor=tk.NW)
kernel_label = tk.Label(stat_frame,text=f"Kernel: {kernel_release}")
kernel_label.pack(anchor=tk.NW)
uptime_label = tk.Label(stat_frame,text=f"Uptime: {get_sys_uptime()}")
uptime_label.pack(anchor=tk.NW)
shell_label = tk.Label(stat_frame,text=f"Shell: {active_shell}")
shell_label.pack(anchor=tk.NW)
res_label = tk.Label(stat_frame,text=f"Resolution: {get_screen_size()}")
res_label.pack(anchor=tk.NW)
cpu_label = tk.Label(stat_frame,text=f"CPU: ({cpu_core_count}) @ {cpu_freq.max:.2f} Mhz")
cpu_label.pack(anchor=tk.NW)
mem_label = tk.Label(stat_frame,text=f"Memory: {(get_size(svmem.used))}/{get_size(svmem.total)}")
mem_label.pack(anchor=tk.NW)
# Starte die Hauptschleife
root.mainloop()
Bye, Bye Platzhalter-Logo
Auch hier gehen wir es langsam an. Wir möchten ja das korrekte Logo unterer Distribution angezeigt bekommen. Wie stellen wir das jetzt an? Um die Basis herauszufinden, können wir wieder ein Modul nutzen.
sudo apt install python3-distro
In manchen Fällen ist distro
aber auch schon vorinstalliert. Nutzen können wir es wie folgt.
import distro
print(f"id: {distro.id()}")
print(f"name: {distro.name()}")
print(f"version: {distro.version()}")
print(f"codename: {distro.codename()}")
>>>
id: ubuntu
name: Ubuntu
version: 24.04
codename: noble
Ich habe bei Distrosea so einige Distributionen durchgesehen und leider stand in den release
-Files immer nur die Basis. Würden wir den obigen Code so nutzen könnten wir also nur auslesen, dass wir ein Ubuntu nutzen aber nicht ein Kubuntu oder Xubuntu. Linux Mint bildet übrigens eine Ausnahme. Sucht man die ID
steht dort nicht ubuntu
,
sondern linuxmint
.
Natürlich ist es schöner gleich das korrekte Logo zu sehen, um zu lernen, wie wir das überhaupt anstellen, gehen wir erstmal nur mit den Logos der Distro-Basis.
Ach und Siduction
habe ich nicht vergessen! Das ist aber ein Sonderfall. Hier müssen wir den Inhalt einer Datei auslesen. Dazu benötigt man with open
. Das muss ich auch erst erklären. (nach der Pause).
Zunächst brauchen wir überhaupt mal Logos. Ich habe uns welche vorbereitet. Ihr könnt sie einfach auf Github herunterladen.
Die PNGs sollten wie folgt in die Ordnerstruktur eingegliedert werden.
~/neofetch-tk$ tree
.
├── images
│ ├── arch_logo_350.png
│ ├── debian_logo_350.png
│ ├── fedora_logo_350.png
│ ├── mint_logo_350.png
│ ├── osuse_logo_350.png
│ ├── test.png
│ └── ubuntu_logo_350.png
└── main.py
Wir gehen in unser main.py
-File und importieren distro
und fügen folgenden Code zu den anderen Variablen hinzu.
distro_id = distro.id()
Nach dem Vorbild unseres Platzhalters erstellen wir nun Variablen, die Logos beinhalten.
distro_logo = tk.PhotoImage(file="images/test.png")
Neue 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")
Der Übersichtlichkeit halber bauen wir für die Nutzung eine Funktion, diese soll am Ende des Scripts ausgeführt werden.
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 == "linuxmint":
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)
else:
distro_icon.configure(image=distro_logo)
[...]
get_distro_logo()
# Starte die Hauptschleife
root.mainloop()
Logik
- Wenn(
if
)distro_id
genau"debian"
entspricht, wird mit der Methodeconfigure
dasimage
durchdebian_logo
ersetzt - Ist dem nicht so, werden die anderen Fälle(
elif
) durchgegangen, bis etwas Passendes gefunden wird - Ist keine Annahme zutreffen (
else:
) wird unser Testbild gesetzt.
Der ganze Code(Mit aktualisierten Kommentaren)
## Teil 8 ###
import tkinter as tk
from PIL import Image, ImageTk
import os
import socket
import distro
import platform
import psutil
import datetime
import distro
# 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 == "linuxmint":
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")
# 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="yellow")
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="yellow")
distro_icon.pack(anchor=tk.NW)
# Einen Frame Zeichen
stat_frame = tk.Frame(root,background="cyan")
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}")
user_host_label.pack(anchor=tk.NW)
# Label mit Text OS:
os_label = tk.Label(stat_frame,text=f"OS: {os_release_pretty}")
os_label.pack(anchor=tk.NW)
# Label mit Text Host:
host_label = tk.Label(stat_frame,text=f"Host: {hostname}")
host_label.pack(anchor=tk.NW)
# Label mit Text Kernel:
kernel_label = tk.Label(stat_frame,text=f"Kernel: {kernel_release}")
kernel_label.pack(anchor=tk.NW)
# Label mit Text Uptime:
uptime_label = tk.Label(stat_frame,text=f"Uptime: {get_sys_uptime()}")
uptime_label.pack(anchor=tk.NW)
# Label mit Text Shell:
shell_label = tk.Label(stat_frame,text=f"Shell: {active_shell}")
shell_label.pack(anchor=tk.NW)
# Label mit Text Resolution:
res_label = tk.Label(stat_frame,text=f"Resolution: {get_screen_size()}")
res_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")
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)}")
mem_label.pack(anchor=tk.NW)
# Führt get_distro_logo aus
get_distro_logo()
# Starte die Hauptschleife
root.mainloop()
Das war's für heute. Nächstes Mal machen wir alles "schön".
Bis nächste Woche.
Wäre es nicht besser die ganzen IF-Statements in einem nur zu machen in Deiner Funktion:
def get_distro_logo()
Dann wäre der Funktionaufruf
get_distro_logo(distro_id)
, ansonsten könnte man auchdistro_name
als Parameter wegwerfen und bei dem.format(distro_name)
gleich.format(distro_id)
einsetzen, um den Parameter zu sparen, falls man unbedingt ohne Parameter arbeiten möchte.Achso für die Leute, die die python3 formatierung mehr mögen hier die alternative zur format Funktion
f"images/{distro_name}_logo_350.png"
Jedenfalls würde ich dies als einfachen Code ansehen, als deine IF-Kombinationen, da es bestimmt auch sehr viel schreib bzw. Copy-Paste Arbeit war.
Freue mich schon auf Dein nächsten Post zum Thema.
Klar, das geht auch. Sieht auch schöner aus. 'isfile' hatten wir nur noch nicht. Der Code ist ja erstmal auch nicht auf Schönheit und Effizienz gemünzt. Es soll ja so einfach wie möglich zeigen, wie man mit "if/elif/else" arbeiten kann. Im späteren Verlauf wird der Code dann sowieso irgendwann komplett über den Haufen geworfen. Ich merke mir deine Herangehensweise und werde das einbauen, wenn wir nochmal die Zügel anziehen.
Hallo ich bin es nochmal. Ich habe mir mal Dein Code geschnappt und auf (Codeberg.Org/claudia_33155/Neofetch)[https://codeberg.org/claudia_33155/Neofetch] gepackt und dort dann meine Python Kenntnisse einfließen lassen. Damit Du mal siehst, wie ich den Code geschrieben hätte. Ich mag sauberen Code mehr irgendwie - Irgendwie habe ich da eine kleine Macke entwickelt. Aber ich möchte es halt Teilen und sehen, ob andere es genauso sehen oder nicht. Gemeinschaftliches Lernen ist schöneres Lernen als alleine. ^^