B4J Library [B4X] [XUI] xChart Class and b4xlib

Laurent95

Active Member
Licensed User
Thank you Klaus.
It's ok, you're welcome, and i share things in open source, since 1978, so i like this way too.
I consider that's a bit normal that some people try to help you in return. Especially that you always gave to community a lot of your time.
So even if sometime it's look more like a drop in the ocean, all that we can do to help to rendering things better it's good.

And indeed respond taken more time than to amend the code 😂
At least, better to see glass half full, it have allowed 2 old school guys to exchange ;)
I'm 66 since January.

Have a nice night,
Laurent
 

klaus

Expert
Licensed User
Thank you Erel.
... and I had the honor to physically see Klaus while he started working it
The honor was on both sides !
Almost 3 years ago.
 

klaus

Expert
Licensed User
@Laurent95
Hi Laurent and others.

Can you please test the latest version 7.0.
Added MissingDataValue Property
Added manual scales for Bar and Radar charts
Added GetMaxNumberBars method
Improved Bar values display, number of digits

1615653315893.png


EDIT: 2021.03.15
Removed the xChart.b4xlib file, it has been updated in the first post.
 
Last edited:

Laurent95

Active Member
Licensed User
@Laurent95
Hi Laurent and others.

Can you please test the latest version 7.0.
Hi Klaus and all others,
Sorry to test it only today, i can't login till now. I have an excellent Internet provider who forward me message with the code for double identification the next day :mad: But it's valid 15 mins... 😂

Added MissingDataValue Property
Added manual scales for Bar and Radar charts
Added GetMaxNumberBars method
Improved Bar values display, number of digits
Tested quickly, result is WOWWWWWWW 😍
See below screenshots on my test project, on ALL charts the min and max of axis match perfect:
Line chart-data since 3-2018.png
VBar chart-data since 4-2018.png
HBar chart-data since 4-2018.png
Radar chart-data since 4-2018.png

Note: by code i add +1 and -1 to the MaxiVal and MiniVal to adapt the axis, just a personal choice for the rendering, it's not related to your code:
Calculate Min and max values:
    Dim MaxiRate As Double
    Dim MiniRate As Double
    Dim MonthLast As Int
    Dim lstMin, lstMax As List
    lstMin.Initialize
    lstMax.Initialize
    For i = 0 To lst.Size - 1
        Dim result() As String = lst.Get(i)
        lstMin.Add(RadarChart1.NumberFormat3(result(1),2))
        lstMax.Add(RadarChart1.NumberFormat3(result(1),2))
        MonthLast = DateTime.GetMonth(result(2))
    Next
    lstMin.Sort(True)    'Sort ascending so the min is the index 0
    lstMax.Sort(False)    'Sort unascending so the max is the index 0
    Log("Minimum rate: " & lstMin.Get(0))
    Log("Maximum rate: " & lstMax.Get(0))
    MiniRate = lstMin.Get(0)-1.0        'To give a bit space
    MaxiRate = lstMax.Get(0)+1.0        'To give a bit space
Still have to test:
- the GetMaxNumberBars method, it will be fast, i just only need to adapt the code on VBar and HBar and put more long time data to test that ;
- the MissingDataValue Property, i need to adapt my code, i resolved this before with adapt the SQL request, maybe a bit more long to test that.

Can you precise me what i need to test for the Improvement of Bar values display, number of digits ? I think i didn't use it till now ? Thank you.

THANK YOU SO MUCH !!!
What to say more than, You are "the great Klaus" ;)

Have a nice Sunday,
Laurent
 

klaus

Expert
Licensed User
Hi Laurent, thank you for the testing.
Note: by code i add +1 and -1 to the MaxiVal and MiniVal to adapt the axis, just a personal choice for the rendering, it's not related to your code:
If I understand well, it is to have a margin on top and bottom.
That is what manual scales are for, they can be adapted to any specific needs.
When I make charts, I like round scale values.
For this it could be useful to adapt the number or Y intervals, RadarChart1.NbYIntervals = n.
But, in your case, from 53 to 66 this is of no help.

Can you precise me what i need to test for the Improvement of Bar values display, number of digits ?
No real need to test it, I tested a lot. The change is the display of values with decimal parts, before I displayed only integers.
Maybe there are some special cases I have not tested, but these may be discovered some time in the future.
 
