Saturday, December 26, 2009

Quidgets DictionaryGrid Real Life Use Case

Bughugger is designed to make it easier for teams to manage Ubuntu related bugs by creating an extensible and fast desktop client for managing large lists of bugs.

Recall that I started working on Quidgets out of my annoyances with PyGtk programming for the Bughugger project. Specifically, the code required for the grid of bugs was extensive, repetitive, and hard to modify. I decided that I would never right TreeView code again, well, at least not directly.

This means that Bughugger is the ideal use case for me to understand the quidgets.widgets API. Here is the code that creates the above grid:

  def __setup_grid(self, bug_tasks):
"""set_up_treeview: sets up the treeview for the BugPan.

bug_tasks -- a dictionary to set up for the Pane
sw_filter = BlankFilterCombo()
sw_filter.append("=",lambda x,y: convert_to_num(x) == float(y) )
sw_filter.append("<",lambda x,y: convert_to_num(x) < float(y) )
sw_filter.append(">",lambda x,y: convert_to_num(x) > float(y) )
sw_filter.append("<=",lambda x,y: convert_to_num(x) <= float(y) )
sw_filter.append(">=",lambda x,y: convert_to_num(x) >= float(y) )
sw_filter2 = BlankFilterCombo()
sw_filter2.append("=",lambda x,y: convert_to_num(x) == float(y) )
sw_filter2.append("<",lambda x,y: convert_to_num(x) < float(y) )
sw_filter2.append(">",lambda x,y: convert_to_num(x) > float(y) )
sw_filter2.append("<=",lambda x,y: convert_to_num(x) <= float(y) )
sw_filter2.append(">=",lambda x,y: convert_to_num(x) >= float(y) )
filter_hints = {"status":sw_filter,"importance":sw_filter2}
type_hints = {"gravity":IntegerColumn,"effected users":IntegerColumn}
if self.keys is not None:
self.grid = DictionaryGrid(bug_tasks,self.keys,type_hints)
self.grid = DictionaryGrid(bug_tasks,[],type_hints)
grid_filt = GridFilter(self.grid,filter_hints)
#add the gridview to the top of the pane
self.pack_start(grid_filt, False)
#wire the treeview to the signal handlers
self.grid.connect("button_press_event", self.__treeview_clicked)
self.grid.connect("cursor_changed", self.selection_changed)
self.grid.connect("move-cursor", self.__cursor_moved)
self.grid.connect("select-all", self.selection_all)
self.grid.connect("select-cursor-row", self.selection_changed)
self.grid.connect("unselect-all", self.selection_none)
self.grid.connect("toggle-cursor-row", self.selection_changed)
#create a scrolled window for the treeview
grid_scroller = gtk.ScrolledWindow()
#add the scrolled window to the bottom of the pane
self.pack_start(grid_scroller, True, True)
If this looks like a lot of code, believe me, it's not. The code required to create a TreeView with the related sorting, filtering, and different types of columns would be probably 1o times the number of lines here, and would by much harder to change.

None the less, looking at the code, there are a few things that I don't like about the API at the moment:
  1. You have to connect to the myriad events related to cursor and selection changes in the DictionaryGrid. I should probably create a single "selection_changed" event, that should capture about half of the events above.
  2. Filter hints are instances of FilterCombo objects, but type hints are types. This is inconsistent and confusing. I should probably make type hints work like filter hints.
  3. You still have to manually place the menu based on the button press event. I should probably create a right click menu property on the grid that you can simply add to the grid. I could also potentially create a set of default right click functions, like "copy", and then have some simple methods to append new commands to it.
  4. There aren't enough conventions for setting types for columns. I should be able to use naming instead of the type hints. Perhaps I shall add "ends with 'count'" as a convention, and then can change "effected users" to "effected users count", and maybe something similar that would grab gravity as well, like "gravity total". Key naming seems more easy and fun than providing type hints and would reduce the number of objects required to customize the grid.
  5. Custom filters use the types stored for displaying in the grid rather than the backing values in the dictionaries. For instance, if you were making a custom filter for a CheckedColumn, rather than receiving a True or False, you would receive, a -1,0, or 1. This seems confusing to me and requires familiarity with the implementation details of DictionaryGrid. Perhaps I will replace the display values with the real values, or perhaps I may provide the display values along with the stored values.
  6. There are a few quidgets.prompts that I would like to add as well, but that's separate from the DictionaryGrid and related functionality. I'm thinking prompts.alert,, and prompts.task. Stay tuned.
In any case, the functionality is complete enough for bughugger, so I'm pushing this code, and will get Quidgets into Universe when I get back from vacation. Then we can get Bughugger into Universe as well.

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


  1. Thanks, Rick. Enjoying the sequence :-), and this is almost making me do graphical thingies...

    BTW -- a syntax on the current PPA/LP branch, on quidgets/ -- pretty much all the function blurbs are being closed by four double-quotes (instead of three). Easy fix ;-)

  2. Nice article, thanks for the information. It's very complete information. I will bookmark for next reference
    jaring futsal | jaring golf | jaring pengaman proyek |
    jaring pengaman bangunan | jaring pengaman gedung