How to run method with JProgressBar - java

I have a function called CreateAccount. I need it to run and also need to show a progress bar. When I click the button, the method will start. And I need to start showing loading progress bar. Once method is done progress bar also should stop at 100. If the method gets more time to do the job, progress bar also needs to load slowly.
I tried using following code but it is not synchronizing progress bar with the method. So how can I do that?
Here is my code:
private static int t = 0;
private void createAccountBtnActionPerformed(java.awt.event.ActionEvent evt) {
progressBar.setValue(0);
progressBar.setStringPainted(true);
new Thread(new Runnable() {
#Override
public void run() {
//CreateAccount();
for (t = 0; t <= 100; t++) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
CreateAccount();
progressBar.setValue(t);
}
});
try {
java.lang.Thread.sleep(100);
}
catch(InterruptedException e) { }
}
}
}).start();
}

Because of the single threaded nature of Swing, you can't perform long running or blocking operations from within the context of the Event Dispatching Thread, nor can you update the UI from outside the context of the Event Dispatching Thread.
See Concurrency in Swing for more details.
Both these things you are taking care of. The problem is, this means that it's possible for the background thread to do more work than is been presented on the UI and there's nothing you can do about. The the best bet is simply trying too keep the UI up-to-date as much as possible
A possible better solution might be to use a SwingWorker, which is designed to make updating the UI easier. See Worker Threads and SwingWorker for more details.
The following example shows a progress bar which will run for 10 seconds with a random delay of up to 500 milliseconds between each update. The progress bar is then update based on the amount of time remaining.
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.time.Duration;
import java.time.Instant;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public final class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JProgressBar pb;
private JButton btn;
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
btn = new JButton("Go");
pb = new JProgressBar();
add(btn, gbc);
add(pb, gbc);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btn.setEnabled(false);
makeItProgress();
}
});
}
protected void makeItProgress() {
SwingWorker<Double, Double> worker = new SwingWorker<Double, Double>() {
#Override
protected Double doInBackground() throws Exception {
Duration duration = Duration.ofSeconds(10);
Instant startTime = Instant.now();
Duration runningTime = Duration.ZERO;
Random rnd = new Random();
setProgress(0);
do {
Thread.sleep(rnd.nextInt(500));
Instant now = Instant.now();
runningTime = Duration.between(startTime, now);
double progress = (double) runningTime.toMillis() / (double) duration.toMillis();
setProgress((int) (progress * 100));
} while (duration.compareTo(runningTime) >= 0);
setProgress(100);
return 1.0;
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
SwingWorker worker = (SwingWorker) evt.getSource();
if (evt.getPropertyName().equals("progress")) {
int value = (int) evt.getNewValue();
pb.setValue(value);
} else if (evt.getPropertyName().equals("state") && worker.getState() == SwingWorker.StateValue.DONE) {
pb.setValue(100);
btn.setEnabled(true);
}
}
});
worker.execute();
}
}
}
The point of this example is, the progress and the work are mixed into a single operation (the doInBackground method of the SwingWorker) so they are more closely related. The SwingWoker then notifies the PropertyChangeListener of updates, to which it can react to safely on the Event Dispatching Thread

Related

How to terminate Java program without closing a window

