Delay mit anderen funktionen

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.
Antworten
Nami
User
Beiträge: 1
Registriert: Mittwoch 5. Juli 2023, 22:29

Hallo, ich hoffe ihr könnt mir helfen :)

Ein Schaltvorgang soll nach Auslösung durch einen Taster um eine fest implementierte Zeit verzögert werden. Die Dauer des Tastendruckes ist egal.
Nach Ablauf der Einschaltverzögerung führt ein erneuter Tastendruck zum Rücksetzen des Schaltzustandes (Ausschalten). Danach kann der Einschaltvorgang wiederholt werden.

Erweiterung:
Nach dem Start der Software soll die Verzögerungszeit durch wiederholtes Drücken des Tasters in drei Stufen einstellbar sein. Voraussetzung dafür ist, dass zur Einstellung der Stufe die Folge der Tastenbestätigungen in einem vorgegebenen (festen) Einstellungsintervall erfolgt. Es kann also durch aufeinander folgendes drücken die Zeitstufe erhöht und wieder gesenkt werden. Jede Stufe wird durch ein LED angezeigt.
Wird die Zeit des Einstellungsintervalls überschritten, ist die Verzögerungszeit für diesen Schaltvorgang gesetzt. Jeder weitere Tastendruck löst dann den Schaltvorgang aus.
Während der Schaltverzögerung ist jeder Tastendruck wirkungslos.


Wurde das gefragt, kann es aber nicht beantworten.
Ich danke euch allen schon einmal für eure Hilfe :)
Benutzeravatar
Dennis89
User
Beiträge: 1556
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

wo soll den mit der Hilfe angesetzt werden?
An welchem Punkt kommst du denn nicht weiter?



Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 14056
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ein Teil der Antwort wird objektorientierte Programmierung (OOP) sein, denn hier muss man sich ja Zustand über Aufrufe hinweg merken. Man muss beim Rückruf der durch den Taster ausgelöst wird, wissen ob man sich noch in der Einstellphase befindet, oder schon darüber hinaus ist. Im letzteren Fall muss man wissen ob gerade Ein- oder Ausgeschaltet ist, und ob man sich in der Schaltverzögerung befindet oder nicht.

Also wie sieht denn die Klasse bisher aus? Und durch welche Daten wird der Gesamtzustand des Systems eindeutig beschrieben?

Da man mit Zeitpunkten und Differenzen rechnen muss, noch der Hinweis: Nicht `time.time()` sondern `time.monotonic()` verwenden.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
__blackjack__
User
Beiträge: 14056
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ein weiteres Stichwort könnte noch „Zustandsautomat“ sein. Die kann man manchmal ganz gut mit einer Funktion pro Zustand umsetzen, welche die Aktion im jeweiligen Zustand als Code enthält. Hier mal eine Lösung für die Aufgabe als kleiner Simulator in HTML + JavaScript. Einfach im Browser öffnen und ausprobieren:

Code: Alles auswählen