Last edited:

Laurent95

Active Member
Licensed User
@klaus

Ok for your responses 👍
That's good, i adapted my test code in the meantime, so, i confirm GetMaxNumberBars method work fine in both cases, horizontal and vertical, but that's logic :)
Screenshots below:
VBar chart-number of bars too much.png
VBar chart-number of bars ok.png

The same in horizontal
HBar chart-number of bars too much.png
HBar chart-number of bars ok.png

Really nice features Klaus, impressive work 👏
I didn't complicate too much the code for test, i use a dialog box, lazy way 😂

If someone is interested by the code to calculate and test it, not optimized, could be a Sub too.
method to test GetMaxNumberBars:
                'Multiply by 3 because 3 columns for each month and round it
                Dim NumberofColumnsInSQL As Int = Round((rateHistoLst.Size)*3)
                Dim numberOfMonths As Int
                'Divide by 3 for the same reason to indicate the exact number of month(s), but test the modulo before
                If(((NumberofColumnsInSQL-VStackedBarChart1.GetMaxNumberBars) Mod 3) > 0) Then
                    numberOfMonths = ((NumberofColumnsInSQL-VStackedBarChart1.GetMaxNumberBars)/3)+1
                Else
                    numberOfMonths = (NumberofColumnsInSQL-VStackedBarChart1.GetMaxNumberBars)/3
                End If
                Log("Number max of columns: " & VStackedBarChart1.GetMaxNumberBars)
                Log("Number of columns with SQL request: " & NumberofColumnsInSQL)
                Log("Number of months to decrease: " & numberOfMonths)
                'So, just need to test if the number max of bars possible is not ok in this case
                If(VStackedBarChart1.GetMaxNumberBars<NumberofColumnsInSQL) Then
                    VStackedBarChart1.DrawEmptyChart
                    Dim msg As String = "Choose a less long time of data, too many columns to display" & CRLF &"Decrease " & numberOfMonths & " month(s)"
                    xui.MsgboxAsync(msg,"TestFillDB")
                    Return
                End If
                VStackedBarChart1.YScaleMinValue = MiniRate
                VStackedBarChart1.YScaleMaxValue = MaxiRate
                VStackedBarChart1.YMinValue = MiniRate
                VStackedBarChart1.YMaxValue = MaxiRate
                VStackedBarChart1.DrawChart
It's the same for the horizontal bars

Have a nice afternoon,
Laurent
 

Laurent95

Active Member
Licensed User
If I understand well, it is to have a margin on top and bottom.
That is what manual scales are for, they can be adapted to any specific needs.
When I make charts, I like round scale values.
For this it could be useful to adapt the number or Y intervals, RadarChart1.NbYIntervals = n.
Bur, in your case, from 53 to 66 this is of no help.
Indeed, it's to have margin on top and bottom.
And you're right in this case the max and min values of all months are rounded, best looking at my idea.
I think it would be a bit more complicate to work with float numbers, and i like the rounded scale values too.
But for each month i keep the float values, because sometime it's less than 1 Philippine peso.
Look at Line chart in my precedent message of your response, the 'PHP' (average in fact, forgot to change it) is 64.28 and the Maxrate of this month is 64.95 :mad:.
Why do simple way, rates exchange maked me mad at begin 😂

[HS]
And for all who can interrogate why Philippine peso .
It's to save my bank account 💸, my wife is filipina. So we help the family in the Philipinnes 😂
[/HS]
 
Last edited:

klaus

Expert
Licensed User
I think it would be a bit more complicate to work with float numbers, and i like the rounded scale values too.
Anyway, if you look at the AddXXX routines, the variable type for all point values is Double.
So no problem to use Float numbers.
For BAR values, insides the bars, I used Int type, which I changed to Double and limit the display to max 6 characters.

For round scales, you can use either automatic scales, these are rounded.
Or adapt the number of intervals to get a round scale according to the min and max values.
In your example, with 53 to 66 and scale interval values of 1.3, you could set 52 to 66 and 7 intervals with scale intervals of 2.
Or 14 intervals with a scale interval of 1, but 14 intervals could make the chart more difficult to read depending on the chart dimension.
 
Last edited:

Laurent95

