0

The GUI doesn't update while I'm running my simulation of moving a square inside a JPanel. It doesn't refresh the JPanel until after the whole simulation is done.

My code:

import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

public class Main {

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

    private static void createAndShowGUI() {
        System.out.println("Created GUI on EDT? "+
        SwingUtilities.isEventDispatchThread());
        JFrame f = new JFrame("Swing Paint Demo");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        f.add(new MyPanel());
        f.pack();
        f.setVisible(true);
    } 
}

class MyPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private int squareX = 50;
    private int squareY = 50;
    private int squareW = 20;
    private int squareH = 20;

    public MyPanel() {

        setBorder(BorderFactory.createLineBorder(Color.black));

        doAnimation();

    }

    private void doAnimation(){
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                for(int x = 0; x < 250-squareW; x+=squareX){
                    for(int y = 0; y < 200-squareH; y+=squareY){
                        moveSquare(x,y);
                    }
                }
            }
        });
    }

    private void moveSquare(int x, int y) {
        int OFFSET = 1;
        if ((squareX!=x) || (squareY!=y)) {
            repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
            squareX=x;
            squareY=y;
            repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
        } 
    }


    public Dimension getPreferredSize() {
        return new Dimension(250,200);
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);       
        g.drawString("This is my custom Panel!",10,20);
        g.setColor(Color.RED);
        g.fillRect(squareX,squareY,squareW,squareH);
        g.setColor(Color.BLACK);
        g.drawRect(squareX,squareY,squareW,squareH);
    }  
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
Brian T Hannan
  • 3,925
  • 18
  • 56
  • 96
  • Does it work if you do `Thread.sleep(100);` after each `moveSquare(x,y);`? – jonhopkins Dec 09 '13 at 19:01
  • 4
    Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens. Instead of calling `Thread.sleep(n)` implement a Swing `Timer` for repeating tasks or a `SwingWorker` for long running tasks. See [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/) for more details. – Andrew Thompson Dec 09 '13 at 19:05
  • No, it does not ... already tried it with Thread.sleep(1000); – Brian T Hannan Dec 09 '13 at 19:05
  • @AndrewThompson, some people will argue that awaiting is not blocking and will give you continuous downvote even if you make a valid point. [Don't believe me? see here](http://stackoverflow.com/questions/20465456/jdialog-invisible-components-clickable/20467523#20467523) – Sage Dec 09 '13 at 19:11

2 Answers2

4

It doesn't refresh the JPanel until after the whole simulation is done.

Yes. The update will be performed once. Because you are running the following code inside the Runnable of invokeLater which is being submitted to EDT by wrapped as an single event:

SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                for(int x = 0; x < 250-squareW; x+=squareX){
                    for(int y = 0; y < 200-squareH; y+=squareY){
                        moveSquare(x,y);
                    }
                }
            }
        });

All the repaint request that you made inside the moveSquare(x, y) method are being wrapped in that single event. Even if, we invoke repaint several time in a row in the same event handler, Swing is smart enough to take that information and repaint those sections of the screen all in one single paint operation. In other words, Swing will not repaint the component several times in a row, even if that is what the code appears to be doing. Check this official tutorial page for detailed explanation and example.

However, running a long loop like this will make your GUI app unresponsive: because it is likely to await the EDT. And take the advantage of Swing Timer class with periodic task to make animation effect.

Sage
  • 15,290
  • 3
  • 33
  • 38
  • Where exactly do I put the timer? I don't get it. – Brian T Hannan Dec 09 '13 at 21:15
  • I grabbed the code from that tutorial page and modified it to do an automatic animation rather than having to manually drag-n-drop. But it freezes. And I mean this is the original code in my question above ... that is what I was doing before I posted this question. – Brian T Hannan Dec 09 '13 at 21:23
  • @BrianTHannan, you won't understand this way if already haven't used it. Please go through the linked page and taste the demo and play around with it. – Sage Dec 09 '13 at 21:28
  • There is no demo for an automatic animation. Link? – Brian T Hannan Dec 09 '13 at 21:35
  • Nevermind, found an example here: http://stackoverflow.com/questions/16316132/animate-jpanel-slide-in-with-timer – Brian T Hannan Dec 09 '13 at 21:39
  • @BrianTHannan, sorry i was a little busy, hence could not response. If you need for demo example with Timer class you can post a new question demonstrating your attempt and goal. In fact doing so will get you a several better answer with interesting demo from many answerers :). Thank you – Sage Dec 11 '13 at 21:48
-1

Look here..

private void moveSquare(int x, int y) {
    int OFFSET = 1;
    if ((squareX!=x) || (squareY!=y)) {
        repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
        squareX=x;  // It makes squareX will be 0 in first iteration
        squareY=y;  // It makes squareY will be 0 in first iteration
        repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
    } 
}

Finally you got infinity loop. So Change it..

subash
  • 3,116
  • 3
  • 18
  • 22