I have action thread and since it is Swing software, EDT.
I want my program to draw dialog window, and when it appears and it's filled with data, I want to get focus on selected text field.
Code flow: When I execute, it will run main thread, which calls method to draw dialog in invokeLater on EDT. Then program proceeds and in main thread it calls next methods that are being run in ED thread, again using invokeLater.
Problem: When I run it normally, it will not get focus on my text field.
Observation: But when I add some sleep (300 milis) to main thread, introducing time gap between one invokeLater call and next call in EDT, it works just like I want.
It seems to me like two actions added to AWT queue must be separated by some time, otherwise the second one doesn't work. I mean here setVisible(true) on dialog, and then requestFocus() on textField. Maybe requestFocus() only work when it sees dialog window drawn?
Question:How can I make things work, some synchronization method, maybe checking on dialog before calling requestFocus() (may be hard, because its in other class).
Solution:I forgot about most important thing - after calling setVisible() next thing I do is call to setEnabled(false) so user cannot do anything before data filling is completed. The problem was there, in setEnabled() I also was adding tasks to AWT queue (by invokeLater()). This task caused corruption of next steps. What I do now to fix it is calling this setEnabled(false) from my main thread inside invokeAndWait(). If I understand it correctly, now the dialog popup section is called first, and then main thread waits until EDT proceed his work and then setEnabled(false) is called. So technically user is not enabled to do anything after the window is drawn, which makes sense for me.
Anyway thanks for your responses.
It's better to call the focus setting from the dialog. Add a WindowListener to the dialog and use either
public void windowOpened(WindowEvent e)
public void windowActivated(WindowEvent e);
to set focus on the JTextField instance
The requestFocusInWindow() method can only be invoked on a visible component. That means the frame/dialog must already be visible when you invoke the method.
If you are trying to do this on a modal dialog you may have problems. Check out Dialog Focus for a simple listener you can use to set focus on a component.
Related
Inside the actionPerformed method of a jButton, I have the following code:
btnLogin.setText("Logging In...");
btnLogin.setPreferredSize(new Dimension(110, 29));
btnLogin.setEnabled(false);
//more stuff here, irrelevant to this
This works, however it only takes visual effect (is repainted) once the method is complete.
If in the //more stuff here area I have code that takes a long time to complete, the effects of the btnLogin changes do not take effect until that code is complete.
I have tried typing:
this.revalidate();
this.repaint();
Directly after the first 3 lines, and multiple other solutions, to try to force the damn thing to repaint DURING the method, but no matter what, it only happens at the end!
Another thing I've noticed is that if I call a JOptionPane in the middle of the method, the frame WILL repaint (in the background), so that's interesting.
What is is that's automatically happening in the end of the method that I need to call to make it happen during the method?
Thanks in advance!
You're blocking the Swing event thread with the long-running code, and this prevents Swing from drawing the text changes. The solution:
Do the long-running code in a background thread such as in a SwingWorker's doInBackground method.
But make sure to make most all Swing calls on the Swing event thread.
Read the Concurrency in Swing tutorial to learn the details on the Swing event thread and threading issues.
I noticed a behavior that I can't explain. In my GUI, on a button click I display a custom Jdialog that has panel and bunch of textfield. I populate these textfields.
Here is the scenario I am seeing using pseduo code.
public void actionPerformed(ActionEvent e) {
CustomDialog viewDialog = new CustomDialog (Jframe, true);
viewDialog.setVisible(true);
viewDialog.populateInfo();
}
When the code above runs then all textfields are empty. However if I move the setVisible to after the populateInfo method then all the textFields are populated. Basically the JTextField.setText inside the populate info does not seem to have an affect if the setVisible happens before
Why is this!
Likely your CustomDialog class is a modal JDialog (also as suggested by the true 2nd constructor parameter). If so, then program flow in the calling code is blocked by the setVisible(true) call, and so your populateInfo() method will only be called after the dialog is no longer visible. The solution is as you already know -- call the method before displaying the dialog.
This is not a bug but a feature. :)
Seriously, since now you know for a fact when program code flow will be halted and when it will resume, and so you can safely query the dialog for its state after the setVisible(true) has been called, and feel confident that in the very least the dialog has been presented to the user, and the user has had time to interact with it and dispose of it.
I had this question while setting a JLabel visible when a button is clicked it is like a loading icon. The p.make() method is executed but the Label is still invisible after the Method returns the Label is visible.
Can someone explain what is happening?
ActionPerformed:
String[] args = {jTextFieldDrgzusatzVariable.getText(),jTextFieldAusgabe.getText(),"C:\\CPOracle",jTextFieldKatalog.getText()};
this.jLblLoading.setVisible(true);
if(jLblLoading.isVisible()){
try{
new P21Make(args[0],args[1],args[2],args[3]).make();
}catch(Exception e){
e.printStackTrace();
}
}
The reason is very simple: Swing is single threaded (see the Swing concurrency tutorial for more information).
What happens is that the actionPerformed method is called on the Swing thread (the E(vent)D(ispatch)T(hread)). When the
this.jLblLoading.setVisible(true);
statement is reached, it will immediately mark the jLblLoading as visible. However, this has no effect yet on the UI. The UI needs to be repainted before the change in visibility has any effect. This repaint is scheduled on the EDT (which is not the same as immediately executed).
This explains why your
if(jLblLoading.isVisible()){
check succeeds, and you still do not see the difference in the UI. The component is marked as visible, but the repaint is still pending. The repaint will remain pending until the EDT becomes available again. Since the thing that is currently occupying the EDT is your actionPerformed call, the rest of the code in that actionPerformed method will be executed before the repaint (meaning before you see a change in the UI).
Your solution using a different thread can indeed fix this. You can however only use that if the new P21Make(...).make() does not affect the UI. If that statement interacts with Swing components in any way, it should be executed on the EDT. In that case, an alternative is to wrap the statement in a SwingUtilities#invokeLater call.
You should probably look at using SwingUtilities.invokeLater to allow actions which modify the gui to complete.
http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
I am working on a program that is meant to keep track of footraces. The program is set up so that a right click(though this may be changed) at any time will indicate a runner has passed the finish line. This is important so that you can do things and still mark a runner as passing at any point. To finish the race and compile results, there is a button. To prevent a misclick from prematurely stopping the timer, I have a dialog that confirms while the timer is still running. This dialog is also right-clickable to mark a runner as passing. I have a method that gets the input from the dialog as a boolean.
public static boolean showDialog(Frame parent, boolean modal,String text,RacePanel r)
{
ConfirmBox c=new ConfirmBox(parent,modal,text);//this makes the dialog
for(Component comp:c.getComponents())//this adds a listener for right-click events, to record passing runners
r.addListener(comp);
c.setVisible(true);
return c.yes;//yes is the boolean that should be returned
}
I originally had the dialog modal, so that the setVisible method would wait for the dialog to close. However, this made the program ignore mouse clicks on the rest of the screen. When I set it as not modal, this showDialog method returned immediately, which was always false. I don't actually care if the dialog is modal or not.
Is there a way to get MouseEvents with a modal dialog up or make a non-modal dialog wait until input?
Thank you in advance for your advice.
Couple of solutions:
Use hot keys to react - will be faster. so F11 means stop race, with F12 to confirm it. And F9 to indicate a runner has passed the finish line
There are JNI packages for this like at http://code.google.com/p/jintellitype/
Have the user click the button and type a key or world like "end" for a sample see http://code.google.com/p/baby-smash/source/browse/src/quick/KeyBoardListen.java Do not like a dialog box as they take time to render and can increase to the timer.
If you must use a dialog box then implement your own. in that class take an instance of your main class (or interface that your controller implements) then call a method on the controller when window is acted on (yes or no pressed or window closed - default). In the window close do not close the window but just hide it. this will mean you can show it faster next time. You can also load this class on start up to make it ready in the background
If you just want mouse events can try extending JFrame and then over ride (javadoc of java.awt.Component):
protected void processMouseEvent(MouseEvent e)
Processes mouse events occurring on this component by dispatching them to any registered MouseListener objects.
This method is not called unless mouse events are enabled for this component. Mouse events are enabled when one of the following occurs:
A MouseListener object is registered via addMouseListener.
Mouse events are enabled via enableEvents.
You will need to use listeners to listen for changes in the dialog class's state. For instance a WindowListener can listen for the dialog's window closing. If you want to listen for other state changes in the dialog's classes, consider creating "bound" properties or fields by use of a PropertyChangeListener.
I have an application which depending on user input changes the entire content of the JFrame. I do this by removing the JPanel containing the current components and replace it by a new JPanel with new components.
After that one of the components needs to get focus and a JScrollpane should scroll to this component. In most cases this works properly.
Now one scenario leads to a JPanel beeing added, which itself contains more than 500 components. Rendering this takes some time and it seems that scrollRectToVisible() is called at a point, where the UI is not fully rendered. If I debug I can actually see that it first scrolls to the right position, but then further rendering is done and the component is moved out of the viewport again.
So I was trying find a Listener, which is called, when rendering is fully done. I tried with ComponentListener and AnchestorListener. Both didn't receive most of the events I was expecting. But even when they did the callback methods were called before any UI change was visible on the screen.
I swap the JPanels in EDT and call validate() on the JFrame afterwards. After that I do not process any further code. However, if I set a breakpoint in the last executed line and go one step further, the UI has not changed on screen. The EDT is actively doing something (I assume rendering the UI). And I would like to get notified, when the EDT has finished rendering.
Another thing I tried:
If I create another Thread that just sleeps for a few seconds (until the UI is definitely rendered) and call the scrollRectToVisible() then, everything works fine.
I'm sorry not to provide an SSCCE. I tried, but it seems to be rather complex. I really appreciate any idea on how I could get notified on the UI beeing fully rendered and visible to the user.
Thanks
If I create another Thread that just sleeps for a few seconds (until the UI is definitely rendered) and call the scrollRectToVisible() then, everything works fine.
Instead of sleeping, just wrap the scrollRectToVisibl() code in a SwingUtilities.invokeLater(). This will add the code to the end of the EDT so it should be processed after all the other rendering.
Add a property change listener:
jPanel.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(final PropertyChangeEvent evt) {
System.out.println(evt);
}
});
Produces something like:
java.beans.PropertyChangeEvent[propertyName=ancestor; oldValue=null; newValue=javax.swing.JPanel[null.contentPane,0,0,0x0,invalid,layout=javax.swing.JRootPane$1,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,preferredSize=]; propagationId=null; source=javax.swing.JPanel[,0,0,0x0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,preferredSize=]]
Maybe JComponent.addNotify() is what you need.
If you override that method, make sure that you call the super.addNotifty() as well!