Search code examples
javateechart

TeeChart ScrollPager realtime update


I'm trying to update main chart series data dynamically, but ScrollPager freezes my chart's frame if I move ColorBand or if I do updating of the chart while it's already repainting with a lot of ArrayOutOfIndex exceptions. I understend why it happens, but don't know how to use it right with TeeChart. Is there some solution I could get it synchronized right?

Here's my code example with using of volatile, it simulates updating of series with filling it with sample value:

public class TestTeeChartCandle extends JFrame {

int sizeX = 500;
int sizeY = 500;

private TChart chart;
private Candle series;
private MyScrollPager scrollPager;

private volatile boolean updatingChart = false;
private volatile boolean updatingSubChart = false;

private volatile boolean updatingChartSeries = false;
private volatile boolean updatingSubChartSeries = false;

private volatile boolean updatingData = false;

private volatile boolean draggingSubChart = false;

public TestTeeChartCandle() {
    super();
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setPreferredSize(new Dimension(520, 520));

    chart = new TChart();
    chart.setPreferredSize(new Dimension(500, 500));

    series = new Candle(chart.getChart());
    series.fillSampleValues(500);

    scrollPager = new MyScrollPager(chart.getChart());

    ChangeEvent changeEvent = new ChangeEvent(null);
    scrollPager.dragMoving(changeEvent);

    MouseMoveListener mouseMoveListener = new MouseMoveListener();
    scrollPager.getColorBandTool().addMouseMotionListener(mouseMoveListener);

    this.addComponentListener(new ComponentListener() {
        public void componentResized(ComponentEvent e) {
            sizeX = e.getComponent().getWidth();
            sizeY = e.getComponent().getHeight();
            setPreferredSize(new Dimension(sizeX, sizeY));
        }

        public void componentMoved(ComponentEvent e) {
        }

        public void componentHidden(ComponentEvent e) {
        }

        public void componentShown(ComponentEvent e) {
        }
    });

    chart.addChartPaintListener(new ChartPaintAdapter() {
        @Override
        public void chartPainted(ChartDrawEvent e) {
            updatingChart = false;
        }

        @Override public void chartPainting(ChartDrawEvent e) {
            while (updatingData) ;
            updatingChart = true;
        }

        @Override public void seriesPainted(ChartDrawEvent e) {
            updatingChartSeries = false;
        }

        @Override public void seriesPainting(ChartDrawEvent e) {
            while (updatingData) ;
            updatingChartSeries = true;
        }
    });


    scrollPager.getSubChartTChart().addChartPaintListener(new ChartPaintAdapter() {
        @Override
        public void chartPainted(ChartDrawEvent e) {
            updatingSubChart = false;
        }

        @Override public void chartPainting(ChartDrawEvent e) {
            while (updatingData) ;
            updatingSubChart = true;
        }

        @Override public void seriesPainted(ChartDrawEvent e) {
            updatingSubChartSeries = false;
        }

        @Override public void seriesPainting(ChartDrawEvent e) {

            while (updatingData) ;
            updatingSubChartSeries = true;
        }
    });

    add(chart);
    pack();
    setVisible(true);

    threadSleep(500);
}

public void drawChart() {

    updatingData = true;

    while (updatingChart && updatingSubChart
            && updatingChartSeries && updatingSubChartSeries
            && draggingSubChart) ;

    scrollPager.getSubChartTChart().removeAllSeries();
    scrollPager.setSeries(null);

    series.beginUpdate();

    if (scrollPager.getSeries() != null)
        scrollPager.getSeries().beginUpdate();

    series.fillSampleValues(500);

    FastLine fl = new FastLine();
    for (int j = 0; j < series.getCount(); j++)
        fl.add(series.getDateValues().getValue(j), series.getCloseValues().getValue(j));

    scrollPager.setSeries(fl);

    series.endUpdate();

    if (scrollPager.getSeries() != null)
        scrollPager.getSeries().endUpdate();


    int lastVisibleId = series.getCount() - 1;
    if (lastVisibleId < 0)
        lastVisibleId = 0;

    int firstVisibleId = lastVisibleId - 100;
    if (firstVisibleId < 0)
        firstVisibleId = 0;

    double last = series.getDateValues().getValue(lastVisibleId);
    double first = series.getDateValues().getValue(firstVisibleId);

    scrollPager.getColorBandTool().setStart(first);
    scrollPager.getColorBandTool().setEnd(last);

    chart.getAxes().getBottom().setMinMax(first, last);

    updatingData = false;

    pack();

    while (updatingChart && updatingSubChart
            && updatingChartSeries && updatingSubChartSeries
            && draggingSubChart) ;

}

public static void main(String[] args) {

    TestTeeChartCandle testTeeChartCandle = new TestTeeChartCandle();

    while (true) {

        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        testTeeChartCandle.drawChart();

    }


}

public void threadSleep(int ms) {
    try {
        Thread.sleep(ms);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}


public class MyScrollPager extends ScrollPager implements com.steema.teechart.events.DragListener {

    public MyScrollPager(com.steema.teechart.IBaseChart iBaseChart) {
        super(iBaseChart);
    }

    @Override
    public void dragFinished(com.steema.teechart.events.ChangeEvent changeEvent) {
        draggingSubChart = false;
    }

    @Override
    public void dragMoving(com.steema.teechart.events.ChangeEvent changeEvent) {
        while (updatingData) ;
        draggingSubChart = true;
    }

}

}


Solution

  • What about pausing the thread when the mouse is being moved on the chart if the data is being updated?

        tChart1.addMouseMotionListener(new MouseMotionAdapter() {
    
            @Override
            public void mouseMoved(MouseEvent e) {
                 while (updatingData) ;
            }
        });
    

    EDIT 1:

    We've been investigating this.
    The example repopulates the series so fast that it easily reproduces the problem; It may not happen so readily in a real situation.

    We've added an AllowMouse property (publicly accessible through the according getter and setter), true by default, that disables any mouse event to be fired on the chart when it's false. This property would allow you to disable the mouse events on the chart before modifying it so the ColorBand in the ScrollPager tool won't work until you reenable the AllowMouse property again, at the end of your modification routine.


    EDIT 2:

    We've made another test demo we are sharing with everyone here.

    Note it doesn't use AllowMouse but it doesn't clear the series everytime; instead, it adds/appends points to the present series and it reuses the series in the scroller instead of reassigning it everytime.
    This, with some extra range and null checks in the sources makes the test application stable for us here.

    Note the colorband gets smaller as the points keep arriving; and if you continue adding points without removing anyone and without rescaling the bottom axis of the scroller, it will end on a very small and almost unusable colorband. There's some code in the example commented that removes the first points after a while.

    Finally, we'd suggest adding points at a slower interval. Adding more points less frequently would be less smooth but would make easier for the user to catch the colorband, to drag or to resize it.