How to make Jbutton run another class? - java

Hi stackoverflow community!
I'm making an AI TicTacToe project for my finals, and I'm having a problem trying to run another class after pressing one of jButtons in the jFrame class.
I'm using NetBean's jFrame class, where you can design easily by placing it from the container, and some of the codes are not editable.
What I want to make is a workable Main Menu (which is a jFrame class) for my gaming project, and it contains three buttons which are Normal, Large and Extra Large. For Normal button, I want to make this button to run TicTacToe (which is a normal java class) after being pressed, but for some reasons I can't make it work. Here are the codes:
MainMenu.java
private void ButtonNormal(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Normal_TicTacToe SIZE1 = new Normal_TicTacToe(); // This is the problem
SIZE1.setVisible(true);
}
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable()
{
public void run()
{
new MainMenu().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton buttonNormal;
// End of variables declaration
}
Normal_TicTacToe.java - I got this code from the internet, and I'm modifying it for Large and Extra Large size. I'll credit this guy as the original author in the documentation.
public final class Normal_TicTacToe extends JApplet
{
private static final long serialVersionUID = 1L;
private final Normal_Tile[] TILES = new Normal_Tile[9];
private final int TILE_SPACING = 96;
private final int WIDTH = 96, HEIGHT = 96;
private final JFrame GAMEFRAME = new JFrame("Tic-Tac-Toe");
private final Normal_TilePainter PAINTER = new Normal_TilePainter(this);
private final Normal_ClickHandler CLICK_HANDLER = new Normal_ClickHandler(this);
private final boolean AI;
private boolean aiTurn = false;
private Normal_Holder turn = Normal_Holder.X;
private int whoseTurn = 0;
private final Dimension FRAME_SIZE = new Dimension(295, 304);
private final int FONT_SIZE = 64;
private int oWins = 0;
private int xWins = 0;
private boolean gameOver = false;
private boolean nextTurn = false;
public final Normal_AI GAME_AI = new Normal_AI(this);
public void init()
{
Normal_TicTacToe game = new Normal_TicTacToe(true);
game.newGame();
}
public Normal_TicTacToe(boolean ai)
{
this.AI = ai;
PAINTER.setSize(FRAME_SIZE);
buildFrame();
loadTiles();
}
Also, both java files are in the same package.
If you're looking for extended codes and all the java files, you can find it here:
My MainMenu.java
Chall's TicTacToe and his java files (Scroll down until you see source files).

The constructor of the Normal_TICTACTOE looks like this:
public Normal_TicTacToe(boolean ai)
{
this.AI = ai;
PAINTER.setSize(FRAME_SIZE);
buildFrame();
loadTiles();
}
It has a boolean variable in its parameter list.
So the default constructor is overwritten.
call the constructor with a boolean value (true or false):
Normal_TicTacToe game = new Normal_TicTacToe(true);
(I think it has something to do with the artifical inteligence on or off)
call the method newGame() on the instance you got.
game.newGame();

You should create an instance of it in your button.
Your code:
buttonXLarge.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
ButtonXLarge(evt); // not sure what it does, but it doesn't make Tic Tac Toe
}
});
What you want most likely want it to do is initialize and start a new tic tac toe board.
buttonXLarge.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
Normal_TicTacToe myBoard = new Normal_TicTacToe(true);
myBoard.newGame();
}
});
I am unsure of how JApplet will handle inside what you're currently doing, as I normally never mix applets with JFrames, but specifically to activate the tic tac toe board, you should be writing what you want it to do within the actionPerformed listener.
If you REALLY wanted to save time on coding, you could probably rebuild TicTacToe using the guts of the JApplet in a JFrame.

Related

Java Swing: 'java.awt.AWTEventMulticaster.mouseMoved' error

