Ways of Implementing a GUI in Swing, Multithreading is a Concern - java

The goal is a GUI where in a window, having the play button pressed will release the GUI resources and start to run my game (the game is done outside of Swing, in LWJGL).
This is the first way I can see myself doing it. To me the safer option looks like this:
public static void main(String[] args) {
// setup gui objects
JFrame frame = ...;
frame.setVisible(true);
// wait for the play button to be pressed
synchronized(frame) {
frame.wait();
}
// free the gui objects, start the game
frame.dispose();
startGame();
}
// by the way, this method is called from the button's action listener
public void onPlayButtonPress(JFrame parent) {
// end the UI
synchronized(parent) {
parent.notify();
}
}
The reason this seems 'safe' to me is that the cleanup for the GUI and the game execution is done inside the main thread. Here is the second way I can see myself doing it, but I'm not sure how I feel about it:
public static void main(String[] args) {
// setup gui objects
JFrame frame = ...;
frame.setVisible(true);
}
public void onPlayButtonPress(JFrame parent) {
// free the gui objects
parent.dispose();
startGameInNewThread();
}
This second way of doing it seems more preferrable in that it is simpler, but I am worried of some problems coming up. For example:
Disposing the frame in the Event Dispatch Thread could mess things up?
Starting a new thread for the game while the main thread has already terminated?
So the reason I'm asking this question is because I'm looking for the best way to implement this, but I'm also kind of curious if and why any wierd things will happen if I implement my program the second way.

Related

How to use a thread to run another JFrame while the main is still running

I have a jframe i want to display while my main frame is running. i want to pause my main code, until the user does the necessary actions on the other frame. I've read a lot of solutions but i need to see it done for my code to understand and grasp it fully. i do not want to use jdialog like I've seen listed as an answer before. My main goal is to understand better threading so that i can use what i learn in different cases.
With the code I've created, when running the thread, only just the frame loads, none of the other features are there on the frame. (the frame is simple it has a label, a list the user selects from, and a button to basically return the chosen list value.) its like the thread is cut off from completing or something.
here is my class calling the screen:
public class myThread implements Runnable {
String result = null;
public void run() {
MessageScreen ms = new MessageScreen();
ms.setVisible(true);
}
public String getResult() {
return result;
}
public void setResult(String AS) {
result = AS;
}
}
in my main code, a method is called that is returning a String[] value, with this method at some point i have the following code calling the new thread to get the value necessary to return in the original main method:
myThread mt = new myThread();
Thread t = new Thread(mt);
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
myreturn = new String[] {"true", mt.getResult()};
without listing the whole code for the second frame, when the user presses the button, and at the end of the listener tied to the button press the i want to close the frame and return a string that was selected from the list:
jf.dispose();
myt.setResult(AdminSelection);
in the frame class, i have the following instance variables declared:
String AdminSelection = null;
myThread myt;
i hope this is enough information for someone to help me out and understand whats gone wrong here.
The function join() waits until the end of the run() method, when you do t.join(), your thread is already or almost ended. This is because in your run() method there is nothing that blocks the thread until the user has clicked the confirm button. And is better like this!
There is no sense to create a thread here, you should use a callback, or more generally in Java, a listener. You can take a look at Creating Custom Listeners.
But, especially if you want to pause your main code, you should use a (modal) JDialog which is made for this! Don't try to block the UI by yourself, you could block the UI thread (handled by Swing/AWT) by mistake. Creating a JDialog is better because everything is already made for this usage on the UI thread.
Also, you must know that create a Thread is really long, use a Thread when you really need it.

Use swing in a different thread