I have created a Java application that goes through hundreds of documents after user clicks "Run" button. Is there a way to terminate the program and leave the GUI running? All I want to be able to stop is the process of reading the documents.
System.exit(0) is not the solution I am looking for as my whole app closes.
It's difficult to say something without to see your application. But probably this piece of code will help you to understand how to implement what you want:
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;
public class SwingWorkerTest implements Runnable {
private JButton cancelButton = new JButton("Cancel");
private JButton runButton = new JButton("Run");
private JLabel label = new JLabel("Press 'Run' to start");
private LongWorker longWorker;
#Override
public void run() {
JFrame frm = new JFrame("Long task test");
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
cancelButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
longWorker.terminate();
}
});
runButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
longWorker = new LongWorker();
runButton.setEnabled(false);
cancelButton.setEnabled(true);
label.setText("Task in progress. Press 'Cancel' to terminate.");
longWorker.execute();
}
});
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
bottomPanel.add(runButton);
bottomPanel.add(cancelButton);
frm.add(label);
frm.add(bottomPanel, BorderLayout.SOUTH);
frm.setSize(400, 200);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new SwingWorkerTest());
}
private class LongWorker extends SwingWorker<Void, Void> {
private volatile boolean terminated;
#Override
protected Void doInBackground() throws Exception {
// check special variable tov determine whether this task still active
for (int i = 0; i < 1000 && !terminated; i++) {
readFile();
}
return null;
}
#Override
protected void done() {
if (terminated) {
label.setText("Process terminated. Press 'Run' to restart.");
} else {
label.setText("Process done. Press 'Run' to restart.");
}
cancelButton.setEnabled(false);
runButton.setEnabled(true);
}
// dummy method - make 10 milliseconds sleep
private void readFile() {
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
// Nothing here
}
}
public void terminate() {
terminated = true;
}
}
}

timer action performs only after the thread is finished

I have a swing GUI named SpyBiteDemo, it will call another class(Parser) and do some calculations and show some data in a jtable inside this GUI(SpyBiteDemo). I have a jbutton1 and I want to when click on it to show my timer to begin like 1,2,3,4,5,....seconds
what happens is my timer is running correctly however it does not show value unless it is done with all the program that is filling the jtable which means the action it is detecting is I perform the action after jtable appears.
I am a complete newbie on Java for event listeners and I have searched timer, timer task, schedule, everything and could not understand what's wrong.I also tried while(true) and did not fix it.I also tried duration of 1000,0,everything no affects.
I tried to use action command,sleeping the thread, it did not help.here is what I did:
public class SpyBiteDemo extends javax.swing.JFrame {
/**
* Creates new form SpyBiteDemo
*/
private long startTime;
Timer timer = new Timer(0, new TimerListener());
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent aEvt) {
long time = (System.currentTimeMillis() - startTime) / 1000;
label3.setText(time + " seconds");
}
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
jButton1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
startTime = evt.getWhen();
String SeedUrl = jTextField1.getText();
Parser P = new Parser(this);
jTable2.setVisible(true);
}
}
it will start showing label3 value only after it is filling the jtable on my jframe.I want the timer to start from when I am clicking the button.
with trashgod's links I had come up with this example which is comepletely runnable on your machine, this works perfect except that when I the program finishes, it does not stop the timer since I don't know where to do it, I know I should do it in addPropertyChangeListener, however I do not have timer value.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package javaapplication7;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
/**
* #see http://stackoverflow.com/a/25526869/230513
*/
public class DisplayLog {
private static final String NAME = "C:\\wamp\\bin\\mysql\\mysql5.6.17\\bin\\scosche.sql";
private static class LogWorker extends SwingWorker<TableModel, String> {
private final File file;
private final DefaultTableModel model;
private LogWorker(File file, DefaultTableModel model) {
this.file = file;
this.model = model;
model.setColumnIdentifiers(new Object[]{file.getAbsolutePath()});
}
#Override
protected TableModel doInBackground() throws Exception {
BufferedReader br = new BufferedReader(new FileReader(file));
String s;
while ((s = br.readLine()) != null) {
publish(s);
}
return model;
}
#Override
protected void process(List<String> chunks) {
for (String s : chunks) {
model.addRow(new Object[]{s});
}
}
#Override
protected void done() {}
}
private void display() {
JFrame f = new JFrame("DisplayLog");
JLabel m=new JLabel("time");
JButton jb=new JButton("run");
f.add(jb,BorderLayout.BEFORE_FIRST_LINE);
f.add(m, BorderLayout.SOUTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultTableModel model = new DefaultTableModel();
JTable table = new JTable(model);
JProgressBar jpb = new JProgressBar();
f.add(jpb, BorderLayout.NORTH);
f.add(new JScrollPane(table));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
jb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
LogWorker lw = new LogWorker(new File(NAME), model);
lw.addPropertyChangeListener((PropertyChangeEvent ev) -> {
SwingWorker.StateValue s = (SwingWorker.StateValue) ev.getNewValue();
jpb.setIndeterminate(s.equals(SwingWorker.StateValue.STARTED));
});
lw.execute();
int timeDelay = 0;
ActionListener time;
time = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
m.setText(System.currentTimeMillis() / 1000 + "seconds");
}
};
Timer timer=new Timer(timeDelay, time);
timer.start();
if(lw.isDone())
timer.stop();
}
});
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new DisplayLog().display();
});
}
}
It looks like the rows that comprise your table come from the network with variable, indeterminate latency. As shown here, you can load data into your TableModel in the background of a SwingWorker. As shown here, you can calculate intermediate progress in a way that makes sense for your application and display it in a PropertyChangeListener.

