Friday, December 25, 2009

Take that TreeView ... I win *ding ding ding*

The above grid was created with this code:

   dicts = [{"key?": True, "price":0.00,"tags" : "aaa bbb ccc","_foo":"bar"},
{"ID": 11, "key?": False, "price":2.00,"tags" : "bbb ccc ddd","_foo":"bar"},
{"key?": True, "price":33.00,"tags" : "ccc ddd eee","_foo":"bar"},
{"ID": 3, "tags" : "ddd eee fff","_foo":"bar"},
{"ID": 4, "price":5.00,"_foo":"bar"}]

grid = DictionaryGrid(dicts, ["ID","tags","price","key?"])

A couple of days ago I was rightfully miffed with the TreeView, as it has proven again and again to be flexible, but HIGHLY inconvenient to learn much less use. Specifically, the underlying data store for a TreeView could only accept values of the defined type, and missing values were automatically set to defaults. This meant there was no way to display partially complete data sets. But if the proper underlying value was not used, columns did not sort correctly for the specified type.

I have therefore spent a few hours over the last couple of days keeping two related backing stores for the dictionary grid. The display store keeps data proper for displaying to a user, particularly important being an empty string for string and number fields. The "real" store is the dictionary stored with each row that sets types one would expect, such as integers for counting columns, floats for currency, booleans for checks, etc...

For a sense of the effort involved in making TreeViews work the way you want, this work involved:
  1. Responding to clicks on the column header and determining whether the user toggled to ascending or descending sort.
  2. Setting the sort icon on and turn off the icon in the last sorted column.
  3. Tracking sorted columns in the DictionaryGrid to turn off the sorted icon when needed.
  4. Writing a sort_ascending and sort_descending function for each type of column.
  5. Creating format functions for two of the column classes to present currency and checkboxes in the correct format.
  6. Writing mappings from "display" values to "real" values and back.
I have evolved the DictionaryGrid over the last couple of weeks so that I could offer developers a much more fun and easy way to display data to users. The DictionaryGrid now supports features like:

DictionaryGrid Basic Features
  1. Infers column types from the names of the columns.
  2. Using spinners or checkboxes for non-string column types.
  3. Ability to handle dictionaries with data in "wrong" types (for example a Currency column with try to translate a literal string "33.00" to the number 33.00.
  4. Column sorting.
  5. Editing of inputted values.
  6. Easy access to underlying data stores
  7. Easy access to selected rows
  8. Easy access to removing selected rows
  9. Easy to add a "filtering" user interface
I am trying to avoid "advanced" features, but there are some extra supported featuers:
  1. Ability to explicitly set a column type
  2. Ability to explicitly set a filter for a column
  3. Ability to implement custom columns (currently requires creating a class with the necessary interface)
  4. Ability to implement custom filters through sub-classing or by adding filters to an existing filter type).
My next steps are to ensure that the GridFilter functionality is working in a robust manner. Then I will port bughugger over to the latest quidgets code. At that point, I will consider Quidgets "ready" for an initial release.

About Quidgets
Quickly + Widgets = Quidgets
There is a Launchpad Project for Quidgets
The most up to date changes are in the Quidgets Trunk Branch
You can install Quidgets from the my PPA