0

I have some code that creates a window with some buttons that when clicked prompt user for input via the console (Note: I'm running this in NetBeans). For now I want to keep using the console, so I'm not really looking for a solution where I create a new frame asking for input via a GUI.

The issue is that while console is taking input from the user the buttons are all still enabled and thus queue up any presses and those get executed when the user finishes inputting stuff into the console. I want to disable the buttons while the console is taking input. I thought the .setEnabled(false) method for buttons would do it, and I think normally it would. It seems that Java prioritizes user input and so the buttons don't get disabled until the user finishes inputting to the console. Once the user finishes input the buttons are supposed to get re-enabled anyways, so they are effectively never disabled. The code in question is below:

        for(int i = 0; i < LABELS.length; i++) {
        menu_a.add(new JButton(LABELS[i]));
        menu_a.get(i).addActionListener((ActionEvent e) -> {
            menu_a.stream().forEach((b) -> {b.setEnabled(false);});
            menu_b.stream().forEach((b) -> {b.setEnabled(false);});
            menu_c.stream().forEach((b) -> {b.setEnabled(false);});
            if(!menu_a.get(menu_a.indexOf(e.getSource())).isEnabled()) {
                MenuActions.MenuActions(menu_a.indexOf(e.getSource()) + 1, data);
                menu_a.stream().forEach((b) -> {b.setEnabled(true);});
                menu_b.stream().forEach((b) -> {b.setEnabled(true);});
                menu_c.stream().forEach((b) -> {b.setEnabled(true);});
            }
        });
        itemPanel.add(menu_a.get(i));
    }

LABELS is just an array of strings which are the labels for the buttons.

MenuActions is a class that based on the index of the button prompts the user for different types of input via the console

data is an object that contains data that gets manipulated based on user input, not really relevant for the question I think.

DamianJ
  • 419
  • 2
  • 8
  • 18
  • 2
    It sounds more like you're blocking the Event Dispatching Thread, see [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/) for more details. A possible solution might be to use [a `SwingWorker`](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html) to read input from the user – MadProgrammer Nov 13 '15 at 00:45

1 Answers1

-1

So @MadProgrammer was right. I had to look-up and implement a SwingWorker to get the code to execute how I wanted it. It ended up looking like this:

EDIT: So I didn't know I was committing a crime against humanity by modifying GUI elements outside their original context. I changed my code and now it looks like this.

       for(int i = 0; i < LABELS.length; i++) {
        menu_a.add(new JButton(LABELS[i]));
        menu_a.get(i).addActionListener((ActionEvent e) -> {
            menu_a.stream().forEach((b) -> {b.setEnabled(false);});
            menu_b.stream().forEach((b) -> {b.setEnabled(false);});
            menu_c.stream().forEach((b) -> {b.setEnabled(false);});
            new SwingWorker<Void, Void>() {
                @Override
                public Void doInBackground() {
                    menuActions(menu_a.indexOf(e.getSource()) + 1);
                    return null;
                }

                @Override
                public void done() {
                    menu_a.stream().forEach((b) -> {b.setEnabled(true);});
                    menu_b.stream().forEach((b) -> {b.setEnabled(true);});
                    menu_c.stream().forEach((b) -> {b.setEnabled(true);});
                }
            }.execute();
        });
        panel_a.add(menu_a.get(i));
    }

I created an anonymous SwingWorker to take care of what I wanted. I ended up having to disable the buttons in the doInBackground() method else it wouldn't disable them, and I was stuck with my original problem. (See edit below) Also, menuActions now refers to a private method within the class, I figured since it's really only called from the GUI I might as well move it in.

EDIT: So I changed the disabling to happen outside of the SwingWorker and now it's working fine. I'll just blame it one me not saving my changes, perhaps I was recompiling old code.

I also found some more info on the whole SwingWorker and EventDispatchThread business that I feel is relevant and helped me understand things a little better.

Workflow

There are three threads involved in the life cycle of a SwingWorker :

Current thread: The execute() method is called on this thread. It schedules SwingWorker for the execution on a worker thread and returns immediately. One can wait for the SwingWorker to complete using the get methods.

Worker thread: The doInBackground() method is called on this thread. This is where all background activities should happen. To notify PropertyChangeListeners about bound properties changes use the firePropertyChange and getPropertyChangeSupport() methods. By default there are two bound properties available: state and progress.

Event Dispatch Thread: All Swing related activities occur on this thread. SwingWorker invokes the process and done() methods and notifies any PropertyChangeListeners on this thread.

https://docs.oracle.com/javase/8/docs/api/javax/swing/SwingWorker.html

Thanks again to @MadProgrammer for pointing me in the right direction, and promptly letting me know my original code was atrocious.

Also, I derived the code from this answer: How do I use SwingWorker in Java?

Community
  • 1
  • 1
DamianJ
  • 419
  • 2
  • 8
  • 18
  • No, no, no, oh for the love of sanity no. Don't not, ever create or modify ANY UI component from outside the context of the Event Dispatching Thread, your code is violating the single thread rules of Swing which could cause untold issues and weirdness. Disable your buttons in the `ActionListener`, create a `SwingWorker` and get the input from the user within the `doInBackground` this will prevent the UI from been blocked and becoming unresponsive. Make sure you also read and understand [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/) – MadProgrammer Nov 13 '15 at 04:09
  • I originally tried what you suggested, but even with input being in doInBackground() it was still being executed before the buttons were disabled (which was in the ActionListener before the SwingWorker was called). I changed my code to something that works for me and hopefully doesn't violate some other rule. Anyways, I'll read up on the info in that link you posted. – DamianJ Nov 13 '15 at 18:41
  • Anything in `doBackground` is out side of the EDT – MadProgrammer Nov 13 '15 at 19:34