Java Swing JButton disable not giving disired results

51 views Asked by At

This is my first time working with Java Swing, so apologies for missing a lot context. This is using Netbeans 7.0.x--business rational. Code is generated using the "Design" feature in Netbeans. Most of the gui code is generated, if not all.



I've tried a few areas of calling setEnabled to disable an upload button until the file transfer completes. For testing purposes, I added a doubly for-loop to do some floating point arithmetic; I have popup messages before the for loops, and after to mirror some "file upload busy work". None of the places I have called the setEnabled function on the button of interest, give the desired result of "greying out" or "disabling" the button--until the guiController.submitUpload() call completes inside the body of swingworker.doInBackground(). For now I commented out the done() functiion.



The desired result: on clicking the buttonX, grey out the button or disable it--that is, I shouldn't be able to click on it again--see the first popup in submitUpload(), then see the second pop up. The submitUpload() function runs it's business logic, and completes. After completing, the buttonX should be re-enabled or disable it. Ideally, I'd like to disable AND grey out this button, buttonX.


I've tried many of the results in SO, and already read up on documentation:
https://docs.oracle.com/javase/tutorial/uiswing/components/button.html https://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html



Can someone help me out with something I'm missing?

=========================
panelFile.java
//private variable
SwingWorker sw = ...;

buttonX.addActionListener(new java.awt.event.ActionListener(){
    public void actionPerformed(java.awt.event.ActionEvent evt) {

        //tried disabling here
        //buttonX.setEnabled(false);
        buttonXActionPerformed(evt);
        //renable
    }
}
.
.
.

private void bottonXActionPerformed(java.awt.event.ActionEvent evt) {

    //also tried disabling here 
    
    sw = new SwingWorker<void, void> () {
         @Override
         public void doInBackground
             {
            try
                {
                if (guiController != null)
                    {
                    guiController.submitUpload();
                    }
                }
            catch (Exception e)
                {
                logger.log(Level.SEVERE, e.toString(), e);
                }
            sw = null;
            return null;
            }
        /*
        @Override
        public void done()
            {
            buttonX.setEnabled(true);
            }         
         */
        };
    
    sw.execute();
}
=========================
guiController.java


public void submitUpload(){
    //do stuff
   
    //for testing I have pop-ups
    // I should only see this popup once, until the "upload completes"
    JOptionPane.showMessageDialog(getFrame(),
                                  "BEFORE",
                                  "test disable",
                                  JOptionPane.INFORMATION_MESSAGE);

    //busy loop here

    JOptionPane.showMessageDialog(getFrame(),
                                  "AFTER",
                                  "test disable",
                                  JOptionPane.INFORMATION_MESSAGE);


}
1

There are 1 answers

0
Hovercraft Full Of Eels On

So yes, call JButton#setEnabled(false) where you are calling it, before you execute the SwingWorker, and then re-enable it when the worker is done. This will work. If it doesn't work for you, then you have Swing threading problems in code not shown.

Here is my example of a proof-of-concept MRE:

import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.awt.BorderLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class FooPanel01 extends JPanel {
  private JButton longActionButton = new JButton("Start Long Action");
  private JTextArea output = new JTextArea(15, 40);
  
  public FooPanel01() {
    longActionButton.addActionListener(e -> longActionPerformed());
    JPanel buttoPanel = new JPanel();
    buttoPanel.add(longActionButton);

    JScrollPane scrollPane = new JScrollPane(output);
    scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

    setLayout(new BorderLayout());
    add(buttoPanel, BorderLayout.PAGE_START);
    add(scrollPane, BorderLayout.CENTER);
  }

  private void longActionPerformed() {
    longActionButton.setEnabled(false);
    output.append("Long Action started..." + System.lineSeparator());
    new MyWorker().execute();
  }

  private class MyWorker extends SwingWorker<Void, String> {
    @Override
    protected Void doInBackground() throws Exception {
      /****************************************
       * 
       * Do not update the GUI or make Swing calls from here!
       * This includes JOptionPanel static method calls
       * 
       ****************************************/

      // Do some long running task
      // here emulated with Thread.sleep
      for (int i = 0; i < 6; i++) {
        Thread.sleep((long) (Math.random() * 1000));
        publish("Processing step " + i);
      }
      return null;
    }

    @Override
    protected void process(List<String> chunks) {
      for (String chunk : chunks) {
        output.append(chunk + System.lineSeparator());
      }
    }

    @Override
    protected void done() {
      longActionButton.setEnabled(true);
      output.append("Long Action finished..." + System.lineSeparator());
      try {
        // always call get() in the done() method to trap exceptions
        // that may have occurred inside the doInBackground() method
        get();
      } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
      }
    }
  }
  
  public static void main(String[] args) throws IOException {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        JFrame frame = new JFrame("GUI");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new FooPanel01());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
      }
    });
  }
}

For more complex interactions between the background thread and the GUI, consider use of a SwingPropertyChangeSupport object and property change listeners. This will allow complex notifications to be sent from the the background thread to the GUI, and the notifications will be sent on the Swing event thread, allowing for thread safety.