Most widgets in a user interface will need to interact with data - either displaying it, or providing a way to manipulate it.
Well designed GUI applications will maintain a strong separation between the data, and how that data is displayed. This separation allows developers to radically change how data is visualized without changing the underlying interface for interacting with this data.
Toga encourages this separation by using data sources. Instead of directly telling a widget to display a particular value (or collection of values), Toga requires you to define a data source, and then tell a widget to display that source.
Built-in data sources¶
There are three built-in data source types in Toga:
- Value Sources: For managing a single value. A
Valuehas a single attribute,
value, which is the value that will be rendered for display purposes.
- List Sources: For managing a list of items, each of which has one or
more values. List data sources support the data manipulation methods you’d
expect of a
list, and return
Rowobjects. The attributes of each
Rowobject are the values that should be displayed.
- Tree Sources: For managing a hierarchy of items, each of which has one
or more values. Tree data sources also behave like a
list, except that each item returned is a
Node. The attributes of the
Nodeare the values that should be displayed; a
Nodealso has children, accessible using the
listinterface on the
Data sources communicate to widgets (and other data sources) using a listener interface. Once a data source has been created, any other object can register as a listener on that data source. When any significant event occurs to the data source, all listeners will be notified.
Notable events include: * Adding a new item * Removing an existing item * Changing a value on an item * Clearing an entire data source
If any attribute of a
Node is modified, the source
will generate a change event.
Custom data sources¶
Although Toga provides built-in data sources, in general, you shouldn’t use them. Toga’s data sources are wrappers around Python’s primitive data types - int, str, list, dict, and so on. While this is useful for quick demonstrations, or to visualize simple data, more complex applications should define their own data sources.
A custom data source enables you to provide a data manipulation API that makes
sense for your application. For example, if you were writing an application to
display files on a file system, you shouldn’t just build a dictionary of
files, and use that to construct a
TreeSource. Instead, you should write
FileSystemSource that reflects the files on the file system. Your
file system data source doesn’t need to expose
methods - because the end user doesn’t need an interface to “insert” files
into your filesystem. However, you might have a create_empty_file() method
that creates a new file in the filesystem and adds a representation to the
Custom data sources are also required to emit notifications whenever notable events occur. This allows the widgets rendering the data source to respond to changes in data. If a data source doesn’t emit notifications, widgets may not reflect changes in data.
A Value source is any object with a “value” attribute.
List data sources need to provide the following methods:
__len__(self)- returns the number of items in the list
__getitem__(self, index)- returns the item at position
indexof the list.
Each item returned by the List source is required to expose attributes matching the accessors for any widget using the source.
Tree data sources need to provide the following methods:
__len__(self)- returns the number of root nodes in the tree
__getitem__(self, index)- returns the root node at position
indexof the tree.
Each node returned by the Tree source is required to expose attributes matching the accessors for any widget using the source. The node is also required to implement the following methods:
__len__(self)- returns the number of children of the node.
__getitem__(self, index)- returns the child at position
indexof the node.
can_have_children(self)- returns True if the node is allowed to have children. The result of this method does not depend on whether the node actually has any children; it only describes whether it is allowed to store children.