Eine Art RS-FlipFlop

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
CrytoChris
User
Beiträge: 10
Registriert: Montag 24. August 2015, 09:19

Hallo, ich habe eine bestimmt einfach zu beantwortende Frage, komme aber einfach nicht drauf:

Ich möchte in Python eine wechselnde Anzeige haben. Unter der eine Art Ladebalken läuft. Sobald der Balken am Ende ist, soll die Anzeige wechseln. Wenn er dann wieder am Ende ist, soll die Anzeige wieder das erste Anzeigen.
Quasi:

Code: Alles auswählen

If Balkenlänge < max
    Print Text 1
If Balkenlänge war mal max und ist wieder auf dem Weg dahin
    Print Text 2
If Balkenlänge hat wieder max erreicht
    Print Text 1
Usw...

Ich hoffe man verstehg das Problem ;-)

Danke

Chris
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@CrytoChris: Und wo ist Dein konkretes Problem? Du hast ja schon verschiedene if-Abfragen. Du brauchst nur noch ein Flag, das sagt, ob Du schoneinmal bei max warst, oder noch nicht.
CrytoChris
User
Beiträge: 10
Registriert: Montag 24. August 2015, 09:19

Ich glaub genau am flag.

das Ganze läuft in einer While-Schleife. Ich bekomme gerade nicht hin, wie ich das flag auch innerhalb der Schleife gesetzt lasse.

Code: Alles auswählen

flag =0
While True:
    draw.rectangle(höhe, breite) 
    breite += 5
    if breite >= max_breite:        
        print ("Text 1")
        breite = 0
        ?? flag = 1  ??
    if breite <= max_breite and flag == 1:
        print ("Text 2")
       ?? flag = 0 ??
Mein Problem ist, dass ich nicht genau weiss, wann ich das Flag zurücksetze. Denn in dem Code oben würde Text 2 genau für einen Durchlauf der Schleife angezeigt werden, statt eine ganze "Balkenlänge" lang.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@CrytoChris: wenn ich Dich richtig verstanden habe, sollte das doch das sein, was Du möchtest:

Code: Alles auswählen

flag =0
while True:
    draw.rectangle(höhe, breite) 
    breite += 5
    if breite >= max_breite:        
        print ("Text 1")
        breite = 0
        flag = 1
    else:
        if flag == 1:
            print ("Text 2")
        else:
            print ("Text 1")
CrytoChris
User
Beiträge: 10
Registriert: Montag 24. August 2015, 09:19

Hi Siruis,
danke, aber das ist auch nur die halbe Lösung.

Text 1 und Text 2 sollen immer im Wechsel angezeigt werden.

In deinem Code klappt der Wechsel nur 1x, danach wird Text 1 wieder nur für einen Durchlauf der Schleife angezeigt.

Vielleicht kann ich es auch nicht gut genug erklären ;-)

Im Prinzip will ich Text 1 und Text 2 alle 5 Sekunden wechseln lassen. Endlos lang.
Jedoch soll der Trigger zum Wechsel des Textes keine Zeit sein, sondern das Erreichen der max_breite des sich erweiternden Ladebalkens.
BlackJack

@CrytoChris: Eine Liste mit beiden Texten und `itertools.cycle()` könnten Teil der Lösung sein. Immer wenn der Balken voll ist lässt Du Dir mit der `next()`-Funktion den ”nächsten” Text geben für die Ausgabe.
Zizibee
User
Beiträge: 229
Registriert: Donnerstag 12. April 2007, 08:36

Oder so etwas in der Art?

Code: Alles auswählen

flag = False
while True:
    draw.rectangle(höhe, breite)
    breite += 5
    if breite >= max_breite:
        breite = 0
        flag = not flag

    if flag:
        print ("Text 1")
    else:
        print ("Text 2")
CrytoChris
User
Beiträge: 10
Registriert: Montag 24. August 2015, 09:19

Hey,