I'm developing a cli-based custom web crawler in Java. Since it needs to have a user-friendly graphical interface for showing the result, I should add a swing frame to that involving some trees, labels, tables and so on.
That graphical interface is one of its switches, and must be started just in case user calls it. Thus, I have to start this interface in a new thread so that the application can proceed with other tasks and update components inside of GUI frame if needs.
My GUI class is some thing like:
public class Frame extends JFrame {
......
public static JLabel urlLabel;
......
public static void run() {
urlLabel = new JLabel();
urlLabel.setText("Test Url");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
.....
}
And, I fork it from my main class like this:
.....
if(cmd.gui){
Frame.run();
Frame.urlLabel.setText("New Url");
}
......
Unfortunately, the text of label doesn't change. I already tested repaint(), revalidate() and such other functions like these, but, nothing turned up.
I tested getText() in order to make sure it is possible to access urlLabel from main class, and it worked (I could retrieved text of label).
I was wondering how I can sort out this issue? (Basically, I need to start a swing frame in a different thread and control its components from the main thread)
Thanks in advance.
If you use invokeLater(), your Runnable will be started in the EventThread after the current operation in this thread is finished. If your label is not updated, it might be that your EventThread is busy doing something else - e.g. crawling the web.
You definitely need to make sure that your crawling work is done in another thread (start a new one, don't use the one that runs anyway, since this is most probably the EventThread). Then you might use invokeLater() in this other Thread to change the label in the EventThread.
Hint: You can check if you are in the EventThread by using SwingUtilities.isEventDispatchThread().
Remember that your data/models will be used by different threads and that this might cause problems when the data is changed in your worker thread while your GUI is trying to display it.
Thank you guys for helping.
Finally, I could overcome this obstacle by using SwingUtilities.invokeLater for updating the label's text.
I mention the approach here, since someone else might need it:
The main class:
public class Frame extends JFrame {
......
private static JLabel urlLabel;
......
public JLabel getU(){
return urlLabel;
}
public static void run() {
urlLabel = new JLabel();
urlLabel.setText("Test Url");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Frame().setVisible(true);
}
});
}
.....
}
The GUI class:
if(cmd.gui){
Frame().run();
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
gui.getU().setText("New Url");
}
});
}
.....
Just a question about this manner:
Since I need to update some labels and tree nodes a couple of times during crawling, wanted to know if starting a new Runnable for each of those would be overload? If yes, how can I manage that?
---UPDATE---
According to the #xander's comment, it is possible to use lambda instead of Runnable. I think lambda doesn't have overload as much as a new object does.

Waiting for frame creation

i have problem in java and I do not know him and I resolved.
I created a simple program that inserts into the text JPanel using for and sleep function.
Like this(This is an example):
public class example{
JFrame frame....
..
..
public example(){
//ini frame and label.. then..
String s = "abcqweewqewqewqewqweqwqeweqweqwq";
//DO ANIMATION
try
{
for(int i = 0;i<s.length();i++)
{
JLABEL.append(String.valueOf(s.charAt(i)));
Thread.sleep(10);
}
}catch(Exception ex){}
}
public static void main.......{
new example();
}
}
It works perfectly (writes characters after a certain time interval)
But, if i call this main using other class-So waiting until everything renders and then the window appears (so does not animation).
Where is a problem? I hope, you understand me.
Swing is single threaded, and properly written swing code runs in the event dispatch thread. Your sample breaks the threading rule by creating the GUI outside the EDT, and also runs the loop in the main thread. Normally, when created correctly in the EDT, or as a response to an event from a button click or similar, the loop blocks the event dispatch thread so that no drawing can happen until the loop has completed.
You get that behaviour if you initialize the GUI in the event dispatch thread:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new example()
}
});
}
The proper way, instead of sleeping in the EDT, is using a Swing Timer.
To sum the above: your code appears to work only because it has the bug that it runs some of the UI code outside the event dispatch thread.

Which statement in my main method is calling all my other methods in my other classes and my main class?

