adams.gui.visualization.instance.InstancePanel.java Source code

Java tutorial

Introduction

Here is the source code for adams.gui.visualization.instance.InstancePanel.java

Source

/*
 *   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 3 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/>.
 */

/*
 * InstancePanel.java
 * Copyright (C) 2009-2017 University of Waikato, Hamilton, New Zealand
 */

package adams.gui.visualization.instance;

import adams.core.Properties;
import adams.core.option.OptionUtils;
import adams.data.instance.Instance;
import adams.data.instance.InstancePoint;
import adams.data.io.output.SpreadSheetWriter;
import adams.data.report.DataType;
import adams.data.report.Field;
import adams.data.spreadsheet.SpreadSheet;
import adams.data.statistics.ArrayHistogram;
import adams.db.AbstractDatabaseConnection;
import adams.db.DatabaseConnection;
import adams.gui.chooser.SpreadSheetFileChooser;
import adams.gui.core.AntiAliasingSupporter;
import adams.gui.core.ColorHelper;
import adams.gui.core.GUIHelper;
import adams.gui.core.Undo;
import adams.gui.dialog.SpreadSheetDialog;
import adams.gui.event.PaintListener;
import adams.gui.scripting.AbstractScriptingEngine;
import adams.gui.scripting.ScriptingEngine;
import adams.gui.visualization.container.AbstractContainerManager;
import adams.gui.visualization.container.ContainerTable;
import adams.gui.visualization.container.DataContainerPanelWithContainerList;
import adams.gui.visualization.core.ColorProvider;
import adams.gui.visualization.core.CoordinatesPaintlet;
import adams.gui.visualization.core.CoordinatesPaintlet.Coordinates;
import adams.gui.visualization.core.DefaultColorProvider;
import adams.gui.visualization.core.Paintlet;
import adams.gui.visualization.core.PlotPanel;
import adams.gui.visualization.core.plot.Axis;
import adams.gui.visualization.core.plot.HitDetectorSupporter;
import adams.gui.visualization.core.plot.TipTextCustomizer;
import weka.core.Instances;

import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * A panel for displaying instances.
 *
 * @author  fracpete (fracpete at waikato dot ac dot nz)
 * @version $Revision$
 */