@Zizibee:
Das war es. Danke
Ich vermute, es fehlte mir an dem Wissen, dass es so etwas wie "not" gibt.
"not" negiert, oder? Ein voriges True, wird False und umgekehrt?

Das erklärt auch, warum ich schon beim Betreff überlegen musste, was ich genau suche und es versuchte mit dem alten Set/Reset-FlipFlop der SPS-Programmierung in der Ausbildung zu beschreiben ;-)
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
"not" negiert, oder? Ein voriges True, wird False und umgekehrt?
Genau, `not` negiert den folgenden Ausdruck.

Gruß, noisefloor
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Code: Alles auswählen

texts = ("Text 1", "Text 2")
index = 0
while True:
    draw.rectangle(hoehe, breite)
    breite += 5
    if breite >= max_breite:
        breite = 0
        index ^= 1
    print(texts[index])
BlackJack

@Alfons Mittelmeyer: Ein Flag durch eine Variable zu ersetzen, welche die Zahlen 0 oder 1 annehmen kann, zu verwenden um ein Flag loszuwerden ist ein kleines bisschen sinnlos. Zumal das ``not`` noch einfach lesbar und verständlich ist, während die Verwendung Exlusiv-Oder-Bitverknüpfung in Hochsprachen eher ein Exot ist der eigentlich nur bei Bitfummeleien in Binärdaten zum Einsatz kommt. Und das ist eher selten.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@CrytoChris: auch wenn Alfons hier oft fragwürdigen Code postet (bisweilen hat es auch den Anschein, dies sei Absicht), so ist die Idee auf ein Flag zu verzichten eine Variante, die je nach Einsatzzweck Vorteile haben kann. Zum Beispiel dann, wenn mehr als ein Zustand unterschieden werden soll. Hier ein Beispiel, wie es ohne Flag geht:

Code: Alles auswählen

import time

texts = ('text 1', 'text 2')
text_index = 0
print(texts[text_index])
width = 0
max_width = 50
width_increment = 5

while True:
    # statt draw.rectangle hier ersatzweise time.sleep:
    time.sleep(0.05)
    width += width_increment
    if width >= max_width:
        text_index = (text_index + 1) % 2
        print(texts[text_index])
        width = 0
Und wie so oft bietet die Standard Library für solche Zwecke bereits fertige Lösungen:

Code: Alles auswählen

import time
from itertools import cycle

texts = ('text 1', 'text 2')
text = cycle(texts)
print(next(text))
width = 0
max_width = 50
width_increment = 5

while True:
    # statt draw.rectangle hier ersatzweise time.sleep:
    time.sleep(0.05)
    width += width_increment
    if width >= max_width:
        print(next(text))
        width = 0
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@kbr: sorry, wenn man zB. etwas blinken lassen will und dann einen Pin von 0 auf 1 oder von 1 auf 0 umschalten will, dann macht man das sicher nicht mit:

pin_status = (pin_status + 1) % 2

So etwas ist eine grauenhafte Zeile, welche von solchen geschrieben wird, für die Bits und Bytes etwas ganz Exotisches sind

Der grebräuchliche Ausdruck ist hierfür eben:

pin_status ^= 1

auch wenn das für solche, die nichts mit Hardwareregistern und hardwarenaher Programmierung zu tun haben, spanisch vorkommt. Meiner Meinung nach sollte man mit den Operatoren und dabei auch mit den bitweisen Operaturen vertraut sein. Diese Operatoren gehören zu ziemlich jeder Programmiersprache und sollten Grundwissen sein.

Recht hast Du, dass der Index einen Vorteil bringt gegenüber dem Flag, was BlackJack anscheinend nicht gesehen hatte, denn:

Code: Alles auswählen

print(texts[index])
ist sicher schöner als so eine Flag Abfrage Logik:

Code: Alles auswählen

if flag:
        print ("Text 1")
    else:
        print ("Text 2")
Bei Flags sollte man überlegen, ob man sie wirklich braucht, oder nicht besser durch etwas ersetzen kann, das den Zustand bereits enthält und so die Logik der Flagauswertung überflüssig macht.

Wenn man nicht mal einen Index braucht, wie Deine Lösung :

print(next(text))

Ist natürlich noch schöner
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Alfons Mittelmeyer: sorry, aber wenn ich hardwarenah programmieren möchte, nehme ich C. Und wenn mir Bits und Bytes nicht exotisch genug sind, wähle ich auch die Elektronen einzeln aus. Und statt Pins nehme ich lieber Spins, dann habe ich auch mehr Zustände als nur 0 und 1.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@kbr: sorry, es geht hier nicht darum, was Du machst und ob Du dafür C oder C++ nehmen würdest, da würde man das Toggeln nämlich auch mit "^=" machen.

Es geht hier auch um andere Mitglieder oder Leser dieses Forums. Und in diesem Forum gibt es auch das Programmierforum "Raspberry Pi und Co". Und beim Raspberry sind besonders die GPIO Pins interessant, über welche in diesem Programmierforum LEDs, Motoren, Sensoren und dergleichen angesteuert werden. Und das in Python. Das ist also gar nichts Exotisches, und kein Mensch außer Dir beschwert sich, dass man auch in Python die GPIO Pins ansteuert.

Und sicher sollte man zum Toggeln das verwenden:

pin_status ^= 1

Und nicht so etwas grauenhaftes wie:

pin_status = (pin_status + 1) % 2

Ach so, was man noch kennen sollte, wenn man flags verwedet. Statt einer Flag Programmlogik eine flagabhängige Ausgabe verwenden, das wäre etwa:

print("Text 1" if flag else "Text 2")

Hier sieht man gleich, dass es ein "print" ist und hat keine vier Zeilen mit if else Programmlogik, die etwas ablenken würden.
BlackJack

@Alfons Mittelmeyer: Der in Python gebräuchliche Ausdruck etwas blinken zu lassen ist ``pin_status = not pin_status``, weil das ein Wahreitswert ist und die üblichen Python-APIs für GPIOs mit `True` und `False` klar kommen. Das mit dem ``^``-Operator ist aus Sprachen die in Maschinensprache übersetzt werden, wo man näher an der Hardware ist und auch eher in Zahlen mit einer bestimmten Anzahl Bits denkt und auf diesen Bits dann operiert und wo die boole'sche Negation bei einigen Sprachen auch mehr macht als nur das unterste Bit zu togglen. Sofern es überhaupt eine boole'sche Negation gibt. Oft haben solche Sprachen nicht einmal einen dedizierten Datentyp für Wahreitswerte.

Das ``not`` muss man sowieso kennen, weil die Negation von Wahreitswerten auch in nicht-hardwarenahen Pythonprogrammen regelmässig zur Anwendung kommt.

Mit `flag` hätte man übrigens auch ``print(texts[flag])`` schreiben können, ohne ``if``/``else``.

Eigentlich muss man sich damit auch gar nicht auseinandersetzen wenn man nicht will und nimmt einfach eine Bibliothek wie `gpiozero` und schreibt: ``led.toggle()``, oder wenn es wirklich kontinuierlich blinken soll ``led.blink()``.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack: ja es gibt Vieles, das in Python geht und wo es mich graust, etwa:

print(1 + True)

Aber überzeugen kannst Du mich nicht, dass man so etwas tun sollte. In vielen Programmiersprachen geht so etwas überhaupt nicht wegen Typinkompatibilität und in manchen Programmiersprachen ist True auch 0xFFFFFFFFFFFFFFFF, also 4294967295 oder -1, bei Vorzeichenbehaftung. Da würde der Index ganz schön daneben gehen.

Und ich empfinde so etwas:

value = (value +1) % 2

genauso häßlich wie so etwas:

value = ~value + 2

wenn es um togglen zwischen 0 und 1 geht

schöner wäre da noch:

