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?
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.
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 ActionListeners) 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 ActionListeners 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();
}
});
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.
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.
Related
My program is to ping a host infinitely and display each ping result on the GUI as RED/GREEN. Here, 'res' is a label that changes its text and background based on the ping result.
The issue is that when i use while(), the GUI becomes unresponsive and the colour of the label does not change, while the ping service continues. How do i solve this issue??
while (true) {
try {
boolean status = InetAddress.getByName(host).isReachable(timeOut);
if (status == true) {
res.setText("ON");
res.setBackground(Color.GREEN);
} else {
res.setText("OFF");
res.setBackground(Color.RED);
}
} catch (IOException ex) {
}
Start by taking a look at Concurrency in Swing
Essentially, from your description, you are blocking the Event Dispatching Thread, preventing it from process new events, including paint events.
Swing is also NOT thread safe, this means you should not update the UI from outside of the context of the EDT.
In this case, you could use a SwingWorker to perform the physical "ping" from within the doInBackground method and make use of the publish/process methods to update the UI.
See Worker Threads and SwingWorker for more details
I im creating a simple testing app that runs a check every hour on the selected directory/s using thread.sleep() through JFileChooser. But when i select the directory and the method runs the ui panel goes grey and the swing bits disappear. The thread seems to be putting the ui to sleep as well as the method its calling.
if (option == JFileChooser.APPROVE_OPTION) {
selectedDirectory = chooser.getSelectedFiles();
try {
while (true) {
runCheck(selectedDirectory);
Thread.sleep(1000*5);//1000 is 1 second
}
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
e1.printStackTrace();
}
}
Im looking for a way around this issue so that i can print the results of the checks being run in the ui .setText(result)
You are correct about the code putting the UI to sleep. Since sleep is called on the Event Dispatch Thread (the thread responsible for running the gui) the UI stops processing events and 'goes to sleep'.
I think what you want is a javax.swing.Timer.
Timer t = new Timer(1000 * 5, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do your reoccuring task
}
});
This will cause your reoccurring task to be performed off of the EDT, and thus it wont leave your ui unresponsive.
If the code you have posted runs on the EventDispatchThread, then there is no way Swing can redraw the GUI. You're blocking (sleeping in) the thread that's supposed to handle that!
This is because you are running you check in the main GUI thread and are using an infinite loop. This check is a background task and should be executed in it's own thread so that the GUI can still receive and react to input by the user.
You also do not need to write your own implementation, Java has a Timer object.
Edit: There is also a Swing specific Timer object. This will have the action occur in the GUI thread, so if your task is long, it can cause the GUI to still lock up while the action is occurring (but not while it is waiting).
I im creating a simple testing app that runs a check every hour on the selected directory/s using thread.sleep() through JFileChooser. But when i select the directory and the method runs the ui panel goes grey and the swing bits disappear. The thread seems to be putting the ui to sleep as well as the method its calling.
if (option == JFileChooser.APPROVE_OPTION) {
selectedDirectory = chooser.getSelectedFiles();
try {
while (true) {
runCheck(selectedDirectory);
Thread.sleep(1000*5);//1000 is 1 second
}
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
e1.printStackTrace();
}
}
Im looking for a way around this issue so that i can print the results of the checks being run in the ui .setText(result)
You are correct about the code putting the UI to sleep. Since sleep is called on the Event Dispatch Thread (the thread responsible for running the gui) the UI stops processing events and 'goes to sleep'.
I think what you want is a javax.swing.Timer.
Timer t = new Timer(1000 * 5, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do your reoccuring task
}
});
This will cause your reoccurring task to be performed off of the EDT, and thus it wont leave your ui unresponsive.
If the code you have posted runs on the EventDispatchThread, then there is no way Swing can redraw the GUI. You're blocking (sleeping in) the thread that's supposed to handle that!
This is because you are running you check in the main GUI thread and are using an infinite loop. This check is a background task and should be executed in it's own thread so that the GUI can still receive and react to input by the user.
You also do not need to write your own implementation, Java has a Timer object.
Edit: There is also a Swing specific Timer object. This will have the action occur in the GUI thread, so if your task is long, it can cause the GUI to still lock up while the action is occurring (but not while it is waiting).
I have a JProgressBar and want to be able to see it dynamically updated. The progress bar should be able to visibly move from one position to another, not just change without the bar visibly changing (think regular loading bars).
public static void dropHPBar(int before, int after) {
Thread.currentThread.sleep(50);
while (before > after) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
GameMain.gW.setHP(before);
b --;
}
and from GameMain.gW:
public void setHP(int x) {
hpBar.setValue(x);
}
Why is the progress bar not being visibly updated?
Calling Thread.sleep in the EDT (event dispatch thread) prevents UI updates. You need to use Swing Worker in order to archive proper concurrency.
From the Oracle website:
Swing consists of three kinds of threads:
Initial threads, the threads that execute initial application code.
The event dispatch thread, where all event-handling code is executed. Most code that interacts with the Swing framework must also
execute on this thread.
Worker threads, also known as background threads, where time-consuming background tasks are executed.
Tasks on the event dispatch thread must finish quickly; if they don't,
unhandled events back up and the user interface becomes unresponsive.
I've got a problem: I've got a jframe1 who calls at ActionPerformed the jframe2.
JFrames are threads or? and so I've tried in the jframe2 the wait() method, and then I would notify the jframe2's in jframe1..
my code in jframe2 (a method what run, when a button is clicked):
private void read(){
synchronized(jframe1){
try {
if(writer.checkLast() == null){
this.wait();
jLabel.setText(writer.getLast());
}
else{
jLabel.setText(writer.getLast());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
But the Problem is, that if I use this.wait(); in jframe2, my jframe1 is also locked.. what I do wrong?
sry for my bad english, thanks if anyone have got an answer!
Frames are threads or?
No, absolutely not. There is one single thread in which all painting and user input events happen, the Event Dispatch Thread. However, this thread is different from the application's main thread, which is probably what lead you to believe that each frame has its own thread.
Since all events happen on the event dispatch thread, you don't have to do any synchronization, and your frames can call each other's methods without requiring any synchronization or notification. Which is the reason for the single threaded design in the first place (The general consensus is that a multithreaded GUI is nearly impossible to work with).
I get the feeling that you're trying to emulate the behavior of a modal dialog by using the wait() method, but as Michael explains well above, don't call wait on a Swing component and don't use Thread.sleep. Instead if you want to display another window modally use a JOptionPane or a modal JDialog. It's all well explained in the tutorials.