JPanel with a specific JRadioButton does not updat

2019-07-24 20:28发布

问题:

I am making a Java GUI application. For reading two types of files required as an input, I am using the SwingWorkerclass which performs the operation in two different background threads. Once, the two types of files are read, there are a lot of components which get updated in the GUI with data and a HeatMap in one of the JPanel. This JPanel contains two JRadioButton, which on switching should display the two different heat maps obtained from the two files read by the application. I can see the heatmap of the first file when the user loads the file. But when I load the second file, heatmap for the second file in not represented (is empty) when the second radio button is active. Same happens, for the JTabbedPane which display the sub files from the two files. I use

imagePanel.removeAll();
imagePanel.add(preprocessedIntensityMap, BorderLayout.CENTER); 
panel.repaint();
panel.revalidate();

while in the second background thread to update the JPanel, but this does nothing. I also tried getter and setter method for the JPanel but this did not change things. Any suggestions?

Here is the compete code of my second SwingWorker class:

public class PeaklistReadWorker extends SwingWorker<REXP, Object> {

    private RConnection rc1;
    private GUIMain guiClassObject;
    private File fileName;
    private JTree tree;
    private String currentPath;
    private REXP preprocessedSpectrumObjects;
    private float minMzValue;
    private float maxMzValue;
    volatile HeatMap preprocessedIntensityMap;
    private JLabel coordinates;
    private JScrollPane spectralFilesScrollPane;
    private final JPanel peakPickedFilesPanel;
    private JTabbedPane tabbedSpectralFiles;
    private final JLabel statusLabel;
    private JMenuItem CGMenuItem;
    private JMenuItem TGMenuItem;
    private JPanel imagePanel;
    private GenerateIntensityMap gim = new GenerateIntensityMap();
    private SortFile sf = new SortFile();
    private JDialog progressDialog;

    public PeaklistReadWorker(GUIMain guiClassObject, File fileName, JTree tree, String currentPath, REXP
            preprocessedSpectrumObjects, float minMzValue, float maxMzValue, HeatMap preprocessedIntensityMap, JLabel coordinates,
                              JScrollPane spectralFilesScrollPane, JPanel peakPickedFilesPanel, JTabbedPane tabbedSpectralFiles,
                              JLabel statusLabel, JMenuItem CGMenuItem, JMenuItem TGMenuItem, JPanel imagePanel, JDialog progressDialog)
    {
        this.guiClassObject = guiClassObject;
        this.fileName = fileName;
        this.tree = tree;
        this.currentPath = currentPath;
        this.preprocessedSpectrumObjects = preprocessedSpectrumObjects;
        this.minMzValue = minMzValue;
        this.maxMzValue = maxMzValue;
        this.preprocessedIntensityMap = preprocessedIntensityMap;
        this.coordinates = coordinates;
        this.spectralFilesScrollPane = spectralFilesScrollPane;
        this.peakPickedFilesPanel = peakPickedFilesPanel;
        this.tabbedSpectralFiles = tabbedSpectralFiles;
        this.statusLabel = statusLabel;
        this.CGMenuItem = CGMenuItem;
        this.TGMenuItem = TGMenuItem;
        this.imagePanel = imagePanel;
        this.progressDialog = progressDialog;
    }

    @Override
    protected REXP doInBackground() throws Exception {

        try {
            rc1 = new RConnection();
            rc1.assign("importPreprocessedSpectra", currentPath.concat("/importPreprocessedSpectra.R"));
            rc1.eval("source(importPreprocessedSpectra)");
            rc1.assign("inputFileDirectory", fileName.toString());
            preprocessedSpectrumObjects = rc1.eval("importPreprocessedSpectra(inputFileDirectory)");

            preprocessedIntensityMap = gim.generateIntensityMap(preprocessedSpectrumObjects, currentPath, minMzValue, maxMzValue, Gradient.GRADIENT_Rainbow, "PROCESSED");           
        } catch (RserveException e1) {
            e1.printStackTrace();
        } catch (REXPMismatchException e1) {
            e1.printStackTrace();
        }   
        return preprocessedSpectrumObjects;
    }