I have been trying to create a 'catch me if you can' game: when I start it, it randomly chooses where to allocate a 'click me' button. I am not supposed to be able to click the button, the text should be re-assigned to another button before I am able to do that.
It works for a while but then it throws the following error: "java.awt.AWTEventMulticaster.mouseMoved".
I have been trying to fix the problem with removeListener() method but I don't seem to be able to find a solution. Any comments?
Here's my code:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.lang.*;
public class Game extends JFrame {
//Panels
private JPanel mainPanel = new JPanel();
// Buttons
private JButton[] buttons = new JButton[9];
private JButton theChosenButton = new JButton();
// other
private int random = 0;
public Game() {
this.setTitle("Catch me if you can");
mainPanel.setLayout(new GridLayout(3, 3));
// creates buttons
for(int i = 0; i < 9 ; i++) {
buttons[i] = new JButton();
mainPanel.add(buttons[i]);
}
// Add everything to frame
this.getContentPane().add(mainPanel);
this.setSize(400, 400);
this.setVisible(true);
}
// generates random number between 1 and 9 to be used
public int clickMeGenerator(){
random = (int) Math.floor(Math.random() * 9);
return random;
}
// randomly assigns clickMeGenerator to a button
// add mouseMoved listener to the chosen button
public void assign(){
int randomButton = this.clickMeGenerator();
theChosenButton = buttons[randomButton];
theChosenButton.addMouseMotionListener(new MouseHover());
theChosenButton.setText("Click me");
}
public void removeListener() {
theChosenButton.removeMouseMotionListener(new MouseHover());
//}
}
// inner class
class MouseHover implements MouseMotionListener {
public void mouseMoved(MouseEvent e) {
theChosenButton.setText("");
Game.this.assign();
}
public void mouseDragged(MouseEvent e) {
}
}
} // end of class
Test class:
public class GameTest {
public static void main (String args[]) {
Game myGame = new Game();
myGame.assign();
}
}
Thank you so much for your help!
Just for clarity, the "actual" error is ...
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at java.desktop/java.awt.AWTEventMulticaster.mouseMoved(AWTEventMulticaster.java:337)
at java.desktop/java.awt.AWTEventMulticaster.mouseMoved(AWTEventMulticaster.java:337)
So looking through the code...
public void assign() {
int randomButton = this.clickMeGenerator();
theChosenButton = buttons[randomButton];
theChosenButton.addMouseMotionListener(new MouseHover());
theChosenButton.setText("Click me");
}
You are repeatedly add a new MouseMotionListener to you buttons, over and over again, and...
public void removeListener() {
theChosenButton.removeMouseMotionListener(new MouseHover());
//}
}
is pointless, as you're trying to remove a new instance of MouseHover from the button, but it will never have been applied in the first place.
The first thing I would do is create an instance of MouseHover as an instance field in Game
private MouseHover mouseHover = new MouseHover();
and use it when calling addMouseMotionListener and removeMouseMotionListener.
I would then, remove the listener from the "currently" active button before adding it to the next one.
Personally, I would do this in the assign method
public void assign() {
int randomButton = this.clickMeGenerator();
if (theChosenButton != null) {
theChosenButton.removeMouseMotionListener(mouseHover);
}
theChosenButton = buttons[randomButton];
theChosenButton.addMouseMotionListener(mouseHover);
theChosenButton.setText("Click me");
}
I would also ensure that assign is called from within the Event Dispatching Thread when the class is first created, as the UI has been realised by the end of the constructor of Game, meaning the first call to assign is outside of the context of the EDT, which is not recommended.
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Game myGame = new Game();
myGame.assign();
}
});
}

"Ghost instances" in Java?

