Reputation: 266
I'm currently developing an utility which is executing queries on a MySQL database and I'm currently working on the interface.
When the user clicks on the button "Connect", the status bar (JTextField) text should change to "Connecting...". This works correctly:
connectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
statusBar.setText("Connecting...");
}
}
});
I implemented a function to connect to a database then the "Connect" button is clicked:
connectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Class.forName("com.mysql.jdbc.Driver");
statusBar.setText("Connecting...");
connection = DriverManager.getConnection("jdbc:mysql://" + database);
}
}
});
In this case, the text of the status bar doesn't change to "Connecting..." until the connection is established.
I removed some of the code, like exception handling, for improved readability.
How can I force the text of the status bar to change before the connection is established?
Upvotes: 1
Views: 602
Reputation: 54725
As others have mentioned the connection logic is best performed on a thread other than the Event Dispatch Thread. However, technically this is not the reason why the text field is not updated until the connection is established.
The actual reason why this occurs is that internally Swing components use a data structure to store listeners (in this case ActionListener
s) whereby listeners are notified in reverse order compared to the order they were added. Hence in your example, the ActionListener
that creates the connection is notified prior to the listener responsible for updating the text.
A simple fix would be to merge the two ActionListener
s into a single block of code; there's no reason you need to add multiple listeners. This will of course cause your GUI to block whilst the connection attempt is being made, which is why others have advised using a mechanism such as SwingWorker
to prevent this.
connectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
statusBar.setText("Connecting...");
new SwingWorker<Void, Void>() {
protected Void doInBackground() {
// Called on a background thread.
connectToDatabase();
return null;
}
protected void done() {
// Called on Event Dispatch thread once connect routine has completed.
try {
get(); // Propagate any exceptions back to Event Dispatch thread.
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(null,
"Failed to connect: " + ex.getMessage(),
"Error",
JOptionPane.ERROR_MESSAGE);
}
}
}.execute();
}
});
Upvotes: 3
Reputation: 68907
This is because you are establishing the connection in the EDT (AWT Event Dispatch Thread). While it is making the connection, nothing is done anymore to updating, handling user input and repainting the window on your screen (graphically). This means the whole application seems to be frozen until the connection is established.
So to solve this you have to make the connection in another Thread. Another dirty, not recommended approach is to force the EDT to repaint the screen after changing the text. This is the simplest way to work, but not the neat one.
This would be accomplished by calling repaint();
and then calling update(getGraphics());
. But it is very dirty. I think your screen will even flicker. But this demonstrates the problem nicely. It might be interesting to test this first to see what actually happens.
Upvotes: 1
Reputation: 692181
This question is asked every two days.
If you execute code in the event dispatch thread, you block this threa, and thus prevents it from executing all its repaint actions necessary for the text to appear in the text field.
Long-running, blocking tasks should be run in a background thread, and this thread must no access the Swing components. Use SwingWorker
. Its javadoc explains everything. It also has a link to the relevant section of the Swing tutorial, that you should read.
Upvotes: 2
Reputation: 44250
Establishing a database connection should not be performed in the Event Dispatch Thread. This is preventing your component from updating. Instead, perform the task in a background thread.
If you need to report results while this action is taking place, either use the SwingWorker
class, or update the component using the SwingUtilities
class, in particular, invokeLater
. Both of these will assure that the component(s) are updated on the EDT and that the long-running task takes place elsewhere.
For more information, please read Concurrency in Swing.
Upvotes: 4