dgteixeira
dgteixeira

Reputation: 61

Update a XY line JFreeChart dinamically with different refresh rates

I'm currently drawing a JFreeChart that I want to print with different "refresh rates". At the moment, I can print it with "one shot", however I haven't been able to print it dinamically. The refresh rate I have it as an int value saved in my code.

Before printing, I make all of the calculations and then print it. What I am trying to do now is print while calculating. As soon as I get the value of one point, print it, and so on.

My calculation code is as follows:

XYLineChart_AWT chartTemp = new XYLineChart_AWT();
int refreshRate = getRefreshRate();
for (int i = 0; i<MaxValue;i++) {
   //calculate values of Array1, Array2 and Array3
   chart1.setChart(chartTemp.runSimGraph("Title", "XLabel", "YLabel",true, new double[][]{Array1,Array2,Array3}));
}

However, this does only prints the JFreeChart at the end of the for-loop (tried it with Thread.sleep() method before the ending of each for iteration).

How may I be able to print the graph dinamically? Do I need to update the dataset as I calculate the values? If so, how can I do that?

EDIT: I've created a small verifiable example of what I want to fulfill. If I press the button, instead of the chart showing up as the calculations are made, it just shows up when it is finished. I want a way for it to show up point by point after its calculation. And yes, calling setChart() every iteration is wildly unefficient.

Code: Test1.java

 package cenas;

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import net.miginfocom.swing.MigLayout;

public class Test1 extends JFrame 
{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    /**
     * 
     */
    private JTabbedPane tabbedBackground;



    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Test2 secondFrame = new Test2();
                    Test1 mainFrame = new Test1(secondFrame);
                    mainFrame.setVisible(true);
                    secondFrame.setVisible(true);
                    secondFrame.setLocationRelativeTo(null);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public Dimension getPreferredSize() {
        // given some values of w & h
        return new Dimension(1000, 650);
    }

    /**
     * Create the frame.
     */
    public Test1(Test2 secondFrame) {

        this.setLocationByPlatform(true);
        setTitle("IMESS Simulator");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setMinimumSize(new Dimension(1000, 650));


        tabbedBackground = new JTabbedPane(JTabbedPane.TOP);
        setContentPane(tabbedBackground);
        initComponentsandRunSimulator(secondFrame);
        pack();
        //setResizable(false);

    }

    private void initComponentsandRunSimulator(Test2 frame2) {


        JPanel panel1 = new JPanel();
        tabbedBackground.addTab("Strategy and Results", null, panel1, null);
        tabbedBackground.setEnabledAt(0,true);
        panel1.setLayout(new MigLayout("", "[400.00px,grow]20[300px,grow]20[300.00px,grow]", "[40px,grow 20][][][][][100px,grow]20[20px]20[250.00px,grow]"));


        JButton myButton = new JButton("Button - Press me");
        myButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                frame2.setComponentsLists(100);
            }
        });

        //Add Simulation Panel to the layout
        panel1.add(myButton, "cell 1 1 2 5,grow");
    }

}

Test2.java

package cenas;

import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import org.jfree.chart.ChartPanel;


import net.miginfocom.swing.MigLayout;

public class Test2 extends JFrame{


    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private JTabbedPane tabbedBackground2;
    private ChartPanel chart1 = new ChartPanel (null);
    private int value;