I have a problem working with instances of different objects and this is what happens:
I have been developing a small game in Java (Swing & AWT) for a while and I have the following classes:
App.java
Play.java
Event.java
GameScene.java
MenuScene.java
Timer.java
Where:
App extends JFrame and is a frame with the main function of the application (main), this class creates the game window, and only exists this JFrame
The MenuScene and GameScene classes are scenes of the application, for example when you see the menu and you want to see the highest score, it is a scene, the levels of game are a scene, etc., but in this case I have only two scenes and I have represented them in JPanels: MenuScene extends JPanel and creates the game menu (buttons, images, etc.), the same applies to the GameScene class, this also extends JPanel and creates the game.
The other classes (Play, Event, Timer) are simple classes, they have the "logic of the game", keyboard control, timers, game operation and are instantiated in three global variables of the GameScene class.
Everything starts with App, creates an instance of it and in its constructor calls a method to "create" the menu (MenuScene.java). Now the menu has a JButton that when pressed "creates" the game (GameScene.java) and this class has a JButton to return to the menu at any time ... It is here where I have problems because if I am playing and I return to the menu Game still exists and I can lose, it does not make sense, it is as if you play but instead of seeing the game you see the menu, interestingly the graphic part works excellent, ie if I press a button it removes what I have and draws the scene that I want it quickly. It is because Play, Timer and Event are instantiated or "exist" in memory if I am not mistaken. So if I press again the "create game" JButton I would recreate a second instance of GameScene? And so infinitely for MenuScene and GameScene. Is there a solution to this? How do you think I should structure the application?
I give you an outline of the most important classes:
App.java
public class App extends JFrame {
private JPanel rootPanel;
public App() {
//Define frame
...
runScene(new MenuScene(this));
}
public void runScene(JPanel scene) {
destroyScene();
rootPanel.setBackground(scene.getBackground());
rootPanel.add(scene);
rootPane.validate();
rootPanel.repaint();
}
private void destroyScene() {
rootPanel.removeAll();
rootPanel.revalidate();
rootPanel.repaint();
}
public static void main(String[] args) { //Main
new App();
}
}
MenuScene.java
public class MenuScene extends JPanel {
private App app;
public MenuScene(App app) {
this.app = app;
//Define JPanel
...
buttonStart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
app.runScene(new GameScene(app));
}
});
}
}
GameScene.java
public class GameScene extends JPanel {
private App;
private Play;
private Timer;
private Event; //Define controls (keyboard)
public GameScene(App app) {
this.app = app;
//Define JPanel, Play, Timer and Event
...
buttonBackMenu.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
app.runScene(new MenuScene(app));
}
});
}
}
Play.java
public class Play {
private JLabel[][] x;
public Play(JLabel[][] x) { //This matrix is important (is an reference), is instanced in GameScene, this is an problem?
this.x = x;
//Define others variables
}
}
I appreciate any help.
I have found a somewhat peculiar solution, but I do not know if it is the best:
The best way is that since the GC does not select the active timers then it is better to stop them and match the other objects to null. Using the Singleton pattern I have a single instance of a Frame, that same instance would be used in any class (Scene) that wants to run another scene, here an implementation:
App.java
public class App extends JFrame {
private JPanel rootPanel;
private static App app;
private App() {
super("x");
createApp();
}
public static App getInstance() {
if (app == null) {
app = new App();
}
return app;
}
private void createApp() {
//Define JFrame, rootPanel, buttons, etc ...
}
public void runScene(JPanel scene) {
rootPanel.removeAll();
rootPanel.add(scene);
rootPanel.revalidate();
rootPanel.repaint();
}
public static void main(String[] args) { //Main
getInstance().runScene(new MenuScene());
}
}
GameScene.java
public class GameScene extends JPanel {
private Play p;
private Timer t;
private Event e; //Define controls (keyboard)
private JLabel[][] mat;
public GameScene() {
//Define JPanel, Matrix, Play, Timer and Event
...
buttonBackMenu.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent x) {
This method is useful to create another scene for example from another instance other than this (GameScene)
changeScene(new MenuScene());
}
});
}
public void changeScene(JPanel scene) {
e.removeKeyDispatcher(); //You must create a method that allows you to move the event key dispatcher
t.stopAllTimers(); //You must create a method to stop all timers, or any object that is active.
t = null;
e = null;
p = null;
//If you have more active objects and work with other instances of other classes they should be "broken" or "stopped" and then match their instance to null
App.getInstance().runScene(scene);
}
//Optional...
public JLabel[][] getMat() {
return mat;
}
}
Play.java, Event.java, Timer.java (X)
public class X {
private GameScene g;
private JLabel[][] mat;
public X(GameScene g) {
this.g = g;
//I would use the instance of the class I need to access the variables I'm going to use, for example:
this.mat = g.getMat();
}
}

Generate a random number of JButtons in a GUI builder?

