Issues with Swingworker and Jprogressbar

Issues with SwingWorker and JProgressBar

You seem to not understand what SwingWorker does.

SwingWorker provides a means by which you can execute long running tasks outside the context of the Event Dispatching Thread. This means that your program won't appear to have become frozen. The benefit of using a SwingWorker (over using a simple Thread) is that it provides a number of easy to use methods to re-sync the updates back to the EDT, which is indented to stop you from breaking the single thread rules of Swing: No UI element should be created or modified on any thread other then the EDT.

Tak a closer look at Worker Threads and SwingWorker and javax.swing.SwingWorker<T,V> and Concurrency in Swing for more details

Convert

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.event.IIOReadProgressListener;
import javax.imageio.event.IIOWriteProgressListener;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ConvertImage {

public static void main(String[] args) {
new ConvertImage();
}

public ConvertImage() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}

TestPane tp = new TestPane();

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(tp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

tp.convert(new File("/your/image/somewhere"));
}
});
}

public class TestPane extends JPanel implements ImageConverstionListener {

private JLabel label = new JLabel("Waiting...");
private JProgressBar pb = new JProgressBar();

public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(label, gbc);
add(pb, gbc);
}

public void convert(File file) {
ConverterWorker worker = new ConverterWorker(file, this);
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(final PropertyChangeEvent evt) {
if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
pb.setValue((int) evt.getNewValue());
}
}
});
worker.execute();
}

@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}

@Override
public void failedToConvertImage(File source, Throwable cause) {
cause.printStackTrace();
JOptionPane.showMessageDialog(this, "<html>Failed to convert " + source + "<br>" + cause.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}

@Override
public void imageConverted(File source, byte[] bytes) {
JOptionPane.showMessageDialog(this, "Converted image to " + bytes.length + " bytes", "Converted", JOptionPane.INFORMATION_MESSAGE);
}

@Override
public void setMessage(String msg) {
label.setText(msg);
}

}

public interface ImageConverstionListener {

public void failedToConvertImage(File source, Throwable cause);

public void imageConverted(File source, byte[] bytes);

public void setMessage(String msg);

}

public class ConverterWorker extends SwingWorker<ByteArrayOutputStream, String> {

private File source;
private ImageConverstionListener listener;

public ConverterWorker(File source, ImageConverstionListener listener) {
this.source = source;
this.listener = listener;
}

@Override
protected void process(List<String> chunks) {
listener.setMessage(chunks.get(chunks.size() - 1));
}

@Override
protected ByteArrayOutputStream doInBackground() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
publish("Reading image...");
try (ImageInputStream iis = ImageIO.createImageInputStream(source)) {
Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
if (readers.hasNext()) {
ImageReader reader = readers.next();
reader.addIIOReadProgressListener(new IIOReadProgressListener() {
@Override
public void sequenceStarted(ImageReader source, int minIndex) {
}

@Override
public void sequenceComplete(ImageReader source) {
}

@Override
public void imageStarted(ImageReader source, int imageIndex) {
}

@Override
public void imageProgress(ImageReader source, float percentageDone) {
setProgress(Math.round(percentageDone));
}

@Override
public void imageComplete(ImageReader source) {
}

@Override
public void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex) {
}

@Override
public void thumbnailProgress(ImageReader source, float percentageDone) {
}

@Override
public void thumbnailComplete(ImageReader source) {
}

@Override
public void readAborted(ImageReader source) {
}
});
reader.setInput(iis);
try {
BufferedImage img = reader.read(0);

publish("Converting image...");
try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) {
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("png");
if (writers.hasNext()) {
ImageWriter writer = writers.next();
writer.addIIOWriteProgressListener(new IIOWriteProgressListener() {
@Override
public void imageStarted(ImageWriter source, int imageIndex) {
}

@Override
public void imageProgress(ImageWriter source, float percentageDone) {
setProgress(Math.round(percentageDone));
}

@Override
public void imageComplete(ImageWriter source) {
}

@Override
public void thumbnailStarted(ImageWriter source, int imageIndex, int thumbnailIndex) {
}

@Override
public void thumbnailProgress(ImageWriter source, float percentageDone) {
}

@Override
public void thumbnailComplete(ImageWriter source) {
}

@Override
public void writeAborted(ImageWriter source) {
}
});

writer.setOutput(ios);
try {
writer.write(img);
} finally {
writer.removeAllIIOWriteProgressListeners();
}
}
}
} finally {
reader.removeAllIIOReadProgressListeners();
}
}
}
return baos;
}

