B4J Library jCharts

Here's my first library for B4J. It's a wrapper of javafx.scene.chart with additional functions and classes.

jCharts.jpg
chart2.jpg


To take a snapshot of a chart, set its animated property to False before adding the data or wait until all animations are over.

Changelog:
v1.21:
- I added a workaround for a bug in OpenJDK 11 (thanks to Chris2)

v1.2:
- XAxis and YAxis created by the designer can now raise events with the default event prefix "XAxis" / "YAxis";
- I added the LegendChanged event to all charts;
- I fixed a bug (NullPointerException) in GetChildStyleMap and StyleMap.

v1.11:
- I added the SetChildStyleMap2 function;
- I modified the fourth demo to show a simple way to add the colored bands.

v1.1:
- I added the Plot event to all charts except PieChart;
- I added a demo to show how to use the Plot event (this project requires jGraphicLib);
- I modified the third demo to use a layout made with the designer.

v1.0:
- All charts can be added as custom views in the visual designer;
- I added the Width and Height properties to all charts and axis;
- I added the Series property to XYCoord;
- I added the MouseToData function to all charts and removed the EventPrefixForPlot parameter from Initialize (MouseToData is more convenient than events);
- I modified the second demo to show MouseToData in use;
- I modified the third demo so that the slider value sets the upper bound of the displayed series.

v0.9:
- I added ForceZeroInRange and TickUnit to NumberAxis;
- I added RemoveAllData to PieChart;
- I added FindNearestX to XYSeries;
- I added FindSeries and RemoveAllSeries to all charts using series;
- I added Padding to all charts;
- I added a new example and some documentation.
 

Attachments

  • jCharts_1_21.zip
    74.1 KB · Views: 809
Last edited:

Jack Bodewes

Member
Licensed User
Longtime User
The barchart is very beautiful.
Thank you for that.

BarChart.AddAllSeries (Array As XYSeries (Series3, Series4))

Now I get Series3 as Series4 with default colors.
Can I adjust the colors of the barchart series3 and series4 itself.


Thanks in advance.
 

Jack Bodewes

Member
Licensed User
Longtime User
Maybe not as it should, but I solved it myself.

For i = 0 To 30
Dim Back As Map = BarChart1.GetChildStyleMap(".data"& i &".chart-bar")
Dim Back1 As Map = BarChart1.GetChildStyleMap(".data"& i &".chart-bar")
Back.Put("-fx-bar-fill", " blue" )
Back1.Put("-fx-bar-fill", " red")
BarChart1.SetChildStyleMap(".data"& i&".chart-bar"&".series0 ", Back)
BarChart1.SetChildStyleMap(".data"& i&".chart-bar"&".series1 ", Back1)
Next

Now the legend.
 

Informatix

Expert
Licensed User
Longtime User
The barchart is very beautiful.
Thank you for that.

BarChart.AddAllSeries (Array As XYSeries (Series3, Series4))

Now I get Series3 as Series4 with default colors.
Can I adjust the colors of the barchart series3 and series4 itself.


Thanks in advance.
For the first series:
BarChart.GetChildStyleMap(".default-color0.chart-bar")

For the second series:
BarChart.GetChildStyleMap(".default-color1.chart-bar")

To set the color:
StyleMap.Put("-fx-bar-fill", MyColor)
 

rboeck

Well-Known Member
Licensed User
Longtime User
Hi, i am trying to use bubblecharts and wanted to show sales data on a bubble chart; the x axis should be the date axis, the y axis the amount of each sales.
Currently the x axis is starting with zero, in java i found that the range of the axis can be defined. Does jChart have this possibility?

Thanks in advance.
 

dk9uv

Member
Licensed User
Longtime User
Hi, another question about generating a dynamic chart. I want to update a chart every second and add a new random value.
This is my code:

B4X:
#Region  Project Attributes
    #MainFormWidth: 700
    #MainFormHeight: 650
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form

    Private LineChart As LineChart
    Private k As Int   
    Private timer1 As Timer

    Private Series1 As XYSeries
    Private XAxisL As NumberAxis   
    Private YAxisL As NumberAxis
   
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
   
    'LINE CHART
    XAxisL.Initialize("")
    YAxisL.Initialize("YAxisL")
    LineChart.Initialize(XAxisL, YAxisL, "LC")

    MainForm.RootPane.AddNode(LineChart, 0, 0, MainForm.RootPane.Width, MainForm.RootPane.Height)
    MainForm.Show

    'timer for 1s intervall
    timer1.Initialize("timer1",1000)
    timer1.Enabled=True

    k=0
    Series1.Initialize("testseries")

    LineChart.AddSeries(Series1)
       
End Sub

Sub timer1_tick

    LineChart.RemoveSeries(Series1)   
   
    k=k+1
    Series1.Add(k,Rnd(1,10))

    LineChart.AddSeries(Series1)
   
End Sub


I though that I can remove the series and add the series again but I always receive a "duplicate series added" error.

I hope someone can give me a hint.

Thanks in advance.
 

Informatix

Expert
Licensed User
Longtime User
Hi, another question about generating a dynamic chart. I want to update a chart every second and add a new random value.
This is my code:

B4X:
#Region  Project Attributes
    #MainFormWidth: 700
    #MainFormHeight: 650
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form

    Private LineChart As LineChart
    Private k As Int 
    Private timer1 As Timer

    Private Series1 As XYSeries
    Private XAxisL As NumberAxis 
    Private YAxisL As NumberAxis
 
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
 
    'LINE CHART
    XAxisL.Initialize("")
    YAxisL.Initialize("YAxisL")
    LineChart.Initialize(XAxisL, YAxisL, "LC")

    MainForm.RootPane.AddNode(LineChart, 0, 0, MainForm.RootPane.Width, MainForm.RootPane.Height)
    MainForm.Show

    'timer for 1s intervall
    timer1.Initialize("timer1",1000)
    timer1.Enabled=True

    k=0
    Series1.Initialize("testseries")

    LineChart.AddSeries(Series1)
     
End Sub

Sub timer1_tick

    LineChart.RemoveSeries(Series1) 
 
    k=k+1
    Series1.Add(k,Rnd(1,10))

    LineChart.AddSeries(Series1)
 
End Sub


I though that I can remove the series and add the series again but I always receive a "duplicate series added" error.

I hope someone can give me a hint.

Thanks in advance.
You already asked the question and I already answered: https://www.b4x.com/android/forum/threads/jcharts.71975/page-2#post-463820
 

dk9uv

Member
Licensed User
Longtime User
sorry, you are right, fiexd that.
The main problem I am facing is the large memory consumption.

This is the corrected program:

B4X:
#Region  Project Attributes
    #MainFormWidth: 700
    #MainFormHeight: 650
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form

    Private LineChart As LineChart
    Private k As Int   
    Private timer1 As Timer

    Private Series1 As XYSeries
    Private XAxisL As NumberAxis   
    Private YAxisL As NumberAxis
   
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
   
    'LINE CHART
    XAxisL.Initialize("")
    YAxisL.Initialize("YAxisL")
    LineChart.Initialize(XAxisL, YAxisL, "LC")

    MainForm.RootPane.AddNode(LineChart, 0, 0, MainForm.RootPane.Width, MainForm.RootPane.Height)
    MainForm.Show

    'timer for 1s intervall
    timer1.Initialize("timer1",10)
    timer1.Enabled=True

    k=0
    Series1.Initialize("testseries")

    LineChart.AddSeries(Series1)

    LineChart.Animated=False
           
End Sub

Sub timer1_tick

   
    LineChart.RemoveSeries(Series1)   
   
    k=k+1
    Series1.Add(k,Rnd(1,10))
   
    Log(Series1.Size)

    LineChart.AddSeries(Series1)
   
End Sub


I get the following memory consumption:
100 points: 100 MByte
500 points: 180 MByte
1000 points: 220 MByte
1500 points: 420 MByte

The programm is then getting slower and slower and finally blocks.