<!DOCTYPE html>
<html lang="de">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Schalttest</title>
    <style type="text/css">
      body {
        font-size: 11pt;
        max-width: 50em;
        margin: auto;
        padding-top: 1em;
        hyphens: auto;
      }
      .device {
        background: lightgray;
        border: medium outset lightgray;
        border-radius: 1em;
        width: fit-content;
        padding: 1em;
      }
      .led {
        font-size: larger;
      }
    </style>
  </head>
  <body>
    <h1>Gerätesimulation</h1>
    
    <p>Das simulierte Gerät ist erst in einer Phase in der man mit dem Taster
    die Schaltverzögerung einstellen kann.  Danach startet ein Tastendruck die
    Verzögerung nach der das Gerät eingeschaltet wird.  Während dieser
    Verzögerung hat der Taster keine Wirkung.  Wenn das Gerät eingeschaltet ist,
    bewirkt der Taster die sofortige Abschaltung.</p>
    
    <div class="device">
      <p>Schaltzustand: <span id="device_state"></span></p>
      <p>Schaltverzögerungsstufe: <span id="leds"></span></p>
      <p><button id="button">Taster</button></p>
    </div>
    <p>Status: <span id="state_description"></span></p>
    <p>
      <button id="restart">Neustart</button>
      (man kann auch einfach die Seite neu laden)
    </p>
    
    <script type="text/javascript">
      const LED_OFF_COLOR = 'gray';
      const LED_ON_COLOR = 'lightgreen';
      const SET_DELAY_DURATION = 3;  // in seconds.
      const DELAY_VALUES = [1, 2, 3];  // in seconds.
      
      function makeSwitchLed(parentElement) {
          const span = document.createElement('span');
          span.className = 'led';
          span.innerText = '●';
          parentElement.appendChild(span);
          const switchLed = function (state) {
            span.style.color = (state) ? LED_ON_COLOR : LED_OFF_COLOR;
          };
          switchLed(false);
          return switchLed;
      }
      
      function makeSetStripLevel(element, count) {
        const switchLed = Array.from(
          {length: count}, () => makeSwitchLed(element)
        );
        return function (value) {
          switchLed.forEach((switchLed, i) => switchLed(i < value));
        };
      }
      
      class Device {
        constructor(
          stateElement,
          ledsElement,
          buttonElement,
          statusTextElement,
          resetButtonElement,
          delayValues
        ) {
          this._isOn = null;
          this.state = null;
          this.delayValues = delayValues;
          this.delayIndex = null;
          this.timeoutID = null;
          
          this.switchStateLed = makeSwitchLed(stateElement);
          this.setStripLevel = makeSetStripLevel(
            ledsElement, delayValues.length
          );
          this.statusTextElement = statusTextElement;
          buttonElement.addEventListener('click', this.onButton.bind(this));
          resetButtonElement.addEventListener('click', this.restart.bind(this));
          this.restart();
        }
        
        get delay() {
          return this.delayValues[this.delayIndex];
        }
        
        set statusText(text) {
          this.statusTextElement.innerText = text;
        }
        
        get isOn() {
          return this._isOn;
        }
        
        set isOn(state) {
          this._isOn = state;
          this.statusText = `Gerät ${(this._isOn) ? 'Ein' : 'Aus'}geschaltet.`;
          this.updateUi();
        }
        
        updateUi() {
          this.switchStateLed(this.isOn);
          this.setStripLevel(this.delayIndex + 1);
        }

        restart() {
          if (this.timeoutID !== null) {
            clearTimeout(this.timeoutID);
          }
          this._isOn = false;
          this.state = this.setDelayState;
          this.delayIndex = 0;
          this.updateUi();
          this.statusText = `Phase 1: Schaltverzögerung einstellen (${SET_DELAY_DURATION} Sekunden).`;
          setTimeout(
            () => {
              this.timeoutID = null;
              this.state = this.deviceIsOffState;
              this.statusText = 'Warte auf Tastendruck.';
            },
            SET_DELAY_DURATION * 1000
          );
        }
        
        setDelayState() {
          this.delayIndex = (this.delayIndex + 1) % this.delayValues.length;
        }
        
        deviceIsOffState() {
          this.state = this.delayState;
          this.timeoutID = setTimeout(
            () => {
              this.timeoutID = null;
              this.isOn = true;
              this.state = this.deviceIsOnState;
            },
            this.delay * 1000
          );
          this.statusText = `Schaltvorgang in ${this.delay} Sekunden.`;
        }
        
        delayState() {}
        
        deviceIsOnState() {
          this.isOn = false;
          this.state = this.deviceIsOffState;
        }
        
        onButton() {
          this.state();
          this.updateUi();
        }
      }
      
      new Device(
        document.getElementById('device_state'),
        document.getElementById('leds'),
        document.getElementById('button'),
        document.getElementById('state_description'),
        document.getElementById('restart'),
        DELAY_VALUES
      );
    </script>
  </body>
</html>
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
Dennis89
User
Beiträge: 1556
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

ich habe das gerade mal getestet, das es funktioniert brauche ich eigentlich nicht extra schreiben, aber wenn ich mich innerhalb von 3 Sekunden nicht für eine Verzögerung entscheiden kann, dann ist die Verzögerung gesetzt, die nach Ablauf der 3 Sekunden gewählt war. Ich würde es cool finden, dass wenn 3 Sekunden lang die Taste nicht mehr gedrückt wurde, dass dann der Zustand gesetzt wird. (Egal ob man das hier braucht oder nicht) Das war für mich eigentlich mal ein Grund, den Code anzuschauen und da stellte sich mir gleich die erste Frage. Wo startet so ein JavaScript-Code denn eigentlich? Sehe ich dass richtig, dass das mit "new Device(..." los geht?
Da wird die Klasse erstellt und dann auch relativ schnell die erste Funktion "makeSwitchLed" aufgerufen und so kann ich den Code dann durchgehen?

