# dupeGuru 4.3.1 documentation

## hscommon.gui.table

`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`.

  append(item: Row) → None

    Appends `item` at the end of the table.

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

  insert(index: int, item: Row) → None

    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.

  remove(row: Row) → None

    Removes `row` from table.

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

  sort_by(column_name: str, desc: bool = False) → None

    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.

  property footer: Row | None

    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.

  property header: Row | None

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

    See `footer` for details.

  property row_count: int

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

    int. read-only.

  property rows: List[Row]

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

    List of `Row`. read-only.

  property selected_row: Row

    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.

  property selected_rows: List[Row]

    List of selected rows based on `selected_indexes`.

    List of `Row`. read-only.

class hscommon.gui.table.Row(table: GUITable)

  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.

  can_edit() → bool

    (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()`.

  can_edit_cell(column_name: str) → bool

    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(attrname: str) → Any

    Get cell value for `attrname`.

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

  load() → None

    (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.

  save() → None

    (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: str, value: Any) → None

    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.

  sort_key_for_column(column_name: str) → Any

    (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`.

  _do_add() → Tuple[Row, int]

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

    Returns `(row, insert_index)`.

  _do_delete() → None

    (Virtual) Delete the selected rows.

  _fill() → None

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

    Called by `refresh()`. Does nothing by default.

  _is_edited_new() → bool

    (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.

  _restore_selection(previous_selection)

    (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() → None

    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: str, row_index: int) → bool

    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()`.

  cancel_edits() → None

    Cancels the current edit operation.

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

  delete() → None

    Delete the currently selected rows.

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

  refresh(refresh_view: bool = True) → None

    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.

  save_edits() → None

    Commit user edits to the model.

    This is done by calling `Row.save()`.

  sort_by(column_name: str, desc: bool = False) → None

    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: Row | 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()`.

  refresh() → None

    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() → None

    Start editing the currently selected row.

    Begin whatever inline editing support that the view supports.

  stop_editing() → None

    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.