Adding a table in my Jwt Clojure app
Here are the results of my exploration of Jwt tables with Clojure. The code is inspired by an example in the Jwt sources.
A table in Jwt is created with WTreeView
let [ tree-view (WTreeView.) ]
and the columns are configure in a model object. In my case, I'll use the WStandardItemModel . When instanciating the model, you tell it how many row and columns it should have, in this case 0 rows (I don't know yet why I should pass anything else than 0 here) and 4 columns:
(WStandardItemModel. 0 4)
Once the model is intanciated, we can set the column headers with the setHeaderData method:
(.setHeaderData model 0 Orientation/Horizontal "Places") (.setHeaderData model 1 Orientation/Horizontal "Weather") (.setHeaderData model 2 Orientation/Horizontal "Drink") (.setHeaderData model 3 Orientation/Horizontal "Visited")
Now we should add data to the table! A data row is represented by a list of WStandardItem objects, each being the data displayed in a column, so in our case we'll have each row represented by a list of 4 elements. A list? That's perfect for Clojure! I'll build a list of the items I want in the table and map them to a list of WStandardItems, like this:
(map #( WStandardItem. % ) '("Leuven" "Rain" "Beer" "Yes") )
For the fun I defined a function building the WStandardItem:
(defn standard-itemize [ item ] (WStandardItem. item))
Which lets me write this code to build a row:
(map standard-itemize '("Leuven" "Rain" "Beer" "Yes") )
When we have the row, we can add it to the table with appendRow :
(.appendRow model row)
We can the set assign this model to the tree-view with :setModel
(.setModel tree-view model)
Once we know that, it's trivial to work on list of rows:
(let [data'(("Leuven" "Rain" "Beer" "Yes")("Brussels" "Rain" "Beer" "Yes")) ) rows (map #( map standard-itemize %) data)] .... (doall (map #(.appendRow model %) rows))
With a function I called standard-rowize taking a row of data and returning a row of WStandardItems, we get somewhat cleaner code:
(defn standard-rowize [ row ] (map standard-itemize row)) (let [data'(("Leuven" "Rain" "Beer" "Yes")("Brussels" "Rain" "Beer" "Yes")) ) rows (map standard-rowize data)] ... (doall (map #(.appendRow model %) rows))
Here's the result, a table with row ordering and alternating row colors
But there's more to it than this. As the table is represented by a tree view, it can group rows and that is achieved really easily: each row can be the container of its sub rows! Here are the only changes to do:
Create a europe-row which will contain european cities
europe-row (WStandardItem. "Europe")
appends the rows to the europe-row rather than to the model:
(doall (map #(.appendRow europe-row %) rows))
and finally, append the europe-row to the model:
(.appendRow model europe-row)
This is all what's needed to get this:
The changes were so small that despite the fact that I discovered all this, it worked at the first try! Here's the code of the function buidling the tree-view:
(defn build-tree-view [] ( let [ tree-view (WTreeView.) model (WStandardItemModel. 0 4) europe-row (WStandardItem. "Europe") rows (map standard-rowize '(("Leuven" "Rain" "Beer" "Yes")("Brussels" "Rain" "Waffles" "Yes")) ) ] (.setHeaderData model 0 Orientation/Horizontal "Places") (.setHeaderData model 1 Orientation/Horizontal "Weather") (.setHeaderData model 2 Orientation/Horizontal "Item") (.setHeaderData model 3 Orientation/Horizontal "Visited") (.setAlternatingRowColors tree-view true) (doall (map #(.appendRow europe-row %) rows)) (.appendRow model europe-row) (.setModel tree-view model) tree-view))
Let's go further and manipulated the table: let's add a button to toggle the alternated row colors. Again, the changes to make are minimal:
- instanciate a WPushButton
- instanciate a WContainerWidget and pass it as the parent widget to the button and treeview constructor
- return the container in place of the treeview
- add a listener to the click event of the button to toggle the rows coloring. This uses the macro written for the Jwt-Clojure previous post.
Here is the code where I indicated the lines that were added or adapted:
(defn build-tree-view [] ( let [ container (WContainerWidget.) ;added button (WPushButton. "Toggle colors" container) ;added tree-view (WTreeView. container) ;adapted model (WStandardItemModel. 0 4) europe-row (WStandardItem. "Europe") rows (map standard-rowize '(("Leuven" "Rain" "Beer" "Yes")("Brussels" "Rain" "Waffles" "Yes")) ) ] (.. button clicked (addListener container (create-listener [mouse-event] (.setAlternatingRowColors tree-view (not (.hasAlternatingRowColors tree-view) ))))) ; added (.setHeaderData model 0 Orientation/Horizontal "Places") (.setHeaderData model 1 Orientation/Horizontal "Weather") (.setHeaderData model 2 Orientation/Horizontal "Item") (.setHeaderData model 3 Orientation/Horizontal "Visited") (.setAlternatingRowColors tree-view true) (doall (map #(.appendRow europe-row %) rows)) (.appendRow model europe-row) (.setModel tree-view model) ;adapted container)) ;adapted
Although there's a lot more to explore (pagination being the first thing I want to explore next), this is is for now. I hope this encourages to test Jwt and Clojure, both being very exciting projects!