workspacewidget.py 10.6 KB
Newer Older
1
# Mantid Repository : https://github.com/mantidproject/mantid
2
#
3
4
5
6
# Copyright © 2017 ISIS Rutherford Appleton Laboratory UKRI,
#     NScD Oak Ridge National Laboratory, European Spallation Source
#     & Institut Laue - Langevin
#    This file is part of the mantid workbench.
Dimitar Tasev's avatar
Dimitar Tasev committed
7
# SPDX - License - Identifier: GPL - 3.0 +
8
9
10
11
#
#
from __future__ import (absolute_import, unicode_literals)

12
from functools import partial
13
from qtpy.QtWidgets import QApplication, QMessageBox, QVBoxLayout
14

15
from mantid.api import AnalysisDataService, WorkspaceGroup
16
from mantid.kernel import logger
Dimitar Tasev's avatar
Dimitar Tasev committed
17
from mantidqt.plotting.functions import can_overplot, pcolormesh, plot, plot_from_names
18
from mantid.plots.utility import MantidAxType
19
from mantid.simpleapi import CreateDetectorTable
Phil's avatar
Phil committed
20
from mantidqt.utils.asynchronous import BlockingAsyncTaskWithCallback
21
from mantidqt.widgets.instrumentview.presenter import InstrumentViewPresenter
22
from mantidqt.widgets.samplelogs.presenter import SampleLogs
23
from mantidqt.widgets.sliceviewer.presenter import SliceViewer
24
from mantidqt.widgets.workspacedisplay.matrix.presenter import MatrixWorkspaceDisplay
25
from mantidqt.widgets.workspacedisplay.table.presenter import TableWorkspaceDisplay
26
from mantidqt.widgets.workspacewidget.algorithmhistorywindow import AlgorithmHistoryWindow
27
from mantidqt.widgets.workspacewidget.workspacetreewidget import WorkspaceTreeWidget
28
from workbench.plugins.base import PluginWidget
29

30
31

class WorkspaceWidget(PluginWidget):
Elliot Oram's avatar
Elliot Oram committed
32
    """Provides a Workspace Widget for workspace manipulation"""
33
34
35
36

    def __init__(self, parent):
        super(WorkspaceWidget, self).__init__(parent)

37
38
        self._ads = AnalysisDataService.Instance()

39
        # layout
40
        self.workspacewidget = WorkspaceTreeWidget()
41
42
43
44
        layout = QVBoxLayout()
        layout.addWidget(self.workspacewidget)
        self.setLayout(layout)

45
        # behaviour
46
47
        self.workspacewidget.plotSpectrumClicked.connect(partial(self._do_plot_spectrum,
                                                                 errors=False, overplot=False))
48
        self.workspacewidget.plotBinClicked.connect(partial(self._do_plot_bin,
Nick Draper's avatar
Nick Draper committed
49
                                                            errors=False, overplot=False))
50
51
52
53
54
55
        self.workspacewidget.overplotSpectrumClicked.connect(partial(self._do_plot_spectrum,
                                                                     errors=False, overplot=True))
        self.workspacewidget.plotSpectrumWithErrorsClicked.connect(partial(self._do_plot_spectrum,
                                                                           errors=True, overplot=False))
        self.workspacewidget.overplotSpectrumWithErrorsClicked.connect(partial(self._do_plot_spectrum,
                                                                               errors=True, overplot=True))
56
        self.workspacewidget.plotColorfillClicked.connect(self._do_plot_colorfill)
57
        self.workspacewidget.sampleLogsClicked.connect(self._do_sample_logs)
58
        self.workspacewidget.sliceViewerClicked.connect(self._do_slice_viewer)
59
        self.workspacewidget.showDataClicked.connect(self._do_show_data)
60
        self.workspacewidget.showInstrumentClicked.connect(self._do_show_instrument)
61
        self.workspacewidget.showAlgorithmHistoryClicked.connect(self._do_show_algorithm_history)
62
        self.workspacewidget.showDetectorsClicked.connect(self._do_show_detectors)
63

64
65
        self.workspacewidget.workspaceDoubleClicked.connect(self._action_double_click_workspace)

66
    # ----------------- Plugin API --------------------
67
68
69
70
71
72

    def register_plugin(self):
        self.main.add_dockwidget(self)

    def get_plugin_title(self):
        return "Workspaces"
73

74
75
76
77
    def readSettings(self, _):
        pass

    def writeSettings(self, _):
78
79
        pass

80
81
    # ----------------- Behaviour --------------------

82
    def _do_plot_spectrum(self, names, errors, overplot):
83
84
85
86
87
        """
        Plot spectra from the selected workspaces

        :param names: A list of workspace names
        :param errors: If true then error bars will be plotted on the points
88
        :param overplot: If true then the add to the current figure if one
89
                         exists and it is a compatible figure
90
        """
91
        if overplot:
92
            compatible, error_msg = can_overplot()
93
94
95
96
            if not compatible:
                QMessageBox.warning(self, "", error_msg)
                return

97
        plot_from_names(names, errors, overplot)
Nick Draper's avatar
Nick Draper committed
98

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
    def _do_plot_bin(self, names, errors, overplot):
        """
        Plot a single bin from the selected workspaces

        :param names: A list of workspace names
        :param errors: If true then error bars will be plotted on the points
        :param overplot: If true then the add to the current figure if one
                         exists and it is a compatible figure
        """
        if overplot:
            compatible, error_msg = can_overplot()
            if not compatible:
                QMessageBox.warning(self, "", error_msg)
                return
        plot_kwargs = {"axis": MantidAxType.BIN}