    @Override
    public Dimension getPreferredSize() {
        // given some values of w & h
        return new Dimension(1000, 650);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Test2 frame = new Test2();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public void setComponentsLists(int refreshRate) {
        this.value=refreshRate;
        System.out.println("ola");
        simulateValues(refreshRate);
    }

    public Test2()  {
        setTitle("IMESS Simulator - Decision System");
        setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
        setLocationRelativeTo(null);
        setMinimumSize(new Dimension(1000, 650));

        tabbedBackground2 = new JTabbedPane(JTabbedPane.TOP);
        setContentPane(tabbedBackground2);
        initComponents();
        pack();
    }

private void initComponents() {

        // Panel
        JPanel panelSimulation = new JPanel();
        tabbedBackground2.addTab("Strategy and Results", null, panelSimulation, null);
        tabbedBackground2.setEnabledAt(0,true);
        panelSimulation.setLayout(new MigLayout("", "[400.00px,grow]20[300px,grow]20[300.00px,grow]", "[40px,grow 20][][][][][100px,grow]20[20px]20[250.00px,grow]"));

        chart1.setPopupMenu(null);
        chart1.setVisible(true);

        //Add Simulation Panel to the layout
        panelSimulation.add(chart1, "cell 0 2");
    }

private void simulateValues(int sliderValue) {



    double[] Array1 = new double[1440];
    double[] Array2 = new double[1440];
    double[] Array3 = new double[1440];
    for(int i = 0; i<1440; i++) {
        Array1[i]=0;
        Array2[i]=0;
        Array3[i]=0;
    }

    int peaks = 0;



    GraphTest chartTemp = new GraphTest();


    //Simulation for 24h (1 point per minute)
    for(int i = 0; i<1440; i++) {
        //Some calculations 
        if(i!=0)
            Array1[i]=Array1[i-1];

        if (((i>5) && (i<300)) || ((i>400) && (i<700)))
        {
            //Increase the energy per minute
            if((Array1[i]+10)<=50)
                Array1[i] = Array1[i]+ 10;

            else { //if we charge at normal rate, it could surpass capacity, so there may be leftovers
                Array1[i]=27;

            }       

        }

        if (peaks==0) {
            //Check if there is enough energy to supply in the ESS
            if(Array1[i]>Array2[i]) {

                Array3[i]=Array2[i]; //usage by the ESS
                Array2[i]=0; //Visualization purposes - grid does not provide any energy
                Array1[i]=Array1[i] - Array3[i]; //energy used by ESS

            }

        }

        chart1.setChart(chartTemp.runSimGraph("Title", "xLabel", "yLabel", true, new double[][]{Array1,Array2,Array3}));
    }


}


}

GraphTest.java

package cenas;

import java.awt.Color;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.data.xy.XYDataset; 
import org.jfree.data.xy.XYSeries; 
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.ChartFactory; 
import org.jfree.chart.plot.PlotOrientation; 
import org.jfree.data.xy.XYSeriesCollection; 
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;



public class GraphTest {


    public JFreeChart runSimGraph(String chartTitle, String xLabel, String yLabel, boolean legend ,double[][] graphValues) {


        JFreeChart xylineChart = ChartFactory.createXYLineChart(
            chartTitle,
            xLabel,
            yLabel,
            createSimDataset(graphValues),
            PlotOrientation.VERTICAL,
            legend, false, false);
        final XYPlot plot = xylineChart.getXYPlot();

        //Axes (Domain - x , Range - y)
        NumberAxis domain = (NumberAxis) plot.getDomainAxis();
        domain.setRange(0,24);
        plot.setBackgroundPaint(new Color(240, 240, 240));
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(false, true);
        //renderer.setBaseLinesVisible(false); // retira as linhas entre os pontos
        //renderer.setBaseShapesFilled(false); //apaga o centro das shapes
        //renderer.setBaseShapesVisible(false); //apaga as shapes
        Shape teste = new Rectangle2D.Double(-(1.2/2), -(1.2/2), 1.2, 1.2);
        renderer.setSeriesShape(0, teste);
        renderer.setSeriesPaint(0, Color.RED);
        //renderer.setSeriesStroke(0, new BasicStroke(1.0f));
        renderer.setSeriesShape(1, teste);
        renderer.setSeriesPaint(1, Color.BLUE);
        renderer.setSeriesShape(2, teste);
        renderer.setSeriesPaint(2, Color.GREEN);
        plot.setRenderer(renderer);

        return xylineChart;
    }