    /**
     * Process method to take care of intermediate results - works in EDT
     * @param chunks
     */
    @Override
    protected void process(List<Object> chunks) {
    }

    @Override
    public void done() {

        try {
            REXP preprocessedSpectrumObjects = get();
            guiClassObject.returnFromBackgroundPeaklistObjects(preprocessedSpectrumObjects);

            // list only files with .txt extension from the directory selected
            File[] filesInDirectory = fileName.listFiles(new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    return name.toLowerCase().endsWith(".txt");
                }
            });
            guiClassObject.setPreprocessedIntensityMap(preprocessedIntensityMap);

            // Calls sortByNumber method in class SortFile to list the files number wise
            filesInDirectory = sf.sortByNumber(filesInDirectory);
            tree = new JTree(guiClassObject.addNodes(null, filesInDirectory, fileName));
            guiClassObject.setTree(tree);
            DefaultMutableTreeNode firstLeaf = ((DefaultMutableTreeNode) tree.getModel().getRoot()).getFirstLeaf();
            tree.setSelectionPath(new TreePath(firstLeaf.getPath()));
            guiClassObject.updateSpectralTableandChartPICKED(firstLeaf);

            // Add a tree selection listener
            tree.addTreeSelectionListener(new TreeSelectionListener() {

                public void valueChanged(TreeSelectionEvent e) {
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath().getLastPathComponent();
                    guiClassObject.updateSpectralTableandChartPICKED(node);
                }
            });

            imagePanel.removeAll();
            imagePanel.add(preprocessedIntensityMap, BorderLayout.CENTER);
            guiClassObject.panelRefresh(imagePanel);
            coordinates.setBounds(31, 31, preprocessedIntensityMap.getWidth() - 31, preprocessedIntensityMap.getHeight() - 31);

            preprocessedIntensityMap.addMouseListener(guiClassObject);
            preprocessedIntensityMap.addMouseMotionListener(guiClassObject);
            spectralFilesScrollPane.setViewportView(tree);
            spectralFilesScrollPane.setPreferredSize(peakPickedFilesPanel.getSize());
            peakPickedFilesPanel.add(spectralFilesScrollPane);
            tabbedSpectralFiles.validate();
            tabbedSpectralFiles.repaint();

            CGMenuItem.setEnabled(true); // active now
            TGMenuItem.setEnabled(true); // active now
            progressDialog.dispose();//close the modal dialog
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

Minimal Example

This minimal example does not even display the imagePanel at all, where the JLabel (HeatMap in the original code) should be displayed. But, the overall layout of this example is similar to my main script. The JLabel is generated in the SwingWorker class itself and then is added to the imagePanel.

    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;

    public class GuiClass {

        public static void main(String[] args) {
            new GuiClass();
        }
        public GuiClass() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (UnsupportedLookAndFeelException e) {
                        e.printStackTrace();
                    } catch (ClassNotFoundException e) {
                        e.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() {

                final String raw = "Raw";
                final String preprocessed = "Preprocessed";

                final JRadioButton rawImage = new JRadioButton(raw, true);
                JRadioButton peakPickedImage = new JRadioButton(preprocessed);
                rawImage.setActionCommand(raw);
                peakPickedImage.setActionCommand(preprocessed);

                ButtonGroup radioButtonGroup = new ButtonGroup();
                radioButtonGroup.add(rawImage);
                radioButtonGroup.add(peakPickedImage);

                JPanel buttons = new JPanel(new
                        FlowLayout(FlowLayout.CENTER, 5, 5));
                buttons.add(rawImage);
                buttons.add(peakPickedImage);

                add(buttons, BorderLayout.SOUTH);

                final CardLayout cl = new CardLayout();
                final JPanel imagePanel = new JPanel(cl);
                imagePanel.setSize(100,100);
                add(imagePanel);
                imagePanel.setVisible(true);

                ActionListener al = new ActionListener() {
                    public void actionPerformed(ActionEvent ae) {
                        if (rawImage.isSelected() && rawImage.isEnabled()) {
                            ImageCreaterRaw icr = new ImageCreaterRaw(imagePanel, raw, cl);
                            icr.execute();

                        } else {
                            ImageCreaterPreprocessed icp = new ImageCreaterPreprocessed(imagePanel, preprocessed, cl);
                            icp.execute();
                        }
                    }
                };
                rawImage.addActionListener(al);
                peakPickedImage.addActionListener(al);
            }
        }
    }

    class ImageCreaterRaw extends SwingWorker<Void, Void> {

    private JPanel imagePanel;
   // private HeatMap rawHeatMap;
    private JLabel labelR;
    private String raw;
    private CardLayout cl;

    public ImageCreaterRaw(JPanel imagePanel, String raw, CardLayout cl)
    {
        this.imagePanel = imagePanel;
        this.raw = raw;
        this.cl = cl;

    }

    @Override
    protected Void doInBackground() throws Exception {
        double[][] data = HeatMap.generateSinCosData(200);
        boolean useGraphicsYAxis = true;
      //  System.out.println("I am herr");
      //  rawHeatMap = new HeatMap(data, useGraphicsYAxis, Gradient.GRADIENT_BlackToWhite);
        labelR = new JLabel("Label for Raw");
        try { Thread.currentThread().sleep(3000); }
        catch(InterruptedException e) {}
        return null;
    }

    public void done()
    {
       // imagePanel.add(rawHeatMap, raw);
        imagePanel.add(labelR, raw);
        cl.show(imagePanel, raw);
    }
}

// First SwingWorker Class

class ImageCreaterPreprocessed extends SwingWorker<Void, Void> {

    private JPanel imagePanel;
    private JLabel labelP;
    private String preprocessed;
    private CardLayout cl;

    public ImageCreaterPreprocessed(JPanel imagePanel, String preprocessed, CardLayout cl)
    {
        this.imagePanel = imagePanel;
        this.preprocessed = preprocessed;
        this.cl = cl;
    }

    @Override
    protected Void doInBackground() throws Exception {
        double[][] data = HeatMap.generateSinCosData(200);
       // boolean useGraphicsYAxis = true;
       // preprocessedHeatMap = new HeatMap(data, useGraphicsYAxis, Gradient.GRADIENT_BlueToRed);
        labelP = new JLabel("Label for Preprocessed");
        try { Thread.currentThread().sleep(3000); }
        catch(InterruptedException e) {}
        return null;
    }

    public void done()
    {
        //imagePanel.add(preprocessedHeatMap, preprocessed);
        imagePanel.add(labelP, preprocessed);
        cl.show(imagePanel, preprocessed);
    }
}

回答1:

Your label or JPanel is likely showing up, but you're not seeing it because the GUI does not re-size to accommodate it. I suggest that you place a dummy heat map, a blank one into the CardLayout at start up, one that is the appropriate size for the added maps, and then let the CardLayout swap them.

Note if all you're doing is swapping images, then I wouldn't even use a CardLayout but rather a single JLabel, and then simply swap ImageIcons that it displays via setIcon(...).

Other issues:

  • Don't set sizes of things but rather best if you can have components size themselves.
  • No need to call setVisible(true) on the imagePanel JPanel since JPanels are by default visible.
  • Better if you rig your SwingWorker to return the object of interest rather than null or Void. This way you can call get() on the worker when its done, and thereby catch exceptions that you otherwise could be missing.
  • Myself, I much prefer to avoid overriding done() in my SwingWorker and instead rig my SwingWorker with a PropertyChangeWorker listening for SwingWorker.StateValue.DONE. This allows me to avoid having GUI-code within my SwingWorker, making it more re-usable.

e.g.,

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import java.util.concurrent.ExecutionException;

import javax.swing.*;

@SuppressWarnings("serial")
public class GuiLongRunning extends JPanel {
    public static final int IMG_W = 800;
    public static final int IMG_H = 500;
    BufferedImage blankImage = new BufferedImage(IMG_W, IMG_H, BufferedImage.TYPE_INT_ARGB);
    private Icon blankIcon = new ImageIcon(blankImage);
    private JLabel imageLabel = new JLabel(blankIcon);
    private ButtonGroup buttonGroup = new ButtonGroup();
    private JDialog runningDialog = null;

