Assign function to Jbutton - java

I have a few classes to create a very simple GUI (JFrame) with a few objects like a JTabbedPanel and a JTree. And one of the classes that creates a ribbon in the JTabbedPanel creates a JButton, that should have a function that updates the JTree.
I do have some getter, but I have no idea how to use the JButton to update something on an object that gets created in the main method (the GUI object, from where I would be able to get to the JTree).
How do I update something on an object that gets created in the main method in the actual class of the JButton?
I might have to change the structure of my project.
public class Gui extends JFrame{
private Ribbon ribbon;
private Status status;
public Panel panel;
public Gui(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("INIT TITLE");
setExtendedState(Frame.MAXIMIZED_BOTH);
setMinimumSize(new Dimension(600,400));
setVisible(true);
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}
ribbon = new Ribbon();
add(ribbon, BorderLayout.NORTH);
status = new Status();
add(status, BorderLayout.SOUTH);
panel = new Panel();
add(panel, JSplitPane.HORIZONTAL_SPLIT);
//panel.setLeftComponent(panel.getTree());
//panel.openProject();
setVisible(true);
panel.loadProject();
}
The Ribbon doesnt do a lot:
public class Ribbon extends JTabbedPane {
public Ribbon(){
addTab("Home", null, new RibbonHome());
addTab("Import", null, new RibbonImport());
addTab("Options", null, new RibbonOptions());
}
}
But the Ribbon-Tab creates some buttons:
public class RibbonHome extends JPanel{
private JButton b1, b2, b3;
public RibbonHome(){
b1 = new JButton("test1");
b2 = new JButton("test2");
b3 = new JButton("test3");
add(b1);
add(b2);
add(b3);
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
//UPDATE the JTree
}
});
}
}

button.addActionListener( main );
main implements ActionListener and provides the method actionPerformed()

A code sample of what you are doing would make things easier, but anyway.
Probably you want to make the component you are planning to update a class member. This way, you can initialize it where it is initialized now, but still reach it from your ActionListener.
Again, this is a bit a guess on how you structured things, so if you can post a code sample, I am sure you can get a much more detailed answer.
EDIT after code sample:
You could give RibbonHome an additional private member of type Gui. I usually call it parent, so it becomes: private Gui parent;
In the constructor of RibbonHome, you add Gui as a parameter, this way, RibbonHome will always have access to its parent.
Also add it to the constructor of Ribbon, because you will want to pass it through there.
As a last step, you will always have to construct Ribbon with Gui as a parameter, so in Gui it will become: ribbon = new Ribbon(this);
As soon as you have done all of this, you can reach the JTree by the getter of you JTree on parent.
There are better solutions, since this will have as a drawback that you will never be able to construct a new Ribbon from another component than a Gui, but if you want that, you can work with interfaces.

The most obvious answer is of course to pass the reference of your Gui class to your Ribbon class, which in turn can pass it to the RibbonHome class.
public Gui{
//...
ribbon = new Ribbon( this );
//...
}
public Ribbon ( Gui gui ){
//...
addTab("Home", null, new RibbonHome( gui ));
//...
}
public RibbonHome( Gui gui){
//...
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
//UPDATE the JTree
}
});
//...
}
However, this ensures a very tight coupling between your different classes. I do not know what exactly you want to update on that JTree but the more fundamental solution might be to share a model between your different views. Your JTree would update itself when the model changes, and the button just updates the model.

Related

What is causing JLabel not to update?

Im using Observer Pattern to display whos turn it is to play. And im being given the right data from my Observable, thats why i dont post it. As you can see, the text is being set, as i can get call label.getText().
How come the label wont update?
Ive tried repaint, revalidate, SwingUtilities.invokeLater, updateUI, and all sort of stuff. I do not know what im doing wrong.
Thank you in advanced.
public class PlayersTurnPane extends JPanel implements Observer {
private JLabel label = new JLabel();
public PlayersTurnPane() {
setLayout(new BorderLayout());
setBackground(Color.gray);
label.setText("White");
label.setHorizontalAlignment(getWidth() / 2);
label.setFont(new Font("Serif", Font.PLAIN, 25));
add(label);
}
#Override
public void update(Observable arg0, Object arg1) {
label.setText((String)arg1); //But it will not update label
System.out.println(label.getText()); //The text is being set
}
}
My Observable
public class TurnObserver extends Observable {
public TurnObserver(PlayersTurnPane playersTurnPane) {
addObserver(playersTurnPane);
}
public void setTurn(String turn){
setChanged();
notifyObservers(turn);
clearChanged();
}
}
Where i send the data
private TurnObserver turnObserver = new TurnObserver(new PlayersTurnPane());
public void grabPiece(BoardTile boardTile) {
if(focusedPiece != null && playerTurn.getCurrentTurn() == focusedPiece.getPieceColor()) {
boardTile.add(focusedPiece);
updateUI();
playerTurn.changeTurn();
turnObserver.setTurn(playerTurn.getCurrentTurn());
}
}
The panel is added here
public class EastPanel extends JPanel {
public EastPanel() {
setLayout(new GridLayout(3,0));
add(new TimePane());
add(new MoveHistoryPane());
add(new PlayersTurnPane());
}
}
If System.out.println(label.getText()); is being printed, then you've got a Swing threading issue in code not shown, likely due to long-running code being run on the Swing event thread, preventing this thread from doing its job -- like painting the updated text on the JLabel. Be sure that all long running code is done on a background thread and that Swing state changes and queries are made on the Swing event thread. Read Concurrency in Swing for the details.
re,
And im being given the right data from my Observable, thats why i dont post it.
But it's that unposted code which holds the key to your problem.
Dang, Mad is correct: you're shadowing:
public TurnObserver() {
addObserver(new PlayersTurnPane());
}
You're adding a new PlayersTurnPane as your observer, but it's likely not the displayed PlayersTurnPane! Key: every time you call new on a constructor, you create a new object, here a PlayersTurnPane. You should pass in the displayed PlayersTurnPane, not a new one.
You've got problems with code like this:
public class EastPanel extends JPanel {
public EastPanel() {
setLayout(new GridLayout(3,0));
add(new TimePane());
add(new MoveHistoryPane());
add(new PlayersTurnPane());
}
}
The dsiplayed PlayersTurnPane is being created anonymously -- the object is being added to a display, but this object is not being referenced by any variable, and this needs to be done if you're going to pass the exact same PlayersTurnPane object into the method that makes it an observable.
I suggest that you use class fields and not anonymous objects where you need a reference to the object. So here, perhaps better would be:
public class EastPanel extends JPanel {
public EastPanel(PlayersTurnPane playersTurnPane) {
setLayout(new GridLayout(3,0));
add(new TimePane()); // you likely need parameters for
add(new MoveHistoryPane()); // these two objects as well
add(playersTurnPane);
}
}
The key concept that you seem to be missing is one of the importance of references which are pointers to an object of interest. Read up on this.

How can I switch between JPanels in a CardLayout using MVC?

I'm making a Maths Game application and recently began implementing MVC.
I have the following structure:
auiAs2
MigJPanel: extends JPanel
ScreenInterface.java: contains global variables, fonts, and difficulty enum
MathsGame.java: extends JFrame
auiAs2.view
DiffView.java: extends MigJPanel implements ScreenInterface
GameView.java: extends MigJPanel implements ScreenInterface
EndGameView.java: extends MigJPanel implements ScreenInterface
auiAs2.controller
DiffControl.java
GameControl.java
EndGameControl.java
auiAs2.model
Model.java: implements ScreenInterface
My MathsGame.java contains a JPanel set to CardLayout which instances of DiffView, GameView, and EndGameView are added to. When I run my program, the diffView 'card' is shown to the user.
If the user clicks "New Game", an ActionListener in DiffControl.java gets the selected difficulty.
public class DiffControl {
private DiffView diffView;
private Model model;
public DiffControl(DiffView diffView, Model model) {
this.diffView = diffView;
this.model = model;
this.diffView.addNewGameListener(new NewGameListener());
}
class NewGameListener implements ActionListener {
String selectedDiff;
#Override
public void actionPerformed(ActionEvent e) {
selectedDiff = diffView.getSelectedDiff();
//MathsGame.setLayCard(panContainer, "New Game"));
}
}
}
This is where I get stuck. Where should I switch between panels in my CardLayout JPanel layCard? (MathsGame.java is shown below with irrelevant code removed. The entire code for relevant classes is linked above if required)
public class MathsGame extends JFrame {
private JPanel panContainer = new JPanel();
private CardLayout layCard = new CardLayout();
public MathsGame() {
panContainer.setLayout(layCard);
setContentPane(panContainer);
setSize(new Dimension(WIDTH, HEIGHT));
setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
setTitle(TITLE);
DiffView panDiffView = new DiffView();
panContainer.add(panDiffView, "Choose Difficulty");
GameView panGameView = new GameView();
panContainer.add(panGameView, "New Game");
EndGameView panEndGameView = new EndGameView();
panContainer.add(panEndGameView, "End Game");
Model model = new Model();
DiffControl diffControl = new DiffControl(panDiffView, model);
//GameControl gameControl = new GameControl(panGameView, model);
//EndGameControl EndGameControl = new EndGameControl(panEndGameView, model);
layCard.show(panContainer, "Choose Difficulty");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(MathsGame::new);
}
}
So my question is:
Where would be the best place to put code related to switching between Views in my CardLayout container?
The model will have a state field, perhaps an enum, that would reflect the view. You could make this a bound property using a SwingPropertyChangeSupport object. Then the view could listen to the state of this property and swap the Card based on its state. In fact the toString() of each enum constant could be used to add card views to the CardLayout-using container.
Edit
You ask:
Could you elaborate on the usage of SwingPropertyChangeSupport?
You can find examples of this here, here, and especially here.
I've attempted using addPropertyChangeListener in the model. And how would the View control the card change when the CardLayout is in MathsGame.java?
The view would be notified of the state change of the model, and then when this happens, the view would call its code to swap cards.
I've had problems with doing that inside View for "calling a static method from a non-static context".
This is a completely different unrelated issue, a basic core Java issue, that I'm sure with a little work, you'll be able to solve. In brief -- don't try to call instance code in a static way. Always call it on a proper reference, not on the class.

