views:

244

answers:

6

Hello, I have problem creating JTable, which will show every second some text. I make MainView, place JTable and have Class "TableHandler(JTable table) implements Runnable" which is supposed to add some text in intervals to JTable ... Here is run method :

public void run() {
    for (int i=0; i<5; i++) {
        table.setValueAt("text", i, i);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

The problem is, that MainView wont "show" until Jtable is filled with data, so user doesn't see filling :-(

EDIT : more code

public MyView(SingleFrameApplication app) {
    super(app);

    initComponents();
    // log of GUI stuff here
   TableHandler th = new TableHandler(myTable);
   th.createTable(); // just loads data
   Timer t = new Timer(100,new ActionListener() {
   public void actionPerformed(ActionEvent e) {
       th.run();
      }
   });
   t.start();
A: 

Try putting a call to repaint after your setValueAt() call. Preferably wrapped in SwingUtilities.invokeLater.

Seth
thanx, but no changes ...
miso
+1  A: 

The mostly likely problem to the behavior you are describing is that you aren't using a separate thread to start the run method. If the run method is called directly then the screen will not update until the main thread is available again, which will not occur until after the run method completes.

Verify that you are not calling run() anywhere in your code as that should be handled in the thread.

EDIT: The code to start the class should look something like this:

new Thread(new TableHandler(table)).start();
Stephan
this looks promising, but I am not sure if I get your code ...should I write it likeThread t = new Thread(new TableHandler(table)).start(); or what did you mean?
miso
@miso See the link I put in the question comments. It tells you how to thread. When you create a thread, you pass a Runnable type to the Thread constructor as Stephan is showing. Then you call `start` on the Thread object you just instantiated.
Jonathon
If you need to store the thread you can do Thread t = new Thread(new TableHandler(table)); t.start(); but in most cases you don't need direct access to the thread object and can just create the thread and start it inline.
Stephan
Well I am doing it exactly as you suggest, but I am getting "void type not allowed here" error
miso
Jonathon - am I not supposed to create instance of Thread? Why start it from static method?
miso
@miso Nobody mentioned statics...edit your question to show what you've tried and hasn't worked. Show the exact code you're using or no one can tell you how to do it right. The `run` method you've shown isn't enough.
Jonathon
Jonathon - added
miso
A: 

You must use SwingUtilities.invokeLater if you are running it in a thread.

This will just queue up requests to your painting routine, so then you need to be sure they are called by putting a pause. It should look something like this:

in your main--or somewhere equivilent:
    new myThread
    myThread.start()

myThread.run()
   new myRoutine();
   while(activelyUpdating)
       invokeLater(myRoutine);
       sleep(something reasonable, like 1000 for updates every second);


myRoutine
    do all your drawing crap
    return (do not loop here, that's done in the thread.  Just return)

There is probably a way to have your sleep wait until the invokeLater queue is empty, but be sure you sleep as well because otherwise the AWT thread (the thread "myRoutine" will run on) will never get a chance to run and invokeLater threads will just queue up.

Bill K
+3  A: 

If you are using Swing and want to perform an action on a time interval, you should use a swing timer, as described in the tutorial.

Translating your loop into a sequence of calls to actionPerformed, you get:

    new Timer ( 1000, new ActionListener () {
        int i = 0; // moved out of loop 

        @Override
        public void actionPerformed ( ActionEvent event ) {
            if ( i < 5 ) {
                i++;

                table.setValueAt ( "text", i, i );

            } else {
                // stop the timer firing events when the loop end 
                // condition is reached
                ( ( Timer ) event.getSource() ).setRepeats ( false );
            }
        }
    } ).start();
Pete Kirkham
I doit waits ...and eventually makes all "steps" at once :/
miso
@miso Do one update per timer tick, as in the code above.
Pete Kirkham
+1 I think it's better to update it like this
OscarRyz
This is fine for this question but if you have a case where you have exceutors running and want to display results as they come in or other asynchronous info then you are better off with invokeLater(). But, Yes, for this exact question the Swing Timer works.
Clint
A: 

As per you comments.

Call start not run as mmyers points, using the former is the correct.

So it should be:

Thread t = new Thread( this.table ); // or new Thread( this )

t.start(); // call start on the read 

and not

this.table.run();

or

t.run();
OscarRyz
Dude you solved it, thanx many times!I also thank to everyone trying to help, appriciated!
miso
This breaks the Swing threading model, so you've traded a simple bug for a complex one.
Pete Kirkham
Actually it wasn't me, it was mmyers who usually answers in comments. I would've answered something different and more in the line of "Pete" :)
OscarRyz
I answered in comments precisely *because* I was giving the short answer, which solved the immediate problem but wasn't actually a correct solution. (That's why I answer in comments a lot; full answers usually take more time than I can give them.)
Michael Myers
+2  A: 

You need to update from the AWT thread:

public void run() {
    for (int i=0; i<5; i++) {
        final int x = i;
        SwingUtilities.invokeLater( new Runnable() {
          public void run() {
            table.setValueAt("text", x, x);
           }});
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

See http://java.sun.com/javase/6/docs/api/javax/swing/SwingUtilities.html#invokeLater(java.lang.Runnable)

Clint
well, it says that I need to make "i" final ... :))
miso
added final int x = i;
Clint