Swing Progress Bar updates via Worker to EventDispatch thread

I have a JAVA6 GUI handling data import to our database. I have implemented a working JProgressBar. I understand that changes made to the GUI must be done via the event dispatch thread--which I do not think I am doing (properly/at all).
the background Worker thread, UploadWorker, is constructed by passing in the a JProgressBar created in the main program, and sets changes the value of the progress bar directly once it is finished:
// when constructed, this gets set to the main program's JProgressBar.
JProgressBar progress;
protected Void doInBackground() throws Exception {
write("<!-- Import starting at " + getCurrentTime() + " -->\n");
boolean chunked = false;
switch (importMethod) {
//do some importing
}
write("<!-- Import attempt completed at " + getCurrentTime() + "-->\n");
//here changes to the GUI are made
progress.setMaximum(0);
progress.setIndeterminate(false);
progress.setString("Finished Working");
return null;
}
This works fine, but sometimes(not always) throws me several NPE's in the std out, and users are complaining:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.plaf.basic.BasicProgressBarUI.updateSizes(Unknown Source)
...etc...
Anyway, I believe there is something I need to do to get these updates executed on the proper thread, correct? How?
There are a number of ways you could do this, you could use the process method of the SwingWorker to also update the progress bar, but for me, this couples your worker to the UI, which isn't always desirable.
A better solution is to take advantage of the SwingWorkers progress and PropertyChange support, for example....
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equalsIgnoreCase(evt.getPropertyName())) {
SwingWorker worker = (SwingWorker) evt.getSource();
switch (worker.getState()) {
case DONE:
// Clean up here...
break;
}
} else if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
// You could get the SwingWorker and use getProgress, but I'm lazy...
pb.setIndeterminate(false);
pb.setValue((Integer)evt.getNewValue());
}
}
});
worker.execute();
This means you could do this for ANY SwingWorker, so long as it was the worker was calling setProgress internally...
public static class ProgressWorker extends SwingWorker {
public static final int MAX = 1000;
#Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < MAX; index++) {
Thread.sleep(250);
setProgress(Math.round((index / (float)MAX) * 100f));
}
return null;
}
}
The benefit of this is that the PropertyChange event notification is called from within the context of the of Event Dispatching Thread, making it safe to update the UI from within.
And fully runnable example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SwingWorkerProgressExample {
public static void main(String[] args) {
new SwingWorkerProgressExample();
}
public SwingWorkerProgressExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JProgressBar pb;
public TestPane() {
setLayout(new GridBagLayout());
pb = new JProgressBar(0, 100);
pb.setIndeterminate(true);
add(pb);
ProgressWorker worker = new ProgressWorker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equalsIgnoreCase(evt.getPropertyName())) {
SwingWorker worker = (SwingWorker) evt.getSource();
switch (worker.getState()) {
case DONE:
// Clean up here...
break;
}
} else if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
// You could get the SwingWorker and use getProgress, but I'm lazy...
System.out.println(EventQueue.isDispatchThread());
pb.setIndeterminate(false);
pb.setValue((Integer) evt.getNewValue());
}
}
});
worker.execute();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public static class ProgressWorker extends SwingWorker {
public static final int MAX = 1000;
#Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < MAX; index++) {
Thread.sleep(250);
setProgress(Math.round((index / (float) MAX) * 100f));
}
return null;
}
}
}
You can just create a new Runnable that performs GUI updates and invoke it in a GUI thread using SwingUtilities.invokeLater

