I have a main class who hold the frame and the JMenu, and another class for the game extending from JPanel. How is possible for when is clicked one option from jMenu create a new game?
At the moment, I have this
class Application {
private static Game game;
private static TheFrame frame;
private static JMenuBar menu;
public Application(){
frame = new TheFrame();
menu = new JMenuBar();
JMenu file = new JMenu("File");
menu.add(file);
frame.setJMenuBar(menu);
JMenuItem newGame = new JMenuItem("New Game");
newGame.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
newGame();
}
});
file.add(newGame);
JMenuItem scores = new JMenuItem("Show Scores");
scores.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new Score();
}
});
file.add(scores);
JMenuItem exit = new JMenuItem("Exit!");
exit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
file.add(exit);
}
private void newGame(){
// frame.getContentPane().removeAll();
frame.getContentPane().setBackground(Color.pink);
game = new Game();
frame.addGameToCanvas(game);
game.runMenu();
}
/**
* Main method
* #param args
*/
public static void main(String args[]) {
Application app = new Application();
}
}
But when I click on new Game, the frame get black and doesn't show anything. I had before this on the command line, and all work fine, but when I try to do this using the menu doesn't work. Any suggestions?
EDIT
the code for frame.addGametoCanva(). What it does, is add the game object to a canvas.
public void addGameToCanvas(Game g){
canvas = g;
add(canvas, BorderLayout.CENTER);
invalidate();
validate();
repaint();
}
And yes, the game object, have a finite loop, for the user inputs (from console)
the game object, have a finite loop, for the user inputs (from console)
The Event-Dispatch thread (EDT) is single threaded - it takes care of painting, event handling, etc... If one attempts to perform a long running tasks on this thread, like waiting for user input from a different source (like the console) the EDT can lock up (in other words an unresponsive UI).
Any long running tasks should be placed inside its own Thread or ran via a SwingWorker. Further, most calls on a non-EDT thread should be dispatched to the EDT using SwingUtilities.invokeLater or SwingUtilities.invokeAndWait (this includes construction of the UI - note the main method operates on a non-EDT thread).
Although, I'm not entirely sure why the mix of UI (eg Console and Swing) - you might consider choosing just a single UI.
Related
I have a JPanel holding a JButton and JScrollPane (in turn holding a JTable) and am currently running into two issues which I believe are related:
The JButton listener's actionPerformed() method is not invoked upon click. The only way in which I can get it to be invoked is by calling doClick() on the JButton. The JButton color changes upon hover but no click animation is shown when the mouse is pressed.
Secondly, if a cell is clicked within the JTable, the cell located 2 rows down in the same column registers the click instead. This offset does not occur when clicking in the column headers (i.e. to adjust cell widths), only when within the cell area.
Left-hand panel. Click position circled
public class InventoryPanel extends JPanel {
// Parent Business object reference for communication and JFrame
private Business parent;
private AddItemPanel addItemPanel;
// Inventory table items
private DefaultTableModel inventoryModel;
private JTable inventoryTable;
private JScrollPane inventoryScrollPane;
private JLabel updateLbl;
private JButton addItemBtn;
// Columns for inventory table
private static final String[] INVENTORY_COLUMNS = {"Item","Stock","Restocking Level","Edit"};
public InventoryPanel(Business parent) {
this.parent = parent;
initGUI();
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
//doStuff
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace(new PrintStream(System.out));
}
}
}
}).start();
}
// INITIALISES GUI
public void initGUI() {
this.setLayout(new BoxLayout(this,BoxLayout.PAGE_AXIS));
this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
JLabel titleLabel = new JLabel("<html><B>Inventory</B></html>");
this.add(titleLabel);
// Create empty inventory table
inventoryModel = new DefaultTableModel(new Object[3][4],INVENTORY_COLUMNS);
inventoryTable = new JTable(inventoryModel);
inventoryScrollPane = new JScrollPane(inventoryTable);
// Create button to allow items to be added
addItemBtn = new JButton("Add item");
addItemBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("ADD ITEM PRESSED");
}
});
updateLbl = new JLabel("Loading inventory...");
this.add(addItemBtn);
this.add(inventoryScrollPane);
this.add(updateLbl);
}
I've tried removing the table from the panel to see if that solves the JButton issue and visa-versa, but no luck. I've also tried changing the project JDK but no luck there either.
There are other JPanels adjacent to the troublesome one in a JFrame which work perfectly fine. Any ideas?
Edit: I can create a working instance of the InventoryPanel alone in a frame in another project, as mentioned in the comments. However the exact same code (no calls being made to other objects/methods) in the current project now produces ClassCastExceptions. After some googling this seems to be due to non-EDT threads updating the GUI.
However there is no use of the Business class, and all GUI operations are performed using the SwingUtilities.invokeLater() method like so:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("test");
frame.add(new InventoryPanel());
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
Note: the no-argument constructor InventoryPanel() just calls initGUI().
Thanks for the help so far...still very confused by this.
I am working on a game that uses a custom console-style GUI to play. I previously asked a question to find out what was causing a NullPointerException. See this link for context.
I now have the error eliminated, but I have a new issue: When I start the game, the GUI only loads the JFrame (EDIT: JPanel not JFrame) but none of the other components. In the original post, the GUI components loaded up normally, but starting the game itself would cause a NullPointerException due to a call to the JTextArea before the GUI dispatch thread was complete.
Here is the current code:
Classic.java
import javax.swing.*;
import java.util.*;
import static javax.swing.SwingUtilities.invokeLater;
public class Classic extends Game{
private static JFrame gui;
private static GUIClassic newContentPane;
...
public void play() {
invokeLater(Classic::startGUI);
invokeLater(Classic::startGame);
}
public static void startGame() {
//Game processes
...
}
private static void startGUI() {
gui = new JFrame("Karma :: Classic Mode");
gui.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
newContentPane = new GUIClassic();
newContentPane.setOpaque(true);
gui.setContentPane(newContentPane);
gui.pack();
gui.setVisible(true);
}
...
}
GUIClassic.java
...
public class GUIClassic extends JPanel implements ActionListener {
private JTextArea output;
private JTextField input;
private boolean inputReady;
private String inputText;
public GUIClassic() {
super();
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
output = new JTextArea(15, 15);
output.setEditable(false);
JScrollPane outputScroll = new JScrollPane(output);
input = new JTextField("",40);
add(outputScroll);
add(Box.createRigidArea(new Dimension(0,5)));
add(input);
}
}
In the play() method of Classic.java, I tried removing the invokeLater(...) methods, that came up with the same result.
public void play() {
startGUI();
startGame();
}
I also tried moving the call to startGame within startGUI:
public static void startGUI() {
...
startGame();
}
However, removing startGame altogether allows it to start up normally:
public void play() {
invokeLater(Classic::startGUI);
}
I am completely at a loss. I don't understand why loading the GUI without playing the game loads it fine, but beginning the game suddenly makes the components disappear.
Please note: there are no exceptions thrown during runtime.
As a side note, the GUI also does not allow me to close it via the [X] button in the current version, but in the cases where only 'startGUI' is called, the components pop up and the [X] allows you to exit.
Try to invoke the startGame() in a new Thread, because all of the painting stuff is done in the main-thread like that
new Thread(new Runnable() {
#Override
public void run() {
Classic.startGame();
}
}).start();
You never set the size of your Panel.
Try to use the setPreferredSize(Dimension)-method of your GUIClassic before you call the pack()-method of your JFrame.
This question already has answers here:
Java swing GUI freezes
(5 answers)
Closed 7 years ago.
I have some Java classes that should simulate an old calculating machine. One of them is a JFrame, called GUI, with a JTabbedPane. The calculating process should take some time, so I have included Thread.sleep(int). The problem is that while the calculating function is working, the tabs of the JTabbedPane in my GUI can't be selected.
Here is some code:
import java.util.*;
import java.awt.*;
public class Engine
{
private GUI gui;
private Store store;
private Mill mill;
public static void main(String[] args) {
Engine e = new Engine();
}
public Engine() {
gui = new GUI(this);
mill = new Mill(this);
store = new Store(this);
store.draw(); // affects GUI elements
}
public void read(String operations) {
clearStatus(); // affects GUI elements
try {
String[] op = operations.split("\n");
for (int i = 0; i < op.length; i++) {
setStatus(op[i]); // affects GUI elements
if (op[i].length() == 0) { // empty line
continue;
}
int number = Integer.parseInt(op[i]);
store.addNumber(number);
Thread.sleep(200);
}
store.draw(); // affects GUI elements
} catch(Exception e) {
System.out.println(e);
}
}
public void clearStatus() {
gui.statusLabel.setText("");
}
public void setStatus(String msg) {
gui.statusLabel.setText(msg);
}
}
Here's my GUI (this is a short version):
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GUI extends JFrame
{
private Engine engine;
// Swing
JTabbedPane tabs;
JPanel mill;
JTextArea input, store;
JLabel statusLabel;
JButton sendInput;
public GUI(Engine e)
{
engine = e;
input = new JTextArea();
sendInput = new JButton("Run");
sendInput.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
pressInputButton(evt);
}
});
JPanel inputPanel = new JPanel();
inputPanel.add(input, BorderLayout.CENTER);
inputPanel.add(sendInput, BorderLayout.SOUTH);
mill = new JPanel();
statusLabel = new JLabel();
mill.add(statusLabel);
store = new JTextArea();
JTextPane helpPane = new JTextPane();
helpPane.setText("[...]");
tabs = new JTabbedPane();
tabs.addTab("Input", inputPanel);
tabs.addTab("Mill", mill);
tabs.addTab("Store", new JScrollPane(store));
tabs.addTab("Help", helpPanel);
add(tabs, BorderLayout.CENTER);
setVisible(true);
pack();
}
public void pressInputButton(ActionEvent evt) {
engine.read(input.getText());
}
}
What #sorifiend means is, calling sleep() from an event handler will "lock up" your application's event dispatch thread. The application will not respond to any other user input until the event handler function returns.
If you want the application to be responsive while the calculator is "working", then what you need to do is:
Record that the state of the calculator is "working"
Disable whatever buttons you don't want the user to press while the calculator is "working", --OR-- change the handlers for those buttons to do something different (simulate jamming the machine?) if they are pressed while in the "working" state.
Submit a timer event for some time in the near future (however long the calculation is supposed to take.)
In the handler for the timer event, display the result of the calculation, re-enable the disabled buttons, and change the state from "working" back to "idle" (or whatever).
I am getting some strange errors when running my Java game. I've been working on this game for weeks as a project for my computer science class and it is due on 6/4/2015, so I appreciate any help. Initially I wrote the game in a class called Game and ran it from a static method called run. Later, I decided to add a GUI menu in a class called Control. To run the game now, I call the main method in the Control class. This menu has a button with an action listener. When the button is clicked, the run method in the Game class is called. If I click and run the run method directly the game works fine. But if I click the button which calls the run method it draws a frame, but not the actual game.
Here is my code for the GUI:
public class Control extends JFrame implements ActionListener {
// JPanel
public JPanel pnlButton = new JPanel();
// Buttons
public JButton btnAddFlight = new JButton("Multiplayer");
public JButton single = new JButton("Singleplayer");
public Control() throws IOException,InterruptedException {
super("Bouncy Ball");
//Set button size
btnAddFlight.setBounds(150, 400, 220, 30);
single.setBounds(150,350,220,30);
// JPanel bounds
pnlButton.setBounds(0, 0, 500, 500);
pnlButton.setBackground(Color.WHITE);
// Adding the Bouncy Ball logo to JPanel
String path = "gg.jpg";
File file = new File(path);
BufferedImage image = ImageIO.read(file);
JLabel label = new JLabel(new ImageIcon(image));
label.setBounds(179,50,150,150);
//Action Listener setup
single.addActionListener(this);
//add buttons and title logo to JPanel
pnlButton.add(btnAddFlight);
pnlButton.add(label);
pnlButton.add(single);
//Set up and add the instructions to the JPanel
JLabel gg = new JLabel("Welcome to Bouncy Ball, a game where you have to manipulate");
gg.setFont(new Font("Serif", Font.PLAIN, 18));
gg.setBounds(0,10,500,500);
pnlButton.add(gg);
JLabel f = new JLabel("the window size with WASD to win! Click the buttons to start.");
f.setFont(new Font("Serif", Font.PLAIN, 18));
f.setBounds(0,28,500,500);
pnlButton.add(f);
pnlButton.setLayout(null);
//Add JPanel to JFrame
this.add(pnlButton);
// JFrame properties
setSize(500, 500);
setTitle("Bouncy Ball");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);;
}
#Override
public void actionPerformed(ActionEvent submitClicked) {
try{
isClicked = true;
Game.run();
}
catch(InterruptedException a){}
}
public static void main(String[] args) throws IOException,InterruptedException{
new Control();
}
}
and here is the run method in my game class:
public static void run() throws InterruptedException {
//Setting up JFrame
frame = new KeyFrame("Bouncy Ball");
frame.setSize(1000, 1000);
//Setting up Scanner and get user level input
Scanner scan = new Scanner (System.in);
System.out.println("Level one, two, three, or four?(must be an int, four is multiplayer)");
f = scan.nextInt();
//Display JFrame
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.toFront();
//Add Game JPanel
game = new Game();
frame.add(game);
//Setting up game basics
b = new Ball(game,0,0,30,30);
setUpLevel();
objList.add(0,b);
//Main Game Loop
while (true) {
//Request Focus
game.requestFocusInWindow();
//Update current Time
lastStartTime = System.currentTimeMillis();
//Move the ball and the player
p.move();
b.move();
//Refresh JPanel
game.repaint();
Thread.sleep(10);
//Check if the player wins or loses
checkWin();
checkLoss();
}
}
If I call the run method directly it works, but not if I click the button in the GUI. I have a feeling that it is a thread issue, because the main thread ends once run is called. I'm not a 100% percent sure as to the cause though. I'd appreciate any responses, I've been working on this game for weeks as a project for my computer science class and it is due on 6/4/2015.
Swing is single threaded - all painting, events, etc...occur on this thread (named the EDT). The Game.run method is called on the EDT, which in turn executing a long running task (eg while (true), Thread.sleep) - this prevents the EDT from doing anything else. To perform animation, consider using a Thread, or better yet a Swing Timer.
So I've been trying to work with the code in this post for making a console window in a JTextArea. That code seems to be functioning with mine, but I'm running into an odd problem.
My program: I'm basically building a quick and dirty gui for a command line tool I made recently. The only thing that the gui contains is a button that says "Start Automation Engine" and then it has a JTextArea that is supposed to display any text my program sends to System.out.println().
At the moment it displays nothing, though the program itself is running and working (and should be displaying output as a result.) I have noticed that when I click the button on my gui the button stays depressed while the program runs. This has lead me to believe that the JFrame is not updating while the program is running, thus the JTextArea, as it's child, is not updating. This is not so good...
Is there a way to get that JTextArea to update while the program is running in the background?
Here's my code for the JFrame, btw, if you'd like to look at it to get a better idea of what I'm talking about. It was mostly constructed in WindowBuilder in Eclipse. The only thing I did was add a button listener to startAutmoatorEngineButton and then add the last few lines of the initalize() method to set the JTextArea (engineOutput) as the System.out.
public class EngineGUI {
private JFrame frmAutomatorEngine;
private File logPath = new File("<redacted>", "<redacted>");
private File masterFile = new File("<redacted>", "<redacted>");
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
EngineGUI window = new EngineGUI();
window.frmAutomatorEngine.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public EngineGUI() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frmAutomatorEngine = new JFrame();
frmAutomatorEngine.setType(Type.UTILITY);
frmAutomatorEngine.setResizable(false);
frmAutomatorEngine.setTitle("Automator Engine");
frmAutomatorEngine.setBounds(100, 100, 636, 335);
frmAutomatorEngine.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JMenuBar menuBar = new JMenuBar();
frmAutomatorEngine.setJMenuBar(menuBar);
JMenu mnEngine = new JMenu("Engine");
menuBar.add(mnEngine);
JMenuItem mntmLoadMasterFile = new JMenuItem("Load Master File...");
mnEngine.add(mntmLoadMasterFile);
JMenuItem mntmExit = new JMenuItem("Exit");
mnEngine.add(mntmExit);
frmAutomatorEngine.getContentPane().setLayout(null);
JTextArea engineOutput = new JTextArea();
engineOutput.setBounds(10, 48, 600, 217);
frmAutomatorEngine.getContentPane().add(engineOutput);
JButton startAutomatorEngineButton = new JButton("Start Automator Engine");
startAutomatorEngineButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MasterFile master = null;
try {
master = new MasterFile(masterFile, logPath);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
AutomationLoop theLoop = new AutomationLoop(master);
theLoop.startLoop();
}
});
startAutomatorEngineButton.setBounds(441, 11, 169, 23);
frmAutomatorEngine.getContentPane().add(startAutomatorEngineButton);
//Set up textArea to be used as output stream for program
TextAreaOutputStream outputStream = new TextAreaOutputStream(engineOutput);
PrintStream printStream = new PrintStream(outputStream);
System.setOut(printStream);
//System.setErr(printStream);
}
}
It's hard to tell for sure because I don't know what your AutomationLoop and TextAreaOutputStream classes do, but it sounds like a threading problem.
All your Swing code needs to execute in the Event Dispatch Thread. If you have a long running code that is not updating the GUI, then you probably want it running another thread, otherwise the GUI doesn't get a chance to update. From your behavior, it sounds like theLoop.startLoop() is running in the Event Dispatch Thread, and so the GUI never gets a chance to update itself.
Does theLoop.startLoop() start a new thread? If not it probably should; otherwise until that code finishes executing, your GUI will not update.