org.fhcrc.cpl.viewer.quant.gui.PanelWithLogRatioHistAndFields.java Source code

Java tutorial

Introduction

Here is the source code for org.fhcrc.cpl.viewer.quant.gui.PanelWithLogRatioHistAndFields.java

Source

/*
 * Copyright (c) 2003-2012 Fred Hutchinson Cancer Research Center
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.fhcrc.cpl.viewer.quant.gui;

import org.fhcrc.cpl.toolbox.gui.chart.PanelWithHistogram;
import org.fhcrc.cpl.toolbox.Rounder;
import org.apache.log4j.Logger;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.plot.XYPlot;

import javax.swing.*;
import java.util.List;
import java.util.ArrayList;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

/**
 * A goofy little GUI thing that shoves together a histogram of log-ratios with an overlay for selecting extreme
 * values, and 3 text fields summarizing the selection.  Kind of specific-seeming, but used in 3 different places
 * in Qurate.
 *
 * The layout is very sensitive to tweaks in dimensions.  Careful when sizing.
 *
 * todo: for some reason a line started showing up at x=0 (log of 1:1).  Would like to control that.
 */
public class PanelWithLogRatioHistAndFields extends JPanel {
    protected static Logger _log = Logger.getLogger(PanelWithLogRatioHistAndFields.class);

    //boundaries for chart values, in non-log space
    protected float maxRatioBound = 40f;
    protected float minRatioBound = 1f / maxRatioBound;

    //Labels for selection summary info
    protected JLabel maxLowRatioLabel;
    protected JLabel minHighRatioLabel;
    protected JLabel numPassingRatiosLabel;

    protected PanelWithHistogram logRatioHistogram;

    //min and max ratio for retention.  Ratio must be > min OR < max, or both
    protected float minHighRatio = 0f;
    protected float maxLowRatio = 999f;

    //for marking a special value
    protected float domainCrosshairValue = Float.NaN;

    //objects to be notified when the user changes the selected range.  Notifications occur while dragging
    //and also when released
    List<ActionListener> rangeUpdateListeners = new ArrayList<ActionListener>();

    protected List<Float> logRatios;

    protected LogRatioHistMouseListener histMouseListener;

    public PanelWithLogRatioHistAndFields() {
        initGUI();
    }

    public PanelWithLogRatioHistAndFields(float maxLowRatio, float minHighRatio) {
        this();
        this.maxLowRatio = maxLowRatio;
        this.minHighRatio = minHighRatio;
        updateFieldText();
    }

    public PanelWithLogRatioHistAndFields(float maxLowRatio, float minHighRatio, List<Float> logRatios) {
        this(maxLowRatio, minHighRatio);
        setLogRatios(logRatios);
    }

