Mantid
Loading...
Searching...
No Matches
SaveWorkspaces.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7//----------------------
8// Includes
9//----------------------
18
19#include <QCloseEvent>
20#include <QDoubleValidator>
21#include <QFileDialog>
22#include <QGroupBox>
23#include <QHBoxLayout>
24#include <QLabel>
25#include <QMessageBox>
26#include <QPushButton>
27#include <QSettings>
28#include <QShowEvent>
29#include <QVBoxLayout>
30
31namespace {
32void setDetectorNamesOnCanSasFormat(QString &saveCommands, const QList<QListWidgetItem *> &wspaces, int j) {
33 saveCommands += ", DetectorNames=";
34 Mantid::API::Workspace_sptr workspace_ptr =
35 Mantid::API::AnalysisDataService::Instance().retrieve(wspaces[j]->text().toStdString());
36 Mantid::API::MatrixWorkspace_sptr matrix_workspace =
37 std::dynamic_pointer_cast<Mantid::API::MatrixWorkspace>(workspace_ptr);
38 if (matrix_workspace) {
39 if (matrix_workspace->getInstrument()->getName() == "SANS2D")
40 saveCommands += "'front-detector, rear-detector'";
41 if (matrix_workspace->getInstrument()->getName() == "LOQ")
42 saveCommands += "'HAB, main-detector-bank'";
43 }
44}
45} // namespace
46
47using namespace MantidQt::MantidWidgets;
48using namespace Mantid::Kernel;
49using namespace Mantid::API;
50
51//----------------------
52// Public member functions
53//----------------------
61SaveWorkspaces::SaveWorkspaces(QWidget *parent, const QString &suggFname,
62 QHash<const QCheckBox *const, QString> &defSavs, bool saveAsZeroErrorFree)
63 : API::MantidDialog(parent), m_saveAsZeroErrorFree(saveAsZeroErrorFree), m_geometryID(""), m_sampleHeight(""),
64 m_sampleWidth(""), m_sampleThickness("") {
65 setAttribute(Qt::WA_DeleteOnClose);
66 setWindowTitle("Save Workspaces");
67
68 // the form is split into 3 lines of controls in horizontal layouts
69 auto *lineOne = new QHBoxLayout;
70 setupLine1(lineOne);
71 auto *lineTwo = new QHBoxLayout;
72 setupLine2(lineTwo, defSavs);
73
74 auto *dialogLayout = new QVBoxLayout;
75 dialogLayout->addLayout(lineOne);
76 dialogLayout->addLayout(lineTwo);
77
78 setLayout(dialogLayout);
79
81 setFileName(suggFname);
82}
89void SaveWorkspaces::setupLine1(QHBoxLayout *const lineOne) {
90 auto *fNameLabel = new QLabel("Filename:");
91 m_fNameEdit = new QLineEdit();
92 auto *fNameButton = new QPushButton("Browse");
93 connect(fNameButton, SIGNAL(clicked()), this, SLOT(saveFileBrowse()));
94
95 lineOne->addWidget(fNameLabel);
96 lineOne->addWidget(m_fNameEdit);
97 lineOne->addWidget(fNameButton);
98
99 fNameLabel->setToolTip("Filename to save under");
100 m_fNameEdit->setToolTip("Filename to save under");
101 fNameButton->setToolTip("Filename to save under");
102}
108void SaveWorkspaces::setupLine2(QHBoxLayout *const lineTwo, const QHash<const QCheckBox *const, QString> &defSavs) {
109 m_workspaces = new QListWidget();
110 auto ws = AnalysisDataService::Instance().getObjectNames();
111 auto it = ws.begin(), wsEnd = ws.end();
112 for (; it != wsEnd; ++it) {
113 Workspace *wksp = FrameworkManager::Instance().getWorkspace(*it);
114 if (dynamic_cast<MatrixWorkspace *>(wksp)) { // only include matrix workspaces, not groups or tables
115 m_workspaces->addItem(QString::fromStdString(*it));
116 }
117 }
118 // allow users to select more than one workspace in the list
119 m_workspaces->setSelectionMode(QAbstractItemView::ExtendedSelection);
120 connect(m_workspaces, SIGNAL(currentRowChanged(int)), this, SLOT(setFileName(int)));
121
122 auto *save = new QPushButton("Save");
123 connect(save, SIGNAL(clicked()), this, SLOT(saveSel()));
124 auto *cancel = new QPushButton("Cancel");
125 connect(cancel, SIGNAL(clicked()), this, SLOT(close()));
126
127 auto *saveRKH = new QCheckBox("RKH (1D/2D)");
128 auto *saveNXcanSAS = new QCheckBox("NXcanSAS (1D/2D)");
129 auto *saveCan = new QCheckBox("CanSAS (1D)");
130
131 // link the save option tick boxes to their save algorithm
132 m_savFormats.insert(saveRKH, "SaveRKH");
133 m_savFormats.insert(saveNXcanSAS, "SaveNXcanSAS");
134 m_savFormats.insert(saveCan, "SaveCanSAS1D");
135 setupFormatTicks(defSavs);
136
137 m_append = new QCheckBox("Append");
138
139 // place controls into the layout, which places them on the form and takes
140 // care of deleting them
141 auto *ly_saveConts = new QVBoxLayout;
142 ly_saveConts->addWidget(save);
143 ly_saveConts->addWidget(cancel);
144 ly_saveConts->addWidget(m_append);
145 ly_saveConts->addStretch();
146
147 auto *ly_saveFormats = new QVBoxLayout;
148 ly_saveFormats->addWidget(saveRKH);
149 ly_saveFormats->addWidget(saveNXcanSAS);
150 ly_saveFormats->addWidget(saveCan);
151 auto *gb_saveForms = new QGroupBox(tr("Save Formats"));
152 gb_saveForms->setLayout(ly_saveFormats);
153 ly_saveConts->addWidget(gb_saveForms);
154
155 lineTwo->addWidget(m_workspaces);
156 lineTwo->addLayout(ly_saveConts);
157
158 m_workspaces->setToolTip("Select one or more workspaces");
159 const QString formatsTip = "Some formats support appending multiple workspaces in one file";
160 gb_saveForms->setToolTip(formatsTip);
161 save->setToolTip(formatsTip);
162 cancel->setToolTip(formatsTip);
163 saveNXcanSAS->setToolTip(formatsTip);
164 saveCan->setToolTip(formatsTip);
165 saveRKH->setToolTip(formatsTip);
166 m_append->setToolTip(formatsTip);
167}
171 QSettings prevValues;
172 prevValues.beginGroup("CustomInterfaces/SANSRunWindow/SaveWorkspaces");
173 m_lastName = prevValues.value("out_name", "").toString();
174 m_append->setChecked(prevValues.value("append", false).toBool());
175}
179void SaveWorkspaces::setFileName(const QString &newName) {
180 if ((!m_append->isChecked()) && (!newName.isEmpty())) {
181 m_fNameEdit->setText(newName);
182 m_lastName = newName;
183 } else {
184 m_fNameEdit->setText(m_lastName);
185 }
186}
192 for (SavFormatsConstIt i = m_savFormats.begin(); i != m_savFormats.end(); ++i) {
193 // find the setting that has been passed for this save format
195 for (; j != defSavs.end(); ++j) {
196 // the values are the algorithm names
197 if (j.value() == i.value()) { // copy over the checked status of the check box
198 i.key()->setChecked(j.key()->isChecked());
199 }
200 }
201 }
202}
206 QSettings prevValues;
207 prevValues.beginGroup("CustomInterfaces/SANSRunWindow/SaveWorkspaces");
208 prevValues.setValue("out_name", m_lastName);
209 prevValues.setValue("append", m_append->isChecked());
210}
215void SaveWorkspaces::closeEvent(QCloseEvent *event) {
216 saveSettings();
217 emit closing();
218 event->accept();
219}
220QString SaveWorkspaces::saveList(const QList<QListWidgetItem *> &wspaces, const QString &algorithm, QString fileBase,
221 bool toAppend, QHash<QString, QString> workspaceMap) {
222 if (wspaces.count() < 1) {
223 throw std::logic_error("");
224 }
225
226 if (toAppend && fileBase.isEmpty()) { // no file name was given, use the name
227 // of the first workspace
228 fileBase = wspaces[0]->text();
229 }
230 QString exten = getSaveAlgExt(algorithm);
231
232 QString saveCommands;
233 for (int j = 0; j < wspaces.count(); ++j) {
234 if (workspaceMap.count(wspaces[j]->text())) {
235 saveCommands += algorithm + "('" + workspaceMap[wspaces[j]->text()] + "','";
236 } else {
237 saveCommands += algorithm + "('" + wspaces[j]->text() + "','";
238 }
239
240 QString outFile = fileBase;
241 if (outFile.isEmpty()) { // if no filename was given use the workspace names
242 outFile = wspaces[j]->text();
243 } else { // we have a file name
244 if ((wspaces.count() > 1) && (!toAppend)) { // but multiple output files, number the files
245 if (outFile.endsWith(exten)) { // put the number before the extension
246 outFile = outFile.split(exten)[0];
247 }
248 outFile += "-" + QString::number(j + 1);
249 }
250 }
251
252 if (!outFile.endsWith(exten)) { // code above sometimes removes the
253 // extension and the possiblity that one
254 // just wasn't added needs dealing with too
255 outFile += exten;
256 }
257 saveCommands += outFile + "'";
258 if (algorithm != "SaveNXcanSAS") {
259 saveCommands += ", Append=";
260 saveCommands += toAppend ? "True" : "False";
261 }
262 if (algorithm == "SaveCanSAS1D") {
263 setDetectorNamesOnCanSasFormat(saveCommands, wspaces, j);
264
265 // Add the geometry information
267 // Remove the first three characters, since they are unwanted
268 saveCommands += ", Geometry='" + m_geometryID + "', SampleHeight=" + m_sampleHeight +
269 ", SampleWidth=" + m_sampleWidth + ", SampleThickness=" + m_sampleThickness;
270 }
271 if (algorithm == "SaveNXcanSAS") {
272 setDetectorNamesOnCanSasFormat(saveCommands, wspaces, j);
273 }
274 saveCommands += ")\n";
275 }
276 return saveCommands;
277}
284QString SaveWorkspaces::getSaveAlgExt(const QString &algName) {
285 IAlgorithm_sptr alg = AlgorithmManager::Instance().create(algName.toStdString());
286 Property *prop = alg->getProperty("Filename");
287 FileProperty *fProp = dynamic_cast<FileProperty *>(prop);
288 if (fProp) {
289 return QString::fromStdString(fProp->getDefaultExt());
290 } else { // the algorithm doesn't have a "Filename" file property which may
291 // indicate an error later on or maybe OK
292 return "";
293 }
294}
299 // Check if the save selection is valid
300 if (!isValid()) {
301 return;
302 }
303
304 // For each selected workspace, provide an zero-error free clone
306
307 QString saveCommands;
308 for (SavFormatsConstIt i = m_savFormats.begin(); i != m_savFormats.end(); ++i) { // the key to a pointer to the check
309 // box that the user may have clicked
310 if (i.key()->isChecked()) { // we need to save in this format
311
312 bool toAppend = m_append->isChecked();
313
314 try {
315 saveCommands += saveList(m_workspaces->selectedItems(), i.value(), m_fNameEdit->text(), toAppend, workspaceMap);
316 } catch (std::logic_error &) {
317 QMessageBox::information(this, "No workspace to save", "You must select at least one workspace to save");
318 return;
319 }
320 } // end if save in this format
321 } // end loop over formats
322
323 saveCommands += "print('success')";
324 QString status(runPythonCode(saveCommands).trimmed());
325
327 removeZeroFreeWorkspaces(workspaceMap);
328 }
329
330 if (status != "success") {
331 QMessageBox::critical(this, "Error saving workspace",
332 "One of the workspaces could not be saved in one of "
333 "the selected formats");
334 }
335}
336
344 // Get the dimensionality of the workspaces
345 auto is2D = false;
346 auto workspacesList = m_workspaces->selectedItems();
347 for (auto &it : workspacesList) {
348 auto wsName = it->text();
349 auto workspace = AnalysisDataService::Instance().retrieveWS<Mantid::API::MatrixWorkspace>(wsName.toStdString());
350 if (workspace->getNumberHistograms() != 1) {
351 is2D = true;
352 }
353 }
354
355 // Check if CanSAS was selected
356 auto isCanSAS = false;
357 for (SavFormatsConstIt i = m_savFormats.begin(); i != m_savFormats.end(); ++i) { // the key to a pointer to the check
358 // box that the user may have clicked
359 if (i.key()->isChecked()) { // we need to save in this format
360 if (i.value() == "SaveCanSAS1D") {
361 isCanSAS = true;
362 }
363 }
364 }
365
366 // Check for errors
367 QString message;
368 auto isValidOption = true;
369
370 if (is2D && isCanSAS) {
371 isValidOption = false;
372 message += "Save option issue: Cannot save in CanSAS format for 2D data.\n";
373 }
374
375 // Print the error message if there are any
376 if (!message.isEmpty()) {
377 QString warning = "Please correct these save settings before proceeding:\n";
378 warning += message;
379 QMessageBox::warning(this, "Inconsistent input", warning);
380 }
381
382 return isValidOption;
383}
384
388void SaveWorkspaces::setFileName(int row) { setFileName(m_workspaces->item(row)->text()); }
393 QString title = "Save output workspace as";
394
395 QSettings prevValues;
396 prevValues.beginGroup("CustomInterfaces/SANSRunWindow/SaveWorkspaces");
397 // use their previous directory first and go to their default if that fails
398 QString prevPath =
399 prevValues.value("dir", QString::fromStdString(ConfigService::Instance().getString("defaultsave.directory")))
400 .toString();
401
402 QString filter = ";;AllFiles (*)";
403 QFileDialog::Option userCon =
404 m_append->isChecked() ? QFileDialog::DontConfirmOverwrite : static_cast<QFileDialog::Option>(0);
405 QString oFile = QFileDialog::getSaveFileName(this, title, prevPath, filter, nullptr, userCon);
406
407 if (!oFile.isEmpty()) {
408 m_fNameEdit->setText(oFile);
409
410 QString directory = QFileInfo(oFile).path();
411 prevValues.setValue("dir", directory);
412 }
413}
414
425 auto wsList = workspaces->selectedItems();
426 QHash<QString, QString> workspaceMap;
427 for (auto &it : wsList) {
428 auto wsName = it->text();
429 auto cloneName = wsName;
431 cloneName += "_clone_temp";
432 emit createZeroErrorFreeWorkspace(wsName, cloneName);
433 }
434
435 if (AnalysisDataService::Instance().doesExist(cloneName.toStdString())) {
436 workspaceMap.insert(wsName, cloneName);
437 }
438 }
439
440 return workspaceMap;
441}
442
449 auto zeroFreeWorkspaceNames = workspaces.values();
450 for (auto &zeroFreeWorkspaceName : zeroFreeWorkspaceNames) {
451 emit deleteZeroErrorFreeWorkspace(zeroFreeWorkspaceName);
452 }
453}
454
462 if (state == 0) {
463 m_saveAsZeroErrorFree = false;
464 } else {
466 }
467}
468
472void SaveWorkspaces::onUpdateGeomtryInformation(QString &geometryID, QString &sampleHeight, QString &sampleWidth,
473 QString &sampleThickness) {
474 m_geometryID = geometryID;
475 m_sampleHeight = sampleHeight;
476 m_sampleWidth = sampleWidth;
477 m_sampleThickness = sampleThickness;
478}
IPeaksWorkspace_sptr workspace
Definition: IndexPeaks.cpp:114
QString runPythonCode(const QString &code, bool no_output=false)
Run python code that is passed to it and, optionally, return anything it wrote to standard output as ...
void setFileName(int row)
Sets the filename to the name of the selected workspace.
void initLayout()
Set up the dialog layout.
void removeZeroFreeWorkspaces(const QHash< QString, QString > &workspaces)
Remove all the zero-error free workspaces.
QHash< QCheckBox *const, QString >::const_iterator SavFormatsConstIt
void onUpdateGeomtryInformation(QString &geometryID, QString &sampleHeight, QString &sampleWidth, QString &sampleThickness)
Recieves an update for the geometry information.
SaveWorkspaces(QWidget *parent, const QString &suggFname, QHash< const QCheckBox *const, QString > &defSavs, bool saveAsZeroErrorFree)
void setupLine2(QHBoxLayout *const lineTwo, const QHash< const QCheckBox *const, QString > &defSavs)
Puts the controls that go on the second line, the workspace list and save commands,...
bool isValid()
Checks if the save option selection is compatible with the dimensionality selection.
void deleteZeroErrorFreeWorkspace(QString &zeroFreeWorkspace)
void createZeroErrorFreeWorkspace(QString &originalWorkspace, QString &zeroFreeWorkspace)
void closeEvent(QCloseEvent *event) override
Called in response to a close event.
QHash< QCheckBox *const, QString > m_savFormats
void readSettings()
Sets up some controls from what is in the QSettings.
void saveSel()
Excutes the selected save algorithms on the workspaces that have been selected to be saved.
void saveFileBrowse()
Raises a browse dialog and inserts the selected file into the save text edit box, outfile_edit.
static QString getSaveAlgExt(const QString &algName)
Returns the save extension expected the name algorithm.
QString saveList(const QList< QListWidgetItem * > &list, const QString &algorithm, QString fileBase, bool toAppend, QHash< QString, QString > workspaceMap)
void saveSettings() const
Saves the state of some controls to the QSettings.
QHash< QString, QString > provideZeroFreeWorkspaces(const QListWidget *workspaces)
Goes through all selected workspaces and maps them to a zero-error free clone, if the user has select...
void onSaveAsZeroErrorFreeChanged(int state)
Reacts to a user change wether the workspace is to be saved as zero-error-free or not.
void setupLine1(QHBoxLayout *const lineOne)
Puts the controls that go on the first line, the output filename commands, on to the layout that's pa...
void setupFormatTicks(const QHash< const QCheckBox *const, QString > &defSavs)
For each save format tick box take the user setting from the main form.
A specialized class for dealing with file properties.
Definition: FileProperty.h:42
std::string getDefaultExt() const
Returns the main file extension that's used.
Definition: FileProperty.h:90
Base MatrixWorkspace Abstract Class.
Base Workspace Abstract Class.
Definition: Workspace.h:30
Base class for properties.
Definition: Property.h:94
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Definition: Workspace_fwd.h:20
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class