Mantid
Loading...
Searching...
No Matches
StartLiveDataDialog.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//----------------------
11#include "MantidAPI/Algorithm.h"
19#include "MantidKernel/Logger.h"
23#include "qboxlayout.h"
24#include <QStringList>
25#include <QVariant>
26
27using namespace MantidQt::CustomDialogs;
28using namespace MantidQt::API;
32using Mantid::Types::Core::DateAndTime;
33
34namespace {
35Mantid::Kernel::Logger g_log("StartLiveDataDialog");
36
37class LiveDataAlgInputHistoryImpl : public AbstractAlgorithmInputHistory {
38private:
39 LiveDataAlgInputHistoryImpl() : AbstractAlgorithmInputHistory("LiveDataAlgorithms") {}
40 ~LiveDataAlgInputHistoryImpl() override = default;
41
42private:
43 friend struct Mantid::Kernel::CreateUsingNew<LiveDataAlgInputHistoryImpl>;
44};
45
46#ifdef _WIN32
47// this breaks new namespace declaraion rules; need to find a better fix
49#endif /* _WIN32 */
52
53class LiveDataPostProcessingAlgInputHistoryImpl : public AbstractAlgorithmInputHistory {
54private:
55 LiveDataPostProcessingAlgInputHistoryImpl() : AbstractAlgorithmInputHistory("LiveDataPostProcessingAlgorithms") {}
56 ~LiveDataPostProcessingAlgInputHistoryImpl() override = default;
57
58private:
60};
61
62#ifdef _WIN32
63// this breaks new namespace declaraion rules; need to find a better fix
65#endif /* _WIN32 */
67using LiveDataPostProcessingAlgInputHistory =
69} // namespace
70
71// Add this class to the list of specialised dialogs in this namespace
74
75// Initialize static members
76const QString StartLiveDataDialog::CUSTOM_CONNECTION = "[Custom]";
77
78//----------------------
79// Public member functions
80//----------------------
83 : AlgorithmDialog(parent), m_scrollbars(this), m_useProcessAlgo(false), m_useProcessScript(false),
84 m_usePostProcessAlgo(false), m_usePostProcessScript(false) {
85 // Create the input history. This loads it too.
86 LiveDataAlgInputHistory::Instance();
87}
88
91 // Save the input history to QSettings
92 LiveDataAlgInputHistory::Instance().save();
93 LiveDataPostProcessingAlgInputHistory::Instance().save();
94}
95
98 ui.setupUi(this);
99
100 // Enable scrollbars (must happen after setupUi()!)
102
103 // To save the history of inputs
104 // RJT: I don't much like this, but at least it's safe from a lifetime point
105 // of view.
106 AbstractAlgorithmInputHistory *history1 = &LiveDataAlgInputHistory::Instance();
107 ui.processingAlgo->setInputHistory(history1);
108 AbstractAlgorithmInputHistory *history2 = &LiveDataPostProcessingAlgInputHistory::Instance();
109 ui.postAlgo->setInputHistory(history2);
110
111 // ========== Set previous values from history =============
112 // 'Instrument' no longer carries a list validator -- which instruments are valid depends on the
113 // facility, which is itself a property -- so `fillAndSetComboBox` has nothing to offer here.
114 // Populate the box from the algorithm's own view of its facility instead.
116 tie(ui.edtUpdateEvery, "UpdateEvery", ui.layoutUpdateEvery);
117 fillAndSetComboBox("AccumulationMethod", ui.cmbAccumulationMethod);
118
119 tie(ui.radNow, "FromNow");
120 tie(ui.radStartOfRun, "FromStartOfRun");
121 tie(ui.radAbsoluteTime, "FromTime");
123
124 tie(ui.chkPreserveEvents, "PreserveEvents");
126
127 tie(ui.cmbRunTransitionBehavior, "RunTransitionBehavior");
128 fillAndSetComboBox("RunTransitionBehavior", ui.cmbRunTransitionBehavior);
129
130 tie(ui.editAccumulationWorkspace, "AccumulationWorkspace", ui.gridLayout);
131 tie(ui.editOutputWorkspace, "OutputWorkspace", ui.gridLayout);
132
133 // ========== Update GUIs =============
134 ui.processingAlgo->update();
135 ui.postAlgo->update();
136
137 // ========== Layout Tweaks =============
138 ui.tabWidget->setCurrentIndex(0);
139 ui.splitterMain->setStretchFactor(0, 0);
140 ui.splitterMain->setStretchFactor(1, 1);
141
142 // ========== Set previous values for Algorithms/scripts ============
143 for (int i = 0; i < 2; i++) {
144 bool post = i > 0;
145 QString prefix = "Processing";
146
147 if (post)
148 prefix = "PostProcessing";
149 QString algo = AlgorithmInputHistory::Instance().previousInput("StartLiveData", prefix + "Algorithm");
150 QString script = AlgorithmInputHistory::Instance().previousInput("StartLiveData", prefix + "Script");
151
152 if (!post) {
153 if (!algo.isEmpty())
154 ui.radProcessAlgorithm->setChecked(true);
155 else if (!script.isEmpty())
156 ui.radProcessScript->setChecked(true);
157 else
158 ui.radProcessNone->setChecked(true);
160 // Set the script to the previous value
161 ui.processingAlgo->setScriptText(script);
162 ui.processingAlgo->setSelectedAlgorithm(algo);
164 } else {
165 if (!algo.isEmpty())
166 ui.radPostProcessAlgorithm->setChecked(true);
167 else if (!script.isEmpty())
168 ui.radPostProcessScript->setChecked(true);
169 else
170 ui.radPostProcessNone->setChecked(true);
172 // Set the script to the previous value
173 ui.postAlgo->setScriptText(script);
174 ui.postAlgo->setSelectedAlgorithm(algo);
176 }
177 }
178
179 //=========== Load Listener Class Names =============
180 // Add available listeners to combo box
181 ui.cmbConnListener->clear();
182 std::vector<std::string> listeners = Mantid::API::LiveListenerFactory::Instance().getKeys();
183 for (const auto &listener : listeners) {
184 ui.cmbConnListener->addItem(QString::fromStdString(listener));
185 }
186
187 //=========== Update UI Elements =============
189 updateUiElements(ui.cmbInstrument->currentText());
190 updateConnectionChoices(ui.cmbInstrument->currentText());
191 updateConnectionDetails(ui.cmbConnection->currentText());
192 setDefaultAccumulationMethod(ui.cmbConnListener->currentText());
193 initListenerPropLayout(ui.cmbConnListener->currentText());
194
195 //=========== SLOTS =============
196 connect(ui.processingAlgo, SIGNAL(changedAlgorithm()), this, SLOT(changeProcessingAlgorithm()));
197 connect(ui.postAlgo, SIGNAL(changedAlgorithm()), this, SLOT(changePostProcessingAlgorithm()));
198
199 connect(ui.radProcessNone, SIGNAL(toggled(bool)), this, SLOT(radioProcessClicked()));
200 connect(ui.radProcessAlgorithm, SIGNAL(toggled(bool)), this, SLOT(radioProcessClicked()));
201 connect(ui.radProcessScript, SIGNAL(toggled(bool)), this, SLOT(radioProcessClicked()));
202
203 connect(ui.radPostProcessNone, SIGNAL(toggled(bool)), this, SLOT(radioPostProcessClicked()));
204 connect(ui.radPostProcessAlgorithm, SIGNAL(toggled(bool)), this, SLOT(radioPostProcessClicked()));
205 connect(ui.radPostProcessScript, SIGNAL(toggled(bool)), this, SLOT(radioPostProcessClicked()));
206
207 connect(ui.radNow, SIGNAL(toggled(bool)), this, SLOT(radioTimeClicked()));
208 connect(ui.radStartOfRun, SIGNAL(toggled(bool)), this, SLOT(radioTimeClicked()));
209 connect(ui.radAbsoluteTime, SIGNAL(toggled(bool)), this, SLOT(radioTimeClicked()));
210
211 connect(ui.chkPreserveEvents, SIGNAL(toggled(bool)), this, SLOT(chkPreserveEventsToggled()));
212
213 connect(ui.cmbConnListener, SIGNAL(currentTextChanged(const QString &)), this,
214 SLOT(setDefaultAccumulationMethod(const QString &)));
215 connect(ui.cmbConnListener, SIGNAL(currentTextChanged(const QString &)), this,
216 SLOT(initListenerPropLayout(const QString &)));
217 connect(ui.cmbInstrument, SIGNAL(currentTextChanged(const QString &)), this, SLOT(updateUiElements(const QString &)));
218 connect(ui.cmbInstrument, SIGNAL(currentTextChanged(const QString &)), this,
219 SLOT(updateConnectionChoices(const QString &)));
220
221 connect(ui.cmbConnection, SIGNAL(currentTextChanged(const QString &)), this,
222 SLOT(updateConnectionDetails(const QString &)));
223
224 QLayout *buttonLayout = this->createDefaultButtonLayout();
225 ui.mainLayout->addLayout(buttonLayout);
226}
227
228//------------------------------------------------------------------------------
231 storePropertyValue("Instrument", ui.cmbInstrument->currentText());
232
233 // "Connection" property does not need to be set, since these override it
234 storePropertyValue("Listener", ui.cmbConnListener->currentText());
235 storePropertyValue("Address", ui.edtConnAddress->text());
236
237 storePropertyValue("AccumulationMethod", ui.cmbAccumulationMethod->currentText());
238
239 storePropertyValue("AccumulationWorkspace", ui.editAccumulationWorkspace->text());
241 storePropertyValue("AccumulationWorkspace", "");
242
243 storePropertyValue("OutputWorkspace", ui.editOutputWorkspace->text());
244
245 storePropertyValue("ProcessingAlgorithm", "");
246 storePropertyValue("ProcessingProperties", "");
247 storePropertyValue("ProcessingScript", "");
249 storePropertyValue("ProcessingAlgorithm", ui.processingAlgo->getSelectedAlgorithm());
250 std::string props;
251 props = m_processingAlg->asString(false);
252 storePropertyValue("ProcessingProperties", QString::fromStdString(props));
253 } else if (m_useProcessScript)
254 storePropertyValue("ProcessingScript", ui.processingAlgo->getScriptText());
255
256 storePropertyValue("PostProcessingAlgorithm", "");
257 storePropertyValue("PostProcessingProperties", "");
258 storePropertyValue("PostProcessingScript", "");
260 storePropertyValue("PostProcessingAlgorithm", ui.postAlgo->getSelectedAlgorithm());
261 std::string props;
262 props = m_postProcessingAlg->asString(false);
263 storePropertyValue("PostProcessingProperties", QString::fromStdString(props));
264 } else if (m_usePostProcessScript)
265 storePropertyValue("PostProcessingScript", ui.postAlgo->getScriptText());
266
267 // Save to QSettings
268 ui.processingAlgo->saveInput();
269 ui.postAlgo->saveInput();
270}
271
272//------------------------------------------------------------------------------
275 m_useProcessAlgo = ui.radProcessAlgorithm->isChecked();
276 ui.processingAlgo->algoVisible(m_useProcessAlgo);
277 m_useProcessScript = ui.radProcessScript->isChecked();
278 ui.processingAlgo->editorVisible(m_useProcessScript);
279}
280
281//------------------------------------------------------------------------------
284 m_usePostProcessAlgo = ui.radPostProcessAlgorithm->isChecked();
285 ui.postAlgo->algoVisible(m_usePostProcessAlgo);
286 m_usePostProcessScript = ui.radPostProcessScript->isChecked();
287 ui.postAlgo->editorVisible(m_usePostProcessScript);
288 // Disable the AccumulationWorkspace widget unless it is needed
289 ui.editAccumulationWorkspace->setEnabled(m_usePostProcessAlgo || m_usePostProcessScript);
290 ui.lblAccumulationWorkspace->setEnabled(m_usePostProcessAlgo || m_usePostProcessScript);
291}
292
293//------------------------------------------------------------------------------
295void StartLiveDataDialog::radioTimeClicked() { ui.dateTimeEdit->setEnabled(ui.radAbsoluteTime->isChecked()); }
296
299 ui.lblPreserveEventsWarning->setVisible(ui.chkPreserveEvents->isChecked());
300}
301
302//------------------------------------------------------------------------------
306 Algorithm_sptr alg = ui.processingAlgo->getAlgorithm();
307 if (!alg)
308 return;
309 m_processingAlg = alg;
310}
311
312//------------------------------------------------------------------------------
316 Algorithm_sptr alg = ui.postAlgo->getAlgorithm();
317 if (!alg)
318 return;
320}
321
322//------------------------------------------------------------------------------
328 if (listener.isEmpty())
329 return;
330 try {
331 // Make sure 'Add' is enabled ahead of the check (the check may throw)
332 int addIndex = ui.cmbAccumulationMethod->findText("Add");
333 ui.cmbAccumulationMethod->setItemData(addIndex, QVariant(Qt::ItemIsSelectable | Qt::ItemIsEnabled),
334 Qt::UserRole - 1);
335
336 // Check whether this listener will give back events. If not, disable 'Add'
337 // as an option
338 // The 'false' 2nd argument means don't connect the created listener
339 Mantid::Kernel::LiveListenerInfo info(listener.toStdString());
340 if (!Mantid::API::LiveListenerFactory::Instance().create(info, false)->buffersEvents()) {
341 // If 'Add' is currently selected, select 'Replace' instead
342 if (ui.cmbAccumulationMethod->currentIndex() == addIndex) {
343 int replaceIndex = ui.cmbAccumulationMethod->findText("Replace");
344 ui.cmbAccumulationMethod->setCurrentIndex(replaceIndex);
345 }
346 // Disable the 'Add' option in the combobox. It just wouldn't make sense.
347 ui.cmbAccumulationMethod->setItemData(addIndex, false, Qt::UserRole - 1);
348 }
349 }
350 // If an exception is thrown, just swallow it and do nothing
351 // getInstrument can throw, particularly while we allow listener names to be
352 // passed in directly
354 }
355}
356
357//------------------------------------------------------------------------------
364void StartLiveDataDialog::updateUiElements(const QString &inst) {
365 if (inst.isEmpty())
366 return;
367 try {
368 if (inst == "TOPAZ") {
369 ui.groupBox->setEnabled(false);
370 ui.radNow->setChecked(true);
371 } else {
372 ui.groupBox->setEnabled(true);
373 }
374 }
375 // If an exception is thrown, just swallow it and do nothing
376 // getInstrument can throw, particularly while we allow listener names to be
377 // passed in directly
379 }
380}
381
383 // Now manually set the StartTime property as there's a computation needed
384 DateAndTime startTime = DateAndTime::getCurrentTime() - ui.dateTimeEdit->value() * 60.0;
385 std::string starttime = startTime.toISO8601String();
386 // Store the value to property value map: property value can be only set from the map to m_algorithm
387 // as the last step before executing
388 QString propertyname = QString::fromStdString("StartTime");
389 QString propertyvalue = QString::fromStdString(starttime);
390 this->storePropertyValue(propertyname, propertyvalue);
391
392 // Call base class
393 AlgorithmDialog::accept(); // accept executes the algorithm
394}
395
400void StartLiveDataDialog::initListenerPropLayout(const QString &listener) {
401 // remove previous listener's properties
402 auto props = m_algorithm->getPropertiesInGroup("ListenerProperties");
403 for (const auto &prop : props) {
404 QString propName = QString::fromStdString((*prop).name());
405 if (m_algProperties.contains(propName)) {
406 m_algProperties.removeAll(propName);
407 }
408 }
409
410 // update algorithm's properties
411 if (ui.cmbInstrument->currentText().toStdString() != "") {
412 // create or clear the layout
413 QLayout *layout = ui.listenerProps->layout();
414 if (!layout) {
415 QGridLayout *listenerPropLayout = new QGridLayout(ui.listenerProps);
416 layout = listenerPropLayout;
417 } else {
418 QLayoutItem *child;
419 while ((child = layout->takeAt(0)) != nullptr) {
420 child->widget()->close();
421 delete child;
422 }
423 }
424
425 // set the instrument and listener properties early to get the listener's properties
426 // this will be overriden by the same values in parseInput() function
427 m_algorithm->setPropertyValue("Instrument", ui.cmbInstrument->currentText().toStdString());
428 m_algorithm->setPropertyValue("Listener", listener.toStdString());
429 // find the listener's properties
430 props = m_algorithm->getPropertiesInGroup("ListenerProperties");
431
432 // no properties - don't show the box
433 if (props.empty()) {
434 ui.listenerProps->setVisible(false);
435 return;
436 }
437
438 auto gridLayout = static_cast<QGridLayout *>(layout);
439 // add widgets for the listener's properties
440 for (auto prop = props.begin(); prop != props.end(); ++prop) {
441 int row = static_cast<int>(std::distance(props.begin(), prop));
442 QString propName = QString::fromStdString((**prop).name());
443 gridLayout->addWidget(new QLabel(propName), row, 0);
444 QLineEdit *propWidget = new QLineEdit();
445 gridLayout->addWidget(propWidget, row, 1);
446 if (!m_algProperties.contains(propName)) {
447 m_algProperties.append(propName);
448 }
449 tie(propWidget, propName, gridLayout);
450 }
451 ui.listenerProps->setVisible(true);
452 }
453}
454
460 ui.cmbInstrument->clear();
461
462 // The dialog offers the *default* facility's instruments; it has no facility selector of its own,
463 // so it leaves the algorithm's 'Facility' property unset. Selecting an instrument from another
464 // facility is possible from a script, by naming 'Facility'.
465 std::vector<std::string> instruments;
466 try {
467 const auto &facility = ConfigService::Instance().getFacility();
468 for (const auto &instrument : facility.instruments()) {
469 if (instrument.hasLiveListenerInfo()) {
470 instruments.emplace_back(instrument.name());
471 }
472 }
473 } catch (const std::runtime_error &e) {
474 // A default facility Mantid cannot resolve should not stop the dialog from opening.
475 g_log.warning() << "Cannot list live-data instruments: " << e.what() << "\n";
476 return;
477 }
478 std::sort(instruments.begin(), instruments.end());
479
480 for (const auto &instrument : instruments) {
481 ui.cmbInstrument->addItem(QString::fromStdString(instrument));
482 }
483
484 const auto previous = AlgorithmInputHistory::Instance().previousInput("StartLiveData", "Instrument");
485 const auto index = ui.cmbInstrument->findText(previous);
486 if (index >= 0) {
487 ui.cmbInstrument->setCurrentIndex(index);
488 }
489}
490
496void StartLiveDataDialog::updateConnectionChoices(const QString &inst_name) {
497 // Reset the connections listed
498 ui.cmbConnection->clear();
499 ui.cmbConnection->addItem(CUSTOM_CONNECTION);
500
501 // Add available LiveListenerInfo names based on selected instrument
502 const auto &inst = ConfigService::Instance().getInstrument(inst_name.toStdString());
503 for (const auto &listener : inst.liveListenerInfoList()) {
504 ui.cmbConnection->addItem(QString::fromStdString(listener.name()));
505 }
506
507 // Select default item
508 try {
509 auto selectName = QString::fromStdString(inst.liveListenerInfo().name());
510 auto index = ui.cmbConnection->findText(selectName);
511 ui.cmbConnection->setCurrentIndex(index);
512 } catch (std::runtime_error &) {
513 // the default instrument has no live listener, don't make a selection
514 }
515}
516
522void StartLiveDataDialog::updateConnectionDetails(const QString &connection) {
523 // Custom connections just enable editting connection parameters
524 if (connection == CUSTOM_CONNECTION) {
525 ui.cmbConnListener->setEnabled(true);
526 ui.edtConnAddress->setEnabled(true);
527 return;
528 }
529
530 // User shouldn't be able to edit values loaded from Facilities.xml
531 ui.cmbConnListener->setEnabled(false);
532 ui.edtConnAddress->setEnabled(false);
533
534 // Get live listener for select instrument and connection
535 const auto &inst = ConfigService::Instance().getInstrument(ui.cmbInstrument->currentText().toStdString());
536 const auto &info = inst.liveListenerInfo(connection.toStdString());
537
538 // Select correct listener
539 auto listener = QString::fromStdString(info.listener());
540 auto index = ui.cmbConnListener->findText(listener);
541 ui.cmbConnListener->setCurrentIndex(index);
542
543 // Set address text box
544 auto address = QString::fromStdString(info.address());
545 ui.edtConnAddress->setText(address);
546 ui.edtConnAddress->home(false); // display long lines from beginning, not end
547}
548} // namespace MantidQt::CustomDialogs
#define DECLARE_DIALOG(classname)
std::map< DeltaEMode::Type, std::string > index
This abstract class deals with the loading and saving of previous algorithm property values to/from M...
This class should be the basis for all customised algorithm dialogs.
void fillAndSetComboBox(const QString &propName, QComboBox *optionsBox) const
Fill a combo box for the named algorithm's allowed values.
Mantid::API::IAlgorithm_sptr m_algorithm
The algorithm associated with this dialog.
QLayout * createDefaultButtonLayout(const QString &helpText=QString("?"), const QString &loadText=QString("Run"), const QString &cancelText=QString("Close"), const QString &keepOpenText=QString("Keep Open"))
Create a row layout of buttons with specified text.
void storePropertyValue(const QString &name, const QString &value)
Adds a property (name,value) pair to the stored map.
QWidget * tie(QWidget *widget, const QString &property, QLayout *parent_layout=nullptr, bool readHistory=true)
Tie a widget to a property.
QStringList m_algProperties
The properties associated with this dialog.
void accept() override
A default slot that can be used for an OK button.
void setEnabled(bool enable)
Enable or disable scrollable behaviour.
Ui::StartLiveDataDialog ui
The form generated by Qt Designer.
void chkPreserveEventsToggled()
Slot called when the preserve events checkbox changes.
void initListenerPropLayout(const QString &listener)
Update the Listener Properties group box for the current LiveListener.
void parseInput() override
Parse the input from the dialog when it has been accepted.
Mantid::API::Algorithm_sptr m_postProcessingAlg
The algorithm for processing the accumulated workspace.
static const QString CUSTOM_CONNECTION
Constant used for custom listener connection setups.
void radioTimeClicked()
Slot called when one of the radio buttons in "starting time" are picked.
StartLiveDataDialog(QWidget *parent=nullptr)
Default Constructor.
void changePostProcessingAlgorithm()
Slot called when picking a different algorithm in the AlgorithmSelectorWidget.
void initLayout() override
Initialize the layout.
void fillInstrumentComboBox()
Fill the instrument combo box from the default facility's live-data instruments.
void updateConnectionChoices(const QString &inst_name)
Slot to update list of available connections when instrument is changed.
void radioPostProcessClicked()
Slot called when one of the radio buttons in "processing" are picked.
Mantid::API::Algorithm_sptr m_processingAlg
The algorithm for processing chunks.
void changeProcessingAlgorithm()
Slot called when picking a different algorithm in the AlgorithmSelectorWidget.
void radioProcessClicked()
Slot called when one of the radio buttons in "processing" are picked.
void updateUiElements(const QString &)
Another slot called when picking a different instrument.
void updateConnectionDetails(const QString &connection)
Slot to update connection parameters when connection selected.
API::WidgetScrollbarDecorator m_scrollbars
This dialog needs scrollbars on small screens.
void setDefaultAccumulationMethod(const QString &)
Slot called when picking a different instrument.
Exception for when an item is not found in a collection.
Definition Exception.h:145
A class that holds information about a LiveListener connection.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
Manage the lifetime of a class intended to be a singleton.
Mantid::Kernel::SingletonHolder< AlgorithmManagerImpl > AlgorithmManager
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition Algorithm.h:52
Kernel::Logger g_log("DetermineSpinStateOrder")
Mantid::Kernel::SingletonHolder< ConfigServiceImpl > ConfigService
Policy class controlling creation of the singleton Implementation classes should mark their default c...