Table() Sortable and selectable sequence of Row.
Row(table) Represents a row in a Table.
GUITable() Cross-toolkit GUI-enabled table view.
GUITableView Expected interface for GUITable’s view.
class hscommon.gui.table.Table

Sortable and selectable sequence of Row.

In fact, the Table is very similar to SelectableList in practice and differs mostly in principle. Their difference lies in the nature of their items they manage. With the Table, rows usually have many properties, presented in columns, and they have to subclass Row.

Usually used with Column.

Subclasses Selectable.


Appends item at the end of the table.

If there’s a footer, the item is inserted before it.

insert(index, item)

Inserts item at index in the table.

If there’s a header, will make sure we don’t insert before it, and if there’s a footer, will make sure that we don’t insert after it.


Removes row from table.

If row is a header or footer, that header or footer will be set to None.

sort_by(column_name, desc=False)

Sort table by column_name.

Sort key for each row is computed from Row.sort_key_for_column().

If desc is True, sort order is reversed.

If present, header and footer will always be first and last, respectively.


If set, a row that always stay at the bottom of the table.

Row. get/set.

When set to something else than None, header and footer represent rows that will always be kept in first and/or last position, regardless of sorting. len() and indexing will include them, which means that if there’s a header, table[0] returns it and if there’s a footer, table[-1] returns it. To make things short, all list-like functions work with header and footer “on”. But things get fuzzy for append() and insert() because these will ensure that no “normal” row gets inserted before the header or after the footer.

Adding and removing footer here and there might seem (and is) hackish, but it’s much simpler than the alternative (when, of course, you need such a feature), which is to override magic methods and adjust the results. When we do that, there the slice stuff that we have to implement and it gets quite complex. Moreover, the most frequent operation on a table is __getitem__, and making checks to know whether the key is a header or footer at each call would make that operation, which is the most used, slower.


If set, a row that always stay at the bottom of the table.

See footer for details.


Number or rows in the table (without counting header and footer).

int. read-only.


List of rows in the table, excluding header and footer.

List of Row. read-only.


Selected row according to Selectable.selected_index.

Row. get/set.

When setting this attribute, we look up the index of the row and set the selected index from there. If the row isn’t in the list, selection isn’t changed.


List of selected rows based on selected_indexes.

List of Row. read-only.

class hscommon.gui.table.Row(table)

Represents a row in a Table.

It holds multiple values to be represented through columns. It’s its role to prepare data fetched from model instances into ready-to-present-in-a-table fashion. You will do this in load().

When you do this, you’ll put the result into arbitrary attributes, which will later be fetched by your table for presentation to the user.

You can organize your attributes in whatever way you want, but there’s a convention you can follow if you want to minimize subclassing and use default behavior:

  1. Attribute name = column name. If your attribute is foobar, whenever we refer to column_name, you refer to that attribute with the column name foobar.
  2. Public attributes are for formatted value, that is, user readable strings.
  3. Underscore prefix is the unformatted (computable) value. For example, you could have _foobar at 42 and foobar at "42 seconds" (what you present to the user).
  4. Unformatted values are used for sorting.
  5. If your column name is a python keyword, add an underscore suffix (from_).

Of course, this is only default behavior. This can be overriden.


(Virtual) Whether the whole row can be edited.

By default, always returns True. This is for the whole row. For individual cells, it’s can_edit_cell().


Returns whether cell for column column_name can be edited.

By the default, the check is done in many steps:

  1. We check whether the whole row can be edited with can_edit(). If it can’t, the cell can’t either.
  2. If the column doesn’t exist as an attribute, we can’t edit.
  3. If we have an attribute can_edit_<column_name>, return that.
  4. Check if our attribute is a property. If it’s not, it’s not editable.
  5. If our attribute is in fact a property, check whether the property is “settable” (has a fset method). The cell is editable only if the property is “settable”.

Get cell value for attrname.

By default, does a simple getattr(), but it is used to allow subclasses to have alternative value storage mechanisms.


(Virtual/Required) Loads up values from the model to be presented in the table.

Usually, our model instances contain values that are not quite ready for display. If you have number formatting, display calculations and other whatnots to perform, you do it here and then you put the result in an arbitrary attribute of the row.