Button running a whole new class

I want my button to run a whole new class that will do different things inside. I don't know if that is even possible cause i'm really bad at java. My code looks like this at the moment:
public class MainMenu {
private class GardenActivities {
public GardenActivities() {
JFrame GardenAct = new JFrame();
GardenAct.setSize(400, 400);
GardenAct.setVisible(true);
}
}
public static void main(String[] args) {
JFrame choice = new JFrame();
choice.setSize(700, 500);
choice.setLocationRelativeTo(null);
choice.setTitle("Seeds");
choice.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(0, 1));
JButton Labora = new JButton();
Labora.setText("Laboratory");
Labora.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ev) {
GardenActivities();
}
});
JButton Garden = new JButton();
Garden.setText("Garden");
Garden.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ev) {
}
});
choice.getContentPane().add(panel);
ButtonGroup group = new ButtonGroup();
group.add(Garden);
group.add(Labora);
panel.add(Garden);
panel.add(Labora);
choice.setVisible(true);
}
}
Just like I said. I need something to run my GardenActivities class just by pressing Garden button.
Your code doesn't compile, does it? When that happens, you need to post compilation errors with your question so that we can help you with them.
You need to add the key word new before the GardenActivities() statement.
#Override
public void actionPerformed(ActionEvent ev) {
new GardenActivities(); // here
}
Also, put the GardenActivities in its own file. There's no reason for making it a private inner class and many reasons not to do this.
Having said this, I recommend against having one JFrame create and display another JFrame since an application should have usually only one JFrame. Instead consider swapping JPanel "views" using a CardLayout, or if you must show a different window, consider showing the second dependent window as a modal or non-modal dialog.
Also more unsolicited advice: Your main method is doing way too much. Most of the code inside of the static main method should go inside the non-static main gui class, whatever that is, perhaps in its constructor or in an initGui() method that the constructor calls. The main method should just create an instance of the main gui class, make it visible, and that's about it.
And regarding:
I don't know if that is even possible cause i'm really bad at java.
Keep writing lots and lots of code, a ton of code, and keep reviewing tutorials and textbooks, and this will change.
I think that you just need to add:
new GardenActivities();
Into your JButton's actionPerformed() method.
Good Luck!
One way to do what you want, we make the GardenActivities class implement ActionListener itself.
Then your code would look something like this:
Garden.addActionListener(new GardenActivities());
Otherwise, your plan should work.
NOTE
See comments for opposing opinions about why one would want to leave the ActionListener in the anonymouse inner class and have it call into GardenActivities.
Thank you #HovercraftFullOfEels
As others have pointed out, this:
#Override
public void actionPerformed(ActionEvent ev)
{
GardenActivities();
}
Should look like:
#Override
public void actionPerformed(ActionEvent ev)
{
new GardenActivities();
}
There is no reason to create an inner class, and GardenActivities can be, and should be, its own class.

Controlling another class from a different class

I am having a bit of problem regarding Swing. I have a JFrame called FrameMain. Inside it is a JPanel called panelChoices.
When FrameMain is called/created, it fills up the panelChoices object with a number of PanelEntries objects, which is a JPanel with a number of JButtons in it (it is a different class that I wrote).
What I want to do is when I click one of the buttons inside the PanelEntries object, I want to destroy/remove FrameMain, along with the rest of it components (including the PanelEntries object that contains the JButton).
I've tried using super but it returns the JPanel (the PanelEntries object) that holds the JButton and not FrameMain that holds them all together. How can I achieve this?
EDIT: It seems that I am not clear enough, so here's a bit more information from my work. I don't have the actual code right now because I am on a different machine but I hope this will help elaborate my question.
public class FrameMain() {
private JFrame frameMain;
private JPanel panelChoices;
public FrameMain(args) {
createGUI();
loadData();
}
private void createGUI() {
JFrame frameMain = new JFrame();
JPanel panelChoices = new JPanel(new GridLayout(1,1));
frameMain.add(panel);
// removed formatting and other design codes since they are not important.
pack();
}
private void loadData() {
boolean available;
for (int i = 1; i <= 10; i++) {
// do some if/else and give value to boolean available
PanelEntries panel = new PanelEntries(i, available);
frameMain.add(panel);
// more code here to handle data.
}
}
}
public class PanelEntries() extends JPanel {
public PanelEntries(int num, boolean avb) {
JButton button = new JButton("Button Number " + num);
button.setEnabled(avb);
add(button);
// add action listener to created button so that it calls 'nextScreen()' when clicked.
// more code
pack();
}
private void nextScreen() {
// destroy/dispose MainFrame here.
// See Notes.
AnotherFrame anotherFrame = new AnotherFrame();
}
}
Notes:
All classes are inside their own .java file.
I need to know how to dispose FrameMain from the button inside the PanelEntries object, not just disposing a JFrame.
As per the given information,
If you want to exit the application, its not a big deal use System.exit(0); :)
If you mean to dispose the frame, jframe.dispose();
If you want to remove a componet / all components you can use .remove(Component) / .removeAll() etc
If this did not help, please re-write your question with more information.

