Progress bar for loading out another JFrame class - java

I have 2 JFrame: NewJFrame and NewJFrame1. NewJFrame has a progress bar and a button. the button is used to call up NewJFrame1. When the button is clicked it should trigger the progress bar to run until NewJFrame1 pop up. However, how do I make use of the swing worker and let the progress to run until NewJFrame1 actually competely loaded all of its component?
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
SwingUtilities.invokeLater(new Runnable() {
int i = 0;
public void run() {
jProgressBar1.setValue(i++);
new NewJFrame1().setVisible(true);
}
});
}

Let your top-level container have a SwingWorker that loads it's content, as shown here, and a PropertyChangeListener, as shown here. When doInBackground() calls setProgress(), the listener will see the event.
Addendum: How do we know how much time left for the GUI to be completely loaded? …in examples, the progress bar is run with random number as simulated latency.
Correct; the simulated latency represents the granularity of your loading. Displaying the fraction of total bytes loaded would be ideal, but you may have to settle for a coarser frequency. Note that models may be constructed in the background, before any components are listening. GUI components should be constructed and manipulated only on the EDT, possibly in the worker's process() implementation.

Related

Java - Where and how to add indeterminant progress bar to existing Swing application that calls a batch job [duplicate]

I need a very simple (skeletal) tutorial on progress bars for developing a GUI in Java. The progress bar can be "indeterminate" and ideally have an updateable text label.
Here is some skeletal code that I hope someone can expand upon just enough to get something working.
class MyFunView extends FrameView {
// class vars and other stuff...
#Action
public void excitingButtonPressed() {
/* I have n files to download */
// Download file 1, set progress bar status to "downloading file 1"
// ...
// Download file n, set progress bar status to "downloading file 2"
}
}
Basically, how and what do I wrap my download code in? (I understand there is some kind of subclass of Task required?)
Thanks much in advance.
EDIT:
We all only have so much precious time to be alive, and no approach can be done in as little code or time as I would have hoped, so I will not be using a progress bar.
I need a very simple (skeletal) tutorial on progress bars for developing a GUI in Java.
How to Use Progress Bars
Concurrency in Swing
Initial Threads
Event Dispatch Thread
SwingWorker
examples here
You can use SwingWorker in conjunction with a JProgressBar.
The other alternative is to execute your task (e.g. Runnable) and periodically update the JProgressBar. Beware though, that in Swing any UI manipulations have to be done on the EDT. SwingWorker has a nice property of taking care of this detail. Your own solution will have to use SwingUtilities.invokeLater,. for example:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressBar.setValue(progressCounter++);
}
});
Lot of code for a stupid thing, isn't it? Maybe the SwingWorker with a PropertyChangeListener is a better solution in the end...

Make an ActionListener commit a change to a JButton before it has finished executing the entire ActionListener

