Skip to content

Commit

Permalink
Merge pull request #1262 from jasongrout/selections
Browse files Browse the repository at this point in the history
Fix _SelectionModel and _MultipleSelectionModel to sync using indices
  • Loading branch information
jasongrout committed Apr 10, 2017
2 parents c71f8a8 + 238a766 commit c9d8088
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 193 deletions.
46 changes: 25 additions & 21 deletions ipywidgets/widgets/tests/test_interaction.py
Expand Up @@ -12,6 +12,7 @@

import os
import nose.tools as nt
from collections import OrderedDict

import ipywidgets as widgets

Expand Down Expand Up @@ -153,10 +154,10 @@ def test_list_str():
d = dict(
cls=widgets.Dropdown,
value=first,
options=values,
_options_labels=tuple(values),
_options_values=tuple(values),
)
d['options'] = tuple(zip(d['_options_labels'], d['_options_values']))
check_widgets(c, lis=d)

def test_list_int():
Expand All @@ -167,10 +168,10 @@ def test_list_int():
d = dict(
cls=widgets.Dropdown,
value=first,
options=values,
_options_labels=("3", "1", "2"),
_options_labels=tuple(str(v) for v in values),
_options_values=tuple(values),
)
d['options'] = tuple(zip(d['_options_labels'], d['_options_values']))
check_widgets(c, lis=d)

def test_list_tuple():
Expand All @@ -181,10 +182,10 @@ def test_list_tuple():
d = dict(
cls=widgets.Dropdown,
value=first,
options=values,
_options_labels=("3", "1", "2"),
_options_values=(300, 100, 200),
)
d['options'] = tuple(zip(d['_options_labels'], d['_options_values']))
check_widgets(c, lis=d)

def test_list_tuple_invalid():
Expand All @@ -202,14 +203,16 @@ def test_dict():
]:
c = interactive(f, d=d)
w = c.children[0]
check_widget(w,
check = dict(
cls=widgets.Dropdown,
description='d',
value=next(iter(d.values())),
options=d,
_options_labels=tuple(d.keys()),
_options_values=tuple(d.values()),
)
check['options'] = tuple(zip(check['_options_labels'], check['_options_values']))
check_widget(w, **check)


def test_ordereddict():
from collections import OrderedDict
Expand All @@ -225,6 +228,7 @@ def test_ordereddict():
_options_labels=("3", "1", "2"),
_options_values=(300, 100, 200),
)
d['options'] = tuple(zip(d['_options_labels'], d['_options_values']))
check_widgets(c, lis=d)

def test_iterable():
Expand All @@ -238,10 +242,10 @@ def yield_values():
d = dict(
cls=widgets.Dropdown,
value=first,
options=list(yield_values()),
_options_labels=("3", "1", "2"),
_options_values=(3, 1, 2),
)
d['options'] = tuple(zip(d['_options_labels'], d['_options_values']))
check_widgets(c, lis=d)

def test_iterable_tuple():
Expand All @@ -252,10 +256,10 @@ def test_iterable_tuple():
d = dict(
cls=widgets.Dropdown,
value=first,
options=values,
_options_labels=("3", "1", "2"),
_options_values=(300, 100, 200),
)
d['options'] = tuple(zip(d['_options_labels'], d['_options_values']))
check_widgets(c, lis=d)

def test_mapping():
Expand All @@ -280,10 +284,10 @@ def items(self):
d = dict(
cls=widgets.Dropdown,
value=first,
options=items,
_options_labels=("3", "1", "2"),
_options_values=(300, 100, 200),
)
d['options'] = tuple(zip(d['_options_labels'], d['_options_values']))
check_widgets(c, lis=d)


Expand All @@ -309,7 +313,7 @@ def f(n, f=4.5, g=1):
)

def test_default_values():
@annotate(n=10, f=(0, 10.), g=5, h={'a': 1, 'b': 2}, j=['hi', 'there'])
@annotate(n=10, f=(0, 10.), g=5, h=OrderedDict([('a',1), ('b',2)]), j=['hi', 'there'])
def f(n, f=4.5, g=1, h=2, j='there'):
pass

