Source code for pyface.data_view.data_models.row_table_data_model
# (C) Copyright 2005-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
""" A row-oriented data model implementation.
This module provides a concrete implementation of a data model for the
case of non-hierarchical, row-oriented data.
"""
from collections.abc import Sequence
from traits.api import Instance, List, observe
from traits.observation.api import trait
from pyface.data_view.abstract_data_model import (
AbstractDataModel, DataViewSetError
)
from pyface.data_view.index_manager import IntIndexManager
from pyface.data_view.data_models.data_accessors import AbstractDataAccessor
[docs]class RowTableDataModel(AbstractDataModel):
""" A data model that presents a sequence of objects as rows.
The data is expected to be a sequence of row objects, each object
providing values for the columns via an AbstractDataAccessor subclass.
Concrete implementations can be found in the data_accessors module that
get data from attributes, indices of sequences, and keys of mappings,
but for more complex situations, custom accessors can be defined.
"""
#: A sequence of objects to display as rows.
data = Instance(Sequence, allow_none=False)
#: An object which describes how to map data for the row headers.
row_header_data = Instance(AbstractDataAccessor, allow_none=False)
#: An object which describes how to map data for each column.
column_data = List(Instance(AbstractDataAccessor, allow_none=False))
#: The index manager that helps convert toolkit indices to data view
#: indices.
index_manager = Instance(IntIndexManager, args=(), allow_none=False)
# Data structure methods
[docs] def get_column_count(self):
""" How many columns in the data view model.
Returns
-------
column_count : non-negative int
The number of columns that the data view provides.
"""
return len(self.column_data)
[docs] def can_have_children(self, row):
""" Whether or not a row can have child rows.
Only the root has children.
Parameters
----------
row : sequence of int
The indices of the row as a sequence from root to leaf.
Returns
-------
can_have_children : bool
Whether or not the row can ever have child rows.
"""
return len(row) == 0
[docs] def get_row_count(self, row):
""" How many child rows the row currently has.
Parameters
----------
row : sequence of int
The indices of the row as a sequence from root to leaf.
Returns
-------
row_count : non-negative int
The number of child rows that the row has.
"""
if len(row) == 0:
return len(self.data)
else:
return 0
# Data value methods
[docs] def get_value(self, row, column):
""" Return the Python value for the row and column.
This uses the row_header_data and column_data accessors to extract
values for the row and column.
Parameters
----------
row : sequence of int
The indices of the row as a sequence from root to leaf.
column : sequence of int
The indices of the column as a sequence of length 0 or 1.
Returns
-------
value : Any
The value represented by the given row and column.
"""
if len(column) == 0:
column_data = self.row_header_data
else:
column_data = self.column_data[column[0]]
if len(row) == 0:
return column_data.title
obj = self.data[row[0]]
return column_data.get_value(obj)
[docs] def can_set_value(self, row, column):
""" Whether the value in the indicated row and column can be set.
This uses the row_header_data and column_data accessors to determine
if the value may be changed.
Parameters
----------
row : sequence of int
The indices of the row as a sequence from root to leaf.
column : sequence of int
The indices of the column as a sequence of length 0 or 1.
Returns
-------
can_set_value : bool
Whether or not the value can be set.
"""
if len(row) == 0:
return False
if len(column) == 0:
column_data = self.row_header_data
else:
column_data = self.column_data[column[0]]
obj = self.data[row[0]]
return column_data.can_set_value(obj)
[docs] def set_value(self, row, column, value):
""" Set the Python value for the row and column.
This uses the row_header_data and column_data accessors to set
the value.
Parameters
----------
row : sequence of int
The indices of the row as a sequence from root to leaf.
column : sequence of int
The indices of the column as a sequence of length 0 or 1.
value : Any
The new value for the given row and column.
Raises
-------
DataViewSetError
If the value cannot be set.
"""
if len(row) == 0:
raise DataViewSetError("Can't set column titles.")
if len(column) == 0:
column_data = self.row_header_data
else:
column_data = self.column_data[column[0]]
obj = self.data[row[0]]
column_data.set_value(obj, value)
self.values_changed = (row, column, row, column)
[docs] def get_value_type(self, row, column):
""" Return the value type of the given row and column.
This uses the row_header_data and column_data accessors to get
the value type.
Parameters
----------
row : sequence of int
The indices of the row as a sequence from root to leaf.
column : sequence of int
The indices of the column as a sequence of length 0 or 1.
Returns
-------
value_type : AbstractValueType or None
The value type of the given row and column, or None if no value
should be displayed.
"""
if len(column) == 0:
column_data = self.row_header_data
else:
column_data = self.column_data[column[0]]
if len(row) == 0:
return column_data.title_type
return column_data.value_type
# data update methods
@observe("data")
def _update_data(self, event):
self.structure_changed = True
@observe(trait("data", notify=False).list_items(optional=True))
def _update_data_items(self, event):
if len(event.added) != len(event.removed):
# number of rows has changed
self.structure_changed = True
else:
if isinstance(event.index, int):
start = event.index
stop = min(event.index + len(event.added), len(self.data)) - 1
else:
start = event.index.start
stop = min(event.index.stop, len(self.data)) - 1
self.values_changed = ((start,), (), (stop,), ())
@observe('row_header_data')
def _update_row_header_data(self, event):
self.values_changed = ((), (), (), ())
@observe('row_header_data:updated')
def _update_row_header_data_event(self, event):
if event.new[1] == 'value':
if len(self.data) > 0:
self.values_changed = ((0,), (), (len(self.data) - 1,), ())
else:
self.values_changed = ((), (), (), ())
@observe('column_data.items')
def _update_all_column_data_items(self, event):
self.structure_changed = True
@observe('column_data:items:updated')
def _update_column_data(self, event):
index = self.column_data.index(event.new[0])
if event.new[1] == 'value':
if len(self.data) > 0:
self.values_changed = (
(0,), (index,), (len(self.data) - 1,), (index,)
)
else:
self.values_changed = ((), (index,), (), (index,))
# default data value
def _data_default(self):
return []