• Post Reply Bookmark Topic Watch Topic
  • New Topic

Problems updating JTextField in a new thread

 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I know others have posted about this, but I am still not understanding how this is supposed to work. My thought was to have a LogHandler class to which I would pass a JTextField. Then anywhere I felt the need to pass the status of my operations, I could just update the text. The problem is that I don't see any of the intermediate messages. I have been trying to get JTextField updating in a separate thread, but with no luck. The println shows messages scrolling in the text window, but I don't see them in the text field, just the last one, so I assume they are still all updating at once when the event queue empties. I left my various failed attempts commented in the code.

Could someone help me understand what I am doing wrong?

 
Michael Dunn
Ranch Hand
Posts: 4632
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
wouldn't you be better off using a JScrollPane/JTextArea, set to, say, 2 lines tall,
then it would be just
textArea.append(logString + "\n");

and you can scroll to see earlier messages
 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I was hoping to use the area as a status of where things were at the moment. There are a lot of posts about JTextArea as well, people using append and having all the lines dumped out at once when the event queue was empty. Which is too late for the messages to have meaning. Unfortunately, the posts often just say 'OK fixed it' and did not post the solution. They key is to have the line appear when I call the method to add the line. Which I tried to get working, but without success.
 
Ranganathan Kaliyur Mannar
Bartender
Posts: 1103
10
Java Netbeans IDE Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
you can simply try with JTextArea - but you don't need that UpdateText class - get rid of it. In the writeLog method, append the stuff from within a Swingutilities.invokeLater() like you have done now. But, you have to get rid of the earlier line which is outside this block. If the text area is within a JScrollPane this should work perfectly.
 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks Ranga,

I am not quite where I need to be yet. I made this change to the writeLogField method:



What I had hoped for is-
start a big procedure
do some stuff
- post status to log field
do some more stuff
- post status to log field
finish doing stuff
- post status to log field

What was happening before the change (with the println outside the invokeLater)

do some stuff
message in command prompt window
do some more stuff
message in command prompt window
finish doing stuff
message in command prompt window
message in text field
message in text field
message in text field
which happens so fast I only see the last message in the text field.

Now I am getting
do some stuff
do some more stuff
finish doing stuff
message in command prompt window
message in text field
message in command prompt window
message in text field
message in command prompt window
message in text field

The opposite of what I am after. Which is why I had tried starting a new Thread, which got me into writing that TextUpdate class, but that didn't result in behavior any different from using invokeLater.

So I am still in need of help in understanding how to accomplish my task.
 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
While I still don't know how to get the behavior I am hoping for, I switched to this definition of my method:



Now I see the "Writing..." lines again interspersed with other text in the command prompt window as I run the various operations. I am still seeing only the last message in the logField. I am assuming that means they are all written after the operations finish and so quickly I don't notice them.

I'm still lost as how to get my logField to show the lines as the operations are running.
 
