views:

124

answers:

5

I'm wondering why Thread.sleep() does not strike in the expected point of time.

I expected the first rectangle to be drawn, a pause of 2 seconds and then the appearance of the second rectangle. However, the pause kicks in first and then both rectangles are drawn 'at once'.

Why does this happen to be the case?

Thanks in advance for any advices.

public class Figure extends JPanel

{

  Point a = new Point(10, 10);

  Point b = new Point(110, 10);

  Point c = new Point(110, 110);

  Point d = new Point(10, 110);

  @Override
  protected void paintComponent(Graphics g)
  {

    super.paintComponent(g);

    g.drawLine(a.x, a.y, b.x, b.y);

    g.drawLine(b.x, b.y, c.x, c.y);

    g.drawLine(c.x, c.y, d.x, d.y);

    g.drawLine(d.x, d.y, a.x, a.y);

    try
    {
      Thread.sleep(2000);
    }
    catch(InterruptedException ex)
    {
    }
    Point newA = rotate(a, 45);
    Point newB = rotate(b, 45);
    Point newC = rotate(c, 45);
    Point newD = rotate(d, 45);

    g.drawLine(newA.x, newA.y, newB.x, newB.y);
    g.drawLine(newB.x, newB.y, newC.x, newC.y);
    g.drawLine(newC.x, newC.y, newD.x, newD.y);
    g.drawLine(newD.x, newD.y, newA.x, newA.y);
  }

  private Point rotate(Point p, int degree)

  {

    //to shift the resulting Point some levels lower
    int offset = 100;

    int x = (int)(Math.cos(degree) * p.x + Math.sin(degree) * p.y);
    int y = (int)(-Math.sin(degree) * p.x + Math.cos(degree) * p.y) + offset;

    Point ret = new Point(x, y);
    return ret;
  }

}
+4  A: 

This happens because you do not enter Swings Event Loop; only then are paint commands guaranteed to execute. Also, that is also one reason why it's a good idea never to sleep in the event loop thread...

Tassos Bassoukos
Would you give me a practical advice on how to enter Swings Event Loop?
yes
@yes: you re-enter the event loop by returning from your `paintComponents()` method. In other word: don't do anything that takes a long time (including sleeping) on the [EDT](http://en.wikipedia.org/wiki/Event_dispatching_thread).
Joachim Sauer
@yes: For your use case, start a new thread that sleeps for the duration, then uses EventQueue#invokeLater() to redraw the second part of your drawing.
Tassos Bassoukos
A: 

I changed the code for the second rectangle to

try
{
  Thread.sleep(2000);
}

catch(InterruptedException ex)
{
}

a = rotate(a, 45);
b = rotate(b, 45);
c = rotate(c, 45);
d = rotate(d, 45);

g.drawLine(a.x, a.y, b.x, b.y);
g.drawLine(b.x, b.y, c.x, c.y);
g.drawLine(c.x, c.y, d.x, d.y);
g.drawLine(d.x, d.y, a.x, a.y);

The method paintComponent seems to be ran through pereptually: Now a rectangle is rotated, its predecessor disappears and so on and so on...

yes
A: 

Calling all those draw methods doesn't physically draw anything, it just puts drawing commands into the graphics pipeline, and all your sleep() is doing is holding up processing of the pipeline. What you should be doing is toggling a variable that tells the method which bits to draw, and externally firing repaint() from a separate thread at the required intervals.

EJP
A: 

Never ever use Thread.sleep() in AWT event thread (e.g. paintComponent() or various onclick handlers, which are always run in AWT event thread) - you are blocking all events from being processed - key, mouse, etc and your app will look like it froze. A quick-and-dirty solution:

private boolean isRotating = false;

 @Override
protected void paintComponent(Graphics g)
{
  super.paintComponent(g);
  if (!isRotating) {
  g.drawLine(a.x, a.y, b.x, b.y);
  g.drawLine(b.x, b.y, c.x, c.y);
  g.drawLine(c.x, c.y, d.x, d.y);
  g.drawLine(d.x, d.y, a.x, a.y);
  isRotating = true;
  Utils.swingInvokeDelayed(new Runnable() { public void run(){ repaint();}}, 2000);
} else {
  Point newA = rotate(a, 45);
  Point newB = rotate(b, 45);
  Point newC = rotate(c, 45);
  Point newD = rotate(d, 45);
  g.drawLine(newA.x, newA.y, newB.x, newB.y);
  g.drawLine(newB.x, newB.y, newC.x, newC.y);
  g.drawLine(newC.x, newC.y, newD.x, newD.y);
  g.drawLine(newD.x, newD.y, newA.x, newA.y);
  isRotating = false;
}
}

public class Utils {
// ideal is to keep 0 as corePoolSize, but then the executor simply does not work :(
private static final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, newDaemonThreadFactory(Utils.class.getSimpleName()));
private static ThreadFactory newDaemonThreadFactory(final String threadName) {
    return new ThreadFactory() {

        public Thread newThread(Runnable r) {
            final Thread result = new Thread(r, threadName);
            result.setDaemon(true);
            return result;
        }
    };
}
public static void swingInvokeDelayed(final Runnable r, final long delay) {
    executor.schedule(new Runnable() {

        public void run() {
            SwingUtilities.invokeLater(r);
        }
    }, delay, TimeUnit.MILLISECONDS);
}
}
Martin Vysny
A: 

Swingworker could be what you are searching for

Hernán Eche