Active Member
Licensed User
Anyway, if you look at the AddXXX routines, the variable type for all point values is Double.
So no problem to use Float numbers.
.../...
Or adapt the number of intervals to get a round scale according to the min and max values.
In your example, with 53 to 66 and scale interval values of 1.3, you could set 52 to 66 and 7 intervals with scale intervals of 2.
Or 14 intervals with a scale interval of 1, but 14 intervals could make the chart more difficult to read depending on the chart dimension. 👍
Hi,

@klaus
Ok, thank you for the explanations, i'll take a look to improve the final looking, till now i always used the automatic scales on all the xCharts.
To be honest, i even not understood well all features, it need time to discover all and go deeply inside :)

It's a very nice and big library now, i need to study it yet 🔍
Hopeffuly i'll not need 3 years to do it 😂
Just kidding, i started also to build my final project and of course i encountered some isues.

Have a nice day,
Laurent
 

klaus

Expert
Licensed User
The xChart library has been updated to version 7.0.

Version 7.0
Added MissingDataValue Property, used for missing data
Added manual scales for Bar and Radar charts
Added AddRadar2 method which allows to add point shapes similar to AddLine2
Added GetMaxNumberBars method
Improved Bar values display, number of digits

Version 6.9
Updated the descriptions for the Designer
Improved BAR chart drawing
Amended error in drawing points in YX_CHART charts
 

Laurent95

Active Member
Licensed User
Hi All,


EDIT really imporant : This issue have nothing to see with xChart
I use on the B4A project a library AndroidResources, this library return Null if ever the key don't exist, that's that who give the issue.
I used a bit internationalisation in my base B4A project, so, i used it when i put the xChart. But no luck, sorry Klaus, a typo gave the issue :(
Anyway i'll contact tha author of this library to ask if t's possible to amend the library and return empty string instead.
[End of EDIT]

@klaus :
Found a problem on B4A with xChartLib v7.0, don't know why, i tried to change the scaleValues for "1!1.2!1.5!1.8 etc." instead of the default values "1!2!2.5!5!10" but it's not that.
Default scaleValues.png
So, i changed the lib for the class module and put a log line before the line 1352, the Graph.YAxisName is null.
Maybe not changed yet, but i'm a bit lost and i don't know what i can do, or change ?
Screenshot below

xChartLib pbm under B4Av2.png

Note: To help you to found the pbm, i sent by pm on a new "conversation" my actual B4A project. Didn't found a way to pm you 🤣
I know you don't like that, but it's not finish, so better to avoid to put it here for now. That'll allow to don't have too many HS in this thread ;)


EDIT: Forget to say B4A is updated on v10.60.
I don't know why but before that was working, at a moment need to redo my layout, got an error, didn't remember which one.
Since this time i have this problem.


Good apettit,
Laurent
 
Last edited:

swChef

Member
Licensed User
@klaus thank you for creating xChart.
I had need to reverse the Y axis for an YXChart, and also: Public Sub ClearPointsFrom(LineIndex As Int)
If you could incorporate into the official that would be great. Let me know if you want the full file instead of the changes below.
B4X:
#DesignerProperty: Key: ReverseYScale, DisplayName: ReverseYScale, FieldType: Boolean, DefaultValue: False, Description: Causes Y axis to reverse, min on top, max on bottom.

    Type ScaleData (Scale As Double, MinVal As Double, MaxVal As Double, MinManu As Double, MaxManu As Double,    IntervalManu As Double, MinAuto As Double, MaxAuto As Double, IntervalAuto As Double, Interval As Double, NbIntervals As Int, Automatic As Boolean, Different As Boolean, Exp As Double, YZeroAxis As Boolean, YZeroAxisHighlight As Boolean, ScaleValues As String, Logarithmic As Boolean, DrawXScale As Boolean, DrawYScale As Boolean, ReverseYScale As Boolean)

        Scale(sY(i)).ReverseYScale = Props.GetDefault("ReverseYScale", False)

Private Sub DrawScaleY
...
                If Scale(sY(0)).ReverseYScale  = False Then
                    txt = NumberFormat3(Scale(sY(0)).MinVal + i * Scale(sY(0)).Interval, 6)
                Else ' reverse Y
                    txt = NumberFormat3(Scale(sY(0)).MaxVal - i * Scale(sY(0)).Interval, 6)
                End If
...

