我负责用Java创建GUI来监控多个地区的人们的心率(出于保密原因进行修改)。
共有8个地区,每个地区包含8个人。这些由JTabbedPane指示,每个区域有1个选项卡。我正在尝试使用XChart库绘制和创建8个实时图形。数据将从文本文件中读取,并作为两个双数组传递。其中一个阵列是以毫秒为单位的时间(x轴),第二个阵列是1人(y轴)的心率数据。每个人都会在相应的区域标签中拥有自己的单独图表。
这类似于观察几个图表来监控医院中的生命体征,以便概述该应用程序应该模拟的内容。
我目前正在使用模型 - 视图 - 控制器架构。
TLDR; 为了将其分解,我只是尝试在JTabbedPane内的JPanel内的选项卡中添加多个实时XYChart。但就目前而言,只需添加 ONE 实时图表就可以了。
View.java
private void initComponents() {
jTabbedPane1 = new javax.swing.JTabbedPane();
tabRegion1 = new javax.swing.JPanel();
jTabbedPane1.setTabPlacement(javax.swing.JTabbedPane.BOTTOM);
tabRegion1.setLayout(new java.awt.GridLayout(4, 2));
// Populate the JPanel with empty static graphs
for(int i = 0; i < Global.MAX_GRAPHS; i++)
tabRegion1.add(Graph.getRandomStaticGraph());
jTabbedPane1.addTab("Region 1", tabRegion1);
}
Graph.java(模型)
// Testing purposes
public static JPanel getRandomStaticGraph() {
XYChart chart = getEmptyXYChart();
XYSeries series = chart.addSeries("HeartRate", null, getRandomWalk(200));
series.setMarker(SeriesMarkers.NONE);
return new XChartPanel(chart);
}
// Testing purposes
public static JPanel getRandomRealTimeGraph() throws InterruptedException {
XYChart chart = getEmptyXYChart();
// Show it
final SwingWrapper<XYChart> sw = new SwingWrapper<XYChart>(chart);
sw.displayChart();
double phase = 0;
double[][] initdata = getSineData(phase);
XYSeries series = chart.addSeries("HeartRate", null, getRandomWalk(200));
while(true) {
phase += 2 * Math.PI * 2 / 20.0;
Thread.sleep(100);
final double[][] data = getSineData(phase);
chart.updateXYSeries("sine", data[0], data[1], null);
sw.repaintChart();
}
}
// To be used to generate random static graphs (testing purposes)
private static double[] getRandomWalk(int numPoints) {
double[] y = new double[numPoints];
y[0] = 0;
for(int i = 1; i < y.length; i++)
y[i] = y[i-1] + Math.random() - .5;
return y;
}
// To be used to generate real time graphs (testing purposes)
private static double[][] getSineData(double phase) {
double[] xData = new double[100];
double[] yData = new double[100];
for(int i = 0; i < xData.length; i++) {
double radians = phase + (2 * Math.PI / xData.length * i);
xData[i] = radians;
yData[i] = Math.sin(radians);
}
return new double[][] {xData, yData};
}
资源和我在哪里看
http://knowm.org/how-to-make-real-time-charts-in-java/:这个网站帮助我为实时图创建了一个基类,但没有办法帮助我在JPanel中集成它,或者如何通过重绘()或更新使其“实时”化( )。不是说使用repaint()或update()是必要的。我几乎可以接受任何建议。
当前环境
- NetBeans IDE 8.2(用于GUI生成器)
- Java 1.8.0_111
- XChart JAR(http://knowm.org/open-source/xchart/)
附加说明
我只是不能调用getRandomRealTimeGraph()来返回JPanel甚至是XChartPanel,所以我可以将它添加到JTabbedPane。我会喜欢任何有用的建议!
如果这有帮助,数据已经存在并被解析为2个双精度数组。但就目前而言,我并不关心x数据和y数据的含义。只要我以后可以替代它,我对任何事情都很满意。
非常感谢你!
答案 0 :(得分:2)
好的,你需要退后一步,想想你想要实现的目标,然后你需要弄清楚如何使它适用于一个图表,如果你能使它适用于一个图表,你应该能够扩展它。
从一个简单的模型概念开始。该模型应抽象出数据,如果它来自文件,Web服务,数据库,串行端口或其他方式并不重要,解决方案的其他部分不应该&#但是,他们应该只关心以共同方式呈现的数据。
像...一样的东西。
public interface HeartMonitorModel {
public List<Double>[] getSineData();
public List<Double> getWalkData();
}
是的,您需要为每个图表单独建立一个模型,否则您将为每个图表显示相同的数据。
现在,为了论证,我只是简单地为您提供代码并生成一个简单的默认实现。
public class DefaultHeartMonitorModel implements HeartMonitorModel {
private double phase = 0;
private int numPoints = 200;
@Override
public List[] getSineData() {
phase += 2 * Math.PI * 2 / 20.0;
List<Double> xData = new ArrayList<>(100);
List<Double> yData = new ArrayList<>(100);
for (int i = 0; i < 100; i++) {
double radians = phase + (2 * Math.PI / 100 * i);
xData.add(radians);
yData.add(Math.sin(radians));
}
return new List[]{xData, yData};
}
@Override
public List getWalkData() {
List<Double> data = new ArrayList<>(numPoints);
data.add(0.0);
for (int i = 1; i < numPoints; i++) {
double last = data.get(i - 1);
data.add(last + + Math.random() - .5);
}
return data;
}
}
理论上,您应该能够创建尽可能多的实例,并且应该提供稍微随机的数据
现在,还有两件事我们需要,我们需要一些方法来显示图表数据和一些更新它的方法。
要更新我们的模型,我们需要以某种方式获取新数据,并以某种方式将其反馈给用户界面,而不会阻止事件调度线程
对此,我从一个简单的界面开始,充当桥梁
public interface ChartMonitor {
public HeartMonitorModel getModel();
public void updateData(List<Double>[] data);
}
然后SwingWorker
使用它来在后台询问新数据并将该信息反馈给EDT,以便在屏幕上安全地更新
public class UpdateWorker extends SwingWorker<Void, List<Double>[]> {
private ChartMonitor monitor;
private XYChart chart;
public UpdateWorker(ChartMonitor monitor) {
this.monitor = monitor;
}
@Override
protected Void doInBackground() throws Exception {
while (true) {
Thread.sleep(100);
publish(monitor.getModel().getSineData());
}
}
@Override
protected void process(List<List<Double>[]> chunks) {
for (List<Double>[] data : chunks) {
monitor.updateData(data);
}
}
}
最后,我们需要将它们全部显示出来......
public class SeriesChartPane extends JPanel implements ChartMonitor {
private HeartMonitorModel model;
private XYChart chart;
public SeriesChartPane(HeartMonitorModel model) {
this.model = model;
chart = new XYChartBuilder().width(400).height(200).title("Are you dead yet?").build();
List<Double> walkData = model.getWalkData();
chart.addSeries("Heart Rate", null, walkData);
List<Double>[] sineData = model.getSineData();
chart.addSeries("sine", sineData[0], sineData[1]);
setLayout(new BorderLayout());
XChartPanel<XYChart> chartPane = new XChartPanel<>(chart);
add(chartPane);
UpdateWorker worker = new UpdateWorker(this);
worker.execute();
}
@Override
public HeartMonitorModel getModel() {
return model;
}
@Override
public void updateData(List<Double>[] data) {
chart.updateXYSeries("sine", data[0], data[1], null);
repaint();
}
}
然后我们可以在自己的窗口中显示它......
public class HeartMonitor {
public static void main(String[] args) {
new HeartMonitor();
}
public HeartMonitor() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
HeartMonitorModel model = new DefaultHeartMonitorModel();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SeriesChartPane(model));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
请查看Concurrency in Swing和Worker Threads and SwingWorker,了解有关Swing和线程如何工作的详细信息
您可能还想对Model-View-Controller范例
进行更新