public class InstancePanel extends
        DataContainerPanelWithContainerList<Instance, InstanceContainerManager, InstanceContainer> implements
        PaintListener, TipTextCustomizer, AntiAliasingSupporter, HitDetectorSupporter<InstancePointHitDetector> {

    /** for serialization. */
    private static final long serialVersionUID = 7985845939008731534L;

    /** paintlet for drawing the graph. */
    protected AbstractInstancePaintlet m_InstancePaintlet;

    /** paintlet for drawing the X-axis. */
    protected CoordinatesPaintlet m_CoordinatesPaintlet;

    /** the undo manager. */
    protected Undo m_Undo;

    /** whether to adjust to visible data or not. */
    protected boolean m_AdjustToVisibleData;

    /** the hit detector for the tooltip. */
    protected InstancePointHitDetector m_InstancePointHitDetector;

    /** the maximum number of columns for the tooltip. */
    protected int m_ToolTipMaxColumns;

    /** the maximum number of rows for the tooltip. */
    protected int m_ToolTipMaxRows;

    /** the zoom overview panel. */
    protected InstanceZoomOverviewPanel m_PanelZoomOverview;

    /** the file chooser for saving a specific sequence. */
    protected SpreadSheetFileChooser m_FileChooser;

    /** the dialog for displaying a sequence. */
    protected List<SpreadSheetDialog> m_ViewDialogs;

    /** the dialog for the histogram setup. */
    protected HistogramFactory.SetupDialog m_HistogramSetup;

    /**
     * Initializes the panel.
     */
    public InstancePanel() {
        super();
    }

    /**
     * Initializes the panel.
     *
     * @param title   the title for the panel
     */
    public InstancePanel(String title) {
        super(title);
    }

    /**
     * Initializes the members.
     */
    @Override
    protected void initialize() {
        m_Undo = null;
        m_AdjustToVisibleData = true;
        m_HistogramSetup = null;

        super.initialize();
    }

    /**
     * Returns the default database connection.
     *
     * @return      the default database connection
     */
    @Override
    protected AbstractDatabaseConnection getDefaultDatabaseConnection() {
        return DatabaseConnection.getSingleton();
    }

    /**
     * Returns the container manager to use.
     *
     * @return      the container manager
     */
    @Override
    protected InstanceContainerManager newContainerManager() {
        return new InstanceContainerManager(this);
    }

    /**
     * Returns the paintlet used for painting the containers.
     *
     * @return      the paintlet
     */
    @Override
    public AbstractInstancePaintlet getContainerPaintlet() {
        return m_InstancePaintlet;
    }

    /**
     * Returns the hit detector.
     *
     * @return      the hit detector
     */
    public InstancePointHitDetector getHitDetector() {
        return m_InstancePointHitDetector;
    }

    /**
     * Initializes the GUI.
     */
    @Override
    protected void initGUI() {
        Properties props;
        JPanel panel;

        super.initGUI();

        props = getProperties();

        m_ToolTipMaxColumns = props.getInteger("Plot.ToolTip.MaxColumns", 80);
        m_ToolTipMaxRows = props.getInteger("Plot.ToolTip.MaxRows", 40);

        setAdjustToVisibleData(props.getBoolean("Plot.AdjustToVisibleData", false));

        panel = new JPanel();
        panel.setMinimumSize(new Dimension(1, props.getInteger("Axis.Bottom.Width", 0)));
        panel.setPreferredSize(new Dimension(1, props.getInteger("Axis.Bottom.Width", 0)));
        m_SidePanel.add(panel, BorderLayout.SOUTH);

        getPlot().setPopupMenuCustomizer(this);

        // paintlets
        m_InstancePaintlet = new InstanceLinePaintlet();
        m_InstancePaintlet.setStrokeThickness(props.getDouble("Plot.StrokeThickness", 1.0).floatValue());
        ((AntiAliasingSupporter) m_InstancePaintlet)
                .setAntiAliasingEnabled(props.getBoolean("Plot.AntiAliasing", true));
        m_InstancePaintlet.setPanel(this);
        m_CoordinatesPaintlet = new CoordinatesPaintlet();
        m_CoordinatesPaintlet.setYInvisible(true);
        m_CoordinatesPaintlet.setPanel(this);
        m_CoordinatesPaintlet.setXColor(props.getColor("Plot.CoordinatesColor." + Coordinates.X, Color.DARK_GRAY));
        m_CoordinatesPaintlet.setYColor(props.getColor("Plot.CoordinatesColor." + Coordinates.Y, Color.DARK_GRAY));

        m_InstancePointHitDetector = new InstancePointHitDetector(this);
        getPlot().setTipTextCustomizer(this);

        try {
            getContainerManager()
                    .setColorProvider((ColorProvider) OptionUtils.forAnyCommandLine(ColorProvider.class,
                            props.getProperty("Plot.ColorProvider", DefaultColorProvider.class.getName())));
        } catch (Exception e) {
            System.err.println(getClass().getName() + " - Failed to set the color provider:");
            getContainerManager().setColorProvider(new DefaultColorProvider());
        }

        m_PanelZoomOverview = new InstanceZoomOverviewPanel();
        m_PlotWrapperPanel.add(m_PanelZoomOverview, BorderLayout.SOUTH);
        m_PanelZoomOverview.setDataContainerPanel(this);
    }

    /**
     * Returns the container list.
     *
     * @return      the list
     */
    @Override
    protected InstanceContainerList createContainerList() {
        InstanceContainerList result;

        result = new InstanceContainerList();
        result.setTitle("Instances");
        result.setManager(getContainerManager());
        result.setAllowSearch(getProperties().getBoolean("ContainerList.AllowSearch", false));
        result.setPopupMenuSupplier(this);
        result.addTableModelListener((TableModelEvent e) -> {
            final ContainerTable table = result.getTable();
            if ((table.getRowCount() > 0) && (table.getSelectedRowCount() == 0)) {
                SwingUtilities.invokeLater(() -> table.getSelectionModel().addSelectionInterval(0, 0));
            }
        });

        return result;
    }

    /**
     * Returns the current container manager.
     *
     * @return      the manager
     */
    public AbstractContainerManager getSequenceManager() {
        return m_Manager;
    }

    /**
     * Sets the undo manager to use, can be null if no undo-support wanted.
     *
     * @param value   the undo manager to use
     */
    public void setUndo(Undo value) {
        m_Undo = value;
    }

    /**
     * Returns the current undo manager, can be null.
     *
     * @return      the undo manager, if any
     */
    public Undo getUndo() {
        return m_Undo;
    }

    /**
     * Returns whether an Undo manager is currently available.
     *
     * @return      true if an undo manager is set
     */
    public boolean isUndoSupported() {
        return (m_Undo != null);
    }

    /**
     * Returns true if the paintlets can be executed.
     *
     * @param g      the graphics context
     * @return      true if painting can go ahead
     */
    @Override
    protected boolean canPaint(Graphics g) {
        return ((getPlot() != null) && (m_Manager != null));
    }

    /**
     * Updates the axes with the min/max of the new data.
     */
    @Override
    public void prepareUpdate() {
        List<InstancePoint> points;
        Iterator<InstancePoint> iter;
        InstancePoint point;
        double minX;
        double maxX;
        double minY;
        double maxY;
        int i;

        minX = Double.MAX_VALUE;
        maxX = -Double.MAX_VALUE;
        minY = 0;
        maxY = -Double.MAX_VALUE;

        for (i = 0; i < getContainerManager().count(); i++) {
            if (m_AdjustToVisibleData) {
                if (!getContainerManager().isVisible(i))
                    continue;
            }

            points = getContainerManager().get(i).getData().toList();

            if (points.size() == 0)
                continue;

            // determine min/max
            if (InstancePoint.toDouble(points.get(0).getX()) < minX)
                minX = InstancePoint.toDouble(points.get(0).getX());
            if (InstancePoint.toDouble(points.get(points.size() - 1).getX()) > maxX)
                maxX = InstancePoint.toDouble(points.get(points.size() - 1).getX());

            iter = points.iterator();
            while (iter.hasNext()) {
                point = iter.next();
                if (InstancePoint.toDouble(point.getY()) > maxY)
                    maxY = InstancePoint.toDouble(point.getY());
                if (InstancePoint.toDouble(point.getY()) < minY)
                    minY = InstancePoint.toDouble(point.getY());
            }
        }

        // update axes
        getPlot().getAxis(Axis.LEFT).setMinimum(minY);
        getPlot().getAxis(Axis.LEFT).setMaximum(maxY);
        getPlot().getAxis(Axis.BOTTOM).setMinimum(minX);
        getPlot().getAxis(Axis.BOTTOM).setMaximum(maxX);
    }

    /**
     * Returns true if storing the color in the report of container's data object
     * is supported.
     *
     * @return      true if supported
     */
    @Override
    public boolean supportsStoreColorInReport() {
        return true;
    }

    /**
     * Stores the color of the container in the report of container's
     * data object.
     *
     * @param indices   the indices of the containers of the container manager
     * @param name   the field name to use
     */
    @Override
    public void storeColorInReport(int[] indices, String name) {
        Field field;
        InstanceContainer cont;

        field = new Field(name, DataType.STRING);
        for (int index : indices) {
            cont = getContainerManager().get(index);
            cont.getData().getReport().addField(field);
            cont.getData().getReport().setValue(field, ColorHelper.toHex(cont.getColor()));
        }
    }

    /**
     * Displays the histograms for the given instances.
     *
     * @param data   the instances to display
     */
    public void showHistogram(List<InstanceContainer> data) {
        HistogramFactory.Dialog dialog;
        int i;
        Instance inst;

        // get parameters for histograms
        if (m_HistogramSetup == null) {
            if (getParentDialog() != null)
                m_HistogramSetup = HistogramFactory.getSetupDialog(getParentDialog(), ModalityType.DOCUMENT_MODAL);
            else
                m_HistogramSetup = HistogramFactory.getSetupDialog(getParentFrame(), true);
        }
        m_HistogramSetup.setLocationRelativeTo(this);
        m_HistogramSetup.setVisible(true);
        if (m_HistogramSetup.getResult() != HistogramFactory.SetupDialog.APPROVE_OPTION)
            return;

        // generate histograms and display them
        if (getParentDialog() != null)
            dialog = HistogramFactory.getDialog(getParentDialog(), ModalityType.MODELESS);
        else
            dialog = HistogramFactory.getDialog(getParentFrame(), false);
        for (i = 0; i < data.size(); i++) {
            inst = data.get(i).getData();
            dialog.add((ArrayHistogram) m_HistogramSetup.getCurrent(), inst, data.get(i).getID());
        }
        dialog.setLocationRelativeTo(this);
        dialog.setVisible(true);
    }

    /**
     * Saves the specified instance as spreadsheet file.
     *
     * @param cont   the instance to save
     */
    public void saveInstance(InstanceContainer cont) {
        int retVal;
        Instance inst;
        SpreadSheetWriter writer;

        if (m_FileChooser == null)
            m_FileChooser = new SpreadSheetFileChooser();

        retVal = m_FileChooser.showSaveDialog(this);
        if (retVal != SpreadSheetFileChooser.APPROVE_OPTION)
            return;

        inst = cont.getData();
        writer = m_FileChooser.getWriter();
        if (!writer.write(inst.toSpreadSheet(), m_FileChooser.getSelectedFile()))
            GUIHelper.showErrorMessage(this,
                    "Failed to save instance to file:\n" + m_FileChooser.getSelectedFile());
    }

    /**
     * Views the specified instance in a table.
     *
     * @param cont   the instance to view
     */
    public void viewInstance(InstanceContainer cont) {
        Instance isnt;
        SpreadSheetDialog dialog;
        SpreadSheet sheet;

        if (m_ViewDialogs == null)
            m_ViewDialogs = new ArrayList<>();

        isnt = cont.getData();
        sheet = isnt.toSpreadSheet();
        if (getParentDialog() != null)
            dialog = new SpreadSheetDialog(getParentDialog(), ModalityType.MODELESS);
        else
            dialog = new SpreadSheetDialog(getParentFrame(), false);
        m_ViewDialogs.add(dialog);
        dialog.setTitle("Instance: " + cont.getDisplayID());
        dialog.setSize(GUIHelper.getInteger("DefaultSmallDialog.Height", 400),
                GUIHelper.getInteger("DefaultSmallDialog.Width", 600));
        dialog.setLocationRelativeTo(this);
        dialog.setSpreadSheet(sheet);
        dialog.setVisible(true);
    }

    /**
     * Sets the zoom overview panel visible or hides it.
     *
     * @param value   if true then the panel is displayed
     */
    public void setZoomOverviewPanelVisible(boolean value) {
        m_PanelZoomOverview.setVisible(value);
    }

    /**
     * Returns whether the zoom overview panel is visible or not.
     *
     * @return      true if visible
     */
    public boolean isZoomOverviewPanelVisible() {
        return m_PanelZoomOverview.isVisible();
    }

    /**
     * Returns the zoom overview panel.
     *
     * @return      the panel
     */
    public InstanceZoomOverviewPanel getZoomOverviewPanel() {
        return m_PanelZoomOverview;
    }

    /**
     * Sets whether the display is adjusted to only the visible data or
     * everything currently loaded.
     *
     * @param value   if true then plot is adjusted to visible data
     */
    public void setAdjustToVisibleData(boolean value) {
        m_AdjustToVisibleData = value;
        update();
    }

    /**
     * Returns whether the display is adjusted to only the visible spectrums
     * or all of them.
     *
     * @return      true if the plot is adjusted to only the visible data
     */
    public boolean getAdjustToVisibleData() {
        return m_AdjustToVisibleData;
    }

    /**
     * Returns the paintlet for painting the instance.
     *
     * @return      the paintlet
     */
    public AbstractInstancePaintlet getDataPaintlet() {
        return m_InstancePaintlet;
    }

    /**
     * Sets the paintlet to use for painting the data.
     *
     * @param value   the paintlet
     */
    @Override
    public void setDataPaintlet(Paintlet value) {
        removePaintlet(m_InstancePaintlet);
        m_InstancePaintlet = (AbstractInstancePaintlet) value;
        m_InstancePaintlet.setPanel(this);
        addPaintlet(m_InstancePaintlet);
    }

    /**
     * Sets whether to use anti-aliasing.
     *
     * @param value   if true then anti-aliasing is used
     */
    public void setAntiAliasingEnabled(boolean value) {
        if (m_InstancePaintlet instanceof AntiAliasingSupporter)
            ((AntiAliasingSupporter) m_InstancePaintlet).setAntiAliasingEnabled(value);
        if (m_PanelZoomOverview.getContainerPaintlet() instanceof AntiAliasingSupporter)
            ((AntiAliasingSupporter) m_PanelZoomOverview.getContainerPaintlet()).setAntiAliasingEnabled(value);
    }

    /**
     * Returns whether anti-aliasing is used.
     *
     * @return      true if anti-aliasing is used
     */
    public boolean isAntiAliasingEnabled() {
        return (m_InstancePaintlet instanceof AntiAliasingSupporter)
                && ((AntiAliasingSupporter) m_InstancePaintlet).isAntiAliasingEnabled();
    }

    /**
     * Returns the currently visible instances.
     *
     * @return      the instances, null if none visible
     */
    public Instances getInstances() {
        Instances result;
        Instance inst;
        int i;
        List<InstanceContainer> list;

        result = null;

        list = getContainerManager().getAllVisible();
        for (i = 0; i < list.size(); i++) {
            inst = list.get(i).getData();
            if (result == null)
                result = inst.getDatasetHeader();
            result.add(inst.toInstance());
        }

        return result;
    }

    /**
     * Processes the given tip text. Among the current mouse position, the
     * panel that initiated the call are also provided.
     *
     * @param panel   the content panel that initiated this call
     * @param mouse   the mouse position
     * @param tiptext   the tiptext so far
     * @return      the processed tiptext
     */
    @Override
    public String processTipText(PlotPanel panel, Point mouse, String tiptext) {
        String result;
        MouseEvent event;
        String hit;

        result = tiptext;
        event = new MouseEvent(getPlot().getContent(), MouseEvent.MOUSE_MOVED, new Date().getTime(), 0,
                (int) mouse.getX(), (int) mouse.getY(), 0, false);

        hit = m_InstancePointHitDetector.detect(event);
        if (hit != null)
            result = GUIHelper.processTipText(hit, m_ToolTipMaxColumns, m_ToolTipMaxRows);

        return result;
    }

    /**
     * Returns the current scripting engine, can be null.
     *
     * @return      the current engine
     */
    @Override
    public AbstractScriptingEngine getScriptingEngine() {
        return ScriptingEngine.getSingleton(getDatabaseConnection());
    }

    /**
     * Hook method, called after the update was performed.
     */
    @Override
    protected void postUpdate() {
        super.postUpdate();

        if (m_PanelZoomOverview != null)
            m_PanelZoomOverview.update();
    }

    /**
     * Cleans up data structures, frees up memory.
     */
    @Override
    public void cleanUp() {
        if (m_InstancePointHitDetector != null) {
            m_InstancePointHitDetector.cleanUp();
            m_InstancePointHitDetector = null;
        }

        if (m_ViewDialogs != null) {
            for (SpreadSheetDialog dialog : m_ViewDialogs)
                dialog.dispose();
            m_ViewDialogs.clear();
            m_ViewDialogs = null;
        }

        if (m_HistogramSetup != null) {
            m_HistogramSetup.dispose();
            m_HistogramSetup = null;
        }

        super.cleanUp();
    }
}