JProgressBar isn't progressing - java

So i'm trying to make a downloader which shows the progress of the download with a progress bar.
But i'm having problems since it doesn't actually update the progress bar. Basically it stays white, when it is meant to be blue. If anyone could help, code is below.
JProgressBar progressBar = new JProgressBar(0, ia);
con.add(progressBar, BorderLayout.PAGE_START);
con.validate();
con.repaint();
progressBar = new JProgressBar(0, ia);
progressBar.setValue(0);
System.out.print("Downloading Files");
while ((count = in.read(data, 0, downloadSpeed)) != -1){
fout.write(data, 0, count);
if (count >= 2){
progressBar.setString("Downloading : " + ia + " # " + count + "Kbs per second");
} else {
progressBar.setString("Downloading : " + ia + " # " + count + "Kb per second");
}
progressBar.setValue(count);
con.add(progressBar, BorderLayout.PAGE_START);
try{
Thread.sleep(1000);
} catch (Exception e){}
}

As #happyburnout has pointed out, you'd be better of processing you download in a separate thread, using a SwingWorker is probably the best solution for what you are doing.
The main reason is you're blocking the Event Dispatching Thread (AKA EDT) from running, preventing any repaint requests (and other UI important things) from been processed.
You should have a read through
Concurrency in Swing
Worker Threads and Swing Worker
Now this is taken almost directly from the API docs, but gives a basic idea of a SwingWoker with a JProgressBar
The "Worker"...
public class Worker extends SwingWorker<Object, Object> {
#Override
protected Object doInBackground() throws Exception {
// The download code would go here...
for (int index = 0; index < 1000; index++) {
int progress = Math.round(((float)index / 1000f) * 100f);
setProgress(progress);
Thread.sleep(10);
}
// You could return the down load file if you wanted...
return null;
}
The "progress pane"
public class ProgressPane extends JPanel {
private JProgressBar progressBar;
public ProgressPane() {
setLayout(new GridBagLayout());
progressBar = new JProgressBar();
add(progressBar);
}
public void doWork() {
Worker worker = new Worker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer) evt.getNewValue());
}
}
});
worker.execute();
}
}
Remember the golden rules of Swing
Never, never, never update a UI component from any Thread other then the EDT
Always perform time consuming tasks on a different Thread
(Something, something, something about layout managers - that's more of a personal thing ;))
And you will have a happy and easy time with Swing :D

Use a combination with SwingWorker.
See an example here:
SwingWorker and Progress Bar
#Hovercraft: You're right. Allow me to refer to the corresponding SwingWorker page of JavaDoc, in my opinion this explains the situation best.

Related

Prob with loading page in Java [duplicate]

So i'm trying to make a downloader which shows the progress of the download with a progress bar.
But i'm having problems since it doesn't actually update the progress bar. Basically it stays white, when it is meant to be blue. If anyone could help, code is below.
JProgressBar progressBar = new JProgressBar(0, ia);
con.add(progressBar, BorderLayout.PAGE_START);
con.validate();
con.repaint();
progressBar = new JProgressBar(0, ia);
progressBar.setValue(0);
System.out.print("Downloading Files");
while ((count = in.read(data, 0, downloadSpeed)) != -1){
fout.write(data, 0, count);
if (count >= 2){
progressBar.setString("Downloading : " + ia + " # " + count + "Kbs per second");
} else {
progressBar.setString("Downloading : " + ia + " # " + count + "Kb per second");
}
progressBar.setValue(count);
con.add(progressBar, BorderLayout.PAGE_START);
try{
Thread.sleep(1000);
} catch (Exception e){}
}
As #happyburnout has pointed out, you'd be better of processing you download in a separate thread, using a SwingWorker is probably the best solution for what you are doing.
The main reason is you're blocking the Event Dispatching Thread (AKA EDT) from running, preventing any repaint requests (and other UI important things) from been processed.
You should have a read through
Concurrency in Swing
Worker Threads and Swing Worker
Now this is taken almost directly from the API docs, but gives a basic idea of a SwingWoker with a JProgressBar
The "Worker"...
public class Worker extends SwingWorker<Object, Object> {
#Override
protected Object doInBackground() throws Exception {
// The download code would go here...
for (int index = 0; index < 1000; index++) {
int progress = Math.round(((float)index / 1000f) * 100f);
setProgress(progress);
Thread.sleep(10);
}
// You could return the down load file if you wanted...
return null;
}
The "progress pane"
public class ProgressPane extends JPanel {
private JProgressBar progressBar;
public ProgressPane() {
setLayout(new GridBagLayout());
progressBar = new JProgressBar();
add(progressBar);
}
public void doWork() {
Worker worker = new Worker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer) evt.getNewValue());
}
}
});
worker.execute();
}
}
Remember the golden rules of Swing
Never, never, never update a UI component from any Thread other then the EDT
Always perform time consuming tasks on a different Thread
(Something, something, something about layout managers - that's more of a personal thing ;))
And you will have a happy and easy time with Swing :D
Use a combination with SwingWorker.
See an example here:
SwingWorker and Progress Bar
#Hovercraft: You're right. Allow me to refer to the corresponding SwingWorker page of JavaDoc, in my opinion this explains the situation best.

JProgressBar update and thread scheduling

Hello stack exchangers,
I have a problem with progress bars in java Swing. I think my confusions arise from my poor understanding of threads and the Swing event queue (I don't know much about java Threads, and exactly what is happening on the AWTEventQueue, although I generally understand what multithreading is about).
The context is that a JButton is pressed to start a long calculation. Before the calculation starts, I make a progress bar in a JFrame, which I thought would be painted, but it isn't. The frame appears, but it is just grey. The button, in this example has "clickMe" written on it.
In the "clickMe" action listener, I first make and display a JFrame in a subtask which is "run" (I'm not clear on when this is scheduled TBH). Then, I call doTask() which is running in the same thread as the action listener (which I think is the AWTEventThread??). The doTask() runs, printing out numbers to the Console. Intermixed with the doTask() output are iteration counts of the progressbar (from when the action listener started makeProgressBar()).
So, from the output, it looks like both the progress bar is running and the AWTEventThread, but the value set in the JProgressBar GUI is never updated.
How can I change my code so that the GUI gets updated? I've tried understanding the JProgressBar tutorial and hunted around the web, but I think my problem is more a conceptual understanding of Java Tasks.
This is my code:
package problemclass;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
public class ProblemClass
{
void progressBarButtonClick()
{
JFrame buttonInAFrame = new JFrame();
JPanel buttonInAFramePanel = new JPanel();
JButton clickMe = new JButton("Click me!");
buttonInAFramePanel.add(clickMe);
clickMe.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
JFrame progBarFrame = makeProgressBar();
doTask();
progBarFrame.dispose();
}
});
buttonInAFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
buttonInAFrame.add(buttonInAFramePanel);
buttonInAFrame.pack();
buttonInAFrame.setVisible(true);
}
private void doTask()
{
for(int i = 0; i < 20000; i++)
{
if (i % 100 == 0)
{
System.out.println("TASK iteration " + i);
try
{
Thread.sleep(1000);
}
catch (InterruptedException e) {}
}
}
}
private JFrame makeProgressBar()
{
JFrame progBarFrame = new JFrame();
JPanel progBarPanel = new JPanel();
JProgressBar progressBar = new JProgressBar();
progBarPanel.add(progressBar);
progressBar.setValue(0);
progressBar.setStringPainted(true);
progressBar.setIndeterminate(true);
new Thread(new Runnable()
{
public void run()
{
for (int i = 0; i <= 100; i++)
{
final int j = i;
System.out.println("Progress Iteration " + j);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
progressBar.setValue(j);
}
});
try
{
java.lang.Thread.sleep(100);
}
catch(Exception e) { }
}
}
}).start();
progBarFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
progBarFrame.add(progBarPanel);
progBarFrame.pack();
progBarFrame.setVisible(true);
return progBarFrame;
}
public static void main(String[] args)
{
EventQueue.invokeLater(() ->
{
new ProblemClass().progressBarButtonClick();
});
}
}
JFrame progBarFrame = makeProgressBar();
doTask();
Not sure exactly what you are trying to do.
The above code has two loops:
In the makePrgressBar() method you start a Thread and invoke SwingUtilities.invokeLater(…), to update the progress bar, which is correct.
but then in doTack() you start another loop. This time you don't start a Thread so the code is invoked on the EDT and since you use Thread.sleep, the EDT will sleep and the GUI will not repaint itself until the entire loop is finished.
I would suggest you get rid of the doTask() method since I don't know why you need two blocks of code that loop. Or if you really need it, then you also need to use a Thread and invokeLater(…).
Just like you, I recently did some work on progress bars and threading and went nuts until I realized that it is just so simple.In a nutshell this is the code I have when my button is clicked:
// Create 2 threads. One handles your GUI. Other does the task
Thread t1 = new Thread(new Runnable() {
#Override
public void run() {
// code goes here.
//In here I choose to hide the button, display the progress bar
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
#Override
public void run() {
// code goes here.
//In here I get the task done, then hide the progress bar
}
});
t2.start();
Works like a charm every time. Hope it helps!

What operations should I put into Platform.runLater()?

Lately I encountered some issues with code that asynchonously updates GUI. I then came across this article, which shined some light on the problem - I was not using Platform.runLater() to update my GUI components, however, consider the original code:
public class Main extends Application {
private TextArea textArea = new TextArea();
private Label statusLabel = new Label("Not Started...");
private Button startButton = new Button("Start");
private Button exitButton = new Button("Exit");
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(final Stage stage) {
startButton.setOnAction(event -> startTask());
exitButton.setOnAction(event -> stage.close());
HBox buttonBox = new HBox(5, startButton, exitButton);
VBox root = new VBox(10, statusLabel, buttonBox, textArea);
root.setStyle("-fx-padding: 10;" +
"-fx-border-style: solid inside;" +
"-fx-border-width: 2;" +
"-fx-border-insets: 5;" +
"-fx-border-radius: 5;" +
"-fx-border-color: blue;");
Scene scene = new Scene(root, 400, 300);
stage.setScene(scene);
stage.setTitle("A simple Concurrency Example");
stage.show();
}
private void startTask() {
Runnable task = this::runTask;
Thread backgroundThread = new Thread(task);
backgroundThread.setDaemon(true);
backgroundThread.start();
}
private void runTask() {
for (int i = 1; i <= 10; i++) {
try {
String status = "Processing " + i + " of " + 10;
statusLabel.setText(status);
textArea.appendText(status + "\n");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The problematic part is the runTask() method. The article explains that instead of simply using statusLabel.setText(status), I should use Platform.runLater(() -> statusLabel.setText(status));. That makes sense to me.
What doesn't make sense, however, is why I don't have to apply the same logic to textArea updates? Notice the fifth line of runTask() method - the textArea.appendText(status + "\n"); part. Why it doesn't give me an exception (java.lang.IllegalStateException: Not on FX application thread) about modifying an FX component from non-FX thread, since it's clearly a way of updating GUI FX component? What operations should I put inside the Platform.runLater() and what operations do now have to be there?
Run later will interfere more or less with the UI Thread. The execution will be queued in the UI thread. When you have a thread sleep or a long calculation the UI will freeze. You can use it for simple tasks. But in your case I do recommend it, because it is only a "set-operation".
Generally you should use Observable properties and bind them to the UI when possible. Then you don't need to do anything of the "Thread" and "Queuing" and "RunLater" operations.
When the WHOLE operation is needed to executed with the "run later", and the calculation would be time costly, you should look, for what you can run later or use a different Mechanism.
But when you just add something with out run laterform a difficult or longer calculation from another thread, an exception will pop up.
So what you want is to update the UI-Elements when it is needed and the new values are calculated or provided by whatever mechanism.
Here the way to go with Properties:
StringProperty text = new SimpleStringProperty("Hello");
Label label = new Label();
label.getTextProperty().bind(text);
Now whenever you Update text from another Thread, the update will automatically occur in the UI without Run Later.
If there is no way around:
What you could (in the case of more costly operations) do is have a queue that contains all the executions of updates for the view that come asynchronously. And execute the changes only in the JavaFX Thread.
You should use the Animationtimer for that.
//thread safe queue
Queue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
Now the approach with runLater:
Platform.runLater(()->{
while(!queue.isEmpty()) {
queue.remove().run();
}
});
And the approach with the AnimationTimer.
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
//Whatever condition or how many changes you would make at one tick
//In this case we just run all "updates" in that tick.
// I would recommend it, you could update 100 things each tick
while(!queue.isEmpty()) {
queue.remove().run();
}
}
};
timer.start();
Then have a thread that would calculate something and when it's done it adds the new change to the queue. Or sleeps or waits for a response etc.
private void startTask() {
Runnable task = this::runTask;
Thread backgroundThread = new Thread(task);
backgroundThread.setDaemon(true);
backgroundThread.start();
}
private void runTask() {
for (int i = 1; i <= 10; i++) {
try {
String status = "Processing " + i + " of " + 10;
//Add the new change as a Runnable here
//It will be run in the in the next update of the UI
tasks.add(()->{
statusLabel.setText(status);
textArea.appendText(status + "\n");
});
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
NOTE
It still depends for what purpose you are going to use it.
Those are just two approaches.
When you only update declared values, you could also use Property Bindings.
(StringProperty in your case)
And I would personally always go with the AnimationTimer.
RunLater decides when it is "fine" with queuing the Runnable. With the Animationtimer, you can be sure it is executed each tick, when a update is needed.
You could also Store all the updates and then do a "poll" of the new Values each tick.
TASK
When you know exactly where you want to put which value you should use Task and bind it the the UI element, that would also work.
Label statusLabel = new Label("Not Started...");
Task<String> t = new Task<String>() {
#Override
protected String call() throws Exception {
int i = 0;
while(//any condition ... ) {
i++;
Thread.sleep(1000);
updateValue("i is " + i);
}
return "last value!";
}
// now Bind the TextProperty of the Label to the ValueProperty of the
// Task
statusLabel.getTextproperty().bind(task.getValueProperty());
Thread backgroundThread = new Thread(task);
backgroundThread.start();

Progress Bar for a GUI application using Java

I am trying to write a progress bar for an application that is downloading information before the GUI runs. Because this is such a long process to download and organize the information, I am wanting to inform the user of the progress. I decided on using a progress bar late in the game and, as such, a majority of the code is written and I'm trying to incorporate the progress bar into the code without a drastic re-working of the code. The following is the code for the progress bar. Currently, the progress bar comes up AFTER everything runs and the GUI pops up.
static class PopulatingCardsWorker extends SwingWorker<Void, Integer> {
JProgressBar jpb;
int max;
JLabel label;
public PopulatingCardsWorker(JProgressBar jpb, int maximum, JLabel label) {
this.jpb = jpb;
this.max = maximum;
this.label = label;
}
#Override
protected void process(List<Integer> chunks) {
int i = chunks.get(chunks.size()-1);
jpb.setValue(i); // The last value in this array is all we care about.
System.out.println(i);
label.setText("Loading " + i + " of " + max);
}
#Override
protected Void doInBackground() throws Exception {
for(int i = 0; i < max; i++) {
Thread.sleep(10); // Illustrating long-running code.
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();
}
}
}
private static void go(int max) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JLabel label = new JLabel("Loading...");
JProgressBar jpb = new JProgressBar();
jpb.setIndeterminate(false);
jpb.setMaximum(max);
panel.add(label);
panel.add(jpb);
frame.add(panel);
frame.pack();
frame.setSize(200,90);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
new PopulatingCardsWorker(jpb, max, label).execute();
}
The program initially calls the GUI application and then runs the database acquisition code as shown below.
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MagicTheGatheringUI().setVisible(true);
}
});
}
public MagicTheGatheringUI() {
int i, size;
colorRefinementFilter = "selected";
try {
my_list=new CardDatabase();
my_list.sortByName(my_list.all_cards);
my_list.populateSubArrays();
size = my_list.all_cards.size();
for(i = 0; i < size; i++)
{
namesList.addElement(my_list.all_cards.get(i).name);
}
} catch(Exception e) {
System.out.println(e);
System.exit(0);
}
initComponents();
}
The swing worker is created during the creation of "my_list=new CardDatabase();". In that class, I have the swing worker and the process the swing worker is supposed to monitor.
I currently call swing worker in a method called "populate_cards()" and I use the following code to create the swing worker. The swing worker is supposed to monitor what's going on in the populate_cards() method. All of the data in the swing worker methods are just temporary until I better understand how to make it work the way I want it to.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
go(1000);
}
});
I believe the issue is that I'm calling the progress bar inside of the "invokeLater" method for the actual GUI.
I have looked at the following questions to try and solve my problem.
How to add a progress bar?
Can a progress bar be used in a class outside main?
and I have also looked at tutorials.
Your code is ok. The reason why the progressBar (and the gui itself) pops after the code ends is that by default in java, the code and the gui runs on the same Thread. Because the code has higher priority then the gui, all the code executes first and after that the gui updates.
You should do that in the "go" method:
new Thread(){
public void run(){
new PopulatingCardsWorker(jpb, max, label).execute();
}
} start();
see that article for more information about threads
Im answering from my phone, so sorry about the bad writing.
Code and GUi threads
and from Wikipedia
last one

