I'm trying to create a pop up dialog progress bar preferably an Indeterminate but that's not too important. I have been looking through "Oracle's ProgressBar Tutorials" and Google searching but not such luck in getting it to work. I'm pasted my code below of my Action Listener and the dialog will not pop up. Any suggestions? Thanks in advance!
Sorry this is my first post on this site. But how it works is that When I press the create button, it goes out and grab some information from different servers and directories and creates a file for me. That is what the new Project is. Features is a Enumeration I made that are set with the text in the JTextBox when the Create Button. The problem is that this process takes time to process, so I want the a progress bar to show that its processing
private class CreateButton implements ActionListener
{
#Override
public void actionPerformed(ActionEvent arg0) {
class Task extends SwingWorker<Void, Void>
{
#Override
protected Void doInBackground()
{
//Set Variables
for(Feature f : Feature.values())
{
if(f.getComp() != null)
{
f.getVariable().setVariable(((JTextField) f.getComp()).getText());
}
}
new Project(jobs.getSelectedValue().split("-")[0].trim(),
jobs.getSelectedValue().split("-")[1].trim(),
features);
return null;
}
}
ProgressMonitor pm = new ProgressMonitor(display, "Testing...", "", 0, 100);
pm.setProgress(0);
Task task = new Task();
task.execute();
}
}
I was not sure about your SSCCE so I am just posting how JProgressBar usually works.
Read about SwingWorker and JProgressBar
During background process show progress bar. A simple example of how it works is shown.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
public class MyProgressBarTest {
private static final long serialVersionUID = 1L;
private static JProgressBar progressBar;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MyProgressBarTest obj = new MyProgressBarTest();
obj.createGUI();
}
});
}
public void createGUI() {
final JFrame frame = new JFrame();
JPanel panel = new JPanel();
final JButton button = new JButton("Progress");
progressBar = new JProgressBar();
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
MyCustomProgressBarDialog progressBarObj = new MyCustomProgressBarDialog(progressBar, frame);
progressBarObj.createProgressUI();
MyActionPerformer actionObj = new MyActionPerformer(progressBar, progressBarObj, button);
actionObj.execute();
}
});
panel.add(button);
frame.add(panel);
frame.setTitle("JProgressBar Example");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setLocationRelativeTo(null);
frame.setSize(200, 300);
frame.setVisible(true);
}
}
class MyActionPerformer extends SwingWorker<String, Object> {
JProgressBar fProgressBar;
MyCustomProgressBarDialog progressDialog;
JButton button;
public MyActionPerformer(JProgressBar progressBar, MyCustomProgressBarDialog progressDialog, JButton button) {
this.fProgressBar = progressBar;
this.fProgressBar.setVisible(true);
this.fProgressBar.setIndeterminate(true);
this.button = button;
this.progressDialog = progressDialog;
this.button.setEnabled(false);
}
protected String doInBackground() throws Exception {
calculateResult();
return "Finished";
}
protected void done() {
fProgressBar.setVisible(false);
this.progressDialog.setVisible(false);
this.button.setEnabled(true);
}
public void calculateResult() {
for (int i = 0; i < 500000; i++) {
System.out.println("Progress Bar: " + i);
}
}
}
class MyCustomProgressBarDialog extends JDialog {
private static final long serialVersionUID = 1L;
private static JProgressBar progressBar;
private JFrame motherFrame;
private JLabel label = new JLabel("loading.. ");
private JButton button;
public MyCustomProgressBarDialog(JProgressBar progressBar, JFrame frame) {
this.progressBar = progressBar;
this.motherFrame = frame;
this.button = button;
}
public void createProgressUI() {
add(label, BorderLayout.NORTH);
add(progressBar, BorderLayout.CENTER);
setSize(50, 40);
setAlwaysOnTop(true);
setLocationRelativeTo(motherFrame);
setUndecorated(true);
setVisible(true);
}
}
This comes from the Oracle javadoc for ProgressMonitor:
Initially, there is no ProgressDialog. After the first
millisToDecideToPopup milliseconds (default 500) the progress monitor
will predict how long the operation will take. If it is longer than
millisToPopup (default 2000, 2 seconds) a ProgressDialog will be
popped up.
Note that it doesn't pop up until at least 1/2 second after you create it. Even then, it only pops up if the process is expected to take over 2 seconds.
This is all based on your repeated calls to setProgress(int) and the time between the progression of values across the range you gave it.
I suspect the conditions that cause the dialog to pop up are not being met. Or, perhaps, your program exits before that amount of time goes by.
You need to define attribute
ProgressMonitor pm;
then should create total progress size
int totalProgress = Feature.values().size();
then in the loop just increment count
int counter = 0;
for(Feature f : Feature.values())
{
if (pm.isCanceled()) {
pm.close();
return null;
}
pm.setProgress(counter);
pm.setNote("Task is " + counter*100/totalProgress + "% completed");
counter++;
call the progress monitor
pm = new ProgressMonitor(display, "Testing...", "", 0, totalProgress);
assumed that the most part of the job is done in the loop, if the other part such as project creation takes time then you could add additional percent counts to totalProgress or reset monitor after features completed.
Related
I've downloaded a small Java project from oracle website to create a progress bar.
I understand it, but I need to apply it in a different way, the application is creating a thread in the background so the progress bar can be updated accordingly (doInBackground()).
My question is, how can I replace this kind of process in the background in this application by a method from my application (method is just doing a kind of batch processing on a database), can someone help please?
Here is code by Oracle:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
import java.util.Random;
public class ProgressBarDemo2 extends JPanel
implements ActionListener, PropertyChangeListener {
private JProgressBar progressBar;
private JButton startButton;
private JTextArea taskOutput;
private Task task;
class Task extends SwingWorker<Void, Void> {
/*
* Main task. Executed in background thread.
*/
#Override
public Void doInBackground() {
Random random = new Random();
int progress = 0;
//Initialize progress property.
setProgress(0);
//Sleep for at least one second to simulate "startup".
try {
Thread.sleep(1000 + random.nextInt(2000));
} catch (InterruptedException ignore) {}
while (progress < 100) {
//Sleep for up to one second.
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException ignore) {}
//Make random progress.
progress += random.nextInt(10);
setProgress(Math.min(progress, 100));
}
return null;
}
/*
* Executed in event dispatch thread
*/
public void done() {
Toolkit.getDefaultToolkit().beep();
startButton.setEnabled(true);
taskOutput.append("Done!\n");
}
}
public ProgressBarDemo2() {
super(new BorderLayout());
//Create the demo's UI.
startButton = new JButton("Start");
startButton.setActionCommand("start");
startButton.addActionListener(this);
progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
//Call setStringPainted now so that the progress bar height
//stays the same whether or not the string is shown.
progressBar.setStringPainted(true);
taskOutput = new JTextArea(5, 20);
taskOutput.setMargin(new Insets(5,5,5,5));
taskOutput.setEditable(false);
JPanel panel = new JPanel();
panel.add(startButton);
panel.add(progressBar);
add(panel, BorderLayout.PAGE_START);
add(new JScrollPane(taskOutput), BorderLayout.CENTER);
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
}
/**
* Invoked when the user presses the start button.
*/
public void actionPerformed(ActionEvent evt) {
progressBar.setIndeterminate(true);
startButton.setEnabled(false);
//Instances of javax.swing.SwingWorker are not reusuable, so
//we create new instances as needed.
task = new Task();
task.addPropertyChangeListener(this);
task.execute();
}
/**
* Invoked when task's progress property changes.
*/
public void propertyChange(PropertyChangeEvent evt) {
if ("progress" == evt.getPropertyName()) {
int progress = (Integer) evt.getNewValue();
progressBar.setIndeterminate(false);
progressBar.setValue(progress);
taskOutput.append(String.format(
"Completed %d%% of task.\n", progress));
}
}
/**
* Create the GUI and show it. As with all GUI code, this must run
* on the event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("ProgressBarDemo2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new ProgressBarDemo2();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
First, I'd recommend by defining a concept of a "progressable" state
public interface Progressable {
public void setProgress(int progress); // I prefer double, but we'll keep it inline with the rest of the API
}
Then, the entry point for your tasks would take a reference to Progressable
public class MySuperAwesomeLongRunningTask ... {
//...
private Progressable progressable;
public void performTask(Progressable progressable) {
this.prgressable = progressable
}
protected void methodThatDoesSomeWork() {
// Do some super duper work...
// calculate the progress of that work some how,
// based on your implementation...
int progress = ...;
progressable.setProgress(progress);
}
}
Then, create a SwingWorker which implements Progressable and calls your work...
class Task extends SwingWorker<Void, Void> implements Progressable {
private MySuperAwesomeLongRunningTask taskToBeDone;
public Task(MySuperAwesomeLongRunningTask taskToBeDone) {
self.taskToBeDone = taskToBeDone;
}
/*
* Main task. Executed in background thread.
*/
#Override
public Void doInBackground() {
taskToBeDone.performTask(this);
return null;
}
/*
* Executed in event dispatch thread
*/
public void done() {
// What ever you need to do...
}
}
Now, because SwingWorker already has a method called setProgress(int) it automatically conforms to Progressable (so long as you implement it), so when MySuperAwesomeLongRunningTask calls setProgress, it will actually be calling the SwingWorkers implementation.
This means, that the rest of the code basically remains the same, expect, I'd change
if ("progress" == evt.getPropertyName()) {
to
if ("progress".equals(evt.getPropertyName())) {
because comparing Strings with == is bad idea (and freaks me out :P)
I am slightly confused, I have a jFrame of which I have made in Netbeans. This jFrame has a jLabel, of which is set to setVisible(false); from the beginning. Whenever a specific method is called, I then set the jLabel to setVisible(true); and then use a timer to set it to false again after 2 seconds. Apparently it won't work and I am unable to figure out why. I am aware of the repaint(); method, but can figure out how to make that work either.
I know the actual method for setting the visibility is called, as I have set it to print a line with the current state, which it does.
My actual code is the one below.
public JFram() {
initComponents();
setResizable(false);
jLabel2.setVisible(false);
}
static void tesMethod() {
try {
//function that does something
} finally {
new JFram().showHide(); //call function which is supposed to change the vissibility of jLabel
}
}
void showHide() {
jLabel2.setVisible(true);
System.out.println("reached show");
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
jLabel2.setVisible(false);
System.out.println("reached timer");
}
},
2000
);
}
The code below here is how I tried to use the repaint(); method.
void showHide() {
jLabel2.setVisible(true);
jLabel2.repaint();
System.out.println("reached show");
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
jLabel2.setVisible(false);
jLabel2.repaint();
System.out.println("reached timer");
}
},
2000
);
}
I think your problem lies mainly in you using a java.util.Timer instead of a javax.swing.Timer and probably you're blocking the Event Dispatch Thread (EDT).
You could try this code and compare it with yours, I also don't see where you're adding your JLabel to your frame.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ShyLabel {
private JFrame frame;
private JLabel label;
private Timer timer;
private boolean isVisible;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ShyLabel().createAndShowGui();
}
});
}
public void createAndShowGui() {
String labelText = "I'm a shy label that hides every 2 seconds";
isVisible = true;
frame = new JFrame(getClass().getSimpleName());
label = new JLabel(labelText);
timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(isVisible ? "" : labelText);
isVisible = !isVisible;
}
});
timer.setInitialDelay(2000);
timer.start();
frame.add(label);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
The below image is produced by the above code, however because of the time I recorded the GIF it looks really fast instead of taking 2 seconds as it should be...
May be it is a problem of layout.
As you set resizable to false before any layout calculation occurred, the label was ignored (as invisible) by the time of the first layout.
You could try revalidate().
I'm quite new on JAVA, and i have a question (i hope my english is not too bad).
Here is my process :
Open a first JFrame in the Main, with a JButton (to open the second
JFrame).
On click, with ActionLister, i call the process to open my second
window, with a black background (this works very well).
BUT, if i add a long process (in my code, just a sleep(5000)) just after setVisible() my second JFrame, this one will appear in white, and waits for the sleep(5000) to end before being black.
Questions :
Can someone tell me why the second JFrames appears white until the
end of process ? Maybe i make something wrong when i build my JFrame
?
Can someone tell me how to show my second JFrame black BEFORE the process ends ?
I searched for a long time, and saw that if my second window is built direct in the main thread it's ok even with the sleep before end of process.
But when i am in another thread (like when i click on the button), that doesn't work good !
SECOND PART :
On click on the button from the first window :
The second window shows up (empty with black background). then, the result's calcul is launched.
Calculate the result cant take 20sec, and will find 1 element each 5 seconds.
Each times an element is found, i want it to be shown in the second window.
For that, i added an observer on this result from the JFrame, which will add an element each time one element is found. I hope you understand.
Here picture of what i want to make :
Process
Here my project .JAR : http://dl.free.fr/b5IUSStBJ
Here my result's calcul :
public void launchCalculateResult(){
String[] tabelements = {"test1","test2", "test3", "test4", "test5"};
for (int i=0; i < 5; i++){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
_elementslist.add(tabelements[i]);
notifyObservers();
}
}
you can see that it adds an element in a list each 2 seconds, and then notify the observers (my second window), then the observer adds an element :
public void refresh(Observable o) {
_otherwindow.addResultElement(_result.getLastElement());
}
The behaviour I got :
The Result calculates good, and in the end the second window looks good, with its 5 elements. But during the result's search, my second windows remains empty and white . . .
I repeat the aim :
Each time an element is added in the Result, i want to show it in my second window.
You're calling the long process on the Swing event thread, and this will tie up the thread preventing it from doing its important jobs, including painting your new JFrame.
The canonical solution is to use a background thread for your long processes, and for Swing GUI's, you'd want to use a SwingWorker -- if the background process needs to communicate with the GUI (which is usually the case).
For the details on this problem and solution, please check out: Concurrency in Swing
Side issue: you'll usually not want to show multiple JFrames in your application. For why this is important and for how you can improve this design, please check out Multiple JFrames
For example
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class SwingExample extends JPanel {
private JButton openDialogBtn = new JButton(new OpenDialogAction("Open Dialog"));
private JDialog dialog;
private DialogPanel dialogPanel = new DialogPanel();
public SwingExample() {
setPreferredSize(new Dimension(400, 400));
add(openDialogBtn);
}
private class OpenDialogAction extends AbstractAction {
public OpenDialogAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
dialogPanel.setText("");
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(SwingExample.this);
dialog = new JDialog(win, "Dialog", ModalityType.MODELESS);
dialog.add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(win);
}
new SwingWorker<Void, Integer> () {
private final int maxI = 5;
#Override
protected Void doInBackground() throws Exception {
for (int i = 0; i < maxI; i++) {
publish(i);
Thread.sleep(1000);
}
return null;
}
protected void process(java.util.List<Integer> chunks) {
for (Integer chunk : chunks) {
dialogPanel.setText("Time: " + chunk);
}
};
protected void done() {
dialogPanel.setText("Done!");
};
}.execute();
dialog.setVisible(true);
}
}
private class DialogPanel extends JPanel {
private JTextField textField = new JTextField(10);
public DialogPanel() {
setBackground(Color.BLACK);
setPreferredSize(new Dimension(200, 200));
add(textField);
}
public void setText(String text) {
textField.setText(text);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
SwingExample mainPanel = new SwingExample();
JFrame frame = new JFrame("SwingExample");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
Example 2: handles Strings being passed into a JList<String> using a SwingWorker<Void, String>
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class SwingExample extends JPanel {
private JButton openDialogBtn = new JButton(new OpenDialogAction("Open Dialog"));
private JDialog dialog;
private DialogPanel dialogPanel = new DialogPanel();
public SwingExample() {
setPreferredSize(new Dimension(400, 400));
add(openDialogBtn);
}
private class OpenDialogAction extends AbstractAction {
public OpenDialogAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
dialogPanel.clearList();
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(SwingExample.this);
dialog = new JDialog(win, "Dialog", ModalityType.MODELESS);
dialog.add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(win);
}
new SwingWorker<Void, String>() {
#Override
protected Void doInBackground() throws Exception {
String[] tabelements = { "test1", "test2", "test3", "test4", "test5" };
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
publish(tabelements[i]);
}
return null;
}
protected void process(java.util.List<String> chunks) {
for (String chunk : chunks) {
dialogPanel.addText(chunk);
}
};
protected void done() {
dialogPanel.addText("Done!");
};
}.execute();
dialog.setVisible(true);
}
}
private class DialogPanel extends JPanel {
private DefaultListModel<String> listModel = new DefaultListModel<>();
private JList<String> jList = new JList<>(listModel);
public DialogPanel() {
jList.setPrototypeCellValue("ABCDEFG HIJKLMNOP");
jList.setVisibleRowCount(6);
JScrollPane scrollPane = new JScrollPane(jList);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
setBackground(Color.BLACK);
setPreferredSize(new Dimension(200, 200));
add(scrollPane);
}
public void clearList() {
listModel.clear();
}
public void addText(String text) {
listModel.addElement(text);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
SwingExample mainPanel = new SwingExample();
JFrame frame = new JFrame("SwingExample");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
//Make constructor class for both JFrame then
//write this code into your JFrame where your button is accesing another JFrame
//Note:- jb=button var name,
// jf=JFrame vatr name,
// addnew()=JFrame Class to be open.
jb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
new addnew();
jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
});
It might work as well.
When I start my application it opens a JFrame (the main window) and a JFilechooser to select an input directory, which is then scanned.
The scan method itself creates a new JFrame which contains a JButton and a JProgressBar and starts a new Thread which scans the selected Directory. Up until this point everything works fine.
Now I change the Directory Path in my Main Window, which calls the scan method again. This time it creates another JFrame which should contain the JProgressBar and the JButton but it shows up empty (The JFrame Title is still set).
update:
minimal example
public class MainWindow
{
private JFrame _frame;
private JTextArea _textArea;
private ProgressBar _progress;
public MainWindow() throws InterruptedException, ExecutionException
{
_frame = new JFrame("Main Window");
_textArea = new JTextArea();
_frame.add(_textArea);
_frame.setSize(200, 200);
_frame.setVisible(true);
_textArea.setText(doStuffinBackground());
_progress.dispose();
}
private String doStuffinBackground() throws InterruptedException,
ExecutionException
{
setUpProgressBar();
ScanWorker scanWorker = new ScanWorker();
scanWorker.execute();
return scanWorker.get();
}
private void setUpProgressBar()
{
// Display progress bar
_progress = new ProgressBar();
}
class ProgressBar extends JFrame
{
public ProgressBar()
{
super();
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
progressBar.setStringPainted(false);
add(progressBar);
setTitle("Progress Window");
setSize(200, 200);
toFront();
setVisible(true);
}
}
class ScanWorker extends SwingWorker<String, Void>
{
#Override
public String doInBackground() throws InterruptedException
{
int j = 0;
for (int i = 0; i < 10; i++)
{
Thread.sleep(1000);
j += 1;
}
return String.valueOf(j);
}
}
public static void main(String[] args) throws InvocationTargetException,
InterruptedException
{
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
// Start the main controller
try
{
new MainWindow();
}
catch (InterruptedException | ExecutionException e) {}
}
});
}
}
From the basic looks of your scan method, you are blocking the Event Dispatching Thread, when you scan the directory, which is preventing it from updating the UI.
Specifically, you don't seem to truly understand what Callable and FutureTask are actually used for or how to use them properly...
Calling FutureTask#run will call the Callable's call method...from within the current thread context.
Take a look at Concurrency in Swing for more details...
Instead of trying to use FutureTask and Callable in this manner, consider using a SwingWorker, which is designed to do this kind of work (and uses Callable and FutureTask internally)
Have a look at Worker Threads and SwingWorker for more details
Now, before you jump down my throat and tell me that "it works the first time I ran it", that's because you're not starting your UI properly. All Swing UI's should be create and manipulated from within the context of the Event Dispatching Thread. You main method is executed in, what is commonly called, the "main thread", which is not the same as the EDT. This is basically setting up fluke situation in where the first time you call scan, you are not running within the context of the EDT, allowing it to work ... and breaking the single thread rules of Swing in the process...
Take a look at Initial Threads for more details...
I would also consider using a JDialog instead of another frame, even if it's not modal, it makes for a better paradigm for your application, as it really should only have a single main frame.
Updated based on new code
So, basically, return scanWorker.get(); is a blocking call. It will wait until the doInBackground method completes, which means it's block the EDT, still...'
Instead, you should be making use of the publish, process and/or done methods of the SwingWorker
import java.util.ArrayList;
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class MainWindow {
private JFrame _frame;
private JTextArea _textArea;
private ProgressBar _progress;
public MainWindow() {
_frame = new JFrame("Main Window");
_textArea = new JTextArea();
_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_frame.add(new JScrollPane(_textArea));
_frame.setSize(200, 200);;
_frame.setVisible(true);
doStuffinBackground();
}
private void doStuffinBackground() {
// _progress = new ProgressBar();
// ScanWorker scanWorker = new ScanWorker();
// scanWorker.execute();
// return scanWorker.get();
_progress = new ProgressBar();
ScanWorker worker = new ScanWorker(_textArea, _progress);
worker.execute();
_progress.setVisible(true);
}
class ProgressBar extends JDialog {
public ProgressBar() {
super(_frame, "Scanning", true);
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
progressBar.setStringPainted(false);
add(progressBar);
setTitle("Progress Window");
pack();
setLocationRelativeTo(_frame);
}
}
class ScanWorker extends SwingWorker<List<String>, String> {
private JTextArea textArea;
private ProgressBar progressBar;
protected ScanWorker(JTextArea _textArea, ProgressBar _progress) {
this.textArea = _textArea;
this.progressBar = _progress;
}
#Override
protected void process(List<String> chunks) {
for (String value : chunks) {
textArea.append(value + "\n");
}
}
#Override
public List<String> doInBackground() throws Exception {
System.out.println("...");
int j = 0;
List<String> results = new ArrayList<>(25);
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
j += 1;
System.out.println(j);
results.add(Integer.toString(j));
publish(Integer.toString(j));
}
return results;
}
#Override
protected void done() {
progressBar.dispose();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MainWindow();
}
});
}
}
An application I am writing consists, among others, a JButton and a JTextArea. A click on the button leads to a long calculation, resulting in a text shown in the JTextArea. Even though the calculation is long, I can have middle-results on the go (think, for example, of an application which approximates pi up to 100 digits - every few seconds I could write another digit). The problem is, that even if I write (being in the ActionListener class because the button invoked the calculation) to set the text of the JTextArea to something, it isn't shown while the calculation is done, and I can only see the end result, after the calculation is over.
Why is it so, and how can I fix it?
Thank you in advance.
Your problem is that you're doing a long calculation in the main Swing thread, the EDT, and this will freeze your entire GUI until the process has completed itself. A solution is to use a background thread for your calculation, and an easy way to do this it to use a SwingWorker to create a thread background to the main Swing thread, the EDT, and publish/process the interim results into the JTextArea. For more on SwingWorkers and the EDT, please look here: Concurrency in Swing
Also, if you provide a decent sscce we can probably give you a more detailed response perhaps even with sample code.
An example SSCCE:
import java.awt.event.*;
import java.text.DecimalFormat;
import java.util.List;
import javax.swing.*;
public class InterimCalc {
private JPanel mainPanel = new JPanel();
private JTextField resultField = new JTextField(10);
private JButton doItBtn = new JButton("Do It!");
private DecimalFormat dblFormat = new DecimalFormat("0.0000000000");
private SwingWorker<Void, Double> mySwingWorker = null;
public InterimCalc() {
mainPanel.add(doItBtn);
mainPanel.add(resultField);
displayResult(0.0);
doItBtn.addActionListener(new DoItListener());
}
public void displayResult(double result) {
resultField.setText(dblFormat.format(result));
}
public JPanel getMainPanel() {
return mainPanel;
}
private class DoItListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (mySwingWorker != null && !mySwingWorker.isDone()) {
mySwingWorker.cancel(true);
}
displayResult(0.0);
mySwingWorker = new MySwingWorker();
mySwingWorker.execute();
}
}
private class MySwingWorker extends SwingWorker<Void, Double> {
private static final int INTERIM_LENGTH = 10000; // how many loops to do before displaying
#Override
protected Void doInBackground() throws Exception {
boolean keepGoing = true;
long index = 1L;
double value = 0.0;
while (keepGoing) {
for (int i = 0; i < INTERIM_LENGTH; i++) {
int multiplier = (index % 2 == 0) ? -1 : 1;
value += (double)multiplier / (index);
index++;
}
publish(value);
}
return null;
}
#Override
protected void process(List<Double> chunks) {
for (Double dbl : chunks) {
displayResult(dbl);
}
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("Decay Const");
frame.getContentPane().add(new InterimCalc().getMainPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
you may also want to display some sort of spinning gif or "progress bar" to show that the answer is being calculated; feedback to the user is good.
(once you are using a swingworker, then the gui won't freeze and the gui can do its own thing while the calculation is taking place)