JDK 1.5.0.3. First, here's an SSCCE that works fine
(I know the SSCCE is supposed to demonstrate the problem.
I can't make an SSCCE that does so):
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Test extends JFrame {
private JTextArea textArea;
private JButton goButton;
public Test() {
super("Test");
Container container = getContentPane();
textArea = new JTextArea();
container.add(textArea, BorderLayout.CENTER);
goButton = new JButton("Go");
goButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doSomething();
}
});
container.add(goButton, BorderLayout.NORTH);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400, 400);
setVisible(true);
}
private void doSomething() {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
textArea.setText("This is start text");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException x) { }
textArea.setText("This is end text");
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
JOptionPane.showMessageDialog(Test.this,
"Done", "Done", JOptionPane.INFORMATION_MESSAGE);
}
});
}
public static void main(String[] args) throws Exception {
new Test();
}
}
This SSCCE sets some text in a text area, changes the cursor to
a wait cursor, goes off and does something (in real life, a db
query, here, just sleep), updates the text area, sets the cursor
back to a default cursor, and pops up a message dialog. Like I
said, this works fine.
Now here's my actual method, analogous to doSomething in the SSCCE:
private void invokeSearch() {
final String dbName = (String) comboBox.getSelectedItem();
final String sessionId = sessionIdField.getText();
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
SearchCriteria s = getSearchCriteria(dbName, sessionId);
final String sql = s.generateSQL();
sqlArea.setText(sql);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
cleanUpResults(dbName, sessionId);
int rowCount = executeSQL(dbName, sql);
tableModel.refresh(dbName, sessionId);
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
JOptionPane.showMessageDialog(SearchTest.this,
"Search found " + rowCount + " row" +
(rowCount == 1 ? "." : "s."),
"Search complete",
JOptionPane.INFORMATION_MESSAGE);
}
});
}
This method does essentially the same thing, but in this case, after
I set the text in the text area, and while the invokeLater is off
doing its thing, I get an I-bar instead of the wait cursor. It
doesn't matter if I set it back to a wait cursor after the setText,
it still goes back to an I-bar.
If I comment out the setText it stays a wait cursor. Is there any
reason for the cursor to stay a wait cursor in my SSCCE, but not with
my actual method?
Thanks for any advice.
Larry Coon
University of California
Vova Reznik - 03 Apr 2006 14:46 GMT
> JDK 1.5.0.3. First, here's an SSCCE that works fine
> (I know the SSCCE is supposed to demonstrate the problem.
[quoted text clipped - 105 lines]
> Larry Coon
> University of California
My response is not about a cursor, but about design.
You have long task. It should be executed in different thread not in
EDT. Running db call using invokeLater incorrect (it is EDT).
I'll recommend to read about SwingWorker.
And, finally, game plan:
1. Set wait cursor
2. Start another thread with all stuff you need.
3. After finishing another thread refreshes screen (of course using
invokeLater) and sets default cursor.
Larry Coon - 03 Apr 2006 22:03 GMT
> You have long task. It should be executed in different thread not in
> EDT. Running db call using invokeLater incorrect (it is EDT).
[quoted text clipped - 4 lines]
> 3. After finishing another thread refreshes screen (of course using
> invokeLater) and sets default cursor.
Thanks for the reply, Vova. You're right, I was never clear on the
times you use a SwingWorker vs. invokeLater(). Now that I've read
through Sun's articles on it, it makes more sense. Still, I can't
get it to work the way I want, even when I use a SwingWorker. Here's
the SSCCE, this time illustrating the problem:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Test extends JFrame {
private JTextArea textArea;
private JButton goButton;
public Test() {
super("Test");
Container container = getContentPane();
textArea = new JTextArea();
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.add(textArea, "Text");
container.add(tabbedPane, BorderLayout.CENTER);
goButton = new JButton("Go");
goButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doSomething();
}
});
container.add(goButton, BorderLayout.NORTH);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400, 400);
setVisible(true);
}
private void doSomething() {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// This text no longer appears before the SwingWorker goes
// off and runs its task.
textArea.setText("This is start text");
SwingWorker worker = new SwingWorker() {
public Object construct() {
try {
Thread.sleep(10000);
} catch (InterruptedException x) { }
// Just to give it a return value...
return "This is the end text";
}
};
worker.start();
textArea.setText(worker.get().toString());
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
JOptionPane.showMessageDialog(
Test.this,
"Done", "Done",
JOptionPane.INFORMATION_MESSAGE);
}
public static void main(String[] args) throws Exception {
new Test();
}
}
The idea is that I want the start text to appear in the text area while
it's off doing its thing (a database query in my real problem). With
this version of the SSCCE the cursor looks the way I want, but it no
longer updates the text area with the start text. I've tried various
combinations of setting it using invokeLater, but none of my attempts
workd (I shouldn't have to anyway, should I? I -want- the EDT to do the
update of this Swing component, don't I?).
Larry Coon
University of California
Vova Reznik - 04 Apr 2006 14:52 GMT
>
>> You have long task. It should be executed in different thread not in
[quoted text clipped - 9 lines]
> times you use a SwingWorker vs. invokeLater(). Now that I've read
> through Sun's articles on it, it makes more sense. Still, I can't
read again :)
> get it to work the way I want, even when I use a SwingWorker. Here's
> the SSCCE, this time illustrating the problem:
[quoted text clipped - 74 lines]
> Larry Coon
> University of California
how about:
private void doSomething() {
/*
* Glass Pane is really very important
* You don't want user to type in your text area anything
* so cover your JFrame with glass pane. Much better to create
* your own glass pane extending, for example JPanel, and adding
* Mouse and Key Listeners to block any users input.
*/
final Component c = getGlassPane();
// --- cover frame ---
c.setVisible(true);
c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
textArea.setText("This is start text");
SwingWorker worker = new SwingWorker() {
// --- uses to construct ---
public Object construct() {
try {
Thread.sleep(5000);
} catch (InterruptedException x) {
}
return "This is the end text";
}
// --- executes by EventQueue.invokeLater ---
// --- (the same as SwingUtilities) ---
public void finished() {
textArea.setText("This is the end text");
c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
c.setVisible(false);
}
};
worker.start();
// --- You may do with your GUI anything you want.
}
Larry Coon - 04 Apr 2006 16:47 GMT
> read again :)
:-)
> how about:
> private void doSomething() {
[quoted text clipped - 35 lines]
> // --- You may do with your GUI anything you want.
> }
This does exactly what I want -- thanks Vova!
Larry Coon
University of California