Tzipirig
Tzipirig

Reputation: 33

Java ActionListener not working on JMenuItem

I have a problem with some ActionListeners that are not working as intended. Here is the code for them:

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

public class GameOfLife extends JFrame implements ActionListener
{
    Timer timer = new Timer(700, this);
    Table world;
    JMenuBar menuBar;
    JMenu gameMode;
    JMenu actions;
    JMenuItem custom, demo, random, start, pause, save, load;

    public GameOfLife(int width, int height)
    {
        super();
        world = new Table(width, height);
        CreateMenu();

        this.setContentPane(world);
        this.setJMenuBar(menuBar);
        this.setPreferredSize(new Dimension(1200, 900));
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.pack();
        StartRandom();
    }

    private void CreateMenu()
    {
        menuBar = new JMenuBar();
        gameMode = new JMenu("Game Mode");
        actions = new JMenu("Actions");

        custom = new JMenuItem("Custom Game");
        custom.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                StartCustom();
            }
        });
        gameMode.add(custom);

        demo = new JMenuItem("Demo Game");
        demo.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                StartDemo();
            }
        });
        gameMode.add(demo);

        random = new JMenuItem("Random Game");
        random.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                StartRandom();
            }
        });
        gameMode.add(random);
        menuBar.add(gameMode);
    }

    private void Demo()
    {
        int[] x =
        {
            5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, 12,
            12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 17, 17, 17, 17, 17, 17
        };
        int[] y =
        {
            7, 8, 9, 13, 14, 15, 5, 10, 12, 17, 5, 10, 12, 17, 5, 10, 12, 17, 7, 8, 9, 13, 14, 15, 7, 8, 9,
            13, 14, 15, 5, 10, 12, 17, 5, 10, 12, 17, 5, 10, 12, 17, 7, 8, 9, 13, 14, 15
        };
        int i = 0;
        while (i < x.length)
        {
            world.SetStartPosition(x[i], y[i++]);
        }
    }

    private void StartCustom()
    {
        // TO-DO
    }

    private void StartDemo()
    {
        Demo();
        Game();
    }

    private void StartRandom()
    {
        world.RandomTable();
        Game();
    }

    private void Game()
    {
        while (world.CountAliveCells() > 0)
        {
            timer.start();
        }
    }

    public static void main(String[] args) {
        new GameOfLife(20,20);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        world.UpdateCellNeighbors();
        world.UpdateTable();
    }

}

When I press one of the menu items from the gameMode menu, the application freezes and I can't do anything else but just stop it from the Eclipse stop button. I also tried with addMouseListener but it works only for writing in console, it dose not run the intended method. I also must mention that the StartDemo and StartRandom methods work if called in the class constructor but they just freeze the application if called in the action listener method. Also, the application freezes even for the StartCustom method which does literally nothing.

EDIT: I changed the Thread.sleep function with a Swing Timer and the problem is still the same. The application still freezes when I try to select a game mode from the menu button but it works perfectly when the StartDemo or StartRandom methods are called from the class constructor.

Upvotes: 3

Views: 360

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347184

FYI: You've added to ActionListeners to custom which calls StartCustom and StartDemo, probably not what you intended

As to your actual problem...

the application freezes and I can't do anything else but just stop it from the Eclipse stop button

In Swing, this means you've blocked the Event Dispatching Thread in some way

If we take a closer look at your code...

private void Game()
{
    while (world.CountAliveCells() > 0)
    {
        world.UpdateCellNeighbors();
        world.UpdateTable();
        try {
            Thread.sleep(700);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

we can see Game is running a loop. Because Game is called from within the context of the ActionListener's actionPerformed method, it's guaranteed to be called within the context of the Event Dispatching Thread, which means that the EDT is no longer going to be running and can no longer process any new events in the Event Queue.

See Concurrency in Swing for more details.

There are number of ways you might be able to change this to work, the simplest would be to use a Swing Timer, see How to use Swing Timers for more details.

When choosing a solution to solve this problem - remember, Swing is NOT thread safe, this means that any updates to the UI MUST be made from within the context of the EDT. Swing Timer, while simple, triggers the registered ActionListeners actionPerformed method within the context of the EDT, making it a safe option

Upvotes: 1

Related Questions