Reputation: 8412
I am working with Java Swing to render my GUI for an application. I have an ActionListener set up on a JButton
to fire off a series of tests. Ideally, I would like to update a status area (currently a JTextArea
) with real-time testing information and results. In other words, the program would set the text area line-by-line as tests are run and completed to something like:
Running Tests... Test 1: Check Something... Success Test 2: Check Something else... Success .. //More testing output All tests have completed successfully. Yay.
The problem is that I can't get the status text to update while my actionPerformed
event method is still running. This is is all fine and dandy when all of the tests complete quickly. However, some of my tests take an indefinite amount of time to complete (think "database connection timeout"). When this happens, users are stuck with whatever text was already in the status area and my application freezes until the tests complete or fail.
I've tried calling repaint
on my JTextArea
every time I update the status, but it seems that just adds it to the event queue (which won't get fired till after my actionPerformed
event completes...doh). I've also tried calling revalidate
and updateUI
, and even tried calling wait()
on the ActionListener
class running my tests, but nothing has worked thus far. Here is the structure of my current code:
..
JTextArea statusOutput_textArea;
..
public void actionPerformed(ActionEvent e) {
setStatusText("Running Tests...");
//Run Tests
int currentTest = 1;
//Check something
appendStatusText(" Test " + currentTest++ + ": Checking Something...");
.. //Check Something Here
appendStatusText(" Success");
//Check something else
appendStatusText(" Test " + currentTest++ + ": Checking Something else...");
.. //Check Something Else Here
appendStatusText(" Success");
//Other tests
.. //Run other tests here
appendStatusText("\nAll tests have completed successfully. Yay.");
}//End of actionPerformed method
public void setStatusText (String statusText) {
statusOutput_textArea.setText(statusText);
statusOutput_textArea.repaint();
}//End of setStatusText method
public void appendStatusText (String statusTextToAppend) {
statusOutput_textArea.setText(statusOutput_textArea.getText() + "\n" + statusTextToAppend);
statusOutput_textArea.repaint();
}//End of appendStatusText method
Any help would be much appreciated :)
UPDATE
For those interested in the general structure of the solution, here it is:
public class RunTestsButtonActionListener implements ActionListener {
JTextArea statusOutput_textArea;
JButton testDatabaseSettings_JButton;
public RunTestsButtonActionListener(JTextArea statusOutput_textArea){
this.statusOutput_textArea = statusOutput_textArea;
}
public void actionPerformed(ActionEvent e) {
testDatabaseSettings_JButton = (JButton) e.getSource();
Thread tests = new Thread(){
public void run() {
//Disable button and add tooltip
testDatabaseSettings_JButton.setEnabled(false);
testDatabaseSettings_JButton.setToolTipText("Running Tests...");
//Run Tests
try {
statusOutput_textArea.setText("Running Tests...");
int currentTest = 1;
//Check something
statusOutput_textArea.append("\n Test " + currentTest++ + ": Checking Something...");
.. //Check Something Here
statusOutput_textArea.append("\n Success");
//Check something else
statusOutput_textArea.append("\n Test " + currentTest++ + ": Checking Something else...");
.. //Check Something Else Here
statusOutput_textArea.append("\n Success");
//Other tests
.. //Run other tests here
statusOutput_textArea.append("\n\nAll tests have completed successfully. Yay.");
} finally {
//Enable button and remove tooltip
testDatabaseSettings_JButton.setEnabled(false);
testDatabaseSettings_JButton.setToolTipText("");
}
}
};
tests.start();
}
}
Upvotes: 2
Views: 2205
Reputation: 921
The problem is you can't have long running tasks happen in an action. As you've noticed, nothing will get updated when this happens.
You should try taking a look at SwingWorker or some other background thread. You would run your tests in the background thread and every time you want to update the UI you would call:
SwingUtils.invokeLater(new Runnable() {
public void run() {
appendStatusText(....);
}
});
Upvotes: 1
Reputation: 1933
You are doing the checks on "the Swing thread", you would be better of performing those tasks in a different thread (which notifies the GUI if something happens/changes).
Upvotes: 2
Reputation: 11381
Run each test in its own thread so they don't block each other. Also, JTextArea already has an thread-safe append
method, no need to use setText
to simulate append
.
Upvotes: 1