In Java, I'm using an ActionListener for an array of JButtons. I would like for an earlier part of the ActionListener to set a new ImageIcon to a JButton, that change to be displayed immediately, then near the end of the ActionListener to set the JButton's ImageIcon back to null after a second long delay.
My problem is that none of the changes that happen to the JButton get displayed in the GUI window that it is set in until the ActionListener is completely finished, making the change in the JButton's ImageIcon unnoticeable. Is there any way to make an ActionListener commit a change to a JButton before it has finished executing the entire ActionListener, or should I be going about this differently?
The reason this is happening:
Swing repaints the buttons on the same thread (EDT) as the ActionListener is ran on. Hence if it is executing you ActionListener it cannot repaint since the thread is busy - as simple as that. You may have noticed that while your action listener is executing you also can't properly move your frames around etc. (GUI freezes up).
The solution:
Move heavy processing outside the EDT. It doesn't belong there anyway. As you could have guessed - use a background thread/thread pool for that. A good guide to it is Swing tutorial for concurrency
Notes:
As portrayed in the guide you do not want to modify components outside the EDT. As such the easiest strategy is to make a Runnable to execute on a background thread, start it, change the picture on the button and return without waiting for the task to finish.
public void actionPerformed(ActionEvent ae) {
Runnable task = new Runnable() {..};
executor.execute(task);
button.setIcon(newIcon);
return;
}
Note that this doesn't lock up the EDT for the task, hence allowing Swing to change the picture immediately.
This of course means that the user has no idea if the task has finished or not (And if there were any exceptions)! It is in the background after all! Hence there is an extra state of your execution: GUI is responsive and non-frozen, button is changed, but task is still running. In most applications this may be a problem (the user will spam the button or your background tasks may interleave). In that case you may want to use a SwingWorker to have a "processing" state as well.
public void actionPerformed(ActionEvent ae) {
new TaskWorker().execute();
button.setIcon(loadingIcon); //Shows loading. Maybe on button, maybe somewhere else.
return;
}
private class TaskWorker extends SwingWorker<Void, T> {
public Void doInBackground() {
//Do your task in the background here
}
protected void done() {
try {
get();
button.setIcon(doneIcon);
catch (<relevant exceptions>) {
button.setIcon(failedIcon);
}
}
}
Here done() is called on the EDT when doInBackground() is finished.
You could create a new Thread or a Thread from a Thread pool if you have one. And with that let the task work on a seperate Thread so that the ActionListener returns immediatly. And then in the other Thread you do your code and repaint the button. This is by the way a threory I'm not sure if it will work.

InvokeLater() - will just once be enough?

I use the recommended code to start my interactive program, which uses Swing. :
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(
new Runnable() {
public void run() {
createAndShowGUI();
} } ) };
It creates a JFrame, call it "Foo", and ends when the user closes that window (by using the OS' Close- Window X icon or another OS way to close the app)
I want to display another window, "Bar", get user input, close that window with my own Swing calls, and then display the "Foo" window.
I could reuse the same JFrame for Foo and Bar, and just clear it out in-between. But I prefer to make each of them more independent in their design. So, an ActionListener in Foo would need to close Foo's frame, and call the code that displays Bar.
Do I need to use InvokeLater() to call the code that displays Bar? To get more design independence, should my main() be starting and synchronizing two threads?
User triggered action listener code is executed in the EDT, so you do not need to wrap it again. That said, it may well be that using CardLayout would be more appropriate than multiple frames.
I could reuse the same JFrame for Foo and Bar, and just clear it out
in-between. But I prefer to make each of them more independent in
their design. So, an ActionListener in Foo would need to close Foo's
frame, and call the code that displays Bar.
Do I need to use InvokeLater() to call the code that displays Bar? To
get more design independence, I am unclear on the operation of the
EDT.
you would use pack() and setVisible(true) wrapped into invokeLater in all cases for
for new Top-Level Container - alyways
for container created but never dispayed - always
for container once visible, then hidden and again visible on the screen - always
doesn't matter if is invoked from Swing Listener (by default on EDT) or not
to my point 3rd. to avoiding any unwanted Graphics lacks in the case that you reused Top-Level Container (is specifics, very short moment, but could be visible, but not, never annoying)
old value is visible, then immediatelly refreshed to current
old JComponent is visible, then immediatelly refreshed with current JComponents
relayout/ pack() , the same issue as a.m.
invokeLater to delay (in most casses with success) this event to the end of EDT
see my view translated to the code demonstration about

Initializing Java Swing in the background

I have a question regarding quick initialization of swing components. At the start of my swing application I have a window that pops up with buttons that allow the user to do a variety of things. Is there I way that I can quickly launch that first window and load the rest of the UI (such as other frames and dialogs) in the background so that there is isn't that initial delay.
Thanks,
Is Swing thread safe?
Yes. Ish. You could do something similar to:
public static void main(String [] args) {
// Construct main Frame on Swing EDT thread
Runnable goVisible = new Runnable() {
public void run() {
JFrame mainFrame = new JFrame();
mainFrame.setVisible();
// etc.
}
};
SwingUtilities.invokeLater(goVisible);
// now the background init stuff
Class.forName("com.yourcompany.view.Dialog1");
Class.forName("com.yourcompany.view.WizardGUI");
Class.forName("com.yourcompany.view.SecondaryFrame");
// Here all the views are loaded and initialized
}
Display blank main frame first and then load the rest of UI. You can use Swing Application Framework (or BSAF now) to init components and build layout when app is ready (main frame is visible) - Application.ready() method. Use http://code.google.com/p/jbusycomponent/ to show that app is loading...
There really is no good solution to this, it is one of the drawbacks of Java. That being said keep reading for my idea.
There are two parts to loading a class.
The JVM loads the class file into
the ClassLoader when it is needed.
The JIT compiles and optimizes the
code the first time the path is run.
You can do what rekin suggests, which is to eagerly load the UI classes before they are needed. That will only partially solve your problem, because you are only getting some of the classes. This will also have the disadvantage of taking up a lot more memory and even the classes in the class loader will be garbage collected if needed.
In order to avoid some of the hassles you are getting with the Reflection Approach.
One method you could try is in your windows make sure the constructor does not display a window, instead have another method that would display the window called init(), Then have a separate Thread from main call create a new on each of the Windows you want to preload.
Do not save the reference to the window.
In the real code you would call the constructor and then init() for each window you wanted to display. This would give you the best possible scenario as far as performance, because now you are loading the classes as well as running the constructor code. Of course the size of the program in memory will be bloated.
public static void main(String [] args) {
// Construct main Frame on Swing EDT thread
Thread thread = new Thread() {
public void run() {
// now the background init stuff
new com.yourcompany.view.Dialog1();
new com.yourcompany.view.WizardGUI();
new com.yourcompany.view.SecondaryFrame();
// Here all the views are loaded and initialized
}
};
JFrame mainFrame = new JFrame();
mainFrame.setVisible();
// etc.
}
That's the goeal of having a Splash Screen(with or without a progress bar - much nicer with it, of course). You should show a nice splash to your users and then you initialize all your components starting with the main window on the EDT thread and at the end you show up your frame. Creating Swing components outside EDT might(it will sure do) create problems with visibility at least but also with concurrent access between your thread and EDT. DON'T do that, it's hard to detect these issues and it might manifest random on different hardware.
Of course, if you have a progress bar you need some free EDT time to render progress bar changes - actually even to refresh the splash screen itself(repainting the background image if another application blocked for a while your splash) you need free time on the EDT.
You should split your initializations in smaller blocks that will not take more than 500ms to run and you schedule them on the EDT with SwingUtilities.invokeLater.

What is the correct way of manipulating Swing components at program startup?

I'm creating an application in Swing using NetBeans. I would like to be able to manipulate some components during its startup (just once), after the window's made visible, for example update a progress bar. To this end, I have the app's main class, called MainWindow:
public class MainWindow extends JFrame
{
public MainWindow()
{
initComponents(); // NetBeans GUI builder-generated function for setting
// up the window components
}
public void Init()
{
loadLabel.setText("Loading....");
loadProgressBar.setValue(20);
doSomething();
loadProgressBar.setValue(40);
doSomething();
loadProgressBar.setValue(80);
doSomething();
loadProgressBar.setValue(100);
loadLabel.setVisible(false);
loadProgressBar.setVisible(false);
}
/* .... */
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
mainHandle = new MainWindow();
mainHandle.setVisible(true);
mainHandle.Init();
}
});
}
}
The problem is that the effect of the statements for updating the progress bar (or manipulating any other GUI component) within the Init() function can't be observed. If the Init() function is called from within main() as shown above, the window appears, but is empty, the Init() function executes and returns, only afterwards the window draws its contents but any changes made by Init() aren't visible because the window was empty and inactive the whole time. I also tried calling init from the windowOpened() AWT event, which executes after the window is fully drawn, but amazingly putting any statements for manipulating components there seems to have no effect, or rather they are put in a queue, and executed rapidly at some point in succession, so only the effect of the last one (hiding of the elements) can be observed. The only way I managed to get it working was to remove the whole invokeLater(new Runnable()...) mantra and put the new MainWindow(), setVisible(), Init() sequence directly in main(), which I guess is very ugly and breaks the concept of the gui running in a threaded manner. What is the right way to do this? Where do I put code to be executed first thing when the gui is ready to be manipulated, execute the statements once and return control to the main event loop?
I guess at the moment this is working in such a way, that while the Init() function is operating, any operations on the gui components are suspended (the drawing thread isn't separate and waits for Init() to finish before the manipulations are executed). Maybe I should make Init() a new thread... only how and what kind?
Thanks.
You could change the EventQueue.invokeLater() to invokeAndWait(), and move the call to init() out to a second EventQueue.invokeLater() call.
If (as looks to be the case) doSomething() takes a noticable amount of time, a better idea is to move the Init code into the body of a SwingWorker. This could be executed from the MainWindow() constructor or after the setVisible() call in main and is the idiomatic way to have a responsive GUI (in case the user gets bored waiting and wants to quit) and display some visible signs of progress.
See the process and publish methods for details on how to update the progress bar between doSomething() calls.
You may also want to look into ProgressMonitors for another alternative that would deal with the dialog box etc for you.
There are several things you can do:
For windows (such as JFrame or JDialog) you can attach WindowListener and do your manipulations in windowOpened method.
Override addNotify method and do your control manipulations there.
Attach HierarchyListener and do your manipulations whenever displayability of component changed.
Always make sure your do your component manipulations on EDT. Use SwingUtilities.invokeLater for simple UI updates or SwingWorker for long running tasks

Categories

Resources