Skip to content

Commit 24dcde3

Browse files
Add table view for group peak workspace (#37166)
* Add group peak workspaces model * Add show data to group peak workspaces * Add group presenter and table model * Add support for sorting, deleting, plotting, stats, and batch loading * Add group model unit tests * Add group table model unit tests * Add group presenter unit tests * Add release note
1 parent 67ac3fe commit 24dcde3

File tree

22 files changed

+1030
-33
lines changed

22 files changed

+1030
-33
lines changed

Framework/API/inc/MantidAPI/WorkspaceGroup.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,10 @@ class MANTID_API_DLL WorkspaceGroup : public Workspace {
6969
/// This method returns true if the group is empty (no member workspace)
7070
bool isEmpty() const;
7171
bool areNamesSimilar() const;
72-
/// Inidicates that the workspace group can be treated as multiperiod.
72+
/// Indicates that the workspace group can be treated as multiperiod.
7373
bool isMultiperiod() const;
74+
/// Check if the workspace group contains just peak workspaces
75+
bool isGroupPeaksWorkspaces() const;
7476
/// Check if a workspace is included in this group or any nested groups.
7577
bool isInGroup(const Workspace &workspaceToCheck, size_t level = 0) const;
7678
/// Prints the group to the screen using the logger at debug

Framework/API/src/WorkspaceGroup.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// SPDX - License - Identifier: GPL - 3.0 +
77
#include "MantidAPI/WorkspaceGroup.h"
88
#include "MantidAPI/AnalysisDataService.h"
9+
#include "MantidAPI/IPeaksWorkspace.h"
910
#include "MantidAPI/MatrixWorkspace.h"
1011
#include "MantidAPI/Run.h"
1112
#include "MantidKernel/IPropertyManager.h"
@@ -455,6 +456,14 @@ bool WorkspaceGroup::isMultiperiod() const {
455456
return true;
456457
}
457458

459+
/**
460+
* @return :: True if all of the workspaces in the group are peak workspaces
461+
*/
462+
bool WorkspaceGroup::isGroupPeaksWorkspaces() const {
463+
return std::all_of(m_workspaces.begin(), m_workspaces.end(),
464+
[](auto ws) { return dynamic_cast<IPeaksWorkspace *>(ws.get()) != nullptr; });
465+
}
466+
458467
/**
459468
* @param workspaceToCheck :: A workspace to check.
460469
* @param level :: The current nesting level. Intended for internal use only
@@ -471,7 +480,7 @@ bool WorkspaceGroup::isInGroup(const Workspace &workspaceToCheck, size_t level)
471480
for (const auto &workspace : m_workspaces) {
472481
if (workspace.get() == &workspaceToCheck)
473482
return true;
474-
auto *group = dynamic_cast<WorkspaceGroup *>(workspace.get());
483+
const auto *group = dynamic_cast<WorkspaceGroup *>(workspace.get());
475484
if (group) {
476485
if (group->isInGroup(workspaceToCheck, level + 1))
477486
return true;
@@ -503,7 +512,7 @@ namespace Mantid::Kernel {
503512
template <>
504513
MANTID_API_DLL Mantid::API::WorkspaceGroup_sptr
505514
IPropertyManager::getValue<Mantid::API::WorkspaceGroup_sptr>(const std::string &name) const {
506-
auto *prop = dynamic_cast<PropertyWithValue<Mantid::API::WorkspaceGroup_sptr> *>(getPointerToProperty(name));
515+
const auto *prop = dynamic_cast<PropertyWithValue<Mantid::API::WorkspaceGroup_sptr> *>(getPointerToProperty(name));
507516
if (prop) {
508517
return *prop;
509518
} else {
@@ -516,7 +525,7 @@ IPropertyManager::getValue<Mantid::API::WorkspaceGroup_sptr>(const std::string &
516525
template <>
517526
MANTID_API_DLL Mantid::API::WorkspaceGroup_const_sptr
518527
IPropertyManager::getValue<Mantid::API::WorkspaceGroup_const_sptr>(const std::string &name) const {
519-
auto *prop = dynamic_cast<PropertyWithValue<Mantid::API::WorkspaceGroup_sptr> *>(getPointerToProperty(name));
528+
const auto *prop = dynamic_cast<PropertyWithValue<Mantid::API::WorkspaceGroup_sptr> *>(getPointerToProperty(name));
520529
if (prop) {
521530
return prop->operator()();
522531
} else {

Framework/PythonInterface/mantid/api/src/Exports/AnalysisDataService.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
66
// SPDX - License - Identifier: GPL - 3.0 +
77
#include "MantidAPI/AnalysisDataService.h"
8+
#include "MantidAPI/IPeaksWorkspace.h"
9+
#include "MantidAPI/WorkspaceGroup.h"
810
#include "MantidKernel/WarningSuppressions.h"
911
#include "MantidPythonInterface/core/Converters/PySequenceToVector.h"
1012
#include "MantidPythonInterface/core/Converters/ToPyList.h"
@@ -67,6 +69,28 @@ list retrieveWorkspaces(AnalysisDataServiceImpl const *const self, const list &n
6769
return Converters::ToPyList<WeakPtr>()(wsWeakPtrs);
6870
}
6971

72+
list retrieveGroupPeaksWorkspaces(AnalysisDataServiceImpl const *const self, const list &names) {
73+
using WeakPtr = std::weak_ptr<Workspace>;
74+
75+
auto wsSharedPtrs = self->retrieveWorkspaces(Converters::PySequenceToVector<std::string>(names)(), false);
76+
77+
auto isNotGroupPeakWorkspace = [](const Workspace_sptr &wksp) {
78+
if (auto gws = dynamic_cast<WorkspaceGroup *>(wksp.get())) {
79+
return !gws->isGroupPeaksWorkspaces();
80+
}
81+
return true;
82+
};
83+
auto end = std::remove_if(wsSharedPtrs.begin(), wsSharedPtrs.end(), isNotGroupPeakWorkspace);
84+
wsSharedPtrs.erase(end, wsSharedPtrs.end());
85+
86+
std::vector<WeakPtr> wsWeakPtrs;
87+
wsWeakPtrs.reserve(wsSharedPtrs.size());
88+
std::transform(wsSharedPtrs.cbegin(), wsSharedPtrs.cend(), std::back_inserter(wsWeakPtrs),
89+
[](const Workspace_sptr &wksp) -> WeakPtr { return WeakPtr(wksp); });
90+
91+
return Converters::ToPyList<WeakPtr>()(wsWeakPtrs);
92+
}
93+
7094
GNU_DIAG_OFF("unused-local-typedef")
7195
// Ignore -Wconversion warnings coming from boost::python
7296
// Seen with GCC 7.1.1 and Boost 1.63.0
@@ -86,6 +110,7 @@ void export_AnalysisDataService() {
86110
.def("retrieveWorkspaces", retrieveWorkspaces,
87111
AdsRetrieveWorkspacesOverloads("Retrieve a list of workspaces by name",
88112
(arg("self"), arg("names"), arg("unrollGroups") = false)))
113+
.def("retrieveGroupPeaksWorkspaces", retrieveGroupPeaksWorkspaces, (arg("self"), arg("names")))
89114
.def("addToGroup", &AnalysisDataServiceImpl::addToGroup, (arg("groupName"), arg("wsName")),
90115
"Add a workspace in the ADS to a group in the ADS")
91116
.def("removeFromGroup", &AnalysisDataServiceImpl::removeFromGroup, (arg("groupName"), arg("wsName")),

buildconfig/CMake/CppCheck_Suppressions.txt.in

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -395,12 +395,10 @@ constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/API/src/MultiPeriodGroupWorke
395395
constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/API/src/MultiPeriodGroupWorker.cpp:261
396396
constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/API/src/MultiPeriodGroupWorker.cpp:268
397397
constVariableReference:${CMAKE_SOURCE_DIR}/Framework/Crystal/src/CalculateUMatrix.cpp:65
398-
constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/API/src/WorkspaceGroup.cpp:78
399-
constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/API/src/WorkspaceGroup.cpp:437
400-
constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/API/src/WorkspaceGroup.cpp:474
401-
constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/API/src/WorkspaceGroup.cpp:519
402-
useStlAlgorithm:${CMAKE_SOURCE_DIR}/Framework/API/src/WorkspaceGroup.cpp:125
403-
useStlAlgorithm:${CMAKE_SOURCE_DIR}/Framework/API/src/WorkspaceGroup.cpp:406
398+
constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/API/src/WorkspaceGroup.cpp:79
399+
constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/API/src/WorkspaceGroup.cpp:438
400+
useStlAlgorithm:${CMAKE_SOURCE_DIR}/Framework/API/src/WorkspaceGroup.cpp:126
401+
useStlAlgorithm:${CMAKE_SOURCE_DIR}/Framework/API/src/WorkspaceGroup.cpp:407
404402
shadowFunction:${CMAKE_SOURCE_DIR}/Framework/API/src/IFunction.cpp:263
405403
shadowFunction:${CMAKE_SOURCE_DIR}/Framework/API/src/IFunction.cpp:313
406404
shadowFunction:${CMAKE_SOURCE_DIR}/Framework/API/src/IFunction.cpp:325
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Added table view for group peaks workspaces, displaying group indices alongside standard peak data, with all the capabilities of a standard table view.

qt/applications/workbench/workbench/plugins/workspacewidget.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,19 @@ def _do_show_data(self, names):
295295
import matplotlib.pyplot
296296

297297
parent, flags = get_window_config()
298+
# Process group peak workspaces first and remove them from the list
299+
for ws in self._ads.retrieveGroupPeaksWorkspaces(names):
300+
try:
301+
TableWorkspaceDisplay.supports(ws)
302+
presenter = TableWorkspaceDisplay(ws, plot=matplotlib.pyplot, parent=parent, window_flags=flags, group=True)
303+
presenter.show_view()
304+
names.remove(ws.name())
305+
except ValueError as e:
306+
logger.error(str(e))
307+
logger.error(
308+
"Could not open workspace: {0} with neither " "MatrixWorkspaceDisplay nor TableWorkspaceDisplay." "".format(ws.name())
309+
)
310+
298311
for ws in self._ads.retrieveWorkspaces(names, unrollGroups=True):
299312
try:
300313
MatrixWorkspaceDisplay.supports(ws)

qt/python/mantidqt/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ set(PYTHON_WIDGET_QT5_ONLY_TESTS
168168
mantidqt/widgets/workspacedisplay/matrix/test/test_matrixworkspacedisplay_view.py
169169
mantidqt/widgets/workspacedisplay/matrix/test/test_matrixworkspacedisplay_io.py
170170
mantidqt/widgets/workspacedisplay/table/test/test_tableworkspacedisplay_error_column.py
171+
mantidqt/widgets/workspacedisplay/table/test/test_tableworkspacedisplay_group_model.py
172+
mantidqt/widgets/workspacedisplay/table/test/test_tableworkspacedisplay_group_table_model.py
173+
mantidqt/widgets/workspacedisplay/table/test/test_tableworkspacedisplay_group_presenter.py
171174
mantidqt/widgets/workspacedisplay/table/test/test_tableworkspacedisplay_marked_columns.py
172175
mantidqt/widgets/workspacedisplay/table/test/test_tableworkspacedisplay_tableworkspace_item.py
173176
mantidqt/widgets/workspacedisplay/table/test/test_tableworkspacedisplay_model.py

qt/python/mantidqt/mantidqt/utils/testing/mocks/mock_mantid.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class MockWorkspace:
6666
def _return_MockSpectrumInfo():
6767
return MockSpectrumInfo()
6868

69-
def __init__(self, read_return=None, axes=2, isHistogramData=True):
69+
def __init__(self, read_return=None, axes=2, isHistogramData=True, name=TEST_NAME):
7070
if read_return is None:
7171
read_return = [1, 2, 3, 4, 5]
7272
# This is assigned to a function, as the original implementation is a function that returns
@@ -96,7 +96,7 @@ def __init__(self, read_return=None, axes=2, isHistogramData=True):
9696

9797
self.setCell = StrictMock()
9898

99-
self.name = StrictMock(return_value=self.TEST_NAME)
99+
self.name = StrictMock(return_value=name)
100100

101101
self._column_names = []
102102
for i in range(self.COLS):
@@ -119,3 +119,59 @@ def __init__(self, read_return=None, axes=2, isHistogramData=True):
119119
self.getLinkedYCol = StrictMock()
120120

121121
self.hasDx = lambda x: x < len(read_return)
122+
123+
def __len__(self):
124+
return self.row_count
125+
126+
127+
class MockWorkspaceGroup:
128+
def __init__(self, name=None, workspaces=None):
129+
super().__init__()
130+
self._workspaces = workspaces if workspaces is not None else []
131+
self._name = name
132+
133+
# Override id from parent
134+
self.id = StrictMock(return_value="WorkspaceGroup")
135+
136+
# Group-specific methods
137+
self.name = StrictMock(return_value=self._name)
138+
self.toString = StrictMock(return_value="MockWorkspaceGroup")
139+
self.getMemorySize = StrictMock(return_value=1000)
140+
self.sortMembersByName = StrictMock()
141+
self.addWorkspace = StrictMock()
142+
self.getNumberOfEntries = StrictMock(return_value=len(self._workspaces))
143+
self.getItem = StrictMock()
144+
self.getAllItems = StrictMock(return_value=self._workspaces)
145+
self.removeItem = StrictMock()
146+
self.removeAll = StrictMock()
147+
self.isEmpty = StrictMock(return_value=len(self._workspaces) == 0)
148+
self.areNamesSimilar = StrictMock(return_value=True)
149+
self.isMultiperiod = StrictMock(return_value=True)
150+
self.isGroupPeaksWorkspaces = StrictMock(return_value=False)
151+
self.isInGroup = StrictMock(return_value=False)
152+
self.print = StrictMock()
153+
self.throwIndexOutOfRangeError = StrictMock()
154+
155+
# ADS-related methods
156+
self.sortByName = StrictMock()
157+
self.add = StrictMock(self._workspaces.append)
158+
self.remove = StrictMock(side_effect=lambda idx: self._workspaces.pop(idx))
159+
self.contains = StrictMock(side_effect=self._contains)
160+
self.getNames = StrictMock(return_value=[ws.name() for ws in self._workspaces])
161+
162+
def _contains(self, ws_or_name) -> bool:
163+
if isinstance(ws_or_name, str):
164+
return any(ws.name() == ws_or_name for ws in self._workspaces)
165+
return ws_or_name in self._workspaces
166+
167+
def __iter__(self):
168+
return iter(self._workspaces)
169+
170+
def __getitem__(self, index):
171+
return self._workspaces[index]
172+
173+
def append(self, value):
174+
self._workspaces.append(value)
175+
176+
def size(self):
177+
return len(self._workspaces)

qt/python/mantidqt/mantidqt/widgets/observers/ads_observer.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@ def wrapper(*args, **kwargs):
3131

3232

3333
class WorkspaceDisplayADSObserver(AnalysisDataServiceObserver):
34-
def __init__(self, presenter, observe_replace=True):
34+
def __init__(self, presenter, observe_replace=True, observe_group_update=False):
3535
super(WorkspaceDisplayADSObserver, self).__init__()
3636
self.presenter = presenter
3737

3838
self.observeClear(True)
3939
self.observeDelete(True)
4040
self.observeReplace(observe_replace)
4141
self.observeRename(True)
42+
self.observeGroupUpdate(observe_group_update)
4243

4344
@_catch_exceptions
4445
def clearHandle(self):
@@ -72,3 +73,7 @@ def replaceHandle(self, name, workspace):
7273
@_catch_exceptions
7374
def renameHandle(self, old_name, new_name):
7475
self.presenter.rename_workspace(old_name, new_name)
76+
77+
@_catch_exceptions
78+
def groupUpdateHandle(self, ws_name, workspace):
79+
self.presenter.group_update(ws_name, workspace)

qt/python/mantidqt/mantidqt/widgets/observers/observing_presenter.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,6 @@ def replace_workspace(self, workspace_name, workspace):
5656
def rename_workspace(self, old_name, new_name):
5757
if self.model.workspace_equals(old_name):
5858
self.container.emit_rename(new_name)
59+
60+
def group_update(self, ws_name, workspace):
61+
return

0 commit comments

Comments
 (0)