views:

377

answers:

3

Hi there,

I'm writing a simple Game of Life program in Java and am having a bit of trouble getting it to animate. I've got a JComponent class LifeDraw, which displays a grid of pixels, with the following paint method:

protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    for (int y = 0; y < lGrid.getHeight(); y++) {
        for (int x = 0; x < lGrid.getWidth(); x++) {
            if (lGrid.getCell(x,y) == 1) {
                g.setColor(Color.red);
                g.fillRect(x * lGrid.getSqSize(), y * lGrid.getSqSize(), lGrid.getSqSize(), lGrid.getSqSize());
            } else {
                g.setColor(Color.white);
                g.fillRect(x * lGrid.getSqSize(), y * lGrid.getSqSize(), lGrid.getSqSize(), lGrid.getSqSize());
            }
        }
    }
}

And then another class LifeGrid that has a method run(), which when called will update the grid of pixels for one generation and then call LifeDraw.repaint(). However, if I try to call run() in a loop, the JComponent doesn't repaint until the loop is finished so all that is ever displayed is the first generation and the last one. I figured it was probably just updating too quickly to repaint, so I tried using Thread.sleep() between iterations but still had the same problem. Theoretically (or at least I was hoping it would), it should repaint the component between each iteration and display an animation of the pixels changing.

I'm not that well versed in Java GUI, so any help would be really appreciated. Hopefully I've explained it clearly enough, let me know otherwise!

A: 

I don't know if it would solve your problem, but you could try calling run() from a Timer instead of a simple for or while loop.

MatrixFrog
Excellent, thank you! Works perfectly. Not entirely sure why it wasn't working, but I guess it had something to do with different threads sleeping while run() still processed the arrays.Thanks a lot :)
Adam
If that fixed the problem, then chances are your "run()" method or whatever was calling it was busy-waiting in the event loop. I assumed from the name that it was the run() method from a Runnable or Thread, but apparently not.
Paul Tomblin
A: 

repaint has to be fired in the event loop, not in another thread. Try replacing your call to repaint() with

SwingUtilities.invokeLater(new Runnable()
{
  public void run()
  {
     repaint();
  }
));
Paul Tomblin
I thought you were supposed to call repaint() in any thread you want, and then it would cause paintComponent() to be called in the appropriate thread. Am I very mistaken?
MatrixFrog
You could be right. I'm very overcautious about not doing GUI things outside the event loop.
Paul Tomblin
I think it's time I bought a book on Swing, there's far too much I don't know. Though I'm pretty sure that'll stay that way considering the size of Swing :)
Adam
+1  A: 

From the javadocs for repaint()

Adds the specified region to the dirty region list if the component is showing. The component will be repainted after all of the currently pending events have been dispatched

All repaint does is signal that the area needs repainting. Try paintImmediately or update.

karoberts
Thanks a lot for the help; as above, using a Timer instead of loop/sleep() seemed to sort out the problem, but I'm keeping note of the other answers in case something else goes wrong!
Adam
In general, I've found that trying to solve Swing problems using Thread.sleep() is not a good idea.
MatrixFrog