@Override
protected void done() {
try {
ByteArrayOutputStream baos = get();
listener.imageConverted(source, baos.toByteArray());
} catch (InterruptedException | ExecutionException ex) {
listener.failedToConvertImage(source, ex);
}
}

}

}

JProgressBar not updating from SwingWorker, while debugs work fine

What you have there are layout problems, not anything SwingWorker related. The JProgressBar is updating nicely, you just can't see it.

Check your main method. You are creating two instances of SelfUpdater, adding one to your GUI and updating the other.

public static void main(String[] args) {
util = new Updater(); // <-- don't do this, it calls Swing code from a thread that is not EDT
// Schedule a job for the event dispatch thread.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {

// setup a new GUI
util.createGUI(); // <-- creates an instance of SelfUpdater and adds it to GUI
JFrame rFrame = util.getRootFrame();
JPanel gFrame = util.getCardLayoutFrame();
CardLayout cards = (CardLayout) gFrame.getLayout();
cards.show(gFrame, "UpdateUpdaterPanel");

rFrame.pack();
rFrame.setLocationRelativeTo(null);
rFrame.setVisible(true);

SelfUpdater selfUp = new SelfUpdater(); // <-- creates another instance
int needUpdate = selfUp.checkForUpdate();
if (needUpdate == 2) {// Update available for download. Download it.
selfUp.downloadUpdate();

}

}
});

}

You can't expect components to be shown, if they are not added anywhere. :)

Can't get JProgressBar to update from SwingWorker class

You are calling doInBackground() directly from your code, something that is akin to calling run() directly in a runnable. This means that your code is not in fact running on a background thread, and so you are likely clobbering the event thread with long running code, preventing the Swing GUI, and your progress bar, from updating.

Solution: don't do this. Call execute() on your worker when you want it to run.

If you need further help, you're going to first have to help us. You understand that you have a ton of code posted, most of it completely unrelated to your problem at hand, and certainly more than you should ask volunteers to go through. Please get rid of all the extraneous unrelated code, and instead create and post a proper MCVE.


Edit
You also appear to be calling code directly from the EDT that should be left to your worker thread here:

        signalSimulator.execute();

// ************* all these signalSimulator calls below ***********
if (rdbtnSineWave.isSelected()) {
data = signalSimulator.generateSineWave(numOfdataPoints,
noiseAmp, offset);
data = signalSimulator.addAnomalies(data, numOfLocalSpikes,
numOfExpSpikes);
} else { // Linear signal is selected
data = signalSimulator.generateLinearSignal(numOfdataPoints,
noiseAmp, slope, offset);
data = signalSimulator.addAnomalies(data, numOfLocalSpikes,
numOfExpSpikes);
}

signalSimulator.writeLogFile(path, ".txt", data);

You also appear to be creating only one worker object which is not proper since you can't re-use a SwingWorker object.

I suggest that you only create your SwingWorker object when it is needed, that you pass the information for what type of signal is needed into its constructor. This way the above methods can be called from the SwingWorker's doInBackground method where they belong.

e.g.,

signalSimulator = SignalSimulator(rdbtnSineWave.isSelected())
signalSimulator.addPropertyChangeListener(...);
signalSimulator.execute();

Note that you have some other significant unrelated problems in the code you've posted, but they will have to be addressed at some other time, but they include use of null layout and setBounds, almost always a very bad idea.


Edit

Just to clarify once again, your main problem is that you're calling long-running code on the Swing event thread. Just because a method is located in your Worker class does not mean that calling it will automatically have it run on a background thread. The only way to guarantee this is to have the code called from within your doInBackground() method. Again, what you want to do is to create your new worker object when it is needed, for instance, inside of some ActionListener, and at the time of its creation, pass into it all the information that it will need to run. Then add your PropertyChangeListener, then .execute() your worker. Do this, and I'll bet your code will work much better.


Edit
For example

import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import java.util.concurrent.ExecutionException;

import javax.swing.*;

