Skip to content
Snippets Groups Projects
Commit fbede83e authored by Stephen's avatar Stephen Committed by Peterson, Peter
Browse files

Separate table presenters for batch and standard table models

parent 38d096ea
No related branches found
No related tags found
No related merge requests found
......@@ -248,7 +248,7 @@ class WorkspaceWidget(PluginWidget):
except ValueError:
try:
TableWorkspaceDisplay.supports(ws)
presenter = TableWorkspaceDisplay(ws, plot=matplotlib.pyplot, parent=self)
presenter = TableWorkspaceDisplay(ws, plot=matplotlib.pyplot, parent=self, batch=True)
presenter.show_view()
except ValueError:
logger.error("Could not open workspace: {0} with neither "
......
......@@ -10,13 +10,13 @@
from enum import Enum
# local imports
from mantidqt.widgets.workspacedisplay.table.presenter \
import TableWorkspaceDataPresenter, create_table_item
from mantidqt.widgets.workspacedisplay.table.presenter_standard \
import TableWorkspaceDataPresenterStandard, create_table_item
from .model import create_peaksviewermodel
from ..adsobsever import SliceViewerADSObserver
class PeaksWorkspaceDataPresenter(TableWorkspaceDataPresenter):
class PeaksWorkspaceDataPresenter(TableWorkspaceDataPresenterStandard):
"""Override create_item method to format table columns more
appropriately
"""
......
......@@ -16,53 +16,15 @@ from mantidqt.widgets.workspacedisplay.status_bar_view import StatusBarView
from mantidqt.widgets.workspacedisplay.table.error_column import ErrorColumn
from mantidqt.widgets.workspacedisplay.table.model import TableWorkspaceDisplayModel
from mantidqt.widgets.workspacedisplay.table.plot_type import PlotType
from mantidqt.widgets.workspacedisplay.table.presenter_batch import TableWorkspaceDataPresenterBatch
from mantidqt.widgets.workspacedisplay.table.presenter_standard import TableWorkspaceDataPresenterStandard
from mantidqt.widgets.workspacedisplay.table.table_model import TableModel
from mantidqt.widgets.workspacedisplay.table.view import TableWorkspaceDisplayView
from mantidqt.widgets.workspacedisplay.table.tableworkspace_item import (QStandardItem, create_table_item, # noqa: F401
RevertibleItem) # noqa: F401
class TableWorkspaceDataPresenter(object):
"""Presenter to handle just displaying data from a table-like object.
Useful for other widgets wishing to embed just the table display"""
__slots__ = ("model", "view")
def __init__(self, model=None, view=None):
"""
:param model: A reference to the model holding the table information
:param view: A reference to the view that is displayed to the user
"""
self.model = model
self.view = view
def refresh(self):
"""Fully refresh the display. Updates column headers and reloads the data"""
self.update_column_headers()
self.load_data(self.view)
def update_column_headers(self):
"""
:param extra_labels: Extra labels to be appended to the column headers.
Expected format: [(id, label), (2, "X"),...]
:type extra_labels: List[Tuple[int, str]]
:return:
"""
# deep copy the original headers so that they are not changed by the appending of the label
column_headers = self.model.original_column_headers()
table_item_model = self.view.model()
extra_labels = self.model.build_current_labels()
if len(extra_labels) > 0:
for index, label in extra_labels:
column_headers[index] += str(label)
table_item_model.setHorizontalHeaderLabels(column_headers)
def load_data(self, table):
table.model().load_data(self.model)
class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, DataCopier):
class TableWorkspaceDisplay(ObservingPresenter, DataCopier):
A_LOT_OF_THINGS_TO_PLOT_MESSAGE = (
"You selected {} spectra to plot. Are you sure you want to plot that many?")
TOO_MANY_SELECTED_FOR_X = ("Too many columns are selected to use as X. Please select only 1.")
......@@ -94,6 +56,7 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
container=None,
window_width=600,
window_height=400,
batch=False,
):
"""
Creates a display for the provided workspace.
......@@ -108,15 +71,13 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
:param ads_observer: ADS observer to be used by the presenter. If not provided the default
one is used. Mainly intended for testing.
"""
model = model if model is not None else TableWorkspaceDisplayModel(ws)
table_model = TableModel(parent=parent, data_model=model)
view = view if view else TableWorkspaceDisplayView(self, parent, table_model=table_model)
TableWorkspaceDataPresenter.__init__(self, model, view)
self.name = name if name else self.model.get_name()
view, model = self.create_table(ws, parent, model, view, batch)
self.view = view
self.model = model
self.name = name if name else model.get_name()
self.container = (container if container else StatusBarView(
parent,
self.view,
view,
self.name,
window_width=window_width,
window_height=window_height,
......@@ -127,15 +88,35 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
self.parent = parent
self.plot = plot
self.view.set_context_menu_actions(self.view)
self.ads_observer = (ads_observer if ads_observer else WorkspaceDisplayADSObserver(self))
self.refresh()
self.presenter.refresh()
def show_view(self):
self.container.show()
def create_table(self, ws, parent, model, view, batch):
if batch:
view, model = self._create_table_batch(ws, parent, view, model)
else:
view, model = self._create_table_standard(ws, parent, view, model)
view.set_context_menu_actions(view)
return view, model
def _create_table_standard(self, ws, parent, view, model):
model = model if model is not None else TableWorkspaceDisplayModel(ws)
view = view if view else TableWorkspaceDisplayView(presenter=self, parent=parent)
self.presenter = TableWorkspaceDataPresenterStandard(model, view)
return view, model
def _create_table_batch(self, ws, parent, view, model):
model = model if model is not None else TableWorkspaceDisplayModel(ws)
table_model = TableModel(parent=parent, data_model=model)
view = view if view else TableWorkspaceDisplayView(presenter=self, parent=parent, table_model=table_model)
self.presenter = TableWorkspaceDataPresenterBatch(model, view)
return view, model
@classmethod
def supports(cls, ws):
"""
......@@ -146,30 +127,30 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
return TableWorkspaceDisplayModel.supports(ws)
def replace_workspace(self, workspace_name, workspace):
if self.model.workspace_equals(workspace_name):
if self.presenter.model.workspace_equals(workspace_name):
# stops triggering itemChanged signal while the data is being reloaded
self.view.blockSignals(True)
self.presenter.view.blockSignals(True)
self.model = TableWorkspaceDisplayModel(workspace)
self.load_data(self.view)
self.view.blockSignals(False)
self.presenter.model = TableWorkspaceDisplayModel(workspace)
self.presenter.load_data(self.presenter.view)
self.presenter.view.blockSignals(False)
self.view.emit_repaint()
self.presenter.view.emit_repaint()
def action_copy_cells(self):
self.copy_cells(self.view)
self.copy_cells(self.presenter.view)
def action_copy_bin_values(self):
self.copy_cells(self.view)
self.copy_cells(self.presenter.view)
def action_copy_spectrum_values(self):
self.copy_cells(self.view)
self.copy_cells(self.presenter.view)
def action_keypress_copy(self):
self.copy_cells(self.view)
self.copy_cells(self.presenter.view)
def action_delete_row(self):
selection_model = self.view.selectionModel()
selection_model = self.presenter.view.selectionModel()
if not selection_model.hasSelection():
self.notify_no_selection_to_copy()
return
......@@ -178,10 +159,10 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
selected_rows_list = [index.row() for index in selected_rows]
selected_rows_str = ",".join([str(row) for row in selected_rows_list])
self.model.delete_rows(selected_rows_str)
self.presenter.model.delete_rows(selected_rows_str)
def _get_selected_columns(self, max_selected=None, message_if_over_max=None):
selection_model = self.view.selectionModel()
selection_model = self.presenter.view.selectionModel()
if not selection_model.hasSelection():
self.notify_no_selection_to_copy()
raise ValueError("No selection")
......@@ -191,7 +172,7 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
if max_selected and message_if_over_max and num_selected_columns > max_selected:
# if over the maximum allowed selection
self.view.show_warning(message_if_over_max)
self.presenter.view.show_warning(message_if_over_max)
raise ValueError("Too many selected")
elif num_selected_columns == 0:
# if no columns are selected
......@@ -209,7 +190,7 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
except ValueError:
return
stats = self.model.get_statistics(selected_columns)
stats = self.presenter.model.get_statistics(selected_columns)
TableWorkspaceDisplay(stats,
parent=self.parent,
name="Column Statistics of {}".format(self.name))
......@@ -220,11 +201,11 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
except ValueError:
return
for column_index in selected_columns:
self.view.hideColumn(column_index)
self.presenter.view.hideColumn(column_index)
def action_show_all_columns(self):
for column_index in range(self.view.columnCount()):
self.view.showColumn(column_index)
for column_index in range(self.presenter.view.columnCount()):
self.presenter.view.showColumn(column_index)
def _action_set_as(self, add_to_list_func, type):
try:
......@@ -234,15 +215,15 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
for col in selected_columns:
add_to_list_func(col)
self.model.set_column_type(col, type)
self.presenter.model.set_column_type(col, type)
self.update_column_headers()
self.presenter.update_column_headers()
def action_set_as_x(self):
self._action_set_as(self.model.marked_columns.add_x, 1)
self._action_set_as(self.presenter.model.marked_columns.add_x, 1)
def action_set_as_y(self):
self._action_set_as(self.model.marked_columns.add_y, 2)
self._action_set_as(self.presenter.model.marked_columns.add_y, 2)
def action_set_as_y_err(self, related_y_column):
"""
......@@ -259,20 +240,20 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
try:
err_column = ErrorColumn(selected_column, related_y_column)
except ValueError as e:
self.view.show_warning(str(e))
self.presenter.view.show_warning(str(e))
return
removed_items = self.model.marked_columns.add_y_err(err_column)
removed_items = self.presenter.model.marked_columns.add_y_err(err_column)
# if a column other than the one the user has just picked as a y err column has been affected,
# reset it's type to None
for col in removed_items:
if col != selected_column:
self.model.set_column_type(int(col), 0)
self.model.set_column_type(selected_column, 5, related_y_column)
self.presenter.model.set_column_type(int(col), 0)
self.presenter.model.set_column_type(selected_column, 5, related_y_column)
self.update_column_headers()
def action_set_as_none(self):
self._action_set_as(self.model.marked_columns.remove, 0)
self._action_set_as(self.presenter.model.marked_columns.remove, 0)
def action_sort(self, sort_ascending):
"""
......@@ -284,7 +265,7 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
except ValueError:
return
self.model.sort(selected_column, sort_ascending)
self.presenter.model.sort(selected_column, sort_ascending)
def action_plot(self, plot_type):
try:
......@@ -292,12 +273,12 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
except ValueError:
return
x_cols = list(set(selected_columns).intersection(self.model.marked_columns.as_x))
x_cols = list(set(selected_columns).intersection(self.presenter.model.marked_columns.as_x))
num_x_cols = len(x_cols)
# if there is more than 1 column marked as X in the selection
# -> show toast to the user and do nothing
if num_x_cols > 1:
self.view.show_warning(self.TOO_MANY_SELECTED_FOR_X)
self.presenter.view.show_warning(self.TOO_MANY_SELECTED_FOR_X)
return
elif num_x_cols == 1:
# Only 1 X column present in the current selection model
......@@ -306,11 +287,11 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
else:
# No X column present in the current selection model
# -> Use the first column marked as X (if present)
if len(self.model.marked_columns.as_x) == 0:
if len(self.presenter.model.marked_columns.as_x) == 0:
# If no columns are marked as X show user message and exit
self.view.show_warning(self.NO_COLUMN_MARKED_AS_X)
self.presenter.view.show_warning(self.NO_COLUMN_MARKED_AS_X)
return
selected_x = self.model.marked_columns.as_x[0]
selected_x = self.presenter.model.marked_columns.as_x[0]
try:
# Remove the X column from the selected columns, this is
......@@ -320,7 +301,7 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
pass
if len(selected_columns) == 0:
self.view.show_warning(self.CANNOT_PLOT_AGAINST_SELF_MESSAGE)
self.presenter.view.show_warning(self.CANNOT_PLOT_AGAINST_SELF_MESSAGE)
return
self._do_plot(selected_columns, selected_x, plot_type)
......@@ -330,7 +311,7 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
def _do_plot(self, selected_columns, selected_x, plot_type):
if self._is_error_plot(plot_type):
yerr = self.model.marked_columns.find_yerr(selected_columns)
yerr = self.presenter.model.marked_columns.find_yerr(selected_columns)
# remove the Y error columns if they are in the selection for plotting
# this prevents them from being treated as Y columns
for err_col in yerr.values():
......@@ -340,17 +321,17 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
# the column is not contained within the selected one
pass
if len(yerr) != len(selected_columns):
column_headers = self.model.original_column_headers()
self.view.show_warning(
column_headers = self.presenter.model.original_column_headers()
self.presenter.view.show_warning(
self.NO_ASSOCIATED_YERR_FOR_EACH_Y_MESSAGE.format(",".join(
[column_headers[col] for col in selected_columns])))
return
x = self.model.get_column(selected_x)
x = self.presenter.model.get_column(selected_x)
fig, ax = self.plot.subplots(subplot_kw={"projection": "mantid"})
fig.canvas.set_window_title(self.model.get_name())
ax.set_xlabel(self.model.get_column_header(selected_x))
ax.wsName = self.model.get_name()
fig.canvas.set_window_title(self.presenter.model.get_name())
ax.set_xlabel(self.presenter.model.get_column_header(selected_x))
ax.wsName = self.presenter.model.get_name()
plot_func = self._get_plot_function_from_type(ax, plot_type)
kwargs = {}
......@@ -358,17 +339,17 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
# if the errors are being plotted, retrieve the data for the column
if self._is_error_plot(plot_type):
yerr_column = yerr[column]
yerr_column_data = self.model.get_column(yerr_column)
yerr_column_data = self.presenter.model.get_column(yerr_column)
kwargs["yerr"] = yerr_column_data
y = self.model.get_column(column)
column_label = self.model.get_column_header(column)
y = self.presenter.model.get_column(column)
column_label = self.presenter.model.get_column_header(column)
try:
plot_func(x, y, label=self.COLUMN_DISPLAY_LABEL.format(column_label), **kwargs)
except ValueError as e:
error_message = self.PLOT_FUNCTION_ERROR_MESSAGE.format(e)
logger.error(error_message)
self.view.show_warning(error_message, self.INVALID_DATA_WINDOW_TITLE)
self.presenter.view.show_warning(error_message, self.INVALID_DATA_WINDOW_TITLE)
return
ax.set_ylabel(column_label)
......@@ -391,4 +372,4 @@ class TableWorkspaceDisplay(TableWorkspaceDataPresenter, ObservingPresenter, Dat
return plot_func
def get_columns_marked_as_y(self):
return self.model.marked_columns.as_y[:]
return self.presenter.model.marked_columns.as_y[:]
# Mantid Repository : https://github.com/mantidproject/mantid
#
# Copyright © 2021 ISIS Rutherford Appleton Laboratory UKRI,
# NScD Oak Ridge National Laboratory, European Spallation Source,
# Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
# SPDX - License - Identifier: GPL - 3.0 +
# This file is part of mantidqt package.
from abc import ABC, abstractmethod
class TableWorkspaceDataPresenterBase(ABC):
"""
Presenter to handle just displaying data from a table-like object.
Useful for other widgets wishing to embed just the table display
"""
__slots__ = ("model", "view")
def __init__(self, model=None, view=None):
"""
:param model: A reference to the model holding the table data
:param view: A reference to the view that is displayed to the user
"""
self.model = model
self.view = view
def refresh(self):
"""
Fully refresh the display. Updates column headers and reloads the data
"""
self.update_column_headers()
self.load_data(self.view)
@abstractmethod
def update_column_headers(self):
"""
Update column headers of the table
"""
pass
@abstractmethod
def load_data(self, table):
"""
Load new data into in the input table
"""
pass
# Mantid Repository : https://github.com/mantidproject/mantid
#
# Copyright © 2021 ISIS Rutherford Appleton Laboratory UKRI,
# NScD Oak Ridge National Laboratory, European Spallation Source,
# Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
# SPDX - License - Identifier: GPL - 3.0 +
# This file is part of mantidqt package.
from mantidqt.widgets.workspacedisplay.table.presenter_base import TableWorkspaceDataPresenterBase
class TableWorkspaceDataPresenterBatch(TableWorkspaceDataPresenterBase):
def __init__(self, view, model):
super().__init__(view, model)
def load_data(self, table):
table.model().load_data(self.model)
def update_column_headers(self):
"""
:param extra_labels: Extra labels to be appended to the column headers.
Expected format: [(id, label), (2, "X"),...]
:type extra_labels: List[Tuple[int, str]]
:return:
"""
# deep copy the original headers so that they are not changed by the appending of the label
column_headers = self.model.original_column_headers()
table_item_model = self.view.model()
extra_labels = self.model.build_current_labels()
if len(extra_labels) > 0:
for index, label in extra_labels:
column_headers[index] += str(label)
table_item_model.setHorizontalHeaderLabels(column_headers)
# Mantid Repository : https://github.com/mantidproject/mantid
#
# Copyright © 2021 ISIS Rutherford Appleton Laboratory UKRI,
# NScD Oak Ridge National Laboratory, European Spallation Source,
# Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
# SPDX - License - Identifier: GPL - 3.0 +
# This file is part of mantidqt package.
from mantidqt.widgets.workspacedisplay.table.presenter_base import TableWorkspaceDataPresenterBase
from mantidqt.widgets.workspacedisplay.table.tableworkspace_item import create_table_item
class TableWorkspaceDataPresenterStandard(TableWorkspaceDataPresenterBase):
def __init__(self, view, model):
super().__init__(view, model)
def load_data(self, table):
num_rows = self.model.get_number_of_rows()
data_model = table.model()
data_model.setRowCount(num_rows)
num_cols = self.model.get_number_of_columns()
data_model.setColumnCount(num_cols)
for col in range(num_cols):
column_data = self.model.get_column(col)
editable = self.model.is_editable_column(col)
for row in range(num_rows):
data_model.setItem(row, col, self.create_item(column_data[row], editable))
def update_column_headers(self):
# deep copy the original headers so that they are not changed by the appending of the label
column_headers = self.model.original_column_headers()
num_headers = len(column_headers)
data_model = self.view.model()
data_model.setColumnCount(num_headers)
extra_labels = self.model.build_current_labels()
if len(extra_labels) > 0:
for index, label in extra_labels:
column_headers[index] += str(label)
data_model.setHorizontalHeaderLabels(column_headers)
@staticmethod
def create_item(data, editable):
"""Create a QStandardItemModel for the data
:param data: The typed data to store
:param editable: True if it should be editable in the view
"""
return create_table_item(data, editable)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment