Bald geht es für mich in die Sommerpause, das heißt, es wird 3-4 Wochen keine Artikel von mir geben. Bis es so weit ist, gibt es aber noch zwei Artikel dieser Serie. Mein Ziel ist es also, bis dahin unser Tool so zusammenzuschrauben, dass etwas Ansehnliches dabei herauskommt. Pause kann man machen, wenn die Arbeit getan ist. Wir spucken also nochmal kräftig in die Hände und geben alles.
Es ist übrigens nicht so, dass ich eine fertige Version des Programms auf der Festplatte habe, aus der ich einfach nur Zeilen kopiere und beschreibe. Die Artikel entstehen parallel zur Software-Entwicklung.
Das war's jetzt aber mit der Einleitung. Auf geht's!
So sieht's aus
Wir können uns mal wieder auf die Schulter klopfen, denn wir haben viel geschafft.
Was steht heute an?
- Shell auslesen
- Bildschirmauflösung
- CPU
Shell auslesen
Das können wir schnell abhandeln. Wenn Ihr, wie empfohlen, mal die Methode envron
des Moduls os
angeguckt habt, sollte euch das Folgende bekannt vorkommen.
os
ist ja schon importiert. Dahingehend müssen wir nicht unternehmen, wir brauchen aber eine neue Variable.
Wie auch beim USER
holen wir uns das key
von SHELL
active_shell = os.environ["SHELL"]
Unter dem Kernel-Label legen wir ein neues Label an:
shell_label = tk.Label(stat_frame,text=f"Shell: {active_shell}")
shell_label.pack(anchor=tk.NW)
Der Output wir höchstwahrscheinlich so aussehen
/bin/bash # Oder zsh oder fish
Eigentlich wollen wir ja nur die Shell, aber hier wird auch der Pfad angezeigt. Das ist nicht weiter problematisch, darum kümmern wir uns im letzten Teil vor der Sommerpause, den Python hat noch so einige Methoden auf Lager um Daten zu manipulieren. Aber alles nach der Reihe.
Die Auflösung
Python hat von Haus aus so einige Features integriert, die wir einfach abrufen können. Um unsere Bildschirmauflösung herauszubekommen, nutzen wir winfo_screenwidth()
und winfo_screenheight()
.
Test-Programm
import tkinter as tk
screen_width = winfo_screenwidth()
screen_height = winfo_screenheight()
root = tk.Tk()
root.title("Test")
root.geometry("300x300")
root.mainloop()
Wenn wir so wie immer an die Sache herangehen, sollte das Programm crashen:
Traceback (most recent call last):
File "", line 3, in
NameError: name 'winfo_screenwidth' is not defined
Wir müssen uns also etwas anderes überlegen.
Das Problem ist, dass winfo
eine Methode des Fensters an sich ist und wir es nur so anwenden können:
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
Wenn wir das nun ändern, bekommen wir diesen Error:
Traceback (most recent call last):
File "", line 3, in
NameError: name 'root' is not defined
Das bedeutet nichts anderes, als dass Python root
nicht kennt. Wir haben eine Methode abgerufen, bevor wir Python gesagt haben wir, was root
eigentlich ist.
Neuer Versuch
Wir platzieren unsere zwei Variablen einfach unter root
import tkinter as tk
root = tk.Tk()
root.title("Test")
root.geometry("300x300")
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
print(screen_width)
print(screen_width)
root.mainloop()
Nun ist das Ganze von Erfolg gekrönt. Wir sind aber leider noch nicht fertig, denn irgendwann wollen wir ja auch Live-Daten auslesen und so eine Auflösung könnte sich ja unter Umständen verändern. Wir packen den Code in eine Definition, fügen ihn in unseren Code ein und schrieben ein neues Label:
def get_screen_size():
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
return f"{screen_width}x{screen_height}"
Diese Definition packen wir unter def get_screen_size
.
res_label = tk.Label(stat_frame,text=f"Resolution: {get_screen_size()}")
res_label.pack(anchor=tk.NW)
Logik
- Eine Definition liest den enthaltenen Code ein, speichert ihn, ruft diesen aber nicht sofort aber. Deswegen bekommen wir keine Fehlermeldung, obwohl sie vor
root
steht. - Mit
return
sagen wir aus, dassget_screen_size()
bei Abfrage den String f"{screenwidth}x{screenheight}" ausgeben soll. - In unserem Label setzen wir mittels
f-String
get_screen_size()
in den Text ein.
CPU
Hier müssen wir wieder nicht importieren, denn wir nutzen das bereits importierte psutil
und dessen Methode cpu_freq
.
Es kommen zwei neue Variablen hinzu
cpu_freq = psutil.cpu_freq()
cpu_core_count = psutil.cpu_count(logical=False)
cpu_freq
beinhaltet mehrere Methoden .z.B: min
, current
und max
.
Für unsere erste Version, die sich an Neofetch orientiert, benötigen wir nur max.cpu_core_count
macht genau das, was der Namen sagt: Es listet die Kerne der CPU auf. Wir benötigen nur die physischen, also die tatsächlichen Kerne, deswegen wird logical
auf False
gesetzt.
In unserem neuen CPU-Label sieht das ganze dann so aus:
cpu_label = tk.Label(stat_frame,text=f"CPU: ({cpu_core_count}) @ {cpu_freq.max:.2f} Mhz")
cpu_label.pack(anchor=tk.NW)
Logik
cpu_freq.max:.2f} Mhz
liest die Maximal-Taktung der des Prozessors aus.:.2f
begrenzt die Zahl auf zwei Stellen nach dem Komma.- Die Ausgabe ist in Mhz
Der ganze Code
## Teil 7 ###
import tkinter as tk
from PIL import Image, ImageTk
import os
import socket
import distro
import platform
import psutil
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}"
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)
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()
Ja, es füllt sich so langsam, wir sind schon bei 90 Zeilen Code und kein Ende in Sicht. Wie immer: Spielt ein bisschen mit den Möglichkeiten herum und nutzt die Suchmaschine eueres Vertrauens, um vielleicht noch Zusatzinfos zu finden.
Bis nächste Woche.
Eine extrem coole Serie; vielen Dank!
Wegen Krankheit hinke ich erheblich hinterher und komme jetzt erst dazu, die Folgen nachzuarbeiten. Glücklicherweise laufen sie ja nicht weg. Bei dieser Folge hat ich das Problem, dass cpu_freq in der cpu_label Zuweisung einen Fehler warf. Auf meinem System wurde dort cpu_freq als Funktionsaufruf interpretiert, was es ja nicht sein soll, sondern eine Variable. Umbenennen von cpu_freq (z.B. in cpu_frequence) war die Lösung.
Nur zur Info: Ich benutze opensuse Tumbleweed, Python Version ist 3.11 Die zu importierenden Python Module heißen dort etwas anders. Meist genügt es, die explizite Pythonversion anzugeben, z.B. sudo zypper install phtyon311-distro, etc. Einzige Ausnahme bisher war PIL, von dem ich nur den Fork Pillow für Tumbleweed finden konnte. Der Assistant in Thonny erzählt mir aber etwas von dass die Module Image und ImageTk gar nicht verwendet würden. ... Line 7 : Unused Image imported from PIL It looks like the imported module or variable is not used. Line 7 : Unused ImageTk imported from PIL ... Das Programm läuft fehlerfrei ohne den Import der Methoden aus dem PIL Modul - zumindest soweit.