I've read many different articles about JProgressBar...including the dodgy code found over at Java; here.
Most indicate you need a SwingWorker to get things happening properly, which makes perfect sense, I understand that much. I am finding that when I call setProgress(value) to update the progressbar, it's not triggering the propertyChange event most of the time. I've checked the value I'm passing to setProgess and it definitely changes every time, so I'm not sure if it's just firing the event too quickly? Please see relevant code below, any help/explanation would be greatly appreciated.
class ProgBar extends SwingWorker
{
public ProgBar()
{
addPropertyChangeListener(new PropertyChangeListener()
{
#Override
public void propertyChange(PropertyChangeEvent evt)
{
if ("progress".equals(evt.getPropertyName()))
{
int value = (Integer)evt.getNewValue();
System.out.println("propertyChange called with: " + value);
loginProg.setValue(value);
}
}
});
loginProg.setStringPainted(true);
loginProg.setValue(0);
setProgress(0);
}
#Override
public Void doInBackground() throws InterruptedException
{
...
int count = 0;
for (Folder f : folders)
{
... // process 'f'
setProgress((int)Math.min(((double)count/folders.length)*100.0, 100.0));
}
...
return null;
}
#Override
public void done()
{
System.out.println("Done called.");
setProgress(100);
loginProg.setValue(100);
}
}
JProgressBar called with this;
private void jButtonActionPerformed(java.awt.event.ActionEvent evt)
{
// Create new thread to run progess bar.
// Otherwise won't be able to update progress bar.
ProgBar pb = new ProgBar();
pb.execute();
}
}
EDIT:
Yeah, so I should have read the Javadocs better;
Because PropertyChangeListeners are notified asynchronously on the Event Dispatch Thread multiple invocations to the setProgress method might occur before any PropertyChangeListeners are invoked. For performance purposes all these invocations are coalesced into one invocation with the last invocation argument only.
For example, the following invokations:
setProgress(1);
setProgress(2);
setProgress(3);
might result in a single PropertyChangeListener notification with the value 3.
I.E. my assumption that setProgress was firing too quickly was correct. A ProgressMonitor might be a better solution.
This isn't an answer but a demonstration sscce, to show you just what I meant:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import javax.swing.*;
public class TestProgBar {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ProgBar progBar = new ProgBar();
// **** this is key and where your code may be deficient ***
JProgressBar prog = progBar.getProg();
progBar.execute();
JOptionPane.showMessageDialog(null, prog);
}
});
}
}
class ProgBar extends SwingWorker<Void, Void> {
private JProgressBar loginProg = new JProgressBar();
public ProgBar() {
addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
int value = (Integer) evt.getNewValue();
System.out.println("propertyChange called with: " + value);
loginProg.setValue(value);
}
}
});
loginProg.setStringPainted(true);
loginProg.setValue(0);
setProgress(0);
}
public JProgressBar getProg() {
return loginProg;
}
#Override
public Void doInBackground() throws InterruptedException {
int count = 0;
int max = 5;
Random random = new Random();
// simulate uploading files
while (count < 100) {
count += random.nextInt(max);
if (count > 100) {
count = 100;
}
setProgress(count);
Thread.sleep(400);
}
// for (Folder f : folders) {
// setProgress((int) Math.min(((double) count / folders.length) * 100.0,
// 100.0));
// }
return null;
}
#Override
public void done() {
System.out.println("Done called.");
setProgress(100);
loginProg.setValue(100);
}
}
Again, this code works fine, suggesting that the code you've loaded does not show the error. You need to do further work isolating the error and getting it into code so we can test it.
Yeah, so I should have read the Javadocs better;
Because PropertyChangeListeners are notified asynchronously on the Event Dispatch Thread multiple invocations to the setProgress method might occur before any PropertyChangeListeners are invoked. For performance purposes all these invocations are coalesced into one invocation with the last invocation argument only.
For example, the following invokations:
setProgress(1);
setProgress(2);
setProgress(3);
might result in a single PropertyChangeListener notification with the value 3.
I.E. my assumption that setProgress was firing too quickly was correct. A ProgressMonitor might be a better solution. I've confirmed this with the SSCCE and my program, both are simply firing setProgress too quickly and as a result, only the last value passed to setProgress is being passed through to the PropertyChange event.
If you want listeners to be called immediately, you can try the following (which worked for me):
setProgress(1);
firePropertyChange("progress", 0, 1);
Related
This question already has answers here:
Can a progress bar be used in a class outside main?
(3 answers)
Closed 7 years ago.
It is the first time I have to work with a progress bar and I'm facing a problem, besides I try to call its setValue(x) from everywhere it keeps on 0% and goes straight to 100% after my method routine finishes.
I tried to make an inner class that extends Thread, then after I tried to start a new Thread within my "main" method, then for the last I tried to use the Observer. These ones seems to have worked according to this posts but unfortunately not to me
Update JProgressBar from new Thread
Problem making a JProgressBar update values in Loop (Threaded)
please, could someone help me???
public class MainClass {
private void checkFiles() {
Task task = new Task();
task.start();
//here I have some Files validation...I don't think it is important to solve the progressbar problem
//so it will be ommited
//in this point I tried to call update to test the observer solution I found in another post here
//task.update(null, null);
JOptionPane.showMessageDialog(this, "Done!");
//here the bar jumps from 0% to 100%
}
private class Task extends Thread implements Observer {
public Task() {
}
//Dont bother with the calculum as I haven't finished working on them....
//The relevant thing here is that it starts a new Thread and I can see the progress
//increasing on console using system.out but my progress bar still don't change from 0%.
public void run() {
int maxSize = 100;
final int partsSize = maxSize / listaArquivosSelecionados.size();
while (listFilesValidated.size() != listFilesToValidate.size()) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int progress = listFilesValidated.size() * partsSize;
System.out.println("Progress" + progress);
progressBar.setValue(progress);
}
});
try {
Thread.sleep(100);
}
catch (InterruptedException e) {}
}
}
//Just tried to set any value to check if it would update before the files validation thread finishes its work.
#Override
public void update(Observable arg0, Object arg1) {
progressBar.setValue(66);
}
}
You can create another class of ProgressBar (see Oracle tutorial) and use this:
ProgressBar pbFrame = new ProgressBar();
pbFrame.setVisible(true);
Executors.newSingleThreadExecutor().execute(new Runnable() {
#Override
public void run() {
// run background process
}
});
Or you can use SwingWorker, for example:
SwingWorker worker = new SwingWorker<MyReturnType, Void>() {
#Override
public MyReturnType doInBackground() {
// do your calculation and return the result. Change MyReturnType to whatever you need
}
#Override
public void done() {
// do stuff you want to do after calculation is done
}
};
I had the same question some years ago.
I have a program currently and have tried to implement a progress bar with my code. Bellow is an example of the code currently. The main GUI is in its own class and instantiates other classes to then execute code within those classes' methods. An example is as follows:
class MainClass {
public javax.swing.JProgressBar progressBar;
private void combineActionPerformed(java.awt.event.ActionEvent evt) {
Combine combiner = new Combine();
combiner.Merge(folder);
}
}
It takes a folder listing and then goes to the Combine class which has the following code:
public class Combine extends SwingWorker<Integer,Integer>{
public void Merge(Folder []){ (for int i=0;i<folder.length;i++){
merge(folder[i]);
}
public void Merge(folder[]){
output stream;
}
}
How do I implement the swing worker properly in this example to make a progress update to the MainClass progress bar as each iteration of i occurs?
To begin, your worker is missing some methods it should implement, such as doInBackground() and done(). You also need a constructor to pass Folder[].
public class Combine extends SwingWorker<Integer,Integer>{
Folder[] folders;
public Combine (Folder[] folders)
{ this.folders = folders; }
private void Merge(Folder [])
{ (for int i=0;i<folder.length;i++)
{
merge(folder[i]);
//Send the message of progress here, it will be executed
//from doInBackground()
setProgress(....);
}
}
private void Merge(folder){
output stream;
}
protected Integer doInBackground()
{
merge(folders);
return null;
}
protected void done()
{ .... }
}
Then you would call this worker with
Combine combiner = new Combine(folders);
combiner.execute();
To track progress, this example is from SwingWorker API:
combiner.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer)evt.getNewValue());
}
}
});
Use the setProgress method as you are doing your processing to send the update. You could simply count how many files there are and call setProgress with (numberProcessed*100)/totalNumber. Note that the multiplication is done first to prevent rounding issues.
This will cause the PropertyChangeListeners to be notified of the changes, this will happen on the EDT so it will be safe to update your JProgressBar from it.
Tons of JProgressBar questions on here I know, but through all the answers and I can't seem to diagnose my issue. I am processing a file with some address verification software. I click the Process button and I need my JProgressBar to update with each file processed.
Here is the button:
private JButton getJButton0() {
...
jButton0.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
jButton0ActionActionPerformed(event);
t.start();
}
...
Per everybody's recommendation, I used the setValue() method within a thread
Thread t = new Thread(){
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jProgressBar0.setValue(BulkProcessor.getPercentComplete());
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
...
BulkProcessor.getPercentComplete() is a method I'm calling from another class which represents the percentage complete. I have tested this method and it updates correctly. The issue is that the progress bar will not update until the files are finished processing, and then it will jump to 100%. I apologize if this is a repeat question, but I have done some serious digging on this site with no luck. Any help much appreciated.
Edit:
Per recommended duplicate, I tried this:
public void update(){
new SwingWorker<Void,Void>() {
protected Void doInBackground() throws Exception {
jProgressBar0.setValue(BulkProcessor.getPercentComplete());
return null;
};
}.execute();
}
And then tried calling this update() method under the actionPerformed() (switched t.start() with update()). I am still having the same issue.
Edit
Based on user1676075's recommendation, however same issue:
public static void update(){
new SwingWorker<Void,Integer>() {
protected Void doInBackground() throws Exception {
do
{
percentComplete = BulkProcessor.getPercentComplete();
publish(percentComplete);
Thread.sleep(100);
} while(percentComplete < 100);
return null;
}
#Override
protected
void process(List<Integer> progress)
{
jProgressBar0.setValue(progress.get(0));
}
}.execute();
}
Edit
Here is the code from my BulkProcessor class
private String getOutputLine( String searchString, String inputLine )
throws QasException
{
..(code for processing lines)..
countRecord++;
percentComplete = (int) Math.round((countRecord/totalRecord)*100);
totalRecord is updated in the main class of my BulkProcessor class
public static void main( String input, String output ){
count.clear();
try{
String inputFile = input;
String outputFile = output;
LineNumberReader lnr = new LineNumberReader(new FileReader(new File(input)));
lnr.skip(Long.MAX_VALUE);
totalRecord = lnr.getLineNumber() + 1; //line count in file
BulkProcessor bulk = new BulkProcessor(inputFile, outputFile, ConfigManager.DFLT_NAME);
bulk.process();
}catch(Exception e ){
e.printStackTrace();
}
}
Looks like you're mixing usages. See the SwingWorker documentation, example at the top: http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html.
Ideally you'd update your BulkProcessor in the doInBackground method of the SwingWorker, and that would call setProgress, and the jProgressBar would be listening for those progress updates as in the example.
If that won't work for you, which it seems like it won't just based on the above, start a SwingWorker from the button press event. Implement the SwingWorker methods kinda like this (pseudocode):
new SwingWorker<Void,Integer>()
{
doInBackground()
{
do
{
percentComplete = BulkProcessor.getPercentComplete();
publish(percentCompete);
Thread.sleep(100);
} while (percentComplete < 100);
}
#Override
process(List<Integer> progress)
{
jProgressBar0.setValue(progress.get(0));
}
}.execute();
You'll need to add error-handling and checks for complete and failure cases, but that should get you started and to where you want to be. doInBackground runs in a background thread so won't block anything, and process() runs on the swing worker thread so will post the updates.
The mistake you probably went on is calling the t.start(); after thejButton0ActionPerformed(event); which makes that after the action is performed the thread will start. Therefore the value of the progress bar is not updated as intended.
You need to start the thread in jButton0ActionPerformed(event); and then update the value in it.
Just a hunch, but...
percentComplete = (int) Math.round((countRecord/totalRecord)*100);
Are you sure this is not integer arithmetic? I don't know the type of totalRecord, so I can't say for sure.
I'd guess everything works fine, and just the progress is 0 all the time, until complete where it magically is 100. This is because an int divided by an int will not have fraction values (ie. 99/100 == 0, 100/100 == 1). This fits perfectly with the symptoms you are experiencing.
Try replacing the line above with:
percentComplete = (int) Math.round((countRecord/(double) totalRecord)*100);
to see it I'm right. :-)
Have you tried to use the PropertyChangeListener-interface?
The calculations will be done by the Swingworker-thread and the main-gui will implement this interface. Some example-code
#Override
public void actionPerformed(ActionEvent e) {
this.myButton.setEnabled(false);
MyWorkerThread thread = new MyWorkerThread(); //Data-processing
thread.addPropertyChangeListener(this.mainguiframe); //Separation of concern
thread.execute();
}
Using the "setProgress"-method of the swing-worker-thread the main-gui-thread will be notified if something has happend.
#Override
public void propertyChange(PropertyChangeEvent property) {
Integer currentValue = new Integer(0);
currentValue = (Integer) property.getNewValue();
this.progressBar.setValue(currentValue.intValue());
}
Swing is not thread-safe. This is not the best solution but perhaps it can help you. Please comment if there is somethin horrible wrong.
I am stuck on probably an easy problem, but I really can't find why it isn't working. I am trying to increase mijnScore with 1 each time the method gets called. But somehow mijnScore goes back to 0 after the method is done.
int mijnScore = 0;
...
public void updateUI() {
System.out.println("updateUI");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ikWin = true;
while(ikWin) {
mijnScore++;
System.out.println("mijnScore" + mijnScore);
Scoresp1.setText(mijnScore + "");
ikWin = false;
positie = 0;
}
}
});
}
Solved
Making the variable static solved my problem.
static int mijnScore = 0;
Please see the javadoc of the method SwingUtilities.invokeLater(..)
http://download.oracle.com/javase/1.4.2/docs/api/javax/swing/SwingUtilities.html#invokeLater(java.lang.Runnable)
It can be that the thread doing the mijnScore increments is invoked only later and this is why in the parent thread you see still the value 0 for it.
I dont know wheather you are calling with different objects or same.Just as a guess Make the variable mijnScore static then it may be ok.
why did you set ikWin = false; then loop ends in first step
If it works after you made it static, you might actually have a different problem!
Do you call updateUI() on a newly constructed class? If so, only call it on a previously constructed instance as mijnScore is local to that instance!
EDIT:
Do your classes look like this? (Maybe you should have posted more code in the question)
// Score.java
public class Score {
int mijnScore = 0;
JLabel scoreSp1 = new JLabel();
public Score(JDialog dialog) {
dialog.add(scoreSp1);
}
...
public void updateUI() {
// Code from question
}
}
// Window.java
public class Game {
...
public void scoredPoint() {
JDialog dialog = new JDialog("You scored!");
new Score(dialog).updateUI();
dialog.setVisible(true);
}
}
In this silly example, the problem is actually in the second class - you shouldn't create a new Score instance every time. For the example, the code should be written like this:
// Window.java
public class Game {
JDialog dialog = new JDialog("You scored!");
Score score = new Score(dialog);
...
public void scoredPoint() {
score.updateUI();
dialog.setVisible(true);
}
}
I am trying to make an application in Java.
To make Swing work correctly, I did this:
public static void main(String[] array){
String outerInput;
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
// I want this string input.
String input = JOptionPane.showInputDialog(
null,"Stop ?", JOptionPane.QUESTION_MESSAGE);
});
// How can I get this input value in String outerInput?
}
How would I get this input string in my main body?
You can use AtomicReference<String> for passing values between threads in a thread-safe manner.
As noted by Hemal, you'll need some synchronization between two threads to make sure it was already executed. For example, you can use CountDownLatch or use SwingUtilities.invokeAndWait (make sure you don't call it from Swing thread!)
Update: here is the complete example using AtomicReference and CountDownLatch
public class Main {
public static void main(String[] args) throws InterruptedException {
final AtomicReference<String> result = new AtomicReference<String>();
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
String input = JOptionPane.showInputDialog(null, "Stop?", "Stop?", JOptionPane.QUESTION_MESSAGE);
result.set(input);
// Signal main thread that we're done and result is set.
// Note that this doesn't block. We never call blocking methods
// from Swing Thread!
latch.countDown();
}
});
// Here we need to wait until result is set. For demonstration purposes,
// we use latch in this code. Using SwingUtilities.invokeAndWait() would
// be slightly better in this case.
latch.await();
System.out.println(result.get());
}
}
Also read this answer about general design of GUI (and Swing) applications.
How would I get this input string in my main body?
You wouldn't. The idea that your "main" would invoke a Swing dialog box and then do something with the results is contrary to the entire idea of a graphical user interface.
In a GUI, you design your program to deal with a series of user-initiated events. Those events may be completely asynchronous, such as the keystrokes, selection, and menu choices of your typical word processor. Or they may be scripted, such as the question-answer format of a "wizard."
Assuming that you want to do something like the latter, you would implement it using the following sequence:
The user initiates some action, perhaps selecting a menu choice. This is turned into an invocation of an ActionListener, which decides that it needs more input from the user.
The ActionListener, which is executed on the event dispatch thread, is permitted to do anything that it wants to the UI, such as displaying a dialog. That dialog may be modal or non-modal; in one case the output is available to the original listener, in the other you need to write a new listener to take subsequent action.
Once you have enough information, you may choose to invoke a background operation. You would typically have a thread-pool to service these requests. You would not attempt to perform the request on the "main" thread; in fact, for all intents the main thread is no longer running.
When your operation completes running, it would push data back to the event dispatch thread using SwingUtilities.invokeLater(). While you could use invokeAndWait() to send results to Swing in the middle of your background operation, that's rarely a good idea. Instead, create a sequence of operations, preferably one that is easily canceled by the user.
The "standard" way to initiate operations on a background thread is via SwingWorker. There are alternatives; for example, you could use a BlockingQueue to send operations to a single long-running background thread, and use invokeLater() to return the results.
Regardless, there's one rule that you do not want to break: never, ever, perform a blocking operation on the event dispatch thread. If you do that, then your application is broken.
Right now you have two threads going: the main thread and the EDT (event dispatch thread). I assume you know that SwingUtilities.invokeLater(runnable) is running a task on the EDT.
To share data between threads, you just need some variable that is in the scope of both threads. The easiest way to accomplish that is to declare a volatile data member or AtomicReference in the class containing the main method.
In order to ensure that you read the value after it is returned by the JOptionPane, the simplest thing you can do here is to change the invokeLater call to an invokeAndWait call. This will cause your main thread to stop executing until what you have put onto the EDT has completed.
Ex:
public class MyClass {
private static volatile String mySharedData;
public static void main(String[] args) throws InterruptedException {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
mySharedData = JOptionPane.showInputDialog(null, "Stop ?", JOptionPane.QUESTION_MESSAGE);
}
});
// main thread is blocked, waiting for the runnable to complete.
System.out.println(mySharedData);
}
}
If your main thread is executing some task that shouldn't be stopped while the option pane is present, then in the main thread you can periodically check (i.e., in the outer part of the loop that is running your task) whether or not mySharedData has been set. If your task doesn't loop and is instead doing some I/O or waiting, you can make use of Thread.interrupt and check mySharedData in the InterruptedExecption handlers.
I suggest using the observer/observable pattern for this, perhaps with a PropertyChangeListener. Then your Swing app will be able to notify any and all listeners if the critical variable(s) state changes.
For example:
import java.awt.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;
public class ListenToSwing {
public static final String STATE = "state";
private static final int STATE_MAX = 10;
private static final int STATE_MIN = -10;
private JPanel mainPanel = new JPanel();
private int state = 0;
private JSlider slider = new JSlider(STATE_MIN, STATE_MAX, 0);
public ListenToSwing() {
mainPanel.add(slider);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setMajorTickSpacing(5);
slider.setMinorTickSpacing(1);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
setState(slider.getValue());
}
});
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
mainPanel.addPropertyChangeListener(listener);
}
public Component getMainPanel() {
return mainPanel;
}
public void setState(int state) {
if (state > STATE_MAX || state < STATE_MIN) {
throw new IllegalArgumentException("state: " + state);
}
int oldState = this.state;
this.state = state;
mainPanel.firePropertyChange(STATE, oldState, this.state);
}
public int getState() {
return state;
}
public static void main(String[] args) {
final ListenToSwing listenToSwing = new ListenToSwing();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("ListenToSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(listenToSwing.getMainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
listenToSwing.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(ListenToSwing.STATE)) {
System.out.println("New state: " + listenToSwing.getState());
}
}
});
}
}
You can use an AtomicReference and invokeAndWait.
public static void main(String[] array){
AtomicReference<String> outerInput = new AtomicReference<String>();
SwingUtilities.invokeAndWait(new Runnable(){
#Override
public void run() {
String input = JOptionPane.showInputDialog(
null,"Stop ?", JOptionPane.QUESTION_MESSAGE);
outerInput.set(input);
});
outerInput.get(); //Here input is returned.
}
You can trivially expose it to the outer class by declaring a String[] in which the runnable sets the value. But note that you will need some synchronization mechanism to know whether it has been assigned by the Runnable.
The following code will do what you want. I have done something similar except I was launching a JFileChooser instead of an input dialog. I found it more convenient than hard coding a bunch of paths into my application or accepting a command line argument, at least for testing purposes. I would like to add that one could modify the prompt() method to return the FutureTask instance for added flexibility.
public class Question {
public static void main(String[] args) {
Question question = new Question();
String message = "Stop?";
System.out.println(message);
// blocks until input dialog returns
String answer = question.ask(message);
System.out.println(answer);
}
public Question() {
}
public String ask(String message) {
try {
return new Prompt(message).prompt();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return null;
}
private class Prompt implements Callable<String> {
private final String message;
public Prompt(String message) {
this.message = message;
}
/**
* This will be called from the Event Dispatch Thread a.k.a. the Swing
* Thread.
*/
#Override
public String call() throws Exception {
return JOptionPane.showInputDialog(message);
}
public String prompt() throws InterruptedException, ExecutionException {
FutureTask<String> task = new FutureTask<>(this);
SwingUtilities.invokeLater(task);
return task.get();
}
}
}