This question already has answers here:
Can a progress bar be used in a class outside main?
(3 answers)
Closed 8 years ago.
I have a swingworker that will be representing a jProgressbar. This is the code
private Swingworker timeOfProccess;
class Swingworker extends SwingWorker<Object, Object> {
#Override
protected Object doInBackground() throws Exception {
jProgressBar1.setStringPainted(true);
int progress = 0;
setProgress(0);
while (progress <= 100) {
jProgressBar1.setValue(progress);
Thread.sleep(5);
progress++;
}
mainProccess();
return null;
}
#Override
protected void done() {
jProgressBar1.setValue(100);
JOptionPane.showMessageDialog(null, "Proses Selesai");
jProgressBar1.setValue(0);
jProgressBar1.setStringPainted(false);
}
}
private void btnExecuteActionPerformed(java.awt.event.ActionEvent evt) {
timeOfProccess = new Swingworker();
timeOfProccess.execute();}
I dont know, why the progressbar running is uncontrolled. it is so fast to 100% even the process still working. But void done is success to pop-up the JoptionPane after main process end. where is I am lost in my code. thanks..
Visit How to Use Progress Bars where You will find good examples on progress bar along with detail description.
Don't use Thread.sleep() that sometime hangs the whole swing application instead try with Swing Timer that is most suitable for swing application.
Read more How to Use Swing Timers
sample code:
// delay for 1500 mill-seconds
Timer timer = new javax.swing.Timer(1500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// call for your next task
}
});
timer.setRepeats(true); // you can turn off the repetition
timer.start();
How to get the progressbar showing the number 1 - 100 %.
Use JProgressBar#setStringPainted() method to show the percent done status.
Related
This question already has answers here:
Calling a method from a JButton is freezing a JFrame?
(2 answers)
Display indeterminate JProgressBar while batch file runs
(1 answer)
Something seems wrong with the layout, JButton showing unexpected behaviour at resize of the window
(4 answers)
Closed 5 years ago.
I am trying to make a program that has a toggle button (regular JButton) that when clicked, runs a while loop that runs until the button is clicked again to stop it.
I have done this, however, when I click the button, the entire JFrame freezes because of it being stuck in the while loop, as the loop will run forever until the button is pressed again. However, it is impossible to click the button again because the JFrame freezes. The button itself also just stays blue because the JFrame freezes before the colour change occurs; right before I click the button.
My code looks something like this:
boolean isRunning=false;
private void buttonClickEvent(ActionEvent evt) {
if(isRunning){
isRunning=false;
System.out.println("Stopped running!");
jButton.setText("Start Running");
} else { // BELOW IS THE CODE THAT CAUSES IT TO LOCK
isRunning=true;
jButton.setText("Stop Routine");
while(isRunning){
// DO STUFF
}
}
}
EDIT: I tried doing the following code (below) and it does print the text and allow the colour change to occur in the button, but the UI still freezes quickly afterward.
boolean isRunning=false;
private void buttonClickEvent(ActionEvent evt) {
if(isRunning){
isRunning=false;
System.out.println("Stopped running!");
jButton.setText("Start Running");
} else { // BELOW IS THE CODE THAT CAUSES IT TO LOCK
isRunning=true;
jButton.setText("Stop Routine");
new Thread(new Runnable() {
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
while(isInRoutine){
System.out.println("lolk");
}
}
});
}
}).start();
}
}
I am having a Jasper report where I am exporting it into PDF using the Java code. My java application is in Swing. I need the Java application to display 'Please wait' message while the work is report is properly exported.
I can remember I did this sometime back with just 4 lines of code, I can't remember what they are now. What I managed to do was display a small note in the screen which says "Please Wait" and which disappears as soon as the work is done. As far I can remember, it is a built in Swing functionality.
> You can use swing worker for that
SwingWorker sw = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
// TODO Auto-generated method stub
progressBar.setVisible(true);
status.setVisible(true);
progressBar.setIndeterminate(true);
progressBar.setBackground(getForeground());
//do some task & it will return something, like
int flag = logRunningThread();
return null;
}
#Override
public void done() {
progressBar.setIndeterminate(false);
progressBar.setBackground(getForeground());
progressBar.setVisible(false);
//After Success Message
System.out.println("Task Completed")
status.setVisible(false);
progressBar.setValue(100);
}
};
sw.execute();
Also take 2 variable
private JProgressBar progressBar;
private JLabel status;
& add them to panel & frame.
I execute the task in this class and the Dialog pops up as a white box. The print statement IS printing out the progress values I'm expecting, but nothing shows up on the Dialog until after the operation is complete. I can see the progress bar flash visible for a millisecond before the dialog is closed at the end. Absolutely no clue what's going on :\
public class ProgressDialog extends JDialog {
private JProgressBar pb;
private SwingWorker<Boolean, Void> task;
public SwingWorker<Boolean, Void> getTask(){
return task;
}
public ProgressDialog(final String call){
setTitle("Working...");
setLayout(new BorderLayout());
setBounds(300,300,300,100);
pb = new JProgressBar(0, 100);
pb.setValue(0);
pb.setVisible(true);
pb.setStringPainted(true);
add(pb, BorderLayout.CENTER);
setVisible(true);
task = new SwingWorker<Boolean, Void>(){
public Boolean doInBackground(){
switch(call){
case "Category": pb.setValue(Category.getProgress());
while(pb.getValue()<99){
try{
Thread.sleep(500);
} catch (InterruptedException e){
Thread.currentThread().interrupt();
}
pb.setValue(Category.getProgress());
System.out.println(pb.getValue());
repaint();
revalidate();
}
break;
}
return true;
}
public void done(){
dispose();
}
};
}
}
EDIT: tried this change. no dice. Why am I not even getting a progress bar at 0%? It only appears once it is at 100%
public class ProgressDialog extends JDialog {
private JProgressBar pb;
private SwingWorker<Boolean, Integer> task;
public SwingWorker<Boolean, Integer> getTask(){
return task;
}
public ProgressDialog(final String call){
setTitle("Working...");
setLayout(new BorderLayout());
setBounds(300,300,300,100);
pb = new JProgressBar(0, 100);
pb.setValue(0);
pb.setStringPainted(true);
add(pb, BorderLayout.CENTER);
setVisible(true);
task = new SwingWorker<Boolean, Integer>(){
public Boolean doInBackground(){
switch(call){
case "Category": setProgress(Category.getProgress());
while(pb.getValue()<99){
try{
Thread.sleep(500);
} catch (InterruptedException e){
Thread.currentThread().interrupt();
}
setProgress(Category.getProgress());
}
break;
}
return true;
}
public void done(){
//dispose();
}
};
task.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
System.out.println((Integer)evt.getNewValue());
pb.setValue((Integer)evt.getNewValue());
pb.revalidate();
pb.repaint();
}
}
});
}
}
You're trying to set the progress bar's state from within the SwingWorker's doInBackground method, from a background thread -- which makes no sense. The whole reason for using a SwingWorker is to allow you to do a background process in a Swing GUI, so you don't make Swing calls from a background thread, and so that you don't tie up the Swing thread with a long-running bit of code.
You should not make Swing calls from this background process. Instead use the publish/process methods as the tutorials will show you. Or perhaps better, set the SwingWorker's progress field, and use a PropertyChangeListener on the SwingWorker to allow the progress bar to react to it.
Regardless, the bottom line:
Use the SwingWorker to do background work.
Do not make Swing calls from within the SwingWorker's doInBackground method.
Use publish to push data from the background method into the Swing thread realm.
Use the process method to handle this data being pushed.
SwingWorker has a progress property that is also handy to use for allowing Swing code to respond to changes in background states.
If you go this route, use a PropertyChangeListener.
You almost never want to use setBounds(...) or null layout. Trust me as someone who has written hundreds of Swing programs, this one will bite you in the end.
It looks as if your Category is using a static method for getting its progress. Again, this is something you almost never want to do. A progress field suggests state, and this should be part of the instance fields of an object, never static.
Here's an SSCCE to demonstrate how you should be updating your JProgressBar. Copy/paste this and run it.
Notice how we update the progress bar by calling publish(i) which sends the integer to the process() method. The SwingWorker sends results to the process() method in chunks, but we are only using an Integer to update the JProgressBar so all we care about it the LAST chunk. In this SSCCE, we go from 1-1000. If you examine the console, you'll see that a lot of numbers between 1-1000 are being skipped because we are updating too fast for the SwingWorker to catch up (but that's ok. That's why it delivers results in chunks).
NOTE: the process() method was originally designed for programmers to return real-time results from their long-running processes and update the GUI. So, if you were doing a database fetch, you could update a JTable with the results you return. I hate doing things that way, though. So 99% of the time I just use an "indeterminate" JProgressBar and wait till the done() method to publish my results. Occaisionally, however, I'll use a "determinate" JProgressBar and update like we do in this SSCCE. Never have I used process() to return and publish actual data. :) But, that's what it was originally designed to do.
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
/**
*
* #author Ryan
*/
public class Test {
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
go();
}
});
}
public static void go() {
JFrame frame = new JFrame();
JProgressBar jpb = new JProgressBar();
jpb.setIndeterminate(false);
int max = 1000;
jpb.setMaximum(max);
frame.add(jpb);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
new Task(jpb, max).execute();
}
static class Task extends SwingWorker<Void, Integer> {
JProgressBar jpb;
int max;
public Task(JProgressBar jpb, int max) {
this.jpb = jpb;
this.max = max;
}
#Override
protected void process(List<Integer> chunks) {
jpb.setValue(chunks.get(chunks.size()-1)); // The last value in this array is all we care about.
System.out.println(chunks.get(chunks.size()-1));
}
#Override
protected Void doInBackground() throws Exception {
for(int i = 0; i < max; i++) {
Thread.sleep(10); // Sleep for 1/10th of a second
publish(i);
}
return null;
}
#Override
protected void done() {
try {
get();
JOptionPane.showMessageDialog(jpb.getParent(), "Success", "Success", JOptionPane.INFORMATION_MESSAGE);
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}
}
}
EDIT: I created a diagram that should be a helpful reference when handling SwingWorker so you know where to place your code.
So in this chunk of code:
//Actions performed when an event occurs.
public void actionPerformed(ActionEvent event)
{
String command = event.getActionCommand();
//If btnConvertDocuments is clicked, the FileConverter method is called and the button is then disabled [so as to prevent duplicates].
if (command.equals("w"))
{
new Thread(new Runnable()
{
public void run()
{
FileConverter fc = new FileConverter();
}
}).start();
btnConvertDocuments.setEnabled(false);
//Validation message ensuring completion of the step.
JOptionPane.showMessageDialog(this, "Step 1 Complete!", "Validation", JOptionPane.INFORMATION_MESSAGE);
}
It seems like the message dialog window pop-ups way too fast, before the FileConverter method isn't even finished being called. I was wondering if the placement of JOptionPane was correct, or if there was a way to delay a message until the method finished processing?
You can use the SwingWorker.
Have a look here, java tutorial.
SwingWorker worker = new SwingWorker<Void, Void>() {
#Override
public Void doInBackground() {
FileConverter fc = new FileConverter();
return null;
}
#Override
public void done() {
JOptionPane.showMessageDialog(this, "Step 1 Complete!", "Validation", JOptionPane.INFORMATION_MESSAGE);
}
};
You should use a Swing Timer with a delay, instead of using your own Thread and Runnable for this.
You can use Swing timers in two ways:
To perform a task once, after a delay.
For example, the tool tip manager uses Swing timers to determine when to show a tool tip and when to hide it.
To perform a task repeatedly.
For example, you might perform animation or update a component that displays progress toward a goal.
An example from the documentation:
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
Timer myTimer = new Timer(delay, taskPerformer);
myTimer.setRepeats(false);
myTimer.start();
How can I update the JProgressBar.setValue(int) from another thread?
My secondary goal is do it in the least amount of classes possible.
Here is the code I have right now:
// Part of the main class....
pp.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
new Thread(new Task(sd.getValue())).start();
}
});
public class Task implements Runnable {
int val;
public Task(int value){
this.val = value;
}
#Override
public void run() {
for (int i = 0; i <= value; i++){ // Progressively increment variable i
pbar.setValue(i); // Set value
pbar.repaint(); // Refresh graphics
try{Thread.sleep(50);} // Sleep 50 milliseconds
catch (InterruptedException err){}
}
}
}
pp is a JButton and starts the new thread when the JButton is clicked.
pbar is the JProgressBar object from the Main class.
How can I update its value?(progress)
The code above in run() cannot see the pbar.
Always obey swing's rule
Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.
What you can do is to create an observer that will update your progress bar -such as
- in this instance you want to show progress of data being loaded on click of a button.
DemoHelper class implements Observable and sends updates to all observers on when certain percent of data is loaded.
Progress bar is updated via public void update(Observable o, Object arg) {
class PopulateAction implements ActionListener, Observer {
JTable tableToRefresh;
JProgressBar progressBar;
JButton sourceButton;
DemoHelper helper;
public PopulateAction(JTable tableToRefresh, JProgressBar progressBarToUpdate) {
this.tableToRefresh = tableToRefresh;
this.progressBar = progressBarToUpdate;
}
public void actionPerformed(ActionEvent e) {
helper = DemoHelper.getDemoHelper();
helper.addObserver(this);
sourceButton = ((JButton) e.getSource());
sourceButton.setEnabled(false);
helper.insertData();
}
public void update(Observable o, Object arg) {
progressBar.setValue(helper.getPercentage());
}
}
Shameless plug: this is from source from my demo project
Feel free to browse for more details.
You shouldn't do any Swing stuff outside of the event dispatch thread. To access this, you need to create a Runnable with your code in run, and then pass that off to SwingUtilities.invokeNow() or SwingUtilities.invokeLater(). The problem is that we need a delay in your JProgressBar checking to avoid jamming up the Swing thread. To do this, we'll need a Timer which will call invokeNow or later in its own Runnable. Have a look at http://www.javapractices.com/topic/TopicAction.do?Id=160 for more details.
There is need not to call pbra.repaint explicitly.
Update JProgressBar shall be done through GUI dispatch thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Remember to make pbar final variable.
pbar.setValue(i);
}
});