Now, I'm not sure if this is possible or even the best way to accomplish what I'm trying to do, but basically I'm creating a very simple simulation program with a very simple Swing GUI.
After each round of the simulation, some buttons on the interface are enabled for the user to make changes and then the user can press the "continue" button to start the simulation again. The simulation itself is basically a while loop that needs to wait for the user action before continuing. My question is how can I have the program stop and wait until the user presses the "continue" button? Please let me know if there are any more details I can provide if this is unclear!
Edit:
I'm going to add some simplified code here so maybe this will make more sense. The program is in two parts, the simulation class and the view. So the button to be pressed is in the view class while the simulation is happening in its class.
Simulation class:
SimulationView view = new SimulationView(); // extends JFrame
while (!some_condition) {
// code
// need user action via button press here before continuing!
}
Most likely the best way to go is enclose one round of the simulation in a method, that would then be executed from the action listener attached to the button you want.
After edit:
Somebody needs to control the simulation in the first place, so you could do the following:
SimluationClass
{
public int startSim()
{
...
}
}
class SimulationView
{
SimulationClass sim = new SimulationClass();
private void init()
{
JButton button = new JButton("Start");
button.addActionListener(new ActionListener() {
void actionPerformed(...)
{
sim.startSim()
}
});
}
}
Keep in mind though that this will freeze your gui as the sim method will be executed from the event thread.
Related
I'm new to Java and I'm learning it for my computing coursework in college.
I have a button which is initially set to off(btn_state = false) while it displays the "off.png",
when the button is pressed I want the boolean var "btn_state" to set to true and display "on.png" and that if I press the button while it's set to true I want it to to set to false and display the "off.png"
If you didn't get what I said basically:
While boolean var button state = off display "off.png"
While state = on display "on.png"
When button is pressed,
if state = true then display "off.png" and set state = false
if state = false then display "on.png" and set state = true
I've tried making the button do as described above but failed D:, it seems pretty simple but I'm somehow incapable of coding it, maybe I'm just being stupid and going the complete wrong way about it lol, sorry if my explanation is still unclear :P
Here is the actual code:
JButton Gravity_btn = new JButton("");
boolean Gravity_btn_state = false;
while (Gravity_btn_state = false) {
Gravity_btn.setIcon(new ImageIcon("off.png"));
}
while (Gravity_btn_state = true) {
Gravity_btn.setIcon(new ImageIcon("on.png"));
}
Gravity_btn.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Button has been pressed");
// I want it so that if "on.jpg" is showing and button is pressed then display "off.jpg" & vice versa
}
});
You are going about this in completely the wrong way.
Swing uses an Event Dispatching Thread (EDT), which calls methods to handle any generated events (including user button presses). When (and only when) those methods have returned control to the EDT will "painting" occur, which will redraw the user interface, including the any new icons on buttons.
while (Gravity_btn_state == false) {
Gravity_btn.setIcon(new ImageIcon("off.png"));
}
This loop will never end, locking up the user interface, and causing the application to never draw anything ever again. Nor will it respond to any user inputs. The button state cannot be changed, because control never returns to the EDT to accept new input from the user. And the UI will never be drawn because control never returns to the EDT.
What you want is to install an ActionListener on the button. When the button is clicked, the ActionListener will be invoked. In that listener, you would toggle gravity.
Since you've talking about gravity, you probably want things moving on the screen, being pulled "down". This means you want animation. Which, with Swing, means you want a Swing Timer. Again, the Timer would have an ActionListener which will be called when the timer expires (again, only if control always returns to the EDT). That timer should move things (i.e., change x,y variables), and call repaint() on the appropriate surface ... and return control to the EDT. The EDT will see that something needs repainting, and (if the event queue has nothing else in it) send the paint message to that thing, so it can draw itself.
There are plenty of tutorials on the Web for "Swing Animation". Do a search on that to find the tutorials, and try to redesign your application. If you get stuck, you can always return here for additional help.
I have a dilema. This might sound stupid but i have no idea how to do this.
I have a password class and a main screen. My main screen has a button that when pressed pops up the password class. Here is the call to the passwordClass from an actionlistener on my main class.
public PasswordClass login(){
pressMe.setVisible(true);
String player="?";
final String playerT = player;
boolean nameCorrect = false;
final PasswordClass hold = new PasswordClass(null);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Turn off metal's use of bold fonts
UIManager.put("swing.boldMetal", Boolean.FALSE);
PasswordClass.createAndShowGUI();
}
});
return hold;
}
the return statement isn't anything related to this I never used it but I had it their for testing purposes. In my password class i have a boolean that tells me if the user input matches the correct login info. i call it worked i run the password class and i had the problem that while the window is popping up my code to check if it worked is running simultaneously. This is a problem because i only want to check if it worked after the user has pressed ok. Here is the code
public void actionPerformed(ActionEvent ae)
{
else if(ae.getActionCommand().equals("Login")){
login();
}
else if(ae.getActionCommand().equals("Press Me To Continue")){
if(PasswordClass.worked){
//worked is a static variable from the PasswordClass class
}
pressMe.setVisible(false);
}
}
So whenever OK is pressed on The PasswordClass JFrame a little button pops up and asks a SECOND time for it to save. I want it to save from the first OK button. The reason i make another button is because i don't know how to stop and wait for the OK button to be pressed. My if loop to check if it worked already returns false automatically before the user presses OK. That is my problem and I am really confused on how to solve it.
Any help? If any more code is needed I will provide it but i think this is enough.
The reason i make another button is because i don't know how to stop and wait for the OK button to be pressed
Use a modal dialog of some kind, see How to Make Dialogs for more details
Conceptually, you want to display a modal dialog, which prompts the user for some information, while blocking at the point in your code that the dialog was made visible. When the dialog is dismissed (for what ever reason), you'll want to check the results from the dialog and take appropriate actions based on what the user did
There needs to be some form of synchronization between the objects (not necessarily related to the statement of that name).
If the code that wants to check the result wants to block until the result is set, you could use a CountDownLatch: the actionPerformed method calls CountDownLatch.countDown() while the other code calls CountDownLatch.await().
On the other hand, if the code checking the result does not want to block, then a simple two-boolean approach would work well. Have one boolean indicate whether the button press complete and the other to tell whether OK was the button pressed.
I have many GUI forms with many buttons on each GUI form. I am wanting to detect when a user clicks on any of these buttons.
My question is this: Should I add code individually to each button, or can I cater to each button press by a public method that is called whenever a button is pressed? If I use a public method (that I would rather do), how does the method detect the button that called the function? Do the buttons have to be a public variable?
Currently I am using this code (that works, I am wondering if I can do it better):
loginButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
System.out.println("You clicked the button");
}});
Should I add code individually to each button, or can I cater to each
button press by a public method that is called whenever a button is
pressed?
To my mind (and sanity), I'd separate the action listeners/handlers. Generally speaking it keeps the individual actions/code simple and to the point.
Each button/menu can have more then one action listener if need be.
I'd also take a look at the Action API
If I use a public method (that I would rather do), how does the method
detect the button that called the function? Do the buttons have to be
a public variable?
If your really keen to follow this method, you can pass the ActionEvent to it, which would contain a reference to the source of the action as well as the action command.
Personally, I wouldn't. These kind of methods are notoriously difficult to manage, maintain, debug and update
I'm trying to code a simple game in Java. The basic structure is a single JFrame with different JPanels that I add/remove at different times. At startup, there is a JPanel that's a basic menu (start game, high scores, etc). Once the "Start" button is pressed it switches to a level selector panel with three buttons to select the difficult level of the game. Once any of the three buttons is pressed, it switches to another panel that will displays a three second countdown, then the actual game. All three buttons call the same method, just with a different difficulty value passed in.
I have all the separate pieces working fine, but I'm having troubles with the transition from the level selection panel to the countdown. If I don't use threads the screen freezes on button press and does not switch to the new panel. I've tried messing around with threads, but I don't know that much about them and have only had limited success (I've got it so it will successfully switch some of the time, but not consistently).
In terms of code, in the level selection panel I have something like this listening for button clicks:
private class ButtonClickedListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
gui.newLevel(1);
}
}
where in place of just gui.newLevel(1) I've messed around with starting new threads and calling the method from them.
The newLevel() method look like:
getContentPane().removeAll();
levelPanel = new LevelPanel(levelNum, this);
add(levelPanel);
validate();
levelPanel.start();
I use very similar code when switching from the start menu JPanel to the level selector panel (again, with an ActionListener on the buttons), which works just fine.
LevelPanel's start() method initializes values for the new JPanel and displays the countdown on screen (currently with the following code, although I messed with putting something like this in the newLevel() method instead) before displaying the actual game:
try {
Thread.sleep(1000);
//update countdown number
validate();
repaint();
Thread.sleep(1000);
//update countdown number
validate();
repaint();
Thread.sleep(1000);
//update countdown number
validate();
repaint();
} catch (Exception e) {
System.out.println(e);
}
//start game
I would really appreciate any help getting this code to work, and I'm pretty sure some sort of threading is the way to go but I'm not quite sure where/how. Any suggestions and/or code samples would be great!
Thanks in advance!
EDIT: I ended up rewriting the countdown using a timer instead of Thread.sleep(), which fixed part of the problem and the rest of it I eventually figured out and was entirely unrelated to GUI stuff, which is why I didn't think to check it in the first place.
never really never use Thread.sleep(1000); during EDT, this code caused freeze on GUI is un_resposible, untill a new event invoke EDT or mouse hover over can alive this container too
1) there are two ways how to dealy any event(s) in the Swing GUI, by implements
Swing Timer
delaying by using Thread.sleep(1000); in the SwingWorker
The layout and painting must be done in EDT. Use SwingUtilities.invokeAndWait to call the validate() and repaint()
You can start some code with a time delay using TimerTask:
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
invokeLater(); // This starts after [delay] ms
// and - if given - will run every [period] ms.
}
}, delay, period);
You could solve your problem with this, though it won't be a pretty solution.
// edit: (see comments) you should synchronize accesses to the gui properly, else it will give you errors.
I have been working on a Java game for a while that I started back in my college days. After taking considerable breaks due to my professional career, I'm getting back to it and realizing how awful the code is. I basically had a "monolithic main" with both GUI and game logic in the same class file. What I'm attempting to do, is abstract and "componentize" the GUI into smaller chunks. My executed class extends JFrame which I add and remove panels to/from when buttons are clicked. Ex.
public class GameFrame extends JFrame{
private JPanel[] _panels = {new MainPanel(), new OptionsPanel(), new DifficultyPanel()};
private int _currentPanelIndex = 0;
public GameFrame(){
...
add(_panels[_currentPanelIndex]);
...
}
}
Within each panel class, I have private ActionListeners for the buttons. In MainPanel, I'm getting the singleton GameFrame instance and setting the current panel kind of like:
class SinglePlayerButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e){
GameFrame instance = GameFrame.getInstance();
JPanel[] panels = instance.getPanels();
int currentPanelIndex = instance.getCurrentPanelIndex();
instance.remove(panels[currentPanelIndex]);
int newPanelIndex;
for(newPanelIndex = 0; newPanelIndex < panels.length; newPanelIndex++){
if(panels[newPanelIndex] instanceof DifficultyPanel){
break;
}
}
instance.add(panels[newPanelIndex]);
}
}
I do not like this deep of coupling in my classes, but I can live with it. However, the real problem is that when I click the last button that takes me into the game, I can't have:
public void actionPerformed(ActionEvent e){
GameFrame.getInstance().startGame();
}
because this will cause my game to run in the Java Event thread, thereby disallowing any other events from being handled. Previously, I had a busy while loop:
...
while(waiting){
//Do nothing
}
remove(_panels[_currentPanelIndex]);
startGame();
...
...
public void actionPerformed(ActionEvent e){
waiting = false;
}
What is the best way to let my GameFrame class know that I've clicked the play button? Can I send an event or message to it from one of its sub-panels telling it to start, and if so, how do I keep the game from running in the Event thread? Should I handle it in the actionPerformed() method and simply spawn a new thread? I would prefer to keep it in the main thread. Thanks.
Go for Slick 2D (a lightweight OpenGL 2D game development framework with lots of cool stuff) or Nifty-GUI.
Slick has also some examples and tutorials and is easy to learn, for example the StateBasedGame can easily be used to implement multiple in-game menus (such as main menu, settings menu, in-game options, in-game pause/resume etc.)
You should have the game logic executing on it's own thread of execution, possibly when the user clicks a button. I don't understand your prior approach with the while(wait) - that would still create problems if your game logic did some time consuming calculation that prevented the even thread from doing updates because the even thread does not stop executing when you call startGame().
EDIT:
It's not clear from the code you pasted that the GUI updates and the game logic are happening on seperate threads of execution - you might want to demonstrate that in your code.