Ereignisprogrammierung, Leerlaufereignis

leisure

Mitglied
Ich versuche "Türme von Hanoi" mit Netbeans zu realisieren. Es existieren 2 Modi: halbautomatisch und automatisch. Bei halbautomatisch wird der nächste Zug durch Anklicken eines Buttons ausgelöst, bei vollautomatisch werden solange Scheiben verschoben, bis der komplette Turm von A nach B verschoben wurde.
Das ist kein Problem, wenn man den Automatik-Modus durch eine Schleife realisiert. Nachteil hierbei: ich kann das Programm nicht vorzeitig durch Betätigung eines Buttons beenden oder die Rahmenbedingungen wie Geschwindigkeit (Slider) etc. beeinflussEN:
Deshalb habe ich den Code etwas nachgebessert: meinem JFrame wurde ein ActionListener-Interface verpasst und der Code um eine actionPerformed-Methode und mehrere Anweisungen wie jButtonEnde.addActionListener(this) ergänzt. In der actionPerformed-Methode wird, wenn keines der vordefinierten Ereignisse eintritt, eine Methode bearbeiteLeerlauf() aufgerufen.
Dabei muss ich leider zwei Feststellungen machen:
1. ein Klick auf einen Button löst jetzt zweimal die mit dem Button verknüpfte Action aus (scheinbar wirkt die im generated Code - Bereich vorliegende addActionListener-Methode auch noch und ich weiss nicht wie man die löscht.
2. meine actionPerformed Methode:
Java:
public void actionPerformed(java.awt.event.ActionEvent pEvt){
    if (pEvt.getSource()==jTextFieldScheiben){}jTextFieldScheibenActionPerformed(pEvt);
    else if (pEvt.getSource()==jRadioButtonManuell)jRadioButtonManuellActionPerformed(pEvt);
    else if (pEvt.getSource()==jRadioButtonAutomatisch) jRadioButtonAutomatischActionPerformed(pEvt);
    else if (pEvt.getSource()==jButtonTakt){}jButtonTaktActionPerformed(pEvt);
    else if (pEvt.getSource()==jButtonEnde)jButtonEndeActionPerformed(pEvt);
    else bearbeiteLeerlauf();
}
Leider wird die Methode bearbeiteLeerlauf() nie ausgeführt. Warum nicht? Wie kann ich das Problem lösen?
 

Michael...

Top Contributor
Wenn ich's richtig verstanden habe geht es hier darum die Lösung als "Animation" darzustellen. Dazu soll die Geschwindigkeit verändert und pausiert werden können.

Dazu ist das Arbeiten mit Threads notwendig. Bei konstanter Animationsgeschwindigkeit kann man es auch recht einfach mit einem Timer realisieren.
 

leisure

Mitglied
Nun, die Verzögerung erreiche ich mit Thread.wait(...). Das Problem ist aber die Schleife. Während die Schleife aktiv ist, kann ich den Regler (Slider) nicht verstellen. Daher wollte ich in der actionPerformed-Methode erreichen, dass wenn keines der anderen Ereignisse eintritt, die LeerlaufMethode aufgerufen wird, wo die Anweisungen, die normalerweise in der Schleife stehen würden, aufgeführt sind.
 

GUI-Programmer

Top Contributor
Mein Vorschlag: Benutze einen Timer! Zum Beispiel den javax.swing.Timer. Dieser führt in festgelegten Abstanden, z.B. jede Millisekunde oder Hundertstelsekunde, die Methode actionPerformed(...) des zugehörigen ActionListeners auf.

Beispiel:
Java:
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class Timertest {
    private final Timer timer;
    private int millisekunden;
    
    public Timertest() {
        millisekunden = 0;
        timer = new Timer(1, new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                millisekunden++;
                
                if(millisekunden==1500) {
                    // Hier stehen Befehle, die nach 1500 Millisekunden
                    // ausgeführt werden
                }
                
                if(millisekunden==2081) {
                    // Hier stehen Befehle, die nach 1500 Millisekunden
                    // ausgeführt werden
                }
            }
        });
        
        // Kann z.B. von einem Button ausgeführt werden
        timer.start();
    }
}