Expand All @@ -329,12 +333,12 @@ def f(n, f=4.5, g=1, h=2, j='there'):
),
h=dict(
cls=widgets.Dropdown,
options={'a': 1, 'b': 2},
options=(('a', 1), ('b', 2)),
value=2
),
j=dict(
cls=widgets.Dropdown,
options=['hi', 'there'],
options=(('hi', 'hi'), ('there', 'there')),
value='there'
),
)
Expand All @@ -352,12 +356,12 @@ def f(f='hi', h=5, j='other'):
),
h=dict(
cls=widgets.Dropdown,
options={'a': 1},
options=(('a', 1),),
value=1,
),
j=dict(
cls=widgets.Dropdown,
options=['hi', 'there'],
options=(('hi', 'hi'), ('there', 'there')),
value='hi',
),
)
Expand Down Expand Up @@ -684,29 +688,29 @@ def test_multiple_selection():

# basic multiple select
w = smw(options=[(1, 1)], value=[1])
check_widget(w, cls=smw, value=(1,), options=[(1, 1)])
check_widget(w, cls=smw, value=(1,), options=(('1', 1),))

# don't accept random other value
with nt.assert_raises(TraitError):
w.value = w.value + (2,)
check_widget(w, value=(1,))

# change options
w.options = w.options + [(2, 2)]
check_widget(w, options=[(1, 1), (2,2)])
# change options, which resets value
w.options = w.options + ((2, 2),)
check_widget(w, options=(('1', 1), ('2',2)), value=())

# change value
w.value = w.value + (2,)
w.value = (1,2)
check_widget(w, value=(1, 2))

# dict style
w.options = {1: 1}
check_widget(w, options={1: 1})
check_widget(w, options=(('1', 1),))

# updating
with nt.assert_raises(TraitError):
w.value = (2,)
check_widget(w, options={1: 1})
check_widget(w, options=(('1', 1),))


def test_interact_noinspect():
Expand Down
20 changes: 15 additions & 5 deletions ipywidgets/widgets/widget.py
Expand Up @@ -11,9 +11,8 @@

from IPython.core.getipython import get_ipython
from ipykernel.comm import Comm
from traitlets.config import LoggingConfigurable
from traitlets.utils.importstring import import_item
from traitlets import Unicode, Dict, Instance, List, Int, Set, Bytes, observe, default
from traitlets import HasTraits, Unicode, Dict, Instance, List, Int, Set, Bytes, observe, default
from ipython_genutils.py3compat import string_types, PY3
from IPython.display import display

Expand Down Expand Up @@ -121,8 +120,19 @@ def _remove_buffers(state):
state = _separate_buffers(state, [], buffer_paths, buffers)
return state, buffer_paths, buffers

class LoggingHasTraits(HasTraits):
"""A parent class for HasTraits that log.
Subclasses have a log trait, and the default behavior
is to get the logger from the currently running Application.
"""
log = Instance('logging.Logger')
@default('log')
def _log_default(self):
from traitlets import log
return log.get_logger()


class CallbackDispatcher(LoggingConfigurable):
class CallbackDispatcher(LoggingHasTraits):
"""A structure for registering and running callbacks"""
callbacks = List()

Expand Down Expand Up @@ -215,7 +225,7 @@ def register(widget):
widget)
return widget

class Widget(LoggingConfigurable):
class Widget(LoggingHasTraits):
#-------------------------------------------------------------------------
# Class attributes
#-------------------------------------------------------------------------
Expand Down Expand Up @@ -498,7 +508,7 @@ def notify_change(self, change):
if name in self.keys and self._should_send_property(name, change['new']):
# Send new state to front-end
self.send_state(key=name)
LoggingConfigurable.notify_change(self, change)
super(Widget, self).notify_change(change)

#-------------------------------------------------------------------------
# Support methods
Expand Down

0 comments on commit c9d8088

Please sign in to comment.