I'm using a GUI builder to make a simple JFrame that contains a JPanel. I want to add a random number of JButtons to the panel, is it possible for me to do this without having to write my own code for the JPanel? I ask because I am not strong with Swing layouts.
Main class:
public static void main( String[] args )
{
int buttonCount = new Random().nextInt(5)+1;
JFoo foo = new JFoo(buttonCount);
foo.setVisible(true);
}
JFoo class:
public class JFoo extends javax.swing.JFrame {
int buttonCount;
public JFoo() {
initComponents();
}
public JFoo(int buttonCount) {
this.buttonCount = buttonCount;
initComponents();
buttonCountLabel.setText("Button Count: "+buttonCount);
}
private void initComponents() {
//generated code
...
}

Access static variable from another class

I have two classes in same package. i have declared a static variable in one class and want to access that variable in another class.
Here is my code in which i have declared the static variable
public class wampusGUI extends javax.swing.JFrame {
static String userCommand;
public wampusGUI() {
initComponents();
}
public void setTextArea(String text) {
displayTextArea.append(text);
}
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
userCommand = commandText.getText();
}
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
wampusGUI w = new wampusGUI();
w.setVisible(true);
Game g = new Game(w);
g.play();
}
});
}
}
Here is the code in which i want to access variable
public class Game {
private wampusGUI gui;
public Game(wampusGUI w) {
world = new World();
world.start();
gui = w;
}
public void play() {
gui.setTextArea(welcome());
gui.setTextArea(describe());
for (;;) {
String s = userCommand; // here value should come should
System.out.println(userCommand);
Command c = Command.create(s);
String r = c.perform(world);
// is game over?
if (r == null) {
break;
}
System.out.println(r);
}
System.out.println("Game over");
}
}
However, i can pass the variable from first class as a argument. but the problem is that, when i will run program the value is going null first time, which i dont want. i want when i enter value in textfield then it should go to another class.
Thank you.
Looking at your code, it seems you want to show dialogs to your user with a certain text
gui.setTextArea(welcome());
gui.setTextArea(describe());
and sometimes, that dialog should capture user input which is handled afterwards.
Those setTextArea calls are not what you want to use. The user will never see the welcome message as it will immediately be replaced by the describe message.
Make sure you do not block the Event Dispatch Thread (EDT) or nothing will be shown at all. I do not know what your Command class will do, but I see an infinite loop on the Event Dispatch Thread which is never a good thing. Take a look at the Concurrency in Swing tutorial for more information
Thanks to that for loop, the user will simply not be capable to input any command as the EDT is busy handling your loop. What you need is a blocking call allowing the user to provide input (not blocking the EDT, but just blocking the execution of your code). The static methods in the JOptionPane class are perfectly suited for this (e.g. the JOptionPane#showInputDialog). These methods also have a mechanism to pass the user input back to the calling code without any static variables, which solves your problem.
I suggest that you use a listener of one sort or another to allow the Game object to listen for and respond to changes in the state of the GUI object. There are several ways to do this, but one of the most elegant and useful I've found is to use Swing's own innate PropertyChangeSupport to allow you to use PropertyChangeListeners. All Swing components will allow you to add a PropertyChangeListener to it. And so I suggest that you do this, that you have Game add one to your WampusGUI class (which should be capitalized) object like so:
public Game(WampusGUI w) {
gui = w;
gui.addPropertyChangeListener(new PropertyChangeListener() {
// ....
}
This will allow Game to listen for changes in the gui's state.
You'll then want to make the gui's userCommand String a "bound property" which means giving it a setter method that will fire the property change support notifying all listeners of change. I would do this like so:
public class WampusGUI extends JFrame {
public static final String USER_COMMAND = "user command";
// ....
private void setUserCommand(String userCommand) {
String oldValue = this.userCommand;
String newValue = userCommand;
this.userCommand = userCommand;
firePropertyChange(USER_COMMAND, oldValue, newValue);
}
Then you would only change this String's value via this setter method:
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
setUserCommand(commandText.getText());
}
The Game's property change listener would then respond like so:
gui.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
// is the property being changed the one we're interested in?
if (WampusGUI.USER_COMMAND.equals(pcEvt.getPropertyName())) {
// get user command:
String userCommand = pcEvt.getNewValue().toString();
// then we can do with it what we want
play(userCommand);
}
}
});
One of the beauties of this technique is that the observed class, the GUI, doesn't have to have any knowledge about the observer class (the Game). A small runnable example of this is like so:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class WampusGUI extends JFrame {
public static final String USER_COMMAND = "user command";
private String userCommand;
private JTextArea displayTextArea = new JTextArea(10, 30);
private JTextField commandText = new JTextField(10);
public WampusGUI() {
initComponents();
}
private void setUserCommand(String userCommand) {
String oldValue = this.userCommand;
String newValue = userCommand;
this.userCommand = userCommand;
firePropertyChange(USER_COMMAND, oldValue, newValue);
}
private void initComponents() {
displayTextArea.setEditable(false);
displayTextArea.setFocusable(false);
JButton enterButton = new JButton("Enter Command");
enterButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
enterButtonActionPerformed(evt);
}
});
JPanel commandPanel = new JPanel();
commandPanel.add(commandText);
commandPanel.add(Box.createHorizontalStrut(15));
commandPanel.add(enterButton);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
mainPanel.add(new JScrollPane(displayTextArea));
mainPanel.add(commandPanel, BorderLayout.SOUTH);
add(mainPanel);
}
public void setTextArea(String text) {
displayTextArea.append(text);
}
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
setUserCommand(commandText.getText());
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
WampusGUI w = new WampusGUI();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
w.pack();
w.setLocationRelativeTo(null);
w.setVisible(true);
Game g = new Game(w);
g.play();
}
});
}
}
class Game {
private WampusGUI gui;
public Game(WampusGUI w) {
gui = w;
gui.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
// is the property being changed the one we're interested in?
if (WampusGUI.USER_COMMAND.equals(pcEvt.getPropertyName())) {
// get user command:
String userCommand = pcEvt.getNewValue().toString();
// then we can do with it what we want
play(userCommand);
}
}
});
}
public void play() {
gui.setTextArea("Welcome!\n");
gui.setTextArea("Please enjoy the game!\n");
}
public void play(String userCommand) {
// here we can do what we want with the String. For instance we can display it in the gui:
gui.setTextArea("User entered: " + userCommand + "\n");
}
}
I agree with Jon Skeet that this is not a good solution...
But in case u want an dirty solution to ur problem then u can try this:
public class wampusGUI extends javax.swing.JFrame
{
private static wampusGUI myInstance;
public wampusGUI( )
{
myInstance = this;
initComponents();
}
public static void getUserCommand()
{
if(myInstance!=null)
{
return myInstance.commandText.getText();
}
else
{
return null;
}
}
......
......
}
in the other class use:
public void play()
{
.....
//String s = userCommand; // here value should come should
String s = wampusGUI.getUserCommand();
.....
}
This kind of code is there in some of our legacy projects... and I hate this.

