• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Adding a progress bar to an otherwise non-gui app

 
Ranch Hand
Posts: 334
2
Netbeans IDE Tomcat Server Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I think I'm being bitten by the lack of a thread safe event loop but I have no clue how to fix it.

The program is a Java Class with a main function. It can be called from the command line or embedded in another program or called from Matlab.

What it does is use a proprietary network protocol to transfer a boat load of data and generates a single image summarizing all that data. It can take a while.

So I created a jFrame with JLables telling what it's doing, and estimating how time left and a progress bar to keep people entertained while I max out their network.

Everything works fine except for occasional exception being printed in the console complaining about null pointers in the Event Loop thread:



As far I can tell everything works and there's no problems from this but a program that throws exceptions is not trust-worthy.

I've done most of my Swing programming with SAF but now that that is deprecated, I'm trying program Swing directly and obviously missing something.

My program basically creates a new object from my class derived from JFrame does a setVisible(true). Then calls methods of that class to call setText on a bunch of JLabels and setValue on a JProgressBar. Then calls dispose() when finished.

I suspect I somehow need to get those JFrame update methods inside the event loop but I'm not sure how to do it.

Can somebody point me to something to read?

Writing this post helped me express the problem much more clearly than could before, so now I have a few terms to Google, but I'd still appreciate any advice and criticism on the whole thing.

Joe
 
Bartender
Posts: 5167
11
Netbeans IDE Opera Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Joe Areeda wrote:Can somebody point me to something to read?


Concurrency in Swing
 
Joe Areeda
Ranch Hand
Posts: 334
2
Netbeans IDE Tomcat Server Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks Darryl,

I've been reading this and I seem to need to do things backwards. Not that that is a rare situation for me to be in. I get a lot out of trying to explain my problems so let's see.

In a normal GUI application as described in your link, an initial thread launches the event dispatch thread which may then launch a worker thread to do the processing.

In this case the complication is I would not only like to use the JFrame as an add on to command line programs which normally run in the background but also add it to long running Matlab processes. I could probably implement the Java programs as worker threads but that's not an option with Matlab.

So it seems my only option is to get the initial thread to synchronize with the ProgressBar object to set fields and then have something in the event loop like a timer event actually update the widgets.

I'd appreciate any comments and suggestions.

Joe
 
Darryl Burke
Bartender
Posts: 5167
11
Netbeans IDE Opera Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
When you need to invoke Swing methods and constructors from a Thread other than the EDT, you need to wrap the code in a Runnable queued via SwingUtilities#invokeLater(...) or, rarely, invokeAndWait(...).
 
Joe Areeda
Ranch Hand
Posts: 334
2
Netbeans IDE Tomcat Server Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Darryl Burke wrote:When you need to invoke Swing methods and constructors from a Thread other than the EDT, you need to wrap the code in a Runnable queued via SwingUtilities#invokeLater(...) or, rarely, invokeAndWait(...).


Everything I've read agrees with you.

The trick, I think, is that the only routines that actually call the Swing methods have to be in that thread. So my current and half-baked plan is have thread safe ways to pass data between the worker and EDT and then do the actual update on a timer event, or possibly there is another way to generate an event. For this project a once a second update is fine.

When I get it to work, I'll post some details.

Thanks for your help Darryl!

Joe
 
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
For passing data between a worker thread and the EDT look at javax.swing.SwingWorker.
For a timer, if it needs to interact with the GUI look at using javax.swing.Timer else use java.util.Timer
 
Joe Areeda
Ranch Hand
Posts: 334
2
Netbeans IDE Tomcat Server Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tony Docherty wrote:For passing data between a worker thread and the EDT look at javax.swing.SwingWorker.
For a timer, if it needs to interact with the GUI look at using javax.swing.Timer else use java.util.Timer



Thanks Tony.

I'm struggling with the SwingWorker concept. I tried to explain my confusion above but it's hard to describe what I don't understand.

Matlab use is the one I have no clue how to implement a SwingWorker thread. I have used SwingWorkers before and they are pretty straight forward, at least as far as multi-threading tasks go. But the problem is that they are spawned in the event loop as the result of some action by the user (or at start).

In my case I already have the Matlab event loop running the processing task that I want to spawn the EDT loop to only handle a Progress Dialog. For those of us not familiar with Matlab it uses Swing objects to implement application level GUI but it doesn't give us access to all of the Swing capabilities. In this case it's the progress bar widget and I think they have good reason for that.

I have a plan, no idea if it will work yet. At the very least I should be able to ask semi-intelligent questions when I run into trouble.

Joe
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

I have used SwingWorkers before and they are pretty straight forward, at least as far as multi-threading tasks go. But the problem is that they are spawned in the event loop as the result of some action by the user (or at start).


Any thread can start a SwingWorker, admittedly they are generally used as you describe but they are not restricted to that scenario.

I'm not clear as to how or what data is being passed from MatLab. If you are pulling it then the SwingWorker background thread can do that, if MatLab is pushing it you could use a producer-consumer design pattern to get the data to the SwingWorkers background thread.
 
Joe Areeda
Ranch Hand
Posts: 334
2
Netbeans IDE Tomcat Server Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tony Docherty wrote:

I have used SwingWorkers before and they are pretty straight forward, at least as far as multi-threading tasks go. But the problem is that they are spawned in the event loop as the result of some action by the user (or at start).


Any thread can start a SwingWorker, admittedly they are generally used as you describe but they are not restricted to that scenario.


I understand. I'm trying to work through my confusion.

I think my issue here is I don't see anything special about the SwingWorker vs another Thread class. Since I already have the worker/processing thread working and validated I'm reluctant to change it in the java code example, and I'm not sure it's possible in the Matlab case.

