loci.visbio.data.ExportPane.java Source code

Java tutorial

Introduction

Here is the source code for loci.visbio.data.ExportPane.java

Source

/*
 * #%L
 * VisBio application for visualization of multidimensional biological
 * image data.
 * %%
 * Copyright (C) 2002 - 2014 Board of Regents of the University of
 * Wisconsin-Madison.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-2.0.html>.
 * #L%
 */

package loci.visbio.data;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

import java.awt.BorderLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.StringTokenizer;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

import loci.formats.FormatException;
import loci.formats.FormatTools;
import loci.formats.gui.AWTImageTools;
import loci.formats.gui.BufferedImageWriter;
import loci.visbio.BioTask;
import loci.visbio.TaskManager;
import loci.visbio.VisBioFrame;
import loci.visbio.util.BioComboBox;
import loci.visbio.util.SwingUtil;
import loci.visbio.util.WizardPane;
import visad.FlatField;
import visad.ImageFlatField;
import visad.util.DataUtility;

/**
 * ExportPane provides a full-featured set of options for exporting a
 * multidimensional data series from VisBio to several different formats.
 */
public class ExportPane extends WizardPane {

    // -- GUI components, page 1 --

    /** Folder on disk text field. */
    private final JTextField folderField;

    /** Button for choosing output folder. */
    private final JButton chooseButton;

    /** File pattern text field. */
    private final JTextField patternField;

    /** File format combo box. */
    private final BioComboBox formatBox;

    // -- GUI components, page 2 --

    /** Panel for dynamic second page components. */
    private final JPanel second;

    /** Check box indicating whether file numbering should use leading zeroes. */
    private final JCheckBox leadingZeroes;

    /** Frames per second for movie formats. */
    private final JTextField fps;

    /** Checkbox for LZW compression (TIFF only). */
    private final JCheckBox lzw;

    /** Combo boxes for mapping each star to corresponding dimensional axes. */
    private BioComboBox[] letterBoxes;

    // -- GUI components, page 3 --

    /** Panel for dynamic third page components. */
    private final JPanel third;

    /** Pane containing export summary. */
    private final JEditorPane summary;

    // -- Other fields --

    /** Associated VisBio frame (for displaying export status). */
    private final VisBioFrame bio;

    /** Writer for saving images to disk. */
    private final BufferedImageWriter saver;

    /** Data object from which exportable data will be derived. */
    private ImageTransform trans;

    /** File pattern, divided into tokens. */
    private String[] tokens;

    /** Number of "stars" in the file pattern. */
    private int stars;

    /** Mapping from each star to corresponding dimensional axis. */
    private int[] maps;

    /** Excluded index (not mapped to a star). */
    private int excl;

    /** Number of total files to export. */
    private int numFiles;

    // -- Constructor --

    /** Creates a multidimensional data export dialog. */
    public ExportPane(final VisBioFrame bio) {
        super("Export data");
        this.bio = bio;
        saver = new BufferedImageWriter();

        // -- Page 1 --

        // folder widgets
        folderField = new JTextField(System.getProperty("user.dir"));
        chooseButton = new JButton("Choose...");
        chooseButton.setMnemonic('h');
        chooseButton.setActionCommand("folder");
        chooseButton.addActionListener(this);

        // pattern fields
        patternField = new JTextField();

        // format combo box
        final String[] formats = new String[] { "TIFF", "MOV" };
        formatBox = new BioComboBox(formats);

        // lay out first page
        final PanelBuilder builder = new PanelBuilder(
                new FormLayout("pref, 3dlu, pref:grow, 3dlu, pref, 3dlu, pref", "pref, 3dlu, pref"));
        final CellConstraints cc = new CellConstraints();
        builder.addLabel("F&older", cc.xy(1, 1)).setLabelFor(folderField);
        builder.add(folderField, cc.xyw(3, 1, 3));
        builder.add(chooseButton, cc.xy(7, 1));

        builder.addLabel("File &pattern", cc.xy(1, 3)).setLabelFor(patternField);
        builder.add(patternField, cc.xy(3, 3));
        builder.addLabel("&Format", cc.xy(5, 3)).setLabelFor(formatBox);
        builder.add(formatBox, cc.xy(7, 3));
        final JPanel first = builder.getPanel();

        // -- Page 2 --

        // pad file numbering with leading zeroes checkbox
        leadingZeroes = new JCheckBox("Pad file numbering with leading zeroes");
        leadingZeroes.setMnemonic('p');

        // frames per second text field
        fps = new JTextField(4);

        // LZW compression checkbox
        lzw = new JCheckBox("LZW compression");
        lzw.setMnemonic('c');

        // lay out second page
        second = new JPanel();
        second.setLayout(new BorderLayout());

        // -- Page 3 --

        // summary text area
        summary = new JEditorPane();
        summary.setEditable(false);
        summary.setContentType("text/html");
        final JScrollPane summaryScroll = new JScrollPane(summary);
        SwingUtil.configureScrollPane(summaryScroll);

        // lay out third page
        third = new JPanel();
        third.setLayout(new BorderLayout());

        // lay out pages
        setPages(new JPanel[] { first, second, third });
    }