Question: Ok, so this is going to sound slightly confusing due to my lack of total understanding on the subject. Currently, I am creating a game following a youtube tutorial (to understand the basics of game development in java).
After following some of the tutorials, I am confused as to what is the real purpose of the main method and the class that contains it. I thought that the main method's job was to call all the methods of its own class and the desired methods in other classes. However in my main method in my main "Game" class, here is all I have:
public static void main (String[] args) {
JFrame frame = new JFrame(title);
frame.add(game);
frame.setSize(WIDTH,HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setFocusable(true);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
frame.pack();
game.start();
}
Does frame.add(game) (game is an instance of Game) "call" all the other methods in other classes? Whatl the exactly does frame.add() do? Also, I am still trying to understand threads despite reading a lot of explanations. When I write game.start(); to "start" the thread, what exactly does this imply? Does it call all the methods in the game class only?
Here is my thread code if its needed:
private synchronized void start() {
if(running) {
return;
} else {
running = true;
}
thread = new Thread(this);
thread.start();
}
public synchronized void stop(){
if(!running) {
return;
} else {
running = false;
}
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit((0));
}
Here are the variables I initialized:
private static final long serialVersionUID = -8921419424614180143L;
public static final int WIDTH = 640;
public static final int HEIGHT = WIDTH / 4*3;
public static String title = "Game";
public static Game game = new Game();
public static GameState state = GameState.MENU;
private boolean running = false;
private Thread thread;
private Renderer gfx;
public static Game getInstance(){
I am just unsure of how all other methods in the game class and my other classes are being called if this is all I have in my main method. Any help would be GREATLY appreciated. I am really struggling here as you can probably tell.
The purpose of the main() method is to give an entry point for stand alone execution.
In your case (you did not show how game is initialized but I guess it was a static Game game = new Game(); initialized field) what happens is that first the static fields are initialized (which will call the Game() constructor).
Then the main method is started. It will set the initialized game as the content of the frame (which means the Frame will call various UI related methods like paint() from that moment on. Threading in Gui is a quite complicated topic, but for the sake of your question you can ignore it).
The rest of the methods in main are used to set up the containing frame. That's unusual for bigger applications but quite common for such small examples. (It does cause methods on game being called, for example for layout or drawing).
So finally main calls game.start() (which is the start() method on Game you have shown). It does create a new thread, which executes (by definition) the run() method on the Runable given to the new Thread(this) constructor (this, which is at this point the same instance as game).
The main() method is like any other method with respect to what it executes. Only what you explicitly execute (call) will be executed.
There is no method to automatically call all the other methods, because there is no purpose for such method. If, for some weird reason, you want such a method, just write it and from within it, do call the other methods.
A thread is a separate execution path in your program. When you call start() it begins and practically it goes to its special run() method, which is executed in parallel.
Any good introductory Java book should have detailed explanations to your questions.
The shortest answers that get at your bolded questions are:
frame.add(...) adds a component (like a button or label) to a frame or panel.
game.start() creates a new thread and calls the run() method of the game class in that thread.
So other methods you've written are called from game.run(). If they aren't in there (and they aren't in main(...)), they aren't being called.
I have to agree with the comment from PM77 though. It sounds like you're beginning in java, and Threading and GUIs are both pretty complex topics. There are basic games you can design (ie tic-tac-toe) that don't require either of them. Try those out before you tackle something as challenging as this.

Problems with ActionListener and SystemUtil.invokeLater

I have look all over the web, and found no solution to my problem. For a AP Comp Sci project, I am making a Set of games, that will be run from a JFrame with JButtons. I have the games all ready, along with action listeners, but the games dont launch properly. The JFrame and JButtons are all setup correctly too.
private static class TetListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
GameCenter.quit();
GameCenter.startTetris();
}
}
GameCenter.quit() does nothing but run JFrame.dispose(), and GameCenter.startTetris(); constructs a new Tetris object, then run the play() method to start the game. All of Tetris is coded properly and works correctly when it is run in the main method (outside the actionlistener). But as soon as I put it in the ActionListener, it fails to be constructed properly. I tracked the problem down to:
public BlockDisplay(BoundedGrid<Block> board)
{
this.board = board;
grid = new JPanel[board.getNumRows()][board.getNumCols()];
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() // <<<<<<<<<<------------------- Problem Here
{
public void run()
{
createAndShowGUI(); // <<<<<<<<<<<<-------- Never Run
}
});
//Wait until display has been drawn
try
{
while (frame == null || !frame.isVisible()) // <<<<<<<-------- Never Resolved
{
Thread.sleep(1);
}
}
catch(InterruptedException e)
{
e.printStackTrace();
System.exit(1);
}
}
So the program always hangs. I also made a Pacman game that uses this SwingUtilities.invokeLater, so it doesnt work either. I cant figure out why this is happening or how to fix it.
Any help is appreciated. Let me know if you need any more info.
If the thread that runs SwingUtilities.invokeLater is already the swing event thread and you run in this while loop, yup, your application will hang.
Get rid of the while loop.

Categories