public class ProgressExampleGui {
private JPanel mainPanel = new JPanel();
private JProgressBar progressBar = new JProgressBar();
private JButton pressMeBtn = new JButton(new MyAction("Press Me", KeyEvent.VK_P, this));

public ProgressExampleGui() {
progressBar.setStringPainted(true);
progressBar.setString("");

mainPanel.add(pressMeBtn);
mainPanel.add(progressBar);
}

public void setProgress(int progress) {
progressBar.setValue(progress);
progressBar.setString(progress + "%");
}

public JComponent getMainComponent() {
return mainPanel;
}

public void setEnabled(boolean enabled) {
pressMeBtn.setEnabled(enabled);
}

private static void createAndShowGui() {
ProgressExampleGui progExampleGui = new ProgressExampleGui();

JFrame frame = new JFrame("Progress Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(progExampleGui.getMainComponent());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

@SuppressWarnings("serial")
class MyAction extends AbstractAction {
private ProgressExampleGui gui;

public MyAction(String name, int mnemonic, ProgressExampleGui gui) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
this.gui = gui;
}

@Override
public void actionPerformed(ActionEvent e) {
AbstractButton source = (AbstractButton) e.getSource();
gui.setProgress(0);
source.setEnabled(false);
MyWorker myWorker = new MyWorker();
myWorker.addPropertyChangeListener(new WorkerPropChngListener(gui));
myWorker.execute();
}
}

class WorkerPropChngListener implements PropertyChangeListener {

private ProgressExampleGui gui;

public WorkerPropChngListener(ProgressExampleGui gui) {
this.gui = gui;
}

@Override
public void propertyChange(PropertyChangeEvent pcEvt) {
MyWorker myWorker = (MyWorker) pcEvt.getSource();
if ("progress".equals(pcEvt.getPropertyName())) {
int progress = ((Integer)pcEvt.getNewValue()).intValue();
gui.setProgress(progress);
}

if (SwingWorker.StateValue.DONE.equals(pcEvt.getNewValue())) {
try {
myWorker.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
gui.setEnabled(true);
}
}

}

class MyWorker extends SwingWorker<Void, Void> {
private static final int MAX_INCR = 8;
private static final long SLEEP_TIME = 200;
private static final int MAX_VALUE = 100;
private int value = 0;
private Random random = new Random();

@Override
protected Void doInBackground() throws Exception {
while (value < MAX_VALUE) {
value += random.nextInt(MAX_INCR);
value = Math.min(value, MAX_VALUE);
Thread.sleep(SLEEP_TIME);
setProgress(value);
}
return null;
}
}

Edit
Regarding your new code, you've got two main problems:

Look at the results of your data output:

Value in setProgress: 0.0
Value in setProgress: 0.001
Value in setProgress: 0.002
Value in setProgress: 0.003
Value in setProgress: 0.004
Value in setProgress: 0.005
Value in setProgress: 0.006
Value in setProgress: 0.007
Value in setProgress: 0.008
Value in setProgress: 0.009
Value in setProgress: 0.01
Value in setProgress: 0.011
Value in setProgress: 0.012
Value in setProgress: 0.013
Value in setProgress: 0.014
Value in setProgress: 0.015
Value in setProgress: 0.016
Value in setProgress: 0.017
Value in setProgress: 0.018
Value in setProgress: 0.019
Value in setProgress: 0.02
Value in setProgress: 0.021
Value in setProgress: 0.022
Value in setProgress: 0.023
Value in setProgress: 0.024
Value in setProgress: 0.025
Value in setProgress: 0.026
Value in setProgress: 0.027
Value in setProgress: 0.028
Value in setProgress: 0.029

At the pace that this is going, your progress value will reach 1 and cause a visible change to the PropertyChangeListener and the JProgressBar when the next ice age is upon us. So first of all, change your sleep times, and change your big number to something more reasonable.

Next, you shadow important variables, notably your JProgressBar variable, progressBar. Here is where you declare it and initialize it in the class:

public class ProgressBarTest implements PropertyChangeListener {

private JFrame frame;
private JButton btnRun;
static JProgressBar progressBar = new JProgressBar(0, 100);

As a side note, this variable should most definitely not be declared static, but that's not the cause of your current problem. The cause is that you in fact re-declare the same variable elsewhere in your initialize method, and then add this new object into your GUI:

private void initialize() {
frame = new JFrame();

// .....

JProgressBar progressBar = new JProgressBar();

// .....

frame.getContentPane().add(progressBar);

Please understand that this new progressBar variable references a completely different JProgressBar, and so if you advance the value of the object created in the class, your GUI will show nothing because it is displaying a completely different object. To solve this, **don't redeclare and initialize a new variable in the initialize method. Instead use the object created in the class.

Other problems with your code: you use null layout and setBounds a lot. This will show to all that you are a newbie Swing programmer, since it means that you like to create rigid programs that are extremely difficult to upgrade, and that may not look good on all systems. Instead use the layout managers. For instance, here's your code with a few changes, all noted by comments:

import java.awt.*;
import java.awt.event.*;
import java.beans.*;

import javax.swing.*;

//!! no need to implement PropertyChangeListener
//!! public class ProgressBarTest implements PropertyChangeListener {
public class ProgressBarTest2 {
private JFrame frame;
private JButton btnRun;

// !! this shouldn't be static!
// !! static JProgressBar progressBar = new JProgressBar(0, 100);
private JProgressBar progressBar = new JProgressBar(0, 100); // !!

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager
.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
ProgressBarTest2 window = new ProgressBarTest2();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

public ProgressBarTest2() {
initialize();
}

private void initialize() {
frame = new JFrame();
// !!frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//!! frame.getContentPane().setLayout(null); //!! never use null layouts
frame.setResizable(false);

// !! don't create a shadowed variable
// !! JProgressBar progressBar = new JProgressBar();

progressBar.setAlignmentX(Component.RIGHT_ALIGNMENT);
//!! progressBar.setBounds(0, 252, 444, 20);
progressBar.setStringPainted(true);
//!! frame.getContentPane().add(progressBar);
frame.getContentPane().add(progressBar, BorderLayout.SOUTH);
btnRun = new JButton("Start Long Run"); //!! no shadowing
//!! btnRun.setBounds(167, 214, 159, 31);
JPanel panel = new JPanel(); //!!
panel.setPreferredSize(new Dimension(450, 300)); //!!
panel.setLayout(new GridBagLayout()); //!!
panel.add(btnRun); //!!
frame.getContentPane().add(panel, BorderLayout.CENTER); //!!
btnRun.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
longRun();
}
});

//!!
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

private void longRun() {
//!! use a more realistic value, one that should show change in listener
//!! LongRunner longRunner = new LongRunner(100000);
LongRunner2 longRunner = new LongRunner2(10000);
longRunner.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
int progress = (int) evt.getNewValue();
System.out.println("Value in propertyChangeListener: "
+ progress);
progressBar.setValue(progress);
}
}
});
longRunner.execute();
}

