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!