Die erste Zahl (int) beim Aufruf des Konstruktors von timer gibt an, wie oft der ActionListener aufgerufen wird. 1 entspricht jede Millisekunde, 10 das zehnfache, also jede Hundertstelsekunde, usw.

Was du dabei beachten musst, ist, den Timer als final zu deklarieren, sonst funktioniert gar nichts.

Die Hilfsvariable dient nur dazu, die Zeit des Timers festzuhalten. Ein Timer kann zu jederzeit gestoppt werden - dann sollte die Hilfsvariable wieder gleich 0 gesetzt werden.


Möglicherweise ist es zudem noch hilfreich, den start() - Befehl (timer.start();) in einem Thread auszuführen.
 
Zuletzt bearbeitet:

leisure

Mitglied
Die Tipps mit Threads und Timer waren schon hilfreich.
Letztlich habe ich mich aber von einem anderen Beitrag in diesem Forum inspirieren lassen und das Problem mit Hilfe der Klasse SwingWorker gelöst:
Java:
public void actionPerformed(ActionEvent pEv ){
    String lCmd=pEv.getActionCommand();

    if(lCmd.equalsIgnoreCase("Start")){
        lWorker = new SwingWorker<String, Void>(){

            @Override
            protected String doInBackground(){
                while(!hatBefehle.isEmpty()){
                    takt();
                    try{
                        Thread.sleep(jSliderSpeed.getValue());
                    }catch(InterruptedException e){}
                }
                return "Neu";
           }

            @Override
            protected void done(){
                try {
                    jButtonTakt.setText(get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
            };
      // Execute the SwingWorker; the GUI will not freeze
            lWorker.execute();
    }

Meine Schleife funktioniert ohne die anderen Komponenten zu blockieren, allerdings muss ich für einen vorzeitigen Abbruch die Bedingung noch etwas anpassen. Aber das Verändern der Geschwindigkeit mit dem Slider funktioniert jetzt.
 

GUI-Programmer

Top Contributor
Das wäre mein nächster Lösungsvorschlag gewesen. Jedoch bin ich der Meinung, dass das ganze dann aber auch in einem Thread funktionieren würde:

Java:
public class MitThread {
    public void actionPerformed(ActionEvent pEv ){
        String lCmd=pEv.getActionCommand();
 
        if(lCmd.equalsIgnoreCase("Start")){
            Thread thread = new Thread(new Runnable(){
                
                public void run() {
                    while(!hatBefehle.isEmpty()){
                        takt();
                        try{
                            Thread.sleep(jSliderSpeed.getValue());
                        }catch(InterruptedException e){}
                    }
                    try {
                        jButtonTakt.setText(get());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
        }
    }
}
...oder so ähnlich!!!

Wie auch immer, es funktioniert ja nun bei dir.
 
Zuletzt bearbeitet:

leisure

Mitglied
Ich hätte das Thema schon selbst geschlossen, dachte aber, das es vielleicht noch Kommentare zu der Lösung geben würde (was ja auch der Fall war).
Aber nochmals Danke für alle Reaktionen.
 

Marco13

Top Contributor
Nun, ich wollte das nicht abwürgen, aber zwei Threads mit dem gleichen Titel und dem "fast" gleichen Thema ... vielleicht sollte man versuchen, sowas schärfer zu trennen. Aber ist vielleicht nicht so wichtig.
 

Wildcard

Top Contributor
@Wildcard
Was meinst du mit 'Swing ist nicht Thread-safe'.
Ein nebenläufiger Thread darf auf keine Methode einer Swing Component einfach so zugreifen, es sei denn sie ist explizit in der API Doc als threadsafe markiert (was die wenigsten sind).
 

Neue Themen


Oben