Subprocess - Anderes Programm im Hintergrund starten

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Subprocess - Anderes Programm im Hintergrund starten? Schau mal ob du hier fündig wirst!

  • Jetzt bin ich doch wieder neugierig. Kurz noch zu der Erklärung des Codes: die Variable "ddserver" wird bei Druck auf einen Taster auf 1 und bei wiederholtem Druck auf den Taster auf 0 gesetzt. '=> Funktioniert, da "print" immer ausgegeben wird.

    An der Stelle fehlt mir das wissen. Wie müsste es denn richtig aussehen?

  • Da gibt es mehrere Möglichkeiten. Ich schreibe Dir nachher gerne eine auf, falls niemand anderes schneller ist (bin gerade etwas knapp mit der Zeit).

    Falls Du Programmieren lernen willst, würde ich Dir aber vor allem dringend raten, zu verstehen, warum Dein Code nicht funktioniert. Tip hierzu: Finde heraus was man im Zusammenhang mit Variablen unter scope versteht und wie sich dieser in Python verhält. Dann setz Dich, am besten mit einem Bleistift, hin und vollziehe Zeile für Zeile nach, was Dein Code macht, wenn er mit ddserver==1 und mit ddserver==0 aufgerufen wird.

  • Servus,

    da hat Manul absolut recht! Du verstehst nicht so ganz was du tust, Zeile 5 beispielsweise macht garnichts, wofür hast du sie eingefügt?

    Python
    def func(): 
            proc=subprocess.Popen( #Prozess)
            while #Button not pressed :
                pass
            proc.kill()
    
    while True:
        if #Button pressed:
            func()    

    Das wäre ne Möglichkeit um das zu regeln, aber Achtung, wenn du das einfach integrierst, dann hängt dein Programm sich auf, da dein Skript in nem aus diesem loop nicht mehr raus kommt!!


    Du könntest ein Skript laufen lassen, das lediglich diese eine Aufgabe hat, dann stört es dich nicht, dass es in nem while True-loop hängt, wenn du es in einem größeren Programm integrieren möchtest, müsstest du dafür beispielsweise einen thread anstarten.

    Gibt bestimmt noch andere und bestimmt auch elegantere Möglichkeiten u, aber das wäre zumindest schon mal ein möglicher Ansatz.

    Natürlich mache ich Fehler :stumm:

  • Vielleicht nochmal was interessantes:

    ich habe deinen Code mal etwas modifiziert, dass er so aussieht, wie ich schätze dass du es geplant hattest:

    Der Code wartet einfach immer auf ne Tastatureingabe, wenn es ne 1 ist, wird der Prozess gestartet, bei ner 0 gekillt.

    Wenn ich das Programm starte und mehrmals hintereinander 1 drücke, läuft alles gut und jedes mal wird ein neuer Prozess angestartet.

    Aber sobald ich 0 eingebe, kommt ne Fehlermeldung:

    Code
    Traceback (most recent call last):
      File "C:\Users\user\Desktop\Test.py", line 15, in <module>
        func(input('0 oder 1'))
      File "C:\Users\user\Desktop\Test.py", line 10, in func
        proc.kill()
    UnboundLocalError: local variable 'proc' referenced before assignment

    Was er mir hiermit sagt, ist

    ich kann den Prozess nicht beenden, ich kenne den Prozess nicht"

    Das ist genau das:

    Finde heraus was man im Zusammenhang mit Variablen unter scope versteht und wie sich dieser in Python verhält.

    Die Fehlermeldung hast du in deinem Programm nicht, weil du noch einen anderen Fehler gemacht hast, du hast immer erst mal den Prozess gestartet, noch bevor du geprüft hast, welchen wert deine Variable hat.

    ACHTUNG: proc=subprocess.Popen(...) ist nicht einfach nur ne variabhlendeklaration, dieser Befehl startet den Prozess bereits!!1

    Natürlich mache ich Fehler :stumm:

  • Ich lerne immer gerne dazu. Und weiß an der Stelle tatsächlich nicht wirklich was ich tue. Ist mehr ein try and error :|

    Mein Ursprünglicher Code sah wie folgt aus und funktionierte insofern super, als dass er ddserver startetet (1. Druck auf den Taster) und ddserver beendet (2. Druck auf den Taster). Letzteres allerdings nur mit dem Zusatz "(defunct)"

    Code
    def wlan_ddserver(ddserver):
        if ddserver == 1:
            subprocess.Popen('./ddserver/ddserver')
            print("ddserver: " + str(ddserver))
        elif ddserver == 0:
            os.system('pkill -x ddserver')
            print("ddserver: " + str(ddserver))

    Was die if-Abfrage bei ddserver = 1 / ddserver = 0 da macht ist mir in diesem Fall bewusst.

    Denke ich habe den Fehler erkannt. Ich habe mit jedem Aufruf der Funktion den Prozess "ddserver" neu gestartet um dann den neu gestartete Prozess direkt wieder zu beenden. Demzufolge würde vermutlich das folgenden funktionieren oder? (Habe grade keine Möglichkeit das zu testen...)

    Code
    def wlan_ddserver(ddserver):
        if ddserver == 1:
            process = subprocess.Popen('./ddserver/ddserver')
            print("ddserver: " + str(ddserver))
        elif ddserver == 0:
            process.kill()
            print("ddserver: " + str(ddserver))

    Auf jeden Fall schon mal vielen Dank für die Hilfe :)

  • Nein, dein letztes Beispiel wird wieder nicht wie gedacht funktionieren. Wie Manul schon schrieb, beschäftige dich mal mit Scopes. Ich bin gerade etwas eingeschränkt (nur am Handy im ICE), mit Code-Beispielen oder längeren Erklärungen kann ich also leider gerade nicht dienen.

    Das bekommst du genau die Fehlermeldung, die ich oben schon gepostet habe ;)

    Natürlich mache ich Fehler :stumm:

  • Was funktionieren würde (glaube ich, müsste eigentlich!), wäre folgendes:

    Python
    def wlan_ddserver(ddserver):
        global process            # SEHR mächtig, sehr unschön
        if ddserver == 1:
            process = subprocess.Popen('./ddserver/ddserver')
            print("ddserver: " + str(ddserver))
        elif ddserver == 0:
            process.kill()
            print("ddserver: " + str(ddserver))

    Allerdings ist programmieren mit globalen Variablen im allgemeinen eher unschön

    Natürlich mache ich Fehler :stumm:

  • Moin

    - global ist scheisse da hast du recht. Dann aber verwendet man es auch nicht.

    - anstelle von kill() verwendet man allgemein terminate()

    Die Suchfunktion hier liefert sehr viel. Eigentlich alles, was hier zum Teil geschrieben wurde. Anbei mal ein Beispiel von hier:

  • Mein erster Gedanke war jetzt auch global :danke_ATDE:, wohl wissend, dass global grundsätzlich vermieden werden sollte.

    Wenn das jedoch funktioniert, frage ich mich an dieser Stelle schon, warum "global" nicht gewünscht ist. Wenn ich mir die anderen Lösungen von Bootsmann und Co. anschaue (Bitte nicht falsch verstehen, vielen Dank für eure Hilfe!), ist eine alternative Lösung auch noch komplexer und weicht von meiner grundsätzlichen Programlogik in Bezug auf die Taster ab. (Das Script ist bereits über 500 Zeilen lang, deshalb poste ich es mal nicht ;)) Ich muss an der Stelle gestehen, dass ich zwar grundsätzlich versuche solche unschönen Dinge zu vermeiden, bin aber auch ein Pragmatiker und möchte es einfach halten.

  • Das was bootsmann gepostet hat funktioniert, ist allerdings wieder das selbe Problem wie bei meinem example oben... du hängst in einem while True loop fest, aus dem kommst du auch nicht mehr raus!

    Das heißt wenn dein Programm mehr tun soll als nur per knopfdruck den Prozess starten oder stoppen, funktioniert das so erst mal nicht.

    Zum Thema globale Variablen finde ich diesen Artikel ganz gut!

    Natürlich mache ich Fehler :stumm:

  • Ich würde es wahrscheinlich ungefähr so machen:

  • global hat so viele Nebeneffekte, welche du im www ausführlich und erläutert findest. Ein Hauptaspekt ist, dass global zu unerwarteten Nebeneffekten führt. Hier rede ich aus Erfahrung. Ich habe eine Anwendung (Alarmanlage - siehe Signatur) geschrieben und am Anfang habe ich hier auch für den Status (ob Alarmanlage eingeschaltet ist oder nicht) ``global`` verwendet. Aber nach kurzer Zeit bemerkte ich, dass ab dem vierten oder dritten mal, als ich die Alarmanlage ausschalten wollte, sie immer noch scharf war - oder umgekehrt. Die Kontrolle entglitt mir. Da ich eh eine Datenbank verwende, habe ich den Wert in die DB gespeichert und hole jedes mal den Wert und überprüfe diesen. Seit dem Moment an habe ich keine Probleme mehr.

    Vorher:

    Nachher:

  • Ich würde es wahrscheinlich ungefähr so machen:

    Diese Variante ist absolut empfehlenswert

    es funktioniert wie ne globale Variable, aber eben wie eine die man im Griff hat!

    Ist wahrscheinlich eine der elegantesten Varianten das zu lösen, aber auch hier gilt wieder: versuch OOP zu verstehen, wenn du es nutzt ;)

    Natürlich mache ich Fehler :stumm:

  • Vielen Dank für eure Mühe. Habe mir grade mal Wurzelbert's Text-Empfehlung durchgelesen und einiges gelernt xD Werde aber aus folgenden Gründen and dieser Stelle bei global bleiben:

    1. Das Skript ist Teil eines Intervallauslösers für meine Kamera inkl. OLED-Display mit Menü und wird noch um eine Schrittmotorsteuerung erweitert. Der Taster für ddserver wird nur in bestimmten Situationen gebraucht und i.d.R nach jeder Benutzung komplett ausgeschaltet

    2. Der Aufwand den ich betreiben müsste, um global zu vermeiden ist mir erheblich zu groß (die Funktion ist Teil eines Scriptes welches über 500 Zeilen hat)

    Sollte ich aber mal in den sauren Apfel beißen und mir gpiozero anschauen, dann werde ich mir nochmal Manul's Lösung anschauen.:)

  • Die Variante von Manul ist eigentlich nichts anderes als eine Umschreibung von global. running ist auch hier global. Exemplare der Klasse werden nicht wirklich benutzt, denn die `__init__()` wird ja nur wegen ihres globalen Effekts aufgerufen wie eine Funktion.

    Wurzelbert das Zauberwort heisst Parameterübergabe. Irgendwo im Programm muss dauerhaft überprüft werden, ob der Prozess läuft oder nicht.

  • Die Variante von Manul ist eigentlich nichts anderes als eine Umschreibung von global. running ist auch hier global. Exemplare der Klasse werden nicht wirklich benutzt, denn die `__init__()` wird ja nur wegen ihres globalen Effekts aufgerufen wie eine Funktion.

    Ja das stimmt schon, aber in irgendeiner Form benötigst du was globales! Oder du musst dir kreative Konstrukte überlegen... Ne Paramterübergabe ist in diesem Fall eigentlich nicht wirklich schön:

    Hier wird keine globale Variable benötigt, man steckt auch nicht in nem whileTrue-loop fest aber schön ist natürlich was anderes...


    Bra!nPa!N Aber schöner als global  ist es aber immernoch :fies:


    bootsmann du als alter Hase solltest doch wissen, dass eine Konvertierung der eingabe in einen int selbstbeschuss ist. Behalte es als string und frag ab if eingabe =='1': dann schmiert nicht alles ab, wenn ich statt "0" oder "1" beispielsweise "Hello World" eingebe ;)

    Natürlich mache ich Fehler :stumm:

Jetzt mitmachen!

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