Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional documentation for the DataView #763

Merged
merged 6 commits into from
Oct 16, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 131 additions & 2 deletions docs/source/data_view.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,104 @@ Note: with the current implementations, the |selection| list should not be
mutated, rather the entire list should be replaced on every change. This
restriction may be relaxed in the future.


Drag and Drop
-------------

The |IDataViewWidget| interface provides hooks to support dragging the
selected values out of the table, or dropping objects onto the data view.
To provide cross-platform and cross-toolkit compatibility, drag and drop
operations require the data that is being exported or imported to be
converted to or from a bytestring in some MIME type.

The DataView infrastructure provides a |DataFormat| named tuple to
encapsulate the process of converting different data objects to bytes.
For string objects this conversion might be as simple as encoding the
text as UTF-8 and declaring it to be a ``text/plain`` MIME type, but for
more complex structures there is serialization and deserialization which
needs to occur. The |DataFormat| objects are expected to provide the
mimetype of the data, a function to serialize an object, and a function
to deserialize bytes.

In practice the sorts of objects being dragged and dropped, can be
classified as simple scalar values (such as might occur when the selection
is a single item), 1D collections of values (such as might occur when
multiple items are selected, or a single row or column is selected),
or 2D collections of values (such as might occur for extended row or
column selections).

The DataView api provides a standard data formats for plain text, CSV,
and .npy format for scalar, 1D and 2D exports; HTML and JSON formats
for scalar values, as well as standard serializers and deserializers
for users to create build their own |DataFormat| instances if the defaults
do not match the needs.

Dragging
~~~~~~~~

To allow dragging the selection, the |exporters| trait should hold a list
of |AbstractDataExporter| instances. This class provides methods to access
the values to be exported from the selected indices, as well as a reference
to a |DataFormat| that will perform the actual serialization and provides
the MIME type.

In practice, users will usually use a standard data exporter, such as the
|ItemExporter| or |RowExporter|. Some care should be taken that
the data exporter provides data in the shape that the |DataFormat| expects.
For example, the |ItemExporter| works best when paired with scalar data
formats. In many cases all that is needed to enable dragging data from a
DataViewWidget is to configure it appropriately:

.. code-block:: python

control = DataViewWidget(
...,
selection_mode='extended',
exporters=[
RowExporter(format=table_format),
RowExporter(format=csv_format),
],
...
)

When multiple exporters are provided, _all_ of the supported formats are
exported as part of the drag operation, and it is up to the target program
to decide which of the supplied formats it can best handle, if any.

Dropping
~~~~~~~~

The |IDataViewWidget| supports dropping of objects via the |IDropHandler|
interface supported by other widgets. Developers using DataViews can
handle dropped data by providing a list of |IDropHandler| instances which
tell the underlying code whether the objects being dropped can be dropped,
and if so, how to handle the drop operation.

For example, to handle files being dropped onto the DataView, a DataView could
use the generic |FileDropHandler| class, coupled with a callback to load the
data from the dropped file.

.. code-block:: python

control = DataViewWidget(
...,
drop_handlers=[
FileDropHandler(
extensions=['.csv', '.tsv', '.npy'],
open_file=self.load_data,
)
],
...
)

When multiple drop handlers are supplied, the first one which says it can
handle the dropped objects is the one which is used.

There are currently no specific drop handlers for supporting dragging
data within the table, but this can be supported by custom drop handlers
that use toolkit code to interact with the underlying toolkit objects.


Index Managers
--------------

Expand Down Expand Up @@ -177,7 +275,8 @@ it corresponds to the keys or the values. The code looks like this:
:end-at: return value

Conversion of values into data channels is done by providing a value type
for each cell. The |get_value_type| method provides an appropriate data
for each cell that implements the |AbstractValueType| interface. The
|get_value_type| method is expected to provide an appropriate data
type for each item in the table. For this data model we have three value
types: the column headers, the keys and the values.

Expand All @@ -199,6 +298,27 @@ value types:
:start-at: def get_value_type
:end-at: return self.value_type

