This discussion is archived
2 Replies Latest reply: Apr 29, 2013 4:47 AM by 903219 RSS

force graphics update from actionPerformed thread

903219 Newbie
Currently Being Moderated
I have code that demonstrates my question. From my internet searches this is a common question but the common answers have not resolved the issue. Java will compress the graphics events into one. When the code initially runs the runnable test class will work correctly. When it is run in response to the button click event it compresses the graphics events and only the second color is displayed in the background of the JPanel object. I want to have a button click event cause the background to change between two different colors with a fixed time interval between the color changes. I've investigated this extensively and tried many of the different recommended fixes and several of my own scenarios using threads and such... all without success.
I am missing something important.. Just can't put my finger on it. Thanks in advance for your help.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class TestClass implements ActionListener {
  JFrame frame;
  JPanel panel;
  ColorChanger test;
  Runnable BlueBackground;
  Runnable YellowBackground;
  
  public TestClass() {
    frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    panel = new JPanel();
    panel.setPreferredSize(new Dimension(100,100));
    panel.setMinimumSize(new Dimension(100,100));
    panel.setVisible(true);
    frame.add(panel);
    frame.setPreferredSize(new Dimension(100,100));
    frame.setMinimumSize(new Dimension(100,100));
    frame.setVisible(true);
    JButton button = new JButton();
    button.addActionListener(this);
    panel.add(button);
    test = new ColorChanger();
    BlueBackground = new Runnable(){public void run(){panel.setBackground(Color.blue); panel.repaint();}};
    YellowBackground = new Runnable(){public void run(){panel.setBackground(Color.yellow); panel.repaint();}};
    test.run();
  }

  /*
   * When run on the EDT this works.  When run on the actionPerformed thread it compressed the graphics events into one.
   * Initial threads - Their most essential job is to create a Runnable object that initializes the GUI and schedule that 
   *   object for execution on the event dispatch thread.
   * EDT event dispatch thread - Once the GUI is created, the program is primarily driven by GUI events, each of which causes 
   *   the execution of a short task on the event dispatch thread. 
   * Worker thread - Application code can schedule additional tasks on the event dispatch thread (if they complete quickly, so 
   *   as not to interfere with event processing) or a worker thread (for long-running tasks).
   */
  public final class ColorChanger implements Runnable {
    public void run(){
      try {
        Thread.sleep(700);
        SwingUtilities.invokeLater(BlueBackground);
        Thread.sleep(700);
        SwingUtilities.invokeLater(YellowBackground);
        Thread.sleep(700);
      } catch (InterruptedException ex) {
        
      }
    }
  }

  @Override
  public void actionPerformed(ActionEvent arg0) {
    test.run();
  }
}

public class Main {
  private static TestClass square;
    /**
     *<p> Initials - Version - Date     - Reason        <br>
     *      JPH    - 0.00    - 01/20/13 - Java Release
     *<p>  
     * main() method is the method used to launch the application.  It also
     * contains the application state data.<br>
     *  
     * @param args - String array of potential run parameters.  Not used.
     * @return nothing
     */
    public static void main(String[] args)  {
      square = new TestClass();
    }
}
  • 1. Re: force graphics update from actionPerformed thread
    Maxideon Explorer
    Currently Being Moderated
    Upon button click, this code
    Thread.sleep(700);
    SwingUtilities.invokeLater(BlueBackground);
    Thread.sleep(700);
    SwingUtilities.invokeLater(YellowBackground);
    Thread.sleep(700);
    gets executed on the Event Dispatch Thread (EDT). Specifically, actionPerformed() is called on the EDT and you then call run(). This is the same thread that will handle those invokelater's as well as the painting itself. It can't do that stuff, though, until it finishes all that sleeping and exit actionPerformed(). Hopefully you can see why this is a problem.

    The above code needs to be executed on a separate thread.
    @Override
    public void actionPerformed(ActionEvent arg0) {
      new Thread(test).start();
    }
  • 2. Re: force graphics update from actionPerformed thread
    903219 Newbie
    Currently Being Moderated
    Thank you Maxideon. I do understand the limitation of the EDT. I incorrectly believed that "public final class ColorChanger implements Runnable" was a new thread. I've studied the threading documentation at Oracle and it is (even now) a STRANGE distinction to me that "test.run();" runs on the EDT and "new Thread(test).start();" does not... but I get it. Clearly my newbe status is correct. Learning is a process of many mistakes. Thanks again.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points