// !! @Override // !! not needed
// public void propertyChange(PropertyChangeEvent evt) {
// }
}

class LongRunner2 extends SwingWorker<Integer, Double> {
private static final long SLEEP_TIME = 15; // !!
int numOfPoints;
double progress;

public LongRunner2(int numOfPoints) {
this.numOfPoints = numOfPoints;
this.progress = 0;
}

private void runLong(int bigNum) {
for (int i = 0; i < bigNum; i++) {
try {
// !! quicker turn-over so that our bigNum can change
// in a realistic way
// !! Thread.sleep(100);
Thread.sleep(SLEEP_TIME);// !!
} catch (InterruptedException e) {
e.printStackTrace();
}
progress = (((double) i * 100) / (double) bigNum);
setProgress((int) progress);
// !! System.out.println("Value in setProgress: " + progress); //!! This will slow us down
}
}

@Override
protected Integer doInBackground() throws Exception {
runLong(numOfPoints);
return null;
}
}

jProgressBar update from SwingWorker

In the first code, you are calling the following line in a non-EDT (Event Dispatcher Thread) thread. So it is not thread safe:

progressBar.setValue(value);

This may result in unexpected behaviour as Swing is not designed as a thread-safe library.

There are different methods to perform this in the Swing way. One correct way of this is what you have done in the second post. Another would be to use publish()/process() methods, and a third method would be writing your own thread instead of SwingWorker and using SwingUtilities.invokeLater().

JProgressBar isn't progressing

Use a combination with SwingWorker.
See an example here:
SwingWorker and Progress Bar

@Hovercraft: You're right. Allow me to refer to the corresponding SwingWorker page of JavaDoc, in my opinion this explains the situation best.

JProgressBar not working properly

Swing is a single threaded environment, that is, there is a single thread which is responsible for processing all the events that occur within the system, including repaint events. Should anything block this thread for any reason, it will prevent Swing from processing any new events, including, repaint events...

So all this ...

EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(BandListGenerator.class.getName()).log(Level.SEVERE, null, ex); }
jProgressBar2.setValue(progress);
}
});

Is constantly pausing the Event Dispatching Thread, preventing it from actually doing any updates (or at least spacing them randomly)...

It's also likely that your outer loop is been run from within the context of the EDT, meaning that until it exists, nothing in the Event Queue will be processed. All your repaint requests will be consolidated down to a single paint request and voila, instant filled progress bar...

You really should use a SwingWorker - I know you said you tried one, but you've not shown any code as to your attempt in this regards, so it's difficult to know why it didn't work, however...

  • SwingWorker and JProgressBar example
  • SwingWorker and JProgressBar example
  • SwingWorker and JProgressBar example
  • SwingWorker and JProgressBar example
  • SwingWorker and dual welding JProgressBar example
  • SwingWorker and JProgressBar example

And forgive me if we haven't said this a few times before :P



Related Topics



Leave a reply



Submit