Sorry dass das von Python abweicht, aber so ein klein bisschen ein Durchblick in den Code würde mich schon interessieren.

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
snafu
User
Beiträge: 6871
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Dennis89 hat geschrieben: Samstag 8. Juli 2023, 13:34 Wo startet so ein JavaScript-Code denn eigentlich?
So gesehen natürlich in der ersten Codezeile, aber das meintest du offenbar nicht. ;)
Dennis89 hat geschrieben: Samstag 8. Juli 2023, 13:34Sehe ich dass richtig, dass das mit "new Device(..." los geht?
Es ist nach den Definitionen der erste ausführbare Code. Und innerhalb der Device()-Instanz werden weitere Dinge veranlasst. Somit liegst du richtig: Das ist der Einstiegspunkt für den Programmfluss.
Dennis89 hat geschrieben: Samstag 8. Juli 2023, 13:34Sorry dass das von Python abweicht, aber so ein klein bisschen ein Durchblick in den Code würde mich schon interessieren.
Ich sehe im Laufzeitverhalten keinen wesentlichen Unterschied zu Python. BJ hat den Code halt nur anders strukturiert als er das typischerweise bei Python-Skripten tut.
Benutzeravatar
__blackjack__
User
Beiträge: 14056
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Dennis89: Wie snafu schon schrieb ist das eigentlich wie bei Python: der Code wird von oben nach unten ausgeführt wie er da im <script>-Element steht. Funktions- und Klassendefinitionen sind auch in JavaScript zur Laufzeit ausgeführter Code.

Ich hätte das in Python auch sehr ähnlich strukturiert, da weiss ich nicht was snafu meint. Man hat kein ``if __name__ == …`` weil es ja kein Modul ist, wo man unterscheiden können muss zwischen Direktaufruf und Import. Wenn ich das `Device`-Objekt an einen Namen gebunden hätte, dann hätte man das in Python in eine Funktion gesteckt, da bin ich in JavaScript manchmal etwas grosszügiger mit globalen Variablen, weil man auf die dann zur Fehlersuche in der Console im Browser zugreifen kann, um sich das Objekt mal ”live” anzuschauen.

Für die gewünschte Änderung könnte man den `setDelayState()` der ja im Moment nur weiterschaltet, erweitern durch löschen des Timeouts und wieder auf 3 Sekunden setzen. Den Code dazu würde ich dann aus `restart()` in eine eigene Methode heraus ziehen, die von `restart()` und `setDelayState()` aufgerufen werden kann.

Wie gesagt, das könnte man nahezu 1:1 in Python (und Tkinter) umsetzen. Das HTML würde dann zu Code der die GUI aufsetzt. `setTimeout()` und `clearTimeout()` sind `after()` und `after_cancel()`.

Die beiden Funktionen/Closures am Anfang wären für Python vielleicht ein bisschen untypischer, und da würde man vielleicht dann eher mit zwei Klassen mit jeweils nur einer Methode neben der `__init__()` leben. In JavaScript sind Closures für so etwas deutlich verbreiteter, weill es ``class`` noch nicht so lange gibt, und man so etwas früher neben der Prototype-OOP auch viel einfach über Closures gelöst hat, wie das in funktionalen Programmiersprachen nicht unüblich ist.

In JavaScript sind anonyme Funktionen mehr verbreitet, weil man da im Gegensatz zum ``lambda`` in Python ganz normale Funktionskörper schreiben kann, die nicht nur aus einem einzelnen Ausdruck bestehen. In Python müsste man da dann beispielsweise lokale Funktionen mit ``def`` erstellen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
Dennis89
User
Beiträge: 1556
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

vielen Dank für die Erklärungen.
Leider ist die Umestzung noch nicht so erfolgreich.

"setDelayState" wird doch nur in "restart" aufgerufen? Den Code, der das Timeout auf 0 setzt, muss dann wo anders rein?
Die "onButton" - Funktion beinhaltet "This.state();", aber ich finde keine Funktion mit dem Namen, was wird denn da gemacht?
"updateUI" dagegen gibt es als Funktion.