In my final application I have a couple of wireless temperature/humidity sensors and will get
a maximum of 10 measurements per minute which is 600 measurement
per hour or 14400 measurement points per day which I would like to draw.
Is this probably already to much for jcharts and I should first group measurements together
so that I do not exceed about 1000 points in the graph?
 

Informatix

Expert
Licensed User
Longtime User
sorry, you are right, fiexd that.
The main problem I am facing is the large memory consumption.

This is the corrected program:

B4X:
#Region  Project Attributes
    #MainFormWidth: 700
    #MainFormHeight: 650
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form

    Private LineChart As LineChart
    Private k As Int  
    Private timer1 As Timer

    Private Series1 As XYSeries
    Private XAxisL As NumberAxis  
    Private YAxisL As NumberAxis
  
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
  
    'LINE CHART
    XAxisL.Initialize("")
    YAxisL.Initialize("YAxisL")
    LineChart.Initialize(XAxisL, YAxisL, "LC")

    MainForm.RootPane.AddNode(LineChart, 0, 0, MainForm.RootPane.Width, MainForm.RootPane.Height)
    MainForm.Show

    'timer for 1s intervall
    timer1.Initialize("timer1",10)
    timer1.Enabled=True

    k=0
    Series1.Initialize("testseries")

    LineChart.AddSeries(Series1)

    LineChart.Animated=False
          
End Sub

Sub timer1_tick

  
    LineChart.RemoveSeries(Series1)  
  
    k=k+1
    Series1.Add(k,Rnd(1,10))
  
    Log(Series1.Size)

    LineChart.AddSeries(Series1)
  
End Sub


I get the following memory consumption:
100 points: 100 MByte
500 points: 180 MByte
1000 points: 220 MByte
1500 points: 420 MByte

The programm is then getting slower and slower and finally blocks.

In my final application I have a couple of wireless temperature/humidity sensors and will get
a maximum of 10 measurements per minute which is 600 measurement
per hour or 14400 measurement points per day which I would like to draw.
Is this probably already to much for jcharts and I should first group measurements together
so that I do not exceed about 1000 points in the graph?
It's a waste of memory indeed to try to display 14400 measurement points when you have only 1900 pixels on your screen. You have to reduce the number of data to plot. I already posted a message about my method:
https://www.b4x.com/android/forum/threads/jcharts.71975/page-2#post-464963
 

dk9uv

Member
Licensed User
Longtime User
It's a waste of memory indeed to try to display 14400 measurement points when you have only 1900 pixels on your screen. You have to reduce the number of data to plot. I already posted a message about my method:
https://www.b4x.com/android/forum/threads/jcharts.71975/page-2#post-464963
Thanks for the advice. I will try to reduce the number of points.
Nevertheless I found that even whit a limited number of points a continuous removing and adding series increases the memory
consumption very quickly until out of heap space.
This program is an example of 5 series each with 500 points. Every second the series are removed and added again.

B4X:
#Region  Project Attributes
    #MainFormWidth: 700
    #MainFormHeight: 650
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form

    Private LineChart As LineChart
    Private i As Int
    Private k As Int 
    Private timer1 As Timer

    Private Series1 As XYSeries
    Private Series2 As XYSeries
    Private Series3 As XYSeries
    Private Series4 As XYSeries
    Private Series5 As XYSeries             
    Private XAxisL As NumberAxis 
    Private YAxisL As NumberAxis
 
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
 
    'LINE CHART
    XAxisL.Initialize("")
    YAxisL.Initialize("YAxisL")
    LineChart.Initialize(XAxisL, YAxisL, "LC")

    MainForm.RootPane.AddNode(LineChart, 0, 0, MainForm.RootPane.Width, MainForm.RootPane.Height)
    MainForm.Show

    'timer for 1s intervall
    timer1.Initialize("timer1",1000)
    timer1.Enabled=True

    i=0
    k=0
    Series1.Initialize("testseries1")
    Series2.Initialize("testseries2")
    Series3.Initialize("testseries3")
    Series4.Initialize("testseries4")
    Series5.Initialize("testseries5")             

    For k=0 To 500
        Series1.Add(k,Sin(k/10))     
        Series2.Add(k,Sin(k/11)) 
        Series3.Add(k,Sin(k/12)) 
        Series4.Add(k,Sin(k/13)) 
        Series5.Add(k,Sin(k/14))                                     
    Next

    LineChart.AddSeries(Series1)
    LineChart.AddSeries(Series2)
    LineChart.AddSeries(Series3)
    LineChart.AddSeries(Series4)
    LineChart.AddSeries(Series5)

    LineChart.Animated=False
         