Michael Dunn
Ranch Hand
Posts: 4632
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
did you ever try it like this? (updating the swing component on Swing's EDT)

also, are all those fields/methods necessary to be 'static'?
 
Ranganathan Kaliyur Mannar
Bartender
Posts: 1103
10
Java Netbeans IDE Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I am not really getting it...when you are updating swing components from an outside thread, you just need to do it via SwingUtilities.invokeLater() and it should work. Why do you need another Thread within the writeLog method?
I wrote a small test program with a simulator which passes the current time to the writeLog() method every 3 seconds and it works perfectly for me:

 
Ranganathan Kaliyur Mannar
Bartender
Posts: 1103
10
Java Netbeans IDE Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jon Swanson wrote:
What I had hoped for is-
start a big procedure
do some stuff
- post status to log field
do some more stuff
- post status to log field
finish doing stuff
- post status to log field


And this quote means you are better off using a SwingWorker as explained in the tutorial
 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks again Ranga,

Hopefully, you will bear with me a little longer, as I am not quite getting how I should be working with threads. I think you may have hit upon my problem in that adding a status window was an afterthought and the rest of the program may not be well structured to support it. Though I guess I echo the thoughts from many other postings I have read on this subject in that it is quite frustrating that println() echos my messages exactly when I want them and it seems amazingly more difficult to have that same message appear in a text field at the desired time.

To simplify things a little, the program has an ObservableValue. The user can make changes in the interface and that results in an update of the observable. It then notifies its observers and, depending on what value changed, one or a number of calculations are done, some of which are a bit time consuming, with the net result that other portions of the interface are updated. I had a request to provide feedback to the user between when they made a change and when the GUI was updated with the final values. So I made this LogHandler that, in theory, would allow the behind the scenes processes to write to a JTextField when they started and finished and at appropriate times while they ran.

What happens in practice is that the user makes a change, the processes start up, I see their messages echoed in the Command Prompt window, but not the JTextField, and then when the processes finish, the JTextField updates. This is whether I just have the setText line, the setText as a Runnable I invokeLater, or fire it off as its own Thread. The only difference I see is that if I create a new Thread, the messages no longer echo to the command prompt window until after the other processes finish and Windows starts beeping at me while the processes run.

Thanks for pointing me at SwingWorker. Am I correct that to get the result I am looking for I need to convert all the methods that might need to be called (there are a hundred or so) to extend SwingWorker, so that instead of running in the event thread they are running in another thread which then publishes back to the GUI running in the event thread? The problem is that I doubt most of what I wrote is thread-safe.

I am running the whole GUI using invokeLater. Is part of the problem that I am trying to setText with invokeLater in the middle of a method that is running in the event queue, resulting in the updates not occurring until the end?

I'll try to put an example together that doesn't use an observable and see if I can better illustrate what I see happening. I'm on other non-Java/non-programming projects for the rest of the week. So it will be early next week.
 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I got sidetracked this week. But here is what I was doing that did not work.

This a simple test that is supposed to post status as the operations are proceeding. What I see is that the "Took some time" messages appear in the Command Prompt windows as desired (at the end of each section of calculations). The messages coming from the LogHandler are all spit out at the end, so in the text field, the only message I see is "Done." That doesn't provide any help to the user in knowing how far along the calculations have gotten.



This is the LogHandler

 
Darryl Burke
Bartender
Posts: 5154
11
Java Netbeans IDE Opera
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jon, have you ever gone through the tutorial on Concurrency in Swing?
 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I tried to go through the tutorials on concurrency and threads. I can write things that use threads. But unfortunately, something is not clicking that would give me that 'aha' moment that I understood it, versus just writing it.

Here is one thing that I wrote.



It uses the same LogHandler that I used before that uses invokeLater to put the writing to the text field in the event dispatch queue. If you run this program, it does more or less what I was saying I wanted- the text field now updates each time a piece of the TakeThreeTime method runs and they don't all just appear at the end.

BUT, notice I added the business about the glasspane and the MouseListener. I did that to prevent the GUI from being used until the TakeThreeTime method finished, as it would cause trouble otherwise (at least in the real code). So you'd think (or I'd think, anyway) it would be more sensible to be running everything in the same thread. Which is why I was keeping everything in event dispatch thread. But I had the idea that things would be invoked in order, that is, I could call TakeTime and then perform setText on a textField and then call TakeTime again and the text field would be updated before the second invocation of TakeTime. Why it doesn't I don't understand, especially since a println has the behavior I was expecting, in that it occurs between TakeTime calls, but the text field does not change.

It was suggested I wrap the setText in an invokeLater, which I did, and I think it had the expected effect, which was that everything wrapped in the invokeLater occurred after all the other calls, even the println.

So after all that I still don't get what is special about updating the text field over writing a line to the command console.

 
Paul Clapham
Sheriff
Posts: 21876
36
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jon Swanson wrote:So after all that I still don't get what is special about updating the text field over writing a line to the command console.


Updating the text field is a Swing operation, since it changes the GUI, so you have to make sure that it's done according to the Swing threading rules. Whereas writing a line to the console isn't a Swing operation so the Swing threading rules don't apply.
 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for all the replies, I understand things better than I did. It seemed after the discussions that my original idea should have worked. So I modified my examples again to make them conform to what I was doing in my actual program (or so I thought).





This works fine. I hit "Press Me" and the TakeTimeThree method is called. Nothing happens when I press "Press Me" again, until the TakeTimeThree method finishes. After each TakeTime call, the text field updates.

The original version of my LogHandler did just what LogHandlerSimple does above. I started the GUI using invokeLater to put it on the EDT. The GUI has an observable. It notifies a bunch of observers. The observers make calls to the LogHandler, but I don't see any change in the LogField until they have all completed their tasks. My example is too simple to illustrate my problem, but at this stage, I don't know what piece of the puzzle to show you that would reproduce it in a simple example.
 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Oops.



