Seite 1 von 1
Label wird nicht angezeigt
Verfasst: Dienstag 29. Oktober 2024, 19:42
von DJ1NG
Servus,
- ich habe an sich eine ganz einfache Geschichte:
- Ein Fenster wird geöffnet
- ein label wird erstellt
- das label wird platziert
- ein Systembefehl wird ausgeführt (zur Demo durch "pwd" ersetzt
- es wird 3 Sekunden gewartet
- Das Fenster schliesst sich selbst
Sinn und Zweck: Eine Sounddatei wird abgespielt über den Systembefehl und solange soll das Fenster anzeigen, dass diese abgespielt wird.
Code: Alles auswählen
from tkinter import *
import os
import time
fenster = Tk()
fenster.config(background = "#00a")
#fenster.attributes("-fullscreen", True)
#fenster.fullScreenState = True
fenster.lift()
label = Label(fenster, text="Soundfile wird abgespielt", fg='white', bg="#00a", font=("Arial", 20))
label.grid(row=0, column=0, padx=0, pady=0)
os.system('pwd') # Stellvertretend für den Aufruf eines Shell-Scripts, nur zur Demo
time.sleep(3)
fenster.destroy()
Ergebnis:
Das Fenster geht auf (später mal in Fullscreen), wird blau - und KEIN Text wird angezeigt
Und obwohl der Code aussieht wie andere funktionierende Codes bei mir, wird das Label nicht angezeigt - und ich weiss nicht warum!!!
Kann mir bitte jemand auf die Sprünge helfen?
Danke, Guido
Re: Label wird nicht angezeigt
Verfasst: Dienstag 29. Oktober 2024, 20:48
von __blackjack__
Damit eine GUI läuft, muss deren Hauptschleife laufen. Bei Dir ist nicht einmal ein Aufruf von der Tk-Hauptschleife. Und `time.sleep()` geht in dem Kontext auch nicht, denn solange blockiert die GUI. Auch wenn die nichts machen soll, kann man das nicht einfach machen, weil dann das Betriebssystem merkt, dass die Anwendung nicht mehr reagiert, und daraufhin aktiv werden kann. Beispielsweise mit einem Fenster das den Benutzer darauf hinweist, dass die Anwendung nicht mehr reagiert, mit der Nachfrage ob sie beendet werden soll.
Weitere Anmerkungen: Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` fast 140 Namen ins Modul von denen nur ein kleiner Bruchteil verwendet wird. Auch Namen die gar nicht in `tkinter` definiert werden, sondern ihrerseits von woanders importiert werden. Das macht Programme unnötig unübersichtlicher und fehleranfälliger und es besteht die Gefahr von Namenskollisionen.
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
`os.system()` sollte nicht verwendet werden. Die Dokumentation zu der Funktion verweist auf das `subprocess`-Modul. Da kann man dann auch gleich eine Variante programmieren die nicht einfach 3 Sekunden wartet, sondern regelmässig nachsieht, ob der externe Prozess fertig ist. Das macht man mit der `after()`-Methode die es auf jedem Tk-Widget gibt.
GUI-Programmierung setzt für jedes nicht-triviale Programm objektorientierte Programmierung voraus. Also eigene Klassen schreiben zu können, weil man sich Zustand über Aufrufe hinweg merken muss. In einfachen Fällen kann man den noch bei den Aufrufen an die Rückruffunktionen durchreichen — da ist `functools.partial()` oft hilfreich — aber dieses Vorgehen hat Grenzen.
Re: Label wird nicht angezeigt
Verfasst: Dienstag 29. Oktober 2024, 20:49
von Dennis89
Hallo,
dir fehlt die `mainloop`, die die GUI-Elemente immer wieder aktualisiert. Dann erreichst du aber deine `destroy()` - Methode nicht mehr. Man darf bei GUI nicht einen linearen Programmablauf im Kopf haben, Änderungen passieren durch Ereignisse.
`tkinter` kennt eine `after` - Methode, die erlaubt in vorgegebenen Zeitabständen Funktionen auszuführen. Im ersten Moment würde ich das vermutlich nutzen um das Fenster nach einer Zeit wieder zu schließen. Allerdings ist es notwendig, dass du weist wie Klassen und Funktionen funktionieren.
Code: Alles auswählen
import tkinter as tk
# in ms
TIME_UNTIL_CLOSE = 3000
class App(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.config(background="#00a")
tk.Label(
self,
text="Soundfile wird abgespielt",
fg="black",
bg="#00a",
font=("Arial", 20),
).grid(row=0, column=0, padx=0, pady=0)
self.exit = False
self._countdown_to_close()
def _countdown_to_close(self):
if self.exit:
self.master.destroy()
else:
self.exit = True
self.after(TIME_UNTIL_CLOSE, self._countdown_to_close)
def main():
root = tk.Tk()
app = App(root)
app.pack()
app.mainloop()
if __name__ == "__main__":
main()
Grüße
Dennis
Re: Label wird nicht angezeigt
Verfasst: Dienstag 29. Oktober 2024, 20:51
von DJ1NG
Ich versuche gerade Beispiel aus dem Netz nachzuvollziehen, und habe dabei gefunden, dass man eine Loop nach der Ausführung eines "idleTask" beenden könne.
Daher sieht mein Code jetzt so aus:
Code: Alles auswählen
from tkinter import *
import os
import time
def idleTask():
os.system('aplay morse_sos.wav')
fenster = Tk()
fenster.config(background = "#00a")
#fenster.attributes("-fullscreen", True)
#fenster.fullScreenState = True
fenster.lift()
label = Label(fenster, text="HILFERUF läuft!!!", fg='white', bg="#00a", font=("Arial", 20))
label.grid(row=0, column=0, padx=0, pady=0)
fenster.after_idle(idleTask)
fenster.mainloop()
Das Ergebnis:
1. Das Fenster wird blau und SOS wird abgespielt.
2. Danach wird der Text vom Label angezeigt und ich muss das Fenster doch wieder gewaltsam schliessen.
Nochmal zur Info, mein gewünschter Ablauf:
1. Fenster öffnen
2. Label "Hilferuf läuft!!!" anzeigen
3. Sound abspielen
4. Fenster schliesst sich von selbst
Für heute muss ich leider aufgeben, da meine Augen gerade schlapp machen. Aber ich würde mich extrem freuen, wenn mir jemand eine Hilfestellung gibt in dieser Angelegenheit.
Have a gud n8,
Guido
Re: Label wird nicht angezeigt
Verfasst: Dienstag 29. Oktober 2024, 20:58
von DJ1NG
Dennis89 hat geschrieben: Dienstag 29. Oktober 2024, 20:49
`tkinter` kennt eine `after` - Methode, die erlaubt in vorgegebenen Zeitabständen Funktionen auszuführen. Im ersten Moment würde ich das vermutlich nutzen um das Fenster nach einer Zeit wieder zu schließen. Allerdings ist es notwendig, dass du weist wie Klassen und Funktionen funktionieren.
Gerade noch gesehen: Danke
Aber leider, wenn ich nach dem Label einfüge
dann wird zuerst die WAV-Datei abgespielt und DANN das Fenster eröffnet mit der Nachricht "Soundfile läuft".
Der Text soll aber unbedingt während das Abspielens zu lesen sein und danach verschwinden - quasi als Blockade für den User, damit er weiss: Jetzt läuft's. Denn das Soundfile wird später über ein Funkgerät ausgestrahlt, und der Benutzer bekommt das nicht mit. Daher brauche ich eine "optische Signalisierung und Sperre", solange der Sound gesendet wird.
Re: Label wird nicht angezeigt
Verfasst: Dienstag 29. Oktober 2024, 23:11
von __blackjack__
@DJ1NG: `os.system()` ist halt doppelt falsch. Man benutzt das nicht, wie ich schon geschrieben habe, *und* es blockiert, genau wie `time.sleep()`, was in einer GUI nicht passieren darf, solange die GUI nicht reagieren kann.
Auf die anderen Anmerkungen könntest Du auch mal eingehen. Also Sternchen-Import, `main()`-Funktion, und so weiter.
Ich würde da auch nicht eine Feste Zeit warten, sondern auf das Ende des externen Programms warten, indem in regelmässigen Abständen per `after()` eine Funktion/Methode aufgerufen wird, die testet ob das externe Programm noch läuft. Im `subprocess`-Modul gibt es einen Datentyp der einen externen Prozess starten kann, und wo man das Objekt dann fragen kann ob der noch läuft, oder schon fertig ist.
`after_idle()` kann man verwenden um das anzustossen, und wenn man keine Klasse schreiben will, kann man sowohl `after_idle()` als auch `after()` noch weitere Argumente mitgeben, die dann beim Rückruf an die Rückruffunktion durchgereicht werden.
Namen werden in Python übrigens per Konvention klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).
Re: Label wird nicht angezeigt
Verfasst: Mittwoch 30. Oktober 2024, 08:33
von __blackjack__
Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from subprocess import Popen
BACKGROUND_COLOUR = "#00a"
def check_for_end(widget, audio_process):
if audio_process.poll() is None:
widget.after(500, check_for_end, widget, audio_process)
else:
widget.quit()
def main():
fenster = tk.Tk()
fenster.config(background=BACKGROUND_COLOUR)
# fenster.attributes("-fullscreen", True)
fenster.lift()
tk.Label(
fenster,
text="HILFERUF läuft!!!",
foreground="white",
background=BACKGROUND_COLOUR,
font=("Arial", 20),
).grid(row=0, column=0)
fenster.after_idle(
lambda: check_for_end(fenster, Popen(["aplay", "morse_sos.wav"]))
)
fenster.mainloop()
if __name__ == "__main__":
main()
Re: Label wird nicht angezeigt
Verfasst: Mittwoch 30. Oktober 2024, 18:32
von DJ1NG
__blackjack__ hat geschrieben: Mittwoch 30. Oktober 2024, 08:33
Ungetestet:
Yippiejahjahyippiehyippieyeah
Mercie vielmohls!