Search Here

Saturday, July 25, 2015

Modern Charting tools for Desktop Applications

Hello Readers,

Time fly so fast. Its became months since I update my blog. Busy timings @work and the real reason is that I became so lazy to write. But that did not stop me exploring things. Here is my thought about the computer "charts" for dekstop applications.

Charts - A graphical representaion of the data (numbers mostly). We all love to see charts because human mind can easily grasp the picture than words or numbers. The right chart that displays the data will greatly assist human brain and help us to think more to make sense out of it.

Think of an example of weather forecast. If we have years and years of the data which is plotted nicely, we could predict the future, It might save human life !!

Example: weather data chart. Thanks to http://www.tgomagazine.co.uk
On the other side, Poor selection of charts / less flexibility of viewing data in various timelines (Ex: zoom into timelines) will not do any better job than just showing the huge data.

Its a developer / UI designers reposibility to choose right chart with maximum flexibility.

Charting Libraries:  Fortunatly for a desktop / web applicaion developers, There is no need to write a graphics code for plotting charts. There are tons of charting tools avilable both for desktop applications / web applications.

I used JFreeChart, GD::Graph, SWF::Chart etc ... (I don't remember which charting lib I have used in VB6).

All of these charting libaries are awesome and served their purpose at that point of view but we need to evolve.

I find few drawbacks in the traditional charting libraries,

  1. Developer need to write a code (Though minimal) to create, customize (colors, look and feel etc...) and plot the graph. This means that developer need to touch the code to update the Chart style.

  2. Usually traditional charting libraries wont provide any IDE / Char designer to visualize how your real data look in the particular chart. You really need to write code first (or see demo applications and visualize your data in your mind)

  3.  Traditional charting libraries render the data into some kind of image format internally (Ex: PNG, GIF, SVG etc..) and shows that image in the image viewer widget. This is not an efficient process if your data changes so quickly or User wants to zoom into the timeliness or for interactive charts. Keep rendering new image is a heay process for the GPUs (Though the modern GPUs are fast enough).

  4. Suddenly if you want to provide you application as a web application. At least there is an extra work to replace traditional charts to web ready.
How do we deal with this problem?

I always envy the JavaScript charting libraries.

  • Java script charting libaries are awesome looking

  • They are so interactive

  • They are not crated as a image and displayed, instead, The data is fed into Java script low level graphics functions and its drawn in browser canvas widget directly. So the zoom in, Zoom out are so fast

  • Java Script runs in the browser platform, Browsers are optimized to run the Java Script and Browsers are also optimized to use the GPUs

  • Most of the JavaScript libs provide an online WYSWYG editor to quickly visualize the real data with real charts.

  • Modern browser's memory foot print is optimized.

  • Most of the GUI frameworks support embedding a Browser inside the GUI applications. 

Did you get the answer?. Yes the idea is to embedded the Browser widget into desktop GUI application and leave the Charting work to Javascript. !!

I hear that. You need to see the code believe it. Here is my GTK2 based dektop application to demonstrate Java Script charting technique.


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#!/usr/bin/perl
use strict;
use warnings;
use Gtk2 -init;
use Gtk2::WebKit;
use Glib qw(TRUE FALSE);
use Math::Random::MT::Auto qw(binomial);
use Data::Dumper;
use FindBin qw($RealBin);

#Globals
$| = 1;
my $webHTML = "";

#Create Binomial distribution gui
my $win = Gtk2::Window->new;
$win->set_default_size( 800, 600 );
$win->signal_connect( destroy => sub { Gtk2->main_quit } );
my $vbox = Gtk2::VBox->new( FALSE, 2 );
$vbox->set_border_width(2);
$win->add($vbox);
my $toolbar = Gtk2::Toolbar->new;
$toolbar->set_icon_size('large-toolbar');
$toolbar->set_show_arrow(FALSE);
$toolbar->set_orientation('GTK_ORIENTATION_HORIZONTAL');
my $button = Gtk2::ToolButton->new_from_stock('gtk-refresh');
$button->signal_connect( "clicked" => \&refreshBinomialDataandLoad, undef );
$toolbar->insert( $button,-1 );
$toolbar->insert( Gtk2::SeparatorToolItem->new, -1 );
$vbox->pack_start( $toolbar, FALSE, FALSE, 0 );

#Create Gtk WebKit browser widget and add to GUI
my $sw = Gtk2::ScrolledWindow->new();
$sw->set_policy( 'automatic', 'automatic' );
$sw->set_size_request( 600, 600 );    # hack to set initial empty browser size


my $webkitView = Gtk2::WebKit::WebView->new();
print "\n Gtk2 WebKit Version: ", Gtk2::WebKit->major_version(), ".",  Gtk2::WebKit->minor_version(), ".", Gtk2::WebKit->micro_version();
$webkitView->get_settings()->set_property( 'enable-file-access-from-file-uris', TRUE );
$sw->add($webkitView);
$vbox->pack_start( $sw, 1, 1, 0 );
$win->show_all;
Gtk2->main;

#Start GTK Main loop. Nothing after this
#Subs
sub genBinomialChartData
{
 my $probabilityP = 0.5;
 my $trialsN      = 100;
 my $count        = 10000;
 my %binomial;

 # Get random numbers and put them in bins
 $webHTML = ' Generating ' . $count . ' Random Numbers. Please wait...';
 loadAndShowHTML();
 for ( 1 .. $count )
 {
  my $exprimentResult = binomial( $probabilityP, $trialsN );
  $binomial{$exprimentResult}++
    ;    #Store the expriment results as a keys in hash
 }

 #Load chart html
 $webHTML = startHTMLChart( $probabilityP, $trialsN, $count );
 foreach my $exprimentResult ( sort keys(%binomial) )
 {
  my $exprimentResultProbability =
    ( $binomial{$exprimentResult} / $count );
  print "\n Probability for $exprimentResult = ",
    $exprimentResultProbability, " (", $binomial{$exprimentResult}, '/',
    $count, ')';

  #Add chart data
  $webHTML .=
    insertHTMLChartData( $exprimentResult, $exprimentResultProbability );
 }
 print "\n";

 #Add chart footer
 $webHTML .= finishHTMLChart();
 print "\n HTML \n\n", $webHTML, "\n\nHTML";
}

sub insertHTMLChartData
{
 my $value       = shift @_;
 my $probability = shift @_;
 return '{
                            "Value": "' . $value . '",
                            "Probability": "' . $probability . '"
                        },';
}

