diff --git a/buildconfig/pycharm.env b/buildconfig/pycharm.env new file mode 100644 index 0000000000000000000000000000000000000000..85b4c3930ea8c3c5c439bb9a6a4a2194582f3db8 --- /dev/null +++ b/buildconfig/pycharm.env @@ -0,0 +1,7 @@ +THIRD_PARTY_DIR=C:\Users\qbr77747\dev\2m\source\external\src\ThirdParty +QT4_BIN=${THIRD_PARTY_DIR}\lib\qt4\bin;${THIRD_PARTY_DIR}\lib\qt4\lib +QT5_BIN=${THIRD_PARTY_DIR}\lib\qt5\bin;${THIRD_PARTY_DIR}\lib\qt5\lib +QT_QPA_PLATFORM_PLUGIN_PATH=${THIRD_PARTY_DIR}\lib\qt5\plugins +PYTHONHOME=${THIRD_PARTY_DIR}\lib\python2.7 +MISC_BIN=${THIRD_PARTY_DIR}\bin;${THIRD_PARTY_DIR}\bin\mingw +PATH=${MISC_BIN};${PYTHONHOME};${QT5_BIN};${QT4_BIN};${PATH} \ No newline at end of file diff --git a/qt/python/mantidqt/widgets/common/__init__.py b/qt/python/mantidqt/widgets/common/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/qt/python/mantidqt/widgets/common/table_copying.py b/qt/python/mantidqt/widgets/common/table_copying.py new file mode 100644 index 0000000000000000000000000000000000000000..4c9ac9cf6d9b44a0ce885eed878ead6509a0dc20 --- /dev/null +++ b/qt/python/mantidqt/widgets/common/table_copying.py @@ -0,0 +1,137 @@ +from __future__ import (absolute_import, division, print_function) + +from qtpy import QtGui +from qtpy.QtCore import QPoint +from qtpy.QtGui import QCursor, QFont, QFontMetrics +from qtpy.QtWidgets import (QTableView, QToolTip) + +NO_SELECTION_MESSAGE = "No selection" +COPY_SUCCESSFUL_MESSAGE = "Copy Successful" + + +def copy_spectrum_values(table, ws_read): + """ + Copies the values selected by the user to the system's clipboard + + :param table: Table from which the selection will be read + :param view: + :param ws_read: The workspace read function, that is used to access the data directly + """ + selection_model = table.selectionModel() + if not selection_model.hasSelection(): + show_no_selection_to_copy_toast() + return + selected_rows = selection_model.selectedRows() # type: list + row_data = [] + + for index in selected_rows: + row = index.row() + data = "\t".join(map(str, ws_read(row))) + + row_data.append(data) + + copy_to_clipboard("\n".join(row_data)) + show_successful_copy_toast() + + +def show_no_selection_to_copy_toast(): + show_mouse_toast(NO_SELECTION_MESSAGE) + + +def show_successful_copy_toast(): + show_mouse_toast(COPY_SUCCESSFUL_MESSAGE) + + +def copy_bin_values(table, ws_read, num_rows): + selection_model = table.selectionModel() + if not selection_model.hasSelection(): + show_no_selection_to_copy_toast() + return + selected_columns = selection_model.selectedColumns() # type: list + + # Qt gives back a QModelIndex, we need to extract the column from it + column_data = [] + for index in selected_columns: + column = index.column() + data = [str(ws_read(row)[column]) for row in range(num_rows)] + column_data.append(data) + + all_string_rows = [] + for i in range(num_rows): + # Appends ONE value from each COLUMN, this is because the final string is being built vertically + # the noqa disables a 'data' variable redefined warning + all_string_rows.append("\t".join([data[i] for data in column_data])) # noqa: F812 + + # Finally all rows are joined together with a new line at the end of each row + final_string = "\n".join(all_string_rows) + copy_to_clipboard(final_string) + show_successful_copy_toast() + + +def copy_cells(table): + """ + :type table: QTableView + :param table: The table from which the data will be copied. + :return: + """ + selectionModel = table.selectionModel() + if not selectionModel.hasSelection(): + show_no_selection_to_copy_toast() + return + + selection = selectionModel.selection() + # TODO show a warning if copying more cells than some number (100? 200? 300? 400??) + selectionRange = selection.first() + + top = selectionRange.top() + bottom = selectionRange.bottom() + left = selectionRange.left() + right = selectionRange.right() + + data = [] + index = selectionModel.currentIndex() + for i in range(top, bottom + 1): + for j in range(left, right): + data.append(index.sibling(i, j).data()) + data.append("\t") + data.append(index.sibling(i, right).data()) + data.append("\n") + + # strip the string to remove the trailing new line + copy_to_clipboard("".join(data).strip()) + show_successful_copy_toast() + + +def keypress_copy(table, view, ws_read, num_rows): + selectionModel = table.selectionModel() + if not selectionModel.hasSelection(): + show_no_selection_to_copy_toast() + return + + if len(selectionModel.selectedRows()) > 0: + copy_spectrum_values(table, ws_read) + elif len(selectionModel.selectedColumns()) > 0: + copy_bin_values(table, ws_read, num_rows) + else: + copy_cells(table) + + +def show_mouse_toast(message): + # Creates a text with empty space to get the height of the rendered text - this is used + # to provide the same offset for the tooltip, scaled relative to the current resolution and zoom. + font_metrics = QFontMetrics(QFont(" ")) + # The height itself is divided by 2 just to reduce the offset so that the tooltip is + # reasonably position relative to the cursor + QToolTip.showText(QCursor.pos() + QPoint(font_metrics.height() / 2, 0), message) + + +def copy_to_clipboard(data): + """ + Uses the QGuiApplication to copy to the system clipboard. + + :type data: str + :param data: The data that will be copied to the clipboard + :return: + """ + cb = QtGui.QGuiApplication.clipboard() + cb.setText(data, mode=cb.Clipboard) diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/__init__.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..57d5ae5a28a63ed0dd44886f201c25df7cac61ca --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/__init__.py @@ -0,0 +1,9 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/__main__.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..db4d4160e01d4dd435662352eb24a0ac656baae8 --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/__main__.py @@ -0,0 +1,27 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# + +# To Run - target this package with PyCharm, and __main__ will be executed + +import matplotlib + +matplotlib.use('Qt5Agg') + +from qtpy.QtWidgets import QApplication # noqa: F402 + +from mantid.simpleapi import Load # noqa: F402 +from mantidqt.widgets.tableworkspacedisplay.presenter import TableWorkspaceDisplay # noqa: F402 +from workbench.plotting.functions import plot # noqa: F402 + +app = QApplication([]) +# DEEE_WS_MON = Load("SavedTableWorkspace.nxs") +DEEE_WS_MON = Load("SmallPeakWS10.nxs") +window = TableWorkspaceDisplay(DEEE_WS_MON, plot) +app.exec_() diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/model.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/model.py new file mode 100644 index 0000000000000000000000000000000000000000..be03f5bdb6e50052a3d2147cf1c6f202cf53b190 --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/model.py @@ -0,0 +1,42 @@ +# coding=utf-8 +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# +from __future__ import (absolute_import, division, print_function) + +from mantid.dataobjects import PeaksWorkspace, TableWorkspace + + +class TableWorkspaceDisplayModel(object): + SPECTRUM_PLOT_LEGEND_STRING = '{}-{}' + BIN_PLOT_LEGEND_STRING = '{}-bin-{}' + + def __init__(self, ws): + if not isinstance(ws, TableWorkspace) and not isinstance(ws, PeaksWorkspace): + raise ValueError("The workspace type is not supported: {0}".format(type(ws))) + + self._ws = ws + + def get_name(self): + return self._ws.name() + + def get_column_headers(self): + return self._ws.getColumnNames() + + def get_column(self, index): + return self._ws.column(index) + + def get_number_of_rows(self): + return self._ws.rowCount() + + def get_number_of_columns(self): + return self._ws.columnCount() + + def is_peaks_workspace(self): + return isinstance(self._ws, PeaksWorkspace) diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/presenter.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/presenter.py new file mode 100644 index 0000000000000000000000000000000000000000..f356745477e8dd98c5dc0a9f9d357954ae9861b1 --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/presenter.py @@ -0,0 +1,103 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# +from __future__ import absolute_import, division, print_function + +from qtpy.QtCore import Qt +from qtpy.QtWidgets import QTableWidgetItem + +from mantidqt.widgets.common.table_copying import copy_cells +from .model import TableWorkspaceDisplayModel +from .view import TableWorkspaceDisplayView + + +class TableWorkspaceDisplay(object): + A_LOT_OF_THINGS_TO_PLOT_MESSAGE = "You selected {} spectra to plot. Are you sure you want to plot that many?" + NUM_SELECTED_FOR_CONFIRMATION = 10 + + def __init__(self, ws, plot=None, parent=None, model=None, view=None): + # Create model and view, or accept mocked versions + self.model = model if model else TableWorkspaceDisplayModel(ws) + self.view = view if view else TableWorkspaceDisplayView(self, parent, self.model.get_name()) + self.plot = plot + self.view.set_context_menu_actions(self.view) + column_headers = self.model.get_column_headers() + self.view.setColumnCount(len(column_headers)) + self.view.setHorizontalHeaderLabels(["{}[Y]".format(x) for x in column_headers]) + self.load_data(self.view, self.model.is_peaks_workspace()) + + def load_data(self, table, peaks_workspace=False): + num_rows = self.model.get_number_of_rows() + table.setRowCount(num_rows) + + num_cols = self.model.get_number_of_columns() + for col in range(num_cols): + column_data = self.model.get_column(col) + for row in range(num_rows): + item = QTableWidgetItem(str(column_data[row])) + if not peaks_workspace or (col != 0 and col != 2 and col != 3 and col != 4): + item.setFlags(item.flags() & ~Qt.ItemIsEditable) + table.setItem(row, col, item) + + def action_copy_cells(self, table): + copy_cells(table) + + def action_copy_bin_values(self, table): + copy_cells(table) + + def action_copy_spectrum_values(self, table): + copy_cells(table) + + def action_keypress_copy(self, table): + copy_cells(table) + # def _do_action_plot(self, table, axis, get_index, plot_errors=False): + # if self.plot is None: + # raise ValueError("Trying to do a plot, but no plotting class dependency was injected in the constructor") + # selection_model = table.selectionModel() + # if not selection_model.hasSelection(): + # self.show_no_selection_to_copy_toast() + # return + # + # if axis == MantidAxType.SPECTRUM: + # selected = selection_model.selectedRows() # type: list + # else: + # selected = selection_model.selectedColumns() # type: list + # + # if len(selected) > self.NUM_SELECTED_FOR_CONFIRMATION and not self.view.ask_confirmation( + # self.A_LOT_OF_THINGS_TO_PLOT_MESSAGE.format(len(selected))): + # return + # + # plot_kwargs = {"capsize": 3} if plot_errors else {} + # plot_kwargs["axis"] = axis + # + # ws_list = [self.model._ws] + # self.plot(ws_list, wksp_indices=[get_index(index) for index in selected], errors=plot_errors, + # plot_kwargs=plot_kwargs) + # + # def action_plot_spectrum(self, table): + # self._do_action_plot(table, MantidAxType.SPECTRUM, lambda index: index.row()) + # + # def action_plot_spectrum_with_errors(self, table): + # self._do_action_plot(table, MantidAxType.SPECTRUM, lambda index: index.row(), plot_errors=True) + # + # def action_plot_bin(self, table): + # self._do_action_plot(table, MantidAxType.BIN, lambda index: index.column()) + # + # def action_plot_bin_with_errors(self, table): + # self._do_action_plot(table, MantidAxType.BIN, lambda index: index.column(), plot_errors=True) + + # def _get_ws_read_from_type(self, type): + # if type == TableWorkspaceTableViewModelType.y: + # return self.model._ws.readY + # elif type == TableWorkspaceTableViewModelType.x: + # return self.model._ws.readX + # elif type == TableWorkspaceTableViewModelType.e: + # return self.model._ws.readE + # else: + # raise ValueError("Unknown TableViewModel type {}".format(type)) diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/table_view_model.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/table_view_model.py new file mode 100644 index 0000000000000000000000000000000000000000..64aca887eb963f753f43f8f7e3e929b2caf78393 --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/table_view_model.py @@ -0,0 +1,208 @@ +# coding=utf-8 +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# +from __future__ import (absolute_import, division, print_function) + +from qtpy import QtGui +from qtpy.QtCore import QVariant, Qt, QAbstractTableModel +from mantid.py3compat import Enum + + +class TableWorkspaceTableViewModelType(Enum): + x = 'x' + y = 'y' + e = 'e' + + +class TableWorkspaceTableViewModel(QAbstractTableModel): + HORIZONTAL_HEADER_DISPLAY_STRING = u"{0}\n{1:0.1f}{2}" + HORIZONTAL_HEADER_TOOLTIP_STRING = u"index {0}\n{1} {2:0.1f}{3} (bin centre)" + + HORIZONTAL_HEADER_DISPLAY_STRING_FOR_X_VALUES = "{0}" + HORIZONTAL_HEADER_TOOLTIP_STRING_FOR_X_VALUES = "index {0}" + + VERTICAL_HEADER_DISPLAY_STRING = "{0} {1}" + VERTICAL_HEADER_TOOLTIP_STRING = "index {0}\nspectra no {1}" + + HORIZONTAL_BINS_VARY_DISPLAY_STRING = "{0}\nbins vary" + HORIZONTAL_BINS_VARY_TOOLTIP_STRING = "index {0}\nbin centre value varies\nRebin to set common bins" + + MASKED_MONITOR_ROW_STRING = "This is a masked monitor spectrum. " + MASKED_ROW_STRING = "This is a masked spectrum. " + + MONITOR_ROW_STRING = "This is a monitor spectrum. " + MASKED_BIN_STRING = "This bin is masked. " + + def __init__(self, ws, model_type): + """ + :param ws: + :param model_type: TableWorkspaceTableViewModelType + :type model_type: TableWorkspaceTableViewModelType + """ + assert model_type in [TableWorkspaceTableViewModelType.x, TableWorkspaceTableViewModelType.y, + TableWorkspaceTableViewModelType.e], "The Model type must be either X, Y or E." + + super(TableWorkspaceTableViewModel, self).__init__() + + self.ws = ws + self.ws_spectrum_info = self.ws.spectrumInfo() + self.row_count = self.ws.getNumberHistograms() + self.column_count = self.ws.blocksize() + + self.masked_rows_cache = [] + self.monitor_rows_cache = [] + self.masked_bins_cache = {} + + self.masked_color = QtGui.QColor(240, 240, 240) + + self.monitor_color = QtGui.QColor(255, 253, 209) + + self.type = model_type + if self.type == TableWorkspaceTableViewModelType.x: + self.relevant_data = self.ws.readX + elif self.type == TableWorkspaceTableViewModelType.y: + self.relevant_data = self.ws.readY + elif self.type == TableWorkspaceTableViewModelType.e: + self.relevant_data = self.ws.readE + else: + raise ValueError("Unknown model type {0}".format(self.type)) + + def _makeVerticalHeader(self, section, role): + axis_index = 1 + # check that the vertical axis actually exists in the workspace + if self.ws.axes() > axis_index: + if role == Qt.DisplayRole: + return self.VERTICAL_HEADER_DISPLAY_STRING.format(section, self.ws.getAxis(axis_index).label(section)) + else: + spectrum_number = self.ws.getSpectrum(section).getSpectrumNo() + return self.VERTICAL_HEADER_TOOLTIP_STRING.format(section, spectrum_number) + else: + raise NotImplementedError("What do we do here? Handle if the vertical axis does NOT exist") + + def _makeHorizontalHeader(self, section, role): + """ + + :param section: The workspace index or bin number + :param role: Qt.DisplayRole - is the label for the header + or Qt.TooltipRole - is the tooltip for the header when moused over + :return: The formatted header string + """ + # X values get simpler labels + if self.type == TableWorkspaceTableViewModelType.x: + if role == Qt.DisplayRole: + return self.HORIZONTAL_HEADER_DISPLAY_STRING_FOR_X_VALUES.format(section) + else: + # format for the tooltip + return self.HORIZONTAL_HEADER_TOOLTIP_STRING_FOR_X_VALUES.format(section) + + if not self.ws.isCommonBins(): + if role == Qt.DisplayRole: + return self.HORIZONTAL_BINS_VARY_DISPLAY_STRING.format(section) + else: + # format for the tooltip + return self.HORIZONTAL_BINS_VARY_TOOLTIP_STRING.format(section) + + # for the Y and E values, create a label with the units + axis_index = 0 + x_vec = self.ws.readX(0) + if self.ws.isHistogramData(): + bin_centre_value = (x_vec[section] + x_vec[section + 1]) / 2.0 + else: + bin_centre_value = x_vec[section] + + unit = self.ws.getAxis(axis_index).getUnit() + if role == Qt.DisplayRole: + return self.HORIZONTAL_HEADER_DISPLAY_STRING.format(section, bin_centre_value, unit.symbol().utf8()) + else: + # format for the tooltip + return self.HORIZONTAL_HEADER_TOOLTIP_STRING.format(section, unit.caption(), bin_centre_value, + unit.symbol().utf8()) + + def headerData(self, section, orientation, role=None): + if not (role == Qt.DisplayRole or role == Qt.ToolTipRole): + return QVariant() + + if orientation == Qt.Vertical: + return self._makeVerticalHeader(section, role) + else: + return self._makeHorizontalHeader(section, role) + + def rowCount(self, parent=None, *args, **kwargs): + return self.row_count + + def columnCount(self, parent=None, *args, **kwargs): + return self.column_count + + def data(self, index, role=None): + row = index.row() + if role == Qt.DisplayRole: + # DisplayRole determines the text of each cell + return str(self.relevant_data(row)[index.column()]) + elif role == Qt.BackgroundRole: + # BackgroundRole determines the background of each cell + + # Checks if the row is MASKED, if so makes it the specified color for masked + # The check for masked rows should be first as a monitor row can be masked as well - and we want it to be + # colored as a masked row, rather than as a monitor row. + # First do the check in the cache, and only if not present go through SpectrumInfo and cache it. This logic + # is repeated in the other checks below + if self.checkMaskedCache(row): + return self.masked_color + + # Checks if the row is a MONITOR, if so makes it the specified color for monitors + elif self.checkMonitorCache(row): + return self.monitor_color + + # Checks if the BIN is MASKED, if so makes it the specified color for masked + elif self.checkMaskedBinCache(row, index): + return self.masked_color + + elif role == Qt.ToolTipRole: + tooltip = QVariant() + if self.checkMaskedCache(row): + if self.checkMonitorCache(row): + tooltip = self.MASKED_MONITOR_ROW_STRING + else: + tooltip = self.MASKED_ROW_STRING + elif self.checkMonitorCache(row): + tooltip = self.MONITOR_ROW_STRING + if self.checkMaskedBinCache(row, index): + tooltip += self.MASKED_BIN_STRING + elif self.checkMaskedBinCache(row, index): + tooltip = self.MASKED_BIN_STRING + return tooltip + else: + return QVariant() + + def checkMaskedCache(self, row): + if row in self.masked_rows_cache: + return True + elif self.ws_spectrum_info.hasDetectors(row) and self.ws_spectrum_info.isMasked(row): + self.masked_rows_cache.append(row) + return True + + def checkMonitorCache(self, row): + if row in self.monitor_rows_cache: + return True + elif self.ws_spectrum_info.hasDetectors(row) and self.ws_spectrum_info.isMonitor(row): + self.monitor_rows_cache.append(row) + return True + + def checkMaskedBinCache(self, row, index): + if row in self.masked_bins_cache: + # retrieve the masked bins IDs from the cache + if index.column() in self.masked_bins_cache[row]: + return True + + elif self.ws.hasMaskedBins(row): + masked_bins = self.ws.maskedBinsIndices(row) + if index.column() in masked_bins: + self.masked_bins_cache[row] = masked_bins + return True diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/test/test_tableworkspacedisplay_model.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/test/test_tableworkspacedisplay_model.py new file mode 100644 index 0000000000000000000000000000000000000000..7a7905053c36a05c500d065d2ab8d519f138723e --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/test/test_tableworkspacedisplay_model.py @@ -0,0 +1,66 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# +from __future__ import (absolute_import, division, print_function) + +import unittest + +from mock import Mock + +from mantid.simpleapi import CreateSampleWorkspace +from mantidqt.widgets.TableWorkspacedisplay.model import TableWorkspaceDisplayModel +from mantidqt.widgets.TableWorkspacedisplay.table_view_model import TableWorkspaceTableViewModelType +from mantidqt.widgets.TableWorkspacedisplay.test_helpers.TableWorkspacedisplay_common import \ + MockWorkspace + + +class TableWorkspaceDisplayModelTest(unittest.TestCase): + + def test_get_name(self): + ws = MockWorkspace() + expected_name = "TEST_WORKSPACE" + ws.name = Mock(return_value=expected_name) + model = TableWorkspaceDisplayModel(ws) + + self.assertEqual(expected_name, model.get_name()) + + def test_get_item_model(self): + ws = MockWorkspace() + expected_name = "TEST_WORKSPACE" + ws.name = Mock(return_value=expected_name) + model = TableWorkspaceDisplayModel(ws) + + x_model, y_model, e_model = model.get_item_model() + + self.assertEqual(x_model.type, TableWorkspaceTableViewModelType.x) + self.assertEqual(y_model.type, TableWorkspaceTableViewModelType.y) + self.assertEqual(e_model.type, TableWorkspaceTableViewModelType.e) + + def test_raises_with_unsupported_workspace(self): + # ws = MockWorkspace() + # expected_name = "TEST_WORKSPACE" + # ws.name = Mock(return_value=expected_name) + self.assertRaises(ValueError, lambda: TableWorkspaceDisplayModel([])) + self.assertRaises(ValueError, lambda: TableWorkspaceDisplayModel(1)) + self.assertRaises(ValueError, lambda: TableWorkspaceDisplayModel("test_string")) + + def test_no_raise_with_supported_workspace(self): + ws = MockWorkspace() + expected_name = "TEST_WORKSPACE" + ws.name = Mock(return_value=expected_name) + + # no need to assert anything - if the constructor raises the test will fail + TableWorkspaceDisplayModel(ws) + + ws = CreateSampleWorkspace(NumBanks=1, BankPixelWidth=4, NumEvents=10) + TableWorkspaceDisplayModel(ws) + + +if __name__ == '__main__': + unittest.main() diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/test/test_tableworkspacedisplay_presenter.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/test/test_tableworkspacedisplay_presenter.py new file mode 100644 index 0000000000000000000000000000000000000000..b41ab6e8d886ac58cf2b70bf285aff9f0621c641 --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/test/test_tableworkspacedisplay_presenter.py @@ -0,0 +1,297 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# +from __future__ import (absolute_import, division, print_function) + +import unittest + +from mock import Mock + +from mantidqt.widgets.TableWorkspacedisplay.presenter import TableWorkspaceDisplay +from mantidqt.widgets.TableWorkspacedisplay.test_helpers.TableWorkspacedisplay_common import MockQModelIndex, \ + MockWorkspace +from mantidqt.widgets.TableWorkspacedisplay.test_helpers.mock_TableWorkspacedisplay import \ + MockTableWorkspaceDisplayView, MockQTableView + + +class TableWorkspaceDisplayPresenterTest(unittest.TestCase): + def assertNotCalled(self, mock): + self.assertEqual(0, mock.call_count) + + def test_setup_table(self): + ws = MockWorkspace() + view = MockTableWorkspaceDisplayView() + TableWorkspaceDisplay(ws, view=view) + self.assertEqual(3, view.set_context_menu_actions.call_count) + self.assertEqual(1, view.set_model.call_count) + + def test_action_copy_spectrum_values(self): + ws = MockWorkspace() + view = MockTableWorkspaceDisplayView() + presenter = TableWorkspaceDisplay(ws, view=view) + + mock_table = MockQTableView() + + # two rows are selected in different positions + mock_indexes = [MockQModelIndex(0, 1), MockQModelIndex(3, 1)] + mock_table.mock_selection_model.selectedRows = Mock(return_value=mock_indexes) + + mock_read = Mock(return_value=[43, 99]) + presenter._get_ws_read_from_type = Mock(return_value=mock_read) + expected_string = "43\t99\n43\t99" + + presenter.action_copy_spectrum_values(mock_table) + + mock_table.selectionModel.assert_called_once_with() + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + view.copy_to_clipboard.assert_called_once_with(expected_string) + view.show_mouse_toast.assert_called_once_with(TableWorkspaceDisplay.COPY_SUCCESSFUL_MESSAGE) + + def test_action_copy_spectrum_values_no_selection(self): + ws = MockWorkspace() + view = MockTableWorkspaceDisplayView() + presenter = TableWorkspaceDisplay(ws, view=view) + + mock_table = MockQTableView() + mock_table.mock_selection_model.hasSelection = Mock(return_value=False) + mock_table.mock_selection_model.selectedRows = Mock() + + presenter.action_copy_spectrum_values(mock_table) + + mock_table.selectionModel.assert_called_once_with() + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + # the action should never look for rows if there is no selection + self.assertNotCalled(mock_table.mock_selection_model.selectedRows) + view.show_mouse_toast.assert_called_once_with(TableWorkspaceDisplay.NO_SELECTION_MESSAGE) + + def test_action_copy_bin_values(self): + ws = MockWorkspace() + view = MockTableWorkspaceDisplayView() + presenter = TableWorkspaceDisplay(ws, view=view) + mock_table = MockQTableView() + + # two columns are selected at different positions + mock_indexes = [MockQModelIndex(0, 0), MockQModelIndex(0, 3)] + mock_table.mock_selection_model.selectedColumns = Mock(return_value=mock_indexes) + # change the mock ws to have 3 histograms + ws.getNumberHistograms = Mock(return_value=3) + + mock_read = Mock(return_value=[83, 11, 33, 70]) + presenter._get_ws_read_from_type = Mock(return_value=mock_read) + expected_string = "83\t70\n83\t70\n83\t70" + + presenter.action_copy_bin_values(mock_table) + + mock_table.selectionModel.assert_called_once_with() + view.copy_to_clipboard.assert_called_once_with(expected_string) + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + view.show_mouse_toast.assert_called_once_with(TableWorkspaceDisplay.COPY_SUCCESSFUL_MESSAGE) + + def test_action_copy_bin_values_no_selection(self): + ws = MockWorkspace() + view = MockTableWorkspaceDisplayView() + presenter = TableWorkspaceDisplay(ws, view=view) + + mock_table = MockQTableView() + mock_table.mock_selection_model.hasSelection = Mock(return_value=False) + mock_table.mock_selection_model.selectedColumns = Mock() + + presenter.action_copy_bin_values(mock_table) + + mock_table.selectionModel.assert_called_once_with() + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + # the action should never look for rows if there is no selection + self.assertNotCalled(mock_table.mock_selection_model.selectedColumns) + view.show_mouse_toast.assert_called_once_with(TableWorkspaceDisplay.NO_SELECTION_MESSAGE) + + def test_action_copy_cell(self): + ws = MockWorkspace() + view = MockTableWorkspaceDisplayView() + presenter = TableWorkspaceDisplay(ws, view=view) + mock_table = MockQTableView() + + # two columns are selected at different positions + mock_index = MockQModelIndex(None, None) + mock_table.mock_selection_model.currentIndex = Mock(return_value=mock_index) + + presenter.action_copy_cells(mock_table) + + mock_table.selectionModel.assert_called_once_with() + self.assertEqual(1, view.copy_to_clipboard.call_count) + self.assertEqual(9, mock_index.sibling.call_count) + view.show_mouse_toast.assert_called_once_with(TableWorkspaceDisplay.COPY_SUCCESSFUL_MESSAGE) + + def test_action_copy_cell_no_selection(self): + ws = MockWorkspace() + view = MockTableWorkspaceDisplayView() + presenter = TableWorkspaceDisplay(ws, view=view) + mock_table = MockQTableView() + mock_table.mock_selection_model.hasSelection = Mock(return_value=False) + + presenter.action_copy_cells(mock_table) + + mock_table.selectionModel.assert_called_once_with() + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + view.show_mouse_toast.assert_called_once_with(TableWorkspaceDisplay.NO_SELECTION_MESSAGE) + + self.assertNotCalled(view.copy_to_clipboard) + + def common_setup_action_plot(self, table_has_selection=True): + mock_ws = MockWorkspace() + mock_view = MockTableWorkspaceDisplayView() + mock_plotter = Mock() + presenter = TableWorkspaceDisplay(mock_ws, plot=mock_plotter, view=mock_view) + + # monkey-patch the spectrum plot label to count the number of calls + presenter.model.get_spectrum_plot_label = Mock() + presenter.model.get_bin_plot_label = Mock() + + mock_table = MockQTableView() + # configure the mock return values + mock_table.mock_selection_model.hasSelection = Mock(return_value=table_has_selection) + return mock_plotter, mock_table, mock_view, presenter + + def setup_mock_selection(self, mock_table, num_selected_rows=None, num_selected_cols=None): + """ + :type mock_table: MockQTableView + :type num_selected_rows: int|None + :type num_selected_cols: int|None + """ + mock_selected = [] + if num_selected_rows is not None: + for i in range(num_selected_rows): + mock_selected.append(MockQModelIndex(i, 1)) + mock_table.mock_selection_model.selectedRows = Mock(return_value=mock_selected) + mock_table.mock_selection_model.selectedColumns = Mock() + elif num_selected_cols is not None: + for i in range(num_selected_cols): + mock_selected.append(MockQModelIndex(1, i)) + mock_table.mock_selection_model.selectedRows = Mock() + mock_table.mock_selection_model.selectedColumns = Mock(return_value=mock_selected) + else: + mock_table.mock_selection_model.selectedRows = Mock() + mock_table.mock_selection_model.selectedColumns = Mock() + return mock_selected + + def test_action_plot_spectrum_plot_many_confirmed(self): + mock_plot, mock_table, mock_view, presenter = self.common_setup_action_plot() + num_selected_rows = TableWorkspaceDisplay.NUM_SELECTED_FOR_CONFIRMATION + 1 + + self.setup_mock_selection(mock_table, num_selected_rows) + + # The a lot of things to plot message will show, set that the user will CONFIRM the plot + # meaning the rest of the function will execute as normal + mock_view.ask_confirmation = Mock(return_value=True) + + presenter.action_plot_spectrum(mock_table) + + mock_table.selectionModel.assert_called_once_with() + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + mock_table.mock_selection_model.selectedRows.assert_called_once_with() + + mock_view.ask_confirmation.assert_called_once_with( + TableWorkspaceDisplay.A_LOT_OF_THINGS_TO_PLOT_MESSAGE.format(num_selected_rows)) + + self.assertNotCalled(mock_table.mock_selection_model.selectedColumns) + self.assertEqual(1, mock_plot.call_count) + + def test_action_plot_spectrum_plot_many_denied(self): + mock_plot, mock_table, mock_view, presenter = self.common_setup_action_plot() + num_selected_rows = TableWorkspaceDisplay.NUM_SELECTED_FOR_CONFIRMATION + 1 + + # return value unused as most of the function being tested is not executed + self.setup_mock_selection(mock_table, num_selected_rows) + + # The a lot of things to plot message will show, set that the user will DENY the plot + # meaning the rest of the function will NOT EXECUTE AT ALL + mock_view.ask_confirmation = Mock(return_value=False) + + presenter.action_plot_spectrum(mock_table) + + mock_table.selectionModel.assert_called_once_with() + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + mock_table.mock_selection_model.selectedRows.assert_called_once_with() + + mock_view.ask_confirmation.assert_called_once_with( + TableWorkspaceDisplay.A_LOT_OF_THINGS_TO_PLOT_MESSAGE.format(num_selected_rows)) + + self.assertNotCalled(mock_table.mock_selection_model.selectedColumns) + self.assertNotCalled(mock_plot) + + def test_action_plot_spectrum_no_selection(self): + mock_plot, mock_table, mock_view, presenter = self.common_setup_action_plot(table_has_selection=False) + + mock_table.mock_selection_model.selectedRows = Mock() + mock_table.mock_selection_model.selectedColumns = Mock() + + presenter.action_plot_spectrum(mock_table) + + mock_view.show_mouse_toast.assert_called_once_with(TableWorkspaceDisplay.NO_SELECTION_MESSAGE) + mock_table.selectionModel.assert_called_once_with() + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + + self.assertNotCalled(mock_table.mock_selection_model.selectedRows) + self.assertNotCalled(mock_table.mock_selection_model.selectedColumns) + self.assertNotCalled(mock_plot) + + def test_action_plot_bin_plot_many_confirmed(self): + mock_plot, mock_table, mock_view, presenter = self.common_setup_action_plot() + num_selected_cols = TableWorkspaceDisplay.NUM_SELECTED_FOR_CONFIRMATION + 1 + self.setup_mock_selection(mock_table, num_selected_cols=num_selected_cols) + mock_view.ask_confirmation = Mock(return_value=True) + + presenter.action_plot_bin(mock_table) + + mock_table.selectionModel.assert_called_once_with() + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + mock_table.mock_selection_model.selectedColumns.assert_called_once_with() + self.assertNotCalled(mock_table.mock_selection_model.selectedRows) + + mock_view.ask_confirmation.assert_called_once_with( + TableWorkspaceDisplay.A_LOT_OF_THINGS_TO_PLOT_MESSAGE.format(num_selected_cols)) + self.assertEqual(1, mock_plot.call_count) + + def test_action_plot_bin_plot_many_denied(self): + mock_plot, mock_table, mock_view, presenter = self.common_setup_action_plot() + num_selected_cols = TableWorkspaceDisplay.NUM_SELECTED_FOR_CONFIRMATION + 1 + + # return value unused as most of the function being tested is not executed + self.setup_mock_selection(mock_table, num_selected_cols=num_selected_cols) + + mock_view.ask_confirmation = Mock(return_value=False) + + presenter.action_plot_bin(mock_table) + + mock_table.selectionModel.assert_called_once_with() + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + mock_table.mock_selection_model.selectedColumns.assert_called_once_with() + + mock_view.ask_confirmation.assert_called_once_with( + TableWorkspaceDisplay.A_LOT_OF_THINGS_TO_PLOT_MESSAGE.format(num_selected_cols)) + + self.assertNotCalled(mock_table.mock_selection_model.selectedRows) + self.assertNotCalled(mock_plot) + + def test_action_plot_bin_no_selection(self): + mock_plot, mock_table, mock_view, presenter = self.common_setup_action_plot(table_has_selection=False) + self.setup_mock_selection(mock_table, num_selected_rows=None, num_selected_cols=None) + + presenter.action_plot_bin(mock_table) + + mock_view.show_mouse_toast.assert_called_once_with(TableWorkspaceDisplay.NO_SELECTION_MESSAGE) + mock_table.selectionModel.assert_called_once_with() + mock_table.mock_selection_model.hasSelection.assert_called_once_with() + + self.assertNotCalled(mock_table.mock_selection_model.selectedRows) + self.assertNotCalled(mock_table.mock_selection_model.selectedColumns) + self.assertNotCalled(mock_plot) + + +if __name__ == '__main__': + unittest.main() diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/test/test_tableworkspacedisplay_tableviewmodel.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/test/test_tableworkspacedisplay_tableviewmodel.py new file mode 100644 index 0000000000000000000000000000000000000000..37e8ece30142f782cb88c8bbb363664652f51676 --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/test/test_tableworkspacedisplay_tableviewmodel.py @@ -0,0 +1,488 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# +from __future__ import absolute_import, absolute_import, division, division, print_function, print_function + +import unittest + +import qtpy +from mock import Mock, call +from qtpy import QtCore +from qtpy.QtCore import Qt + +from mantidqt.widgets.TableWorkspacedisplay.table_view_model import TableWorkspaceTableViewModel, \ + TableWorkspaceTableViewModelType +from mantidqt.widgets.TableWorkspacedisplay.test_helpers.TableWorkspacedisplay_common import \ + MockQModelIndex, MockWorkspace, setup_common_for_test_data, AXIS_INDEX_FOR_VERTICAL, MockMantidAxis, MockSpectrum, \ + MockMantidSymbol, AXIS_INDEX_FOR_HORIZONTAL, MockMantidUnit + + +class TableWorkspaceDisplayTableViewModelTest(unittest.TestCase): + WORKSPACE = r"C:\Users\qbr77747\dev\m\workbench_TableWorkspace\test_masked_bins.nxs" + + def test_correct_model_type(self): + ws = MockWorkspace() + model = TableWorkspaceTableViewModel(ws, TableWorkspaceTableViewModelType.x) + self.assertEqual(model.type, TableWorkspaceTableViewModelType.x) + + model = TableWorkspaceTableViewModel(ws, TableWorkspaceTableViewModelType.y) + self.assertEqual(model.type, TableWorkspaceTableViewModelType.y) + + model = TableWorkspaceTableViewModel(ws, TableWorkspaceTableViewModelType.e) + self.assertEqual(model.type, TableWorkspaceTableViewModelType.e) + + def test_correct_cell_colors(self): + ws = MockWorkspace() + model = TableWorkspaceTableViewModel(ws, TableWorkspaceTableViewModelType.x) + self.assertEqual((240, 240, 240, 255), model.masked_color.getRgb()) + self.assertEqual((255, 253, 209, 255), model.monitor_color.getRgb()) + + def test_correct_relevant_data(self): + ws = MockWorkspace() + model = TableWorkspaceTableViewModel(ws, TableWorkspaceTableViewModelType.x) + msg = "The function is not set correctly! The wrong data will be read." + self.assertEqual(ws.readX, model.relevant_data, msg=msg) + model = TableWorkspaceTableViewModel(ws, TableWorkspaceTableViewModelType.y) + self.assertEqual(ws.readY, model.relevant_data, msg=msg) + model = TableWorkspaceTableViewModel(ws, TableWorkspaceTableViewModelType.e) + self.assertEqual(ws.readE, model.relevant_data, msg=msg) + + def test_invalid_model_type(self): + ws = MockWorkspace() + with self.assertRaises(AssertionError): + TableWorkspaceTableViewModel(ws, "My Model Type") + + def test_data_display_role(self): + # Create some mock data for the mock workspace + row = 2 + column = 2 + # make a workspace with 0s + mock_data = [0] * 10 + # set one of them to be not 0 + mock_data[column] = 999 + # pass onto the MockWorkspace so that it returns it when read from the TableViewModel + self._check_correct_data_is_displayed(TableWorkspaceTableViewModelType.x, column, mock_data, row) + self._check_correct_data_is_displayed(TableWorkspaceTableViewModelType.y, column, mock_data, row) + self._check_correct_data_is_displayed(TableWorkspaceTableViewModelType.e, column, mock_data, row) + + def _check_correct_data_is_displayed(self, model_type, column, mock_data, row): + ws = MockWorkspace(read_return=mock_data) + model = TableWorkspaceTableViewModel(ws, model_type) + index = MockQModelIndex(row, column) + output = model.data(index, Qt.DisplayRole) + model.relevant_data.assert_called_once_with(row) + self.assertEqual(str(mock_data[column]), output) + + def test_row_and_column_count(self): + ws = MockWorkspace() + model_type = TableWorkspaceTableViewModelType.x + TableWorkspaceTableViewModel(ws, model_type) + # these are called when the TableViewModel is initialised + ws.getNumberHistograms.assert_called_once_with() + ws.blocksize.assert_called_once_with() + + def test_data_background_role_masked_row(self): + ws, model, row, index = setup_common_for_test_data() + + model.ws_spectrum_info.isMasked = Mock(return_value=True) + + output = model.data(index, Qt.BackgroundRole) + + model.ws_spectrum_info.hasDetectors.assert_called_once_with(row) + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + + index.row.assert_called_once_with() + self.assertFalse(index.column.called) + + self.assertEqual(model.masked_color, output) + + # Just do it a second time -> This time it's cached and should be read off the cache. + # If it is not read off the cache the assert_called_once below will fail, + # as the functions would be called a 2nd time + output = model.data(index, Qt.BackgroundRole) + + model.ws_spectrum_info.hasDetectors.assert_called_once_with(row) + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + self.assertEqual(model.masked_color, output) + + # assert that the row was called twice with no parameters + self.assertEqual(2, index.row.call_count) + self.assertFalse(index.column.called) + + def test_data_background_role_monitor_row(self): + ws, model, row, index = setup_common_for_test_data() + + model.ws_spectrum_info.isMasked = Mock(return_value=False) + model.ws_spectrum_info.isMonitor = Mock(return_value=True) + + output = model.data(index, Qt.BackgroundRole) + + model.ws_spectrum_info.hasDetectors.assert_called_with(row) + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + + index.row.assert_called_once_with() + self.assertFalse(index.column.called) + + self.assertEqual(model.monitor_color, output) + + # Just do it a second time -> This time it's cached and should be read off the cache. + # If it is not read off the cache the assert_called_once below will fail, + # as the functions would be called a 2nd time + output = model.data(index, Qt.BackgroundRole) + + model.ws_spectrum_info.hasDetectors.assert_called_with(row) + # assert that it has been called twice with the same parameters + model.ws_spectrum_info.isMasked.assert_has_calls([call.do_work(row), call.do_work(row)]) + # only called once, as the 2nd time should have hit the cached monitor + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + self.assertEqual(model.monitor_color, output) + + # assert that the row was called twice with no parameters + self.assertEqual(2, index.row.call_count) + self.assertFalse(index.column.called) + + def test_data_background_role_masked_bin(self): + ws, model, row, index = setup_common_for_test_data() + + model.ws_spectrum_info.isMasked = Mock(return_value=False) + model.ws_spectrum_info.isMonitor = Mock(return_value=False) + + output = model.data(index, Qt.BackgroundRole) + + model.ws_spectrum_info.hasDetectors.assert_called_with(row) + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + ws.hasMaskedBins.assert_called_once_with(row) + ws.maskedBinsIndices.assert_called_once_with(row) + + index.row.assert_called_once_with() + index.column.assert_called_once_with() + + self.assertEqual(model.masked_color, output) + # Just do it a second time -> This time it's cached and should be read off the cache. + # If it is not read off the cache the assert_called_once below will fail, + # as the functions would be called a 2nd time + output = model.data(index, Qt.BackgroundRole) + + # masked bins is checked last, so it will call all other functions a second time + model.ws_spectrum_info.hasDetectors.assert_has_calls([call.do_work(row), call.do_work(row)]) + model.ws_spectrum_info.isMasked.assert_has_calls([call.do_work(row), call.do_work(row)]) + model.ws_spectrum_info.isMonitor.assert_has_calls([call.do_work(row), call.do_work(row)]) + + # these, however, should remain at 1 call, as the masked bin cache should have been hit + ws.hasMaskedBins.assert_called_once_with(row) + ws.maskedBinsIndices.assert_called_once_with(row) + + self.assertEqual(model.masked_color, output) + + self.assertEqual(2, index.row.call_count) + self.assertEqual(2, index.column.call_count) + + def test_data_tooltip_role_masked_row(self): + if not qtpy.PYQT5: + self.skipTest("QVariant cannot be instantiated in QT4, and the test fails with an error.") + ws, model, row, index = setup_common_for_test_data() + + model.ws_spectrum_info.hasDetectors = Mock(return_value=True) + model.ws_spectrum_info.isMasked = Mock(return_value=True) + model.ws_spectrum_info.isMonitor = Mock(return_value=False) + + output = model.data(index, Qt.ToolTipRole) + + model.ws_spectrum_info.hasDetectors.assert_has_calls([call.do_work(row), call.do_work(row)]) + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + + self.assertEqual(TableWorkspaceTableViewModel.MASKED_ROW_STRING, output) + + output = model.data(index, Qt.ToolTipRole) + + # The row was masked so it should have been cached + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + + # However it is checked if it is a monitor again + self.assertEqual(3, model.ws_spectrum_info.hasDetectors.call_count) + self.assertEqual(2, model.ws_spectrum_info.isMonitor.call_count) + + self.assertEqual(TableWorkspaceTableViewModel.MASKED_ROW_STRING, output) + + def test_data_tooltip_role_masked_monitor_row(self): + if not qtpy.PYQT5: + self.skipTest("QVariant cannot be instantiated in QT4, and the test fails with an error.") + ws, model, row, index = setup_common_for_test_data() + + model.ws_spectrum_info.isMasked = Mock(return_value=True) + model.ws_spectrum_info.isMonitor = Mock(return_value=True) + + output = model.data(index, Qt.ToolTipRole) + + model.ws_spectrum_info.hasDetectors.assert_has_calls([call.do_work(row), call.do_work(row)]) + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + + self.assertEqual(TableWorkspaceTableViewModel.MASKED_MONITOR_ROW_STRING, output) + + # Doing the same thing a second time should hit the cache, so no additional calls will have been made + output = model.data(index, Qt.ToolTipRole) + model.ws_spectrum_info.hasDetectors.assert_has_calls([call.do_work(row), call.do_work(row)]) + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + + self.assertEqual(TableWorkspaceTableViewModel.MASKED_MONITOR_ROW_STRING, output) + + def test_data_tooltip_role_monitor_row(self): + if not qtpy.PYQT5: + self.skipTest("QVariant cannot be instantiated in QT4, and the test fails with an error.") + ws, model, row, index = setup_common_for_test_data() + + # necessary otherwise it is returned that there is a masked bin, and we get the wrong output + ws.hasMaskedBins = Mock(return_value=False) + + model.ws_spectrum_info.isMasked = Mock(return_value=False) + model.ws_spectrum_info.isMonitor = Mock(return_value=True) + + output = model.data(index, Qt.ToolTipRole) + + model.ws_spectrum_info.hasDetectors.assert_has_calls([call.do_work(row), call.do_work(row)]) + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + + self.assertEqual(TableWorkspaceTableViewModel.MONITOR_ROW_STRING, output) + + # Doing the same thing a second time should hit the cache, so no additional calls will have been made + output = model.data(index, Qt.ToolTipRole) + model.ws_spectrum_info.hasDetectors.assert_has_calls([call.do_work(row), call.do_work(row)]) + self.assertEqual(2, model.ws_spectrum_info.isMasked.call_count) + # This was called only once because the monitor was cached + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + + self.assertEqual(TableWorkspaceTableViewModel.MONITOR_ROW_STRING, output) + + def test_data_tooltip_role_masked_bin_in_monitor_row(self): + if not qtpy.PYQT5: + self.skipTest("QVariant cannot be instantiated in QT4, and the test fails with an error.") + + ws, model, row, index = setup_common_for_test_data() + + model.ws_spectrum_info.isMasked = Mock(return_value=False) + model.ws_spectrum_info.isMonitor = Mock(return_value=True) + ws.hasMaskedBins = Mock(return_value=True) + ws.maskedBinsIndices = Mock(return_value=[index.column()]) + + output = model.data(index, Qt.ToolTipRole) + + model.ws_spectrum_info.hasDetectors.assert_has_calls([call.do_work(row), call.do_work(row)]) + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + + self.assertEqual( + TableWorkspaceTableViewModel.MONITOR_ROW_STRING + TableWorkspaceTableViewModel.MASKED_BIN_STRING, output) + + # Doing the same thing a second time should hit the cache, so no additional calls will have been made + output = model.data(index, Qt.ToolTipRole) + model.ws_spectrum_info.hasDetectors.assert_has_calls([call.do_work(row), call.do_work(row)]) + self.assertEqual(2, model.ws_spectrum_info.isMasked.call_count) + # This was called only once because the monitor was cached + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + + self.assertEqual( + TableWorkspaceTableViewModel.MONITOR_ROW_STRING + TableWorkspaceTableViewModel.MASKED_BIN_STRING, output) + + def test_data_tooltip_role_masked_bin(self): + if not qtpy.PYQT5: + self.skipTest("QVariant cannot be instantiated in QT4, and the test fails with an error.") + + ws, model, row, index = setup_common_for_test_data() + + model.ws_spectrum_info.isMasked = Mock(return_value=False) + model.ws_spectrum_info.isMonitor = Mock(return_value=False) + ws.hasMaskedBins = Mock(return_value=True) + ws.maskedBinsIndices = Mock(return_value=[index.column()]) + + output = model.data(index, Qt.ToolTipRole) + + model.ws_spectrum_info.hasDetectors.assert_has_calls([call.do_work(row), call.do_work(row)]) + model.ws_spectrum_info.isMasked.assert_called_once_with(row) + model.ws_spectrum_info.isMonitor.assert_called_once_with(row) + + self.assertEqual(TableWorkspaceTableViewModel.MASKED_BIN_STRING, output) + + # Doing the same thing a second time should hit the cache, so no additional calls will have been made + output = model.data(index, Qt.ToolTipRole) + self.assertEqual(4, model.ws_spectrum_info.hasDetectors.call_count) + self.assertEqual(2, model.ws_spectrum_info.isMasked.call_count) + self.assertEqual(2, model.ws_spectrum_info.isMonitor.call_count) + # This was called only once because the monitor was cached + ws.hasMaskedBins.assert_called_once_with(row) + ws.maskedBinsIndices.assert_called_once_with(row) + + self.assertEqual(TableWorkspaceTableViewModel.MASKED_BIN_STRING, output) + + def test_headerData_not_display_or_tooltip(self): + if not qtpy.PYQT5: + self.skipTest("QVariant cannot be instantiated in QT4, and the test fails with an error.") + ws = MockWorkspace() + model_type = TableWorkspaceTableViewModelType.x + model = TableWorkspaceTableViewModel(ws, model_type) + output = model.headerData(0, Qt.Vertical, Qt.BackgroundRole) + self.assertTrue(isinstance(output, QtCore.QVariant)) + + def test_headerData_vertical_header_display_role(self): + ws = MockWorkspace() + model_type = TableWorkspaceTableViewModelType.x + model = TableWorkspaceTableViewModel(ws, model_type) + mock_section = 0 + output = model.headerData(mock_section, Qt.Vertical, Qt.DisplayRole) + + ws.getAxis.assert_called_once_with(AXIS_INDEX_FOR_VERTICAL) + ws.mock_axis.label.assert_called_once_with(mock_section) + + expected_output = TableWorkspaceTableViewModel.VERTICAL_HEADER_DISPLAY_STRING.format(mock_section, + MockMantidAxis.TEST_LABEL) + + self.assertEqual(expected_output, output) + + def test_headerData_vertical_header_tooltip_role(self): + ws = MockWorkspace() + model_type = TableWorkspaceTableViewModelType.x + model = TableWorkspaceTableViewModel(ws, model_type) + mock_section = 0 + output = model.headerData(mock_section, Qt.Vertical, Qt.ToolTipRole) + + ws.getSpectrum.assert_called_once_with(mock_section) + ws.mock_spectrum.getSpectrumNo.assert_called_once_with() + + expected_output = TableWorkspaceTableViewModel.VERTICAL_HEADER_TOOLTIP_STRING.format(mock_section, + MockSpectrum.TEST_SPECTRUM_NO) + self.assertEqual(expected_output, output) + + def test_headerData_horizontal_header_display_role_for_X_values(self): + ws = MockWorkspace() + model_type = TableWorkspaceTableViewModelType.x + model = TableWorkspaceTableViewModel(ws, model_type) + mock_section = 0 + output = model.headerData(mock_section, Qt.Horizontal, Qt.DisplayRole) + expected_output = TableWorkspaceTableViewModel.HORIZONTAL_HEADER_DISPLAY_STRING_FOR_X_VALUES.format( + mock_section) + self.assertEqual(expected_output, output) + + def test_headerData_horizontal_header_tooltip_role_for_X_values(self): + ws = MockWorkspace() + model_type = TableWorkspaceTableViewModelType.x + model = TableWorkspaceTableViewModel(ws, model_type) + mock_section = 0 + output = model.headerData(mock_section, Qt.Horizontal, Qt.ToolTipRole) + + expected_output = TableWorkspaceTableViewModel.HORIZONTAL_HEADER_TOOLTIP_STRING_FOR_X_VALUES.format( + mock_section) + self.assertEqual(expected_output, output) + + def test_headerData_horizontal_header_display_role_histogram_data(self): + mock_section = 0 + mock_return_values = [0, 1, 2, 3, 4, 5, 6] + expected_bin_centre = (mock_return_values[mock_section] + mock_return_values[mock_section + 1]) / 2.0 + is_histogram_data = True + + self._run_test_headerData_horizontal_header_display_role(is_histogram_data, mock_return_values, mock_section, + expected_bin_centre) + + def test_headerData_horizontal_header_display_role_not_histogram_data(self): + mock_section = 0 + mock_return_values = [0, 1, 2, 3, 4, 5, 6] + expected_bin_centre = mock_return_values[mock_section] + is_histogram_data = False + + self._run_test_headerData_horizontal_header_display_role(is_histogram_data, mock_return_values, mock_section, + expected_bin_centre) + + def _run_test_headerData_horizontal_header_display_role(self, is_histogram_data, mock_return_values, mock_section, + expected_bin_centre): + ws = MockWorkspace(read_return=mock_return_values, isHistogramData=is_histogram_data) + model_type = TableWorkspaceTableViewModelType.y + model = TableWorkspaceTableViewModel(ws, model_type) + output = model.headerData(mock_section, Qt.Horizontal, Qt.DisplayRole) + + ws.isHistogramData.assert_called_once_with() + ws.readX.assert_called_once_with(0) + ws.getAxis.assert_called_once_with(AXIS_INDEX_FOR_HORIZONTAL) + ws.mock_axis.getUnit.assert_called_once_with() + ws.mock_axis.mock_unit.symbol.assert_called_once_with() + ws.mock_axis.mock_unit.mock_symbol.utf8.assert_called_once_with() + expected_output = TableWorkspaceTableViewModel \ + .HORIZONTAL_HEADER_DISPLAY_STRING \ + .format(mock_section, expected_bin_centre, MockMantidSymbol.TEST_UTF8) + self.assertEqual(expected_output, output) + + def test_headerData_horizontal_header_tooltip_role_histogram_data(self): + mock_section = 0 + mock_return_values = [0, 1, 2, 3, 4, 5, 6] + expected_bin_centre = (mock_return_values[mock_section] + mock_return_values[mock_section + 1]) / 2.0 + is_histogram_data = True + + self._run_test_headerData_horizontal_header_tooltip_role(is_histogram_data, mock_return_values, mock_section, + expected_bin_centre) + + def test_headerData_horizontal_header_tooltip_role_not_histogram_data(self): + mock_section = 0 + mock_return_values = [0, 1, 2, 3, 4, 5, 6] + expected_bin_centre = mock_return_values[mock_section] + is_histogram_data = False + self._run_test_headerData_horizontal_header_tooltip_role(is_histogram_data, mock_return_values, mock_section, + expected_bin_centre) + + def _run_test_headerData_horizontal_header_tooltip_role(self, is_histogram_data, mock_return_values, mock_section, + expected_bin_centre): + ws = MockWorkspace(read_return=mock_return_values, isHistogramData=is_histogram_data) + model_type = TableWorkspaceTableViewModelType.y + model = TableWorkspaceTableViewModel(ws, model_type) + output = model.headerData(mock_section, Qt.Horizontal, Qt.ToolTipRole) + + ws.isHistogramData.assert_called_once_with() + ws.readX.assert_called_once_with(0) + ws.getAxis.assert_called_once_with(AXIS_INDEX_FOR_HORIZONTAL) + ws.mock_axis.getUnit.assert_called_once_with() + ws.mock_axis.mock_unit.symbol.assert_called_once_with() + ws.mock_axis.mock_unit.caption.assert_called_once_with() + ws.mock_axis.mock_unit.mock_symbol.utf8.assert_called_once_with() + + expected_output = TableWorkspaceTableViewModel \ + .HORIZONTAL_HEADER_TOOLTIP_STRING \ + .format(mock_section, MockMantidUnit.TEST_CAPTION, expected_bin_centre, MockMantidSymbol.TEST_UTF8) + self.assertEqual(expected_output, output) + + def test_not_common_bins_horizontal_display_role(self): + mock_section = 0 + mock_return_values = [0, 1, 2, 3, 4, 5, 6] + is_histogram_data = False + + ws = MockWorkspace(read_return=mock_return_values, isHistogramData=is_histogram_data) + ws.isCommonBins = Mock(return_value=False) + model_type = TableWorkspaceTableViewModelType.y + model = TableWorkspaceTableViewModel(ws, model_type) + output = model.headerData(mock_section, Qt.Horizontal, Qt.DisplayRole) + + self.assertEqual(TableWorkspaceTableViewModel.HORIZONTAL_BINS_VARY_DISPLAY_STRING.format(mock_section), output) + + def test_not_common_bins_horizontal_tooltip_role(self): + mock_section = 0 + mock_return_values = [0, 1, 2, 3, 4, 5, 6] + is_histogram_data = False + + ws = MockWorkspace(read_return=mock_return_values, isHistogramData=is_histogram_data) + ws.isCommonBins = Mock(return_value=False) + model_type = TableWorkspaceTableViewModelType.y + model = TableWorkspaceTableViewModel(ws, model_type) + output = model.headerData(mock_section, Qt.Horizontal, Qt.ToolTipRole) + + self.assertEqual(TableWorkspaceTableViewModel.HORIZONTAL_BINS_VARY_TOOLTIP_STRING.format(mock_section), output) + + +if __name__ == '__main__': + unittest.main() diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/test_helpers/__init__.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/test_helpers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..57d5ae5a28a63ed0dd44886f201c25df7cac61ca --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/test_helpers/__init__.py @@ -0,0 +1,9 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/test_helpers/mock_tableworkspacedisplay.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/test_helpers/mock_tableworkspacedisplay.py new file mode 100644 index 0000000000000000000000000000000000000000..072b0f512d5eebfecdf5d361441e30d4d081bfe6 --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/test_helpers/mock_tableworkspacedisplay.py @@ -0,0 +1,81 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# +from mock import Mock + +from mantidqt.widgets.TableWorkspacedisplay.table_view_model import TableWorkspaceTableViewModelType + + +class MockQTableHeader(object): + def __init__(self): + self.addAction = Mock() + + +class MockQSelection: + def __init__(self): + self.mock_item_range = MockQItemRange() + self.first = Mock(return_value=self.mock_item_range) + + +class MockQItemRange(object): + def __init__(self): + self.top = Mock(return_value=0) + self.bottom = Mock(return_value=2) + self.left = Mock(return_value=0) + self.right = Mock(return_value=2) + + +class MockQSelectionModel: + def __init__(self): + self.hasSelection = Mock() + self.selectedRows = None + self.selectedColumns = None + self.currentIndex = None + self.mock_selection = MockQSelection() + self.selection = Mock(return_value=self.mock_selection) + + +class MockQTableViewModel: + def __init__(self): + self.type = TableWorkspaceTableViewModelType.x + + +class MockQTableView: + def __init__(self): + self.setContextMenuPolicy = Mock() + self.addAction = Mock() + self.mock_horizontalHeader = MockQTableHeader() + self.mock_verticalHeader = MockQTableHeader() + self.horizontalHeader = Mock(return_value=self.mock_horizontalHeader) + self.verticalHeader = Mock(return_value=self.mock_verticalHeader) + self.setModel = Mock() + self.mock_model = MockQTableViewModel() + self.model = Mock(return_value=self.mock_model) + + self.mock_selection_model = MockQSelectionModel() + + self.selectionModel = Mock(return_value=self.mock_selection_model) + + +class MockTableWorkspaceDisplayView: + def __init__(self): + self.set_context_menu_actions = Mock() + self.table_x = MockQTableView() + self.table_y = MockQTableView() + self.table_e = MockQTableView() + self.set_model = Mock() + self.copy_to_clipboard = Mock() + self.show_mouse_toast = Mock() + self.ask_confirmation = None + + +class MockTableWorkspaceDisplayModel: + def __init__(self): + self.get_spectrum_plot_label = Mock() + self.get_bin_plot_label = Mock() diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/test_helpers/tableworkspacedisplay_common.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/test_helpers/tableworkspacedisplay_common.py new file mode 100644 index 0000000000000000000000000000000000000000..9301a4c03ee90961e72f5793fc01068bc4175db1 --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/test_helpers/tableworkspacedisplay_common.py @@ -0,0 +1,134 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# +from __future__ import (absolute_import, division, print_function) + +from mock import Mock + +from mantidqt.widgets.TableWorkspacedisplay.table_view_model import TableWorkspaceTableViewModel, \ + TableWorkspaceTableViewModelType + +AXIS_INDEX_FOR_HORIZONTAL = 0 +AXIS_INDEX_FOR_VERTICAL = 1 + + +def setup_common_for_test_data(): + """ + Common configuration of variables and mocking for testing + TableWorkspaceDisplayTableViewModel's data and headerData functions + """ + # Create some mock data for the mock workspace + row = 2 + column = 2 + # make a workspace with 0s + mock_data = [0] * 10 + # set one of them to be not 0 + mock_data[column] = 999 + model_type = TableWorkspaceTableViewModelType.x + # pass onto the MockWorkspace so that it returns it when read from the TableViewModel + ws = MockWorkspace(read_return=mock_data) + ws.hasMaskedBins = Mock(return_value=True) + ws.maskedBinsIndices = Mock(return_value=[column]) + model = TableWorkspaceTableViewModel(ws, model_type) + # The model retrieves the spectrumInfo object, and our MockWorkspace has already given it + # the MockSpectrumInfo, so all that needs to be done here is to set up the correct method Mocks + model.ws_spectrum_info.hasDetectors = Mock(return_value=True) + index = MockQModelIndex(row, column) + return ws, model, row, index + + +class MockMantidSymbol: + TEST_ASCII = "MANTID_ASCII_SYMBOL" + TEST_UTF8 = "MANTID_UTF8_SYMBOL" + + def __init__(self): + self.utf8 = Mock(return_value=self.TEST_UTF8) + self.ascii = Mock(return_value=self.TEST_ASCII) + + +class MockMantidUnit: + TEST_CAPTION = "MANTID_TEST_CAPTION" + + def __init__(self): + self.mock_symbol = MockMantidSymbol() + self.symbol = Mock(return_value=self.mock_symbol) + self.caption = Mock(return_value=self.TEST_CAPTION) + + +class MockMantidAxis: + TEST_LABEL = "MANTID_TEST_AXIS" + + def __init__(self): + self.label = Mock(return_value=self.TEST_LABEL) + + self.mock_unit = MockMantidUnit() + self.getUnit = Mock(return_value=self.mock_unit) + + +class MockQModelIndexSibling: + TEST_SIBLING_DATA = "MANTID_TEST_SIBLING_DATA" + + def __init__(self): + self.data = Mock(return_value=self.TEST_SIBLING_DATA) + + +class MockQModelIndex: + + def __init__(self, row, column): + self.row = Mock(return_value=row) + self.column = Mock(return_value=column) + self.mock_sibling = MockQModelIndexSibling() + self.sibling = Mock(return_value=self.mock_sibling) + + +class MockSpectrumInfo: + def __init__(self): + self.hasDetectors = None + self.isMasked = None + self.isMonitor = None + + +class MockSpectrum: + TEST_SPECTRUM_NO = 123123 + + def __init__(self): + self.getSpectrumNo = Mock(return_value=self.TEST_SPECTRUM_NO) + + +class MockWorkspace: + TEST_NAME = "THISISAtestWORKSPACE" + + @staticmethod + def _return_MockSpectrumInfo(): + return MockSpectrumInfo() + + def __init__(self, read_return=None, axes=2, isHistogramData=True): + if read_return is None: + read_return = [1, 2, 3, 4, 5] + # This is assigned to a function, as the original implementation is a function that returns + # the spectrumInfo object + self.spectrumInfo = self._return_MockSpectrumInfo + self.getNumberHistograms = Mock(return_value=1) + self.isHistogramData = Mock(return_value=isHistogramData) + self.blocksize = Mock(return_value=len(read_return)) + self.readX = Mock(return_value=read_return) + self.readY = Mock(return_value=read_return) + self.readE = Mock(return_value=read_return) + self.axes = Mock(return_value=axes) + self.hasMaskedBins = None + self.maskedBinsIndices = None + self.isCommonBins = Mock(return_value=True) + + self.mock_spectrum = MockSpectrum() + self.getSpectrum = Mock(return_value=self.mock_spectrum) + + self.mock_axis = MockMantidAxis() + self.getAxis = Mock(return_value=self.mock_axis) + + self.name = None diff --git a/qt/python/mantidqt/widgets/tableworkspacedisplay/view.py b/qt/python/mantidqt/widgets/tableworkspacedisplay/view.py new file mode 100644 index 0000000000000000000000000000000000000000..a291367eb4f505a1afc80a01950013f3b494d4b6 --- /dev/null +++ b/qt/python/mantidqt/widgets/tableworkspacedisplay/view.py @@ -0,0 +1,103 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +# This file is part of the mantid workbench. +# +# +from __future__ import (absolute_import, division, print_function) + +from functools import partial + +from qtpy import QtGui +from qtpy.QtCore import QPoint, Qt +from qtpy.QtGui import QCursor, QFont, QFontMetrics, QKeySequence +from qtpy.QtWidgets import (QAction, QHeaderView, QMessageBox, QTableView, QTableWidget, QToolTip) + +import mantidqt.icons + + +class TableWorkspaceDisplayView(QTableWidget): + def __init__(self, presenter, parent=None, name=''): + super(TableWorkspaceDisplayView, self).__init__(parent) + + self.presenter = presenter + self.COPY_ICON = mantidqt.icons.get_icon("fa.files-o") + + # change the default color of the rows - makes them light blue + # monitors and masked rows are colored in the table's custom model + # palette = self.palette() + # palette.setColor(QtGui.QPalette.Base, QtGui.QColor(128, 255, 255)) + # self.setPalette(palette) + + self.setWindowTitle("{} - Mantid".format(name)) + self.setWindowFlags(Qt.Window) + + self.resize(600, 400) + self.show() + + def keyPressEvent(self, event): + if event.matches(QKeySequence.Copy): + self.presenter.action_keypress_copy(self) + super(TableWorkspaceDisplayView, self).keyPressEvent(event) + + def set_context_menu_actions(self, table): + """ + Sets up the context menu actions for the table + :type table: QTableView + :param table: The table whose context menu actions will be set up. + :param ws_read_function: The read function used to efficiently retrieve data directly from the workspace + """ + copy_action = QAction(self.COPY_ICON, "Copy", table) + # sets the first (table) parameter of the copy action callback + # so that each context menu can copy the data from the correct table + decorated_copy_action_with_correct_table = partial(self.presenter.action_copy_cells, table) + copy_action.triggered.connect(decorated_copy_action_with_correct_table) + + table.setContextMenuPolicy(Qt.ActionsContextMenu) + table.addAction(copy_action) + + horizontalHeader = table.horizontalHeader() + horizontalHeader.setContextMenuPolicy(Qt.ActionsContextMenu) + horizontalHeader.setSectionResizeMode(QHeaderView.Fixed) + + copy_bin_values = QAction(self.COPY_ICON, "Copy", horizontalHeader) + copy_bin_values.triggered.connect(partial(self.presenter.action_copy_bin_values, table)) + + horizontalHeader.addAction(copy_bin_values) + + verticalHeader = table.verticalHeader() + verticalHeader.setContextMenuPolicy(Qt.ActionsContextMenu) + verticalHeader.setSectionResizeMode(QHeaderView.Fixed) + + copy_spectrum_values = QAction(self.COPY_ICON, "Copy", verticalHeader) + copy_spectrum_values.triggered.connect(partial(self.presenter.action_copy_spectrum_values, table)) + + separator1 = QAction(verticalHeader) + separator1.setSeparator(True) + + verticalHeader.addAction(copy_spectrum_values) + verticalHeader.addAction(separator1) + + @staticmethod + def copy_to_clipboard(data): + """ + Uses the QGuiApplication to copy to the system clipboard. + + :type data: str + :param data: The data that will be copied to the clipboard + :return: + """ + cb = QtGui.QGuiApplication.clipboard() + cb.setText(data, mode=cb.Clipboard) + + + def ask_confirmation(self, message, title="Mantid Workbench"): + """ + :param message: + :return: + """ + reply = QMessageBox.question(self, title, message, QMessageBox.Yes, QMessageBox.No) + return True if reply == QMessageBox.Yes else False