Java Swing Concurrency display JTextArea

I need to execute/display a series of events from a Arraylist to a JTextArea, however, each Event gets execute with different time. Following is a quick example of my goal:
public void start(ActionEvent e)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
jTextArea.append("Test" + "\n");
try
{
Thread.sleep(3000);
} catch (InterruptedException e1)
{
e1.printStackTrace();
}
jTextArea.append("Test1" + "\n");
}
});
}
So right now, "Test" and "Test1" display on JTextArea after whole execution is completed.
How do I make "Test" display first, then 3 secs later, display "Test1"
Thank u all in advance
invokeLater schedules the runnable to run on the Event Dispatch Thread. You shouldn't sleep within it or you will starve the dispatch thread. Try using a separate worker thread instead:
Thread worker = new Thread(new Runnable(){
public void run(){
jTextArea.append("Test" + "\n");
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
jTextArea.append("Test1" + "\n");
}
});
worker.start();
If your tasks are time/cpu intensive, then yes, definitely use a background thread to do this such as a SwingWorker object or a Runnable run in a Thread. If however what you need to do is to stagger the display of something and all you are looking for is the Swing equivalent of Thread.sleep(3000), then your best option is to use a Swing Timer. There is an excellent tutorial on how to use these which you can find here: http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html
For example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Fu extends JPanel {
private static final int TIMER_DELAY = 600;
protected static final int MAX_COUNT = 20;
private JTextArea jTextArea = new JTextArea(10, 10);
private JButton startBtn = new JButton("Start");
private Timer timer;
public Fu() {
startBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
startAction(e);
}
});
add(new JScrollPane(jTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
add(startBtn);
}
private void startAction(ActionEvent e) {
if (timer != null && timer.isRunning()) {
// prevent multiple instances of timer from running at same time
return;
}
timer = new Timer(TIMER_DELAY, new ActionListener() {
private int count = 0;
public void actionPerformed(ActionEvent e) {
if (count < MAX_COUNT) {
count++;
jTextArea.append("Test " + count + "\n");
} else {
jTextArea.append("Done! \n");
timer.stop();
timer = null;
}
}
});
timer.setInitialDelay(0);
timer.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Foo");
frame.getContentPane().add(new Fu());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
As pointed out, this is a bad idea, as you will block the event thread.
However, understanding the reason for this is important as well. As you seem to know, all code that affects the state of Swing components needs to happen in the event handling thread (which is the reason why invokeLater and friends should always be used).
What is a bit less better known is that paining code also executes in the event handling thread. When your call to Thread.sleep is executing, it's not only blocking the event thread, it's also blocking any painting of components. This is why the full update appears to happen in one go -- the JTextArea is updated but it can't be repainted until your run method returns.
Lots of info available here: http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html

Categories

Resources