Posts tagged: Chart

Charts with JVx and JavaFX

Post to Twitter

In my last article about JavaFX, JVx and data binding you saw a first impression of our chart binding. We have additional screenshots for you. The first two images show a Swing application with different charts. The next two images show the same application, with JavaFX. Which UI do you like more?

Bar chart with Swing

Bar chart with Swing

Line chart with Swing

Line chart with Swing

Bar chart with JavaFX

Bar chart with JavaFX

Line chart with JavaFX

Line chart with JavaFX

The JavaFX UI is clean and fresh. It looks modern compared to Swing. A desktop application should look modern and not dusty!

How about the data binding?

Our screen has two charts. Every chart gets its records from a data book (our model class). The labels and values are dynamic and every change in the databook updates the chart. Here's the source code for the chart definition:

//BAR chart
UIChart chartBar.setChartStyle(UIChart.STYLE_BARS);
chartBar.setTitle("Annual Statistics");
chartBar.setXAxisTitle("Year");
chartBar.setYAxisTitle("Grosstotalprice");
chartBar.setDataBook(rdbV_statistic_order_offer_year);
chartBar.setXColumnName("YEAR_");
chartBar.setYColumnNames(new String[] {"OFFER_GROSSTOTALPRICE", "ORDER_GROSSTOTALPRICE"});

//LINE chart
UIChart chartLineArea.setChartStyle(UIChart.STYLE_LINES);
chartLineArea.setTitle("Monthly Statistics");
chartLineArea.setXAxisTitle("Month");
chartLineArea.setYAxisTitle("Grosstotalprice");
chartLineArea.setDataBook(rdbV_statistic_order_offer_month);
chartLineArea.setXColumnName("MONTH");
chartLineArea.setYColumnNames(new String[] { "OFFER_GROSSTOTALPRICE", "ORDER_GROSSTOTALPRICE" });

The complete source code is available on sourceforge. It's from our Demo ERP application.

The databooks are remote databooks because our Demo ERP is a 3-tier application. The databook definition:

//BAR Chart
RemoteDataBook rdbV_statistic_order_offer_year = new RemoteDataBook();
rdbV_statistic_order_offer_year.setName("v_statistic_order_offer_year");
rdbV_statistic_order_offer_year.setDataSource(getDataSource());
rdbV_statistic_order_offer_year.setReadOnly(true);
rdbV_statistic_order_offer_year.open();

//LINE Chart
RemoteDataBook rdbV_statistic_order_offer_month = new RemoteDataBook();
rdbV_statistic_order_offer_month.setName("v_statistic_order_offer_month");
rdbV_statistic_order_offer_month.setDataSource(getDataSource());
rdbV_statistic_order_offer_month.setReadOnly(true);
rdbV_statistic_order_offer_month.setMasterReference(
                                      new ReferenceDefinition(new String[] { "YEAR_" },
                                                              rdbV_statistic_order_offer_year,
                                                              new String[] { "YEAR_" }));
rdbV_statistic_order_offer_month.open();

Both remote databooks are connected to a remote storage, identified by name (v_statistic_order_offer_year, v_statistic_order_offer_month). The storages are defined as server objects:

//Name: v_statistic_order_offer_year (BAR CHART)
DBStorage dbsV_statistic_order_offer_year = new DBStorage();
dbsV_statistic_order_offer_year.setWritebackTable("v_statistic_order_offer_year");
dbsV_statistic_order_offer_year.setDBAccess(getDBAccess());
dbsV_statistic_order_offer_year.open();

//Name: v_statistic_order_offer_month (LINE CHART)
DBStorage dbsV_statistic_order_offer_month = new DBStorage();
dbsV_statistic_order_offer_month.setWritebackTable("v_statistic_order_offer_month");
dbsV_statistic_order_offer_month.setDBAccess(getDBAccess());
dbsV_statistic_order_offer_month.open();

Both storages read data from database views (instead of tables). The views are not magic and could also be simple tables, but our Demo ERP has views for demonstration purposes.

Here's the definition of the view, used for the BAR chart:

CREATE OR REPLACE VIEW v_statistic_order_offer_year AS
SELECT ofe.YEAR_ AS YEAR_,
       sum(ofe.GROSSTOTALPRICE) AS OFFER_GROSSTOTALPRICE,
       sum(case when ord.GROSSTOTALPRICE IS NULL
                then 0
                else ord.GROSSTOTALPRICE
           end) AS ORDER_GROSSTOTALPRICE
  FROM (v_statistic_offer ofe
        LEFT JOIN v_statistic_order ord
               ON (((ofe.YEAR_ = ord.YEAR_) AND (ofe.MONTH = ord.MONTH))))
 GROUP BY ofe.YEAR_;

That's it. It's really simple to bind a JavaFX chart to a database table or view.

JavaFX, JVx and data binding

Post to Twitter

We made great progress with our JavaFX UI for JVx. The whole UI stuff was already implemented and worked with some known problems. The next thing was data binding because this should fill the gap between UI and business logic. This means: bindings for a table, tree, chart, editors, cell editors, cell renderers, ... (hard work).

