I have some simple code with a for loop. In each pass of the loop I must increment the JProgressBar; however, this isn't working. See below:
public void atualizarBarraDeProgresso(final int valorGeral, final int valorAtual) {
Thread threadProgressoCarregamento = new Thread() {
#Override
public void run() {
jProgressBarPersistindo.setValue(valorAtual);
}
};
threadProgressoCarregamento.start();
}
I'm calling the method "atualizarBarraDeProgresso" in a loop like below:
progressBar.setMinimum(0);
progressBar.setMaximum(qtd);
for(int i = 0; i < qtd; i++) {
atualizarBarraDeProgresso(qtd, i + 1);
doSomething();
}
But nothing happens with my progressBar.
try adding a thread before a for statment. I hope it works
progressBar.setMinimum(0);
progressBar.setMaximum(qtd);
new Thread(){
#Override
public void run() {
for(int i = 0; i < qtd; i++) {
atualizarBarraDeProgresso(qtd, i + 1);
doSomething();
}
}
}.start();
This should be easily managed with a SwingWorker implementation. SwingWorkers are useful when you need to "do something" but don't want to block the GUI while doing it. The class also gives you a useful API for communicating back to the EDT when you need to update a GUI component while doing other work via the publish()/process() methods.
The below implementation handles your loop on a worker thread so that it does not block the EDT (the GUI thread). Calls to publish(Integer...) are relayed to the EDT as a call to process(List) which is where you want to update your JProgressBar, because like all Swing components you should only update a JProgressBar on the EDT.
public class MySwingWorker extends SwingWorker<Void, Integer> {
private final int qtd;
private final JProgressBar progressBar;
public MySwingWorker(JProgressBar progressBar, int qtd){
this.qtd = qtd;
this.progressBar = progressBar;
}
/* This method is called off the EDT so it doesn't block the GUI. */
#Override
protected Void doInBackground() throws Exception {
for(int i = 0; i < qtd; i++) {
/* This sends the arguments to the process(List) method
* so they can be handled on the EDT. */
publish(i + 1);
/* Do your stuff. */
doSomething();
}
return null;
}
/* This method is called on the EDT in response to a
* call to publish(Integer...) */
#Override
protected void process(List<Integer> chunks) {
progressBar.setValue(chunks.get(chunks.size() - 1));
}
}
You can start it like this
int qtd = ...;
progressBar.setMinimum(0);
progressBar.setMaximum(qtd);
SwingWorker<? ,?> worker = new MySwingWorker(progressBar, qtd);
worker.execute();
Related
All interactions with Swing JComponent must be done in Event Dispatch Thread. Painting too.
Long task must be run outside Event Dispatch Thread, otherwise they will block GUI.
But what if long task is painting application in Graphics2D for example 1000 times? Is this conflicting requirements?
public class MyObject extends JPanel {
...
public void paintComponent(Graphics g) {...}
...
}
If I need to call this method so much times, that it can be considered as a long task, what should I do, to avoid blocking GUI? As far as I understand it is not allowed just delegate it in SwingWorker. It there any workaround for such "long tasks"?
I think this is an exception. As long as you're not painting to the screen, then you can invoke it in a background thread. The point is that the actual UI stuff should happen on the event-dispatch thread so all the changes will be visible for the user.
This is a skeleton for how this should happen:
public class MyObject extends JPanel {
private static final int NUMBER_OF_PDFS = 10_000;
private JProgressBar progressBar = new JProgressBar(0, NUMBER_OF_PDFS);
public void paintPdfs() {
ExecutorService threadPool = Executors.newFixedThreadPool(5); // this can come somewhere else too
for (int i = 0; i < NUMBER_OF_PDFS; i++) {
final int newProgressBarValue = i; // you might need some mapping, depends on the setup of the taskbar
threadPool.execute(() -> {
try {
Graphics pdfG2 = getPdfGraphics();
MyObject.this.paintComponent(pdfG2);
} finally {
SwingUtilities.invokeLater(() -> {
int progressBarValue = progressBar.getValue();
if (progressBarValue < newProgressBarValue) {
progressBar.setValue(newProgressBarValue);
}
});
}
});
}
}
private Graphics getPdfGraphics() {
// I don't know how to do this. On the other hand, you do :)
return null;
}
#Override
public void paintComponent(Graphics g) {
// ...
}
}
I can see only one caveat: the swing objects should not change during this print. If they do, then you need another trick. This other trick would be printing the pdfs in the event-dispatch thread one-by-one:
public void paintPdfs() {
for (int i = 0; i < NUMBER_OF_PDFS; i++) {
final int newProgressBarValue = i; // you might need some mapping, depends on the setup of the taskbar
SwingUtilities.invokeLater(() -> {
try {
Graphics pdfG2 = getPdfGraphics();
MyObject.this.paintComponent(pdfG2);
} finally {
int progressBarValue = progressBar.getValue();
if (progressBarValue < newProgressBarValue) {
progressBar.setValue(newProgressBarValue);
}
}
});
}
}
This second approach runs one paintComponent() at a time, so it won't freeze your UI. This is why it doesn't need any executor or worker thread. It just makes sure that it adds
So I have a thread running and inside that thread, it updates my textArea and it should update my progressBar. I'd like to be able to update the progressBar by calling progressBar.setProgress(progress/totalProgress); without having to bind a task to it. Is this even possible?
Some sample/pseudocode because I can't actually upload the code that's being run...
private int progress;
private ProgressBar progressBar;
private int totalProgress;
public void init() {
progress = 0;
totalProgress = 10; // some max progress number
progressBar.setProgress(progress/totalProgress);
new Thread(new Runnable() {
public void run() {
try {
startThread();
} catch (Exception ex) {
System.out.println(ex);
}
}
}).start();
}
public void startThread() {
for(int i = 0; i < someSize; i++) {
textArea.append("some new message);
progress++;
progressBar.setProgress(progress/totalProgress);
}
}
When I print progress and progressBar.getProgress inside the for loop, I can see it incrementing and actually "updating" but I can't get the progressBar UI to update.
Similar to how Swing has a JProgressBar and you can update it via progressBar.setValue(counter); inside a thread that's already doing other things, I'd like to be able to update the ProgressBar for FX inside a thread that's also running other things.
I have a method like below.
ProgressWindow is a sub class of JFrame containing JProgressBar.
addProgress() increments a value in the JProgressBar.
If I call this method from a method in another class, a frame of ProgressWindow will show up but not JProgressBar and some JLabels inside the frame. They show up after the last line (System.out.println("finish")).
If I call this method in a main method in the class containing this method, then every component (Bar, labels...) instantly shows up.
What can I do for showing the window correctly?
static void search(){
ProgressWindow window = new ProgressWindow();
window.setVisible(true);
ExecutorService execs = Executors.newFixedThreadPool(Runtime
.getRuntime().availableProcessors());
Collection<Callable<Void>> processes = new LinkedList<>();
for (int i = 0; i < 100; i++) {
processes.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
progressWindow.addProgress(); // increment progress value
return null;
}
});
}
try {
execs.invokeAll(processes);
} catch (Exception e) {
e.printStackTrace();
} finally {
execs.shutdown();
}
System.out.println("finish");
The main problem is you seem to be calling search from the context of the Event Dispatching Thread.
The problem occurs because you are using execs.invokeAll which blocks until all the callables have finished running.
This means that the EDT is unable to process new events in Event Queue, including repaint events, this is why your UI is coming to a stand still...
There are a number of issues you are now going to face...
You should never update/modify a UI component from any thread other than the EDT
You should block the EDT for any reason
You seem to want to know when the search is complete, so you know need some kind of event notification...
The first thing we need is some way to be notified that the search has completed, this means you can no longer rely on search returning when the search is complete...
public interface SearchListener {
public void searchCompleted();
}
Next we need an intermeditate search method that builds the UI and ensure that the search is launched within it's own Thread...
static void search(final SearchListener listener) {
final ProgressWindow window = new ProgressWindow();
window.setVisible(true);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
search(listener, window);
}
});
t.start();
}
Then we need to modify the original search method to utilise the SearchListener interface to provide notification when the search is complete...
static void search(final SearchListener listener, final ProgressWindow window){
ExecutorService execs = Executors.newFixedThreadPool(Runtime
.getRuntime().availableProcessors());
Collection<Callable<Void>> processes = new LinkedList<>();
for (int i = 0; i < 100; i++) {
processes.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
// This method needs to ensure that
// what ever it does to the UI, it is done from within
// the context of the EDT!!
progressWindow.addProgress();
return null;
}
});
}
try {
execs.invokeAll(processes);
} catch (Exception e) {
e.printStackTrace();
} finally {
execs.shutdown();
}
System.out.println("finish");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
listener.searchCompleted();
}
});
}
Now, without the source code for addProgress, I might be tempted to use
processes.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
progressWindow.addProgress();
}
});
return null;
}
});
}
Instead...
Take a look at Concurrency in Swing for more details
Sounds like you what you're wanting to do is invoke the setVisible on the Swing UI thread, you can do this with invokeAndWait or invokeLater.
So something like:
final ProgressWindow window = new ProgressWindow();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
window.setVisible(true);
}
});
hi id like to know whats the best way to add text to a jtextarea from a swingworkerthread, ive created another class which a jbutton calls by Threadsclass().execute();
and the thread runs in parallel fine with this code
public class Threadsclass extends SwingWorker<Object, Object> {
#Override
protected Object doInBackground() throws Exception {
for(int x = 0; x< 10;x++)
try {
System.out.println("sleep number :"+ x);
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(eftcespbillpaymentsThreads.class.getName()).log(Level.SEVERE, null, ex);
}
throw new UnsupportedOperationException("Not supported yet.");
}
}
now what id like to do is add the value of x to the text area on the main gui, any ideas much appreciated.
There is an excellent example from the JavaDocs
class PrimeNumbersTask extends
SwingWorker<List<Integer>, Integer> {
PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
//initialize
}
#Override
public List<Integer> doInBackground() {
List<Integer> numbers = new ArrayList<Integer>(25);
while (!enough && !isCancelled()) {
number = nextPrimeNumber();
numbers.add(number);
publish(number);
setProgress(100 * numbers.size() / numbersToFind);
}
return numbers;
}
#Override
protected void process(List<Integer> chunks) {
for (int number : chunks) {
textArea.append(number + "\n");
}
}
}
JTextArea textArea = new JTextArea();
final JProgressBar progressBar = new JProgressBar(0, 100);
PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
task.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer)evt.getNewValue());
}
}
});
task.execute();
System.out.println(task.get()); //prints all prime numbers we have got
Take a look at publish and process
The underlying intention is that you need to update the UI from only within the Event Dispatching Thread, by passing the data you want to updated to the UI via the publish method, SwingWorker will call process for you within the context of the EDT
Within doInBackground(), use publish(V... chunks) to send data to process(List<V> chunks).
See How SwingWorker works.
I am running a very heavy process under an anonymous SwingWorker thread. In the meantime, I'm reporting progress to the GUI using a progress bar. However, Swing threading is doing me in. It's simply not updating anything in time. I'm not sure how to do it, as I've tried updating the GUI from the SwingWorker thread, and outside, and both refuse to work.
How can I reliably update the Swing UI while a heavy worker thread is running?
Things I've tried
This does not work (with or without wrapping in the invokeLater command).
new LocalCompressor(compressor).execute();
while (!compressionDone) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
int percent = compressor.getPercentDone();
progressBar.setValue(percent);
statusLabel.setText(percent);
}
});
}
Additionally, attempting to update the UI from a concurrent measuring thread does not work:
class LocalCompressor extends SwingWorker<Void, Void> {
// [...]
public LocalCompressor(Compressor compressor) {
this.compressor = compressor;
// [...]
}
#Override
protected Void doInBackground() {
final Thread t1 = new Thread(new Runnable() {
#Override
public void run(){
compressor.compress();
}
});
final Thread t2 = new Thread(new Runnable() {
#Override
public void run() {
t1.start();
while (t1.isAlive()) {
updateUI(compressor.getPercentDone());
}
}
});
t2.start();
return null;
}
// [...]
}
You're not really using your SwingWorker. The worker already is a Thread for itself. If you have the possibility to put your long running code into the doInBackground(), put it there. Then just call publish(Integer) with your actual progress and process the chunks you get in the process(List<Integer>)-method. In process() you can update the gui, it's on the EDT.
EDIT:
Actually, what you're doing right now is polling in several-while loops, this is kinda power-consuming. That's why I think its better to you events in your algorithm, everytime you got a percent or everytime the loop starts a new round or something like that.
Did you try the very simple and basic way of using a SwingWorker? Like #Zhedar previously said, a SwingWorker already is a Thread for itself. So remove both your inner threads (t1, t2) and just use your time-consuming compress() method in doInBackground().
Something very basic like the following:
class LocalCompressor extends SwingWorker<Void, Integer> {
// .....
// Your constructor here
// .....
#Override
protected Void doInBackground() throws Exception {
compress();
return null;
}
#Override
protected void process(List<Integer> chunks) {
for (Integer chunk : chunks) {
progressBar.setValue(chunk);
statusLabel.setText(chunk);
}
}
}
Now this compress() method should be moved inside the SwingWorker and it must have somewhere a publish(), in your case it might be publish(getPercentDone()) or whatever.
private void compress() {
// .....
publish(getPercentDone());
// .....
}
This is how things are usually done with a SwingWorker.
Expanding on the answers and advice provided here already, here is one way to code it. I'm assuming the compressor itself has no ability to do callbacks but you can ask it for the percent done.
Within the swingworker thread (doInBackground) we start the real compression thread. Then start a polling loop in the background thread, to update the UI a few times a second. To notify the UI thread, call publish. This will cause the overridden method process to be called periodially in the event thread. From here we can safely update the progress bar and status label.
public class LocalCompressor extends SwingWorker<Void, Integer>
{
private Compressor compressor;
public LocalCompressor(Compressor compressor)
{
this.compressor = compressor;
// [...]
}
#Override
protected void done()
{
System.out.println("Compression is done. Going to do something with it...");
}
#Override
protected void process(List<Integer> chunks)
{
for (Integer percent : chunks)
{
progressBar.setValue(percent);
statusLabel.setText(percent);
}
}
#Override
protected Void doInBackground() throws Exception
{
final Thread t1 = new Thread(new Runnable()
{
#Override
public void run()
{
compressor.compress();
}
});
t1.start();
while (t1.isAlive())
{
int percentDone = compressor.getPercentDone();
publish(percentDone);
Thread.sleep(200);
}
return null;
}
}
You could employee a producer/consumer pattern...
Here's a really basic concept...
public class ProducerComsumer {
public static void main(String[] args) {
new ProducerComsumer();
}
public ProducerComsumer() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JPanel panel = new JPanel(new GridBagLayout());
panel.setBorder(new EmptyBorder(12, 12, 12, 12));
JProgressBar progressBar = new JProgressBar();
panel.add(progressBar);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Producer producer = new Producer();
producer.start();
Consumer consumer = new Consumer(producer, progressBar);
consumer.start();
}
});
}
public class Producer extends Thread {
private volatile float progress;
private volatile boolean done;
public Producer() {
setPriority(NORM_PRIORITY - 1);
setDaemon(true);
}
public float getProgress() {
return progress;
}
public boolean isDone() {
return done;
}
#Override
public void run() {
done = false;
for (int index = 0; index < Integer.MAX_VALUE; index++) {
progress = (float) index / (float) Integer.MAX_VALUE;
}
done = true;
System.out.println("All done...");
}
}
public class Consumer extends Thread {
private Producer producer;
private JProgressBar progressBar;
public Consumer(Producer producer, JProgressBar progressBar) {
setDaemon(true);
setPriority(NORM_PRIORITY - 1);
this.producer = producer;
this.progressBar = progressBar;
}
public JProgressBar getProgressBar() {
return progressBar;
}
public Producer getProducer() {
return producer;
}
#Override
public void run() {
while (!producer.isDone()) {
updateProgress();
try {
sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(ProducerComsumer.class.getName()).log(Level.SEVERE, null, ex);
}
}
updateProgress();
}
protected void updateProgress() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
int progress = Math.round(getProducer().getProgress() * 100f);
System.out.println("Update progress to " + progress);
getProgressBar().setValue(progress);
}
});
}
}
}
Have a play around with the Thread.setPriority values and see if it makes any difference
I'm assuming (ya know how that goes) that the call to LocalCompressor.execute() is blocking. If that's the case, your while loop won't run until it's all done, and then you're defeating the purpose of getting a steady stream of updates on your UI.
Give this, or something similar, a shot:
LocalCompressor comp = new LocalCompressor(compressor);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
while (!compressionDone) {
int percent = compressor.getPercentDone();
progressBar.setValue(percent);
statusLabel.setText(percent);
}
}
});
comp.execute();
}