(Virtual/Required) Saves user edits into your model.

If your table is editable, this is called when the user commits his changes. Usually, these are typed up stuff, or selected indexes. You have to do proper parsing and reference linking, and save that stuff into your model.

set_cell_value(attrname, value)

Set cell value to value for attrname.

By default, does a simple setattr(), but it is used to allow subclasses to have alternative value storage mechanisms.


(Virtual) Return the value that is to be used to sort by column column_name.

By default, looks for an attribute with the same name as column_name, but with an underscore prefix (“unformatted value”). If there’s none, tries without the underscore. If there’s none, raises AttributeError.

class hscommon.gui.table.GUITable

Cross-toolkit GUI-enabled table view.

Represents a UI element presenting the user with a sortable, selectable, possibly editable, table view.

Behaves like the Table which it subclasses, but is more focused on being the presenter of some model data to its GUIObject.view. There’s a refresh() mechanism which ensures fresh data while preserving sorting order and selection. There’s also an editing mechanism which tracks whether (and which) row is being edited (or added) and save/cancel edits when appropriate.

Subclasses Table and GUIObject. Expected view: GUITableView.


(Virtual) Creates a new row, adds it in the table.

Returns (row, insert_index).


(Virtual) Delete the selected rows.


(Virtual/Required) Fills the table with all the rows that this table is supposed to have.

Called by refresh(). Does nothing by default.


(Virtual) Returns whether the currently edited row should be considered “new”.

This is used in cancel_edits() to know whether the cancellation of the edit means a revert of the row’s value or the removal of the row.

By default, always false.


(Virtual) Restores row selection after a contents-changing operation.

Before each contents changing operation, we store our previously selected indexes because in many cases, such as in refresh(), our selection will be lost. After the operation is over, we call this method with our previously selected indexes (in previous_selection).

The default behavior is (if we indeed have an empty selected_indexes) to re-select previous_selection. If it was empty, we select the last row of the table.

This behavior can, of course, be overriden.


Add a new row in edit mode.

Requires do_add() to be implemented. The newly added row will be selected and in edit mode.

can_edit_cell(column_name, row_index)

Returns whether the cell at row_index and column_name can be edited.

A row is, by default, editable as soon as it has an attr with the same name as column. If Row.can_edit() returns False, the row is not editable at all. You can set editability of rows at the attribute level with can_edit_* properties.

Mostly just a shortcut to Row.can_edit_cell().


Cancels the current edit operation.

If there’s an edited row, it will be re-initialized (with Row.load()).


Delete the currently selected rows.

Requires _do_delete() for this to have any effect on the model. Cancels editing if relevant.


Empty the table and re-create its rows.

_fill() is called after we emptied the table to create our rows. Previous sort order will be preserved, regardless of the order in which the rows were filled. If there was any edit operation taking place, it’s cancelled.

Parameters:refresh_view (bool) – Whether we tell our view to refresh after our refill operation. Most of the time, it’s what we want, but there’s some cases where we don’t.

Commit user edits to the model.

This is done by calling Row.save().

sort_by(column_name, desc=False)

Sort table by column_name.

Overrides Table.sort_by(). After having performed sorting, calls _update_selection() to give you the chance, if appropriate, to update your selected indexes according to, maybe, the selection that you have in your model.

Then, we refresh our view.

edited = None

The row being currently edited by the user. None if no edit is taking place.

class hscommon.gui.table.GUITableView

Expected interface for GUITable’s view.

Not actually used in the code. For documentation purposes only.

Our view, some kind of table view, is expected to sync with the table’s contents by appropriately behave to all callbacks in this interface.

When in edit mode, the content types by the user is expected to be sent as soon as possible to the Row.

Whenever the user changes the selection, we expect the view to call Table.select().


Refreshes the contents of the table widget.

Ensures that the contents of the table widget is synced with the model. This includes selection.


Start editing the currently selected row.

Begin whatever inline editing support that the view supports.


Stop editing if there’s an inline editing in effect.

There’s no “aborting” implied in this call, so it’s appropriate to send whatever the user has typed and might not have been sent down to the Row yet. After you’ve done that, stop the editing mechanism.