Fehlermeldung bei der Erstellung von GUI

L I V E Stammtisch ab 20:30 Uhr im Chat
  • Hallo,

    ich habe mal eine Frage. Ich versuche gerade mein Pi per GUI anzusteuern, dabei programmiere ich in Python.

    Im Anhang lade ich mal mein Code hoch. Wenn ich meine "def aktionStopp():" ausführe, dann möchte ich, dass wenn ich auf den Stopp-Button in meiner GUI drücke, dass die Cam an meinem Pi anfängt aufzunehmen. Ich weiß, Stopp ist etwas verwirrt, aber das soll jetzt erstmal nur als Test dienen. Wenn ich in meinem Code, die Zeile 13 ("shutdown")ausführe, dann klappt alles wunderbar. Also der Pi fährt dann runter. Nur wenn ich die Zeile 14 ("raspivid") ausführen möchte, dann erhalte ich eine Fehlermeldung (FileNotFoundError: [Errno 2] No such file or directory: '/sbin/raspivid': '/sbin/raspivid').

    Wenn ich den Pfad folge, dann habe ich die Datei "shutdown", nur die Datei "raspivid" fehlt. Was ist das für eine Datei? Reicht es, wenn ich da einfach eine .txt Datei erstelle und es unter raspivid abspeichere?

    Oder muss ich da eine bestimmte Bibliothek in meinem Code einbinden?

    Danke

    LG

    Anton

  • Zur hilfreichsten Antwort springen
  • Anton Nein das reicht natürlich nicht, das ist das Programm, welches Videos aufnimmt. Das kann man nicht einfach durch eine leere Textdatei ersetzen. Du musst halt mal schauen wo dieses Programm bei Dir tatsächlich liegt. ``/opt/vc/bin/raspivid`` ist beispielsweise auch nicht unüblich.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Anmerkungen zum Quelltext: Weder `picamera` noch `GPIO` werden in dem Programm verwendet. Dann braucht man die auch nicht importieren.

    Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

    Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

    Namen nummeriert man nicht durch und man sollte auch keine kryptischen Abkürzungen verwenden. Also nicht `schaltf1` und `schaltf2` mit Kommentaren was das für Buttons sind, sondern `play_button` und `stop_button` und schon kann man sich auch die Kommentare sparen.

    Wobei die Namen hier ja auch nicht wirklich benutzt werden, dann kann man sie sich eigentlich auch ganz sparen.

    Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

    Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen als Argument(e) übergeben. Die beiden Rückruffunktionen verwenden `root` das irgendwie magisch aus dem Nichts kommt, was aber nicht funktioniert wenn das sauber, lokal in einer Hauptfunktion definiert ist.

    Hier kommt man noch mit `functools.partial()` aus, aber eigentlich braucht man für jede nicht-triviale GUI objektorientierte Programmierung (OOP), also mindestens eine eigene Klasse.

    Kommentare und Methodennamen sind etwas verwirrend — „play“ ist Abspielen, nicht Aufnehmen.

    Warum sind die `label*` *in* den Funktionen nummeriert als wenn da nicht die gleichen Namen vorkommen dürften in verschiedenen Funktionen? Das macht keinen Sinn.

    Der `run()`-Aufruf hat ganz sicher kein sinnvolles Ergebnis für das zweite Argument von `Label`. Da würde eine Abbildung erwartet, also beispielsweise ein Wörterbuch mit Tk-Optionen für das `Label`. `run()` liefert aber etwas völlig anderes als Rückgabewert.

    Du wirst Probleme mit der GUI bekommen weil `run()` ein blockierender Aufruf ist. Das heisst solange der läuft, blockiert die GUI. Sie wird währenddessen weder aktuialisiert, noch reagiert sie auf den Benutzer oder andere Ereignisse.

    Zwischenstand (ungetestet):

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Programm im Pfad finden:

    Code
    which raspivid

    Python-Äquivalent:

    Code
    shutil.which

    Man muss nicht unbedingt den Pfad zu einem Programm mit angeben. Das spielt besonders dann eine Rolle, wenn man mit unterschiedlichen Distributionen arbeitet, die ihre Programme auch manchmal an anderen Orten unterbringen. Deswegen gibt es die Umgebungsvariable PATH, die dazu verwendet wird, Programme zu finden. subprocess.run sucht auch im Pfad.

    Ich hätte raspivid z.B. in /bin/ erwartet, aber ist wohl in /opt/...

    • Hilfreichste Antwort

    Hallo,

    nutze für dein GUI absolute Pfade:

    Aber die Datei wird jetzt jedes mal überschrieben, daran musst du noch arbeiten.

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • TODO abgearbeitet, jetzt sollte der Dateiname aus dem aktuellen Datum mit Uhrzeit bestehen und in '/home/pi' abgelget werden.

    Ungetestet:

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Hallo Dennis,

    herzlichen Dank für deine Hilfe. Ja, ich werde daran arbeiten, dass die Datei nicht jedes Mal überschrieben wird.

    Ein, zwei Fragen hätte ich aber noch.

    Code
    Du schreibst "def on_record(master)".

    Was macht der "master" hier genau?

    Und in der Zeile 25 verwendest Du

    Code
    command=partial(video_stop, root)

    Was genau macht "partial"? Kannst Du das vielleicht an meinem Beispiel erklären? Ich habe zwar was online gefunden, aber so richtig verstanden habe ich das nicht. Würde es vielleicht anhand meines Beispiels verstehen. Wozu dient "from functools import partial"?

    Und was macht die Zeile 29? Ich verstehe das so, dass wenn der "name" gleich "main" ist, dann führe Zeile 19 "def main()" aus.

    Code
    if __name__== "__main__":
        main()

    Danke für deine Hilfe.

    LG

    Anton

  • Hallo,

    vorweg muss ich erst mal verdeutlichen, das ich nur den fertigen (ungetesteten) Code von __blackjack__ genommen habe und nur absolute Pfade bzw. individuelle Dateinamen eingebaut habe. Will mich hier nicht mit fremden Federn schmücken, bin ja kein Papagei.

    Zu deinen Fragen: Die Funktion 'on_record' wird aufgerufen, wenn du den Button "Record" drückst. Wenn du wissen willst, was 'master' ist, dann musst du mal die Definition des Buttons "Record" anschauen, denn von dor aus wird das Argument 'master' an die Funktion 'on_record' übergeben.

    Wie du dann feststellst, gibt es dort den Name 'master' nicht. Aber die Funktion 'on_record' nimmt nur ein Argument (master) entgegen und beim Funktionsaufruf wird nur ein Argument übergeben: root.

    Jetzt weist du, das 'master' == 'root' ist. Was ist 'root'? Ein paar Zeilen höher findest du die Antwort. Das ist die Instanz der 'Tk'-Klasse. Dadurch wird tkinter inizialisiert und ein erstes bzw. das Hauptfenster erstellt.

    Wenn du nun Labels, Buttons etc. erstellst/änderst musst du das Fenster in dem sich das Element befindet als Referenz angeben. Schau dir alle Zeilen an, in denen tkinter-Elemente erstellt oder geändert werden. Du findest immer als erstes 'root' (root==masster).

    Für die Frage zu der if-Bedingung habe ich zwei Links für dich:

    https://www.freecodecamp.org/news/if-name-main-python-example/

    https://stackoverflow.com/questions/4191…if-name-main-do

    Edit: Habe mir gerade dein original Programm angeschaut. __blackjack__ hatte ja erwähnt das kein ausführbarer Code auf Modulebene (Die Ebene ohne Einrückungen) stehen soll. Die 'if'-Abfrage ist die Ausnahme. Ich hoffe mit den Links verstehst du den Grund dafür besser.

    Zum Thema 'partial'.

    Du bist es vermutlich gewöhnt, das du eine Funktion mit Argumenten so aufrufst:

    Code
    def do_something(name):
        print(name)
    
    
    def main():
        do_something('Peter')
    
    if __name__ == '__main__':
        main()

    Schau dir das mal genau an, die 'if-Bedingung ist erfüllt, dann wird 'main' ausgeführt. Hinter dem Aufruf steht aber ein Klammerpaar. Weiter gehts in 'main', hier wird 'do_something' ausgeführt und als Argument wird '"Peter"' übergeben. Auch hier das Klammerpaar. Sprich der Interpreter ruft die Funktion auf, sobald er dahinter die Klammern entdeckt.

    So wenn der Interpreter jetzt deinen Button-Code liest, soll er die Funktion 'on_record' ja nicht gleich ausführen. Also kein Klammerpaar dahinter. Eigentlich recht easy, aber was ist mit 'root', das musst du beim Funktionsaufruf ja mit übergeben.

    Hier kommt jetzt 'partial' zum Einsatz. partial(funktionsname, argument, naechstes_argument). Jetzt wird die Funktion erst ausgeführt, wenn der Button gedrückt wird und dann 'command' zum Einsatz kommt.

    Du wirst das ein oder andere mal im Internet anstatt 'partial' einen Ausdruck finden in dem irgendwas mit 'lambda' vor kommt. Das kann man auch verwenden, ich finde aber die Syntax von 'partial' einfacher bzw. lesbarer.

    Wozu dient "from functools import partial"?

    'partial' ist nicht einfach so verfügbar. Das musst du in dein Programm importieren, ebenso wie 'tkinter', 'datetime', 'Path' etc.

    So jetzt hast du das bis hier her gelesen und bist vielleicht so schlau wie davor?

    Dann musst du beginnen dir die Grundlagen von Python anzueigenen. Es ist wichtig dass du mit Funktionen arbeiten kannst, dass du weist wo welcher Name verfügbar ist und wie du die Namen durch das Programm übergibst. Da führt gar kein Weg daran vorbei. Sobald dein GUI etwas komplexer wird, reicht dir das aber auch nicht mehr aus und du musst mit Klassen arbeiten.

    Zum Glück bietet Python auch ein offizielles Tutorial mit dem du das alles lernen kannst:

    https://docs.python.org/3/tutorial/

    Du hast ja bestimmt gemerkt, dass du mit deutsch nicht all zu weit kommst. Es gibt das Tutorial auch in deutsch, allerdings *nicht* in der aktuellen Version. Die absoluten Grundlagen kannst du dir damit zwar auch aneignen, aber es empfiehlt sich immer das aktuelle Tutorial durchzuarbeiten.

    Naja hier trotzdem mal noch die deutsche Version:

    https://buildmedia.readthedocs.org/media/pdf/py-t…tutorial-de.pdf

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

    Einmal editiert, zuletzt von Dennis89 (26. November 2021 um 12:14)

  • Hallo,

    ich habe mal eine Frage. Mein GUI läuft nun soweit ganz gut. Also "Record" zeichnet auf und speichert an dem vorgesehenen Ort.

    Für "Record" nutze ich den Befehl im SSH "sudo raspivid "-o" Dateiname.h265".

    Das habe ich soweit verstanden, wie ich diesen Befehl in mein Code implementieren muss.

    Nun will ich für "Pause" und "Stop" die Befehle in mein Code einbauen. Wenn ich mit dem Terminal (SSH) die Kamera steure, dann verwende ich fürs Stoppen der Aufnahme STRL-C. Nur weiß ich nicht, wie ich dies in mein Code einbinden kann. Kann ich diesen Keyboard-Befehl verwenden oder gibt es eine Bibliothek die man stattdessen verwenden sollte, weil es einfacher wäre?

    Grüße

    Anton

  • Die Signale werden auch oft von den *nix-Tools untersützt.

    Man kann z.B. den Fortschritt von dd mit dem Signal SIGUSR1 ausgeben lassen.

    Hier mal ein dd-Beispiel:

    Sending a USR1 signal to a running 'dd' process makes it print

    I/O statistics to standard error and then resume copying.

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!