    // -- ExportPane API methods --

    /** Associates the given data object with the export pane. */
    public void setData(final ImageTransform trans) {
        this.trans = trans;
        if (trans == null)
            return;
    }

    /** Exports the data according to the current input parameters. */
    public void export() {
        final int[] lengths = trans.getLengths();
        final int numImages = excl < 0 ? 1 : lengths[excl];
        final int numTotal = numFiles * numImages;
        final String format = (String) formatBox.getSelectedItem();
        final TaskManager tm = (TaskManager) bio.getManager(TaskManager.class);
        final BioTask task = tm.createTask("Export " + trans.getName() + " to disk");
        new Thread() {

            @Override
            public void run() {
                try {
                    int count = 0;
                    final int max = 2 * numTotal;
                    task.setStatus(0, max, "Exporting data");
                    final boolean doLZW = lzw.isSelected();
                    final boolean padZeroes = leadingZeroes.isSelected();
                    final int[] plen = new int[stars];
                    for (int i = 0; i < stars; i++)
                        plen[i] = lengths[maps[i]];

                    final int[] lengths = trans.getLengths();
                    for (int i = 0; i < numFiles; i++) {
                        final int[] pos = FormatTools.rasterToPosition(plen, i);
                        final int[] npos = new int[lengths.length];
                        for (int j = 0; j < stars; j++)
                            npos[maps[j]] = pos[j];

                        // construct filename
                        final StringBuffer sb = new StringBuffer();
                        for (int j = 0; j < stars; j++) {
                            sb.append(tokens[j]);
                            if (padZeroes) {
                                final int len = ("" + lengths[maps[j]]).length() - ("" + (pos[j] + 1)).length();
                                for (int k = 0; k < len; k++)
                                    sb.append("0");
                            }
                            sb.append(pos[j] + 1);
                        }
                        sb.append(tokens[stars]);
                        final String filename = sb.toString();

                        // construct data object
                        if (excl < 0) {
                            task.setStatus(count++, max, "Reading #" + (i + 1) + "/" + numTotal);
                            final FlatField ff = (FlatField) trans.getData(null, npos, 2, null);
                            final Image image = ff instanceof ImageFlatField ? ((ImageFlatField) ff).getImage()
                                    : DataUtility.extractImage(ff, false);
                            task.setStatus(count++, max, "Writing #" + (i + 1) + "/" + numTotal);
                            saver.setId(filename);
                            if (doLZW)
                                saver.setCompression("LZW");
                            saver.savePlane(0, AWTImageTools.makeBuffered(image));
                        } else {
                            for (int j = 0; j < lengths[excl]; j++) {
                                final int img = numImages * i + j + 1;
                                task.setStatus(count++, max, "Reading #" + img + "/" + numTotal);
                                npos[excl] = j;
                                final FlatField ff = (FlatField) trans.getData(null, npos, 2, null);
                                final Image image = ff instanceof ImageFlatField ? ((ImageFlatField) ff).getImage()
                                        : DataUtility.extractImage(ff, false);
                                task.setStatus(count++, max, "Writing #" + img + "/" + numTotal);
                                saver.setId(filename);
                                if (doLZW)
                                    saver.setCompression("LZW");
                                saver.savePlane(j, AWTImageTools.makeBuffered(image));
                            }
                        }
                    }
                    task.setCompleted();
                } catch (final FormatException exc) {
                    exc.printStackTrace();
                    JOptionPane.showMessageDialog(dialog, "Error exporting data: " + exc.getMessage(), "VisBio",
                            JOptionPane.ERROR_MESSAGE);
                } catch (final IOException exc) {
                    exc.printStackTrace();
                    JOptionPane.showMessageDialog(dialog, "Error exporting data: " + exc.getMessage(), "VisBio",
                            JOptionPane.ERROR_MESSAGE);
                }
            }
        }.start();
    }

    // -- DialogPane API methods --

    /** Resets the wizard pane's components to their default states. */
    @Override
    public void resetComponents() {
        super.resetComponents();
        patternField.setText("");
        formatBox.setSelectedIndex(0);
    }

    // -- ActionListener API methods --

