StripChart.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 19k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    StripChart.java
  18.  *    Copyright (C) 2002 Mark Hall
  19.  *
  20.  */
  21. package weka.gui.beans;
  22. import javax.swing.JPanel;
  23. import java.awt.Image;
  24. import java.awt.Graphics;
  25. import java.awt.BorderLayout;
  26. import java.awt.Color;
  27. import java.util.Random;
  28. import java.awt.image.CropImageFilter;
  29. import java.awt.image.FilteredImageSource;
  30. import javax.swing.JFrame;
  31. import java.util.Vector;
  32. import java.awt.Font;
  33. import java.awt.FontMetrics;
  34. import javax.swing.JLabel;
  35. import java.awt.Dimension;
  36. import javax.swing.BorderFactory;
  37. import javax.swing.border.TitledBorder;
  38. import javax.swing.border.EtchedBorder;
  39. import java.util.LinkedList;
  40. import java.io.ObjectInputStream;
  41. import java.io.IOException;
  42. import java.util.Enumeration;
  43. import java.io.Serializable;
  44. import weka.core.Queue;
  45. /**
  46.  * Bean that can display a horizontally scrolling strip chart. Can
  47.  * display multiple plots simultaneously
  48.  *
  49.  * @author <a href="mailto:mhall@cs.waikato.ac.nz">Mark Hall</a>
  50.  * @version $Revision: 1.3 $
  51.  */
  52. public class StripChart 
  53.   extends JPanel 
  54.   implements ChartListener, Visible, 
  55.      BeanCommon, UserRequestAcceptor, 
  56.      Serializable {
  57.   
  58.   /** default colours for colouring lines */
  59.   protected Color [] m_colorList = {Color.green,
  60.     Color.red,
  61.     Color.blue,
  62.     Color.cyan,
  63.     Color.pink,
  64.     new Color(255, 0, 255),
  65.     Color.orange,
  66.     new Color(255, 0, 0),
  67.     new Color(0, 255, 0),
  68.     Color.white};
  69.   // Class providing a panel for the plot
  70.   private class StripPlotter extends JPanel {
  71.     public void paintComponent(Graphics g) {
  72.       super.paintComponent(g);
  73.       if (m_osi != null) {
  74. g.drawImage(m_osi,0,0,this);
  75.       }
  76.     }
  77.   }
  78.   private transient JFrame m_outputFrame = null;
  79.   private transient StripPlotter m_plotPanel = null;
  80.   private transient Image m_osi = null;
  81.   /**
  82.    * Max value for the y axis
  83.    */
  84.   private double m_max = 1;
  85.   /**
  86.    * Min value for the y axis
  87.    */
  88.   private double m_min = 0;
  89.   /**
  90.    * Scale update requested
  91.    */
  92.   private boolean m_yScaleUpdate = false;
  93.   private double m_oldMax;
  94.   private double m_oldMin;
  95.   private Font m_labelFont = new Font("Monospaced", Font.PLAIN, 10);
  96.   private FontMetrics m_labelMetrics;
  97.   //  private int m_plotCount = 0;
  98.   private Vector m_legendText = new Vector();
  99.   /**
  100.    * Class providing a panel for displaying the y axis
  101.    */
  102.   private JPanel m_scalePanel = new JPanel() {
  103.       public void paintComponent(Graphics gx) {
  104. super.paintComponent(gx);
  105. if (m_labelMetrics == null) {
  106.   m_labelMetrics = gx.getFontMetrics(m_labelFont);
  107. }
  108. gx.setFont(m_labelFont);
  109. int hf = m_labelMetrics.getAscent();
  110. String temp = ""+m_max;
  111. gx.setColor(m_colorList[m_colorList.length-1]);
  112. gx.drawString(temp, 1, hf-2);
  113. temp = ""+(m_min + ((m_max - m_min)/2.0));
  114. gx.drawString(temp, 1, (this.getHeight() / 2)+(hf / 2));
  115. temp = ""+m_min;
  116. gx.drawString(temp, 1, this.getHeight()-1);
  117.       }
  118.     };
  119.   /**
  120.    * Class providing a panel for the legend
  121.    */
  122.   private JPanel m_legendPanel = new JPanel() {
  123.       public void paintComponent(Graphics gx) {
  124. super.paintComponent(gx);
  125. if (m_labelMetrics == null) {
  126.   m_labelMetrics = gx.getFontMetrics(m_labelFont);
  127. }
  128. int hf = m_labelMetrics.getAscent();
  129. int x = 10; int y = hf+15;
  130. gx.setFont(m_labelFont);
  131. for (int i = 0; i < m_legendText.size(); i++) {
  132.   String temp = (String)m_legendText.elementAt(i);
  133.   gx.setColor(m_colorList[(i % m_colorList.length)]);
  134.   gx.drawString(temp,x,y);
  135.   y+=hf;
  136. }
  137. StripChart.this.revalidate();
  138.       }
  139.     };
  140.   /**
  141.    * Holds the data to be plotted. Entries in the list are arrays of
  142.    * y points.
  143.    */
  144.   private LinkedList m_dataList = new LinkedList();
  145.   //  private double [] m_dataPoint = new double[1];
  146.   private double [] m_previousY = new double[1];
  147.   private transient Thread m_updateHandler;
  148.   protected BeanVisual m_visual = 
  149.     new BeanVisual("StripChart", 
  150.    BeanVisual.ICON_PATH+"StripChart.gif",
  151.    BeanVisual.ICON_PATH+"StripChart_animated.gif");
  152.   private Object m_listenee = null;
  153.   private transient weka.gui.Logger m_log = null;
  154.   /**
  155.    * Print x axis labels every m_xValFreq points
  156.    */
  157.   private int m_xValFreq = 500;
  158.   private int m_xCount = 0;
  159.   /**
  160.    * Shift the plot by this many pixels every time a point is plotted
  161.    */
  162.   private int m_refreshWidth = 1;
  163.   /**
  164.    * Plot every m_refreshFrequency'th point
  165.    */
  166.   private int m_refreshFrequency = 5;
  167.   public StripChart() {
  168.     //    m_plotPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
  169.     setLayout(new BorderLayout());
  170.     add(m_visual, BorderLayout.CENTER);
  171.     // start a thread to handle plot updates
  172.     initPlot();
  173.   }
  174.   /**
  175.    * GUI Tip text
  176.    *
  177.    * @return a <code>String</code> value
  178.    */
  179.   public String xLabelFreqTipText() {
  180.     return "Show x axis labels this often";
  181.   }
  182.   /**
  183.    * Set the frequency for printing x label values
  184.    *
  185.    * @param freq an <code>int</code> value
  186.    */
  187.   public void setXLabelFreq(int freq) {
  188.     m_xValFreq = freq;
  189.     setRefreshWidth();
  190.   }
  191.   /**
  192.    * Get the frequency by which x axis values are printed
  193.    *
  194.    * @return an <code>int</code> value
  195.    */
  196.   public int getXLabelFreq() {
  197.     return m_xValFreq;
  198.   }
  199.   /**
  200.    * GUI Tip text
  201.    *
  202.    * @return a <code>String</code> value
  203.    */
  204.   public String refreshFreqTipText() {
  205.     return "Plot every x'th data point";
  206.   }
  207.   /**
  208.    * Set how often (in x axis points) to refresh the display
  209.    *
  210.    * @param freq an <code>int</code> value
  211.    */
  212.   public void setRefreshFreq(int freq) {
  213.     m_refreshFrequency = freq;
  214.     setRefreshWidth();
  215.   }
  216.   /**
  217.    * Get the refresh frequency
  218.    *
  219.    * @return an <code>int</code> value
  220.    */
  221.   public int getRefreshFreq() {
  222.     return m_refreshFrequency;
  223.   }
  224.   private void setRefreshWidth() {
  225.     m_refreshWidth = 1;
  226.     if (m_labelMetrics == null) {
  227.       getGraphics().setFont(m_labelFont);
  228.       m_labelMetrics = getGraphics().getFontMetrics(m_labelFont);
  229.     }
  230.     int refWidth = m_labelMetrics.stringWidth("99000");
  231.     // compute how often x label will be rendered
  232.     int z = (getXLabelFreq() / getRefreshFreq());
  233.     if (z < 1) {
  234.       z = 1;
  235.     }
  236.     
  237.     if (z * m_refreshWidth < refWidth+5) {
  238.       m_refreshWidth *= (((refWidth+5) / z) + 1) ;
  239.     }
  240.   }
  241.   /*  public void setXValFreq(int newFreq) {
  242.     m_xValFreq = newFreq;
  243.   }
  244.   
  245.   public int getXValFreq() {
  246.     return m_xValFreq;
  247.     }*/
  248.   /**
  249.    * Provide some necessary initialization after object has
  250.    * been deserialized.
  251.    *
  252.    * @param ois an <code>ObjectInputStream</code> value
  253.    * @exception IOException if an error occurs
  254.    * @exception ClassNotFoundException if an error occurs
  255.    */
  256.   private void readObject(ObjectInputStream ois)
  257.     throws IOException, ClassNotFoundException {
  258.     try {
  259.       ois.defaultReadObject();
  260.       initPlot();
  261.       //      startHandler();
  262.     } catch (Exception ex) {
  263.       ex.printStackTrace();
  264.     }
  265.   }
  266.   private void initPlot() {
  267.     m_plotPanel = new StripPlotter();
  268.     m_plotPanel.setBackground(Color.black);
  269.     m_scalePanel.setBackground(Color.black);
  270.     m_legendPanel.setBackground(Color.black);
  271.     m_xCount = 0;
  272.   }
  273.   private void startHandler() {
  274.     if (m_updateHandler == null) {
  275.       m_updateHandler = new Thread() {
  276.   private double [] dataPoint;
  277.   public void run() {
  278.     while (true) {
  279.       if (m_outputFrame != null) {
  280. synchronized(m_dataList) {
  281.   while(m_dataList.isEmpty()) {
  282.   //   while (m_dataList.empty()) {
  283.     try {
  284.       m_dataList.wait();
  285.     } catch (InterruptedException ex) {
  286.       return;
  287.     }
  288.   }
  289.   dataPoint = (double [])m_dataList.remove(0);
  290.   //dataPoint  = (double [])m_dataList.pop();
  291. }
  292. if (m_outputFrame != null) {
  293.   StripChart.this.updateChart(dataPoint);
  294. }
  295.       }
  296.     }
  297.   }
  298. };
  299.       //      m_updateHandler.setPriority(Thread.MIN_PRIORITY);
  300.       m_updateHandler.start();
  301.     }
  302.   }
  303.   /**
  304.    * Popup the chart panel
  305.    */
  306.   public void showChart() {
  307.     if (m_outputFrame == null) {      
  308.       m_outputFrame = new JFrame("Strip Chart");
  309.       m_outputFrame.getContentPane().setLayout(new BorderLayout());
  310.       m_outputFrame.getContentPane().add(m_legendPanel, BorderLayout.WEST);
  311.       m_outputFrame.getContentPane().add(m_plotPanel, BorderLayout.CENTER);
  312.       m_outputFrame.getContentPane().add(m_scalePanel, BorderLayout.EAST);
  313.       m_legendPanel.setMinimumSize(new Dimension(100,getHeight()));
  314.       m_legendPanel.setPreferredSize(new Dimension(100,getHeight()));
  315.       m_scalePanel.setMinimumSize(new Dimension(30, getHeight()));
  316.       m_scalePanel.setPreferredSize(new Dimension(30, getHeight()));
  317.       Font lf = new Font("Monospaced", Font.PLAIN, 12);
  318.       m_legendPanel.setBorder(BorderFactory.
  319.       createTitledBorder(BorderFactory.
  320.     createEtchedBorder(Color.gray,
  321.        Color.darkGray),
  322.     "Legend" ,
  323.     TitledBorder.CENTER,
  324.     TitledBorder.DEFAULT_POSITION, lf, 
  325.     Color.blue));
  326.       m_outputFrame.addWindowListener(new java.awt.event.WindowAdapter() {
  327.   public void windowClosing(java.awt.event.WindowEvent e) {
  328.     if (m_updateHandler != null) {
  329.       System.err.println("Interrupting");
  330.       m_updateHandler.interrupt();
  331.       m_updateHandler = null;
  332.     }
  333.     synchronized (m_dataList) {
  334.       m_dataList = new LinkedList();
  335.     }
  336.     m_outputFrame.dispose();
  337.     m_outputFrame = null;
  338.   }
  339. });
  340.       m_outputFrame.pack();
  341.       m_outputFrame.setSize(600,150);
  342.       m_outputFrame.setResizable(false);
  343.       m_outputFrame.setVisible(true);
  344.       int iwidth = m_plotPanel.getWidth();
  345.       int iheight = m_plotPanel.getHeight();
  346.       m_osi = m_plotPanel.createImage(iwidth, iheight);
  347.       Graphics m = m_osi.getGraphics();
  348.       m.fillRect(0,0,iwidth,iheight);
  349.       m_previousY[0] = convertToPanelY(0);
  350.       setRefreshWidth();
  351.       if (m_updateHandler == null) {
  352. System.err.println("Starting handler");
  353. startHandler();
  354.       }
  355.     }
  356.   }
  357.   private int convertToPanelY(double yval) {
  358.     int height = m_plotPanel.getHeight(); 
  359.     double temp = (yval - m_min) / (m_max - m_min);
  360.     temp = temp * height;
  361.     temp = height - temp;
  362.     return (int)temp;
  363.   }
  364.   /**
  365.    * Update the plot
  366.    *
  367.    * @param dataPoint contains y values to plot
  368.    */
  369.   protected void updateChart(double [] dataPoint) {
  370.     int iwidth = m_plotPanel.getWidth();
  371.     int iheight = m_plotPanel.getHeight();
  372.     //    System.err.println(dataPoint[0]);
  373.     if (dataPoint.length-1 != m_previousY.length) {
  374.       m_previousY = new double [dataPoint.length-1];
  375.       //      m_plotCount = 0;
  376.       for (int i = 0; i < dataPoint.length-1; i++) {
  377. m_previousY[i] = convertToPanelY(0);
  378.       }
  379.     }
  380.     Graphics osg = m_osi.getGraphics();
  381.     
  382.     Graphics g = m_plotPanel.getGraphics();
  383.     // paint the old scale onto the plot if a scale update has occured
  384.     if (m_yScaleUpdate) {
  385.       String maxVal = numToString(m_oldMax);
  386.       String minVal = numToString(m_oldMin);
  387.       String midVal = numToString((m_oldMax - m_oldMin) / 2.0);
  388.       if (m_labelMetrics == null) {
  389. m_labelMetrics = g.getFontMetrics(m_labelFont);
  390.       }
  391.       osg.setFont(m_labelFont);
  392.       int wmx = m_labelMetrics.stringWidth(maxVal);
  393.       int wmn = m_labelMetrics.stringWidth(minVal);
  394.       int wmd = m_labelMetrics.stringWidth(midVal);
  395.       int hf = m_labelMetrics.getAscent();
  396.       osg.setColor(m_colorList[m_colorList.length-1]);
  397.       osg.drawString(maxVal, iwidth-wmx, hf-2);
  398.       osg.drawString(midVal, iwidth-wmd, (iheight / 2)+(hf / 2));
  399.       osg.drawString(minVal, iwidth-wmn, iheight-1);
  400.       m_yScaleUpdate = false;
  401.       System.err.println("Here");
  402.     }
  403.     osg.copyArea(m_refreshWidth,0,iwidth-m_refreshWidth,
  404.  iheight,-m_refreshWidth,0);
  405.     osg.setColor(Color.black);
  406.     osg.fillRect(iwidth-m_refreshWidth,0, iwidth, iheight);
  407.     double pos;
  408.     for (int i = 0; i < dataPoint.length-1; i++) {
  409.       osg.setColor(m_colorList[(i % m_colorList.length)]);
  410.       pos = convertToPanelY(dataPoint[i]);
  411.       osg.drawLine(iwidth-m_refreshWidth, (int)m_previousY[i], 
  412.    iwidth-1, (int)pos);
  413.       m_previousY[i] = pos;
  414.       if (dataPoint[dataPoint.length-1] % m_xValFreq == 0) {
  415. // draw the actual y value onto the plot for this curve
  416. String val = numToString(dataPoint[i]);
  417. if (m_labelMetrics == null) {
  418.   m_labelMetrics = g.getFontMetrics(m_labelFont);
  419. }
  420. int hf = m_labelMetrics.getAscent();
  421. if (pos - hf < 0) {
  422.   pos += hf;
  423. }
  424. int w = m_labelMetrics.stringWidth(val);
  425. osg.setFont(m_labelFont);
  426. osg.drawString(val, iwidth-w, (int)pos);
  427.       }
  428.     }
  429.     
  430.     // last element in the data point array contains the data point number
  431.     if (dataPoint[dataPoint.length-1] % m_xValFreq == 0) {
  432.       String xVal = ""+(int)dataPoint[dataPoint.length-1];
  433.       osg.setColor(m_colorList[m_colorList.length-1]);
  434.       int w = m_labelMetrics.stringWidth(xVal);
  435.       osg.setFont(m_labelFont);
  436.       osg.drawString(xVal, iwidth-w, iheight - 1);
  437.     }
  438.     g.drawImage(m_osi,0,0,m_plotPanel);
  439.     //    System.err.println("Finished");
  440.     //    m_plotCount++;
  441.   }
  442.   private static String numToString(double num) {
  443.     int precision = 1;
  444.     int whole = (int)Math.abs(num);
  445.     double decimal = Math.abs(num) - whole;
  446.     int nondecimal;
  447.     nondecimal = (whole > 0) 
  448.       ? (int)(Math.log(whole) / Math.log(10))
  449.       : 1;
  450.     
  451.     precision = (decimal > 0) 
  452.       ? (int)Math.abs(((Math.log(Math.abs(num)) / 
  453.       Math.log(10))))+2
  454.       : 1;
  455.     if (precision > 5) {
  456.       precision = 1;
  457.     }
  458.     String numString = weka.core.Utils.doubleToString(num,
  459.       nondecimal+1+precision
  460.       ,precision);
  461.     
  462.     return numString;
  463.   }
  464.   /**
  465.    * Accept a data point (encapsulated in a chart event) to plot
  466.    *
  467.    * @param e a <code>ChartEvent</code> value
  468.    */
  469.   public void acceptDataPoint(ChartEvent e) {
  470.     if (e.getReset()) {
  471.       m_xCount = 0;
  472.       m_max = 1;
  473.       m_min = 0;
  474.     }
  475.     if (m_outputFrame != null) {
  476.       boolean refresh = false;
  477.       if (e.getLegendText() != null & e.getLegendText() != m_legendText) {
  478. m_legendText = e.getLegendText();
  479. refresh = true;
  480.       }
  481.       
  482.       if (e.getMin() != m_min || e.getMax() != m_max) {
  483. m_oldMax = m_max; m_oldMin = m_min;
  484. m_max = e.getMax();
  485. m_min = e.getMin();
  486. refresh = true;
  487. m_yScaleUpdate = true;
  488.       }
  489.     
  490.       if (refresh) {
  491. m_legendPanel.repaint();
  492. m_scalePanel.repaint();
  493.       }
  494.       
  495.       acceptDataPoint(e.getDataPoint());
  496.     }
  497.     m_xCount++;
  498.   }
  499.   /**
  500.    * Accept a data point to plot
  501.    *
  502.    * @param dataPoint a <code>double[]</code> value
  503.    */
  504.   public void acceptDataPoint(double [] dataPoint) {
  505.  
  506.     if (m_outputFrame != null && (m_xCount % m_refreshFrequency == 0 )) {
  507.       double [] dp = new double[dataPoint.length+1];
  508.       dp[dp.length-1] = m_xCount;
  509.       System.arraycopy(dataPoint, 0, dp, 0, dataPoint.length);
  510.       // check for out of scale values
  511.       for (int i = 0; i < dataPoint.length; i++) {
  512. if (dataPoint[i] < m_min) {
  513.   m_oldMin = m_min; m_min = dataPoint[i];
  514.   m_yScaleUpdate = true;
  515. }
  516. if (dataPoint[i] > m_max) {
  517.   m_oldMax = m_max; m_max = dataPoint[i];
  518.   m_yScaleUpdate = true;
  519. }
  520.       }
  521.       synchronized(m_dataList) {
  522. m_dataList.add(m_dataList.size(), dp);
  523. // m_dataList.push(dp);
  524. m_dataList.notifyAll();
  525. /* if (m_dataList.size() != 0) {
  526.   System.err.println("***** "+m_dataList.size());
  527.   } */
  528. //      System.err.println(m_xCount);
  529.       }
  530.     }
  531.   }
  532.   /**
  533.    * Set the visual appearance of this bean
  534.    *
  535.    * @param newVisual a <code>BeanVisual</code> value
  536.    */
  537.   public void setVisual(BeanVisual newVisual) {
  538.     m_visual = newVisual;
  539.   }
  540.   /**
  541.    * Get the visual appearance of this bean
  542.    */
  543.   public BeanVisual getVisual() {
  544.     return m_visual;
  545.   }
  546.   
  547.   /**
  548.    * Use the default visual appearance for this bean
  549.    */
  550.   public void useDefaultVisual() {
  551.     m_visual.loadIcons(BeanVisual.ICON_PATH+"StripChart.gif",
  552.        BeanVisual.ICON_PATH+"StripChart_animated.gif");
  553.   }
  554.   /**
  555.    * Stop any processing that the bean might be doing.
  556.    */
  557.   public void stop() {
  558.     // nothing to be done
  559.   }
  560.   /**
  561.    * Set a logger
  562.    *
  563.    * @param logger a <code>weka.gui.Logger</code> value
  564.    */
  565.   public void setLog(weka.gui.Logger logger) {
  566.     m_log = logger;
  567.   }
  568.   /**
  569.    * Returns true if, at this time, 
  570.    * the object will accept a connection via the named event
  571.    *
  572.    * @param eventName the name of the event
  573.    * @return true if the object will accept a connection
  574.    */
  575.   public boolean connectionAllowed(String eventName) {
  576.     if (m_listenee == null) {
  577.       return true;
  578.     }
  579.     return false;
  580.   }
  581.   /**
  582.    * Notify this object that it has been registered as a listener with
  583.    * a source for recieving events described by the named event
  584.    * This object is responsible for recording this fact.
  585.    *
  586.    * @param eventName the event
  587.    * @param source the source with which this object has been registered as
  588.    * a listener
  589.    */
  590.   public void connectionNotification(String eventName, Object source) {
  591.     if (connectionAllowed(eventName)) {
  592.       m_listenee = source;
  593.     }
  594.   }
  595.   
  596.   /**
  597.    * Notify this object that it has been deregistered as a listener with
  598.    * a source for named event. This object is responsible
  599.    * for recording this fact.
  600.    *
  601.    * @param eventName the event
  602.    * @param source the source with which this object has been registered as
  603.    * a listener
  604.    */
  605.   public void disconnectionNotification(String eventName, Object source) {
  606.     m_listenee = null;
  607.   }
  608.   /**
  609.    * Describe <code>enumerateRequests</code> method here.
  610.    *
  611.    * @return an <code>Enumeration</code> value
  612.    */
  613.   public Enumeration enumerateRequests() {
  614.     Vector newVector = new Vector(0);
  615.     if (m_outputFrame == null) {
  616.       newVector.addElement("Show chart");
  617.     }
  618.     return newVector.elements();
  619.   }
  620.   /**
  621.    * Describe <code>performRequest</code> method here.
  622.    *
  623.    * @param request a <code>String</code> value
  624.    * @exception IllegalArgumentException if an error occurs
  625.    */
  626.   public void performRequest(String request) throws IllegalArgumentException {
  627.     if (request.compareTo("Show chart") == 0) {
  628.       showChart();
  629.     } else {
  630.       throw new 
  631. IllegalArgumentException(request
  632.  + " not supported (StripChart)");
  633.     }
  634.   }
  635.   /**
  636.    * Tests out the StripChart from the command line
  637.    *
  638.    * @param args ignored
  639.    */
  640.   public static void main(String [] args) {
  641.     try {
  642.       final javax.swing.JFrame jf =
  643. new javax.swing.JFrame("Weka Knowledge Flow : StipChart");
  644.       jf.getContentPane().setLayout(new BorderLayout());
  645.       final StripChart jd = new StripChart();
  646.       jf.getContentPane().add(jd, BorderLayout.CENTER);
  647.       jf.addWindowListener(new java.awt.event.WindowAdapter() {
  648. public void windowClosing(java.awt.event.WindowEvent e) {
  649.   jf.dispose();
  650.   System.exit(0);
  651. }
  652.       });
  653.       jf.pack();
  654.       jf.setVisible(true);
  655.       jd.showChart();
  656.       Random r = new Random(1);
  657.       for (int i = 0; i < 1020; i++) {
  658. double [] pos = new double[1];
  659. pos[0] = r.nextDouble();
  660. jd.acceptDataPoint(pos);
  661.       }
  662.       System.err.println("Done sending data");
  663.     } catch (Exception ex) {
  664.       ex.printStackTrace();
  665.       System.err.println(ex.getMessage());
  666.     }
  667.   }
  668. }