Private Sub DrawYXLines
...
        If Scale(sY(0)).Logarithmic = False Then
            If Scale(sY(0)).ReverseYScale = False Then
                y0 = Graph.Bottom - (yxVal(1) - Scale(sY(0)).MinVal) * Scale(sY(0)).Scale
            Else ' reverse Y
                y0 = Graph.Bottom - (Scale(sY(0)).MaxVal - yxVal(1)) * Scale(sY(0)).Scale
            End If
        Else
...
...
            If Scale(sY(0)).Logarithmic = False Then
                If Scale(sY(0)).ReverseYScale = False Then
                    y1 = Graph.Bottom - (yxVal(1) - Scale(sY(0)).MinVal) * Scale(sY(0)).Scale
                Else ' xxx reverse Y
                    y1 = Graph.Bottom - (Scale(sY(0)).MaxVal - yxVal(1)) * Scale(sY(0)).Scale
                End If
            Else
...


Public Sub ClearPointsFrom(LineIndex As Int)
    Private ID As ItemData
    If Graph.ChartType = "YX_CHART" And Items.Size > LineIndex Then
            ID = Items.Get(LineIndex)
            ID.YXArray.Initialize
    End If
End Sub


Public Sub setReverseYScale(ReverseYScale As Boolean)
    Scale(sY(0)).ReverseYScale = ReverseYScale
    Scale(sY(1)).ReverseYScale = ReverseYScale
    Scale(sY(2)).ReverseYScale = ReverseYScale
    Scale(sY(3)).ReverseYScale = ReverseYScale
End Sub
 

klaus

Expert
Licensed User
I have updated the xChart library to version 7.2 beta.
I would be pleased if some users could test it.
It is still a work in progress.

It includes the ReverseYScale property, only for LINE and YX_CHART charts.
And it includes a new feature ZOOM.
With xChart.SetZoomIndexes(BeginIndex, EndIndex) you can set the zoom and scroll horizontally.
This method is intended currently only for BAR, STACKED_BAR and LINE charts, it has not yet been implemented for H_BAR and H_STACKEDBAR charts.
With xChart.UnZoom you can unzoom the chart.

It is a big change and I am afraid that there are still some bugs.

Attached the B4J demo project.

1616874492753.png


Unzoomed.

1616874524969.png


Zoomed.
 

Attachments

Last edited:

Peter Simpson

Expert
Licensed User
Good morning @klaus.
Everything is working as expected with no serious issues in a couple of my previous apps when using the xChart class.

One small issue with the demo though, nothing serious and the issue was probably there before. If you click the H Bars button twice, on the second click you will see a crash, this only happens with that one button in the demo and only on the second click. As I said nothing serious.

B4X:
Picked up _JAVA_OPTIONS: -Xmx1024m -Xms1024m -XX:-UseGCOverheadLimit
WARNING: package com.sun.javafx.embed.swing.oldimpl not in javafx.swing
Waiting for debugger to connect...
Program started.
Error occurred on line: 2389 (xChart)
java.lang.IndexOutOfBoundsException: Index 4 out of bounds for length 4
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
    at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
    at java.base/java.util.Objects.checkIndex(Objects.java:373)
    at java.base/java.util.ArrayList.get(ArrayList.java:426)
    at anywheresoftware.b4a.objects.collections.List.Get(List.java:105)
    at b4j.ChartsDemo.xchart._getbarpointsminmaxvalues(xchart.java:3231)
    at b4j.ChartsDemo.xchart._calcscaleauto(xchart.java:2582)
    at b4j.ChartsDemo.xchart._initcharth(xchart.java:14157)
    at b4j.ChartsDemo.xchart._initchart(xchart.java:8401)
    at b4j.ChartsDemo.xchart._drawchart(xchart.java:933)
    at b4j.ChartsDemo.main._createhbardata(main.java:1745)
    at b4j.ChartsDemo.main._btnhbarcharts_click(main.java:1585)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:632)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:234)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
    at jdk.internal.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:91)
    at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:98)
    at anywheresoftware.b4a.BA$1.run(BA.java:216)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
    at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
    at java.base/java.lang.Thread.run(Thread.java:832)
 

klaus

Expert
Licensed User
Thank you Peter for testing.
I had also seen the problem in the mean time with the second click.
It has already been amended for the 'official' update.
 
Last edited:
Top