Combo box for reference data in image viewer doesn't update when renaming
When renaming a dataset, the labels in the reference combo box don't change
diff --git a/CHANGES.md b/CHANGES.md
index fadbf6d6..208dd9f8 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -9,6 +9,9 @@ v0.14.0 (unreleased)
 v0.13.3 (unreleased)
 --------------------
 
+* Fixed a bug that caused combo boxes with Data objects to not update
+  when a data label was changed. [#1704]
+
 * Fixed a bug related to callback functions when restoring sessions.
   [#1695]
 
diff --git a/glue/core/data.py b/glue/core/data.py
index b96189e1..ae4bac89 100644
--- a/glue/core/data.py
+++ b/glue/core/data.py
@@ -143,8 +143,11 @@ class Data(object):
     def label(self, value):
         """ Set the label to value
         """
-        self._label = value
-        self.broadcast(attribute='label')
+        if getattr(self, '_label', None) != value:
+            self._label = value
+            self.broadcast(attribute='label')
+        elif value is None:
+            self._label = value
 
     @property
     def size(self):
diff --git a/glue/core/data_combo_helper.py b/glue/core/data_combo_helper.py
index d2d6f4bf..c3e70b5d 100644
--- a/glue/core/data_combo_helper.py
+++ b/glue/core/data_combo_helper.py
@@ -102,6 +102,21 @@ class ComboHelper(HubListener):
         prop = getattr(type(self.state), self.selection_property)
         return prop.set_display_func(self.state, display)
 
+    def _on_rename(self, msg):
+        # If a component ID is renamed, we don't need to refresh because the
+        # list of actual component IDs is the same as before. However, we do
+        # need to trigger a refresh of any GUI combos that use this, so we
+        # make the property notify a change. However, if we are inside a
+        # delay_callback block, the property will not be enabled, and notify()
+        # won't have any effect, in which case we set the 'force_next_sync'
+        # option which means that when exiting from the delay_callback block,
+        # this property will show up as having changed
+        prop = getattr(type(self.state), self.selection_property)
+        if prop.enabled(self.state):
+            prop.notify(self.state, self.selection, self.selection)
+        else:
+            prop.force_next_sync(self.state)
+
 
 class ComponentIDComboHelper(ComboHelper):
     """
@@ -360,21 +375,6 @@ class ComponentIDComboHelper(ComboHelper):
 
         self.choices = choices
 
-    def _on_rename(self, msg):
-        # If a component ID is renamed, we don't need to refresh because the
-        # list of actual component IDs is the same as before. However, we do
-        # need to trigger a refresh of any GUI combos that use this, so we
-        # make the property notify a change. However, if we are inside a
-        # delay_callback block, the property will not be enabled, and notify()
-        # won't have any effect, in which case we set the 'force_next_sync'
-        # option which means that when exiting from the delay_callback block,
-        # this property will show up as having changed
-        prop = getattr(type(self.state), self.selection_property)
-        if prop.enabled(self.state):
-            prop.notify(self.state, self.selection, self.selection)
-        else:
-            prop.force_next_sync(self.state)
-
     def _filter_msg(self, msg):
         return msg.data in self._data or msg.sender in self._data_collection
 
@@ -469,6 +469,12 @@ class BaseDataComboHelper(ComboHelper):
     def register_to_hub(self, hub):
         pass
 
+    def _on_data_update(self, msg):
+        if msg.attribute == 'label':
+            self._on_rename(msg)
+        else:
+            self.refresh()
+
 
 class ManualDataComboHelper(BaseDataComboHelper):
     """
@@ -513,14 +519,17 @@ class ManualDataComboHelper(BaseDataComboHelper):
         except AttributeError:  # PY2
             self._datasets[:] = []
         for data in unique_data_iter(datasets):
-            self._datasets.append(data)
+            self.append_data(data, refresh=False)
         self.refresh()
 
-    def append_data(self, data):
+    def append_data(self, data, refresh=True):
         if data in self._datasets:
             return
+        if self.hub is None and data.hub is not None:
+            self.hub = data.hub
         self._datasets.append(data)
-        self.refresh()
+        if refresh:
+            self.refresh()
 
     def remove_data(self, data):
         if data not in self._datasets:
@@ -533,7 +542,7 @@ class ManualDataComboHelper(BaseDataComboHelper):
         super(ManualDataComboHelper, self).register_to_hub(hub)
 
         hub.subscribe(self, DataUpdateMessage,
-                      handler=self.refresh,
+                      handler=self._on_data_update,
                       filter=lambda msg: msg.sender in self._datasets)
         hub.subscribe(self, DataCollectionDeleteMessage,
                       handler=lambda msg: self.remove_data(msg.data),
@@ -567,7 +576,7 @@ class DataCollectionComboHelper(BaseDataComboHelper):
     def register_to_hub(self, hub):
         super(DataCollectionComboHelper, self).register_to_hub(hub)
         hub.subscribe(self, DataUpdateMessage,
-                      handler=self.refresh,
+                      handler=self._on_data_update,
                       filter=lambda msg: msg.sender in self._datasets)
         hub.subscribe(self, DataCollectionAddMessage,
                       handler=self.refresh,
diff --git a/glue/core/tests/test_data_combo_helper.py b/glue/core/tests/test_data_combo_helper.py
index 2f74ff32..44d1141e 100644
--- a/glue/core/tests/test_data_combo_helper.py
+++ b/glue/core/tests/test_data_combo_helper.py
@@ -240,35 +240,55 @@ def test_component_id_combo_helper_add():
     assert selection_choices(state, 'combo') == "x:y:z"
 
 
-def test_manual_data_combo_helper():
+@pytest.mark.parametrize('initialize_data_collection', [False, True])
+def test_manual_data_combo_helper(initialize_data_collection):
 
+    # The case with initialize_data_collection=False is a regression test for a
+    # bug which meant that when a ManualDataComboHelper was initialized without
+    # a data collection, it did not change when a data object added later has a
+    # label changed.
+
+    callback = MagicMock()
     state = ExampleState()
+    state.add_callback('combo', callback)
 
     dc = DataCollection([])
 
-    helper = ManualDataComboHelper(state, 'combo', dc)
+    if initialize_data_collection:
+        helper = ManualDataComboHelper(state, 'combo', dc)
+    else:
+        helper = ManualDataComboHelper(state, 'combo')
 
     data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1')
 
     dc.append(data1)
 
+    assert callback.call_count == 0
+
     assert selection_choices(state, 'combo') == ""
 
     helper.append_data(data1)
+    assert callback.call_count == 1
 
     assert selection_choices(state, 'combo') == "data1"
 
     data1.label = 'mydata1'
     assert selection_choices(state, 'combo') == "mydata1"
+    assert callback.call_count == 2
 
-    dc.remove(data1)
+    if initialize_data_collection:
 
-    assert selection_choices(state, 'combo') == ""
+        dc.remove(data1)
+
+        assert selection_choices(state, 'combo') == ""
+        assert callback.call_count == 3
 
 
 def test_data_collection_combo_helper():
 
+    callback = MagicMock()
     state = ExampleState()
+    state.add_callback('combo', callback)
 
     dc = DataCollection([])
 
@@ -276,15 +296,23 @@ def test_data_collection_combo_helper():
 
     data1 = Data(x=[1, 2, 3], y=[2, 3, 4], label='data1')
 
+    assert callback.call_count == 1
+
     dc.append(data1)
 
+    assert callback.call_count == 2
+
     assert selection_choices(state, 'combo') == "data1"
 
     data1.label = 'mydata1'
     assert selection_choices(state, 'combo') == "mydata1"
 
+    assert callback.call_count == 3
+
     dc.remove(data1)
 
+    assert callback.call_count == 4
+
     assert selection_choices(state, 'combo') == ""
 
 
diff --git a/glue/viewers/matplotlib/qt/tests/test_data_viewer.py b/glue/viewers/matplotlib/qt/tests/test_data_viewer.py
index 55167d16..ad0300fb 100644
--- a/glue/viewers/matplotlib/qt/tests/test_data_viewer.py
+++ b/glue/viewers/matplotlib/qt/tests/test_data_viewer.py
@@ -533,7 +533,7 @@ class BaseTestMatplotlibDataViewer(object):
         assert self.draw_count == 0
         self.viewer.add_data(self.data)
         assert self.draw_count == 1
-        data = Data()
+        data = Data(label=self.data.label)
         for cid in self.data.visible_components:
             data.add_component(self.data[cid] * 2, cid.label)
         self.data.update_values_from_data(data)
['CHANGES.md', 'glue/core/tests/test_data_combo_helper.py', 'glue/core/data_combo_helper.py', 'glue/viewers/matplotlib/qt/tests/test_data_viewer.py', 'glue/core/data.py']