user3549772
user3549772

Reputation: 1

Java, thread.sleep() blocks use of buttons (does not recognize button clicks)

I'm writing a program that draws a train that moves across the screen. I'm using the graphics class with the fillRect, drawOval, and draw lines methods. I have a button that starts the train on the right hand side of the screen. After disappearing off screen, it should reappear with random y location and run again. This should loop until the stop button is clicked. The problem is I'm using the thread.sleep() method to pause the program before it updates the train's position. For some reason, I can't click any buttons while the program is running. Any ideas on how to make my stop button work? Thanks.

Here's the code I am using. It also has the normal code that a JFrame form has in netbeans.

private void btnStartTrainActionPerformed(java.awt.event.ActionEvent evt) {                                              
    run = true;
    while (run) {
    Graphics g = jPanel1.getGraphics(); 
    int x =0;
    int y = (int)(Math.random() *500) + 20;
    int smoke =1;

    for( x = 900; x > -600; x--)
    {
        drawTrain(g, x, y, smoke);        
    try {
          Thread.sleep(17);
        } 
catch (InterruptedException e) 
    {
             e.printStackTrace();
    }
    clearJFrame(g);
    smoke++;
    x = x-4; 
    }
    if (x == -599)
    {
        x = 900;
        y = (int)(Math.random() *500) + 20;
    }
    }
    }                                             

    private void btnStopTrainActionPerformed(java.awt.event.ActionEvent evt) {                                             
    run = false;
    } 
public void drawTrain(Graphics g, int x, int y, int smoke)
{
         // draw locomotive
    g.setColor(Color.RED);
    g.fillRect(x, y, 100, 30); 
    g.fillRect(x +100, y-30, 40, 60); 
    g.setColor(Color.BLACK);
    g.fillRect(x +110, y-20, 20, 10);
    g.drawLine(x +10, y, x, y-20); 
    g.drawLine(x +11, y, x, y-20);
    g.drawLine(x, y-20, x +30, y-20);
    g.drawLine(x +30, y-20, x +20, y);
    g.drawLine(x +31, y-20, x +20, y);
    g.drawLine(x, y+30, x-20, y+25);
    g.drawLine(x-20, y+25, x-20, y+20); 
    g.drawLine(x-20, y+20, x, y+15); 
    g.drawOval(x +10, y+20, 25, 25); //draw wheels
    g.drawOval(x +35, y+20, 25, 25);
    g.drawOval(x +60, y+20, 25, 25);
    g.drawOval(x +85, y+20, 25, 25);
    g.drawOval(x +110, y+20, 25, 25);
    if (smoke >20) 
        g.drawOval(x +8, y-33, 12, 12); // draw smoke
    if (smoke >40) 
        g.drawOval(x +12, y-53, 12, 12);
    if (smoke >60)
        g.drawOval(x +18, y-73, 13, 13);
    if (smoke >80)
        g.drawOval(x +25, y-100, 14, 14);
    if(smoke >100)
        g.drawOval(x+31, y-120, 15, 15);
    if (smoke > 120)
        g.drawOval(x+37, y - 140, 16, 16);
    if (smoke > 140)
        g.drawOval(x+44, y-160, 17, 17);
    g.setColor(Color.RED);
    g.fillRect(x +160, y, 80, 30);      // draw additional cars
    g.fillRect(x +260, y, 80, 30);
    g.fillRect(x +360, y, 80, 30);
    g.setColor(Color.BLACK);
    g.drawOval(x +160, y+20, 25, 25); 
    g.drawOval(x +215, y+20, 25, 25);
    g.drawOval(x +260, y+20, 25, 25);
    g.drawOval(x +315, y+20, 25, 25);
    g.drawOval(x +360, y+20, 25, 25);
    g.drawOval(x +415, y+20, 25, 25);


 }
 public void clearJFrame(Graphics g)
 {
     g.setColor(jPanel1.getBackground());
     g.fillRect(0, 0, jPanel1.getWidth(), jPanel1.getHeight());
 }

Upvotes: 0

Views: 807

Answers (3)

slaadvak
slaadvak

Reputation: 4779

Unfortunately, when you work with Swing (and almost every UI framework) you cannot put a sleep like you do right in the UI thread (also called the Event Dispatch Thread), because, its really this thread that manages the whole UI. So, by sleeping it, its normal that it becomes unresponsive for the whole sleeping duration.

What you should do is to start another thread from btnStartTrainActionPerformed and make the sleep in this thread instead.

Something like :

private void btnStartTrainActionPerformed(java.awt.event.ActionEvent evt) {
    Thread thread = new Thread() {
      public void run()
      {
        run = true;
        while (run)
        {
            Graphics g = jPanel1.getGraphics(); 
            int x =0;
            int y = (int)(Math.random() *500) + 20;
            int smoke =1;

            for( x = 900; x > -600; x--)
            {

            // Needed to make this change back on the UI Thread
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        drawTrain(g, x, y, smoke);        
                }
            });

            try
            {
                Thread.sleep(17);
            } 
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }

            clearJFrame(g);
            smoke++;
            x = x-4; 
        }

        if (x == -599)
        {
            x = 900;
            y = (int)(Math.random() *500) + 20;
        }
      }
     }
    };

    thread.start();
}

Upvotes: 0

Haozhun
Haozhun

Reputation: 6521

When a Swing program needs to execute a long-running task, you need to use SwingWorker. Blocking on the Event Dispatch Thread will freeze your GUI.

Read these two tutorial from oracle, and you'll know what you should do:

Upvotes: 2

Marko Topolnik
Marko Topolnik

Reputation: 200206

You are executing Thread.sleep() on the Event Dispatch Thread, making the GUI freeze for the entire duration of that call.

The proper way to delay an action to be taken on the GUI is by scheduling it using the Swing Timer.

Upvotes: 3

Related Questions