views:

485

answers:

3

I'm working on a Java Swing application. I have a button whose action runs a query on a database and then plots the results. These commands are performed from the listener on the Run button. As I understand it, this means that the thread running at this point is from the EventQueue.

Given certain input, I need to halt processing and wait for the user to click on the plot area. I'm struggling with finding a way to do this. I've looked all over, but I can't seem to find a solution that works. I've tried to create a new thread that waits for the user input (I call join on this thread). The problem is that this makes the previous thread (from the EventQueue) wait. For some reason, the mouse clicks are not causing an interrupt so the mouse clicks are never captured.

Any help would be much appreciated. Thanks in advance.

+4  A: 

Let the EventDispatcherThread handle input. Rather, your spawned thread should be the one doing calculations. I built an interactive UI that did something similar a year or two ago, and that's how I accomplished this behavior.

Essentially, you create a producer/consumer model between the GUI and the data processing thread. When an input is fired off, you feed to the consumer thread and resume activity.

Stefan Kendall
All you need to do is create a SwingWorker instance where the long-running database code is in the doInBackground() method and the subsequent UI updates are in the done() method, and execute this SwingWorker thread in your button's ActionListener.
Chris B.
+4  A: 

Use the SwingWorker class.

I built my app before 1.6. If you have access to 1.6, definitely use SwingWorker.
Stefan Kendall
Unfortunately, I'm stuck with 1.5. I have an Intel-based Mac and, last time I checked, 1.6 was not available to me.
Ryan
SwingWorker has been back-ported to 1.5. https://swingworker.dev.java.net/
Nemi
The `SwingWorker` class is great for demos, but I woudln't want to see it used in production code. It cripples the design by tightly coupling UI action on the EDT with non-UI actions off the EDT.
Tom Hawtin - tackline
@Tom Hawtin - What would you suggest for production code then? I agree this is low level, but honestly, when you need to run a long-running task off the EDT, you have little other choice, and in that case a time tested, debugged API is the right way to go.
Nemi
SwingWorker is definitely the way to go. You can get the backport for 1.5 of the 1.6 version here: https://swingworker.dev.java.net/
Chris B.
A: 

It seems like you are doing roughly the right thing but you should not be calling join on the background thread from the UI thread as that will freeze the UI until the background thread has completed.

Here is a highly compressed example of the basic method I would use (if SwingWorker is not available):

class QueryPerformer implements Runnable {
     private volatile boolean plotAreaClicked;

     public void run() {
         // Perform query and process
         while (!plotAreaClicked) {
              try {
                   Thread.sleep(500);
              } catch (InterruptedException exception) {
              }
         }
         // Perform tasks following plot area click
     }

     public void setPlotAreaClicked(boolean plotAreaClicked) {
         this.plotAreaClicked = plotAreaClicked;
     }
}

And in your UI listeners:

private QueryPerformer queryPerformer;

public void actionPerformed(ActionEvent event) {
    // Run button pressed
    queryPerformer = new QueryPerformer();
    new Thread(queryPerformer).start();
}

public void mouseClicked(MouseEvent event) {
    // Plot area clicked
    if (queryPerformer != null) {
        queryPerformer.setPlotAreaClicked(true);
    }
}

Please note that the above is not optimal and some things have not been handled (such as multiple run button presses).

Russ Hayward