Difficulty removing all components from a Jpanel

G'day all,
I am coding a main menu for a project. The menu displays properly. I have also set up ActionListeners for the three buttons on the menu.
What I wish to do is reuse the JPanel for a new set of radio buttons when the user chooses "Start a New Game".
However, coding ActionPerformed to remove the existing components from the JPanel has me stumped. I know removeAll is somehow important, but unfortunately NetBeans informs me I cannot call it on my mainMenu JPanel object within ActionPerformed. So i have commented it out in my code below, but left it in so you can see what I am trying to do.
Your thoughts or hints are appreciated.
Here is my main code:
public class Main {
public static void main(String[] args) {
MainMenu menu = new MainMenu();
menu.pack();
menu.setVisible(true);
}
}
Here is my mainMenu code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MainMenu extends JFrame implements ActionListener {
JButton startNewGame = new JButton("Start a New Game");
JButton loadOldGame = new JButton("Load an Old Game");
JButton seeInstructions = new JButton("Instructions");
public MainMenu() {
super("RPG Main Menu");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainMenu = new JPanel();
mainMenu.setLayout(new FlowLayout());
startNewGame.setMnemonic('n');
loadOldGame.setMnemonic('l');
seeInstructions.setMnemonic('i');
startNewGame.addActionListener(this);
loadOldGame.addActionListener(this);
seeInstructions.addActionListener(this);
mainMenu.add(startNewGame);
mainMenu.add(loadOldGame);
mainMenu.add(seeInstructions);
setContentPane(mainMenu);
}
public void actionPerformed(ActionEvent evt) {
Object source = evt.getSource();
if (source == startNewGame) {
// StartNewGame code goes here
// mainMenu.removeAll();
}
if (source == loadOldGame) {
// LoadOldGame code goes here
}
if (source == seeInstructions) {
// Quit code goes here
}
}
}
Consider using a CardLayout instead, which manages two or more components (usually JPanel instances) that share the same display space. That way you don't have to fiddle with adding and removing components at runtime.
You need mainMenu to be a member variable:
public class MainMenu extends JFrame implements ActionListener {
JButton startNewGame = new JButton("Start a New Game");
JButton loadOldGame = new JButton("Load an Old Game");
JButton seeInstructions = new JButton("Instructions");
JPanel mainMenu = new JPanel();
Why do you feel the need to re-use this object?
You don't have a reference to mainMenu actionPerformed use. If you declare mainMenu with the buttons. It would work.
The problem is that the actionPerformed method is trying to call the JPanel mainMenu which is out of scope, i.e. the mainMenu variable is not visible from the actionPerformed method.
One way to get around this is to have the JPanel mainMenu declaration in the class itself and make it an instance field which is accessible to all instance methods of the class.
For example:
public class MainMenu extends JFrame implements ActionListener
{
...
JPanel mainMenu;
public MainMenu()
{
...
mainMenu = new JPanel();
...
}
public void actionPerformed(ActionEvent e)
{
...
mainMenu.removeAll();
}
}
Avoid attempting to "reuse" stuff. Computers are quite capable of tidying up. Concentrate on making you code clear.
So instead of attempting to tidy up the panel, simply replace it with a new one.
Generally a better way to write listeners is as anonymous inner classes. Code within these will have access to final variables in the enclosing scope and to members of the enclosing class. So, if you make mainMenu final and you ActionListeners anonymous inner classes, your code should at least compile.
Also don't attempt to "reuse" classes. Try to make each class do one sensible thing, and avoid inheritance (of implementation). There is almost never any need to extend JFrame, so don't do that. Create an ActionListener for each action, rather than attempting to determine the event source.
Also note, you should always use Swing components on the AWT Event Dispatch Thread. Change the main method to add boilerplate something like:
public static void main(final String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
runEDT();
}});
}

Categories

Resources