Skip to content

Commit

Permalink
Merge branch 'maint/6.1' into release/6.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
corranwebster committed Jun 3, 2019
2 parents 6b400a3 + a324afc commit 1d61a78
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 4 deletions.
109 changes: 108 additions & 1 deletion traitsui/tests/editors/test_tree_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import unittest

from traits.api import Bool, HasTraits, Instance, List, Str
from traitsui.api import Item, TreeEditor, TreeNode, View
from traitsui.api import (
Item, ObjectTreeNode, TreeEditor, TreeNode, TreeNodeObject, View
)

from traitsui.tests._tools import (
press_ok_button, skip_if_null, skip_if_not_qt4,
Expand Down Expand Up @@ -80,6 +82,47 @@ def default_traits_view(self):
return traits_view


class BogusTreeNodeObject(TreeNodeObject):
""" A bogus tree node. """

name = Str("Bogus")

bogus_list = List


class BogusTreeNodeObjectView(HasTraits):
""" A traitsui view visualizing Bogus objects as trees. """

bogus = Instance(BogusTreeNodeObject)

hide_root = Bool

nodes = List(TreeNode)

def _nodes_default(self):
return [
TreeNode(
node_for=[BogusTreeNodeObject],
children='bogus_list',
label='=Bogus'
)
]

def default_traits_view(self):
tree_editor = TreeEditor(
nodes=self.nodes,
hide_root=self.hide_root,
editable=False,
)

traits_view = View(
Item(name='bogus', id='engine', editor=tree_editor),
buttons=['OK'],
)

return traits_view


class TestTreeView(unittest.TestCase):

def _test_tree_editor_releases_listeners(self, hide_root, nodes=None,
Expand Down Expand Up @@ -110,6 +153,34 @@ def _test_tree_editor_releases_listeners(self, hide_root, nodes=None,
notifiers_list = bogus.trait(trait)._notifiers(False)
self.assertEqual(0, len(notifiers_list))

def _test_tree_node_object_releases_listeners(self, hide_root, nodes=None,
trait='bogus_list',
expected_listeners=1):
""" The TreeEditor should release the listener to the root node's children
when it's disposed of.
"""

with store_exceptions_on_all_threads():
bogus = BogusTreeNodeObject(bogus_list=[BogusTreeNodeObject()])
tree_editor_view = BogusTreeNodeObjectView(
bogus=bogus,
hide_root=hide_root,
nodes=nodes,
)
ui = tree_editor_view.edit_traits()

# The TreeEditor sets a listener on the bogus object's
# children list
notifiers_list = bogus.trait(trait)._notifiers(False)
self.assertEqual(expected_listeners, len(notifiers_list))

# Manually close the UI
press_ok_button(ui)

# The listener should be removed after the UI has been closed
notifiers_list = bogus.trait(trait)._notifiers(False)
self.assertEqual(0, len(notifiers_list))

@skip_if_null
def test_tree_editor_listeners_with_shown_root(self):
nodes = [
Expand Down Expand Up @@ -160,6 +231,42 @@ def test_tree_editor_xgetattr_label_listener(self):
expected_listeners=2,
)

@skip_if_null
def test_tree_node_object_listeners_with_shown_root(self):
nodes = [
ObjectTreeNode(
node_for=[BogusTreeNodeObject],
children='bogus_list',
label='=Bogus'
)
]
self._test_tree_node_object_releases_listeners(
nodes=nodes, hide_root=False)

@skip_if_null
def test_tree_node_object_listeners_with_hidden_root(self):
nodes = [
ObjectTreeNode(
node_for=[BogusTreeNodeObject],
children='bogus_list',
label='=Bogus'
)
]
self._test_tree_node_object_releases_listeners(
nodes=nodes, hide_root=True)

@skip_if_null
def test_tree_node_object_label_listener(self):
nodes = [
ObjectTreeNode(
node_for=[BogusTreeNodeObject],
children='bogus_list',
label='name'
)
]
self._test_tree_node_object_releases_listeners(
nodes=nodes, hide_root=False, trait='name')

@skip_if_null
def test_smoke_save_restore_prefs(self):
bogus = Bogus(bogus_list=[Bogus()])
Expand Down
9 changes: 6 additions & 3 deletions traitsui/tree_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ def append_child(self, object, child):
def insert_child(self, object, index, child):
""" Inserts a child into the object's children.
"""
children = self.get_children()
children = self.get_children(object)
children[index:index] = [child]

#-------------------------------------------------------------------------
Expand Down Expand Up @@ -1742,6 +1742,9 @@ class TreeNodeObject(HasPrivateTraits):
""" Represents the object that corresponds to a tree node.
"""

#: A cache for listeners that need to keep state.
_listener_cache = Dict

#-------------------------------------------------------------------------
# Returns whether chidren of this object are allowed or not:
#-------------------------------------------------------------------------
Expand Down Expand Up @@ -1889,11 +1892,11 @@ def tno_when_label_changed(self, node, listener, remove):
"""
label = node.label
if label[:1] != '=':
memo = ('label', label, object, listener)
memo = ('label', label, node, listener)
if not remove:
def wrapped_listener(target, name, new):
""" Ensure listener gets called with correct object. """
return listener(object, name, new)
return listener(node, name, new)

self._listener_cache[memo] = wrapped_listener
else:
Expand Down

0 comments on commit 1d61a78

Please sign in to comment.