value = 1 - value
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Alfons Mittelmeyer: es ging bei dem ursprünglichen Beispiel mit sinngemäß

value = (value +1) % 2

zwar um das toggeln zwischen zwei Texten, der mögliche Vorteil dieser Variante war bei der Erklärung aber daran geknüpft, dass im Gegensatz zu einem Flag auch mehr als zwei Zustände auf diese Weise behandelt werden können, so dass sich der allgemeine Fall von

text_index = (text_index +1) % len(texts)

ergibt. Diese Abstrahierung ergibt eine flexible und elegante Lösung. Bei nur zwei Zuständen ist die Negierung des Zustands eines boolschen Flags der pythonische Weg. Bitfummelei a la C ist in Python zumeist ein Zeichen für einen schlechten Lösungsansatz.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@kbr: value = 1 - value ist keine Bitfummelei. Sorry dass ich Bits nicht als Fummelei verstehe, sondern so etwas als elegant empfinde: value ^= 1

Bitfummelei wäre, wenn jemand unnötiger Weise eine Funktion schriebe, mit der man zwei Integerwerte miteinander multiplizieren kann, etwa:

Code: Alles auswählen

def multiply_integer(a,b):

    c = 0
    for i in range(32):
        c <<= 1
        if b & 0x80000000:
            c += a
        b <<= 1
    return c

# Test
print(multiply_integer(12,8))
Es gibt durchaus Systeme, für die man eine solche Funktion schreiben muss, ist aber für Python unangebrachtt.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@CrytoChris: Brechen wir an dieser Stelle die sinnlose Diskussion ab, mit welchem Ausdruck man am Besten einen Index umstellt und widmen wir uns der Frage, welche Möglichkeiten wir haben, und welche am Besten in Frage kommt.

Die einfachste Möglichkeit ist das Flag:

Code: Alles auswählen

if flag:
    print ("Text 2")
else:
    print ("Text 1")

# oder

print("Text 2" if flag else "Text 1")
Wenn es um eine einfache Sache geht, ist nichts einzuwenden. Doch wenn das Flag an mehreren Stellen abzufragen ist, wenn nicht nur der Text, sondern auch anderes geändert behandelt werden soll, und wenn es auch noch andere Flags gibt, dann wird der Code leicht ein unübersichtlicher Verhau mit jeder Menge von if else Abfragen auf Flags, die evtl. dann auch noch tief verschachtelt sind.

Eine bessere Möglichkeit, wäre dann eine Verwendung eines Indexes, weil dann die if else Logik überflüssig wird:

Code: Alles auswählen

print(text[index])
Noch besser ist es, auch auf den Index zu verzichten, sondern gleich den geänderten Text zu haben:

Code: Alles auswählen

print(my_text)
Wenn sich nicht nur der Text ändern soll, sondern auch noch Anderes, empfiehlt sich, ein geändertes Objekt zu verwenden:

Code: Alles auswählen

print(my_object.text)
Beispiel für print(my_text):

Code: Alles auswählen

import time
max_breite = 10
breite = 0

my_text = "Text 1"
while True:
    #draw.rectangle(hoehe, breite)
    time.sleep(1)
    breite += 5
    if breite >= max_breite:        
        my_text = "Text 1" if my_text == "Text 2" else "Text 2"
        breite = 0

    print(my_text)
Beispiel für print(my_object.text):

Code: Alles auswählen

import time

class My_Object():
    def __init__(self,next_object = None,text=''):

        self.next_object = self if next_object == None else next_object
        self.text = text
        # evtl. weitere Eigenschaften

my_object2 = My_Object(None,"Text 2")
my_object1 = My_Object(my_object2,"Text 1")
my_object2.next_object = my_object1

max_breite = 10
breite = 0

my_object = my_object1
while True:
    #draw.rectangle(hoehe, breite)
    time.sleep(1)
    breite += 5
    if breite >= max_breite:        
        my_object = my_object.next_object
        breite = 0

    print(my_object.text)
Antworten