I am writing a piece of code using Java Swing. Basically what it does is that it processes some lengthy task. While the task is running, I want to have a waiting pop-up window with a GIF image in it.
My question is that
final InfoDialog infoDialog = new InfoDialog("Parsing file: " + fileToBeUploaded.getName());
final File finalFileToBeUploaded = fileToBeUploaded;
class FileParsingWorker extends SwingWorker<Void, String> {
#Override
protected Void doInBackground() throws Exception {
String text = fileParsers.parseFile(finalFileToBeUploaded);
publish(text);
return null;
}
#Override
protected void process(List<String> chunks) {
infoDialog.setVisible(false);
}
}
infoDialog.setVisible(true);
FileParsingWorker fileParsingWorker = new FileParsingWorker();
fileParsingWorker.execute();
The InfoDialog is the small UI pop-up window with a GIF animation in it. Basically, I put the lengthy task in the worker but the UI's setVisibles in two places. I am thinking if there is any ways I can run the InfoDialog UI in a thread so that I can reuse that bit of code?
The problem I have is that I want to try to run the InfoDialog indefinitely until I deliberately stop it. If I put setVisible(true) in a thread, that thread immediately terminates and my UI won't be updated.
Can someone show me how to do this?
Please have a read on Concurrency in Swing specifically The Event Dispatch Thread. This is the thread on which all Swing components should be created and manipulated. i.e:
SwingUtilities.invokeLater(new Runnable () {
#Override
public void run() {
final InfoDialog infoDialog = new InfoDialog("Parsing file: " + fileToBeUploaded.getName());
final File finalFileToBeUploaded = fileToBeUploaded;
...
infoDialog.setVisible(true);
FileParsingWorker fileParsingWorker = new FileParsingWorker();
fileParsingWorker.execute();
}
});
Also I think another problem is you set the dialog back to invisible in overriden process(List<String> chunks) of the Swing worker, thus as the first chunk is read the dialog will be closed. I think Swing Workers done() method might be more what you want, and its executed on EDT:
class FileParsingWorker extends SwingWorker<Void, String> {
#Override
protected Void doInBackground() throws Exception {
String text = fileParsers.parseFile(finalFileToBeUploaded);
publish(text);
return null;
}
#Override
protected void process(List<String> chunks) {
//each chunk will get processed here
}
#Override
protected void done() {//when Swing worker is finished this method is called
infoDialog.setVisible(false);
}
}
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
I have an action added to a JButton created, following is my code
private void myButtonActionPerformed(java.awt.event.ActionEvent evt) {
txtResult.setText("");
myButton.setText("Working ...");
myButton.setEnabled(false);
myButton.repaint();
System.out.println("Doing Action ...");
SwingUtilities.invokeLater(new Runnable() {
public void run() { // some code inside that is memory intensive
}
});
segmentButton.setText("Original Text");
segmentButton.setEnabled(true);
}
While I am able to see the system out, my component is not getting updated at all, more over I am unable to update any other component on the JFrame as if the whole thread is blocked
The answer was that the main thread gets blocked due to the singular nature of swing main thread.
"The Swing single-thread rule: Swing components and models should be
created, modified, and queried only from the event-dispatching
thread."
—Java Concurrency in Practice.
I have updated my code to accommodate the blocker code in a SwingWorker as described below
private void myButtonActionPerformed(java.awt.event.ActionEvent evt) {
txtResult.setText("");
myButton.setText("Working ...");
myButton.setEnabled(false);
myButton.repaint();
System.out.println("Doing Action ...");
SwingWorker worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
//Memory intensive code
}
#Override
protected void done() {
segmentButton.setText("Original Text");
segmentButton.setEnabled(true);
super.done(); //To change body of generated methods, choose Tools | Templates.
}
};
worker.execute();
}
Thanks #copeg for putting me on the right direction.
I am having a hard time to use the Swing worker to work with my project. It has two programs, one is the logic (full program) and the other is GUI. I am calling the logic program from the GUI. And because of its unresponsiveness, I tried using Swing worker. But even if I use Swing worker, its still unresponsive. If I run the program, it displays the GUI, but if I click on start, the another program starts and it becomes unresponsive.
This the snippet of GUI program (full program):
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
state.setText("Listening");
System.out.println("Started Listening");
state.setBackground(new Color(51, 204, 0));
doRun(args);
}
});
public void doRun(String[] args) {
SwingWorker<Void, String> worker = new SwingWorker<Void, String>(){
#Override
protected Void doInBackground() throws Exception {
// Object to use from another program
HelloWorld obj = new HelloWorld();
obj.main(args);
return null;
}};
worker.execute();
}
Because it requires interaction, it may be inconvenient to run HelloWorld#main() in the background. As suggested here, instantiate a LiveSpeechRecognizer directly in your SwingWorker and publish() interim results for display in the GUI. You can specify Configuration information in the SwingWorker constructor or pass it as a parameter. In outline based on examples here and here,
private class BackgroundTask extends SwingWorker<Void, String> {
LiveSpeechRecognizer recognizer;
public BackgroundTask() {
statusLabel.setText((this.getState()).toString());
Configuration configuration = new Configuration();
configuration.setAcousticModelPath("resource:/edu/cmu/sphinx/models/en-us/en-us");
configuration.setDictionaryPath("resource:/edu/cmu/sphinx/models/en-us/cmudict-en-us.dict");
configuration.setLanguageModelPath("resource:/edu/cmu/sphinx/models/en-us/en-us.lm.dmp");
recognizer = new LiveSpeechRecognizer(configuration);
recognizer.startRecognition(true);
}
#Override
protected Integer doInBackground() {
while (!isCancelled()) {
SpeechResult result = recognizer.getResult();
List<WordResult> list = result. getWords();
for (WordResult w : list) {
// get information to publish, e.g. getPronunciation()
// publish(getSpelling());
}
}
}
#Override
protected void process(java.util.List<String> messages) {
statusLabel.setText((this.getState()).toString());
for (String message : messages) {
textArea.append(message + "\n");
}
}
#Override
protected void done() {
recognizer.stopRecognition();
statusLabel.setText((this.getState()).toString() + " " + status);
stopButton.setEnabled(false);
startButton.setEnabled(true);
bar.setIndeterminate(false);
}
}
I have to perform two tasks. I like two threads perform each task simultaneously. The tasks don't share data.
Before the tasks start, is shown a dialog with a info "Wait, processing...".
Here the codes:
final JDialog dialog = new JDialog(this, true);
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
// Do the job
return null;
}
#Override
protected void done() {
// Must close dialog? The other finished?
}
};
SwingWorker<Void, Void> worker2 = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
// Do the job
return null;
}
#Override
protected void done() {
// Must close dialog? The other finished?
}
};
worker.execute();
worker2.execute();
dialog.setVisible(true);
// Must close dialog?
I would like to close the dialog only when the two threads ended. How to know when they ended? Who and when should close the dialog?
Update: the threads must run simultaneously, not in sequential mode.
Create a CountDownLatch, set to 2
Create your two SwingWorkers, passing each a reference to the CountDownLatch. In there done methods, call countDown on the latch. Do this in the done method, as it will be called regardless of how the doInBackground method exited (ie in case it throws an Exception)
Create a third SwingWorker, passing it a reference to the CountDownLatch, in this worker wait for the latch in the doInBackground method. Once this SwingWorker's done method is called, you should now be able to dispose of the dialog safely
You should call get() on both workers
For now I have made a sample code which will help you to understand the logic behind this.
import java.awt.BorderLayout;
import javax.swing.*;
public class DemoTest {
JFrame frame = new JFrame();
JLabel lbl1 = new JLabel();
JLabel lbl2 = new JLabel();
SwingWorker<Void,Void> worker1 = new SwingWorker<Void,Void>()
{
#Override
protected Void doInBackground() throws Exception {
for(int i = 0;i<=100;i++)
{
lbl1.setText("Counter1 Value:"+Integer.toString(i));
try
{
Thread.sleep(100);
}
catch(Exception e)
{
e.printStackTrace();
}
}
return null;
}
#Override
protected void done()
{
lbl1.setText("Thread 1 completed its job");
worker2.execute();
}
};
SwingWorker<Void,Void> worker2 = new SwingWorker<Void,Void>()
{
#Override
protected Void doInBackground() throws Exception {
for(int i = 0;i<=100;i++)
{
lbl2.setText("Counter1 Value:"+Integer.toString(i));
try
{
Thread.sleep(100);
}
catch(Exception e)
{
e.printStackTrace();
}
}
return null;
}
#Override
protected void done()
{
lbl2.setText("Thread 2 completed its job");
}
};
public DemoTest()
{
frame.setSize(400,400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(lbl1,BorderLayout.NORTH);
frame.add(lbl2,BorderLayout.SOUTH);
frame.setVisible(true);
try
{
worker1.execute();
}
catch(Exception e)
{
e.printStackTrace();
}
//close dialog box
}
public static void main(String []args)
{
DemoTest d = new DemoTest();
}
}
I would use something like a counting lock for this. It is definitely using the least possible resources. The class below is a counting lock. Basically you initialise it with the constructor and specify the number of threads you need to wait for.
In the main thread (or UI thread) you call "waitForAll()" once you are done with setup. You can see that waitForAll is basically waiting for a notify from any other thread. If a notify is received it checks whether or not the number of active workers has reached zero. If the number of active workers is still greater 0 it waits again.
The workers however call unlock() on the lock. Notify decreases the counter by one and calls notify() which makes the main thread wake up and perform the above mentioned procedure.
public class CountingLock {
private int counter;
/**
* Number of workers
*
* #param n
*/
public CountingLock(int n) {
this.counter = n;
}
/**
* Wait until counter == 0
* #throws InterruptedException
*/
public synchronized void waitForAll() throws InterruptedException {
while(counter > 0) {
this.wait();
}
}
/**
* Deduce counter and notify
*/
public synchronized void unlock() {
this.counter--;
this.notify();
}
}
In the dialog prior launching the threads do the following:
CountingLock lock = new CountingLock(2);
/** put your thread setup code from your example here */
lock.waitForAll();
dialog.setVisible(false);
Make sure to pass a reference of lock to your threads and at the end of each thread call the following:
lock.unlock();
As per the comment to this answer, Java as of Java 1.5 (verified) provides a class java.concurrent.CountDownLatch with the exactly same behaviour. The use is well documented in the API.
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html
example with CoundDownLatch
CountDownLatch lock = new CountDownLatch(2);
/** put your thread setup code from your example here */
lock.await();
dialog.setVisible(false);
In the threads do the following:
lock.countDown();
full example
final CountingLock lock = new CountingLock(2);
final JDialog dialog = new JDialog(this, true);
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
// Do the job
return null;
}
#Override
protected void done() {
// Must close dialog? The other finished?
lock.unlock();
}
};
SwingWorker<Void, Void> worker2 = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
// Do the job
return null;
}
#Override
protected void done() {
// Must close dialog? The other finished?
lock.unlock();
}
};
worker.execute();
worker2.execute();
dialog.setVisible(true);
lock.waitForAll();
dialog.setVisible(false);
Actually you should also consider to move the waitForAll or await call and setting dialog.setVisible(false) in another background thread since you most likely will not want the UI to stall.
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.