I have a SwingWorker thread that launches a modal dialog box (from a property change listener that listens to the StateValue of started) and the swing worker proceeds to do its work. However, it looks like the done method is not called because that is called on the EDT but the swing worker's modal dialog is blocking the EDT. So, I can't close the dialog from the EDT (or from the done method). Right now I'm just closing the dialog from the doInBackground at the end of that method, but that seems a little unsafe from the doInBackground since it's not on the EDT. What's the best way to handle this? thanks.
The dispatch loop should continue to dispatch the events associated with SwingWorker even when a modal dialog is displayed.
This works for me.
import javax.swing.*;
public class Unions {
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
runEDT();
}});
}
private static void runEDT() {
final JDialog dialog = new JDialog((JFrame)null, true);
new SwingWorker<Void,Void>() {
#Override protected Void doInBackground() throws Exception {
// But this is working.
Thread.sleep(3000);
return null;
}
#Override protected void done() {
dialog.setVisible(false);
}
}.execute();
dialog.setVisible(true);
}
}
For reference:
When a modal dialog is launched in Swing, the execution of that thread is stopped until the dialog is closed.
This is why your done() method was never called (doInBackground() couldn't finish and done() is only called after that).
Opening a modal-dialog from an action called by the EDT thread is slightly different. The EDT itself will continue to process events but the actual event thread code (the code of the action) which opens the modal dialog still gets blocked (and waits until the dialog is closed).
Naturally, in case of non-modal dialogs, this problem never surfaces.
By the way: You should never open a dialog from outside the EDT.
If the decision is made on a non-EDT thread, you need to use SwingUtilities.invokeLater() to actually open the dialog.
Sounds complicated but it is actually not, once you master the concept of the EDT.
Related
I originally was attempting to update a JFrame and JPanel several times while in a Java Action Listener, but both would only update when the Action Listener completed all its tasks. Here is the link to my original question (Refreshing a JFrame while in an Action Listener).
I was told in the feedback to that question that Swing Worker should solve my problems. However, when I implemented Swing Worker (as seen below), nothing changed. The JFrame and JPanel still updated only when the Action Listener completed all tasks. My question is, am I missing something below? If not, how can I implement this in an Action Listener to properly update the Frame and Panel timely?
#Override
protected Integer doInBackground() throws Exception{
//Downloads and unzips the first video.
if(cameraBoolean==true)
panel.add(this.downloadRecording(camera, recording));
else
panel.add(new JLabel("Could not contact camera "+camera.getName()));
panel.repaint();
jframe.repaint();
return 1;
}
private JLabel downloadRecording(Camera camera, Recording recording){
//does a bunch of calculations and returns a jLabel, and works correctly
}
protected void done(){
try{
Date currentTime = new Timestamp(Calendar.getInstance().getTime().getTime());
JOptionPane.showMessageDialog(jframe, "Camera "+camera.getName()+" finished downloading at "+currentTime.getTime());
}catch (Exception e){
e.printStackTrace();
}
}
You have a misundertanding about how SwingWorker works. This class is intended to provide a way to update the GUI while heavy tasks are being performed. All of this is because Swing components updates take place in the Event Dispatch Thread (a.k.a. EDT) which is a particular thread.
For instance, if you click a button and perform a time consuming task all in the EDT, then this thread will block untill this task finishes. Consequently, you'll see your GUI is frozen.
Keeping this in mind, doInBackground() method runs in another different thread that's not the EDT which is ok. So don't call any Swing method in there:
protected Integer doInBackground() throws Exception{
//Downloads and unzips the first video.
if(cameraBoolean==true) // just use if(cameraBoolean), since this is a boolean
panel.add(this.downloadRecording(camera, recording)); // NO!
else
panel.add(new JLabel("Could not contact camera "+camera.getName())); //NO!
panel.repaint(); //NO, never!
jframe.repaint();//NO, never!
return 1;
}
Add a JLabel to this panel before executing your SwingWorker and update its text using publish() and process() methods instead:
JPanel panel = new JPanel();
final JLabel progressLabel = new JLabel("Some text before executing SwingWorker");
panel.add(progressLabel);
SwingWorker<Integer, String> worker = new SwingWorker<Integer, String>() {
#Override
protected Integer doInBackground() throws Exception {
if(cameraBoolean){
pubish("Starting long process...");
//Some processing here
publish("Intermediate result to be published #1");
//Some other processing stuff
publish("Intermediate result to be published #2");
//And so on...
return 0;
} else {
publish("Could not contact camera "+camera.getName());
return -1;
}
}
#Override
protected void process(List<String> chunks) {
for(String string : chunks){
progressLabel.setText(string);
}
}
#Override
protected void done() {
progressLabel.setText("Finished!!!");
}
};
worker.execute();
Both process() and done() methods take place in the EDT so it's safe make GUI updates there. Take a look to this excelent example: Swing Worker Example for more details.
Maybe because you repaint your panel/frame just when the synchronous call this.downloadRecording(camera, recording) is finished?
Try to only put this call into the doInBackground() method, because (so I guess) that's the one that takes a long time and for all this time the JFrame gets not refreshed.
You can't update UI in next way:
panel.repaint();
jframe.repaint();
In your doInBackground method you must to call publish(V... chunks) method, that Sends data chunks to the process(java.util.List<V>) method.(according docs) and than in method process(List<V> chunks) you can update your UI(according docs process method - Receives data chunks from the publish method asynchronously on the Event Dispatch Thread.). SwingWorker docs.
So, override process method for updating, and call publish method.
Also you can use Executors for background processes. In this case your UI will be working in EDT and your background process in another thread. Example:
Executors.newSingleThreadExecutor().execute(new Runnable() {
#Override
public void run() {
// run background process
}
});
EDIT: good example of SwingWorker
Details:
I have a JAVA application that takes some time to shutdown. There is a call to close a port, that takes a really long time. I want to add a dialog box that indicates to the user that the application is shutting down. Normally, I would create a dialog box, start a thread to do long work and close dialog, then display the dialog. Once the work is done, the dialog would be close. This does not work for shutting down an application because it seems the window listener closes all windows (kind of makes sense, it supposed to do that). I'm not sure a way around this.
Code:
public void windowClosing(WindowEvent we)
{
shutDown();
}
public void shutdown()
{
final JDialog dialog = createDialog();
Thread t = new Thread
{
public void run()
{
saveProperties();
ClosePort();
dialog.setVisible(false);
System.exit(0);
}
};
t.start();
dialog.setVisible(true);
}
t.setDaemon(true);
as daemon threads stay alive even if the rest is gone.
I have a Window Listener on the main JFrame of my application. I also have a button listener on a button within the application. I used this as the pattern for the button listener:
good.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
// We're going to do something that takes a long time, so we
// spin off a thread and update the display when we're done.
Thread worker = new Thread() {
public void run() {
// Report the result using invokeLater().
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for(int i=0; i<1000000; i++){
System.out.println("foo");
}
}
});
}
};
worker.start(); // So we don't hold up the dispatch thread.
}
});
When I click on the button, I see the printout for each iteration, but the window listener is not triggered until after the loop has finished executing. The print 'foo' is just there to simulate something that takes a while, and I want the listener to be triggered as soon as the window event occurs (which could be somewhere in the middle of the run() method execution), but it seems like it's not being triggered until the end of the for loop.
Any idea why?
Swing is single-threaded - the Event Dispatch Thread (EDT) manages painting and events for Swing. Any long running tasks placed on the EDT will prevent the EDT from doings tasks until the long running task is complete (in other words, the UI will seem like it has locked up). Here, your code dispatches the long running process (the for loop) onto the EDT using SwingUtilities - if you have a long running task and want a responsive Swing UI during the process, then place your long running task into it's own Thread, or use a SwingWorker.
So, all this...
// Report the result using invokeLater().
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for(int i=0; i<1000000; i++){
System.out.println("foo");
}
}
});
Will do is, is cause the loop to be executed within the context of the Event Dispatching Thread, which will prevent the EDT from processing the event queue, including paint request.
In your case, it might be easier to use a SwingWorker which you can use to publish/process information from the background thread to the EDT as well as supports progress notification
See How to use SwingWorker for more details
I'm developing a Swing app, and I need to run an infinite loop in the background (which runs until: 1) the cancel button of my JDialog is selected or 2) the input data it is searching for is found) while a modal dialog shows an indeterminate progress bar.
Something I've noticed is that if the JDialog is modal, then the SwingWorker will not execute its tasks until the JDialog is closed (and releases its deathgrip on the EDT, I guess...?). If the JDialog is not modal, then SwingWorker's tasks will execute happily in the background.
I've been doing some research, but I'm no thread/EDT expert and am having a hard time figuring the reason/solution.
Any input on this situation/threads/EDT/SwingWorker, or a suggested solution, would be greatly appreciated.
(Question pulled directly from: http://www.coderanch.com/t/346275/GUI/java/SwingWorker-Modal-JDialogs)
I tried the solution regarding the setVisible call of the JDialog like this user found to be the solution, but I still can't execute both threads simultaneously. Any help would be appreciated.
Relevant:
public Dialog(JFrame parentFrame, String equipmentName) {
super(parentFrame, "Progress");
this.hasRequestedCancel = false;
this.equipmentName = equipmentName;
add(createMainPanel());
setIconImage(Toolkit.getDefaultToolkit().getImage(SomeClass.class.getResource(ICON_PATH)));
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
setModalityType(ModalityType.DOCUMENT_MODAL);
pack();
setSize(550, 100);
setResizable(false);
setLocationRelativeTo(parentFrame);
setVisible(true);
}
And
SwingWorker<File, Void> worker = createSwingWorker(params, ...);
worker.execute();
And
private SwingWorker<File, Void> createSwingWorker(final File someFile, final SomeClass asdf, final String param3) throws IOException {
SwingWorker<File, Void> swingWorker = new SwingWorker<File, Void>() {
#Override
protected File doInBackground() throws IOException {
Dialog progressBar = new Dialog(SomeClass.this, SomeClass.this.equipManufacturerDevice);
try {
while(!someFile.exists() && !progressBar.hasRequestedCancel()) {
Thread.sleep(SomeClass.SLEEP_DURATION);
System.out.println("yo");
}
} catch (InterruptedException e) {
}
...
}
#Override
protected void done() {
...
}
};
return swingWorker;
}
The problem is that you are calling setVisible(true) inside the Dialog’s constructor which is a discouraged practice anyway (you just found one reason, why).
Separate the creation and opening of the dialog and you don’t have that problem anymore. The following sample code demonstrates how this can be achieved:
final Dialog d=new Dialog((Window)null);
d.setSize(300, 300);
d.setModal(true);
new SwingWorker<Object,Object>() {
#Override
protected Object doInBackground() throws Exception {
System.out.println("long running stuff");
TimeUnit.SECONDS.sleep(10);
System.out.println("end of long running stuff");
return null;
}
#Override
protected void done() {
d.dispose();
}
}.execute();
System.out.println("before setVisible(true)");
d.setVisible(true);// will block
System.out.println("after setVisible(true)");
What if you moved the data input logic from the main frame and kept it running on a separate, dedicated, background thread whose sole job is to listen for connections and handle them. This would leave your parent JFrame to handle UI interactions thereby giving you the freedom to freeze it when one of your JDialog has focus.
I have a problem while creating a JProgressBar which is set to indeterminate.
The following code is my implementation of the JProgressBar and is called/constructed from another class:
public class Progress implements Runnable
{
private JFrame frameProgress;
private JProgressBar progressBar;
public Progress(String title, String message)
{
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e)
{
GlobalVariables.LOGGING_logger.error("Error instatiating progress bar.",
e);
}
UIManager.put("ProgressBar.selectionForeground", Color.black);
UIManager.put("ProgressBar.selectionBackground", Color.black);
this.frameProgress = new JFrame(title);
this.frameProgress.setIconImage(GlobalVariables.GUI_icon.getImage());
this.frameProgress.setSize(300, 60);
this.frameProgress.setLocation(16, 16);
this.progressBar = new JProgressBar();
this.progressBar.setStringPainted(true);
this.progressBar.setString(message);
this.progressBar.setIndeterminate(true);
this.frameProgress.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frameProgress.add(this.progressBar);
this.frameProgress.setResizable(false);
this.frameProgress.setVisible(true);
}
public void start()
{
new Thread(this).start();
}
public void close()
{
this.frameProgress.dispose();
this.frameProgress = null;
this.progressBar = null;
}
#Override
public void run()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// do nothing, because progress bar is indeterminate
}
});
}
}
The caller of this JProgressBar is the following code snippet:
Progress p = new Progress("bla", "blub");
p.start();
boolean successfull = xmlWriter.writeCommonSettingsFromGUI(this);
p.close();
And now i want, while the xmlWriter.writeCommonSettingsFromGUI(this); is doing something, that the JProgressBar is shown to the user and is working while the algorithm is running.
How can I achieve this? I don't know so much about threading and searched in many other forums, but I don't found any answer for my question.
Please help me and thank you in advance ;)
EDIT:
The Progress JFrame opens up with no content for that time, the algorithm is running.
You are probably facing concurrency issues with Swing. Assuming that the following code runs on the EDT (Event Dispatching Thread):
Progress p = new Progress("bla", "blub");
eventually, this will open a JFrame with a progress bar in it.
I would consider using a JDialog instead of a JFrame
I would not force the size of the JFrame, but rather call pack()
Then, still running on the EDT (and thus blocking all UI-events such as repaint, mouse clicks, etc...), you call p.start() which starts a new Thread() which will invoke run() which itself calls
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// do nothing, because progress bar is indeterminate
}
});
This basically won't do anything except push an additional event on the EventQueue and it will run after all currently pending events. This event will run... "nothing" since your Runnable is just empty. The new Thread dies almost immediately. So all this code is useless.
Still pursuing on the EDT, you call boolean successfull = xmlWriter.writeCommonSettingsFromGUI(this); (btw, "successful" ends with only one 'l'). This will continue on blocking the EDT, preventing repaints from occurring and preventing the JProgressBar from painting itself. Eventually you will dispose the JFrame but since all this code is running on the EDT, the user will not see much of the progress bar and the UI will look frozen.
Consider reading the Swing tag wiki (especially the very last part with 3 important links).
Using a SwingWorker should help you out in this.