    private XYDataset createSimDataset(double[][] values) {

        double[] gridAr=values[0];
        double[] essAr=values[1];
        double[] availableEnergy=values[2];

        final XYSeries temp1 = new XYSeries("1");
        final XYSeries temp2 = new XYSeries("2");
        final XYSeries temp3 = new XYSeries("3");


        for (double i = 0; i < 1440; i++) {
            temp1.add(i / 60, gridAr[(int) i]);
            temp2.add(i/60, essAr[(int)i]);
            temp3.add(i/60, availableEnergy[(int) i]);
        }
        final XYSeriesCollection dataset = new XYSeriesCollection();
        dataset.addSeries(temp1);
        dataset.addSeries(temp2);
        dataset.addSeries(temp3);
        return dataset;
    }




}

Thanks in advance and sorry for the "long post",

Nhekas

Upvotes: 0

Views: 1269

Answers (1)

copeg
copeg

Reputation: 8348

Swing is single threaded - painting and events occur on the Event dispatch thread (EDT). From your comment above, the loop you posted is called within an ActionListener - which occurs on the EDT, so changes to the UI (eg changes in charts) will not occur until the EDT is free to repaint (eg sometime after the actionPerformed method ends).

what I want to print with different "refresh rates"

If you want to refresh the entire chart at a given rate, I would recommend using a javax.swing.Timer, updating the chart as necessary. For example, to fire the timer from the ActionListener of a JButton at a rate of once per second:

ActionListener buttonListener = new ActionListener(){

    @Override
    public void actionPerformed(ActionEvent e) {

        javax.swing.Timer timer = new javax.swing.Timer(1000, new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent e) {
                //create the arrays
                chart1.setChart(chartTemp.runSimGraph("Title", "XLabel", "YLabel",true, new double[][]{Array1,Array2,Array3}));
            }

        });
        timer.start();
    }
};
myButton.addActionListener(buttonListener);

If you want to refresh a chart with points as they become available, consider a) performing your calculations in its own thread and then b) add the points to the XYSeries (on the EDT - this is as opposed to recreating the dataset every time). An example, adapted from Adding points to XYSeries dynamically with JfreeChart :

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;


public class DynamicPlotAddition extends JFrame {

    private static final String title = "Dynamic Point Addition";
    private final Random rand = new Random();
    private XYSeries series = new XYSeries("Added");

    public DynamicPlotAddition(String s) {
        super(s);
        final ChartPanel chartPanel = createDemoPanel();
        this.add(chartPanel, BorderLayout.CENTER);
        Runnable runner = new Runnable(){

            @Override
            public void run() {
                int total = 1000;
                int iter = 0;
                while ( iter++ < total ){
                    SwingUtilities.invokeLater(new Runnable(){

                        @Override
                        public void run() {
                            series.add(rand.nextGaussian(), rand.nextGaussian());
                        }

                    });

                    try{Thread.sleep(100);}catch(Exception e){}
                }
            }

        };
        new Thread(runner).start();
    }

    private ChartPanel createDemoPanel() {
        JFreeChart jfreechart = ChartFactory.createScatterPlot(
            title, "X", "Y", createDataset(),
            PlotOrientation.VERTICAL, true, true, false);
        XYPlot xyPlot = (XYPlot) jfreechart.getPlot();
        xyPlot.setDomainCrosshairVisible(true);
        xyPlot.setRangeCrosshairVisible(true);
        XYItemRenderer renderer = xyPlot.getRenderer();
        renderer.setSeriesPaint(0, Color.blue);
        NumberAxis domain = (NumberAxis) xyPlot.getDomainAxis();
        domain.setVerticalTickLabels(true);
        return new ChartPanel(jfreechart);
    }

    private XYDataset createDataset() {
        XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
        xySeriesCollection.addSeries(series);
        return xySeriesCollection;
    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                DynamicPlotAddition demo = new DynamicPlotAddition(title);
                demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                demo.pack();
                demo.setLocationRelativeTo(null);
                demo.setVisible(true);
            }
        });
    }
}

In the example posted, you make the call to simulateValues from a new thread, and as each point pair is generated update the appropriate series

Upvotes: 2

Related Questions