End Sub

Sub timer1_tick

 
    LineChart.RemoveSeries(Series1) 
    LineChart.RemoveSeries(Series2) 
    LineChart.RemoveSeries(Series3) 
    LineChart.RemoveSeries(Series4) 
    LineChart.RemoveSeries(Series5) 
 
 
    i=i+1
    Log(i)
 

    LineChart.AddSeries(Series1)
    LineChart.AddSeries(Series2)
    LineChart.AddSeries(Series3)
    LineChart.AddSeries(Series4)
    LineChart.AddSeries(Series5)
 
End Sub


After 500 seconds the memory consumption is already at 350 MByte and after 1000 secondes
already 640 MByte. I am not sure if I have done anything wrong and would appreciate an advice very much.
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
Thanks for the advice. I will try to reduce the number of points.
Nevertheless I found that even whit a limited number of points a continuous removing and adding series increases the memory
consumption very quickly until out of heap space.
This program is an example of 5 series each with 500 points. Every second the series are removed and added again.

B4X:
#Region  Project Attributes
    #MainFormWidth: 700
    #MainFormHeight: 650
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form

    Private LineChart As LineChart
    Private i As Int
    Private k As Int
    Private timer1 As Timer

    Private Series1 As XYSeries
    Private Series2 As XYSeries
    Private Series3 As XYSeries
    Private Series4 As XYSeries
    Private Series5 As XYSeries            
    Private XAxisL As NumberAxis
    Private YAxisL As NumberAxis

End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1

    'LINE CHART
    XAxisL.Initialize("")
    YAxisL.Initialize("YAxisL")
    LineChart.Initialize(XAxisL, YAxisL, "LC")

    MainForm.RootPane.AddNode(LineChart, 0, 0, MainForm.RootPane.Width, MainForm.RootPane.Height)
    MainForm.Show

    'timer for 1s intervall
    timer1.Initialize("timer1",1000)
    timer1.Enabled=True

    i=0
    k=0
    Series1.Initialize("testseries1")
    Series2.Initialize("testseries2")
    Series3.Initialize("testseries3")
    Series4.Initialize("testseries4")
    Series5.Initialize("testseries5")            

    For k=0 To 500
        Series1.Add(k,Sin(k/10))    
        Series2.Add(k,Sin(k/11))
        Series3.Add(k,Sin(k/12))
        Series4.Add(k,Sin(k/13))
        Series5.Add(k,Sin(k/14))                                    
    Next

    LineChart.AddSeries(Series1)
    LineChart.AddSeries(Series2)
    LineChart.AddSeries(Series3)
    LineChart.AddSeries(Series4)
    LineChart.AddSeries(Series5)

    LineChart.Animated=False
        
End Sub

Sub timer1_tick


    LineChart.RemoveSeries(Series1)
    LineChart.RemoveSeries(Series2)
    LineChart.RemoveSeries(Series3)
    LineChart.RemoveSeries(Series4)
    LineChart.RemoveSeries(Series5)


    i=i+1
    Log(i)


    LineChart.AddSeries(Series1)
    LineChart.AddSeries(Series2)
    LineChart.AddSeries(Series3)
    LineChart.AddSeries(Series4)
    LineChart.AddSeries(Series5)

End Sub


After 500 seconds the memory consumption is already at 350 MByte and after 1000 secondes
already 640 MByte. I am not sure if I have done anything wrong and would appreciate an advice very much.
I notice the same thing under Windows 10 64 bits, but the big question is: does the program raise an OutOfMemory error? does it crash after some time?
 

dk9uv

