package com.webfoot.prefuse;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JToolBar;
import prefuse.data.Table;
import prefuse.data.io.DataIOException;
import prefuse.data.io.DelimitedTextTableReader;
import com.webfoot.prefuse.HistogramTable;
/**
* A simple histogram visualization that allows different columns
* in a data table to be histogramized and displayed on the fly.
* extended by Kaitlin Duck Sherwood to show histograms.
*
* Kaitlin Duck Sherwood's modifications are granted as is for any
* commercial or non-commercial use, with or without attribution.
* The only conditions are that you can't pretend that you wrote them,
* and that you leave these notices about her authorship in the source.
*
* Possible limitations: I'm not completely sure if the histogram
* graph gets cleaned up properly. I *think* that when I do the
* getContentPane().remove() of the histoGraph and toolbar
* in getToolbar, that that frees them for garbage
* collection. However, I have not verified that.
*
* Known bug: If you use the default data (fisher.iris.txt),
* and display the "Species Name" (a String column) and then display
* PetalWidth (a double column), then the first two axis labels use
* names from Species Name. I've looked at this carefully and suspect
* it is a Prefuse bug. I could be wrong.
*
* @author jeffrey heer
* @author Kaitlin Duck Sherwood
*/
public class HistogramFrame extends JFrame {
Table m_dataTable;
HistogramTable m_histoTable;
JToolBar m_toolbar;
/**
* @param dataFileName - the name of a file in CSV format that holds the
* data to be histogrammized.
* @param defaultFieldName - the name of the field (column) of the data table
* whose histogram is to be shown in the histogram graph.
* @param aBinCount - the number of bins that the histogram should sort data values into
*/
public HistogramFrame(String dataFileName, String defaultFieldName, int aBinCount) {
super("histogram demo");
m_dataTable = getDataTable(dataFileName);
int binCount = (aBinCount > 0) ? aBinCount : 50;
m_histoTable = new HistogramTable(m_dataTable, binCount);
// histoTable.printWholeTable(); // debug
HistogramGraph m_histoGraph = new HistogramGraph(m_histoTable, defaultFieldName);
m_toolbar = getToolbar(m_histoGraph, defaultFieldName, binCount);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().add(m_toolbar, BorderLayout.NORTH);
getContentPane().add(m_histoGraph, BorderLayout.CENTER);
pack();
setVisible(true);
}
/**
* @param dataFileName - the name of a file in CSV format that holds the
* data to be histogrammized
* @return table - a prefuse Table with non-histogrammed data values
*/
private Table getDataTable(String dataFileName) {
Table table = null;
try {
table = new DelimitedTextTableReader().readTable(dataFileName);
} catch ( ArrayIndexOutOfBoundsException e ) {
e.printStackTrace();
System.exit(-2);
} catch ( DataIOException e) {
System.err.println("Uh-oh, there was some problem with the file "+dataFileName+":");
System.err.println(" "+e.getLocalizedMessage());
System.exit(-78);
}
return table;
}
/**
* @param histoGraph - a JComponent showing a graph of the histogram data
* @param fieldName - the name of the field (column) of the data table whose
* histogram is to be shown in the histogram graph.
* @param binCount - the number of bins that the histogram should sort data values into
* @return toolbar - a JToolbar that controls the field to display and the number of bins
*/
private JToolBar getToolbar(final HistogramGraph histoGraph,
final String fieldName, final int binCount)
{
int spacing = 10;
String[] fieldNames = HistogramTable.getFieldNames(m_dataTable);
// create toolbar that allows displaying different histograms
JToolBar toolbar = new JToolBar();
toolbar.setLayout(new BoxLayout(toolbar, BoxLayout.X_AXIS));
toolbar.add(Box.createHorizontalStrut(spacing));
final JComboBox fieldsBox = new JComboBox(fieldNames);
if(fieldName == null)
{
fieldsBox.setSelectedItem(fieldNames[0]);
} else {
fieldsBox.setSelectedItem(fieldName);
}
fieldsBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String dataField = (String)fieldsBox.getSelectedItem();
histoGraph.updateAxes(dataField);
}
});
toolbar.add(fieldsBox);
toolbar.add(Box.createHorizontalStrut(2*spacing));
// I don't have a good reason for giving these particular values.
// Really this should be an editable combobox, but it wasn't
// important enough to me to do that. If you are interested in
// doing so, please send me the code and I'll fold it in. -- KDS
toolbar.add(new JLabel("# of bins: "));
String binCountString = ((Integer)binCount).toString();
String[] binCountsList = new String[6];
binCountsList[0] = "3";
binCountsList[1] = "5";
binCountsList[2] = "15";
binCountsList[3] = "40";
binCountsList[4] = "70";
binCountsList[5] = "100";
final JComboBox bcb = new JComboBox(binCountsList);
if(binCountString == null)
{
bcb.setSelectedItem(binCountsList[2]);
} else {
bcb.setSelectedItem(binCountString);
}
bcb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String binString = (String)bcb.getSelectedItem();
int binCount = Integer.parseInt(binString);
HistogramTable ht = new HistogramTable(m_dataTable, binCount);
String field = (String)fieldsBox.getSelectedItem();
HistogramGraph newHistogramGraph = new HistogramGraph(ht, field);
getContentPane().remove(histoGraph);
getContentPane().remove(m_toolbar);
m_toolbar = getToolbar(newHistogramGraph, field, binCount);
getContentPane().add(m_toolbar, BorderLayout.NORTH);
getContentPane().add(newHistogramGraph, BorderLayout.CENTER);
pack();
setVisible(true);
}
});
toolbar.add(bcb);
toolbar.add(Box.createHorizontalStrut(2*spacing));
return toolbar;
}
/**
* @param argv - first arg is the file name for the CSV data file,
* second arg is the bin count,
* third arg is the name of the field (column) to display first
*/
public static void main(String[] argv) {
String dataFileName = "/fisher.iris.txt";
String defaultFieldName = null;
int binCount = -1;
// if the file doesn't exist, it will fail later
if (argv.length > 0)
{
dataFileName = argv[0];
}
if (argv.length > 1)
{
try {
binCount = Integer.parseInt(argv[1]);
} catch (NumberFormatException e) {
System.err.println("Uh-oh. I don't know how to make an integer out of the argument \""+argv[1]+"\"");
printUsage();
System.exit(-5);
}
} else {
binCount = HistogramGraph.getDefaultBinCount();
}
// need to check if the field is valid later, after
// the data table has been created
if (argv.length > 2)
{
defaultFieldName = argv[2];
}
new HistogramFrame( dataFileName, defaultFieldName, binCount);
}
public static void printUsage() {
System.err.println("Usage: HistogramDemo [dataFileName] [binCount]");
}
}