0

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());
 }
  • 2
    You are also drawing your graphics inside the EDT Thread which is not good. Take a look at this post http://stackoverflow.com/questions/3256269/jtextfields-on-top-of-active-drawing-on-jpanel-threading-problems/3256941#3256941 it has an example in one of the answers. I would just read through all of the answers on that question and you will get a good idea on how to properly display graphics in java. – ug_ Apr 18 '14 at 18:30

3 Answers3

3

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.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
2

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:

Haozhun
  • 6,331
  • 3
  • 29
  • 50
0

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();
}
slaadvak
  • 4,611
  • 1
  • 24
  • 34
  • I've implemented your code however now I am getting an error calling the paint train method inside run. It is saying that the variables need to be final. But if they are final they cannot be changed. Any suggestions? Thanks – user3549772 Apr 22 '14 at 17:42