How to disable a JButton for certain period of time?

I would like to disable a JButton for about 10 seconds. Is there way to do this?
Thank you
Use a Swing Timer, when triggered, it notifies the registered listener within the context of the Event Dispatching Thread, making it safe to update the UI from.
See How to use Swing Timers and Concurrency in Swing for more details
First read the answer from #MadProgrammer and go through the links provided there. If you still need a working example based on those suggestions, following is one.
why the solution is better than few solutions presented
It's because it uses a javax.swing.Timer to enable the button that enables GUI related tasks to be automatically executed on the event-dispatch thread (EDT). This saves the swing application from being intermixed with non EDT operations.
Please try the following example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SwingDemo extends JPanel {
private final JButton button;
private final Timer stopwatch;
private final int SEC = 10;
public SwingDemo() {
button = new JButton("Click me to disable for " + SEC + " secs");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton toDisable = (JButton) e.getSource();
toDisable.setEnabled(false);
stopwatch.start();
}
});
add(button);
stopwatch = new Timer(SEC * 1000, new MyTimerListener(button));
stopwatch.setRepeats(false);
}
static class MyTimerListener implements ActionListener {
JComponent target;
public MyTimerListener(JComponent target) {
this.target = target;
}
#Override
public void actionPerformed(ActionEvent e) {
target.setEnabled(true);
}
}
public static void main(String[] args) {
final JFrame myApp = new JFrame();
myApp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myApp.setContentPane(new SwingDemo());
myApp.pack();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
myApp.setVisible(true);
}
});
}
}
You can use Thread, Task or the simpler Timer class.
you can use Thread.sleep(time in mil seconds)
ex:
Thread.sleep(10000); // sleep for 10 seconds
JButton button = new JButton("Test");
try {
button.setEnabled(false);
Thread.sleep(10000);
button.setEnabled(true);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
but it must be in a separate thread or it will make all the GUI hang for 10 seconds.
you can post more details about the code and i can help

Using Sleep method inside a For loop (on hold)

I working on a program that prints 100 numbers in a jTable. Also, there will be an if statement to validate the results, and will set a jPanel in a Specific color according to the value that is printed. I need to print those values a little bit slower and also make sure that jPanel changes it colors according to each value. I tried the following code, but seems to have an error:
try{
int n = 100;
int m = 1513;
int a = 19713;
double x = 177963;
int c = 1397;
double r;
int i;
Object[] res = new Object[n];
for(i=0;i< n;i++){
r = (a*x+c)%m;
x = r;
r = r/m;
res[i] = r;
Thread.sleep(1000);
if(r>=0.3){
jPanel3.setBackground(Color.green);
}else{
jPanel3.setBackground(Color.red);
}
}
DefaultTableModel dtm = new DefaultTableModel();
dtm.addColumn("Results", res);
// dtm.addColumn("resultado2", res);
jTable1.setModel(dtm);
}catch(Exception e){
Thread.currentThread().interrupt();
}
but seems to have an error...
What error?
Note that you'll never want to call Thread.sleep(...) in a Swing application's event thread. Use a Swing Timer instead.
Swing is a single threaded framework. The Event Dispatching Thread is responsible for processing, amongst other things, repaint requests. Any action which stops the EDT from running, will prevent it from processing any repaints requests and other events, making your application look like it's hung...
You are also required to ensure that all updates to the UI are made from within the context of the EDT.
In your case, you are executing a loop and using Thread.sleep, which are two big no, no's when dealing with Swing...
Take a look at Concurrency In Swing
As was mentioned in your duplicate question, you should be using a javax.swing.Timer.
This means you are going to have to revamp your loop conditions to work, for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ColorPane {
public static void main(String[] args) {
new ColorPane();
}
public ColorPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Color[] colors = new Color[]{Color.RED, Color.GREEN, Color.BLUE};
private int colorIndex = -1;
public TestPane() {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
colorIndex++;
if (colorIndex >= colors.length) {
colorIndex = 0;
}
setBackground(colors[colorIndex]);
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}

Categories