Nick Draper's avatar
Nick Draper committed
114
115
        plot(self._ads.retrieveWorkspaces(names, unrollGroups=True), errors=errors,
             overplot=overplot,wksp_indices=[0], plot_kwargs=plot_kwargs)
116
117
118
119
120
121
122
123

    def _do_plot_colorfill(self, names):
        """
        Plot a colorfill from the selected workspaces

        :param names: A list of workspace names
        """
        try:
124
            pcolormesh(self._ads.retrieveWorkspaces(names, unrollGroups=True))
125
126
127
        except BaseException:
            import traceback
            traceback.print_exc()
128
129

    def _do_sample_logs(self, names):
130
131
132
133
134
        """
        Show the sample log window for the given workspaces

        :param names: A list of workspace names
        """
135
        for ws in self._ads.retrieveWorkspaces(names, unrollGroups=True):
136
137
138
139
140
            try:
                SampleLogs(ws=ws, parent=self)
            except Exception as exception:
                logger.warning("Could not open sample logs for workspace '{}'."
                               "".format(ws.name()))
141
142
                logger.warning("{}: {}".format(type(exception).__name__,
                                               exception))
143

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
    def _do_slice_viewer(self, names):
        """
        Show the sliceviewer window for the given workspaces

        :param names: A list of workspace names
        """
        for ws in self._ads.retrieveWorkspaces(names, unrollGroups=True):
            try:
                SliceViewer(ws=ws, parent=self)
            except Exception as exception:
                logger.warning("Could not open slice viewer for workspace '{}'."
                               "".format(ws.name()))
                logger.debug("{}: {}".format(type(exception).__name__,
                                             exception))

159
160
161
162
163
164
165
    def _do_show_instrument(self, names):
        """
        Show an instrument widget for the given workspaces

        :param names: A list of workspace names
        """
        for ws in self._ads.retrieveWorkspaces(names, unrollGroups=True):
166
167
168
169
170
171
172
173
174
            if ws.getInstrument().getName():
                try:
                    presenter = InstrumentViewPresenter(ws, parent=self)
                    presenter.show_view()
                except Exception as exception:
                    logger.warning("Could not show instrument for workspace "
                                   "'{}':\n{}.\n".format(ws.name(), exception))
            else:
                logger.warning("Could not show instrument for workspace '{}':"
175
                               "\nNo instrument available.\n"
176
                               "".format(ws.name()))
177
178

    def _do_show_data(self, names):
179
180
        # local import to allow this module to be imported without pyplot being imported
        import matplotlib.pyplot
181
        for ws in self._ads.retrieveWorkspaces(names, unrollGroups=True):
182
183
184
185
186
            try:
                MatrixWorkspaceDisplay.supports(ws)
                # the plot function is being injected in the presenter
                # this is done so that the plotting library is mockable in testing
                presenter = MatrixWorkspaceDisplay(ws, plot=plot, parent=self)
187
                presenter.show_view()
188
189
190
191
            except ValueError:
                try:
                    TableWorkspaceDisplay.supports(ws)
                    presenter = TableWorkspaceDisplay(ws, plot=matplotlib.pyplot, parent=self)
192
                    presenter.show_view()
193
                except ValueError:
194
                    logger.error(
195
                        "Could not open workspace: {0} with neither "
196
197
                        "MatrixWorkspaceDisplay nor TableWorkspaceDisplay."
                        "".format(ws.name()))
198
199
200

    def _do_show_algorithm_history(self, names):
        for name in names:
201
202
203
204
205
206
            if not isinstance(self._ads.retrieve(name), WorkspaceGroup):
                try:
                    AlgorithmHistoryWindow(self, name).show()
                except Exception as exception:
                    logger.warning("Could not open history of '{}'. "
                                   "".format(name))
207
                    logger.warning("{}: {}".format(type(exception).__name__, exception))
208

209
    def _do_show_detectors(self, names):
210
        successful_workspaces = []
211
        for ws in self._ads.retrieveWorkspaces(names, unrollGroups=True):
Phil's avatar
Phil committed
212
            try:
213
214
215
                task = BlockingAsyncTaskWithCallback(self._run_create_detector_table, [ws],
                                                     blocking_cb=QApplication.processEvents)
                task.start()
Phil's avatar
Phil committed
216
            except RuntimeError:
217
218
                continue
            else:
219
                successful_workspaces.append(ws.name())
220

221
        self._do_show_data(list(map(lambda x: x + "-Detectors", successful_workspaces)))
222
223
224

    def _run_create_detector_table(self, ws):
        CreateDetectorTable(InputWorkspace=ws)
225

226
    def _action_double_click_workspace(self, name):
227
        ws = self._ads.retrieve(name)
228
229
230
        try:
            # if this is a table workspace (or peaks workspace),
            # then it can't be plotted automatically, so the data is shown instead
231
            TableWorkspaceDisplay.supports(ws)
232
233
            self._do_show_data([name])
        except ValueError:
234
            if hasattr(ws, 'blocksize') and ws.blocksize() == 1:
235
236
237
                #this is just single bin data, it makes more sense to plot the bin
                plot_kwargs = {"axis": MantidAxType.BIN}
                plot([ws],errors=False, overplot=False, wksp_indices=[0], plot_kwargs=plot_kwargs)
238
            else:
239
                plot_from_names([name], errors=False, overplot=False, show_colorfill_btn=True)
240
241
242

    def refresh_workspaces(self):
        self.workspacewidget.refreshWorkspaces()