Ich habe mir einen Editor von JetBrains heruntergeladen und kurz vor der ersten Verzweiflung und einem anderen Post, denn ich hier schon geschrieben hatte, hat mir der Editor dann gezeigt, das es globale Funktionen wie "clearTimeout" gibt. Ich habe mich schon verrückt gesucht.

Wie testet ihr so einen Code? Wenn ich Änderungen mache, dann wird im Browser der "alte" Code ausgeführt. Ich muss jede Änderung in einer neuen Datei speichern und die dann aufrufen. Das ist etwas nervig. (Restart vom Webserver bringt auch nichts).

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
snafu
User
Beiträge: 6871
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Eine Suche nach ``this.state =`` könnte Licht ins Dunkle bringen. Man kommt dann auf insgesamt 6 Treffer.

Zum Programmieren gehört ein stückweit auch, dass man eine Suchmaschine bedienen kann. Sei es im Internet auf Google oder im Editor. Früher musste man noch vieles in der Doku der verwendeten Programmiersprache bzw. Bibliothek suchen. Inzwischen nimmt Google einem das meistens ab.
Benutzeravatar
__blackjack__
User
Beiträge: 14056
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Dennis89: `setDelayState()` wird *nur* in `onButton()` aufgerufen. In `restart()` wird `setDelayState()` *nicht* aufgerufen. Das ist in JavaScript genau wie in Python: Für den *Aufruf* muss man dort auch Klammern schreiben. Ohne bezieht sich der Name auf die Funktion/Methode selbst als Wert. Also ``this.state = setDelayState;`` weisst die Funktion zu, während ``this.state = setDelayState();`` die Funktion aufrufen und den Rückgabewert zuweisen würde.

Bezüglich Testen: Entwickelt habe ich das ohne Webserver, einfach direkt die lokale Datei geöffnet. Wenn von Server, dann mit geöffneten Webentwickler-Werkzeugen (F12 im Firefox), dann ist der Browsercache deaktiviert und es wird immer alles tatsächlich neu geladen, auch wenn es im Cache sein sollte.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
__blackjack__
User
Beiträge: 14056
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich war wohl ein bisschen zu sehr auf den Zustandsautomaten fixiert bei der Namenswahl. Hier ist im Grunde noch mal genau das gleiche, nur mit IMHO besseren Namen für die Methoden. Vielleicht ist das jetzt einfacher zu verstehen.

Code: Alles auswählen

