from qtpy.QtCore import Qt
from qtpy import QtWidgets
from echo import add_callback
from glue.core.qt.layer_artist_model import QtLayerArtistContainer, LayerArtistWidget
from glue.utils.qt import set_cursor, messagebox_on_error
from glue.core.qt.dialogs import warn
from glue.utils.noconflict import classmaker
from glue.config import viewer_tool
from glue.viewers.common.qt.base_widget import BaseQtViewerWidget
from glue.viewers.common.tool import SimpleToolMenu
from glue.viewers.common.qt.toolbar import BasicToolbar
from glue.viewers.common.viewer import Viewer
from glue.viewers.common.utils import get_viewer_tools
__all__ = ['DataViewer', 'get_viewer_tools']
class ToolbarInitializer(object):
"""
This is a meta-class which ensures that initialize_toolbar is always called
on DataViewer instances and sub-class instances after all the __init__ code
has been executed. We need to do this, because often the toolbar can only
be initialized after everything else (e.g. canvas, etc.) has been set up,
so we can't do it in DataViewer.__init__.
"""
def __call__(cls, *args, **kwargs):
obj = type.__call__(cls, *args, **kwargs)
obj.initialize_toolbar()
return obj
@viewer_tool
class SaveTool(SimpleToolMenu):
"""
A generic 'save/export' tool that plugins can register new save/export tools
with.
To register a new save/export option, add an entry to the viewer
``subtools['save']`` list.
"""
tool_id = 'save'
icon = 'glue_filesave'
tool_tip = 'Save/export the plot'
# Note: we need to use classmaker here because otherwise we run into issues when
# trying to use the meta-class with the Qt class.
[docs]class DataViewer(Viewer, BaseQtViewerWidget,
metaclass=classmaker(left_metas=(ToolbarInitializer,))):
"""
Base class for all Qt DataViewer widgets.
This defines a minimal interface, and implements the following::
* An automatic call to unregister on window close
* Drag and drop support for adding data
"""
_layer_artist_container_cls = QtLayerArtistContainer
_layer_style_widget_cls = None
_toolbar_cls = BasicToolbar
# This defines the mouse mode to be used when no toolbar modes are active
_default_mouse_mode_cls = None
inherit_tools = True
tools = ['save', 'window']
subtools = {
'save': [],
'window': ['window:movetab', 'window:title']
}
_close_on_last_layer_removed = True
_options_cls = None
large_data_size = None
def __init__(self, session, state=None, parent=None):
"""
:type session: :class:`~glue.core.session.Session`
"""
BaseQtViewerWidget.__init__(self, parent)
Viewer.__init__(self, session, state=state)
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)
# Set up the options widget, which will include options that control the
# viewer state
if self._options_cls is None:
self.options = QtWidgets.QWidget()
else:
self.options = self._options_cls(viewer_state=self.state,
session=session)
self._tb_vis = {} # store whether toolbars are enabled
self.toolbar = None
self._toolbars = []
self._warn_close = True
# close window when last plot layer deleted
if self._close_on_last_layer_removed:
self._layer_artist_container.on_empty(self._close_nowarn)
self._layer_artist_container.on_changed(self.update_window_title)
add_callback(self.state, 'title', self._on_title_change)
self.update_window_title()
[docs] @property
def selected_layer(self):
return self._view.layer_list.current_artist()
[docs] @set_cursor(Qt.WaitCursor)
def apply_roi(self, roi):
pass
[docs] def warn(self, message, *args, **kwargs):
return warn(message, *args, **kwargs)
def _close_nowarn(self):
return self.close(warn=False)
[docs] def closeEvent(self, event):
super(DataViewer, self).closeEvent(event)
Viewer.cleanup(self)
# We tell the toolbar to do cleanup to make sure we get rid of any
# circular references
if self.toolbar:
self.toolbar.cleanup()
def _on_title_change(self, title):
self.update_window_title()
[docs] def layer_view(self):
return self._view
[docs] def set_focus(self, state):
super(DataViewer, self).set_focus(state)
if state:
self.show_toolbars()
else:
self.hide_toolbars()
def __gluestate__(self, context):
state = Viewer.__gluestate__(self, context)
state['size'] = self.viewer_size
state['pos'] = self.position
state['_protocol'] = 1
return state
[docs] def update_viewer_state(rec, context):
pass
@classmethod
def __setgluestate__(cls, rec, context):
if rec.get('_protocol', 0) < 1:
cls.update_viewer_state(rec, context)
viewer = super(DataViewer, cls).__setgluestate__(rec, context)
viewer.viewer_size = rec['size']
x, y = rec['pos']
viewer.move(x=x, y=y)
return viewer
[docs] @messagebox_on_error("Failed to add data")
def add_data(self, data):
return super(DataViewer, self).add_data(data)
[docs] @messagebox_on_error("Failed to add subset")
def add_subset(self, subset):
return super(DataViewer, self).add_subset(subset)