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
Related
In my program, I need to wait for the user to input from a JFrame. When the user is done with the first input they press a JButton. This calls a constructor for a Class I made: HumanTrainer. In the constructor I need the user to have more inputs. I made two functions to wait and notify. But when the code gets to wait, everything freezes, and the JFrame doesn't update to what it should.
This is the action the first button preforms
startingButton.addActionListener((e)->{
Trainer[]t=new Trainer[2];//HumanTrainer extends Trainer
String[]names=new String[2];
for(int a=0;a<2;a++)
names[a]=((JTextField)(startingInputs[2][1+a])).getText();
grid.removeAll();//The JPanel that the Frame has
Frame.repaint();//The JFrame
Frame.validate();
if(isHuman[0])
t[0]=new HumanTrainer(names[0],grid,Frame);//The constructor
if(isHuman[1])
t[1]=new HumanTrainer(names[1],grid,Frame);
});
And the constructor for HumanTrainer
HumanTrainer(String name,JPanel grid,JFrame Frame){
super(name);
GridBagConstraints manager=new GridBagConstraints();
manager.gridx=0;
manager.gridy=0;
manager.gridheight=1;
manager.gridwidth=1;
manager.fill=GridBagConstraints.HORIZONTAL;
Font Format=new Font("Courier New",Font.PLAIN,14);
JButton cont=new JButton("Continue");//This is the button that when clicked should run the function that notifies
grid.add(cont,manager);
grid.repaint();//One of these four things SHOULD change the view of the frame
grid.validate();
Frame.repaint();
Frame.validate();
System.out.print("TEST");//This prints
cont.addActionListener((e)->{
made();//This is a function contained in HumanTrainer that only calls notify();
});
make();//This is a function contained in HumanTrainer that only calls wait(); With the proper try and catch
}
But when starterButton is pressed the screen freezes and doesn't update so that cont can be pressed.
Start by taking a look at Concurrency in Swing. Swing uses a single thread to dispatch it's events from, if you perform any long running or blocking operation from within the EDT (like calling wait), then it freeze your program and the user will have to terminate it.
You have two basic choices. You can either use a modal dialog to gather information from the user, see How to Make Dialogs, which will block the execution of the code at the point they are displayed, without blocking the entire EDT OR use a observer pattern, which can generate notifications that the user has provided what ever information you are expecting.
To be honest, a modal dialog is generally easier and can help prevent unexpected side effects
This...
make();//This is a function contained in HumanTrainer that only calls wait(); With the proper try and catch
would seem to be the core of your problem, but without more information and context of what you are trying to do, it's impossible to really suggest what you should do, however I'd recommend having a look at model-view-controller and seperare your code into more appropriate layers
First of all, you may want to close that for loop with {} so that it doesn't loop the whole code twice. Also you should check if wait() and notify() work correctly by individually testing them without java awt.
I know this is sort of a vague question, but I will try to make it as clear as possible. When my Java app launches for the first time, it does some checking to see if files and directories exist and checks for an internet connection.
The app then allows the user to move on to the dashboard, which requires loading and adding many Swing components to the Background Panel. This takes time.
I was wondering how I could make it so that during the loading process at the start, the app loads all of the Swing components Images etc. so that they appear instantly when the user executes the command to do so.
I can load all of the components like this:
JButton = new JButton("blah");
but I'm not sure that's enough to make the components appear instantly, wouldn't adding several image filled Swing components at the same time still lag the UI thread, even if it was already "loaded" as seen above?
Thanks!
on the components use
setVisible(false)
for example
public class myParentPanel extends JPanel{
public myParentPanel{
//add all the child components
//Do what ever takes along time
setVisible(false); //this then makes in invisible but still
//allows all the set up code to run
}
public void showParent(){
setVisible(true);
invalidate();
}
}
and then create a method to make them visible when required. Hence all your setting in the constructors ca be called then when you call your method say:
drawWindow()
it then only has to call setVisible(true) and invalidate the screen to call their painting methods :).
An advancement on this would be to *run your setups i.e your checking and loading the panels methods on a separate threads*so you don't have to wait for a sequential loading. So on your loading you may to use an anonymous class
SwingUtilities.invokeLater(new Runnable(){ public void run(){
//do my display set up
}});
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.
I have a closeWindow() method which uses dispose() for the current JFrame to close down. When I show the window again, the controls (textboxes, lists, tables etc.) still have their previous values in place that were there when I dispose():d the frame... Why is that? Is there another way to completley close and clear a frame?
This is the code that another JFrame uses to show the other window, am I doing something wrong here?
#Action
public void showAddProductToOrderView() {
if (addProductToOrderView == null) addProductToOrderView = new AddProductToOrderView(this);
addProductToOrderView.setVisible(true);
}
Disposing a window will not clear its child text components. Dispose will release native resources. The javadoc for java.awt.Window also states:
The Window and its subcomponents can be made displayable again by rebuilding the native resources with a subsequent call to pack or show. The states of the recreated Window and its subcomponents will be identical to the states of these objects at the point where the Window was disposed (not accounting for additional modifications between those actions).
As suggested by others, create a new instance each time instead. If that's to expensive I believe your best option is to clear sub components when the view becomes visible, e.g. by overriding setVisible.
EDIT:
Remove the null check to create a new frame each time.
#Action
public void showAddProductToOrderView() {
addProductToOrderView = new AddProductToOrderView(this);
addProductToOrderView.setVisible(true);
}
I don't know about the rest of your code, if there's something else depending on the frame being reused. For example, if you have attached listeners, ensure they are unregistered to not leak them.
The simplest thing to do would be to re-create the whole frame (using its constructor) before using show() to show it again. That will give you a whole new set of components, assuming that the constructor creates and places them.
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