I understand that EventQueue.invokeLater() is a function called to make the Java Swing components Thread-Safe.
Also, I know that the argument to this function is an object with implements Runnable.
However, I am unable to understand the syntax for this function call, i.e. this call -
EventQueue.invokeLater(()-> {
new Screen();
});
Here, Screen() is a class that extends JFrame.
public class Screen extends JFrame
{
Screen()
{
setSize(1000, 1000);
JPanel j1 = new Board();
j1.setBounds(0,0,500, 500);
JPanel j2 = new DiceModel();
j2.setBounds(500, 0, 500, 500);
add(j1);
add(j2);
setLayout(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args)
{
EventQueue.invokeLater(()-> {
new Screen();
});
}
}
This code runs as expected.
Board and DiceModel are two classes I have defined that which extend JPanel.
The invocation
EventQueue.invokeLater( new Screen() );
gives the expected error that Screen is not an object of type Runnable.
So,my question is, what is the meaning of the syntax for the function call for invokeLater() ?
Is it a kind of anonymous function call in Java ?
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.
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);
}
});
}
Related
Basically I have got a game with main class.
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Mini Tennis");
Game game = new Game();
frame.add(game);
frame.setSize(300, 400);
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (true) {
game.move();
game.repaint();
Thread.sleep(10);
}
}
When I am loading a class directly it working fine.
But when I am calling it from another class, it does not paint anything.
private void btnGameActionPerformed(java.awt.event.ActionEvent evt) {
try {
String[] args = null;
Game.main(args);
} catch (InterruptedException ex) {
Logger.getLogger(DrawerMain.class.getName()).log(Level.SEVERE, null, ex);
}
}
And after a few seconds it paints that I have lost a game. So basically game is running but I can't do anything and I don't see anything.
The reason for this is that in the 2nd case, where you are calling the main method from an ActionListener, you are running your main method on the UI thread. And since you have your active wait block in this code, the UI is never able to draw anything. You should check out javax.swing.Timer and replace the loop I've copied over below with an implementation that uses Timer
while (true) {
game.move();
game.repaint();
Thread.sleep(10);
}
You need to move your main code into a Constructor, main should be only on the 1st class or the "main" class you're going to run.
The main method is the entry point for your application to run, you shouldn't be calling it as if it were any other method.
public Game () {
JFrame frame = new JFrame("Mini Tennis");
Game game = new Game();
frame.add(game);
frame.setSize(300, 400);
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while (true) {
game.move();
game.repaint();
Thread.sleep(10);
}
}
Then you call it as:
private void btnGameActionPerformed(java.awt.event.ActionEvent evt) {
try {
String[] args = null;
Game game = new Game();
} catch (InterruptedException ex) {
Logger.getLogger(DrawerMain.class.getName()).log(Level.SEVERE, null, ex);
}
}
You should read more about Constructors and Classes and Objects
Also as mentioned in other answers, Thread.sleep() will cause your application to freeze, you should use a Swing Timer instead to handle it in another thread, so your application won't freeze.
With Thread.sleep() your application will wait the time inside it before repainting it.
Asothers have said, you shouldn't call main() yourself. That said, you cannot have a Thread.sleep() in your loop - if it is run on the EventDispatchThread (which it must be) that will block the thread and you will never get repaints. You need to use a SwingWorker to do the looping.
First, you should not run everything from your main thread. And the main method is not for you to call, it's supposed to be the entry point of your application.
In your main method, do this:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI(); // in this method, you create your frame and other UI components and show them
}
});
This Runnable will run in the event dispatch thread and will make the UI ready before any actions related to the UI.
Once you have your UI ready, say, you created the frame and linked it to a game. Then use a class which acts like a controller of the frame and the game to listen to events it's interested in and update the UI when it needs to.
If you need to create a window running this game each time when some event happens, then you should define this window as a class. You just need to create an instance of this class and make the window visible. Or if you just need to restart the game, then use a single instance of the class across your application, and only restart the game in it. The controller class is always useful to notify the game to restart and as well as update the UI.
testing a game, i sometimes get exceptions thrown when a component is not displayable. i added a wait loop on isDisplayable().
seems like my game can take a few hundred ms. to become displayable.
is this a sane way to handle this problem?
i am testing game clients that talk over sockets to a server.
thanks
edit 1: thanks for the comments. i discovered that i am adding the mediator for the gui (an observer) to the model (an observable) before the gui completes its construction and initialization. it gets worse, as i am initializing a panel with calls to createImage which returns null and throws.
import java.awt.*;
import javax.swing.*;
public class WaitForFrameToBeVisible {
JFrame frame=new JFrame("FrameDemo");
private void createAndShowGUI() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel emptyLabel=new JLabel("");
emptyLabel.setPreferredSize(new Dimension(175,100));
frame.getContentPane().add(emptyLabel,BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
void run() throws Exception {
System.out.println("waiting for frame to be displayable");
long t0=System.nanoTime();
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
while (!frame.isDisplayable())
Thread.sleep(1);
long dt=System.nanoTime()-t0;
System.out.println("waited "+dt/1_000_000.+" ms. for frame to be displayable");
}
public static void main(String[] args) throws Exception {
new WaitForFrameToBeVisible().run();
}
}
You don't need to use isDisplayable(). You could use invokeAndWait(...) for what you are trying to do :
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createAndShowGUI();
}
});
// control will reach here when the GUI will be created
Notice that this way you are not actually checking if the frame is visible but you are checking if createAndShowGUI() has done its work
Another thing is you should not call isDisplayable() or any function like this in loop. It will consume unnecessary processing speed. Instead use wait-notify.
But as far as your case is concerned to use WindowListener is very good idea as suggested by MadProgrammer in comment.
What is the best practice way to start a java swing application? Maybe there is another way to do it.
I want to know if i have to use the SwingUtilities class to start the application (secound possibility) or not (first possibility).
public class MyFrame extends JFrame {
public void createAndShowGUI() {
this.setSize(300, 300);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
// add components and stuff
this.setVisible(true);
}
public static void main(String[] args) {
// First possibility
MyFrame mf = new MyFrame();
mf.createAndShowGUI();
// Secound possibility
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MyFrame mf = new MyFrame();
mf.createAndShowGUI();
}
});
}
}
Only the second way is correct. Swing components must be created and accessed only in the event dispatch thread. See concurrency in swing. The relevant quote:
Why does not the initial thread simply create the GUI itself? Because almost all code that creates or interacts with Swing components must run on the event dispatch thread. This restriction is discussed further in the next section.
So yes, you need to use invokeLater().
My code as below is not working, can anyone tell me why? Please also correct my code, I am very new to Java. Besides that, I am searching for the "loading panel component", something like ProgressMonitor but maybe more attractive and which animates better. Please suggest me if anyone has used such things before.
public class Main extends JFrame {
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main frame = new Main();
frame.setVisible(true);
ProgressMonitor pm = new ProgressMonitor(frame, "Loading...",
"waiting...",
0, 100000);
for (int i = 0 ; i < 100000 ; i ++){
pm.setProgress(i);
pm.setNote("Testing");
System.out.println(i);
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Main() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
}
}
As #Mark Rotteveel already indicated, you are keeping the EDT (Event Dispatch Thread) occupied. The tutorial on 'How to show progress bars/monitors' contains valuable information and code samples.
But basically it comes down to moving your calculations to a worker Thread (e.g. using a SwingWorker), and showing the ProgressMonitor on the EDT. It is up to the worker thread to indicate to the ProgressMonitor what progress has already been made.
And here is a direct link to the sample code of that tutorial which clearly shows how the work is done in the SwingWorker extension (the Task class in that example), and how the ProgressMonitor gets updated by adding a PropertyChangeListener to the SwingWorker, where the listener passes the progress to the ProgressMonitor.
I would also suggest to read the Concurrency in Swing tutorial which contains more information on how to handle Threads in combination with Swing, and why you can't/shouldn't do heavy calculations on the EDT
In Swing, the event-thread is what modifies and updates the GUI. You are keeping the event-thread busy with that for-loop and sleep, so it cannot update the GUI. All things you do on the event-thread should be short-lived. Everything else should be moved off the event-thread.
So you need to move that for-loop out of the event-thread.
1. Consider this as the rule of thumb, UI work on UI thread, and Non-UI work on Non-UI thread.
2. Event Dispatcher Thread (EDT) is the UI thread here, and so you should keep your Non-UI process intensive work on a separate thread OUT of the EDT.
3. You can do this in 2 ways.....
i. Create a separate Thread to do this.
ii. Use SwingWorker to synchronize the Non-UI and the UI thread.
4. Always keep the main() method only for making the JFrame visible in the EDT.
eg:
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
public void run(){
myframe.setVisible(true);
}
}
}
Take a look at this site for the working example of SwingWorker:
http://www.kodejava.org/examples/381.html
When I initiate my Swing dialog layout does it make a difference whether I do it in the class's run method:
public void run()
{
frame = new JFrame();
...
frame.setVisible( true );
}
or the class constructor?
public MyClass
{
frame = new JFrame();
...
frame.setVisible( true );
}
public void run()
{
}
Thanks
Yes it does matter, and the reason is that you should call most Swing code, including creation of your JFrame, on the Swing event thread (the Event Dispatch Thread or EDT). To do this, you usually create your Swing GUI in a Runnable, and queue the Runnable on the event thread by calling something like:
SwingUtilities.invokeLater(new Runnable(){
public void run() {
// create your Swing GUI here
frame = new JFrame();
...
frame.setVisible( true );
}
});
The exceptions are Swing method calls that are documented in the API to be thread-safe such as the repaint() method of Components.
This is contextual. As #hovercraftfullofeels points out, you need to make sure ALL your UI code is executed in the EDT, including initalisation.
If you're already running in the EDT, then there shouldn't be any need to use InvokeLater (unless you really want to), otherwise you MUST resync the call back to the EDT.
Best to check with EventQueue.isDispatchingThread