<!DOCTYPE html>
<html lang="de">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Schalttest</title>
    <style type="text/css">
      body {
        font-size: 11pt;
        max-width: 50em;
        margin: auto;
        padding-top: 1em;
        hyphens: auto;
      }
      .device {
        background: lightgray;
        border: medium outset lightgray;
        border-radius: 1em;
        width: fit-content;
        padding: 1em;
      }
      .led {
        font-size: larger;
      }
    </style>
  </head>
  <body>
    <h1>Gerätesimulation</h1>
    
    <p>Das simulierte Gerät ist erst in einer Phase in der man mit dem Taster
    die Schaltverzögerung einstellen kann.  Danach startet ein Tastendruck die
    Verzögerung nach der das Gerät eingeschaltet wird.  Während dieser
    Verzögerung hat der Taster keine Wirkung.  Wenn das Gerät eingeschaltet ist,
    bewirkt der Taster die sofortige Abschaltung.</p>
    
    <div class="device">
      <p>Schaltzustand: <span id="device_state"></span></p>
      <p>Schaltverzögerungsstufe: <span id="leds"></span></p>
      <p><button id="button">Taster</button></p>
    </div>
    <p>Status: <span id="state_description"></span></p>
    <p>
      <button id="restart">(Neu)start</button>
      (man kann auch einfach die Seite neu laden)
    </p>
    
    <script type="text/javascript">
      const LED_OFF_COLOR = 'gray';
      const LED_ON_COLOR = 'lightgreen';
      const SET_DELAY_DURATION = 3;  // in seconds.
      const DELAY_VALUES = [1, 2, 3];  // in seconds.
      
      function makeSwitchLed(parentElement) {
          const span = document.createElement('span');
          span.className = 'led';
          span.innerText = '●';
          parentElement.appendChild(span);
          const switchLed = function (state) {
            span.style.color = (state) ? LED_ON_COLOR : LED_OFF_COLOR;
          };
          switchLed(false);
          return switchLed;
      }
      
      function makeSetStripLevel(element, count) {
        const switchLed = Array.from(
          {length: count}, () => makeSwitchLed(element)
        );
        return function (value) {
          switchLed.forEach((switchLed, i) => switchLed(i < value));
        };
      }
      
      class Device {
        constructor(
          stateElement,
          ledsElement,
          buttonElement,
          statusTextElement,
          resetButtonElement,
          delayValues
        ) {
          this._isOn = null;
          this.handleButtonClick = null;
          this.delayValues = delayValues;
          this.delayIndex = null;
          this.timeoutID = null;
          
          this.switchStateLed = makeSwitchLed(stateElement);
          this.setStripLevel = makeSetStripLevel(
            ledsElement, delayValues.length
          );
          this.statusTextElement = statusTextElement;
          buttonElement.addEventListener('click', this.onButton.bind(this));
          resetButtonElement.addEventListener('click', this.restart.bind(this));
          this.restart();
        }
        
        get delay() {
          return this.delayValues[this.delayIndex];
        }
        
        set statusText(text) {
          this.statusTextElement.innerText = text;
        }
        
        get isOn() {
          return this._isOn;
        }
        
        set isOn(state) {
          this._isOn = state;
          this.statusText = `Gerät ${(this._isOn) ? 'Ein' : 'Aus'}geschaltet.`;
          this.updateUi();
        }
        
        updateUi() {
          this.switchStateLed(this.isOn);
          this.setStripLevel(this.delayIndex + 1);
        }

        restart() {
          this._isOn = false;
          this.handleButtonClick = this.onButtonWhenSettingDelay;
          this.delayIndex = 0;
          this.statusText = `Phase 1: Schaltverzögerung einstellen (${SET_DELAY_DURATION} Sekunden).`;
          this.updateUi();
          if (this.timeoutID !== null) {
            clearTimeout(this.timeoutID);
          }
          setTimeout(
            () => {
              this.timeoutID = null;
              this.handleButtonClick = this.onButtonWhenDeviceIsOff;
              this.statusText = 'Warte auf Tastendruck.';
            },
            SET_DELAY_DURATION * 1000
          );
        }
        
        onButtonWhenSettingDelay() {
          this.delayIndex = (this.delayIndex + 1) % this.delayValues.length;
        }
        
        onButtonWhenDeviceIsOff() {
          this.handleButtonClick = this.onButtonWhileDelay;
          this.timeoutID = setTimeout(
            () => {
              this.timeoutID = null;
              this.isOn = true;
              this.handleButtonClick = this.onButtonWhenDeviceIsOn;
            },
            this.delay * 1000
          );
          this.statusText = `Schaltvorgang in ${this.delay} Sekunden.`;
        }
        
        onButtonWhileDelay() {}
        
        onButtonWhenDeviceIsOn() {
          this.isOn = false;
          this.handleButtonClick = this.onButtonWhenDeviceIsOff;
        }
        
        onButton() {
          this.handleButtonClick();
          this.updateUi();
        }
      }
      
      new Device(
        document.getElementById('device_state'),
        document.getElementById('leds'),
        document.getElementById('button'),
        document.getElementById('state_description'),
        document.getElementById('restart'),
        DELAY_VALUES
      );
    </script>
  </body>
</html>
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
Dennis89
User
Beiträge: 1556
Registriert: Freitag 11. Dezember 2020, 15:13

Edit: Der Beitrag entstand schon, da habe ich den neuen Code noch nicht gesehen. "handleButtonClick" ist auf jeden Fall verständlicher als "state"., wobei ich mit euren Erklärungen das (hoffentlich) dann verstanden habe. Danke für die Mühe, dass du den Code extra umgeschrieben hast. 🙂


Start des originalen Beitrags:
Danke für die weiteren Erklärungen. 🙂

Jetzt verstehe ich etwas mehr und habe entdeckt, am "Ende" der Klasse wird "restart" aufgerufen und da wird "setDelayState" an "state" gebunden und "state" wird dann mit dem Klick auf den Button aufgerufen.