    /**
     * Lay out GUI components, but NOT the histogram
     */
    protected void initGUI() {
        setLayout(new GridBagLayout());
        maxLowRatioLabel = new JLabel("Max Low Ratio: ");
        minHighRatioLabel = new JLabel("Min High Ratio: ");
        numPassingRatiosLabel = new JLabel("Retained: ");
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.PAGE_START;
        gbc.insets = new Insets(0, 0, 0, 0);
        gbc.weighty = 0;
        gbc.weightx = 1;
        gbc.gridwidth = 1;
        add(maxLowRatioLabel, gbc);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        add(minHighRatioLabel, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        add(numPassingRatiosLabel, gbc);
    }

    /**
     * Set the log ratios, build the histogram and display, removing the old one if there was one.  Nothing gets
     * cleaned up related to the old chart; it'll just hang around intil GC
     * todo: do I need to dispose of the old chart in a better way?
     * @param logRatios
     */
    public void setLogRatios(List<Float> logRatios) {
        _log.debug("setLogRatios 1");
        if (logRatioHistogram != null) {
            remove(logRatioHistogram);
        }
        this.logRatios = logRatios;

        float minLogRatioBound = (float) Math.log(minRatioBound);
        float maxLogRatioBound = (float) Math.log(maxRatioBound);

        _log.debug("setLogRatios 2");

        List<Float> boundedLogRatios = new ArrayList<Float>(logRatios.size());
        for (float logRatio : logRatios)
            boundedLogRatios.add(Math.min(maxLogRatioBound, Math.max(minLogRatioBound, logRatio)));

        logRatioHistogram = new PanelWithHistogram(boundedLogRatios, "Log Ratios", 200);
        Dimension histDimension = new Dimension(300, 80);
        if (!Float.isNaN(domainCrosshairValue)) {
            logRatioHistogram.getChart().getXYPlot().setDomainCrosshairValue(domainCrosshairValue, true);
            logRatioHistogram.getChart().getXYPlot().setDomainCrosshairVisible(true);
        }

        _log.debug("setLogRatios 1");

        logRatioHistogram.setPreferredSize(histDimension);
        logRatioHistogram.getChart().removeLegend();
        logRatioHistogram.getChart().getXYPlot().getDomainAxis().setLowerBound(minLogRatioBound);
        logRatioHistogram.getChart().getXYPlot().getDomainAxis().setUpperBound(maxLogRatioBound);

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.BOTH;
        gbc.anchor = GridBagConstraints.LINE_START;
        gbc.insets = new Insets(0, 0, 0, 0);
        gbc.weighty = 10;
        gbc.weightx = 1;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        add(logRatioHistogram, gbc);

        histMouseListener = new LogRatioHistMouseListener(logRatioHistogram);
        ChartPanel histChartPanel = logRatioHistogram.getChartPanel();
        histChartPanel.setMouseZoomable(false);
        logRatioHistogram.getChartPanel().addMouseListener(histMouseListener);
        logRatioHistogram.getChartPanel().addMouseMotionListener(histMouseListener);
        histMouseListener.addRangeUpdateListener(new LogRatioHistogramListener(this));

        logRatioHistogram.updateUI();
        //if there are specified minHigh and maxHigh values, and they're valid, draw the initially selected region
        if (minHighRatio > maxLowRatio && minHighRatio > 0 && maxLowRatio > 0)
            updateSelectedRegion();

        //remove axes from chart
        ((XYPlot) logRatioHistogram.getPlot()).getRangeAxis().setVisible(false);
        ((XYPlot) logRatioHistogram.getPlot()).getDomainAxis().setVisible(false);

        logRatioHistogram.updateUI();
    }

    /**
     * Resizing the histogram is tricky
     * @param width
     * @param height
     */
    public void setSize(int width, int height) {
        super.setSize(width, height);
        super.setPreferredSize(new Dimension(width, height));
        int chartWidth = width;
        int chartHeight = height - 45;
        Dimension histDimension = new Dimension(chartWidth, chartHeight);
        if (logRatioHistogram != null) {
            logRatioHistogram.setPreferredSize(histDimension);
            logRatioHistogram.updateUI();
        }
    }

    /**
     * Update all 3 text fields after selection change
     */
    public void updateFieldText() {
        maxLowRatioLabel.setText("Max Low Ratio: " + Rounder.round(maxLowRatio, 2));
        minHighRatioLabel.setText("Min High Ratio: " + Rounder.round(minHighRatio, 2));
        if (logRatios != null) {
            int numRetained = 0;
            float maxLowLogRatio = (float) Math.log(maxLowRatio);
            float minHighLogRatio = (float) Math.log(minHighRatio);

            for (float logRatio : logRatios) {
                if (logRatio <= maxLowLogRatio || logRatio >= minHighLogRatio)
                    numRetained++;
            }
            numPassingRatiosLabel.setText("Retained: " + numRetained + " / " + logRatios.size() + " ("
                    + Rounder.round(100f * numRetained / logRatios.size(), 1) + "%)");
        } else
            numPassingRatiosLabel.setText("Retained: ");
    }

    public float getMinHighRatio() {
        return minHighRatio;
    }

    /**
     * Also updates region selected and text of fields
     * @param minHighRatio
     */
    public void setMinHighRatio(float minHighRatio) {
        this.minHighRatio = minHighRatio;
        updateFieldText();
        updateSelectedRegion();
    }

    public float getMaxLowRatio() {
        return maxLowRatio;
    }

    /**
     * Also updates region selected and text of fields
     * @param maxLowRatio
     */
    public void setMaxLowRatio(float maxLowRatio) {
        this.maxLowRatio = maxLowRatio;
        updateFieldText();
        updateSelectedRegion();
    }

    protected void updateSelectedRegion() {
        if (histMouseListener != null)
            histMouseListener.setSelectedRegionWithChartValues((float) Math.log(maxLowRatio),
                    (float) Math.log(minHighRatio));
    }

    public void addRangeUpdateListener(ActionListener listener) {
        rangeUpdateListeners.add(listener);
    }

    /**
     * A chart listener that picks up events indicating changes to the selected area, recalculates the
     * low and high ratios defined (in not-log space), and passes on the notification to all listeners
     */
    protected class LogRatioHistogramListener implements ActionListener {
        protected PanelWithLogRatioHistAndFields parent;

        public LogRatioHistogramListener(PanelWithLogRatioHistAndFields parent) {
            this.parent = parent;
        }

        public void actionPerformed(ActionEvent e) {
            LogRatioHistMouseListener logRatioHistMouseListener = (LogRatioHistMouseListener) e.getSource();
            minHighRatio = (float) Math.exp(logRatioHistMouseListener.getSelectedRealXMaxValue());
            maxLowRatio = (float) Math.exp(logRatioHistMouseListener.getSelectedRealXMinValue());
            updateFieldText();

            for (ActionListener listener : rangeUpdateListeners)
                listener.actionPerformed(new ActionEvent(parent, 0, maxLowRatio + "," + minHighRatio));
        }
    }

    public float getMaxRatioBound() {
        return maxRatioBound;
    }

    public void setMaxRatioBound(float maxRatioBound) {
        this.maxRatioBound = maxRatioBound;
    }

    public float getMinRatioBound() {
        return minRatioBound;
    }

    public void setMinRatioBound(float minRatioBound) {
        this.minRatioBound = minRatioBound;
    }

    public float getDomainCrosshairValue() {
        return domainCrosshairValue;
    }

    public void setDomainCrosshairValue(float domainCrosshairValue) {
        this.domainCrosshairValue = domainCrosshairValue;
    }
}