Member
Licensed User
Longtime User
I notice the same thing under Windows 10 64 bits, but the big question is: does the program raise an OutOfMemory error? does it crash after some time?
There is a nice lib to retrieve memory consumption and so on

Private robot As AWTRobot

Log(robot.JVMMemoryFree)
Log(robot.JVMMemoryHeapAllocated)
Log(robot.JVMMemoryHeapUsed)
Log(robot.JVMMemoryTotalAvailable)


I will try to investigate the program behaviour in detail.
After about 2000 times removing and adding series (with 100ms update rate) I receive: java.lang.OutOfMemoryError: GC overhead limit exceeded
I will repeat that with slower update rate.
 

dk9uv

Member
Licensed User
Longtime User
There is a nice lib to retrieve memory consumption and so on

Private robot As AWTRobot

Log(robot.JVMMemoryFree)
Log(robot.JVMMemoryHeapAllocated)
Log(robot.JVMMemoryHeapUsed)
Log(robot.JVMMemoryTotalAvailable)


I will try to investigate the program behaviour in detail.
After about 2000 times removing and adding series (with 100ms update rate) I receive: java.lang.OutOfMemoryError: GC overhead limit exceeded
I will repeat that with slower update rate.

I read a lot on javafx scene charts and I am a little bit concerned about a couple of memory leak bug reports like this
https://bugs.openjdk.java.net/browse/JDK-8168930 Can anybody of the Java experts asses this issue?
 

stevel05

Expert
Licensed User
Longtime User
If it's a known bug, there is going to be little that can be done until it's fixed in JavafX. Unless you can find a workaround in the meantime. For instance, it may slow down the display, but what happens if you create a complete new graph each time you update the data? Does it stop the memory leak?

Out of interest, what version of JDK are you using?
 

dk9uv

Member
Licensed User
Longtime User
If it's a known bug, there is going to be little that can be done until it's fixed in JavafX. Unless you can find a workaround in the meantime. For instance, it may slow down the display, but what happens if you create a complete new graph each time you update the data? Does it stop the memory leak?

Out of interest, what version of JDK are you using?
I am using JDK 1.8.0_121.
Unfortunately removing the graph did not help.

I tried another "strange" alternative using the nice javascript graph lib amcharts and loading a sample graph in
a B4J webview. After playing a little bit in the webview the program crashes. I guess it is not a good idea to intensively use javscript in a webview.
 

raphaelcno

Active Member
Licensed User
Longtime User
Really nice library :)
Is it possible to have two Y axes with different values, one on the left side and one on the right side of the chart?
Something like that:
2y_axis_graph.gif
 

raphaelcno

Active Member
Licensed User
Longtime User
I'm experimenting the superposition of two charts.
I didn't know how to align the 0 point of both X axes, but finally found a solution with this code in LC_MouseMoved(), inspired by code found in the 'Charts.b4j' example:
B4X:
'Dim NB, NB_2 As NodeBounds
NB = LineChart.GetChildNodeBounds(".chart-plot-background")
NB_2 = LineChart_2.GetChildNodeBounds(".chart-plot-background")
' Here with 100px from left edge to 0 on X axis
LineChart.SetPadding(0, 0, 0, 100 - NB.LocalX)
LineChart_2.SetPadding(0, 0, 0, 100 - NB_2.LocalX)

I also tried to put this code in AppStart() and LC_plot(), but apparently it didn't work.
EDIT: The code can also be used after adding the last value to the series "SeriesXX.Add(XXX, YYY)". That way you don't even need to move the mouse over the chart to get the alignement.

2017-03-26 - B4J Forum - Align 2 charts.png


In order to change the color of the tick labels on Y axis, you can use one of these methods:
B4X:
YAxisL.Style = "-fx-tick-label-fill: blue;"
YAxisL_2.Style = "-fx-tick-label-fill: red;"
or
B4X:
CSSUtils.SetStyleProperty(YAxisL,"-fx-tick-label-fill", "blue")  ' With CSSUtils Library
CSSUtils.SetStyleProperty(YAxisL_2,"-fx-tick-label-fill", "red")
 
Last edited:
Top