How to return value from SwingWorker class and use

2019-01-20 19:07发布

问题:

I am using SwingWorker class to make a process run in another thread. What I want is, once this thread has finished processing, it should return a String and also it should enable a JMenuItem. I am using the done() method in the SwingWorker class to enable the JMenuItem but I receive a NullPinterException. The doInBackground() method returns a String which I want to access in the main GUI class - GUIMain.java, present in the same package. How should I do that? I saw many examples which implement done() or onPostExecute() methods, but I think I am going wrong somewhere. Here is the code which I have implemented:

 public class GUIMain extends JFrame implements ActionListener, FocusListener, ItemListener, MouseListener, MouseMotionListener {

        private JMenuBar menuBar;    // Defined a menuBar item
        private JMenu recalibrationMenu;  // Define the recalibration menu item
        private JMenuItem CGMenuItem;
        private JMenuItem TGMenuItem;
        private JMenu viewResultsMenu;  // Define the View Results menu item
        public JMenuItem cgResults;
        public JMenuItem tgResults;    
        private JPanel TGImage;
        private JPanel TGPanel;
        private JPanel CGImage;
        private JPanel CGPanel;
        private JPanel CGRecalibrationParameters;

        private JDialog resultsParameters;
        private JLabel massPeakLabel = new JLabel("Select mass peak (m/z)");
        private JTextField massPeakField = new JTextField(5);
        private JLabel massWindowLabel = new JLabel("Mass window (Da)");
        private JTextField massWindowField = new JTextField(5);
        private float mzPeakValue;
        private float massWindowValue;
        private JButton massPeakSelectionButton = new JButton("OK");
        private JButton CloseDialogButton = new JButton("Cancel");
        private String CGRecalibratedFilesPath;
        private String TGRecalibratedFilesPath;


        /** Constructor to setup the GUI */
        public GUIMain(String title) {

            super(title);
            setLayout(new BorderLayout());



            menuBar = new JMenuBar();
            menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));

            // build the Recalibration menu
            recalibrationMenu = new JMenu("Recalibration");
            CGMenuItem = new JMenuItem("Crystal Growth");
            CGMenuItem.setEnabled(false); //initially disabled when app is launched
            CGMenuItem.addActionListener(this);

            TGMenuItem = new JMenuItem("Topological Greedy");
            TGMenuItem.setEnabled(false); //initially disabled when app is launched
            TGMenuItem.addActionListener(this);

            recalibrationMenu.add(CGMenuItem);
            recalibrationMenu.add(TGMenuItem);

            // build the View Results menu
            viewResultsMenu = new JMenu("View Results");
            cgResults = new JMenuItem("CG Recalibration");
            cgResults.setEnabled(false); //initially disabled when app is launched
            cgResults.addActionListener(this);
            tgResults = new JMenuItem("TG Recalibration");
            tgResults.setEnabled(false); //initially disabled when app is launched
            tgResults.addActionListener(this);

            viewResultsMenu.add(cgResults);
            viewResultsMenu.add(tgResults);

            // add menus to menubar
            menuBar.add(fileMenu);
            menuBar.add(recalibrationMenu);
            menuBar.add(viewResultsMenu);


            // put the menubar on the frame
            setJMenuBar(menuBar);      

            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Exit program if close-window button clicked
            setPreferredSize(new Dimension(1300, 800)); // Set the preferred window size
            pack();
            setLocationRelativeTo(null);
            setVisible(true);
        }

        public GUIMain()
        {

        } 
    }


        public void actionPerformed(ActionEvent e) {

            //Handle CG menu item action
            **if ((e.getSource() == CGMenuItem)) {
                RecalibrationWorker rw = new RecalibrationWorker(file,"CG");
                rw.processCompleted();
                rw.setVisible(true);
                cgResults.setEnabled(true);
            }**


            if ((e.getSource() == cgResults)) {
                viewRecalibrationResults("CG Recalibration - View Results", "CG");
            }

            if ((e.getSource() == tgResults)) {
                viewRecalibrationResults("TG Recalibration - View Results", "TG");
            }

        }


        private void viewRecalibrationResults(String dialogTitle, final String recalibrationType) {
            resultsParameters = new JDialog();
            resultsParameters.setLayout(new GridBagLayout());
            resultsParameters.setTitle(dialogTitle);
            GridBagConstraints gc = new GridBagConstraints();

            gc.gridx = 0;
            gc.gridy = 0;
            gc.anchor = GridBagConstraints.WEST;

            gc.ipady = 20;
            // gc.anchor = GridBagConstraints.WEST;
            resultsParameters.add(massPeakLabel, gc);
            commonMassThresholdLabel.setLabelFor(massPeakField);

            gc.gridx = 1;
            gc.gridy = 0;
            gc.ipady = 0;
            gc.anchor = GridBagConstraints.CENTER;
            resultsParameters.add(massPeakField,gc);
            massPeakField.setText("");
            massPeakField.addActionListener(this);
            massPeakField.addFocusListener(this);

            gc.gridx = 0;
            gc.gridy = 1;
            gc.ipady = 20;
            gc.anchor = GridBagConstraints.WEST;
            resultsParameters.add(massWindowLabel, gc);
            massWindowLabel.setLabelFor(massWindowField);

            gc.gridx = 1;
            gc.gridy = 1;
            gc.ipady = 0;
            gc.anchor = GridBagConstraints.CENTER;
            resultsParameters.add(massWindowField,gc);
            massWindowField.setText("");
            massWindowField.addActionListener(this);
            massWindowField.addFocusListener(this);


            gc.gridx = 0;
            gc.gridy = 2;
            gc.anchor = GridBagConstraints.CENTER;
            resultsParameters.add(massPeakSelectionButton, gc);

            massPeakSelectionButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if ("CG".equals(recalibrationType))
                        recalibrationResults("CG");

                    else if ("TG".equals(recalibrationType))
                        recalibrationResults("TG");
                }
            });

            massPeakSelectionButton.addKeyListener(new KeyListener() {
                @Override
                public void keyTyped(KeyEvent e) {

                }

                @Override
                public void keyPressed(KeyEvent e) {

                    if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                        if ("CG".equals(recalibrationType))
                            recalibrationResults("CG");

                        else if ("TG".equals(recalibrationType))
                            recalibrationResults("TG");
                    }
                }

                @Override
                public void keyReleased(KeyEvent e) {

                }
            });

            gc.gridx = 1;
            gc.gridy = 2;
            gc.anchor = GridBagConstraints.EAST;
            resultsParameters.add(CloseDialogButton,gc);
            CloseDialogButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    resultsParameters.dispose();
                }
            });

            resultsParameters.setVisible(true);
            resultsParameters.setLocationRelativeTo(this);
            resultsParameters.setSize(270, 150);
            resultsParameters.setResizable(false);
        }
    }