The data controls of JavaFX were very useful and we didn't implement our own controls because standard controls were working. Sure, we had a different model in JVx than the "model" in JavaFX, but also Swing and vaadin had different models. We're experts in such things.

We started with the table, because a table implementation should be easy without CRUD operations and of course, it was easy. The next thing was the integration of our cell renderers and cell editors because a date field should automatically use a date editor and a number field should use a number(only) editor. The same applies for checkboxes and comboboxes. This was a hard job because JavaFX' table had different concepts than e.g. swing. We're still working on the last bits but most functionality was implemented.

It's already possible to bind a database table/view to a JavaFX TableView without additional JDBC, ORM. Thanks to JVx, this works with all supported architectures (mem, 2-tier, 3-tier). Here's a first impression, followed by the source code:

TableView bound to JVx' DataBook

TableView bound to JVx' DataBook

UITable table = new UITable();
table.setDataBook(dataBook);

UIFormLayout editorsPaneLayout = new UIFormLayout();
editorsPaneLayout.setNewlineCount(2);

UIPanel editorsPane = new UIPanel();
editorsPane.setLayout(editorsPaneLayout);

addEditor(editorsPane, dataBook, "ID");
addEditor(editorsPane, dataBook, "BOOLEAN");
addEditor(editorsPane, dataBook, "STRING");
addEditor(editorsPane, dataBook, "CHOICE");
addEditor(editorsPane, dataBook, "DATETIME");
addEditor(editorsPane, dataBook, "NUMBER");
addEditor(editorsPane, dataBook, "TYPE_ID");
addEditor(editorsPane, dataBook, "TYPE_NAME");

UISplitPanel splitPanel = new UISplitPanel(UISplitPanel.SPLIT_LEFT_RIGHT);
splitPanel.setDividerAlignment(UISplitPanel.DIVIDER_BOTTOM_RIGHT);
splitPanel.setFirstComponent(table);
splitPanel.setSecondComponent(editorsPane);

UIPanel content = new UIPanel();
content.setLayout(new UIBorderLayout());
content.add(splitPanel, UIBorderLayout.CENTER);

(see Kitchensink application, hosted on github)

This was the whole source code for the table binding (= UI). The missing piece is the model. In our example, we've used the member dataBook. A databook is the model and controller of JVx. We have different implementations: MemDataBook, RemoteDataBook. A mem is like a database table, but in memory. A remote databook is connected to a remote/local storage. The storage provides data (from a database, filesystem, twitter, ...).

We didn't use a database in our kitchensink, so the dataBook was defined as MemDataBook:

RowDefinition rowdef = new RowDefinition();            
rowdef.addColumnDefinition(new ColumnDefinition("ID",
                                new BigDecimalDataType()));
rowdef.addColumnDefinition(new ColumnDefinition("STRING",
                                new StringDataType(new UITextCellEditor())));
rowdef.addColumnDefinition(new ColumnDefinition("BOOLEAN",
                                new BooleanDataType(new UICheckBoxCellEditor(
                                                    Boolean.TRUE, Boolean.FALSE))));
rowdef.addColumnDefinition(new ColumnDefinition("CHOICE",
                                new StringDataType(choiceCellEditor)));
rowdef.addColumnDefinition(new ColumnDefinition("DATETIME",
                                new TimestampDataType(new UIDateCellEditor("dd.MM.yyyy"))));
rowdef.addColumnDefinition(new ColumnDefinition("NUMBER",
                                new BigDecimalDataType(new UINumberCellEditor())));
rowdef.addColumnDefinition(new ColumnDefinition("TYPE_ID",
                                new BigDecimalDataType()));
rowdef.addColumnDefinition(new ColumnDefinition("TYPE_NAME",
                                new StringDataType(
                                    new UILinkedCellEditor(referenceDefinition))));
               
IDataBook dataBook = new MemDataBook(rowdef);
dataBook.setName("DATABINDING");               
dataBook.open();

It's the definition of a table with some columns and different column types, like String, Date, Number, ComboBox. It's easy to use a real database table if you read following article or this one.

The difference

What's the difference to standard JavaFX without JVx and why should you use JVx?

Here's the official documentation of Table View from Oracle. It's very complex to work with tables ;-) (seems to be so). The example in the official documentation doesn't use a database, like our example above!

The first difference: Save time and LoC.
This will reduce complexity and saves your dev time. It'll be easier to maintain an application with 1.000 LoC instead of 50.000.

Second difference: JVx already is a framework and library - don't create your own and waste dev time.

Advantage of JVx: Simply bind a database table with 10 LoC:

DBAccess dba = DBAccess.getDBAccess("jdbc:hsqldb:hsql://localhost/testdb");
dba.setUsername("sa");
dba.setPassword("");
dba.open();

DBStorage dbs = new DBStorage();
dbs.setDBAccess(dba);
dbs.setWritebackTable("testtable");
dbs.open();

StorageDataBook dataBook = new StorageDataBook(dbs);
dataBook.open();

This is enough to get full CRUD support for your tables.

This article was the first intro of our databinding implementation for JavaFX. The next artile will cover our chart implementation. Here's first a impression:

FX Chart binding

FX Chart binding

We love the clean UI of JavaFX and our maximized internal frames :)