The |AbstractValueType| interface provides getters (and in some cases setters)
for various data channels the most obvious of these is the text to display
in an item, but channels allow checked state, image, color and tooltips
to also be associated with a value. How (or even if) these values are
displayed or used is up to the implementation of the |IDataViewWidget|.

As noted above, the DataView API provides a number of pre-definited value
type implementations that cover common cases, but where they do not meet the
needs of a particular design, developers should create their own
implementations with the desired properties.

Invalid Values
~~~~~~~~~~~~~~

If no valid value can be generated for some *expected* reason, value
generation code can raise a |DataViewGetError| exception. This error
will be handled and silently ignored by the DataView code, and no value
will be displayed. Any other errors raised by value generation are
assumed to be unexpected and will be logged and re-raised, which is
likely to cause an application crash.

Handling Updates
~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -278,11 +398,19 @@ the |has_editor_value| method returns ``False``.

.. |AbstractIndexManager| replace:: :py:class:`~pyface.data_view.index_manager.AbstractIndexManager`
.. |AbstractDataModel| replace:: :py:class:`~pyface.data_view.abstract_data_model.AbstractDataModel`
.. |DataViewSetError| replace:: :py:class:`~pyface.data_view.abstract_data_model.DataViewSetError`
.. |AbstractDataExporter| replace:: :py:class:`~pyface.data_view.abstract_data_exporter.AbstractDataExporter`
.. |AbstractValueType| replace:: :py:class:`~pyface.data_view.abstract_value_type.AbstractValueType`
.. |DataFormat| replace:: :py:class:`~pyface.data_view.i_data_wrapper.DataFormat`
.. |DataViewGetError| replace:: :py:class:`~pyface.data_view.data_view_errors.DataViewGetError`
.. |DataViewSetError| replace:: :py:class:`~pyface.data_view.data_view_errors.DataViewSetError`
.. |EditableValue| replace:: :py:class:`~pyface.data_view.value_types.editable_value.EditableValue`
.. |FileDropHandler| replace:: :py:class:`~pyface.drop_handler.FileDropHandler`
.. |IDataViewWidget| replace:: :py:class:`~pyface.data_view.i_data_view_widget.IDataViewWidget`
.. |IDropHandler| replace:: :py:class:`~pyface.i_drop_handler.IDropHandler`
.. |IntIndexManager| replace:: :py:class:`~pyface.data_view.index_manager.IntIndexManager`
.. |IntValue| replace:: :py:class:`~pyface.data_view.value_types.numeric_value.IntValue`
.. |ItemExporter| replace:: :py:class:`~pyface.data_view.exporters.item_exporter.ItemExporter`
.. |RowExporter| replace:: :py:class:`~pyface.data_view.exporters.row_exporter.RowExporter`
.. |TextValue| replace:: :py:class:`~pyface.data_view.value_types.text_value.TextValue`
.. |TupleIndexManager| replace:: :py:class:`~pyface.data_view.index_manager.TupleIndexManager`
.. |can_have_children| replace:: :py:meth:`~pyface.data_view.abstract_data_model.AbstractDataModel.can_have_children`
Expand All @@ -292,6 +420,7 @@ the |has_editor_value| method returns ``False``.
.. |get_value| replace:: :py:meth:`~pyface.data_view.abstract_data_model.AbstractDataModel.get_value`
.. |get_value_type| replace:: :py:meth:`~pyface.data_view.abstract_data_model.AbstractDataModel.get_value`
.. |has_editor_value| replace:: :py:meth:`~pyface.data_view.abstract_value_type.AbstractValueType.has_editor_value`
.. |exporters| replace:: :py:attr:`~pyface.data_view.i_data_view_widget.IDataViewWidget.exporters`
.. |selection| replace:: :py:attr:`~pyface.data_view.i_data_view_widget.IDataViewWidget.selection`
.. |selection_mode| replace:: :py:attr:`~pyface.data_view.i_data_view_widget.IDataViewWidget.selection_mode`
.. |selection_type| replace:: :py:attr:`~pyface.data_view.i_data_view_widget.IDataViewWidget.selection_type`
Expand Down