2008年10月1日星期三

JFreeChart的实时曲线图的例子

好久没更新过技术相关的东西了,今天要开始写管理控制台,里面需要用到实时曲线图,决定使用JFreeChart做,这东西还真的挺好用,简单的几句代码就能搭建一个看起来不错的程序:-)
预览图:



/*
* RealTimeGraphPanel.java
*
* Created on 2008年10月1日, 下午2:04
*/
package cn.bearice.ipcontroller.manager.graph;

import cn.bearice.java.util.StopableThread;
import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;

/**
*
* @author Bearice
*/
public class RealTimeGraphPanel extends javax.swing.JPanel {

private static final long serialVersionUID = 8250077948583513551L;
TimeSeriesCollection tcl;
ChartPanel cp;
JFreeChart jfc;
ArrayList tss = new ArrayList();
TimeSeries tts = new TimeSeries("Avg.", Second.class);

/** Creates new form RealTimeGraphPanel */
public RealTimeGraphPanel() {
tcl = new TimeSeriesCollection();
tcl.addSeries(tts);
addTimeSeries();
jfc = ChartFactory.createTimeSeriesChart("Random Numbers", "Time", "Value", tcl, true, true, false);
XYPlot xyplot = jfc.getXYPlot();
ValueAxis valueaxis = xyplot.getDomainAxis();
valueaxis.setAutoRange(true);
// valueaxis.setInverted(true);
valueaxis = xyplot.getRangeAxis();
valueaxis.setAutoRange(true);
cp = new ChartPanel(jfc);
initComponents();
}

public void addTimeSeries() {
TimeSeries timeSeries = new TimeSeries("Random" + tss.size(), Second.class);
tcl.addSeries(timeSeries);
tss.add(timeSeries);
}

public static void main(String[] args) {
JFrame frame = new JFrame("Test Chart");
final RealTimeGraphPanel rtgp = new RealTimeGraphPanel();
frame.getContentPane().add(rtgp, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);

frame.addWindowListener(new WindowAdapter() {

@Override
public void windowClosing(WindowEvent windowevent) {
System.exit(0);
}
});
}

/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
//
private void initComponents() {

jbtnStartStop = new javax.swing.JButton();
jPanel1 = (cp);
jbtnFixed = new javax.swing.JButton();
jbtnScaled = new javax.swing.JButton();
jbtnClear = new javax.swing.JButton();
jbtnAdd = new javax.swing.JButton();

setName("Form"); // NOI18N

jbtnStartStop.setText("Start");
jbtnStartStop.setName("jbtnStartStop"); // NOI18N
jbtnStartStop.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnStartStopActionPerformed(evt);
}
});

jPanel1.setName("jPanel1"); // NOI18N

javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 261, Short.MAX_VALUE)
);

jbtnFixed.setText("Fixed");
jbtnFixed.setName("jbtnFixed"); // NOI18N
jbtnFixed.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnFixedActionPerformed(evt);
}
});

jbtnScaled.setText("Scaled");
jbtnScaled.setName("jbtnScaled"); // NOI18N
jbtnScaled.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnScaledActionPerformed(evt);
}
});

jbtnClear.setText("Clear");
jbtnClear.setName("jbtnClear"); // NOI18N
jbtnClear.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnClearActionPerformed(evt);
}
});

jbtnAdd.setText("Add");
jbtnAdd.setName("jbtnAdd"); // NOI18N
jbtnAdd.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnAddActionPerformed(evt);
}
});

javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jbtnStartStop)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jbtnFixed)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jbtnScaled)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jbtnClear)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jbtnAdd)
.addContainerGap(53, Short.MAX_VALUE))
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jbtnStartStop)
.addComponent(jbtnFixed)
.addComponent(jbtnScaled)
.addComponent(jbtnClear)
.addComponent(jbtnAdd))
.addContainerGap())
);
}//


class T1 extends StopableThread {

@Override
protected void loop() throws InterruptedException {
for (TimeSeries timeSeries : tss) {
double d = randomNum();
num = (num + d) / 2;
timeSeries.add(new Second(), d);
}
tts.add(new Second(), num);
Thread.sleep(1000);
}
double num;
Random rand = new Random();

private double randomNum() {
return rand.nextGaussian();
}
}
T1 t1;
private void jbtnStartStopActionPerformed(java.awt.event.ActionEvent evt) {
if (t1 == null) {
t1 = new T1();
t1.start();
jbtnStartStop.setText("stop");
} else {
t1.exit();
t1 = null;
jbtnStartStop.setText("start");
}
}

private void jbtnFixedActionPerformed(java.awt.event.ActionEvent evt) {
// cp.setAutoscrolls(true);
XYPlot xyplot = jfc.getXYPlot();
ValueAxis valueaxis = xyplot.getDomainAxis();
valueaxis.setFixedAutoRange(50000);
}

private void jbtnScaledActionPerformed(java.awt.event.ActionEvent evt) {
XYPlot xyplot = jfc.getXYPlot();
ValueAxis valueaxis = xyplot.getDomainAxis();
valueaxis.setFixedAutoRange(0);
}

private void jbtnClearActionPerformed(java.awt.event.ActionEvent evt) {
for (TimeSeries timeSeries : tss) {
timeSeries.clear();
}
}

private void jbtnAddActionPerformed(java.awt.event.ActionEvent evt) {
addTimeSeries();
}
// Variables declaration - do not modify
private javax.swing.JPanel jPanel1;
private javax.swing.JButton jbtnAdd;
private javax.swing.JButton jbtnClear;
private javax.swing.JButton jbtnFixed;
private javax.swing.JButton jbtnScaled;
private javax.swing.JButton jbtnStartStop;
// End of variables declaration
}

PS:里面用到了一个自己实现的StopableThread,代码如下:

/*
* Copyrights (C) 2008 Bearice (Bearice@Gmail.com)
* Release under GNU/GPL Version 2.
*/
package cn.bearice.java.util;

/**
* 可以停止的线程。
* @author Bearice
*/
public abstract class StopableThread extends Thread {

protected boolean exit = false;

/**
* 线程开始后,进入循环前调用。
*/
protected void before() {
//System.out.println("Starting thread: "+this);
}

/**
* 线程退出前,循环退出后调用。
*/
protected void after() {
// System.out.println("Ending thread: "+this);
}

/**
* 循环体代码。
* @throws java.lang.InterruptedException
*/
protected abstract void loop() throws InterruptedException;

/**
* 指示线程退出循环。
*/
public synchronized void exit() {
exit = true;
interrupt();
}

@Override
public final void run() {
before();
while (!exit) {
try {
loop();
} catch (InterruptedException ex) {
}
}
after();
}
}

2 条评论:

  1. JFC……感觉比MFC只管多了,直逼WinForm……
    ps:考拉把这个评论放在页面内,顺便把验证码去掉~

    回复删除