From 2e8200ef9d813c8086677a462edeedad6b678367 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 12 Jul 2017 17:29:55 +0100 Subject: [PATCH 1/2] Get rid of QTimers for updating the data collection and layer artist lists, and instead refresh whenever a message is sent from the hub (which results in immediate changes rather than waiting up to a second for things to change). --- CHANGES.md | 5 +++++ glue/core/qt/data_collection_model.py | 26 ++++++++++++-------------- glue/core/qt/layer_artist_model.py | 27 ++++++++++++++++----------- glue/viewers/common/qt/data_viewer.py | 3 ++- 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d983f053d..938ef1992 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,11 @@ Full changelog v0.11.0 (unreleased) -------------------- +* Get rid of QTimers for updating the data collection and layer artist + lists, and instead refresh whenever a message is sent from the hub + (which results in immediate changes rather than waiting up to a + second for things to change). [#1343] + * Made it possible to delay callbacks from the Hub using the ``Hub.delay_callbacks`` context manager. Also fixed the Hub so that it uses weak references to classes and methods wherever possible. [#1339] diff --git a/glue/core/qt/data_collection_model.py b/glue/core/qt/data_collection_model.py index 8a206d13f..b877fefbc 100644 --- a/glue/core/qt/data_collection_model.py +++ b/glue/core/qt/data_collection_model.py @@ -12,8 +12,8 @@ from glue.icons.qt import layer_icon from glue.core.qt.style_dialog import StyleDialog -from glue.utils import nonpartial from glue.utils.qt import PyMimeData +from glue.core.message import Message DATA_IDX = 0 SUBSET_IDX = 1 @@ -449,31 +449,19 @@ def mimeTypes(self): return [LAYERS_MIME_TYPE] -class DataCollectionView(QtWidgets.QTreeView): +class DataCollectionView(QtWidgets.QTreeView, HubListener): selection_changed = QtCore.Signal() def __init__(self, parent=None): super(DataCollectionView, self).__init__(parent) self.doubleClicked.connect(self._edit) - # this keeps the full-row of the selection bar in-sync - self.pressed.connect(nonpartial(self._update_viewport)) - # only edit label on model.new_item self.setItemDelegate(LabeledDelegate()) self.setEditTriggers(self.NoEditTriggers) self.setIconSize(QtCore.QSize(16, 16)) - self._timer = QtCore.QTimer(self) - self._timer.timeout.connect(nonpartial(self._update_viewport)) - self._timer.start(1000) - - def _update_viewport(self): - # We have to do this here to make sure we always get the latest - # viewport instance. - self.viewport().update() - def selected_layers(self): idxs = self.selectedIndexes() return self._model.glue_data(idxs) @@ -511,6 +499,16 @@ def set_data_collection(self, data_collection): self.setDropIndicatorShown(True) self.setDragDropMode(QtWidgets.QAbstractItemView.DragOnly) + # Update when any message is emitted which would indicate a change in + # data collection content, colors, labels, etc. It's easier to simply + # listen to all events since the viewport update is fast. + data_collection.hub.subscribe(self, Message, handler=self._update_viewport) + + def _update_viewport(self, *args, **kwargs): + # This forces the widget containing the list view to update/redraw, + # reflecting any changes in color/labels/content + self.viewport().update() + def edit_label(self, index): if not (self._model.flags(index) & Qt.ItemIsEditable): return diff --git a/glue/core/qt/layer_artist_model.py b/glue/core/qt/layer_artist_model.py index b69fe0d67..5ddc51f14 100644 --- a/glue/core/qt/layer_artist_model.py +++ b/glue/core/qt/layer_artist_model.py @@ -21,7 +21,8 @@ from glue.core.qt.mime import LAYERS_MIME_TYPE from glue.utils import nonpartial from glue.utils.qt import PythonListModel, PyMimeData - +from glue.core.hub import HubListener +from glue.core.message import Message class LayerArtistModel(PythonListModel): @@ -169,13 +170,13 @@ def row_artist(self, row): return self.artists[row] -class LayerArtistView(QtWidgets.QListView): +class LayerArtistView(QtWidgets.QListView, HubListener): """A list view into an artist model. The zorder of each artist can be shuffled by dragging and dropping items. Right-clicking brings up a menu to edit style or delete""" - def __init__(self, parent=None): + def __init__(self, parent=None, hub=None): super(LayerArtistView, self).__init__(parent) self.setDragEnabled(True) self.setAcceptDrops(True) @@ -190,13 +191,15 @@ def __init__(self, parent=None): self._actions = {} self._create_actions() - self._timer = QtCore.QTimer(self) - self._timer.timeout.connect(nonpartial(self._update_viewport)) - self._timer.start(1000) + # Update when any message is emitted which would indicate a change in + # data collection content, colors, labels, etc. It's easier to simply + # listen to all events since the viewport update is fast. + self.hub = hub + self.hub.subscribe(self, Message, self._update_viewport) - def _update_viewport(self): - # We have to do this here to make sure we always get the latest - # viewport instance. + def _update_viewport(self, *args): + # This forces the widget containing the list view to update/redraw, + # reflecting any changes in color/labels/content self.viewport().update() def rowsInserted(self, index, start, end): @@ -283,16 +286,18 @@ class LayerArtistWidget(QtWidgets.QWidget): options for the layer artists. """ - def __init__(self, parent=None, layer_style_widget_cls=None): + def __init__(self, parent=None, layer_style_widget_cls=None, hub=None): super(LayerArtistWidget, self).__init__(parent=parent) + self.hub = None + self.layout = QtWidgets.QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.layer_style_widget_cls = layer_style_widget_cls - self.layer_list = LayerArtistView(parent=self) + self.layer_list = LayerArtistView(parent=self, hub=hub) self.layout.addWidget(self.layer_list) self.layer_options = QtWidgets.QWidget() diff --git a/glue/viewers/common/qt/data_viewer.py b/glue/viewers/common/qt/data_viewer.py index 448549927..1d4281d21 100644 --- a/glue/viewers/common/qt/data_viewer.py +++ b/glue/viewers/common/qt/data_viewer.py @@ -62,7 +62,8 @@ def __init__(self, session, parent=None): QtWidgets.QMainWindow.__init__(self, parent) ViewerBase.__init__(self, session) self.setWindowIcon(get_qapp().windowIcon()) - self._view = LayerArtistWidget(layer_style_widget_cls=self._layer_style_widget_cls) + self._view = LayerArtistWidget(layer_style_widget_cls=self._layer_style_widget_cls, + hub=session.hub) self._view.layer_list.setModel(self._layer_artist_container.model) self._tb_vis = {} # store whether toolbars are enabled self.setAttribute(Qt.WA_DeleteOnClose) From 24d204e2877f1473addf1927df1a070d97aa18d6 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Thu, 13 Jul 2017 12:03:57 +0100 Subject: [PATCH 2/2] Fix LayerArtistView test --- glue/core/qt/tests/test_layer_artist_model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/glue/core/qt/tests/test_layer_artist_model.py b/glue/core/qt/tests/test_layer_artist_model.py index 20def4d8a..d450d922f 100644 --- a/glue/core/qt/tests/test_layer_artist_model.py +++ b/glue/core/qt/tests/test_layer_artist_model.py @@ -4,7 +4,7 @@ from qtpy.QtCore import Qt from qtpy import PYQT5 -from glue.core import Data +from glue.core import Data, Hub from glue.core.layer_artist import MatplotlibLayerArtist as _LayerArtist from ..layer_artist_model import LayerArtistModel, LayerArtistView @@ -217,7 +217,8 @@ class TestLayerArtistView(object): def setup_method(self, method): self.model, self.artists = setup_model(2) - self.view = LayerArtistView() + self.hub = Hub() + self.view = LayerArtistView(hub=self.hub) self.view.setModel(self.model) def test_current_row(self):