I'm not clear as to how or what data is being passed from MatLab. If you are pulling it then the SwingWorker background thread can do that, if MatLab is pushing it you could use a producer-consumer design pattern to get the data to the SwingWorkers background thread.


Thanks, I go reread the producer/consumer pattern. That is essentially my plan.

The data in the current implementation is being pushed from Matlab to Java and I believe it has to be done that way. It's straight forward to call Java from Matlab and as far as I can tell impossible to call Matlab from Java without setting up some sort of socket listener.

I haven't found any multi-threading in user level Matlab code outside the Parallel Toolbox which cannot be required for this project.

The current implementation has Matlab GUI sets up a bunch of parameters, then on a button push the transfer or crunch function is called which then pushes regular status updates to the Progress Dialog. I'm thinking that's a fairly general pattern and if I can encapsulate all this synchronization into the Progress Dialog class that it will be easiest to reuse.

Joe
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

I think my issue here is I don't see anything special about the SwingWorker vs another Thread class.


SwingWorker is essentially an implementation of a producer-consumer design pattern where the producer is the worker thread and the consumer is the EDT. You do your work on the worker thread and then call publish() to pass some data to the EDT. The EDT runs the process() method to get the published data and update the GUI.

The data in the current implementation is being pushed from Matlab to Java and I believe it has to be done that way.


Ok, so essentially you need to take the data passed to Java by MatLab and pass it to the EDT to update the GUI.
There are a couple of ways I can think of for doing this:
1. For each piece of data you receive from MatLab you can process it and then create a Runnable with the data for the GUI. You pass this Runnable to EventQueue.invokeLater(). The runnable will be executed on the EDT and so can update the GUI.
2. You pass the data directly to your sub-class of SwingWorker. I'm not sure if any thread can call the SwingWorker's publish method or whether is has to be the worker thread but if any thread can then you process the data and pass the GUI data to the publish method(). If you can't use any thread then you can push the data to something like a java.util.concurrent.ConcurrentLinkedQueue and pop it from the queue using the worker thread and then call the publish() method.

 
Joe Areeda
Ranch Hand
Posts: 334
2
Netbeans IDE Tomcat Server Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks Tony and Darryl, I really appreciate the discussion. I think I have the problem solved so I'll described what I did for review and so other might be able to find a solution if they have the same problem.

I am certainly not making any claims that is the best way or the only way to do this. In fact it's only positive is that it is the first way I've found that works.

My issues are a bit different from the general case but not much.

I have a degenerate case of the producer-consumer pattern in that I don't need a queue. If the worker (producer) sets progress to 10% and then sets it again to 20% before the progress dialog (consumer) deals with the first one, it only has to display the last value. We don't care about the missed ones. So I implement it with a value & boolean. I may change it to keep the last value and check if it's changed but I think it's more efficient if less elegant to check a boolean instead of compare strings.

So I have worker thread that already exists when we create the Event Dispatcher Thread (EDT). I have a Progress Bar class that is derived from JFrame that was created in the NetBeans GUI builder.

It's still not clear to me whether I need to implement the Runable interface and use SwingUtilities.invokeLater. Everything seems to work and even does what I expect if I just use new to create the object. I realize it is creating it's own EDT but that's actually what I want. Matlab's EDT is blocked by the processing program.

Now the issue is to only call any Swing methods from inside the EDT not the worker. So the way I implemented this is:

Class implements the ActionListener interface.
Constructor starts a Timer and the ActionPerformed method is the only place that updates any Swing component. It is a series of if statements like:

For each component there is a corresponding access function that looks like:


Then when all the processing is over we have to kill the time and close the window to stop the EDT.



It just ran a twenty minute process that didn't produce any errors and had a very responsive ProgressBar with the timer set at 250ms. No exceptions in the terminal window.

Please poke holes in my implementation, that's the main reason I posted it.

Joe
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

I have a degenerate case of the producer-consumer pattern in that I don't need a queue. If the worker (producer) sets progress to 10% and then sets it again to 20% before the progress dialog (consumer) deals with the first one, it only has to display the last value. We don't care about the missed ones.


Yes, that's is perfectly ok if you are only interested in the current value such as when updating a progress bar.

So I implement it with a value & boolean.


Not sure about the need for a boolean without seeing the whole code.
Do make sure you make proper use of synchronized code blocks if you are accessing variables from different threads. You'll possibly want to declare such variable as volatile as well.

It's still not clear to me whether I need to implement the Runable interface and use SwingUtilities.invokeLater


If your Timer is a javax.swing.Timer then this runs the action listeners on the EDT so as long as all your GUI updates are done by the Timer's action listeners this should work as is.

It just ran a twenty minute process that didn't produce any errors


If only I had a Pound for every time I'd heard someone say that only to have a bug reported as soon as the app goes live.
 
Joe Areeda
Ranch Hand
Posts: 334
2
Netbeans IDE Tomcat Server Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Not sure about the need for a boolean without seeing the whole code.
Do make sure you make proper use of synchronized code blocks if you are accessing variables from different threads. You'll possibly want to declare such variable as volatile as well.


Ah volatile, I've been bitten by forgetting to use that before. Thank you. Is the boolean necessary? Well it could just update everything on every timer tick, there's not much but this way I save a couple of nanoseconds every once in a while.

It's still not clear to me whether I need to implement the Runable interface and use SwingUtilities.invokeLater


If your Timer is a javax.swing.Timer then this runs the action listeners on the EDT so as long as all your GUI updates are done by the Timer's action listeners this should work as is.


If only I had a Pound for every time I'd heard someone say that only to have a bug reported as soon as the app goes live.


I'd settle for a dollar every time I said it and had it fail the next time I ran it. Agreed! There's a lot more testing to do.
 
reply
    Bookmark Topic Watch Topic
  • New Topic