Java/Swing Components/Chart
Chart Bean 2
The various table charting classes
<source lang="java">
/* Java Swing, 2nd Edition By Marc Loy, Robert Eckstein, Dave Wood, James Elliott, Brian Cole ISBN: 0-596-00408-7 Publisher: O"Reilly
- /
// ChartTester.java //A test harness for the various table charting classes. (see //ChartPainter.java, PieChartPainter.java, TableChart.java and //TableChartPopup.java) // import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.plaf.ruponentUI; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; public class ChartTester extends JFrame {
public ChartTester() { super("Simple JTable Test"); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); TableModel tm = new AbstractTableModel() { String data[][] = { { "Ron", "0.00", "68.68", "77.34", "78.02" }, { "Ravi", "0.00", "70.89", "64.17", "75.00" }, { "Maria", "76.52", "71.12", "75.68", "74.14" }, { "James", "70.00", "15.72", "26.40", "38.32" }, { "Ellen", "80.32", "78.16", "83.80", "85.72" } }; String headers[] = { "", "Q1", "Q2", "Q3", "Q4" }; public int getColumnCount() { return headers.length; } public int getRowCount() { return data.length; } public String getColumnName(int col) { return headers[col]; } public Class getColumnClass(int col) { return (col == 0) ? String.class : Number.class; } public boolean isCellEditable(int row, int col) { return true; } public Object getValueAt(int row, int col) { return data[row][col]; } public void setValueAt(Object value, int row, int col) { data[row][col] = (String) value; fireTableRowsUpdated(row, row); } }; JTable jt = new JTable(tm); JScrollPane jsp = new JScrollPane(jt); getContentPane().add(jsp, BorderLayout.CENTER); final TableChartPopup tcp = new TableChartPopup(tm); JButton button = new JButton("Show me a chart of this table"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { tcp.setVisible(true); } }); getContentPane().add(button, BorderLayout.SOUTH); } public static void main(String args[]) { ChartTester ct = new ChartTester(); ct.setVisible(true); }
} //TableChartPopup.java //A popup framework for showing a chart of table data. This class also //turns on tooltips for the chart. // class TableChartPopup extends JFrame {
public TableChartPopup(TableModel tm) { super("Table Chart"); setSize(300, 200); TableChart tc = new TableChart(tm); getContentPane().add(tc, BorderLayout.CENTER); // Use the next line to turn on tooltips: ToolTipManager.sharedInstance().registerComponent(tc); }
} //TableChart.java //A chart-generating class that uses the TableModel interface to get //its data. // class TableChart extends JComponent implements TableModelListener {
protected TableModel model; protected ChartPainter cp; protected double[] percentages; // pie slices protected String[] labels; // labels for slices protected String[] tips; // tooltips for slices protected java.text.NumberFormat formatter = java.text.NumberFormat .getPercentInstance(); public TableChart(TableModel tm) { setUI(cp = new PieChartPainter()); setModel(tm); } public void setTextFont(Font f) { cp.setTextFont(f); } public Font getTextFont() { return cp.getTextFont(); } public void setTextColor(Color c) { cp.setTextColor(c); } public Color getTextColor() { return cp.getTextColor(); } public void setColor(Color[] clist) { cp.setColor(clist); } public Color[] getColor() { return cp.getColor(); } public void setColor(int index, Color c) { cp.setColor(index, c); } public Color getColor(int index) { return cp.getColor(index); } public String getToolTipText(MouseEvent me) { if (tips != null) { int whichTip = cp.indexOfEntryAt(me); if (whichTip != -1) { return tips[whichTip]; } } return null; } public void tableChanged(TableModelEvent tme) { // Rebuild the arrays only if the structure changed. updateLocalValues(tme.getType() != TableModelEvent.UPDATE); } public void setModel(TableModel tm) { // get listener code correct. if (tm != model) { if (model != null) { model.removeTableModelListener(this); } model = tm; model.addTableModelListener(this); updateLocalValues(true); } } public TableModel getModel() { return model; } // Run through the model and count every cell (except the very first column, // which we assume is the slice label column). protected void calculatePercentages() { double runningTotal = 0.0; for (int i = model.getRowCount() - 1; i >= 0; i--) { percentages[i] = 0.0; for (int j = model.getColumnCount() - 1; j >= 0; j--) { // First try the cell as a Number object. Object val = model.getValueAt(i, j); if (val instanceof Number) { percentages[i] += ((Number) val).doubleValue(); } else if (val instanceof String) { // oops, it wasn"t numeric... // Ok, so try it as a string try { percentages[i] += Double.valueOf(val.toString()) .doubleValue(); } catch (Exception e) { // not a numeric string...give up. } } } runningTotal += percentages[i]; } // Make each entry a percentage of the total. for (int i = model.getRowCount() - 1; i >= 0; i--) { percentages[i] /= runningTotal; } } // This method just takes the percentages and formats them for use as // tooltips. protected void createLabelsAndTips() { for (int i = model.getRowCount() - 1; i >= 0; i--) { labels[i] = (String) model.getValueAt(i, 0); tips[i] = formatter.format(percentages[i]); } } // Call this method to update the chart. We try to be a bit efficient here // in that we allocate new storage arrays only if the new table has a // different number of rows. protected void updateLocalValues(boolean freshStart) { if (freshStart) { int count = model.getRowCount(); if ((tips == null) || (count != tips.length)) { percentages = new double[count]; labels = new String[count]; tips = new String[count]; } } calculatePercentages(); createLabelsAndTips(); // Now that everything"s up-to-date, reset the chart painter with the // new // values. cp.setValues(percentages); cp.setLabels(labels); // Finally, repaint the chart. repaint(); }
} //PieChartPainter.java //A pie chart implementation of the ChartPainter class. // class PieChartPainter extends ChartPainter {
protected static PieChartPainter chartUI = new PieChartPainter(); protected int originX, originY; protected int radius; private static double piby2 = Math.PI / 2.0; private static double twopi = Math.PI * 2.0; private static double d2r = Math.PI / 180.0; // Degrees to radians. private static int xGap = 5; private static int inset = 40; public int indexOfEntryAt(MouseEvent me) { int x = me.getX() - originX; int y = originY - me.getY(); // Upside-down coordinate system. // Is (x,y) in the circle? if (Math.sqrt(x * x + y * y) > radius) { return -1; } double percent = Math.atan2(Math.abs(y), Math.abs(x)); if (x >= 0) { if (y <= 0) { // (IV) percent = (piby2 - percent) + 3 * piby2; // (IV) } } else { if (y >= 0) { // (II) percent = Math.PI - percent; } else { // (III) percent = Math.PI + percent; } } percent /= twopi; double t = 0.0; if (values != null) { for (int i = 0; i < values.length; i++) { if (t + values[i] > percent) { return i; } t += values[i]; } } return -1; } public void paint(Graphics g, JComponent c) { Dimension size = c.getSize(); originX = size.width / 2; originY = size.height / 2; int diameter = (originX < originY ? size.width - inset : size.height - inset); radius = (diameter / 2) + 1; int cornerX = (originX - (diameter / 2)); int cornerY = (originY - (diameter / 2)); int startAngle = 0; int arcAngle = 0; for (int i = 0; i < values.length; i++) { arcAngle = (int) (i < values.length - 1 ? Math .round(values[i] * 360) : 360 - startAngle); g.setColor(colors[i % colors.length]); g.fillArc(cornerX, cornerY, diameter, diameter, startAngle, arcAngle); drawLabel(g, labels[i], startAngle + (arcAngle / 2)); startAngle += arcAngle; } g.setColor(Color.black); g.drawOval(cornerX, cornerY, diameter, diameter); // Cap the circle. } public void drawLabel(Graphics g, String text, double angle) { g.setFont(textFont); g.setColor(textColor); double radians = angle * d2r; int x = (int) ((radius + xGap) * Math.cos(radians)); int y = (int) ((radius + xGap) * Math.sin(radians)); if (x < 0) { x -= SwingUtilities.ruputeStringWidth(g.getFontMetrics(), text); } if (y < 0) { y -= g.getFontMetrics().getHeight(); } g.drawString(text, x + originX, originY - y); } public static ComponentUI createUI(JComponent c) { return chartUI; }
} //ChartPainter.java //A simple chart-drawing UI base class. This class tracks the basic fonts //and colors for various types of charts including pie and bar. The paint() //method is abstract and must be implemented by subclasses for each type. // abstract class ChartPainter extends ComponentUI {
protected Font textFont = new Font("Serif", Font.PLAIN, 12); protected Color textColor = Color.black; protected Color colors[] = new Color[] { Color.red, Color.blue, Color.yellow, Color.black, Color.green, Color.white, Color.gray, Color.cyan, Color.magenta, Color.darkGray }; protected double values[] = new double[0]; protected String labels[] = new String[0]; public void setTextFont(Font f) { textFont = f; } public Font getTextFont() { return textFont; } public void setColor(Color[] clist) { colors = clist; } public Color[] getColor() { return colors; } public void setColor(int index, Color c) { colors[index] = c; } public Color getColor(int index) { return colors[index]; } public void setTextColor(Color c) { textColor = c; } public Color getTextColor() { return textColor; } public void setLabels(String[] l) { labels = l; } public void setValues(double[] v) { values = v; } public abstract int indexOfEntryAt(MouseEvent me); public abstract void paint(Graphics g, JComponent c);
}
</source>