sub finishHTMLChart
{
 return ']
                }
            );
        </script>
    </head>
    <body>
        <div id="chartdiv" style="width: 100%; height: 400px; background-color: #FFFFFF;" ></div>
    </body>
</html>';
}

sub startHTMLChart
{
 my $p = shift @_;
 my $t = shift @_;
 my $c = shift @_;
 return '<!DOCTYPE html>
<html>
    <head>
        <title>chart created with amCharts | amCharts</title>
        <meta name="description" content="chart created using amCharts live editor" />

        <!-- amCharts javascript sources -->
        <script src="file://' . $RealBin
   . '/amcharts/amcharts.js" type="text/javascript"></script>
        <script src="file://' . $RealBin
   . '/amcharts/serial.js" type="text/javascript"></script>

        <!-- amCharts javascript code -->
        <script type="text/javascript">
            AmCharts.makeChart("chartdiv",
                {
                    "type": "serial",
                    "path": "https://www.amcharts.com/lib/3/",
                    "categoryField": "Value",
                    "mouseWheelZoomEnabled": true,
                    "startDuration": 1,
                    "handDrawn": true,
                    "categoryAxis": {
                        "gridPosition": "start"
                    },
                    "trendLines": [],
                    "graphs": [
                        {
                            "balloonText": " Value=[[category]],  Probability=[[value]]",
                            "bullet": "round",
                            "bulletBorderThickness": 5,
                            "bulletSize": 10,
                            "gapPeriod": 0,
                            "id": "AmGraph-1",
                            "labelOffset": 2,
                            "title": "Observed",
                            "type": "smoothedLine",
                            "valueField": "Probability"
                        }
                    ],
                    "guides": [],
                    "valueAxes": [
                        {
                            "id": "ValueAxis-1",
                            "title": "Probability"
                        }
                    ],
                    "allLabels": [],
                    "balloon": {},
                    "legend": {
                        "labelText": "Observed ",
                        "useGraphSettings": true,
                        "valueWidth": 20
                    },
                                    "titles": [
                        {
                            "id": "Title-1",
                            "size": 15,
                            "text": "Binomial Probability Distribution \n              (p='
   . $p . ', n='
   . $t
   . ', count='
   . $c . ')"
                        }
                    ],
                    "dataProvider": [
                    ';
}

sub loadAndShowHTML
{
 $webkitView->load_string( $webHTML, 'text/html', 'UTF-8', 'file://' );
}

sub refreshBinomialDataandLoad
{
 my $htmlString;
 genBinomialChartData();

 #Load HTML Now
 loadAndShowHTML();
 return TRUE;
}


Note: This code is not optimized. I create a full html string every time and then load into Gtk2::WebKit browser widget. The more elegant way would be writing the changed data into a PIPE / Other IPC mechanism, From Javascript read from the PIPE. Hey, didn't I tell you already, Its only for demo .... Having said that, This code runs very very fast. I see the charts are alive within no time.

What am I doing?

1. I created a binomial distribution data for the purpose of charting. Mostly inspired from http://maths.uncommons.org/

2.  I used perl language and GTK2 GUI framework. I use Perl + Gtk2 a lot @work for creating cross platform GUI applications. I really like the Gtk's Glade GUI designer. It gratly simplifies the effort of GUI design.

3. I choose WebKit as my browser of choice. Webkit is running in iPhone!!. I used Gtk2::WebKit perl binding of the WebKit

4. I AMCharts JavaScript awesome library. They do provide online chart creator tool. My most of the HTML and Java Script code is just copy paste from their online chart designer's output.

Note: AMCharts allows to use their charting library for free for personal usage and you need to use their javascript libs from their site. For commercial usage there is a license option. This blog post's idea is to illustae the concept, You can choose other free javascript charting library.

5. In the code, I load all the widgets, Bring up the GUI and calculate the Binomial probability distribution.

6. Once the data is avilable, I feed the data to the AMCharts via Java Script, JSON kind of data format.

7. Finally the whole HTML string (including Java script code) goes to browser widget, It renders the chart in micro seconds.

8. Any zoom in / Zoom out or Interactive popups are taken care by browser widget and my main application loop is free to do other jobs.

Here are the screenshots of the above code,

Sample Binomial Distribution

Interactive Chart, Shows the point details when cursor move on data point

Zoom in. So fast zoom in and Zoom out
Conclusion

In the early days of computer science, The thought process was to keep the whole system to limit to one programming language and limited libraries and toolkits.

In the modern object oriented thinking, Use the best toolkit or language which serves the purpose.

Javascripts and browsers are highly optimised for the chart works and very simple to use.

I think, Modern computer chartings to be best done by the Javascript + Browser.

P.S: Thanks to Shakthi, for patiently explaining the probability distribution mathematical concepts to me. 

1 comment: