zufällige Variabele auswählen

  • Also nehmen wir an ich habe 4 variablen [var1; var2; var3 und var4] ich möchte jetzt eine zufällige variable unter diesen auswählen, aber es sollen nur die in betracht kommen die den wert 1 haben. dann soll eine variable namens wlicht je nach dem welche variable gewählt wurde einen wert bekommen. (Für var1 soll es 1 sein für var2 soll es zwei sein ... usw)

    Ich hoffe jemand hat eine Idee

    Schonmal danke auf jeden fall

  • Das ist abstrakt und konfus beschrieben. Was willst du erreichen? Wozu dient dieser Code?

  • oke war vll etwas zu sehr drin in meinem code:

    Schritt 1:

    ich habe var1; var2; var3; var4

    zuerst will ich das geprüft wird welche variablen den wert 1 haben, diese sollen im nächsten schritt berücksichtigt werden die anderen nicht.

    Schritt 2:

    es wird unter den variablen die berücksichtigt werden eine zufällige ausgewählt.

    Schritt 3:

    wenn var1 ausgewählt wurde, soll: wlicht = 1

    wenn var2 ausgewählt wurde, soll: wlicht = 2

    usw.

    Ist das verständlicher ?

  • Tell

    könntest du mir das erklären ich blick irgendwie nicht ganz durch. Bin n anfänger.

    also wie tu ich jetzt meine vier variablen da einfügen?

    Und wenn du zeit hast: Was tut das Programm da im einzelnen

    Danke schonmal

  • > also wie tu ich jetzt meine vier variablen da einfügen?

    vars[0] ist die erste

    vars[1] ist die zweite

    vars[2] ist die dritte

    vars[3] ist die vierte

    Das Script checkt ob ueberhaupt eine der Variablen 1 ist, Wenn nicht, macht es nichts.

    Dann erfindet es eine Zahl im Bereich von 0..3 und prueft ob die entsprechende Variable 1 ist. Wenn das nicht erfuellt ist, erfindet es die naechste Zahl.

    Frueher oder spaeter findet es eine Variable die 1 ist und setzt dann wlicht.

  • danke


    Jetzt taucht ein Problem auf und ich weiß nicht wo der Fehler ist vllt findet ja von euch einer was:

    Mein Code (läuft unter Thonny am RPi 4)

    findet ihr Fehler oder habt Verbesserungsvorschläge.

    soll-Funtionsweise:

    Schritt1:

    im GUI werden die zwei werte festgelegt die später für die verzögerung bzw. die dauer bei der Lampe stehen

    dannach werden mit den checkboxen die led s ausgewählt die später berücksichtigt werden sollen

    Schritt3:

    warte bis button gedrückt

    Schritt4:

    Wähle aus den vorher gewählten Leds eine zufällig aus

    Schritt5:

    zurück zu schritt3:

    2 Mal editiert, zuletzt von Janic (15. Februar 2021 um 00:07)

  • In einem GUI-Programm darf es nur einen Endlosloop geben: den Event-Loop in der GUI-Library.

    Die while True Schleife muss weg!

    - - - - - -

    Und zudem:

    * rand nur einmal initialisieren

    * diese Sequenz in eine Funktion packen

    GPIO.output(kontroll, GPIO.LOW)
    time.sleep(zeit_au_la)
    GPIO.output(lu, GPIO.HIGH)
    time.sleep(zeit_an)
    GPIO.output(lu, GPIO.LOW)

  • Tell Sich ein `SystemRandom`-Objekt selbst zu erstellen ist ungewöhnlich und unnötig.

    Ich weiss es ist nur ein Beispiel, aber `vars` ist der Name einer eingebauten Funktion, die sollte man eher nicht verdecken.

    Der Algorithmus selbst ist nicht so wirklich schön mit der Codewiederholung und der Laufzeit die von der Anzahl der Werte die 1 bzw. nicht 1 sind abhängt, weil das die Wahrscheinlichkeit beeinflusst wie oft die Schleife durchlaufen werden muss.

    Wähle eine zufällige Nummer einer ”Variablen” die den Wert 1 hat aus, würde ich IMHO verständlicher so ausdrücken:

    Python
    #!/usr/bin/env python3
    import random
    
    values = [0, 1, 1, 0]
    
    random_number = random.choice(
        [number for number, value in enumerate(values, 1) if value == 1]
    )
    
    print(random_number)

    Da wir mittlerweile durch den Quelltext von Janic wissen, dass es sich nicht wirklich um beliebige Zahlen, sondern eigentlich um Wahrheitswerte handelt und das die Nummerierung gar nicht bei 1 anfangen muss (oder überhaupt sollte), lässt sich das noch ein bisschen vereinfachen auf:

    Python
    #!/usr/bin/env python3
    import random
    
    flags = [False, True, True, False]
    
    random_flag_index = random.choice(
        [index for index, flag in enumerate(flags) if flag]
    )
    
    print(random_flag_index)

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

  • Janic Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` fast 200 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. Zumal Du ja *auch* schon den üblichen Import unter dem Namen `tk` verwendest, nur halt nicht für alles aus dem Modul.

    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.

    ``as`` beim importieren ist zum umbenennen gedacht, `GPIO` wird aber gar nicht umbenannt. Auf längere Sicht würde ich auch das `gpiozero`-Modul vorziehen. Das hat die bessere, modernere API.

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

    Das bedeutet das Funktionen (und Methoden) alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen. `start()` braucht also die ”Variablen” und Eingabefeldwerte als Argumente. Das kann man hier noch mit `functools.partial()` lösen, aber letztlich wird man für jede nicht-triviale GUI objektorientierte Programmierung (OOP), also mindestens eine eigene Klasse benötigen.

    Warnungen sind dazu da das man die Ursache beseitigt, nicht dass man sie ignoriert. Wenn man dafür sorgt, dass das Programm am Ende hinter sich aufräumt, also die `GPIO.cleanup()`-Funktion aufräumt, braucht man die Warnungen die durch das nicht-aufräumen entstehen auch nicht ignorieren.

    Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase). `GUI` ist keine Konstante, sollte also nicht so geschrieben werden als wäre es eine.

    Namen sollten keine kryptischen Abkürzungen enthalten, oder gar nur daraus bestehen, und nicht supergenerisch sein, so dass der Leser nicht weiss was der Wert dahinter eigentlich bedeutet, oder der Autor die Bedeutung in einem Kommentar hinter den Namen schreiben muss.

    Namen werden auch nicht nummeriert. Dann will man entweder passendere Namen wählen oder gar keine einzelnen Werte, sondern eine Datenstruktur. Oft eine Liste. Und man muss auch nicht jedes Zwischenergebnis an einen Namen binden, dann muss man sich weniger gute Namen ausdenken, die am Ende nicht wirklich gebraucht werden.

    Die vier Variablen sind im Grunde Wahrheitswerte, also `tk.BooleanVar` statt `tk.IntVar`. Die beiden Eingabefelder verwenden `tk.StringVar`, die Werte daraus werden dann aber als Zahlen verwendet, was zu einem Fehler führen wird, denn `time.sleep()` kann mit Zeichenketten nix anfangen.

    Statt den vorgegebenen Wert hinterher in das `Entry` einzufügen, wäre es einfacher den Wert direkt beim Erstellen des `tk.*Var`-Objekts anzugeben.

    Bei vielen Widgets hast Du beim erstellen vergessen das Elternwidget zu übergeben. Dass dafür dann als Rückfallwert das Hauptfenster genommen wird ist zwar praktisch, macht aber ein verschieben von Elementen in andere Containerwidgets unnötig aufwendig. Man sollte das Elternwidget immer explizit angeben.

    Konstanten werden am Anfang des Quelltextes definiert und das Aufsetzen der Pins sollte nur einmal am Programmanfang erfolgen und nicht jedes mal wenn der Startknopf gedrückt wurde.

    Der Wert von dem vor der ``while``-Schleife definierten `bewegungsstatus` wird überhaupt nicht verwendet. Eigentlich wird diese Variable gar nicht wirklich verwendet sondern ist einfach nur ein Zwischenergebnis das unnötigerweise einen Namen bekommt.

    Da wird auf den Bewegungssensor in einer Schleife gewartet die 100% CPU-Zeit verbrät. Das geht mit `GPIO.wait_for_edge()` effizienter. Wobei das die Semantik etwas ändert, da müsste man schauen was hier beabsichtigt ist.

    Statt eine Nummer beginnend ab 1 zu wählen, würde man besser einen Index ab 0 wählen und das dann nicht über so viel wiederholenden Code und ``if``/``elif``\s lösen, sondern den Index für einen Zugriff in eine Liste mit den Pin-Nummern verwenden.

    Die Behandlung der Kontrollleuchte erscheint ”asymmetrisch” zu sein, weil die LOW startet, beim Bewegungsereignis dann noch mal auf LOW und am Ende auf HIGH gesetzt wird‽ Die verhält sich also beim ersten Ereignis anders als bei den folgenden Ereignissen. Ist das so gewollt?

    Hier mal der überarbeitete Quelltext, ungetestet, und immer noch mit dem Problem das GUIs und lang laufende Schleifen sich überhaupt nicht vertragen:

    Um das Problem zu beheben muss man das objektorientert umbauen, denn die Zustände die hier entstehen sind zu komplex als dass sich das noch sinnvoll und am Ende halbwegs verständlich mit `functools.partial()` lösen liesse. Denn man muss ja nicht nur den Startknopf berücksichtigen, sondern auch während die ”Leuchtsequenz” läuft, darf auch kein neues Bewegungsereigniss verarbeitet werden.

    Zumal man für das effiziente Warten auf den Bewegungsmelder wohl auch zusätzlich noch ein Thread und eine `queue.Queue` ins Spiel kommen müssten.

    Die Beschreibung im Beitrag finde ich auch nicht wirklich eindeutig, denn der Schritt 5 mit dem Sprung zu Schritt 3 findet sich im Code nicht wieder, dafür wird der Bewegungssensor in der Beschreibung überhaupt gar nicht berücksichtigt. Also was soll der Button tatsächlich auslösen? Den Start einer “Endlosschleife“? Das heisst der Button wird eigentlich nach dem drücken nie wieder aktiv? Oder soll der nach erkannter Bewegung und aAblauf der GPIO-Ausgabesequenz wieder aktiv werden und die ”Endlossschleife” nicht mehr aktiv sein? Falls der Button inaktiv bleiben soll, wie ist das mit den beiden Werten aus den Eingabefeldern: müssen die dann auch deaktiviert werden, oder soll es möglich sein diese Werte im ”laufenden Betrieb” zu ändern?

    Es fehlt auch eine Behandlung/Reaktion für den Fall, dass die eingegebenen Zeitwerte sich nicht als Zahlen interpretieren lassen oder negativ sind. Und eventuell möchte man sich auch gegen die Scherzkekse wappnen, die dort "Inf" oder "NaN" eintragen.

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

  • Janic

    Hier hast du schon gefragt was man besser machen kann, ohne das du deinen Code vorher getestet hast.

    Mit dem dir gezeigten Code hast du dich offenbar nicht beschäftigt und fängst jetzt gleich mit tkinter an.

    Mach doch erstmal eins richtig fertig.

  • Janic

    Hier hast du schon gefragt was man besser machen kann, ohne das du deinen Code vorher getestet hast.

    Mit dem dir gezeigten Code hast du dich offenbar nicht beschäftigt und fängst jetzt gleich mit tkinter an.

    Mach doch erstmal eins richtig fertig.

    Hatte den Code zum laufen gebracht (ohne GUI) und getestet, hat funktioniert. Jetzt wollte ich eben noch eine GUI damit das bedienen einfacher ist. Also ich hab schon eins nach dem anderen gemacht. Bloß Gpio zero hab ich noch nicht weil ich da erst die neuen befehle nachschauen muss usw

  • __blackjack__

    Erstmal vielen Dank.

    Statt eine Nummer beginnend ab 1 zu wählen, würde man besser einen Index ab 0 wählen und das dann nicht über so viel wiederholenden Code und ``if``/``elif``\s lösen, sondern den Index für einen Zugriff in eine Liste mit den Pin-Nummern verwenden.

    Also hier verstehe ich nicht ganz was ich da machen soll. mit Index und so.

    Um das Problem zu beheben muss man das objektorientert umbauen, denn die Zustände die hier entstehen sind zu komplex als dass sich das noch sinnvoll und am Ende halbwegs verständlich mit `functools.partial()` lösen liesse. Denn man muss ja nicht nur den Startknopf berücksichtigen, sondern auch während die ”Leuchtsequenz” läuft, darf auch kein neues Bewegungsereigniss verarbeitet werden.


    Zumal man für das effiziente Warten auf den Bewegungsmelder wohl auch zusätzlich noch ein Thread und eine `queue.Queue` ins Spiel kommen müssten.

    Das ist nochmal was wo ich nicht verstehe was ich tun soll

    Die Beschreibung im Beitrag finde ich auch nicht wirklich eindeutig, denn der Schritt 5 mit dem Sprung zu Schritt 3 findet sich im Code nicht wieder, dafür wird der Bewegungssensor in der Beschreibung überhaupt gar nicht berücksichtigt. Also was soll der Button tatsächlich auslösen? Den Start einer “Endlosschleife“? Das heisst der Button wird eigentlich nach dem drücken nie wieder aktiv? Oder soll der nach erkannter Bewegung und aAblauf der GPIO-Ausgabesequenz wieder aktiv werden und die ”Endlossschleife” nicht mehr aktiv sein? Falls der Button inaktiv bleiben soll, wie ist das mit den beiden Werten aus den Eingabefeldern: müssen die dann auch deaktiviert werden, oder soll es möglich sein diese Werte im ”laufenden Betrieb” zu ändern?

    Also ich habe ja die GUI und lege da die Werte fest, danach soll sich die endlosschleife starten die immer wieder prüft ob grad jemand durch den Bewegungsmelder läuft, und dann die entsprechende Lampe leuchten lässt. super wäre, ich wenn ich in der GUI während das Programm läuft die Werte ändern könnte.

  • Hallo,

    Also hier verstehe ich nicht ganz was ich da machen soll. mit Index und so.

    Schau mal, ich habe im ersten Abschnitt 3 nummerierte Namen erstellt und an alle ist ein Text gebunden. Das macht man so aber nicht, wie __blackjack__ erklärt hat. Sondern man erstellt zum Beispiel eine Liste und greift auf den Inhalt mit dem Index (die eckige Klammer) der Liste zu. Der Index beginnt immer bei 0.

    Das ist nochmal was wo ich nicht verstehe was ich tun soll

    Kannst du Objekt orientiert programmieren? Also kannst du mit Klassen umgehen? Wenn nicht, dann solltest du das als erstes lernen. Das offizielle Python-Tutorial kann hier helfen.

    Soweit ich dein Problem verstehe, ist das notwendig um deine Wünsche erfüllen zu können.

    Grüße

    Dennis

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

  • Hab mal etwas experimentiert, müsste doch so gehen, oder ? (hatte mal zumindest keine Fehlermeldung) Es fehlt eben noch das GPIO setup und die umstellung auf gpio zero aber sonst ?

  • Das geht so nicht. Das faengt schon mal damit an, dass du Variablen nicht durchnummerieren solltest. Entweder haben die einen sinnvollen Namen - also zB einschalt_wartezeit, auschalt_wartezeit, etc - oder du willst gleich eine Liste von Objekten haben. Und das *machst* du ja sogar: ECKEN ist eine Liste. Falsch benannt, sollte kleingeschrieben sein, aber trotzdem.

    Und dann ballerst du dir mit deinem

    var1 = var1.get()

    gleich schoen den Namen der tkinter-Variable weg, und hast nur noch den eigentlichen Wert. Der damit nie wieder durch die GUI beeinflussbar ist.


    Dann hast du eine while-Schleife, und das geht auch nicht. Dann blockiert die GUI. Du brauchst also entweder after, oder einen Thread im Hintergrund.

    Salopp: das ist alles Murks.

    Du hast das aber auch schon von BlackJack richtig vorgemacht bekommen. Warum hast du darauf nicht aufgebaut?

Jetzt mitmachen!

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