I am very curious why we have to use java.awt.EventQueue.invokeLater to control swing components.
Why can't we do that in a normal thread? What exactly is going on behind the scenes? From what I have noticed if I have a JFrame I can set visibility to true or false from the main thread without getting any errors, and it does seem to work. So what exactly do I achieve by using java.awt.EventQueue.invokeLater? I am also fully aware that I can use SwingUtilities.invokeLater but as explained here, they seem to be one and the same thing.
Thanks to anyone for their explanation. Hopefully this is a valid question.
EDIT: to answer wumpz question
We can create a jframe
JFrame frame = new JFrame("Hello world");
frame.setSize(new Dimension(300, 300));
frame.setPreferredSize(new Dimension(300, 300));
frame.setMaximumSize(new Dimension(300, 300));
frame.setMinimumSize(new Dimension(300, 300));
frame.setVisible(true);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
And on the same thread it was created do the following.
for (int i = 0; i < 34; i++)
{
System.out.println("Main thread setting to "+(!frame.isVisible()));
frame.setVisible(!frame.isVisible());
}
And no complaints.
The complete Swing processing is done in a thread called EDT (Event Dispatching Thread). Therefore you would block the GUI if you would compute some long lasting calculations within this thread.
The way to go here is to process your calculation within a different thread, so your GUI stays responsive. At the end you want to update your GUI, which have to be done within the EDT. Now EventQueue.invokeLater comes into play. It posts an event (your Runnable) at the end of Swings event list and is processed after all previous GUI events are processed.
Also the usage of EventQueue.invokeAndWait is possible here. The difference is, that your calculation thread blocks until your GUI is updated.
So it is obvious that this must not be used from the EDT.
Be careful not to update your Swing GUI from a different thread. In most cases this produces some strange updating/refreshing issues.
Still there is Java code out there that starts a JFrame simple from the main thread. This could cause issues, but is not prevented from Swing. Most modern IDEs now create something like this to start the GUI:
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
All supported platforms offer single-threaded graphics libraries. Swing is cross-platform. Therefore, Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
As an aside, SwingUtilities.invokeLater() is a cover for EventQueue.invokeLater() since version 1.3.
Related
If I understand correctly then when I create GUI swing components, for example I have this:
public class frameExample extends JFrame{
public frameExample(){
//Here adding bunch if components
setVisible(true);
}
}
So as long as I don't call the setVisible method the components are being made from the thread the instance was created. So if in a class where i have my main method write:
JFrame test=new frameExample();
and I sysout
Thread.currentThread.getName();
in the constructor in frameExample just before setVisible I should get: main.
After that the responsibility of creating and maintaining swing elements are passed to the event-dispatch-thread and because it is not thread safe every component add/remove/modify should be done within the EDT thread.
So I should place my setVisible as the last line of code in my constructor, or call it separately.
As I understand all event listening goes through the EDT. So if I create a new component within for example an actionPerformed method it should do fine.
Also if I pass a runnable instance to invokeLater or invokeAndWait then all the run() method will be done by the EDT.
So here is why I'm confused.
I made this code:
public class GUI extends JFrame {
JButton btn = new JButton("Change");
JMenuBar m = new JMenuBar();
public GUI() {
super("Test");
setSize(400, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
m.add(new JMenu("menu"));
add(m, BorderLayout.NORTH);
add(btn, BorderLayout.SOUTH);
System.out.println("Current thread: before setVisible "+Thread.currentThread().getName());
setVisible(true);
System.out.println("Current thread: after setVisible "+Thread.currentThread().getName());
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
add(new JButton("testbtn1"), BorderLayout.EAST);
add(new JButton("testbtn2"));
System.out.println("Current thread: "+Thread.currentThread().getName());
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 1E8; i++) {
Math.sin(5.0);
}
System.out.println("Current thread: "+Thread.currentThread().getName());
}
}).start();
}
});
}
}
So in my anonim class where I add the two buttons im in the EDT, but the components are not added to my frame just after i resize it (which forces the edt to update its components??? what is the reason for this?).
So i dont get my new components even within edt, however when i create a random thread outside edt and make it change some gui element property (for example setText) in just works fine outside the edt.
So my first question is: why my components are not updated within edt and why they are visible after resize
second one: why can i make changes to swing components outside edt and everything works fine? Is this just random thread behvior where for example things just work fine without sync block but when you rerun you program at some point it will eventually crash because the lack of sync.
So as long as I don't call the setVisible method the components are being made from the thread the instance was created
Wrong. As long as you do not specifically create a new Thread or use a utility method (like for example the SwingUtilities#invoke... methods), each call is invoked on the current Thread, including the setVisible call.
It looks like you think that making Swing components visible somehow makes your code switch threads. No, the painting of the Swing components will happen on the EDT. And as you correctly stated, Swing is not thread safe.
That is why you should create the component on the EDT as well, and not on another thread. It might work without problems the majority of the time, but eventually you will stumble upon weird bugs.
As I understand all event listening goes through the EDT. So if I create a new component within for example an actionPerformed method it should do fine.
Also if I pass a runnable instance to invokeLater or invokeAndWait then all the run() method will be done by the EDT.
Correct.
So in my anonim class where I add the two buttons im in the EDT, but the components are not added to my frame just after i resize it
Adding components to a Container requires you to revalidate the layout (see the javadoc of the Container#add method). Just call
revalidate();
repaint();
after you add the buttons and it will work as expected. Manually resizing the frame has the same effect as you already noticed.
So i dont get my new components even within edt, however when i create a random thread outside edt and make it change some gui element property (for example setText) in just works fine outside the edt.
As stated before, it might work most of the time, but there is no guarantee it will work 100% of the time. The reason the adding of your components did not work as expected is explained above, and has nothing to do with threading issues.
public class TestFrame extends JFrame
{
public TestFrame()
{
setBounds(10, 10, 500, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(3);
}
public static void main(String[] args) throws InterruptedException
{
TestFrame tf = new TestFrame();
tf.add(new JButton("test1"));
tf.setVisible(true);
Thread.sleep(2000);
tf.getContentPane().removeAll();
tf.add(new JButton("test2"));
System.out.print("Show test");
}
}
I want the program show JButton("test2") after 2 seconds.
Then I add thread.sleep(2000) after test1.
But I don't know why the program stops at showing the test1 JButton,
not showing test2 JButton and the "show test" message can sucess print out
Short answer, don't.
Swing is a single threaded framework, this means that any thing that blocks the Event Dispatching Thread will prevent it from updating the UI or processing any new events (making your UI look like it's hung, cause it has).
Sure, you could use a Thread, but Swing is also not thread safe. This means that ALL modifications to the UI MUST be made from within the context of the Event Dispatching Thread. While there are ways to overcome this, the easiest way is to simply use a Swing Timer.
Take a closer look at How to use Swing Timers and Concurrency in Swing for more details
You should also take a look at Initial Threads.
When updating the UI, it may be required to call revaldiate and repaint after you have added the new components to force the UI to update to re-layout it's contents.
I'm doing a basic Pong game for a class. I have the Pong working, and I have a GUI display on startup, unfortunately I can't seem to start the game from the start JButton. I've commented where the problem is on the code, and removed irrelevant code.
frame.add(GUIPanel);
JButton startButton = new JButton("Start!");
GUIPanel.add(startButton, BorderLayout.CENTER);
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
frame.getContentPane().remove(GUIPanel);
frame.validate();
frame.repaint();
drawPanel = new DrawPanel();
drawPanel.requestFocus();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
//This is the part that freezes it, everything else works fine
//except that the playGame method isn't called. If I remove the whole
//startButton and whatnot I can call playGame and it works perfectly.
playGame();
}
});
}
any ideas?
Swing is a single threaded framework.
That is, all interactions and modifications to the UI are to be made from within the context of the Event Dispatching Thread. Anything that blocks this thread will prevent it from processing, amongst other things, repaint requests and user input/interactions.
My guess is that playGame is using something like Thread.sleep or some kind of while(true) and is blocking the EDT, causing your program to appear as if it's frozen
Have a read through Concurrency in Swing for more details.
A simple solution would be to use a Swing Timer to act as you game loop. Each time it ticks, you would update the state of your game and call (something like) repaint on you game's component
It looks like you've got a parenthesis that doesn't belong next to your second to last semicolon. Try removing it.
I'm just getting to grips with GUI programming in java. Here is a trivial program (from O'Reilly's "Head First Java") which on the face of it looks easy to understand, but there's an aspect of it which I don't follow.
import javax.swing.*;
public class Test {
public static void main(String[] args) {
JFrame frame=new JFrame();
JButton button = new JButton("click me");
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
}
}
This simple program, when compiled and run, will open a window with a button on it.
What I don't understand is what is happening with the flow of execution. When I run this program, the static main method of the Test class runs, all the commands in main() are executed -- so why doesn't the process terminate after the window appears? Why am I still sitting on what looks like an infinite loop? What is looping?
If I add the line
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
then I find the result even more imcomprehensible. Now, of course, the program terminates
once I've closed the window. But again I don't see why. The frame will be on the stack but I don't see where the program flow is and just the existence of something on the stack is not enough to keep the program alive, surely? I'm missing something fundamental which as far as I can see is not covered in the book I'm reading. I am slightly surprised by this -- "Head first Java" has been very good up until now at pointing out subtleties and explaining what is really going on, but doesn't seem to address this point (at least not that I've spotted).
why doesn't the process terminate after the window appears?
Because the Java Virtual Machine exits only after all non-daemon threads have finished. While not apparent, there's in fact two threads in your program: the main thread, and the event dispatching thread, which does everything related to the Swing GUI components. The event dispatching thread keeps going as long as any GUI components are visible.
Actually the program, while it may work, is wrong, because you're creating and accessing Swing components from the main thread. You ought to be doing all GUI work in the event dispatching thread. That is, it should be something like:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
JFrame frame=new JFrame();
JButton button = new JButton("click me");
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
});
}
The Java process terminates when the last non-demon thread dies. Normally there is just one, the main thread. When you display Swing components additional non-demon threads for event dispatching and GUI shotdown are started. Those terminate when the last top-level component gets disposed. In your sample the main thread dies after leaving the main method. You can have a look into the threads with a debugger or jvisualvm from the JDK tools.
The rest of the GUI flow is event driven. When you e.g. click on the frame's close button an event is generated and sent to the appropriate listeners within the event dispatching thread.
Setting JFrame.EXIT_ON_CLOSE as default close operation is like adding a default event listener to the frame. A quite harsh one, it just shuts down the JVM without respect to the rest of the application's state.
I am writing a genetic algorithm that approximates an image with a polygon. While going through the different generations, I'd like to output the progress to a JFrame. However, it seems like the JFrame waits until the GA's while loop finishes to display something. I don't believe it's a problem like repainting, since it eventually does display everything once the while loop exits. I want to GUI to update dynamically even when the while loop is running.
Here is my code:
while (some conditions) {
//do some other stuff
gui.displayPolygon(best);
gui.displayFitness(fitness);
gui.setVisible(true);
}
public void displayPolygon(Polygon poly) {
BufferedImage bpoly = ImageProcessor.createImageFromPoly(poly);
ImageProcessor.displayImage(bpoly, polyPanel);
this.setVisible(true);
}
public static void displayImage(BufferedImage bimg, JPanel panel) {
panel.removeAll();
panel.setBounds(0, 0, bimg.getWidth(), bimg.getHeight());
JImagePanel innerPanel = new JImagePanel(bimg, 25, 25);
panel.add(innerPanel);
innerPanel.setLocation(25, 25);
innerPanel.setVisible(true);
panel.setVisible(true);
}
However, it seems like the JFrame
waits until the GA's while loop
finishes to display something. I don't
believe it's a problem like repainting
Yes, if the looping code execute on the EDT then the GUI can't repaint itself until the loop finishes. The looping code should execute in its own Thread so it doesn't block the EDT.
Read the section from the Swing tutorial on Concurrency for more information.
I think your problem is that Java won't let you update the GUI from another thread than the GUI thread itself. This causes grief to everybody at some point, but fortunately a reasonably convenient workaround is provided.
The idea is to pass the code that does the updating as a Runnable to the method SwingUtilities.invokeAndWait or SwingUtilities.invokeLater. Here's an example.
To run your GA at maximum speed and exploit parallelism, I guess invokeLater would be appropriate.
EDIT: Oh wait, camickr's solution hints that you're doing something else: You're running the GA in the GUI's thread. Well, that can only do one or the other, calculate or display. So the true solution would combine both changes:
Run the GA in a separate thread (you could run it in the thread used by main() after you've instantiated the GUI); and
Use invokeLater to communicate updates to the GUI thread (which camickr calls the EDT, or Event Dispatch Thread).