Karlovsky120
Karlovsky120

Reputation: 6352

EDT queue cutting

There is this article:


Someone jumped the queue! Every now and then it appears that some swing events are processed in the incorrect order in the Event Queue (and nothing gets my blood boiling like when someone cuts into a queue) resulting in strange behavior. This is best illustrated with a small code snippet. Read the snippet below and think carefully in what order you imagine events will take place.

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent arg0) {
         repaint();
         doSomething();
    }
});

Most developers would image that repaint() method will result in a painting operation taking place before the doSomething() method call. However this is actually not the case, the call to repaint() will create a new paint event that will be added to the end of the Event Queue. This new paint event will only be processed (dispatched) after the current Action Event has completed. This means that the doSomething() method will be executed before the new Paint Event on the queue is dispatched.

The key point here is that calls to repaint() will create a new paint event that will be added to the end Event Queue and not processed immediately. This means that no events jump the queue (and my blood can remain at its correct temperature).

(source)


My question is, how can I force Swing to do the repaint(); BEFORE doSomething();?

Also, if there were calls to the repaint() method WITHIN the doSomething(); they would be executed only after doSomething(); is completed. Is there a way I can pause the doSomething(); mid-executin, then throw in the reapaint();, get done with it, and then resume doSomething();?

Only solution I have found so far is this(link), but it's not really practical...

Upvotes: 3

Views: 334

Answers (2)

Mikle Garin
Mikle Garin

Reputation: 10143

Well, you and the author of the quoted article are missing the point here. "repaint" method calls simply inform the repaint manager that:

  1. There is a component to repaint (on which you call "repaint")
  2. It should be repainted with (x,y,w,h) clip (if you call "repaint" w/o specifying rect -it will be the whole component bounds, (0,0,w,h))

So it doesn't really matter when the repaint will occur since you might not even notice it if you are calling A LOT of repaints one by one for the same component. That is also why such consequent calls might get merged. Check this example:

private static int repaintCount = 0;

public static void main ( String[] args )
{
    final JComponent component = new JComponent ()
    {

        protected void paintComponent ( Graphics g )
        {
            try
            {
                // Simulate heavy painting method (10 milliseconds is more than enough)
                Thread.sleep ( 10 );
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace ();
            }

            g.setColor ( Color.BLACK );
            g.drawLine ( 0, 0, getWidth (), getHeight () );

            repaintCount++;
            System.out.println ( repaintCount );
        }
    };
    component.setPreferredSize ( new Dimension ( 200, 200 ) );

    JFrame frame = new JFrame ();
    frame.add ( component );
    frame.pack ();
    frame.setLocationRelativeTo ( null );
    frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
    frame.setVisible ( true );

    new Thread ( new Runnable ()
    {
        public void run ()
        {
            try
            {
                Thread.sleep ( 1000 );
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace ();
            }
            System.out.println ( "Starting repaint calls" );
            for ( int i = 0; i < 100000; i++ )
            {
                component.repaint ();
            }
            System.out.println ( "Finishing repaint calls" );
        }
    } ).start ();
}

This is the approximate output you will see (might vary depending on computer speed, Java version and lots of other conditions):

1
Starting repaint calls
2
3
4
5
6
Finishing repaint calls
7
8

"1" - the initial repaint when frame is displayed.
"2,3,4..." - other seven repaints occured due to the calls from a separate non-EDT thread.

"But i have called 100000 repaints, not 7!" - you will say. Yes, repaint manager merged those that were similar and in the same time in the repainting queue. That is made to optimize repaints and speedup the UI overall.

By the way, you don't need to call repaint from EDT since it doesn't perform any real painting and just queue your component update for future. It is already thread-safe method.

To summ up - there should be no situations when you really need to repaint the component right before doing some other action (that could also cause its repaint again). Just call the repaint when you need to repaint the component (with specified rectangle when possible) - repaint manager will do the rest. That works well unless you put some calculations inside the paint method which is totally wrong and might cause a lot of problems.

Upvotes: 6

dkatzel
dkatzel

Reputation: 31648

repaint() adds a new paint request to the end of the queue of the Event Dispatch Thread (EDT). So making several calls to repaint() within doSomething() will only repaint after doSomething() completes. (I assume doSomething() is always called from the EDT. Your code example calls doSomething() from insideactionPerformed which is always called from the EDT.)

The reason paint() requests are queued up is to decrease the number of times a component is painted. Queuing up repaint() requests allows several methods to mark different components as dirty so they can all be repainted at the same time in one expensive paint() operation.

If you really want to force a repaint immediate, there are methods like paintImmediately(Rectangle r) and paintImmediately(int x, int y,int w,int h)but you will have to know the dimensions to repaint.

You can also always call paint(Graphics g) yourself if you have a reference to the current Graphics that the Swing component is using. (You can also use this technique to create your own graphics object from a Image object, if you want to take a screenshot and write it a picture file).

Upvotes: 2

Related Questions