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.
Related
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);
}
});
}
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.
I am trying to develop a JFrame which has two buttons that would let me to call the main method of other classes. The first try was to put it directly into the actionPerformed of each button, this will cause the JFrame of the other class to open but showing only the title of it and not showing any contents of the JPanel additionally freezing the program (can't even press close button, have to go into task manager or eclipse to kill it). The second try was adding a method call in actionPerformed, and adding the method will this time call the main method of other class however the same result (freeze of program).
For testing purposes I have placed the call to main method of other class, straight in this class main method which has proven to me that the frame of other class has successfully appeared, including all its JPanel contents, functionality etc.
I know I could make some kind of infinite loop in my main method to wait until a boolean is set to true, but then I though there must be some less-expensive way to get it working. So here I am asking this question to you guys.
Here is the code of the 2nd try;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Chat {
public static void main (String[] args) {
JFrame window = new JFrame("Chat Selection");
//Set the default operation when user closes the window (frame)
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set the size of the window
window.setSize(600, 400);
//Do not allow resizing of the window
window.setResizable(false);
//Set the position of the window to be in middle of the screen when program is started
window.setLocationRelativeTo(null);
//Call the setUpWindow method for setting up all the components needed in the window
window = setUpWindow(window);
//Set the window to be visible
window.setVisible(true);
}
private static JFrame setUpWindow(JFrame window) {
//Create an instance of the JPanel object
JPanel panel = new JPanel();
//Set the panel's layout manager to null
panel.setLayout(null);
//Set the bounds of the window
panel.setBounds(0, 0, 600, 400);
JButton client = new JButton("Run Client");
JButton server = new JButton("Run Server");
JLabel author = new JLabel("By xxx");
client.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//run client main
runClient();
}
});
server.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//run server main
}
});
panel.add(client);
client.setBounds(10,20,250,200);
panel.add(server);
server.setBounds(270,20,250,200);
panel.add(author);
author.setBounds(230, 350, 200, 25);
window.add(panel);
return window;
}
private static void runClient() {
String[] args1={"10"};
ClientMain.main(args1);
}
}
Only one main method is allowed per application. Honestly I am not sure what you are trying to do or think is supposed to happen when you call main on other classes. When you call main on other classes all you are doing is calling a method that happens to be called main and passing args to it. Your freezing is probably because you are not using Swing correctly:
http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
The problem you're having is that Java Swing is single threaded. When you're running the main function of the other class, however you do it, the GUI won't be able to keep running until it returns. Try spawning off a new thread that calls the second main method.
private static void runClient() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
String[] args1={"10"};
ClientMain.main(args1);
}
});
}
EDIT: Updated, as per #Radiodef's suggestion. Missed at the top when you said this second class had to display things on the GUI. Definitely want to go with the invokeLater then.
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().
Here's an interesting bug (read: i've probably missed something) in Java Swing I spent the last 2 days trying to trace.
First things first, create a SSCCE.
Here you go.
class GUI extends JFrame{
public static void main(String[] args){
// All GUI work should be handled in the EDT.
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new GUI().setVisible(true);
}
});
}
GUI(){
// Make a nice icon
ImageIcon img = new ImageIcon(this.getClass().getClassLoader().getResource("img/1.png"));
// Make a TrayIcon with the image
SystemTray sysTray = SystemTray.getSystemTray();
TrayIcon trayIcon = new TrayIcon(img.getImage());
try {
sysTray.add(trayIcon);
}
catch(AWTException e) {
e.printStackTrace();
System.out.println("System Tray unsupported!");
}
this.setTitle("Example GUI");
this.setIconImage(img.getImage());
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
}
}
If I run this and close the Window, I would expect it to dispose, and the various Threads to terminate. This is the case if I comment out the "Make a TrayIcon" try/catch block.
The sysTray.add() line seems to not create an Exception, but having it in the code stops the Threads from terminating, as the code hangs on a wait() in the AWT-EventQueue Thread.
Is this a bug, or something I'm missing?
Cheers all.
To get the program to terminate properly when you close it, you need to set the DefaultCloseOperation to EXIT_ON_CLOSE, like this:
GUI.setDefaultCloseOperation(EXIT_ON_CLOSE);
EXIT_ON_CLOSE is defined in JFrame so you don't need to define it or import it from anywhere.
Check the API for more exit operations: