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
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 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.
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!
In a method of a class, I update the same label twice. The first time, it shows the user message to wait, but the second time shows the user the completed message. Something like the following:
MyClass{
myMethod(){
jLabel.setText("Please wait...");
//does calculation
jLabel.setText("Completed successfully!");
}
}
When I run the app, all I see is the "Completed successfully" message. Is the JLabel updating too quickly? How do I control it? I tried using the following but no luck :(
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
jLabel.setText("Please wait...");
}
});
If the calculation is done in the event dispatch thread, then it blocks the thread and prevents it from doing all its repaintings. You must do the computation in another thread, and have this thread change the label text when it ends (in the event dispatch thread, using SwingUtilities.invokeLater, or by using the SwingWorker mechanism). If the computation is really fast, it's not worth it, though, because the second text will appear so quickly after the first one that you won't even notice the first one.
Have a look at SwingWorker, which is designed for such use-cases. Its javadoc contains a useful example.
if you want to delay some Action/Event then use javax.swing.Timer, or wrap your code to the Runnable#Thread,
notice: never use Thread.sleep(int) durring EDT, your GUI freeze until Thread.sleep(int) ended
example for javax.swing.Timer & Runnable#Thread & Freeze GUI by implements Thread.sleep(int) durring EDT here
Possibly because your calculations are happening too fast. Did you try putting a delay after your calc.
Also as Nizet points out above if this is happening in EDT the component will not repaint until end of the thread which means it will take the last set value.
I'm using the java swing library to develop a board game called DAO.
The problem is that after the human player makes its move, by clicking on the JButton with the piece image that he wants to play, I call the computer AI routine but inside the mouse event function. By doing this only when the function returns, the computer ends its turn, do the JButtons refresh their Images (setIcon comes in).
I'd like to know how can I force the JButtons to change their image at the moment they are clicked and not only when the mouse event function ends (as I need to handle data inside it).
I've tried all of this
myButtons[i][j].setIcon(xIcon);
myButtons[i][j].revalidate();
myButtons[i][j].repaint();
myButtons[i][j].validate();
None worked.
Thx in advance
You may want to try putting the action performed upon clicking the JButton into a Swing worker. This will allow the task to go on in the background, while the user can still click other buttons, etc.
See http://java.sun.com/docs/books/tutorial/uiswing/concurrency/simple.html.
There is a single thread used for all Swing activity.
Here's the process.
One event appears on the event queue
it is pulled from the queue and executed by The AWT Thread
Any new events created while this is executing are placed on the queue to be held until the currently running AWT event returns.
The event executing returns and the next event on the queue is dequeued and executed.
This means that if you need to do anything that takes more than, say 1/100 of a second or so, you shouldn't do it any thread started from a swing event. Instead, spawn your own thread and return the swing thread to the system so the GUI can be updated.
Now, your thread MUST NOT update any GUI objects! If you need to update a GUI object, use invokeLater to place your code back on the AWT thread.
New Java programmers not conforming to this rule and executing tasks on the AWT thread is almost certainly the biggest reason people think Java is slow.