    public GuiLongRunning() {
        JPanel topPanel = new JPanel();
        for (ImageType imageType : ImageType.values()) {
            JRadioButton radioButton = new JRadioButton(imageType.getName());
            radioButton.addItemListener(new RadioItemListener(imageType));
            topPanel.add(radioButton);
            buttonGroup.add(radioButton);
            int mnemonic = imageType.getName().charAt(0);
            radioButton.setMnemonic(mnemonic);
        }

        setLayout(new BorderLayout());
        add(topPanel, BorderLayout.PAGE_START);
        add(imageLabel, BorderLayout.CENTER);
    }

    public void workerRunning(boolean running) {
        if (!running) {
            if (runningDialog != null) {
                runningDialog.dispose();
            }
        } else {
            if (runningDialog == null) {
                JProgressBar progressBar = new JProgressBar();
                progressBar.setPreferredSize(new Dimension(300, 30));
                progressBar.setIndeterminate(true);
                Window thisWindow = SwingUtilities.getWindowAncestor(GuiLongRunning.this);
                runningDialog = new JDialog(thisWindow, "Creating Image", ModalityType.APPLICATION_MODAL);
                runningDialog.add(progressBar);
                runningDialog.pack();
                runningDialog.setLocationRelativeTo(thisWindow);
            }
            runningDialog.setVisible(true);
        }
    }

    private class RadioItemListener implements ItemListener {
        private ImageType imageType;

        public RadioItemListener(ImageType imageType) {
            this.imageType = imageType;
        }

        @Override
        public void itemStateChanged(ItemEvent e) {
            if (e.getStateChange() == ItemEvent.SELECTED) {
                int w = GuiLongRunning.IMG_W;
                int h = GuiLongRunning.IMG_H;
                ProcessImageWorker worker = new ProcessImageWorker(imageType, w, h);
                worker.addPropertyChangeListener(new WorkerListener());
                worker.execute();
                workerRunning(true);
            }
        }
    }

    private class WorkerListener implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
                ProcessImageWorker worker = (ProcessImageWorker) evt.getSource();
                try {
                    Icon icon = worker.get();
                    imageLabel.setIcon(icon);
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
                // delay it a little so won't call this before the dialog is
                // visible
                SwingUtilities.invokeLater(() -> workerRunning(false));
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui() {
        GuiLongRunning mainPanel = new GuiLongRunning();
        JFrame frame = new JFrame("GuiLongRunning");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }
}

class ProcessImageWorker extends SwingWorker<Icon, Void> {
    private static final float DELTA_H = 0.01f;
    private static final long SLEEP_TIME = 1500;
    private ImageType imageType;
    private int width;
    private int height;
    private Random random = new Random();

    public ProcessImageWorker(ImageType imageType, int width, int height) {
        this.imageType = imageType;
        this.width = width;
        this.height = height;
    }

    @Override
    protected Icon doInBackground() throws Exception {
        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Raster raster = img.getData();
        DataBufferInt dataBuffer = (DataBufferInt) raster.getDataBuffer();
        int[][] data = dataBuffer.getBankData();

        if (imageType == ImageType.RAW) {
            rawProcess(data);
        } else if (imageType == ImageType.PREPROCESSED) {
            colorProcess(data);
        }
        img.setData(raster);
        Thread.sleep(SLEEP_TIME); // !! 

        return new ImageIcon(img);
    }

    private int[][] rawProcess(int[][] data) {
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[i].length; j++) {
                int randInt = (int) (256 * Math.random());
                data[i][j] = new Color(randInt, randInt, randInt).getRGB();
            }
        }
        return data;
    }

    // some random colors
    private int[][] colorProcess(int[][] data) {
        float h = 0f;
        for (int i = 0; i < data.length; i++) {
            float h2 = h;
            for (int j = 0; j < data[i].length; j++) {
                h2 += DELTA_H * random.nextFloat();
                h2 += 1f;
                h2 %= 1f;

                Color c = Color.getHSBColor(h2, 1f, 1f);
                int randInt = c.getRGB();
                data[i][j] = randInt;
            }
        }
        return data;
    }
}

enum ImageType {
    RAW("Raw"), PREPROCESSED("Preprocessed");
    private String name;

    private ImageType(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return name;
    }
}

Note that the SwingWorker is completely GUI agnostic.