0

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

dgteixeira
  • 61
  • 9
  • `My calculattion code is as follows:` Where is this 'calculation' code being called? In other words, what context? Is this in its own thread? On the EDT? – copeg Jun 29 '16 at 17:19
  • @copeg you're right, It is not a complete explanation. I have a main .java class that is a Jframe with various components. When I press a specific button the calculations begin. Therefore, that `for` loop is called inside a Jbutton's `ActionListener`. – dgteixeira Jun 29 '16 at 17:26
  • `That for loop is called inside a button.` Do you mean inside an ActionListener of the JButton? – copeg Jun 29 '16 at 17:27
  • Yes, it is inside an ActionListener of a Jbutton. – dgteixeira Jun 29 '16 at 17:30

1 Answers1

2

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

Community
  • 1
  • 1
copeg
  • 8,290
  • 19
  • 28
  • The updates to the chart should be done on the EWT, So either use a SwingWorker instead of Timer, or wrap the setChart() call inside a SwingUtilities.invokeLater() – FredK Jun 29 '16 at 17:44
  • @copeg Thanks, but that solution didn't work, because if I put that timer within the loop, It only performs the actionPerformed method after it finishes. And I want for it to update the chart each iteration. – dgteixeira Jun 29 '16 at 17:50
  • @FredK Its a Swing timer....From [How to use...](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) `Swing timer's task is performed in the event dispatch thread` – copeg Jun 29 '16 at 17:50
  • @FredK how can I accomplish your suggestion? Could you make an example, please? Thanks in advance – dgteixeira Jun 29 '16 at 17:51
  • @Nhekas Don't place it in a loop. You should start the timer once and it will fire the ActionListener for every iteration until it is told to stop. – copeg Jun 29 '16 at 17:53
  • @copeg I did what you said. Started the timer before the loop and stopped after it. It never called the `actionPerformed` method. – dgteixeira Jun 29 '16 at 17:58
  • @Nhekas, why are you stopping the Timer? And why still looping? Just start the timer. See updated code – copeg Jun 29 '16 at 18:01
  • @copeg the looping is required to calculate the values of the 3 arrays. And I've tried as you specified. The setChart() method does not draw the graph until it is completely finished, I don't know why. Thanks for the help anyway – dgteixeira Jun 29 '16 at 19:05
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/116007/discussion-between-copeg-and-nhekas). – copeg Jun 29 '16 at 19:09
  • @trashgod I checked that example and it worked, it started to input values dinamically. However, I want to calculate the values, then send them to the graph like shown in `Test2.java` . How can I send the values to `GraphTest.java` after the calculation and only then update the graph series? Thanks in advance – dgteixeira Jun 30 '16 at 09:11
  • @trashgod I haven't completely understood this last question/answer. However, I will try to implement it and see the result. – dgteixeira Jun 30 '16 at 09:57
  • @trashgod I was able to use the swing worker to insert values on the graph in one series. However, how can I send multiple values for multiple series with the `publish()` method? – dgteixeira Jun 30 '16 at 10:42
  • @copeg Thanks for the edit. You and trashgod explained and detailed well your answers! I was finally able to do it. Thanks! – dgteixeira Jun 30 '16 at 10:51