I have been trying to understand how to add a progress bar, I can create one within the GUI I am implementing and get it to appear but even after checking through http://docs.oracle.com/javase/tutorial/uiswing/components/progress.html I am still no clearer on how I can set a method as a task so that I can create a progress bar for running a method. Please can someone try to explain this to me or post an example of a progress bar being used in the GUI with a task being set as a method. Thanks.
Maybe I can help you with some example code:
public class SwingProgressBarExample extends JPanel {
JProgressBar pbar;
static final int MY_MINIMUM = 0;
static final int MY_MAXIMUM = 100;
public SwingProgressBarExample() {
// initialize Progress Bar
pbar = new JProgressBar();
pbar.setMinimum(MY_MINIMUM);
pbar.setMaximum(MY_MAXIMUM);
// add to JPanel
add(pbar);
}
public void updateBar(int newValue) {
pbar.setValue(newValue);
}
public static void main(String args[]) {
final SwingProgressBarExample it = new SwingProgressBarExample();
JFrame frame = new JFrame("Progress Bar Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(it);
frame.pack();
frame.setVisible(true);
// run a loop to demonstrate raising
for (int i = MY_MINIMUM; i <= MY_MAXIMUM; i++) {
final int percent = i;
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
it.updateBar(percent);
}
});
java.lang.Thread.sleep(100);
} catch (InterruptedException e) {
;
}
}
}
}
Your question is a bit vague, but it sounds to me like you want the progress bar to show progress for a specific running method, which I'll call the "work()" method. Unfortunately, there's no way to just pass a reference to your method to a progress bar - your method needs to explicitly tell the progress bar what to display. Here's what I would do:
Make the progress bar's reference available to work() - either pass it in as an argument to work(), or provide an accessor method that your code in work() can call to get a reference to the progress bar.
Inside work(), after you've obtained a reference to the progress bar (which I'll call "pb", call pb.setMinimum(0) and pb.setMaximum(n) where n is the number of steps your method has to get through.
As your method completes each step, call pb.setValue(pb.getValue()+1);
At the end of your method, call pb.setValue(0); to reset the progress bar prior to returning.
Also, if you want your progress bar to display a String message, you first have to call pb.setStringPainted(true), then subsequent calls to pb.setString(string) will show up on the progress bar.
See my answer on another SO question which includes an example of a JProgressBar which gets updated by using a SwingWorker. The SwingWorker is used to execute a long running task in the background (in case of the example it is just a regular Thread.sleep) and report on progress at certain intervals.
I would also strongly suggest to take a look at the Swing concurrency tutorial for more background info on why you should use a SwingWorker when performing long-running tasks which interfere with the UI.
A similar example as the one I posted is available in the Swing tutorial about JProgressBars, which it also worth looking at
How about this,
JFrame->JButton (BorderLayout.NORTH)
JFrame-> JPanel->JProgressBar (BorderLayout.SOUTH)
You can add button part where ever you like, for example when Progress progress = ...; state=true; progress.waitFor(); state=false;
private static void daa() {
//Frame
JFrame frame = new JFrame("Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
frame.setSize(frame.getWidth() + 55, frame.getHeight() + 55);
//Button
JButton jButton = new JButton("State");
frame.add(jButton, BorderLayout.NORTH);
//Progress Bar
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
//Text for progress bar
JPanel panel = new JPanel(new BorderLayout());
panel.add(progressBar);
panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
//linking
panel.add(progressBar);
frame.add(panel, BorderLayout.SOUTH);
boolean[] state = {false};
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
state[0] = !state[0];
state();
}
private void state() {
if (state[0] == true) {
panel.hide();
} else {
panel.show();
}
}
});
}
Related
I have 2 icons, and I want them to change every second. I also want it to always run and not stop. I am doing it with this code but I am not successful.
public static void main(String[] args) {
Timer timer = new Timer();
JFrameLeds jframeLeds = new JFrameLeds();
jframeLeds.setVisible(true);
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
Icon icono;
icono = new ImageIcon(getClass().getResource("camera.png"));
jframeLeds.jLabel1.setIcon(icono);
icono = new ImageIcon(getClass().getResource("target.png"));
jframeLeds.jLabel1.setIcon(icono);
}
};
timer.schedule(timerTask, 0, 1000);
}
Using Thread class, or TimerTask is not recommended in a Swing environment. You should be using Swing Timers or Swing Workers since component updates should only take place to the Event Dispatch Thread. Take a look at this example.
However, in your case a flag boolean might be required in order to achieve what you want. An example that changes icons to a label:
public class ChangeIconsTest extends JFrame {
private boolean icon1IsActive;
public ChangeIconsTest(Icon icon1, Icon icon2) {
super("test");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
JLabel label = new JLabel(icon1);
icon1IsActive = true;
Timer swingTimer = new Timer(1000, e -> {
label.setIcon(icon1IsActive ? icon2 : icon1);
icon1IsActive = !icon1IsActive;
});
swingTimer.start();
add(label);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
ImageIcon icon1 = new ImageIcon(
new URL("https://4.img-dpreview.com/files/p/E~TS590x0~articles/3925134721/0266554465.jpeg"));
Icon icon2 = new ImageIcon(new URL("https://www.sample-videos.com/img/Sample-png-image-500kb.png"));
ChangeIconsTest test = new ChangeIconsTest(icon1, icon2);
test.setVisible(true);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
}
}
As many people here reminded, using TimerTask class from java.util is highly NOT recommended while working in Swing or JavaFX environment.
The Swing components aren't thread-safe, changing the state or repainting the components in different thread than the one used by Swing components may lead to unexpected behaviour and strange bugs.
The Swing and AWT components are using Event Dispach Thread as main background thread to process the events. Events are fired inside every component method that might cause the change of interface. The setIcon() and even setText() methods of JLabel are also firing an event to the EDT.
To avoid future bugs every component state change should be done undnder EDT. The EDT can be called through EventQueue.invokeLater(Runnable), but since you are using Swing, you can call the SwingUtilities.invokeLater(Runnable) which calls the EventQueue inside.
The invokeLater method schedules the task and returns, there's also a invokeAndWait which schedules the task and waits until it's finished before returning.
For the sample below I borrowed the icon urls from the George Z. answer.
Sample code for covering the timed icon change:
public class TimedIconChange {
static String ICON_1_URL = "https://4.img-dpreview.com/files/p/E~TS590x0~articles/3925134721/0266554465.jpeg";
static String ICON_2_URL = "https://www.sample-videos.com/img/Sample-png-image-500kb.png";
static String ICON_3_URL = "http://www.frankieballard.com/sites/g/files/g2000005856/f/Sample-image10-highres.jpg";
public static void main(String[] args) throws MalformedURLException {
Icon icon1 = new ImageIcon(new URL(ICON_1_URL));
Icon icon2 = new ImageIcon(new URL(ICON_2_URL));
Icon icon3 = new ImageIcon(new URL(ICON_3_URL));
List<Icon> circularIcons = new ArrayList<>() {
int i = 0;
#Override
public Icon get(int index) {
return get();
}
private Icon get() {
if (i == size()) {
i = 0;
}
return super.get(i++);
}
};
circularIcons.add(icon3);
circularIcons.add(icon2);
circularIcons.add(icon1);
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame();
JLabel label = new JLabel();
label.setIcon(icon1);
frame.setLayout(new BorderLayout());
frame.add(label);
frame.pack();
frame.setVisible(true);
new Timer(1000, e -> label.setIcon(circularIcons.get(0))).start();
});
}
}
The sample contains a little implementation of circular list for circularIcons variable, to reduce the need of using boolean flag.
Additionaly, for longer tasks which are supposed to be working in the background using the SwingWorker class is recommended.
References and further reading on EDT:
https://en.wikipedia.org/wiki/Event_dispatching_thread
Why should I use a separate thread to show a GUI in JAVA
Why does my boilerplate Java desktop app JFrame use EventQueue.invokeLater in the main method?
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html
When a user clicks a button, a long task of approximately 10 seconds will run. During this time I want to show a progress bar to the user. But the main thread has to wait for the worker thread to finish because the worker thread will set a variable that the main thread will use. If I don't wait the worker thread I will get a NullPointerException when using the variable. So after the worker thread finishes, I will also close the progress bar dialog.
When I wait for the worker thread using join() the progress bar dialog shows (interestingly without the progress bar though) and hangs there.
Thread runnable = new Thread() {
public void run() {
try {
System.out.println("thread basladi");
threadAddSlaveReturnMessage = request.addSlave(
ipField.getText(), passField.getText(),
nicknameField.getText());
System.out.println("thread bitti");
} catch (LMCTagNotFoundException e) {
e.printStackTrace();
}
}
};
Thread runnable_progress = new Thread() {
public void run() {
JTextArea msgLabel;
JDialog dialog;
JProgressBar progressBar;
final int MAXIMUM = 100;
JPanel panel;
progressBar = new JProgressBar(0, MAXIMUM);
progressBar.setIndeterminate(true);
msgLabel = new JTextArea("deneme");
msgLabel.setEditable(false);
panel = new JPanel(new BorderLayout(5, 5));
panel.add(msgLabel, BorderLayout.PAGE_START);
panel.add(progressBar, BorderLayout.CENTER);
panel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));
dialog = new JDialog(Frame.getFrames()[0], "baslik", true);
dialog.getContentPane().add(panel);
dialog.setResizable(false);
dialog.pack();
dialog.setSize(500, dialog.getHeight());
dialog.setLocationRelativeTo(null);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setAlwaysOnTop(false);
dialog.setVisible(true);
msgLabel.setBackground(panel.getBackground());
}
};
runnable.start();
System.out.println("runnable start");
runnable_progress.start();
System.out.println("progress start");
runnable.join();
System.out.println("runnable join");
runnable_progress.join();
System.out.println("progress join");
if (threadAddSlaveReturnMessage.equalsIgnoreCase("OK")) {
fillInventoryTable(inventoryTable);
JOptionPane.showMessageDialog(this, messages.getString("centrum.addslavepanel.SUCCESS"), null, JOptionPane.INFORMATION_MESSAGE);
}
"progress join"
doesn't get printed.
You can use a SwingWorker here. A short example :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.godel.nio;
import java.awt.BorderLayout;
import java.util.List;
import javax.swing.*;
/**
*
* #author internet_2
*/
public class Test {
public static void main(String[] args) {
new Test().doJob();
}
public void doJob() {
JTextArea msgLabel;
JProgressBar progressBar;
final int MAXIMUM = 100;
JPanel panel;
progressBar = new JProgressBar(0, MAXIMUM);
progressBar.setIndeterminate(true);
msgLabel = new JTextArea("deneme");
msgLabel.setEditable(false);
panel = new JPanel(new BorderLayout(5, 5));
panel.add(msgLabel, BorderLayout.PAGE_START);
panel.add(progressBar, BorderLayout.CENTER);
panel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));
final JDialog dialog = new JDialog();
dialog.getContentPane().add(panel);
dialog.setResizable(false);
dialog.pack();
dialog.setSize(500, dialog.getHeight());
dialog.setLocationRelativeTo(null);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setAlwaysOnTop(false);
dialog.setVisible(true);
msgLabel.setBackground(panel.getBackground());
SwingWorker worker = new SwingWorker() {
#Override
protected void done() {
// Close the dialog
dialog.dispose();
}
#Override
protected void process(List chunks) {
// Here you can process the result of "doInBackGround()"
// Set a variable in the dialog or etc.
}
#Override
protected Object doInBackground() throws Exception {
// Do the long running task here
// Call "publish()" to pass the data to "process()"
// return something meaningful
return null;
}
};
worker.execute();
}
}
Edit : "publish()" should be called in "doInBackground()" to pass the data to "process()".
you have the issue with Concurency is Swing, your GUI is visible after all thread are done
is possible to moving with JProgressBar (I'm talking about you code) but you have to
create and show JDialog, create once time and reuse this container
then to start Thread,
better could be from Runnable#Thread
output to the Swing GUI must be wrapped into invokeLater()
this is exactly job for using SwingWorker and with PropertyChangeListener
As the previous answers already mentioned, SwingWorker is the way to go, if use want to use concurrency with Swing.I found this SwingWorker and ProgressBar tutorial quite useful in understanding how it all works together.
Coming back to your actual problem: I assume you use a GUI, because you stated the user has to click a button. The question is, what is your "main thread" doing? Does it really have to run all the time? Doesn't look like it. Because you said the thread needs a variable which is set by another thread, which is a result of a user interaction. In short: Why does it need to run if it's dependent on a user interaction anyway? The usual way would be to first get all the data you need and then run the calculations or whatever. In your case, either run everything in a single background thread(set the variable first then do the rest), started by the ActionListener of your button or run the other thread after the thread where you set the variable has completed.You could for example use the method done(), provided by SwingWorker, to launch the next task. Or if you really have to, you could wait in a loop for task.isDone()to return true. But don't forget to check for isCancelled() too.Anyway, I think you should rethink your design. Because what I can see from the limited information provided looks overly complicated to me.
I have created a form on which two components are present, button and progressbar (Netbeans drag and drop).Form contains the main method from where my application starts.I have created another class as well in which i have written a function.What i want is that when i press a button the application goes into the function and the progressbar runs simultaneously with it and when that function is complete with its functionality the the progress bar shows 100% complete.Now this function can take anytime for its completion so i cannot set the max value for the progressbar.So, what to do in this case?Can anyone please provide me with a good example .
JProgressBar.setIndeterminate(true)
Since what sort of a work you are doing inside that so called "Called Function", so it's tough to say, what you want in the scenario, though you can put your lines like progressBar.setValue(someProgress); at regular intervals with it's Indeterminate State to true, and at the end of the function you can simply say that progressBar.setValue(100); and the Indeterminate State will turn to false here, so that it can show that to the end user.
Have a look at this sample program :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ProgressExample
{
public static JProgressBar progressBar;
private void createAndDisplayGUI()
{
JFrame frame = new JFrame("Progress Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout(5, 5));
progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
JButton button = new JButton("START");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
progressBar.setIndeterminate(true);
WorkingDialog wd = new WorkingDialog();
wd.createAndDisplayDialog();
}
});
contentPane.add(progressBar, BorderLayout.PAGE_START);
contentPane.add(button, BorderLayout.PAGE_END);
frame.setContentPane(contentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new ProgressExample().createAndDisplayGUI();
}
});
}
}
class WorkingDialog extends JDialog
{
private String message = "HelloWorld";
private int count = 0;
private JTextField tfield;
private Timer timer;
private ActionListener timerAction = new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
if (count == 10)
{
timer.stop();
ProgressExample.progressBar.setIndeterminate(false);
ProgressExample.progressBar.setValue(100);
ProgressExample.progressBar.setStringPainted(true);
dispose();
return;
}
tfield.setText(tfield.getText() + message.charAt(count));
count++;
}
};
public void createAndDisplayDialog()
{
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setLocationByPlatform(true);
JPanel panel = new JPanel();
tfield = new JTextField(10);
panel.add(tfield);
add(panel);
pack();
setVisible(true);
timer = new Timer(1000, timerAction);
timer.start();
}
}
So , it seems like you are write
ProgressExample.progressBar.setIndeterminate(false);
ProgressExample.progressBar.setValue(100);
ProgressExample.progressBar.setStringPainted(true);
after your while loop.
You can take a look at my answer in a previous SO question, which contains a sample using a JProgressBar which gets updates from another Thread by using a SwingWorker. Whether or not to use a SwingWorker depends a bit on your use case. If the function take some time to run you better use the SwingWorker to avoid blocking the UI.
First of all: I know this question seems to have been asked a few million times, but none of the answers given to other questions seem to work with me.
Sometimes, when I run Message.popup(String, int) in the code below, the text displays correctly, but sometimes the JDialog is empty, like if the component wasn't added at all.
public class Message extends JDialog {
private int width;
private int height;
private JLabel content;
public Message(String _content, int _margin) {
super();
this.content = new JLabel(_content);
content.setFont(new Font("Monospaced", Font.BOLD, 20));
this.margin = _margin;
this.width = content.getPreferredSize().width + _margin;
this.height = content.getPreferredSize().height + _margin;
createComponents();
setProperties();
}
public static void popup(String _content, int _time) {
if (SwingUtilities.isEventDispatchThread()) {
runPopup(_content, _time);
}
else {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
runPopup(_content, _time);
}
});
}
}
private static void runPopup(String _content, int _time) {
final Message message = new Message(_content);
new Timer(_time, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
message.dispose();
}
}).start();
}
private void createComponents() {
setLayout(new BorderLayout());
Box box = Box.createHorizontalBox();
box.add(Box.createHorizontalGlue());
box.add(content, BorderLayout.CENTER);
box.add(Box.createHorizontalGlue());
add(box);
}
private void setProperties() {
setSize(width, height);
setLocation(Coordinator.calculateCenteredWindowLocation(width, height));
setUndecorated(true);
setResizable(false);
setTitle(content.getText());
setVisible(true);
update(getGraphics());
}
}
Without the update(getGraphics());, the frame is always empty, but with it, it depends of what direction the wind is blowing... (go figure!)
As mentioned by #Riduidel, it is important that anything Swing-related occur on the Event Dispatch Thread, or EDT. This is because Swing is not thread-safe. When invoking popup(), you ought to do the following
if(SwingUtilities.isEventDispatchThread()){
Message.popup(...);
}
else{
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
Message.popup(...);
}
});
}
This will ensure that the JFrame is created on the EDT. Also, from the code snippet you've posted, it would seem likely that Message should have a private constructor. In addition, since you're not doing any custom rendering, why not just make a JFrame member variable instead of extending the class? -- seems a bit superfluous to me.
Regardless, you should also never sleep in the EDT either, since this will make the GUI appear to "freeze" and blocks execution of other queued events. When performing long-running tasks, use either SwingWorker, or as #Riduidel mentioned, javax.swing.Timer. But if you prefer to use the java.util.Timer, use the SwingUtilities utility class as shown above to post the Runnable task on the EventQueue to be executed in the EDT.
EDIT
Here is what I'd do (and yes, it works)
public class Message {
// Private constructor to prevent external instantiation
private Message(){
}
public static void createAndShowDialog(final String content, final int time){
final JDialog dialog = new JDialog();
dialog.setLayout(new BorderLayout());
dialog.setUndecorated(true);
JLabel label = new JLabel(content);
label.setFont(new Font("Monospaced", Font.BOLD, 20));
Box b = Box.createHorizontalBox();
b.add(Box.createHorizontalGlue());
b.add(label, BorderLayout.CENTER);
b.add(Box.createHorizontalGlue());
dialog.add(b);
dialog.pack();
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
// kick-off timer
Timer t = new Timer(time, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
dialog.dispose();
}
});
t.setRepeats(false);
t.start();
}
}
And wherever you invoke createAndShowDialog(...), do the following
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
Message.createAndShowDialog("Content", 5000); // wait 5 seconds before disposing dialog
}
});
Are you sure your code is executing in the EDT ? indeed, if not (which is what I expect to be, since you sleep the current thread, what Swing would typically don't like), your frame will have trouble rendering.
To avoid those typical Swing threading issues, please take a look at the SwingUtilities class, which provide you methods to ensure you're running in EDT. Additionnaly, instead of directly sleeping your thread, you could repalce it with a Swing javax.swing.Timer (beware not to confuse it with the java.util.Timer).
update(getGraphics());
Never use the update() method or the getGraphics() method.
Invoking update() is used for AWT NOT Swing.
If you need to do custom painting then you override the paintComponent() method of your component which already has access to the graphics object.
I want to know how to display loading dialog at center of app screen. An indefinite progress bar.
If you're talking about a JDialog, after calling pack() on it, call setLocationRelativeTo(null) to center it.
Here's how I typically show a "loading..." progress bar. The loading itself must happen on a background thread to make sure the progress bar keeps updating. The frame with the progress bar will be shown in the center of the screen.
public static void main(String[] args) throws Throwable {
final JFrame frame = new JFrame("Loading...");
final JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
final JPanel contentPane = new JPanel();
contentPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
contentPane.setLayout(new BorderLayout());
contentPane.add(new JLabel("Loading..."), BorderLayout.NORTH);
contentPane.add(progressBar, BorderLayout.CENTER);
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Runnable runnable = new Runnable() {
public void run() {
// do loading stuff in here
// for now, simulate loading task with Thread.sleep(...)
try {
System.out.println("Doing loading in background step 1");
Thread.sleep(1000);
System.out.println("Doing loading in background step 2");
Thread.sleep(1000);
System.out.println("Doing loading in background step 3");
Thread.sleep(1000);
System.out.println("Doing loading in background step 4");
Thread.sleep(1000);
System.out.println("Doing loading in background step 5");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// when loading is finished, make frame disappear
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.setVisible(false);
}
});
}
};
new Thread(runnable).start();
}
display loading dialog at center of app screen.
dialog.setLocationRelativeTo(...);
An indefinite progress bar.
Read the section from the Swing tutorial on How to Use Progress Bars
From the documentation:
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
This will create the progress bar. To center it, you should take a look at the different kinds of layout managers in java.
Without any examples of your existing code, it's hard to give a more precise answer to your question.