public class RecalibrationWorker extends JDialog {

    private boolean isStarted = false;
    private JLabel counterLabel = new JLabel("Recalibration not yet started");
    private Worker worker = new Worker();
    private JPanel recalibrationParameters;
    private ButtonGroup radioButtonGroup = new ButtonGroup();
    private JRadioButton rawSpectra = new JRadioButton("Use raw spectra");
    private JRadioButton preprocessedSpectra = new JRadioButton("Use preprocessed spectra");
    private static float commonMassThresholdValue;
    private static float recalibrationThresholdValue;
    private static double mergeSpectraThresholdValue;
    private JLabel commonMassThresholdLabel = new JLabel("<html>Common Mass Window<br>(value between 0.01-0.9 Da)</html>");
    private JTextField commonMassThresholdField = new JTextField(String.valueOf(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            commonMassThresholdValue = Float.parseFloat(commonMassThresholdField.getText());
            recalibrationThresholdField.requestFocusInWindow();

        }
    }));
    private JLabel recalibrationThresholdLabel = new JLabel("<html>Mass threshold to recalibrate two spectra<br>(value between 0.01-0.9 Da)</html>");
    private JTextField recalibrationThresholdField = new JTextField(String.valueOf(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            recalibrationThresholdValue = Float.parseFloat(recalibrationThresholdField.getText());
            mergeSpectraThresholdField.requestFocusInWindow();

        }
    }));
    private JLabel mergeSpectraThresholdLabel = new JLabel("<html>Mass threshold to merge spectra<br>(value between 0.01-0.9 Da)</html>");
    private JTextField mergeSpectraThresholdField= new JTextField(String.valueOf(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            mergeSpectraThresholdValue = Float.parseFloat(mergeSpectraThresholdField.getText());
            startButton.requestFocusInWindow();

        }
    }));
    private JTextArea recalibrationStatus = new JTextArea(7,32);
    private JScrollPane textAreaScrolling = new JScrollPane(recalibrationStatus);


    private JButton startButton = new JButton(new AbstractAction("Recalibrate") {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            if(!isStarted) {
                Worker w = new Worker();
                w.addPropertyChangeListener(new RecalibrationWorkerPropertyHandler(RecalibrationWorker.this));
                w.execute();
                isStarted = false;
            }
        }

    });
    private JButton stopButton = new JButton(new AbstractAction("Stop") {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            worker.cancel(true);
        }

    });

    public File file;
    public String type;
    public String resultFilePath;


    public RecalibrationWorker(File dataFile, String recalibrationType) {

        file = dataFile;
        type = recalibrationType;

        setLayout(new GridBagLayout());
        if("CG".equals(recalibrationType))
            setTitle("Crystal Growth Recalibration");
        else if("TG".equals(recalibrationType))
            setTitle("Topological Greedy Recalibration");

        recalibrationParameters = new JPanel(new GridBagLayout());
        GridBagConstraints gc = new GridBagConstraints();

        radioButtonGroup.add(rawSpectra);
        radioButtonGroup.add(preprocessedSpectra);

        gc.gridx = 0;
        gc.gridy = 0;
        gc.anchor = GridBagConstraints.WEST;
        recalibrationParameters.add(preprocessedSpectra,gc);

        gc.gridx = 1;
        gc.gridy = 0;
        recalibrationParameters.add(rawSpectra,gc);

        gc.gridx = 0;
        gc.gridy = 1;
        gc.ipady = 20;
        gc.anchor = GridBagConstraints.WEST;
        recalibrationParameters.add(commonMassThresholdLabel, gc);
        commonMassThresholdLabel.setLabelFor(commonMassThresholdField);
        commonMassThresholdField.setColumns(3);
        commonMassThresholdField.setText("");

        gc.gridx = 1;
        gc.gridy = 1;
        gc.ipady = 0;
        gc.anchor = GridBagConstraints.CENTER;
        recalibrationParameters.add(commonMassThresholdField, gc);
        commonMassThresholdField.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {

            }

            @Override
            public void focusLost(FocusEvent e) {
                commonMassThresholdValue = Float.parseFloat(commonMassThresholdField.getText());
                recalibrationThresholdField.requestFocusInWindow();
            }
        });

        gc.gridx = 0;
        gc.gridy = 2;
        gc.ipady = 20;
        gc.anchor = GridBagConstraints.WEST;
        recalibrationParameters.add(recalibrationThresholdLabel, gc);
        recalibrationThresholdLabel.setLabelFor(recalibrationThresholdField);
        recalibrationThresholdField.setColumns(3);
        recalibrationThresholdField.setText("");

        gc.gridx = 1;
        gc.gridy = 2;
        gc.ipady = 0;
        gc.anchor = GridBagConstraints.CENTER;
        recalibrationParameters.add(recalibrationThresholdField, gc);
        recalibrationThresholdField.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {

            }

            @Override
            public void focusLost(FocusEvent e) {
                recalibrationThresholdValue = Float.parseFloat(recalibrationThresholdField.getText());
                mergeSpectraThresholdField.requestFocusInWindow();
            }
        });

        gc.gridx = 0;
        gc.gridy = 3;
        gc.ipady = 20;
        gc.anchor = GridBagConstraints.WEST;
        recalibrationParameters.add(mergeSpectraThresholdLabel, gc);
        mergeSpectraThresholdLabel.setLabelFor(mergeSpectraThresholdField);
        mergeSpectraThresholdField.setText("");

        gc.gridx = 1;
        gc.gridy = 3;
        gc.ipady = 0;
        gc.anchor = GridBagConstraints.CENTER;
        recalibrationParameters.add(mergeSpectraThresholdField, gc);
        mergeSpectraThresholdField.setColumns(3);
        mergeSpectraThresholdField.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {

            }

            @Override
            public void focusLost(FocusEvent e) {
                mergeSpectraThresholdValue = Double.parseDouble(mergeSpectraThresholdField.getText());
                startButton.requestFocusInWindow();
            }
        });

        recalibrationParameters.setBorder(
                BorderFactory.createCompoundBorder(
                        BorderFactory.createTitledBorder("Parameters"),
                        BorderFactory.createEmptyBorder(5, 5, 5, 5)));

        gc.gridx = 0;
        gc.gridy = 4;
        gc.anchor = GridBagConstraints.EAST;
        recalibrationParameters.add(startButton, gc);

        gc.gridx = 1;
        gc.gridy = 4;
        gc.anchor = GridBagConstraints.WEST;
        recalibrationParameters.add(stopButton, gc);

        gc.gridx = 0;
        gc.gridy = 0;
        gc.anchor = GridBagConstraints.CENTER;
        add(recalibrationParameters, gc);

        textAreaScrolling.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createTitledBorder("Recalibration status"),
                BorderFactory.createEmptyBorder(5, 5, 5, 5)));

        gc.gridx = 0;
        gc.gridy = 1;
        gc.anchor = GridBagConstraints.CENTER;
        add(textAreaScrolling, gc);

        recalibrationStatus.setWrapStyleWord(true);
        recalibrationStatus.setLineWrap(true);
        recalibrationStatus.setEditable(false);

        setVisible(true);
        setLocationRelativeTo(this);
        setSize(415, 425);
        setResizable(false);
        pack();
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);

    }

    // constructor
    public RecalibrationWorker()
    {

    }

    public float commonMassThreshold() {
        return commonMassThresholdValue;
    }

    public float recalibrationThreshold() {
        return recalibrationThresholdValue;
    }

    public double mergeSpectraThreshold() {
        return mergeSpectraThresholdValue;
    }

    protected String processCompleted() {
        return resultFilePath;
    }

    protected void processFailed() {
        System.out.println("Recalibration failed");
    }



    class Worker extends SwingWorker<String,String> {


      //  String  resultfilePath;

        @Override
        public String doInBackground() throws Exception {

            InputData inputDataObject1 = new InputData();
            if(type == "CG")
            {
                resultFilePath = inputDataObject1.startRecalibration(file, type);
                publish("Recalibration Successful!\nFiles can be found at: " + resultFilePath);
            }
            else if (type == "TG")
            {
                resultFilePath = inputDataObject1.startRecalibration(file, type);
                publish("Recalibration Successful!\nFiles can be found at: " + resultFilePath);
            }
            return resultFilePath;
        }

       public void done() {
           try {
               get();
           } catch (Exception e) {
           }
       }

        @Override
        protected void process(java.util.List<String> chunks) {
            String value = chunks.get(chunks.size() - 1);
            recalibrationStatus.append("\n"+value);
            // recalibrationStatus.append(chunks.toString());
        }

    }

import javax.swing.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;

/**
 * Created by localadm on 27/09/15.
 */
public class RecalibrationWorkerPropertyHandler implements PropertyChangeListener {

    private RecalibrationWorker rwObject;

    public RecalibrationWorkerPropertyHandler(RecalibrationWorker rwObject) {
        this.rwObject = rwObject;
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
      //  System.out.println(evt.getPropertyName());
        if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
            //  int value = (int) evt.getNewValue();
            // rwObject.showProgress(value);
        } else if ("state".equalsIgnoreCase(evt.getPropertyName())) {
            SwingWorker worker = (SwingWorker) evt.getSource();
            if (worker.isDone()) {
                try {
                    worker.get();
                    System.out.println("I am here");
                    rwObject.processCompleted();
                } catch (InterruptedException exp) {
                    rwObject.processFailed();
                } catch (ExecutionException e) {
                    rwObject.processFailed();
                }
            }
        }
    }

}

回答1:

Since you're using a JDialog to manage the SwingWorker, you can actual use the modal state of the dialog to you advantage.

Essentially, when a dialog is modal, it will block the code execution where the dialog is made visible. It will block until the dialog is hidden, at which time, the code will continue to execute.

So, you can disable the button before the dialog is made visible and re-enable it when it's closed.

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new GridBagLayout());
            JButton btn = new JButton("Go");
            add(btn);

            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    btn.setEnabled(false);
                    SomeWorkerDialog worker = new SomeWorkerDialog(TestPane.this);
                    // This is just so we can see the button ;)
                    Point point = btn.getLocationOnScreen();
                    worker.setLocation(worker.getX(), point.y + btn.getHeight());
                    worker.setVisible(true);
                    btn.setEnabled(true);
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

    }

    public class SomeWorkerDialog extends JDialog {

        private JProgressBar progressBar;
        private SomeWorkerSomeWhere worker;


        public SomeWorkerDialog(JComponent parent) {
            super(SwingUtilities.getWindowAncestor(parent), "Working Hard", DEFAULT_MODALITY_TYPE);

            progressBar = new JProgressBar();
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.HORIZONTAL;
            gbc.weightx = 1;
            gbc.insets = new Insets(10, 10, 10, 10);

            add(progressBar, gbc);

            worker = new SomeWorkerSomeWhere();
            worker.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    String name = evt.getPropertyName();
                    switch (name) {
                        case "state":
                            switch (worker.getState()) {
                                case DONE:
                                    setVisible(false);
                                    break;
                            }
                            break;
                        case "progress":
                            progressBar.setValue(worker.getProgress());
                            break;
                    }
                }
            });

            addWindowListener(new WindowAdapter() {

                @Override
                public void windowOpened(WindowEvent e) {
                    worker.execute();
                }

                @Override
                public void windowClosed(WindowEvent e) {
                    worker.cancel(true);
                }

            });

            pack();
            setLocationRelativeTo(parent);

        }

    }

    public class SomeWorkerSomeWhere extends SwingWorker {

        @Override
        protected Object doInBackground() throws Exception {
            for (int index = 0; index < 100 && !isCancelled(); index++) {
                setProgress(index);
                Thread.sleep(10);
            }
            return "AllDone";
        }

    }

}

Update

To get the value returned by the worked, you can add a method to the dialog which returns the calculated value...

public String getValue() throws Exception {
    return worker.get()
}

So, once the dialog is closed and the execution of your code continues, you can enable the components you want and call the getValue method

You can also store the result when the state changes and the PropertyChangeListener is notified