我正在尝试构建一个图表应用程序作为大学项目,使用 JFreeChart 构建图表,使用 Swing 构建 UI。经过一些调试后,文件解析器正在正确读取数据,正在构建图表并向用户显示包含图表的面板。但是图表不包含数据。这是构建散点图的代码:
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.FastScatterPlot;
public class ScatterPlot extends Chart{
public ScatterPlot(String title, float[][] data) {
this.data = data;
//CSVParser.printArrayToConsole(this.data[1]);
NumberAxis domainAxis = new NumberAxis("X");
domainAxis.setAutoRangeIncludesZero(false);
NumberAxis rangeAxis = new NumberAxis("Y");
rangeAxis.setAutoRangeIncludesZero(true);
FastScatterPlot plot = new FastScatterPlot(this.data, domainAxis, rangeAxis);
JFreeChart chart = new JFreeChart("Fast Scatter Plot", plot);
ChartUtils.applyCurrentTheme(chart);
panel = new ChartPanel(chart);
panel.setPreferredSize(new java.awt.Dimension(500, 270));
panel.setMinimumDrawHeight(10);
panel.setMaximumDrawHeight(2000);
panel.setMinimumDrawWidth(20);
panel.setMaximumDrawWidth(2000);
}
}
这是继承自的 Chart 类:
import org.jfree.chart.ChartPanel;
import javax.swing.JPanel;
import org.jfree.chart.plot.FastScatterPlot;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.JFreeChart;
public class Chart {
float[][] data;
ChartPanel panel;
public Chart(){
initChartPanel();
}
private void initChartPanel(){
float[][] tempData = {{0}, {0}};
FastScatterPlot plot = new FastScatterPlot(tempData, new NumberAxis(""), new NumberAxis(""));
JFreeChart chart = new JFreeChart("If you're seeing this, something went wrong.", plot);
panel = new ChartPanel(chart);
}
}
这是我用于此输出的数据集(从 CSV 文件读取):
0,1,2,3,4,5,6,7,8,9,10,11
4.2,4,3.7,3.4,3.2,2.9,2.6,2.4,2.1,1.8,1.5,1.2
这是运行此代码的输出:
我已经尝试过:
作为旁注,ScatterPlot 类是由我的一位队友编写的,我非常确定他不是自己编写的,而是从教程中复制并粘贴的。我们同意由我构建 UI,由他负责图表构建方面的工作。我做了一些修改以适应我所拥有的,但这就是说我对 JFreeChart 不太熟悉,我相信他也不是,所以我不能向他寻求帮助。
如有任何帮助,我们将不胜感激。
编辑:ChartPanel 已添加到该类中的 MainFrame,这里是能够运行获取该输出的路径所需的所有类。
package javacharts;
import java.awt.*;
public class JavaCharts {
static GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
public static void main(String[] args) {
MainFrame mainFrame = new MainFrame();
device.setFullScreenWindow(mainFrame);
}
}
package javacharts;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.atomic.*;
public class MainFrame extends JFrame{
JPanel mainPanel;
JLabel title, choiceLabel;
JButton[] buttons;
JLabel[] buttonLabels;
GridBagConstraints constraints;
//These values need ot be atomic or []
//because the compiler complains about it being referenced from a lambda as an int
AtomicInteger graphType;
AtomicInteger inputType;
public MainFrame(){
graphType = new AtomicInteger();
inputType = new AtomicInteger();
this.setLayout(new BorderLayout());
mainPanel = new JPanel();
mainPanel.setLayout(new GridBagLayout());
constraints = new GridBagConstraints();
//declare big labels
title = new JLabel("Welcome to Java Charts", SwingConstants.CENTER);
title.setFont(title.getFont().deriveFont(48.0f));
choiceLabel = new JLabel("", SwingConstants.CENTER);
choiceLabel.setFont(choiceLabel.getFont().deriveFont(30.0f));
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 3;
mainPanel.add(choiceLabel, constraints);
//declare buttons, used for both graph type and input type
buttons = new JButton[3];
buttons[0] = new JButton();
buttons[1] = new JButton();
buttons[2] = new JButton();
//declare button labels
buttonLabels = new JLabel[3];
buttonLabels[0] = new JLabel("Scatter Plot", SwingConstants.CENTER);
buttonLabels[0].setFont(buttonLabels[0].getFont().deriveFont(18.0f));
buttonLabels[1] = new JLabel("Box Plot", SwingConstants.CENTER);
buttonLabels[1].setFont(buttonLabels[1].getFont().deriveFont(18.0f));
buttonLabels[2] = new JLabel("Ogive", SwingConstants.CENTER);
buttonLabels[2].setFont(buttonLabels[2].getFont().deriveFont(18.0f));
//add buttons to mainPanel
constraints.gridy = 1;
constraints.gridwidth = 1;
constraints.insets = new Insets(15, 10, 0, 10);
mainPanel.add(buttons[0], constraints);
constraints.gridx = 1;
mainPanel.add(buttons[1], constraints);
constraints.gridx = 2;
mainPanel.add(buttons[2], constraints);
//add button labels to main panel
constraints.gridy = 2;
constraints.gridx = 0;
constraints.insets.top = 0;
mainPanel.add(buttonLabels[0], constraints);
constraints.gridx = 1;
mainPanel.add(buttonLabels[1], constraints);
constraints.gridx = 2;
mainPanel.add(buttonLabels[2], constraints);
this.add(title, BorderLayout.NORTH);
this.add(mainPanel, BorderLayout.CENTER);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setUndecorated(true);
this.pack();
this.setVisible(true);
buildChartSelectionMenu();
}
public void buildChartSelectionMenu(){
choiceLabel.setText("Which type of graph would you like to build?");
//change icons of buttons
//IMPORTANT: change images to the actual outputted graphs at the end
ImageIcon icon1 = new ImageIcon(getClass().getResource("..\\images\\scatter_plot.jpeg"));
buttons[0].setIcon(icon1);
ImageIcon icon2 = new ImageIcon(getClass().getResource("..\\images\\box_plot.jpeg"));
buttons[1].setIcon(icon2);
ImageIcon icon3 = new ImageIcon(getClass().getResource("..\\images\\ogive.jpeg"));
buttons[2].setIcon(icon3);
//add action listeners to buttons
for(AtomicInteger i = new AtomicInteger(0); i.get() < buttons.length; i.incrementAndGet()){
int chartT = i.get();
buttons[i.get()].addActionListener((ActionEvent e) -> {
graphType.set(chartT);
buildInputSelectionMenu();
});
}
//change button labels
buttonLabels[0].setText("Scatter Plot");
buttonLabels[1].setText("Box Plot");
buttonLabels[2].setText("Ogive");
}
public void buildInputSelectionMenu(){
//remove all action listeners from buttons
for(int i = 0; i < buttons.length; ++i){
buttons[i].removeActionListener(buttons[i].getActionListeners()[0]);
}
//change icons of buttons
ImageIcon icon1 = new ImageIcon(getClass().getResource("../images/excel.jpeg"));
buttons[0].setIcon(icon1);
ImageIcon icon2 = new ImageIcon(getClass().getResource("../images/csv.jpeg"));
buttons[1].setIcon(icon2);
ImageIcon icon3 = new ImageIcon(getClass().getResource("../images/mouse_cursor.jpeg"));
buttons[2].setIcon(icon3);
//change labels
choiceLabel.setText("How would you like to build this graph?");
buttonLabels[0].setText("Excel file");
buttonLabels[1].setText("CSV file");
buttonLabels[2].setText("Input data manually");
//add new action listeners to buttons
for(AtomicInteger i = new AtomicInteger(0); i.get() < buttons.length; i.incrementAndGet()){
int inputT = i.get();
buttons[i.get()].addActionListener((ActionEvent e) -> {
inputType.set(inputT);
try{
ChartBuilder chartBuilder = new ChartBuilder(graphType.get(), inputType.get());
//System.out.println("Hello from mainfram");
getContentPane().removeAll();
super.add(chartBuilder.chart.panel);
} catch(Exception ex){
buildChartSelectionMenu();
ex.printStackTrace();
}
});
}
}
}
package javacharts;
import java.io.*;
import javax.swing.JOptionPane;
public class ChartBuilder {
int chartType;
int inputType;
File fileIn;
InputHandler inputHandler;
Chart chart;
//JFreeChart chart;
public ChartBuilder(int chartType, int inputType)throws FileNotFoundException{
//System.out.println(inputType);
float[][] data = new float[2][];
switch(inputType){
case 0:
break;
case 1:
inputHandler = new CSVParser();
if(inputHandler.file == null){
JOptionPane.showMessageDialog(null, "File is null",
"Error in ChartBuilder",
JOptionPane.ERROR_MESSAGE);
break;
}
data = inputHandler.parseData();
if(data == null){
JOptionPane.showMessageDialog(null, "CSVParser.parseData() failed to return valid array",
"Error in CSVParser",
JOptionPane.ERROR_MESSAGE);
break;
}
if(data[0] == null){
JOptionPane.showMessageDialog(null, "CSVParser.convertToFloarArray() failed to return valid array",
"Error in CSVParser",
JOptionPane.ERROR_MESSAGE);
break;
}
break;
case 2:
break;
}
switch(chartType){
case 0:
chart = new ScatterPlot("title", data);
break;
case 1:
break;
case 2:
break;
}
}
}
package javacharts;
import java.io.*;
import javax.swing.JOptionPane;
public class CSVParser extends InputHandler{
final public String EXTENSION = ".csv";
public CSVParser() throws FileNotFoundException{
try{
JOptionPane.showMessageDialog(null, "Make sure your CSV file contains only numerical data (no titles/labels)",
"Attention",
JOptionPane.WARNING_MESSAGE);
file = openFile();
} catch(Exception e){
JOptionPane.showMessageDialog(null, "Failed to open file",
"Error in CSVParser",
JOptionPane.ERROR_MESSAGE);
file = null;
}
boolean validFile = false;
try{
validFile = isValidFile(EXTENSION);
} catch(Exception e){
validFile = false;
}
if(!validFile){
JOptionPane.showMessageDialog(null, "Please select a CSV file",
"Invalid file type",
JOptionPane.ERROR_MESSAGE);
file = null;
}
}
@Override
public float[][] parseData() throws FileNotFoundException{
float[][] data = new float[2][];
BufferedReader fileReader = new BufferedReader(new FileReader(file));
try{
final String DELIMITER = ",";
String line;
int i = 0;
while((line = fileReader.readLine()) != null){
String[] lineData = line.split(DELIMITER);
//printArrayToConsole(lineData);
data[i] = convertToFloatArray(lineData);
//printArrayToConsole(data[i]);
++i;
}
} catch(Exception e){
JOptionPane.showMessageDialog(null, "Failed to read file",
"Error in CSVParser",
JOptionPane.ERROR_MESSAGE);
System.out.println(e);
return null;
}
return data;
}
public float[] convertToFloatArray(String[] arrIn){
float[] arrOut = new float[arrIn.length];
try{
for(int i = 0; i < arrIn.length; ++i){
arrOut[i] = Float.parseFloat(arrIn[i]);
}
} catch(Exception e){
JOptionPane.showMessageDialog(null, "Failed to convert all values to floats",
"Error in CSVParser",
JOptionPane.ERROR_MESSAGE);
System.out.println(e);
return null;
}
return arrOut;
}
public void printArrayToConsole(String[] arr){
System.out.println("String array");
for(int i = 0; i < arr.length; ++i){
System.out.println(arr[i]);
}
}
public static void printArrayToConsole(float[] arr){
System.out.println("Float Array");
for(int i = 0; i < arr.length; ++i){
System.out.println(arr[i]);
}
}
}
package javacharts;
import java.io.*;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
public abstract class InputHandler {
float[][] data = new float[2][];
File file;
String filePath;
public File openFile(){
JFrame frame = new JFrame();
JFileChooser chooser = new JFileChooser();
int retVal = chooser.showOpenDialog(frame);
if(retVal == JFileChooser.APPROVE_OPTION){
this.filePath = chooser.getSelectedFile().getAbsolutePath();
return chooser.getSelectedFile();
} else{
return null;
}
}
public boolean isValidFile(String extension){
return filePath.endsWith(extension) ? true : false;
}
abstract public float[][] parseData() throws FileNotFoundException;
}
显然我还有一些工作要做,因为我还没有构建其他 2 个 InputHandler 或实现其他 2 种图表类型,但到目前为止我所做的只是通过应该从 CSV 文件创建散点图的路径.
我希望代码不要太多,但这就是重现我的输出所需要的。
编辑 2:我通过更改整个 ScatterPlot 类以使用 XYDataset 和 ChartFactory.createScatterPlot() 方法而不是使用 FastScatterPlot 来使其工作。这就是该类现在的样子:
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartFactory;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
public class ScatterPlot extends Chart{
public ScatterPlot(String[] labels, float[][] data) {
super(labels, data);
XYDataset dataset = createDataset();
JFreeChart chart = ChartFactory.createScatterPlot(
labels[0], labels[1], labels[2],
dataset
);
setChartPanel(chart);
}
private XYDataset createDataset() {
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries series = new XYSeries("Series 1");
for(int i = 0; i < data[0].length; ++i){
series.add(data[0][i], data[1][i]);
}
dataset.addSeries(series);
return dataset;
}
}
仅关注图表的显示,下面的示例使用setData()
提供的
FastScatterPlot
方法原位更新图表。按 Update 按钮会调用
setData()
,并且收听图表会自行更新作为响应。您的 actionPerformed()
可以请求文件、解析其数据并根据需要更新绘图。
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.FastScatterPlot;
/**
* @see http://stackoverflow.com/questions/8531844
*/
public class FastScatterPlotDemo {
private static final int N = 10 * 1_000;
private static final Random rnd = new Random();
private void display() {
var f = new JFrame("Fast Scatter Plot");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
var domainAxis = new NumberAxis("X");
domainAxis.setAutoRangeIncludesZero(false);
NumberAxis rangeAxis = new NumberAxis("Y");
var plot = new FastScatterPlot(createData(), domainAxis, rangeAxis);
var chart = new JFreeChart(f.getTitle(), plot);
var panel = new ChartPanel(chart, true) {
@Override
public Dimension getPreferredSize() {
return new Dimension(16 * 32, 10 * 32);
}
};
f.add(panel);
var buttonPanel = new JPanel();
buttonPanel.add(new JButton(new AbstractAction("Update") {
@Override
public void actionPerformed(ActionEvent e) {
plot.setData(createData());
}
}));
f.add(buttonPanel, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private float[][] createData() {
float[][] data = new float[2][N];
for (int i = 0; i < data[0].length; i++) {
data[0][i] = (float) i;
data[1][i] = (float) rnd.nextGaussian() * N;
}
return data;
}
public static void main(final String[] args) {
EventQueue.invokeLater(new FastScatterPlotDemo()::display);
}
}