Source code for pyface.tree.node_tree_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!
""" The model for a tree control with extensible node types. """
from traits.api import Dict, Instance
from .node_manager import NodeManager
from .tree_model import TreeModel
[docs]class NodeTreeModel(TreeModel):
""" The model for a tree control with extensible node types. """
# 'NodeTreeModel' interface --------------------------------------------
# The node manager looks after all node types.
node_manager = Instance(NodeManager, ())
# Private interface ----------------------------------------------------
# Node monitors.
_monitors = Dict()
# ------------------------------------------------------------------------
# 'TreeModel' interface.
# ------------------------------------------------------------------------
[docs] def has_children(self, node):
""" Returns True if a node has children, otherwise False.
This method is provided in case the model has an efficient way to
determine whether or not a node has any children without having to
actually get the children themselves.
"""
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
if node_type.allows_children(node):
has_children = node_type.has_children(node)
else:
has_children = False
return has_children
[docs] def get_children(self, node):
""" Returns the children of a node. """
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
# Does the node allow children (ie. a folder allows children, a file
# does not)?
if node_type.allows_children(node):
# Get the node's children.
children = node_type.get_children(node)
else:
children = []
return children
[docs] def get_default_action(self, node):
""" Returns the default action for a node. """
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.get_default_action(node)
[docs] def get_drag_value(self, node):
""" Get the value that is dragged for a node.
By default the drag value is the node itself.
"""
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.get_drag_value(node)
[docs] def can_drop(self, node, data):
""" Returns True if a node allows an object to be dropped onto it. """
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.can_drop(node, data)
[docs] def drop(self, node, data):
""" Drops an object onto a node. """
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
node_type.drop(node, data)
[docs] def get_image(self, node, selected, expanded):
""" Returns the label image for a node.
Return None (the default) if no image is required.
"""
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.get_image(node, selected, expanded)
[docs] def get_key(self, node):
""" Generate a unique key for a node. """
return self.node_manager.get_key(node)
[docs] def get_selection_value(self, node):
""" Get the value that is used when a node is selected.
By default the selection value is the node itself.
"""
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.get_selection_value(node)
[docs] def get_text(self, node):
""" Returns the label text for a node.
Return None if no text is required. By default we return 'str(node)'.
"""
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.get_text(node)
[docs] def can_set_text(self, node, text):
""" Returns True if the node's label can be set. """
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.can_set_text(node, text)
[docs] def set_text(self, node, text):
""" Sets the label text for a node. """
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.set_text(node, text)
[docs] def is_collapsible(self, node):
""" Returns True if the node is collapsible, otherwise False. """
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.is_collapsible(node)
[docs] def is_draggable(self, node):
""" Returns True if the node is draggable, otherwise False. """
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.is_draggable(node)
[docs] def is_editable(self, node):
""" Returns True if the node is editable, otherwise False.
If the node is editable, its text can be set via the UI.
"""
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.is_editable(node)
[docs] def is_expandable(self, node):
""" Returns True if the node is expandanble, otherwise False. """
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
return node_type.is_expandable(node)
[docs] def add_listener(self, node):
""" Adds a listener for changes to a node. """
# Determine the node type for this node.
node_type = self.node_manager.get_node_type(node)
# Create a monitor to listen for changes to the node.
monitor = node_type.get_monitor(node)
if monitor is not None:
self._start_monitor(monitor)
self._monitors[self.node_manager.get_key(node)] = monitor
[docs] def remove_listener(self, node):
""" Removes a listener for changes to a node. """
key = self.node_manager.get_key(node)
monitor = self._monitors.get(key)
if monitor is not None:
self._stop_monitor(monitor)
del self._monitors[key]
return
# ------------------------------------------------------------------------
# 'NodeTreeModel' interface.
# ------------------------------------------------------------------------
# ------------------------------------------------------------------------
# 'Private' interface
# ------------------------------------------------------------------------
def _start_monitor(self, monitor):
""" Starts a monitor. """
monitor.observe(self._on_nodes_changed, "nodes_changed")
monitor.observe(self._on_nodes_inserted, "nodes_inserted")
monitor.observe(self._on_nodes_removed, "nodes_removed")
monitor.observe(self._on_nodes_replaced, "nodes_replaced")
monitor.observe(self._on_structure_changed, "structure_changed")
monitor.start()
def _stop_monitor(self, monitor):
""" Stops a monitor. """
monitor.observe(self._on_nodes_changed, "nodes_changed", remove=True)
monitor.observe(self._on_nodes_inserted, "nodes_inserted", remove=True)
monitor.observe(self._on_nodes_removed, "nodes_removed", remove=True)
monitor.observe(self._on_nodes_replaced, "nodes_replaced", remove=True)
monitor.observe(
self._on_structure_changed, "structure_changed", remove=True
)
monitor.stop()
return
# Trait event handlers -------------------------------------------------
# Static ----
# fixme: Commented this out as listeners are added and removed by the tree.
# This caused duplicate monitors to be created for the root node.
## def _root_changed(self, old, new):
## """ Called when the root of the model has been changed. """
## if old is not None:
## # Remove a listener for structure/appearance changes
## self.remove_listener(old)
## if new is not None:
## # Wire up a listener for structure/appearance changes
## self.add_listener(new)
## return
# Dynamic ----
def _on_nodes_changed(self, event):
""" Called when nodes have changed. """
self.nodes_changed = event.new
def _on_nodes_inserted(self, event):
""" Called when nodes have been inserted. """
self.nodes_inserted = event.new
def _on_nodes_removed(self, event):
""" Called when nodes have been removed. """
self.nodes_removed = event.new
def _on_nodes_replaced(self, event):
""" Called when nodes have been replaced. """
self.nodes_replaced = event.new
def _on_structure_changed(self, event):
""" Called when the structure of a node has changed drastically. """
self.structure_changed = event.new
return