Java Swing -- Jpanel with Jbuttons embedded within a JTable

Im building a Ui in Swing wherein my requirement is to have JPanes within a JTable. These JPanes will have JButtons within them.
My use case is as follows --
Im writing a MethodEditor wherein im providing a UI to store the methods within a supplied file. The UI would also allow editing the parameters being passed to the method on the click of a button.
Every single method would have a UI representation as follows --
My basic representation of the Method class is as follows --
public Class Method {
String methodName;
List<String> InputVariableNames;
String OutputVariableName;
}
Now i have a list of Method objects, List<Method> methodList on which i want to base my JTable.
This List is contained in a MethodModel class as follows --
public class MethodModel {
List<Method> methodModel;
}
I had asked a question earlier and have based my code on the answer provided there. My code however does not seem to be working. My code is as follows --
public class MethodEditor extends JTable {
private static final long serialVersionUID = 1L;
private MethodEditorModel model ;
private MethodCellRenderer cellRenderer;
public MethodEditor(MethodModel bean) {
setRowHeight(25);
this.setPreferredSize(new Dimension(500, 500));
model = new MethodEditorModel(bean);
this.setModel(model);
setupComponent();
}
private void setupComponent() {
cellRenderer = new MethodCellRenderer();
this.setDefaultRenderer(Object.class,cellRenderer);
this.setBorder(BorderFactory.createLineBorder(Color.GRAY));
}
private static class MethodEditorModel extends DefaultTableModel implements PropertyChangeListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private MethodModel bean;
public MethodEditorModel(MethodModel bean) {
this.bean = bean;
bean.addPropertyChangeListener(this);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
fireTableDataChanged();
}
}
private static class MethodCellRenderer implements TableCellRenderer {
/**
*
*/
private static final long serialVersionUID = 1L;
private MethodEditorCellPanel renderer = new MethodEditorCellPanel();
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
MethodModel methodModel = (MethodModel)value;
for(Method method : methodModel.getMethodList()) {
renderer.setComponents((Method) method);
}
return renderer;
}
}
private static class MethodEditorCellPanel extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
private JButton upButton;
private JButton downButton;
private JButton methodDetailsButton;
private Method method;
public MethodEditorCellPanel() {
upButton = new JButton("Up");
downButton = new JButton("Down");
}
public void setComponents(Method method)
{
this.method = method;
methodDetailsButton = new JButton(method.getMethodName());
upButton.addActionListener(this);
downButton.addActionListener(this);
methodDetailsButton.addActionListener(this);
Box verticalBar = Box.createHorizontalBox();
verticalBar.add(upButton);
verticalBar.add(Box.createHorizontalStrut(15));
verticalBar.add(methodDetailsButton);
verticalBar.add(Box.createHorizontalStrut(15));
verticalBar.add(downButton);
add(verticalBar);
}
#Override
public void actionPerformed(ActionEvent evt) {
if(evt.getSource().equals(downButton)) {
}
if(evt.getSource().equals(upButton)) {
}
if(evt.getSource().equals(methodDetailsButton)) {
}
}
}
}
The code compiles but the JTable does not show up.
Any pointers on what i may be doing wrong would be of great help.
Don't include another components to JTable. Let alone components with multiple other components. The reason is that JTable won't pass mouse events to its cells. So even when you have buttons inside JTable, then you would have to take care about pressing them by yourself, by:
get cell it was clicked to
get the exact coordinates
extrapolate these coordinates to the inner component
manually call click on the corresponding button.
And even then you won't get button animation and stuff.
If you need to arrange components into a table, use JPanel with GridLayout or GridBagLayout.

Categories