    /** Handles button press events. */
    @Override
    public void actionPerformed(final ActionEvent e) {
        final String command = e.getActionCommand();
        if (command.equals("next")) {
            if (page == 0) { // lay out page 2
                // ensure file pattern ends with appropriate format extension
                // also determine visibility of "frames per second" text field
                String pattern = folderField.getText() + File.separator + patternField.getText();
                final String format = (String) formatBox.getSelectedItem();
                final String plow = pattern.toLowerCase();
                boolean doFPS = false, doLZW = false;
                if (format.equals("PIC")) {
                    if (!plow.endsWith(".pic"))
                        pattern += ".pic";
                } else if (format.equals("TIFF")) {
                    if (!plow.endsWith(".tiff") && !plow.endsWith(".tif")) {
                        pattern += ".tif";
                        doLZW = true;
                    }
                } else if (format.equals("MOV")) {
                    if (!plow.endsWith(".mov"))
                        pattern += ".mov";
                    doFPS = true;
                }

                // parse file pattern
                final StringTokenizer st = new StringTokenizer("#" + pattern + "#", "*");
                tokens = new String[st.countTokens()];
                for (int i = 0; i < tokens.length; i++) {
                    String t = st.nextToken();
                    if (i == 0)
                        t = t.substring(1);
                    if (i == tokens.length - 1)
                        t = t.substring(0, t.length() - 1);
                    tokens[i] = t;
                }
                stars = tokens.length - 1;
                final String[] dims = trans.getDimTypes();
                final int q = dims.length - stars;
                if (q < 0 || q > 1) {
                    String msg;
                    if (dims.length == 0) {
                        msg = "Please specify a single filename (no asterisks).";
                    } else if (dims.length == 1) {
                        msg = "Please specify either a single filename (no asterisks) "
                                + "or a file pattern with\none asterisk to indicate where the "
                                + "dimensional axes should be numbered.";
                    } else {
                        msg = "Please specify either " + (dims.length - 1) + " or " + dims.length
                                + " asterisks in the file pattern to\nindicate "
                                + "where the dimensional axes should be numbered.";
                    }
                    JOptionPane.showMessageDialog(dialog, msg, "VisBio", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                // determine visibility of "leading zeroes" checkbox
                final boolean doZeroes = stars > 0;

                // build file pattern
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < stars; i++) {
                    sb.append(tokens[i]);
                    sb.append("<");
                    sb.append((char) ('A' + i));
                    sb.append(">");
                }
                sb.append(tokens[stars]);
                final String pat = sb.toString();

                // build list of dimensional axes
                final String[] dimChoices = new String[dims.length];
                for (int i = 0; i < dims.length; i++) {
                    dimChoices[i] = "<" + (i + 1) + "> " + dims[i];
                }

                // construct second page panel
                sb = new StringBuffer("pref");
                if (doFPS)
                    sb.append(", 3dlu, pref");
                if (doLZW)
                    sb.append(", 3dlu, pref");
                if (doZeroes)
                    sb.append(", 3dlu, pref");
                for (int i = 0; i < stars; i++) {
                    sb.append(", 3dlu, pref");
                }
                final PanelBuilder builder = new PanelBuilder(
                        new FormLayout("pref:grow, 3dlu, pref", sb.toString()));
                final CellConstraints cc = new CellConstraints();
                builder.addSeparator(pat, cc.xyw(1, 1, 3));
                int row = 3;
                if (doFPS) {
                    builder.addLabel("&Frames per second", cc.xy(1, row, "right,center")).setLabelFor(fps);
                    builder.add(fps, cc.xy(3, row));
                    row += 2;
                }
                if (doLZW) {
                    builder.add(lzw, cc.xyw(1, row, 3, "right, center"));
                    row += 2;
                }
                if (doZeroes) {
                    builder.add(leadingZeroes, cc.xyw(1, row, 3, "right,center"));
                    row += 2;
                }
                letterBoxes = new BioComboBox[stars];
                for (int i = 0; i < stars; i++) {
                    final char letter = (char) ('A' + i);
                    builder.addLabel("<" + letter + "> =", cc.xy(1, row, "right,center"));
                    letterBoxes[i] = new BioComboBox(dimChoices);
                    letterBoxes[i].setSelectedIndex(i);
                    builder.add(letterBoxes[i], cc.xy(3, row));
                    row += 2;
                }
                second.removeAll();
                second.add(builder.getPanel());
            } else if (page == 1) { // lay out page 3
                final String[] dims = trans.getDimTypes();
                final int[] lengths = trans.getLengths();

                // file pattern
                StringBuffer sb = new StringBuffer("File pattern: ");
                for (int i = 0; i < stars; i++) {
                    sb.append(tokens[i]);
                    final char letter = (char) ('A' + i);
                    sb.append("<");
                    sb.append(letter);
                    sb.append(">");
                }
                sb.append(tokens[stars]);

                // file format
                sb.append("\nFormat: ");
                final String format = (String) formatBox.getSelectedItem();
                if (format.equals("PIC"))
                    sb.append("Bio-Rad PIC file");
                else if (format.equals("TIFF")) {
                    if (dims.length == stars)
                        sb.append("TIFF image");
                    else
                        sb.append("Multi-page TIFF stack");
                    if (lzw.isSelected())
                        sb.append(" (LZW)");
                } else if (format.equals("MOV"))
                    sb.append("QuickTime movie");
                else
                    sb.append("Unknown");
                sb.append("\n \n");

                // dimensional mappings
                maps = new int[stars];
                if (stars > 0) {
                    for (int i = 0; i < stars; i++) {
                        final char letter = (char) ('A' + i);
                        sb.append("<");
                        sb.append(letter);
                        sb.append("> numbered across dimension: ");
                        maps[i] = letterBoxes[i].getSelectedIndex();
                        sb.append("<");
                        sb.append(maps[i] + 1);
                        sb.append("> ");
                        sb.append(dims[maps[i]]);
                        sb.append("\n");
                    }
                } else
                    sb.append(" \n");

                // count dimensional axis exclusions
                excl = -1;
                final boolean[] b = new boolean[dims.length];
                for (int i = 0; i < stars; i++)
                    b[maps[i]] = true;
                int exclude = 0;
                for (int i = 0; i < dims.length; i++) {
                    if (!b[i]) {
                        excl = i;
                        exclude++;
                    }
                }

                // file contents
                sb.append("Each file will contain ");
                final int q = dims.length - stars;
                if (q == 0)
                    sb.append("a single image.\n \n");
                else { // q == 1
                    final int len = lengths[excl];
                    sb.append(len);
                    sb.append(" image");
                    if (len != 1)
                        sb.append("s");
                    sb.append(" across dimension: <");
                    sb.append(excl + 1);
                    sb.append("> ");
                    sb.append(dims[excl]);
                    sb.append("\n \n");
                }

                // verify dimensional axis mappings are acceptable
                if (exclude != q) {
                    JOptionPane.showMessageDialog(dialog, "Please map each letter to a different dimensional axis.",
                            "VisBio", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                // verify number of range components is acceptable
                final int rangeCount = trans.getRangeCount();
                if (format.equals("PIC") && rangeCount != 1) {
                    JOptionPane.showMessageDialog(dialog,
                            "Bio-Rad PIC format requires a data object with a single "
                                    + "range component. Please subsample your data object accordingly.",
                            "VisBio", JOptionPane.ERROR_MESSAGE);
                    return;
                } else if (rangeCount != 1 && rangeCount != 3) {
                    JOptionPane.showMessageDialog(dialog,
                            format + " format requires a data object with either 1 or 3 "
                                    + "range components. Please subsample your data object accordingly.",
                            "VisBio", JOptionPane.ERROR_MESSAGE);
                    return;
                }

                // number of files
                final boolean padZeroes = leadingZeroes.isSelected();
                sb.append("\nFirst file: ");
                for (int i = 0; i < stars; i++) {
                    sb.append(tokens[i]);
                    if (padZeroes) {
                        final int len = ("" + lengths[maps[i]]).length() - 1;
                        for (int j = 0; j < len; j++)
                            sb.append("0");
                    }
                    sb.append("1");
                }
                sb.append(tokens[stars]);
                sb.append("\nLast file: ");
                numFiles = 1;
                for (int i = 0; i < stars; i++) {
                    sb.append(tokens[i]);
                    sb.append(lengths[maps[i]]);
                    numFiles *= lengths[maps[i]];
                }
                sb.append(tokens[stars]);
                sb.append("\nTotal number of files: ");
                sb.append(numFiles);

                // construct third page panel
                final StringTokenizer st = new StringTokenizer(sb.toString(), "\n");
                final int numLines = st.countTokens();
                sb = new StringBuffer("pref, 3dlu");
                for (int i = 0; i < numLines; i++)
                    sb.append(", pref");
                sb.append(", 5dlu");
                final PanelBuilder builder = new PanelBuilder(new FormLayout("pref:grow", sb.toString()));
                final CellConstraints cc = new CellConstraints();
                builder.addSeparator("Summary", cc.xy(1, 1));
                for (int i = 0; i < numLines; i++) {
                    builder.addLabel(st.nextToken(), cc.xy(1, i + 3));
                }
                third.removeAll();
                third.add(builder.getPanel());
            }

            super.actionPerformed(e);
        } else if (command.equals("folder")) {
            // pop up folder chooser
            File dir = new File(folderField.getText());
            final JFileChooser chooser = dir.isDirectory() ? new JFileChooser(dir) : new JFileChooser();
            chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
                dir = chooser.getSelectedFile();
                if (dir != null)
                    folderField.setText(dir.getPath());
            }
        } else
            super.actionPerformed(e);
    }

}