I forgot to update main. So this simple case does not work, same as the GUI. Is there something I can read that will help me understand Swing threading rules better? It seems odd that the only way to update a text field is to run code in another thread and block the GUI while the thread is running, rather than running sequentially in a single thread. The Sun/Oracle docs are just not doing it for me.
 
Ranganathan Kaliyur Mannar
Bartender
Posts: 1103
10
Java Netbeans IDE Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Why haven't you still tried using SwingWorker? You can try that with this small program. It works perfectly for me.
 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ranga,

Did you mean to post some code?

One of my requirements is that until the processing is finished, the user should not be able to interact with the GUI. While I can understand why in many applications, this would be undesirable. In my case I am doing scientific calculations and there is no sensible action the user can take until they finish and display a result. I am also concerned that since I originally implemented an Observer/Observable model, that I will run into problems if I allow the possible calculations that might occur to occur in parallel. I've seen at least one post related to problems with this model and multiple threads.

I'm not seeing any way to send all my non-GUI processing to a single persistent thread, so that they are implemented in the order that they are started. I did post a test case of mine that blocks the GUI when I start a second thread. For the real case, I think I would need to rewrite a lot of methods so they would synchronized. Does that sound right?
 
Paul Clapham
Sheriff
Posts: 21876
36
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jon Swanson wrote:The observers make calls to the LogHandler, but I don't see any change in the LogField until they have all completed their tasks.


Yes. That's an excellent demonstration of the way it works: All of that long-running code runs in the Swing EDT thread, so any updates to the GUI while that's going on don't get finalized in the view until all of it has finished.

So, Rule Number 1: Don't do long-running things in the EDT thread. I'm sure the Swing threading tutorial says that.

I'm not seeing any way to send all my non-GUI processing to a single persistent thread, so that they are implemented in the order that they are started.


Well, if you were looking for Swing to provide that, you have been misled. Seems to me that a ThreadPoolExecutor with a single thread would take care of that requirement.
 
Paul Clapham
Sheriff
Posts: 21876
36
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Paul Clapham wrote:
I'm not seeing any way to send all my non-GUI processing to a single persistent thread, so that they are implemented in the order that they are started.


Well, if you were looking for Swing to provide that, you have been misled. Seems to me that a ThreadPoolExecutor with a single thread would take care of that requirement.


Actually, now that I read the docs for ThreadPoolExecutor a bit, they say that Executors.newSingleThreadExecutor() is the usual answer to that question.
 
Paul Clapham
Sheriff
Posts: 21876
36
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
So, in summary:

(1) Don't do your long-running processing on the Swing EDT thread; start another thread to do it, or use an Executor if you have a list of tasks to run.

(2) In that long-running processing, if you want to update the GUI, wrap that update in a call to SwingUtilities.invokeLater().
 
Jon Swanson
Ranch Hand
Posts: 220
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for steering me toward ThreadPoolExecutor. I didn't come across this in previous google searches about threads, but now I know what to look for there is a lot of information.

Originally I was told the slowest processor would be a 2GHz i3, where the process runs in about 100ms (actually a bunch of processes set in motion by a change in the observable). Then they decided to use AMD E350 machines, then they decided to deploy on Pentium M machines. And they added a "high accuracy" mode that takes 20x longer. That's what really did in my original design.

It still confuses me a bit that none of the setText commands from any of the 10 or so methods that are triggered by the change in the observable get executed until the last one is done. Would there be any simple explanations of how this works in the EDT or is this something that I either need to devote the time to really learn the internals of the EDT or just be pragmatic and take for granted that is what happens?
 
Ranganathan Kaliyur Mannar
Bartender
Posts: 1103
10
Java Netbeans IDE Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jon,
Many of your replies seems to indicate that you have not gone through the worker tutorial. Particularly, this page deals with how to handle the publishing of interim results which perfectly fits your scenario.
You requirement of not allowing user to work on the GUI until the processing is done is perfectly understandable. Have you considered using Progress bars to indicate the progress?
This page shows how to do that. Also, in the same page, it has been explained how to use a ProgressMonitor instead - though I have never used that.

And I modified your code to use SwingWorker thus:
 
Don't get me started about those stupid light bulbs.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!