GorillaApe GorillaApe - 4 days ago 6
Java Question

Java Swing + Threads

This code draws two lines but waits a second..
I am looking how to do that in a separate thread so it wont freeze the application..
To draw one line and display it to the user and then the second..
Sorry but i am confused.. found too many solutions

public class Askisi2_3 extends JFrame {

private class LineJPanel extends JPanel {

public LineJPanel() {
setSize(500,500);
}

private void drawRandomLines(Graphics g) {
g.drawLine(5, 4, 50, 100);
try{
Thread.sleep(1000);
} catch(InterruptedException ex) {

}
g.drawLine(5, 4, 50, 200);
}

@Override
public void paint(Graphics g) {
super.paint(g);
drawRandomLines(g);
}

}


public Askisi2_3() {
initialiseComponents();
}

private void initialiseComponents() {
JPanel panel = new LineJPanel();

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(panel);
setSize(500, 500);
setVisible(true);
}
}


EDIT

Thank you for your responses!!!
A requirement for this is to use

try{
Thread.sleep(1000);
}


Is this possible ?

Here is my updated code

@Override
public void paint(Graphics g) {
super.paint(g);
for (int i = 0; i < lines.length; i++) {
try{
Thread.sleep(1000);
}catch(InterruptedException e) {

}
g.drawLine(lines[i].getX1(),lines[i].getY1(), lines[i].getX2(), lines[i].getY2());

}
}


Before opening this thread googled it and found about Timer.. But i am forced to use
Thread.Sleep()
.. so is there a solution or not?

So pst you are suggesting to put the sleep outside somehow?

Answer

Short answer

You can use Thread.sleep but not from the paint method. Use it from outside and just reapaint your panel.

Long answer

As it is now, your code paints the panel, and until the pause is finish it returns. Visually it would look like the paint took too much time to finish.

What you need, is to have a "model" to paint. Your component would just paint that model and finish.

Then you add more "things" to your model every second, and that's it.

For instance. Let's say your model is an array of lines:

class Line {
    int x1, y1, x2, y2;
}

class LineJPanel extends JPanel {
// this is the private model
private Line[] lines = new Line[10];
.....

What you need to do in your paint method is to draw those lines:

// exactly as you have them:
@Override
public void paint(Graphics g) {
    super.paint(g);
    drawRandomLines(g);
}
// Changed. Do no "sleep" here, or you'll freeze the GUI
// just draw whatever your model is/has.
private void drawRandomLines(Graphics g) {
    for( Line line : lines ){
        if( line != null ){ 
            g.drawLine( line.x1, line.y1, line.x2, line.y2 );
        }
    }
}

And that's it. That way you won't freeze the GUI.

To add the effect of having more and more lines, you'll create a separate thread and add lines to it.

To keep things simple, you can add that thread in the constructor:

public LineJPanel() {
    setSize(500,500);
    Thread t = new Thread(){
         public void run(){
             while( true ) {
                 // add random lines and repaint
                 // sleep for a while
                 // and repeat.
            }
        }
    };
    t.start();
 }

That should as simple as adding more lines into the "model" ( the array ) and let the component re-paint them.

So to complete the code we could add a addRandomLine method that creates a line, set some random values, and put it in the array:

private void addRandomLine(){
    Line line = new Line();
    line.x1  = random.nextInt(500);
    line.y1  = random.nextInt(500);
    line.x2  = random.nextInt(500);
    line.y2  = random.nextInt(500);
    lines[count++] = line;//put it in the next position
    // if we reach the limit, start all over again 
    // from 0
    if( count == lines.length ){
        count = 0;
    } 
}

So, wrapping up your new thread would look like:

   Thread t = new Thread(){
        public void run(){
            while( true ){
                addRandomLine();
                repaint();
                // and as per the requiement: 
                try{
                    Thread.sleep( 1000 );
                }catch( InterruptedException ie ){}
            }
        }
    };

Be aware that, this would invoke repaint in other thread different to the EDT. To fix this we would use: SwingUtilities.invokeLater which let us define a method to be invoked "eventually" in the EDT:

So, the final code ( with some formatting enhancements from my part ) would be:

import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class Askisi2_3 extends JFrame {

    public Askisi2_3() {
        initialiseComponents();
    }

    private void initialiseComponents() {
        JPanel panel = new LineJPanel();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(panel);
        setSize(500, 500);
        setVisible(true);
    }
    public static void main( String [] args ) {
        new Askisi2_3();
    }
}
// line abstraction
class Line {
    int x1, y1, x2, y2;
}
class LineJPanel extends JPanel {
    // this is the private model
    private Line[] lines = new Line[10];// fixed of size 10 by now.
    // private internal index position
    private int count = 0;

    // generates "random" numbers
    private Random random = new Random();

    // create the panel and start adding more lines in a separate thread.
    public LineJPanel() {
        setSize(500,500);

        Thread t = new Thread(){
            public void run(){
                // forever:
                while( true ){
                    //add another line
                    addRandomLine();
                    // rapaint it
                    SwingUtilities.invokeLater( new Runnable(){
                        public void run(){
                            repaint();
                        }
                    });
                    // sleep for while
                    try{
                        Thread.sleep( 1000 );
                    }catch( InterruptedException ie ){}
                }
            }
        };
        t.start();
    }
    // just draw the model
    private void drawRandomLines(Graphics g) {
        for( Line line : lines ){
            if( line != null ){ 
                g.drawLine( line.x1, line.y1, line.x2, line.y2 );
            }
        }
    }
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        drawRandomLines(g);
    }
    // add another line to the "model"
    private void addRandomLine(){
        Line line = new Line();
        line.x1  = random.nextInt(500);
        line.y1  = random.nextInt(500);
        line.x2  = random.nextInt(500);
        line.y2  = random.nextInt(500);
        lines[count++] = line;
        if( count == lines.length ){
            count = 0;
        } 
    }

} 

The result is a very nice "line animated" panel:

like this

Comments