enthought/enaml — pull request #51 by rkern (2011-12-22 15:27:43+00:00)
Title: Selection models

I have implemented a `BaseSelectionModel` component that is intended to be the sole child of an `AbstractItemView` like the `TableView`. It has an intentionally sparse interface. It has methods to get and set the selection and an `Event` for when the selection changes (and also methods and an `Event` for the so-called "current" cell, but I haven't done much with that). More interesting interfaces are meant to be implemented as subclasses. I provide one such `RowSelectionModel` for `TableViews` and the future `ListView` that synchronizes the selection with a `selected_rows = List(Int)` much like the interface exposed by the `TabularEditor`.

A few points for review:

* Since the Qt object behind the `QtBaseSelectionModel` is not a `QWidget`, I had to work around some of the assumptions in `QtBaseComponent`. It would be nice, but not necessary to relax some of those assumptions. For now, I just keep the `.widget` attribute `None` and guard against that possibility in `QtBaseComponent`.

* Some flags controlling selection UI behavior live on `AbstractItemView`. This follows Qt's architecture. However, this does mean that the user may set these flags at odds with the semantics demanded by a particular `BaseSelectionModel` subclass. For example, leaving the default `selection_behavior = 'items'` while using a `RowSelectionModel` may not work well. We could move these flags onto the selection models such that subclasses could provide sensible defaults and to "package up" all of the selection options in one place in the Enaml hierarchy. The only oddity would be that the `QtBaseSelectionModel` implementation would be reaching up to call methods on its parent widget. I'd be willing to live with that internal oddity to make things safer and more streamlined for Enaml users.

* In the implementation of `RowSelectionModel`, I need to do two-way synchronization between a trait and the Qt object. When reading the Qt values and setting the trait values to match, we don't want a notification coming the other way to set the Qt values. I follow Traits UI's example, and keep a flag that controls when we are setting the trait "internally" such that the "set Qt values from the trait" handler will do nothing when it gets notified. We will need to do this often in `Component` implementations. We could generalize this and make it generally available as part of the `BaseComponent` interface. We could keep a set (well, a bag would be better) of trait names that are currently being set, a context manager method that adds the trait name to the set before we read the toolkit values and set the trait then removes the trait name from the set afterwards, and a method to check if a trait name is in the set.

* I haven't tested if `Include` works well.