"clearTimeout" bricht den eingestellten Timer ab und mit "setTimeout" kann ich einen neuen Timer definieren. Mit denen Infos habe ich mir die Änderung eigentlich recht einfach vorgestellt.
Wenn ich meinen Button anklicke, dann wird "setDelayState" aufgerufen und dann will ich erst mal den Timer abbrechen, dann meine LED's anders setzen und danach setze ich wieder einen Timer von 3 Sekunden. Bevor ich das in eine Funktion auslagere, habe ich einfach mal das was ich hier in Worte gefasst habe, so in "setDelayState" geschrieben und enttäuschend musste ich feststellen, dass das nichts änderte. Bzw. an dem Status-Text den ich eingefügt habe, sehe ich, dass der Timer der in "restart" gesetzt wurde nicht gelöscht wird. Denn nach einem Klick auf den Button erscheint erst "Warte auf Tastendruck" und dann 3 Sekunden später "Mein neuer Timer ist abgelaufen".
"this" habe ich so wie "self" in Python verstanden und dachte dass ich damit den Timer in der Klasse auch abbrechen kann.
Eigentlich kopiere ich ja nur einen Teil aus "reset" weil da genau das gemacht wird was ich brauche. Und deswegen sicherlich auch gleich der Lösungsweg, dass man eine Funktion schreibt, die in "restart" dann auch aufgerufen werden kann.

Code: Alles auswählen

        setDelayState() {
            clearTimeout(this.timeoutID);
            this.delayIndex = (this.delayIndex + 1) % this.delayValues.length;
            setTimeout(
                () => {
                    this.timeoutID = null;
                    this.state = this.deviceIsOffState;
                    this.statusText = "Mein neuer Timer ist abgelaufen"
                },
                SET_DELAY_DURATION * 1000
            );
        }

Falls das hier jetzt was offensichtliches ist und ich jetzt hier natürlich mit 0-JavaScript-Erfahrung rum rate und ihr merkt ohne Grundlagen wird das so nichts, bitte gleich sagen. Dann kommt das mal auf die ToDo-Liste. Der Code hat mich nur sehr neugierig gemacht.

Habe die Datei jetzt auch lokal im Browser offen und zum testen etwas mit den Texten gespielt, die Änderungen werden jedes mal korrekt angezeigt.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 14056
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wie gesagt, die beiden Funktionen machen im Grunde das gleiche wie `after()` und `after_cancel()` auf `tkinter.Widget`-Objekten.

Und ja, `this` hat hier die gleiche Bedeutung wie `self` in Python.

Du bist der Lösung ein *Stück* näher gekommen, denn Du startest den Timer *einmal* neu, so dass Du auf bis zu 6 Sekunden Verzögerung kommen kannst. Du hast vergessen das Ergebnis von `setTimeout()` auch wieder an `this.timeoutID` zu binden, damit das `clearTimeout()` dann auch beim nächsten Druck auf den Button nicht noch die ID vom alten Timer versucht zu stoppen. 🙂

Hier noch mal spasseshalber ein UML-Zustandsdiagramm (schon mit der Änderung im Verhalten die Du implementieren willst). Vielleicht kann Nami was damit anfangen.
Bild
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Benutzeravatar
Dennis89
User
Beiträge: 1556
Registriert: Freitag 11. Dezember 2020, 15:13

Ach jaaa, deswegen wurde der Timer nicht abgebrochen, dankeschön :)

Jetzt funktioniert es:

Code: Alles auswählen

<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Schalttest</title>
    <style type="text/css">
        body {
            font-size: 11pt;
            max-width: 50em;
            margin: auto;
            padding-top: 1em;
            hyphens: auto;
        }
        .device {
            background: lightgray;
            border: medium outset lightgray;
            border-radius: 1em;
            width: fit-content;
            padding: 1em;
        }
        .led {
            font-size: larger;
        }
    </style>
</head>
<body>
<h1>Gerätesimulation</h1>

<p>Das simulierte Gerät ist erst in einer Phase in der man mit dem Taster
    die Schaltverzögerung einstellen kann.  Danach startet ein Tastendruck die
    Verzögerung nach der das Gerät eingeschaltet wird.  Während dieser
    Verzögerung hat der Taster keine Wirkung.  Wenn das Gerät eingeschaltet ist,
    bewirkt der Taster die sofortige Abschaltung.</p>

