diff --git a/traitsui/editor_factory.py b/traitsui/editor_factory.py index 10a835b1f..3a0a63072 100644 --- a/traitsui/editor_factory.py +++ b/traitsui/editor_factory.py @@ -309,15 +309,3 @@ class EditorWithListFactory(EditorFactory): #: Name of the trait on 'object' containing the enumeration data name = Str() - - #: Fired when the **values** trait has been updated: - values_modified = Event() - - def _values_changed(self): - """ Recomputes the mappings whenever the **values** trait is changed. - """ - self._names, self._mapping, self._inverse_mapping = enum_values_changed( - self.values, strfunc=self.string_value - ) - - self.values_modified = True diff --git a/traitsui/qt4/enum_editor.py b/traitsui/qt4/enum_editor.py index d017030b9..d20f484f0 100644 --- a/traitsui/qt4/enum_editor.py +++ b/traitsui/qt4/enum_editor.py @@ -59,7 +59,8 @@ class BaseEditor(Editor): # ------------------------------------------------------------------------- def values_changed(self): - """ Recomputes the cached data based on the underlying enumeration model. + """ Recomputes the cached data based on the underlying enumeration model + or the values of the factory. """ self._names, self._mapping, self._inverse_mapping = enum_values_changed( self._value(), self.string_value @@ -91,8 +92,10 @@ def init(self, parent): self._values_changed, " " + self._name, dispatch="ui" ) else: + self._value = lambda: self.factory.values + self.values_changed() factory.on_trait_change( - self.rebuild_editor, "values_modified", dispatch="ui" + self._values_changed, "values", dispatch="ui" ) def dispose(self): @@ -104,7 +107,7 @@ def dispose(self): ) else: self.factory.on_trait_change( - self.rebuild_editor, "values_modified", remove=True + self._values_changed, "values", remove=True ) super(BaseEditor, self).dispose() @@ -118,31 +121,23 @@ def dispose(self): def _get_names(self): """ Gets the current set of enumeration names. """ - if self._object is None: - return self.factory._names - return self._names def _get_mapping(self): """ Gets the current mapping. """ - if self._object is None: - return self.factory._mapping - return self._mapping def _get_inverse_mapping(self): """ Gets the current inverse mapping. """ - if self._object is None: - return self.factory._inverse_mapping - return self._inverse_mapping # Trait change handlers -------------------------------------------------- def _values_changed(self): - """ Handles the underlying object model's enumeration set being changed. + """ Handles the underlying object model's enumeration set or factory's + values being changed. """ self.values_changed() self.rebuild_editor() diff --git a/traitsui/qt4/image_enum_editor.py b/traitsui/qt4/image_enum_editor.py index 1d810edf4..21d925637 100644 --- a/traitsui/qt4/image_enum_editor.py +++ b/traitsui/qt4/image_enum_editor.py @@ -57,6 +57,8 @@ def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ + super(ReadonlyEditor, self).init(parent) + self.control = QtGui.QLabel() self.control.setPixmap(self.get_pixmap(self.str_value)) self.set_tooltip() @@ -67,6 +69,12 @@ def update_editor(self): """ self.control.setPixmap(self.get_pixmap(self.str_value)) + def rebuild_editor(self): + """ Rebuilds the contents of the editor whenever the original factory + object's **values** trait changes. + """ + pass + class SimpleEditor(BaseEditor, SimpleEnumEditor): """ Simple style of image enumeration editor, which displays a combo box. diff --git a/traitsui/qt4/set_editor.py b/traitsui/qt4/set_editor.py index 8afd5c869..4bb93e3a1 100644 --- a/traitsui/qt4/set_editor.py +++ b/traitsui/qt4/set_editor.py @@ -76,8 +76,10 @@ def init(self, parent): self._values_changed, self._name, dispatch="ui" ) else: + self._value = lambda: self.factory.values + self.values_changed() factory.on_trait_change( - self.update_editor, "values_modified", dispatch="ui" + self._values_changed, "values", dispatch="ui" ) blayout = QtGui.QVBoxLayout() @@ -120,25 +122,16 @@ def init(self, parent): def _get_names(self): """ Gets the current set of enumeration names. """ - if self._object is None: - return self.factory._names - return self._names def _get_mapping(self): """ Gets the current mapping. """ - if self._object is None: - return self.factory._mapping - return self._mapping def _get_inverse_mapping(self): """ Gets the current inverse mapping. """ - if self._object is None: - return self.factory._inverse_mapping - return self._inverse_mapping def _create_listbox(self, col, handler1, handler2, title): @@ -171,14 +164,16 @@ def _create_button(self, label, layout, handler): return button def values_changed(self): - """ Recomputes the cached data based on the underlying enumeration model. + """ Recomputes the cached data based on the underlying enumeration model + or the values of the factory. """ self._names, self._mapping, self._inverse_mapping = enum_values_changed( self._value(), self.string_value ) def _values_changed(self): - """ Handles the underlying object model's enumeration set being changed. + """ Handles the underlying object model's enumeration set or factory's + values being changed. """ self.values_changed() self.update_editor() @@ -260,7 +255,7 @@ def dispose(self): ) else: self.factory.on_trait_change( - self.update_editor, "values_modified", remove=True + self._values_changed, "values", remove=True ) self.context_object.on_trait_change( diff --git a/traitsui/tests/editors/test_enum_editor.py b/traitsui/tests/editors/test_enum_editor.py index 01f856528..617c138d3 100644 --- a/traitsui/tests/editors/test_enum_editor.py +++ b/traitsui/tests/editors/test_enum_editor.py @@ -196,17 +196,10 @@ class IntEnumModel(HasTraits): with store_exceptions_on_all_threads(): editor = self.setup_ui(IntEnumModel(), formatted_view) - # FIXME issue enthought/traitsui#782 - with self.assertRaises(AssertionError): - self.assertEqual(editor.names, ["FALSE", "TRUE"]) - self.assertEqual(editor.mapping, {"FALSE": 0, "TRUE": 1}) - self.assertEqual( - editor.inverse_mapping, {0: "FALSE", 1: "TRUE"} - ) - self.assertEqual(editor.names, ["0", "1"]) - self.assertEqual(editor.mapping, {"0": 0, "1": 1}) + self.assertEqual(editor.names, ["FALSE", "TRUE"]) + self.assertEqual(editor.mapping, {"FALSE": 0, "TRUE": 1}) self.assertEqual( - editor.inverse_mapping, {0: "0", 1: "1"} + editor.inverse_mapping, {0: "FALSE", 1: "TRUE"} ) enum_editor_factory.values = [1, 0] @@ -238,47 +231,19 @@ class IntEnumModel(HasTraits): with store_exceptions_on_all_threads(): editor = self.setup_ui(model, formatted_view) - # FIXME issue enthought/traitsui#835 - if is_current_backend_wx(): - with self.assertRaises(AssertionError): - self.assertEqual(editor.names, ["FALSE", "TRUE"]) - self.assertEqual(editor.mapping, {"FALSE": 0, "TRUE": 1}) - self.assertEqual( - editor.inverse_mapping, {0: "FALSE", 1: "TRUE"} - ) - self.assertEqual(editor.names, ["0", "1"]) - self.assertEqual(editor.mapping, {"0": 0, "1": 1}) - self.assertEqual( - editor.inverse_mapping, {0: "0", 1: "1"} - ) - else: - self.assertEqual(editor.names, ["FALSE", "TRUE"]) - self.assertEqual(editor.mapping, {"FALSE": 0, "TRUE": 1}) - self.assertEqual( - editor.inverse_mapping, {0: "FALSE", 1: "TRUE"} - ) + self.assertEqual(editor.names, ["FALSE", "TRUE"]) + self.assertEqual(editor.mapping, {"FALSE": 0, "TRUE": 1}) + self.assertEqual( + editor.inverse_mapping, {0: "FALSE", 1: "TRUE"} + ) model.possible_values = [1, 0] - # FIXME issue enthought/traitsui#835 - if is_current_backend_wx(): - with self.assertRaises(AssertionError): - self.assertEqual(editor.names, ["TRUE", "FALSE"]) - self.assertEqual(editor.mapping, {"TRUE": 1, "FALSE": 0}) - self.assertEqual( - editor.inverse_mapping, {1: "TRUE", 0: "FALSE"} - ) - self.assertEqual(editor.names, ["1", "0"]) - self.assertEqual(editor.mapping, {"1": 1, "0": 0}) - self.assertEqual( - editor.inverse_mapping, {1: "1", 0: "0"} - ) - else: - self.assertEqual(editor.names, ["TRUE", "FALSE"]) - self.assertEqual(editor.mapping, {"TRUE": 1, "FALSE": 0}) - self.assertEqual( - editor.inverse_mapping, {1: "TRUE", 0: "FALSE"} - ) + self.assertEqual(editor.names, ["TRUE", "FALSE"]) + self.assertEqual(editor.mapping, {"TRUE": 1, "FALSE": 0}) + self.assertEqual( + editor.inverse_mapping, {1: "TRUE", 0: "FALSE"} + ) def test_simple_editor_mapping_values(self): self.check_enum_mappings_value_change("simple", "radio") diff --git a/traitsui/tests/editors/test_image_enum_editor.py b/traitsui/tests/editors/test_image_enum_editor.py index 20f15a05c..6e2d57172 100644 --- a/traitsui/tests/editors/test_image_enum_editor.py +++ b/traitsui/tests/editors/test_image_enum_editor.py @@ -155,25 +155,14 @@ def check_enum_mappings_value_change(self, style): with store_exceptions_on_all_threads(): editor = self.setup_ui(EnumModel(), formatted_view) - # FIXME issue enthought/traitsui#782 - with self.assertRaises(AssertionError): - self.assertEqual(editor.names, ["TOP LEFT", "TOP RIGHT"]) - self.assertEqual( - editor.mapping, - {"TOP LEFT": "top left", "TOP RIGHT": "top right"} - ) - self.assertEqual( - editor.inverse_mapping, - {"top left": "TOP LEFT", "top right": "TOP RIGHT"} - ) - self.assertEqual(editor.names, ["top left", "top right"]) + self.assertEqual(editor.names, ["TOP LEFT", "TOP RIGHT"]) self.assertEqual( editor.mapping, - {"top left": "top left", "top right": "top right"} + {"TOP LEFT": "top left", "TOP RIGHT": "top right"} ) self.assertEqual( editor.inverse_mapping, - {"top left": "top left", "top right": "top right"} + {"top left": "TOP LEFT", "top right": "TOP RIGHT"} ) image_enum_editor_factory.values = ["top right", "top left"] diff --git a/traitsui/tests/editors/test_set_editor.py b/traitsui/tests/editors/test_set_editor.py index f64ab280c..599eef763 100644 --- a/traitsui/tests/editors/test_set_editor.py +++ b/traitsui/tests/editors/test_set_editor.py @@ -170,17 +170,10 @@ class IntListModel(HasTraits): with store_exceptions_on_all_threads(): editor = self.setup_ui(IntListModel(), formatted_view) - # FIXME issue enthought/traitsui#782 - with self.assertRaises(AssertionError): - self.assertEqual(editor.names, ["FALSE", "TRUE"]) - self.assertEqual(editor.mapping, {"FALSE": 0, "TRUE": 1}) - self.assertEqual( - editor.inverse_mapping, {0: "FALSE", 1: "TRUE"} - ) - self.assertEqual(editor.names, ["0", "1"]) - self.assertEqual(editor.mapping, {"0": 0, "1": 1}) + self.assertEqual(editor.names, ["FALSE", "TRUE"]) + self.assertEqual(editor.mapping, {"FALSE": 0, "TRUE": 1}) self.assertEqual( - editor.inverse_mapping, {0: "0", 1: "1"} + editor.inverse_mapping, {0: "FALSE", 1: "TRUE"} ) set_editor_factory.values = [1, 0] @@ -211,47 +204,19 @@ class IntListModel(HasTraits): with store_exceptions_on_all_threads(): editor = self.setup_ui(model, formatted_view) - # FIXME issue enthought/traitsui#835 - if is_current_backend_wx(): - with self.assertRaises(AssertionError): - self.assertEqual(editor.names, ["FALSE", "TRUE"]) - self.assertEqual(editor.mapping, {"FALSE": 0, "TRUE": 1}) - self.assertEqual( - editor.inverse_mapping, {0: "FALSE", 1: "TRUE"} - ) - self.assertEqual(editor.names, ["0", "1"]) - self.assertEqual(editor.mapping, {"0": 0, "1": 1}) - self.assertEqual( - editor.inverse_mapping, {0: "0", 1: "1"} - ) - else: - self.assertEqual(editor.names, ["FALSE", "TRUE"]) - self.assertEqual(editor.mapping, {"FALSE": 0, "TRUE": 1}) - self.assertEqual( - editor.inverse_mapping, {0: "FALSE", 1: "TRUE"} - ) + self.assertEqual(editor.names, ["FALSE", "TRUE"]) + self.assertEqual(editor.mapping, {"FALSE": 0, "TRUE": 1}) + self.assertEqual( + editor.inverse_mapping, {0: "FALSE", 1: "TRUE"} + ) model.possible_values = [1, 0] - # FIXME issue enthought/traitsui#835 - if is_current_backend_wx(): - with self.assertRaises(AssertionError): - self.assertEqual(editor.names, ["TRUE", "FALSE"]) - self.assertEqual(editor.mapping, {"TRUE": 1, "FALSE": 0}) - self.assertEqual( - editor.inverse_mapping, {1: "TRUE", 0: "FALSE"} - ) - self.assertEqual(editor.names, ["1", "0"]) - self.assertEqual(editor.mapping, {"1": 1, "0": 0}) - self.assertEqual( - editor.inverse_mapping, {1: "1", 0: "0"} - ) - else: - self.assertEqual(editor.names, ["TRUE", "FALSE"]) - self.assertEqual(editor.mapping, {"TRUE": 1, "FALSE": 0}) - self.assertEqual( - editor.inverse_mapping, {1: "TRUE", 0: "FALSE"} - ) + self.assertEqual(editor.names, ["TRUE", "FALSE"]) + self.assertEqual(editor.mapping, {"TRUE": 1, "FALSE": 0}) + self.assertEqual( + editor.inverse_mapping, {1: "TRUE", 0: "FALSE"} + ) @skip_if_null diff --git a/traitsui/wx/enum_editor.py b/traitsui/wx/enum_editor.py index bb7894d62..80049dd76 100644 --- a/traitsui/wx/enum_editor.py +++ b/traitsui/wx/enum_editor.py @@ -29,12 +29,13 @@ # traitsui.editors.drop_editor file. from traitsui.editors.enum_editor import ToolkitEditorFactory +from traitsui.helper import enum_values_changed + from .editor import Editor from .constants import OKColor, ErrorColor from .helper import ( - enum_values_changed, TraitsUIPanel, disconnect, disconnect_no_id, @@ -82,32 +83,25 @@ def init(self, parent): self._values_changed, " " + self._name, dispatch="ui" ) else: + self._value = lambda: self.factory.values + self.values_changed() factory.on_trait_change( - self.rebuild_editor, "values_modified", dispatch="ui" + self._values_changed, "values", dispatch="ui" ) def _get_names(self): """ Gets the current set of enumeration names. """ - if self._object is None: - return self.factory._names - return self._names def _get_mapping(self): """ Gets the current mapping. """ - if self._object is None: - return self.factory._mapping - return self._mapping def _get_inverse_mapping(self): """ Gets the current inverse mapping. """ - if self._object is None: - return self.factory._inverse_mapping - return self._inverse_mapping def rebuild_editor(self): @@ -117,14 +111,16 @@ def rebuild_editor(self): raise NotImplementedError def values_changed(self): - """ Recomputes the cached data based on the underlying enumeration model. + """ Recomputes the cached data based on the underlying enumeration model + or the values of the factory. """ self._names, self._mapping, self._inverse_mapping = enum_values_changed( - self._value() + self._value(), self.string_value ) def _values_changed(self): - """ Handles the underlying object model's enumeration set being changed. + """ Handles the underlying object model's enumeration set or factory's + values being changed. """ self.values_changed() self.rebuild_editor() @@ -138,7 +134,7 @@ def dispose(self): ) else: self.factory.on_trait_change( - self.rebuild_editor, "values_modified", remove=True + self._values_changed, "values", remove=True ) super(BaseEditor, self).dispose() diff --git a/traitsui/wx/helper.py b/traitsui/wx/helper.py index aded386cf..53bc1d5ea 100644 --- a/traitsui/wx/helper.py +++ b/traitsui/wx/helper.py @@ -266,41 +266,6 @@ def top_level_window_for(control): return control -def enum_values_changed(values): - """ Recomputes the mappings for a new set of enumeration values. - """ - - if isinstance(values, dict): - data = [(str(v), n) for n, v in values.items()] - if len(data) > 0: - data.sort(key=itemgetter(0)) - col = data[0][0].find(":") + 1 - if col > 0: - data = [(n[col:], v) for n, v in data] - elif not isinstance(values, SequenceTypes): - handler = values - if isinstance(handler, CTrait): - handler = handler.handler - if not isinstance(handler, BaseTraitHandler): - raise TraitError("Invalid value for 'values' specified") - if handler.is_mapped: - data = [(str(n), n) for n in handler.map.keys()] - data.sort(key=itemgetter(0)) - else: - data = [(str(v), v) for v in handler.values] - else: - data = [(str(v), v) for v in values] - - names = [x[0] for x in data] - mapping = {} - inverse_mapping = {} - for name, value in data: - mapping[name] = value - inverse_mapping[value] = name - - return (names, mapping, inverse_mapping) - - def disconnect(control, *events): """ Disconnects a wx event handle from its associated control. """ @@ -687,4 +652,4 @@ def _erase_background(self, event): def Destroy(self): self.Unbind(wx.EVT_ERASE_BACKGROUND) - super().Destroy() \ No newline at end of file + super().Destroy() diff --git a/traitsui/wx/image_enum_editor.py b/traitsui/wx/image_enum_editor.py index 91496a1a3..220281729 100644 --- a/traitsui/wx/image_enum_editor.py +++ b/traitsui/wx/image_enum_editor.py @@ -30,12 +30,16 @@ from .editor import Editor +from .enum_editor import BaseEditor as BaseEnumEditor + from .helper import bitmap_cache, position_window, TraitsUIPanel from .constants import WindowColor from .image_control import ImageControl +from traitsui.wx import toolkit + # ------------------------------------------------------------------------- # 'ReadonlyEditor' class: # ------------------------------------------------------------------------- @@ -97,7 +101,7 @@ def popup_editor(self, control): ImageEnumDialog(self) -class CustomEditor(Editor): +class CustomEditor(BaseEnumEditor): """ Custom style of image enumeration editor, which displays a grid of ImageControls. The user can click an image to select the corresponding value. @@ -113,13 +117,23 @@ def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ - self._create_image_grid(parent) + super(CustomEditor, self).init(parent) + + # Create the panel to hold the ImageControl buttons: + self.control = TraitsUIPanel(parent, -1) + self._create_image_grid() - def _create_image_grid(self, parent): + def rebuild_editor(self): + # Clear any existing content: + self.control.SetSizer(None) + toolkit.destroy_children(self.control) + + self._create_image_grid() + + def _create_image_grid(self): """ Populates a specified window with a grid of image buttons. """ - # Create the panel to hold the ImageControl buttons: - self.control = panel = TraitsUIPanel(parent, -1) + panel = self.control # Create the main sizer: if self.factory.cols > 1: @@ -129,10 +143,9 @@ def _create_image_grid(self, parent): # Add the set of all possible choices: factory = self.factory - mapping = factory._mapping cur_value = self.value - for name in self.factory._names: - value = mapping[name] + for name in self.names: + value = self.mapping[name] control = ImageControl( panel, bitmap_cache( diff --git a/traitsui/wx/set_editor.py b/traitsui/wx/set_editor.py index 883a896ea..a10095990 100644 --- a/traitsui/wx/set_editor.py +++ b/traitsui/wx/set_editor.py @@ -31,9 +31,11 @@ # traitsui.editors.set_editor file. from traitsui.editors.set_editor import ToolkitEditorFactory +from traitsui.helper import enum_values_changed + from .editor import Editor -from .helper import enum_values_changed, TraitsUIPanel +from .helper import TraitsUIPanel # ------------------------------------------------------------------------- @@ -81,8 +83,10 @@ def init(self, parent): self._values_changed, self._name, dispatch="ui" ) else: + self._value = lambda: self.factory.values + self.values_changed() factory.on_trait_change( - self.update_editor, "values_modified", dispatch="ui" + self._values_changed, "values", dispatch="ui" ) self.control = panel = TraitsUIPanel(parent, -1) @@ -139,25 +143,16 @@ def init(self, parent): def _get_names(self): """ Gets the current set of enumeration names. """ - if self._object is None: - return self.factory._names - return self._names def _get_mapping(self): """ Gets the current mapping. """ - if self._object is None: - return self.factory._mapping - return self._mapping def _get_inverse_mapping(self): """ Gets the current inverse mapping. """ - if self._object is None: - return self.factory._inverse_mapping - return self._inverse_mapping def _create_listbox(self, parent, sizer, handler1, handler2, title): @@ -197,14 +192,16 @@ def _create_button(self, label, parent, sizer, space_before, handler): return button def values_changed(self): - """ Recomputes the cached data based on the underlying enumeration model. + """ Recomputes the cached data based on the underlying enumeration model + or the values of the factory. """ self._names, self._mapping, self._inverse_mapping = enum_values_changed( - self._value() + self._value(), self.string_value ) def _values_changed(self): - """ Handles the underlying object model's enumeration set being changed. + """ Handles the underlying object model's enumeration set or factory's + values being changed. """ self.values_changed() self.update_editor() @@ -287,7 +284,7 @@ def dispose(self): ) else: self.factory.on_trait_change( - self.update_editor, "values_modified", remove=True + self._values_changed, "values", remove=True ) self.context_object.on_trait_change(