crayk
crayk

Reputation: 37

Putting event-dispatch thread on hold until Swing timer stops

I'm creating a program that plays a sequence of notes on a piano and then determines whether the user's clicks on the piano match what was just played. I got the program to play a sequence of notes automatically using a Swing timer, but I need it to wait until the sequence is done playing before it continues. Here's a simplified example of what I have now:

import java.awt.event.*;
import javax.swing.*;

public class myProgram {

    Timer timer = new Timer(1000, new AutoPlay());

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                myProgram myProgram = new myProgram();
                System.out.println("Computer plays notes.");
                myProgram.compTurn();
                System.out.println("Your turn.");
            }
        });   
    }

    class AutoPlay implements ActionListener {

        int i = 0;

        public void actionPerformed(ActionEvent e) {
            System.out.println("Auto-played note.");
            i++;
            if (i == 2) timer.stop();
        }
    }

    // TODO - Play a melody for the user to repeat
    public void compTurn() {
        timer.start();
    }
}

"Your turn" prints too soon. If it helps, the point of the timer is to have the program trigger an audio note, update the piano graphic so you can see the pressed note, load up the next note that will be played, and repeat until the sequence is complete.

Upvotes: 1

Views: 166

Answers (2)

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285450

Use a variant of the observer pattern to be notified of when the timer is done. For instance a PropertyChangeListener would work nicely. For example...

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.*;

public class MyProg2 extends JPanel {
   public static final String TIMER_COMPLETE = "timer complete";
   private static final int TIMER_DELAY = 1000;
   private Timer timer;
   public MyProg2() {
      // TODO create GUI
   }

   public void playNotes() {
      if (timer != null && timer.isRunning()) {
         return;
      }
      timer = new Timer(TIMER_DELAY, new TimerListener());
      timer.start();
   }

   private class TimerListener implements ActionListener {
      private int i = 0;

      @Override
      public void actionPerformed(ActionEvent e) {
         System.out.println("Auto-played note.");
         i++;
         if (i == 2) {
            timer.stop();
            firePropertyChange(TIMER_COMPLETE, false, true);
         }
      }
   }

   private static void createAndShowGui() {
      System.out.println("Starting program");
      MyProg2 mainPanel = new MyProg2();
      mainPanel.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent pcEvt) {
            if (MyProg2.TIMER_COMPLETE.equals(pcEvt.getPropertyName()) && pcEvt.getNewValue() == Boolean.TRUE) {
               System.out.println("Your turn");
            }
         }
      });
      mainPanel.playNotes();

      JFrame frame = new JFrame("MyProg2");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Upvotes: 1

Stefan
Stefan

Reputation: 12462

Disable user input options when the computer starts playing. Enable them when it is the users turn. Never disable the edt.

Upvotes: 1

Related Questions