<div class="device">
    <p>Schaltzustand: <span id="device_state"></span></p>
    <p>Schaltverzögerungsstufe: <span id="leds"></span></p>
    <p><button id="button">Taster</button></p>
</div>
<p>Status: <span id="state_description"></span></p>
<p>
    <button id="restart">(Neu)start</button>
    (man kann auch einfach die Seite neu laden)
</p>

<script type="text/javascript">
    const LED_OFF_COLOR = 'gray';
    const LED_ON_COLOR = 'lightgreen';
    const SET_DELAY_DURATION = 3;  // in seconds.
    const DELAY_VALUES = [1, 2, 3];  // in seconds.

    function makeSwitchLed(parentElement) {
        const span = document.createElement('span');
        span.className = 'led';
        span.innerText = '●';
        parentElement.appendChild(span);
        const switchLed = function (state) {
            span.style.color = (state) ? LED_ON_COLOR : LED_OFF_COLOR;
        };
        switchLed(false);
        return switchLed;
    }

    function makeSetStripLevel(element, count) {
        const switchLed = Array.from(
            {length: count}, () => makeSwitchLed(element)
        );
        return function (value) {
            switchLed.forEach((switchLed, i) => switchLed(i < value));
        };
    }

    class Device {
        constructor(
            stateElement,
            ledsElement,
            buttonElement,
            statusTextElement,
            resetButtonElement,
            delayValues
        ) {
            this._isOn = null;
            this.handleButtonClick = null;
            this.delayValues = delayValues;
            this.delayIndex = null;
            this.timeoutID = null;

            this.switchStateLed = makeSwitchLed(stateElement);
            this.setStripLevel = makeSetStripLevel(
                ledsElement, delayValues.length
            );
            this.statusTextElement = statusTextElement;
            buttonElement.addEventListener('click', this.onButton.bind(this));
            resetButtonElement.addEventListener('click', this.restart.bind(this));
            this.restart();
        }

        get delay() {
            return this.delayValues[this.delayIndex];
        }

        set statusText(text) {
            this.statusTextElement.innerText = text;
        }

        get isOn() {
            return this._isOn;
        }

        set isOn(state) {
            this._isOn = state;
            this.statusText = `Gerät ${(this._isOn) ? 'Ein' : 'Aus'}geschaltet.`;
            this.updateUi();
        }

        updateUi() {
            this.switchStateLed(this.isOn);
            this.setStripLevel(this.delayIndex + 1);
        }

        restart() {
            this._isOn = false;
            this.handleButtonClick = this.onButtonWhenSettingDelay;
            this.delayIndex = 0;
            this.statusText = `Phase 1: Schaltverzögerung einstellen (${SET_DELAY_DURATION} Sekunden).`;
            this.updateUi();
            this.restartTimer();
        }

        restartTimer() {
            clearTimeout(this.timeoutID);
            this.timeoutID = setTimeout(
                () => {
                    this.timeoutID = null;
                    this.handleButtonClick = this.onButtonWhenDeviceIsOff;
                    this.statusText = "Warte auf Tastendruck.";
                },
                SET_DELAY_DURATION * 1000
            );
        }
        onButtonWhenSettingDelay() {
            this.delayIndex = (this.delayIndex + 1) % this.delayValues.length;
            this.restartTimer();
        }

        onButtonWhenDeviceIsOff() {
            this.handleButtonClick = this.onButtonWhileDelay;
            this.timeoutID = setTimeout(
                () => {
                    this.timeoutID = null;
                    this.isOn = true;
                    this.handleButtonClick = this.onButtonWhenDeviceIsOn;
                },
                this.delay * 1000
            );
            this.statusText = `Schaltvorgang in ${this.delay} Sekunden.`;
        }

        onButtonWhileDelay() {}

        onButtonWhenDeviceIsOn() {
            this.isOn = false;
            this.handleButtonClick = this.onButtonWhenDeviceIsOff;
        }

        onButton() {
            this.handleButtonClick();
            this.updateUi();
        }
    }

    new Device(
        document.getElementById('device_state'),
        document.getElementById('leds'),
        document.getElementById('button'),
        document.getElementById('state_description'),
        document.getElementById('restart'),
        DELAY_VALUES
    );
</script>
</body>
</html>
Danke für den kleinen Ausflug in JavaScript 🙂

Sorry @Nami das ich mich da so in dein Thread eingeschlichen habe.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten