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"
21#include "qboxlayout.h"
22#include <QStringList>
23#include <QVariant>
24
25using namespace MantidQt::CustomDialogs;
26using namespace MantidQt::API;
30using Mantid::Types::Core::DateAndTime;
31
32namespace {
33class LiveDataAlgInputHistoryImpl : public AbstractAlgorithmInputHistory {
34private:
35 LiveDataAlgInputHistoryImpl() : AbstractAlgorithmInputHistory("LiveDataAlgorithms") {}
36 ~LiveDataAlgInputHistoryImpl() override = default;
37
38private:
39 friend struct Mantid::Kernel::CreateUsingNew<LiveDataAlgInputHistoryImpl>;
40};
41
42#ifdef _WIN32
43// this breaks new namespace declaraion rules; need to find a better fix
45#endif /* _WIN32 */
48
49class LiveDataPostProcessingAlgInputHistoryImpl : public AbstractAlgorithmInputHistory {
50private:
51 LiveDataPostProcessingAlgInputHistoryImpl() : AbstractAlgorithmInputHistory("LiveDataPostProcessingAlgorithms") {}
52 ~LiveDataPostProcessingAlgInputHistoryImpl() override = default;
53
54private:
56};
57
58#ifdef _WIN32
59// this breaks new namespace declaraion rules; need to find a better fix
61#endif /* _WIN32 */
63using LiveDataPostProcessingAlgInputHistory =
65} // namespace
66
67// Add this class to the list of specialised dialogs in this namespace
70
71// Initialize static members
72const QString StartLiveDataDialog::CUSTOM_CONNECTION = "[Custom]";
73
74//----------------------
75// Public member functions
76//----------------------
79 : AlgorithmDialog(parent), m_scrollbars(this), m_useProcessAlgo(false), m_useProcessScript(false),
80 m_usePostProcessAlgo(false), m_usePostProcessScript(false) {
81 // Create the input history. This loads it too.
82 LiveDataAlgInputHistory::Instance();
83}
84
87 // Save the input history to QSettings
88 LiveDataAlgInputHistory::Instance().save();
89 LiveDataPostProcessingAlgInputHistory::Instance().save();
90}
91
94 ui.setupUi(this);
95
96 // Enable scrollbars (must happen after setupUi()!)
98
99 // To save the history of inputs
100 // RJT: I don't much like this, but at least it's safe from a lifetime point
101 // of view.
102 AbstractAlgorithmInputHistory *history1 = &LiveDataAlgInputHistory::Instance();
103 ui.processingAlgo->setInputHistory(history1);
104 AbstractAlgorithmInputHistory *history2 = &LiveDataPostProcessingAlgInputHistory::Instance();
105 ui.postAlgo->setInputHistory(history2);
106
107 // ========== Set previous values from history =============
108 fillAndSetComboBox("Instrument", ui.cmbInstrument);
109 tie(ui.edtUpdateEvery, "UpdateEvery", ui.layoutUpdateEvery);
110 fillAndSetComboBox("AccumulationMethod", ui.cmbAccumulationMethod);
111
112 tie(ui.radNow, "FromNow");
113 tie(ui.radStartOfRun, "FromStartOfRun");
114 tie(ui.radAbsoluteTime, "FromTime");
116
117 tie(ui.chkPreserveEvents, "PreserveEvents");
119
120 tie(ui.cmbRunTransitionBehavior, "RunTransitionBehavior");
121 fillAndSetComboBox("RunTransitionBehavior", ui.cmbRunTransitionBehavior);
122
123 tie(ui.editAccumulationWorkspace, "AccumulationWorkspace", ui.gridLayout);
124 tie(ui.editOutputWorkspace, "OutputWorkspace", ui.gridLayout);
125
126 // ========== Update GUIs =============
127 ui.processingAlgo->update();
128 ui.postAlgo->update();
129
130 // ========== Layout Tweaks =============
131 ui.tabWidget->setCurrentIndex(0);
132 ui.splitterMain->setStretchFactor(0, 0);
133 ui.splitterMain->setStretchFactor(1, 1);
134
135 // ========== Set previous values for Algorithms/scripts ============
136 for (int i = 0; i < 2; i++) {
137 bool post = i > 0;
138 QString prefix = "Processing";
139
140 if (post)
141 prefix = "PostProcessing";
142 QString algo = AlgorithmInputHistory::Instance().previousInput("StartLiveData", prefix + "Algorithm");
143 QString script = AlgorithmInputHistory::Instance().previousInput("StartLiveData", prefix + "Script");
144
145 if (!post) {
146 if (!algo.isEmpty())
147 ui.radProcessAlgorithm->setChecked(true);
148 else if (!script.isEmpty())
149 ui.radProcessScript->setChecked(true);
150 else
151 ui.radProcessNone->setChecked(true);
153 // Set the script to the previous value
154 ui.processingAlgo->setScriptText(script);
155 ui.processingAlgo->setSelectedAlgorithm(algo);
157 } else {
158 if (!algo.isEmpty())
159 ui.radPostProcessAlgorithm->setChecked(true);
160 else if (!script.isEmpty())
161 ui.radPostProcessScript->setChecked(true);
162 else
163 ui.radPostProcessNone->setChecked(true);
165 // Set the script to the previous value
166 ui.postAlgo->setScriptText(script);
167 ui.postAlgo->setSelectedAlgorithm(algo);
169 }
170 }
171
172 //=========== Load Listener Class Names =============
173 // Add available listeners to combo box
174 ui.cmbConnListener->clear();
175 std::vector<std::string> listeners = Mantid::API::LiveListenerFactory::Instance().getKeys();
176 for (const auto &listener : listeners) {
177 ui.cmbConnListener->addItem(QString::fromStdString(listener));
178 }
179
180 //=========== Update UI Elements =============
182 updateUiElements(ui.cmbInstrument->currentText());
183 updateConnectionChoices(ui.cmbInstrument->currentText());
184 updateConnectionDetails(ui.cmbConnection->currentText());
185 setDefaultAccumulationMethod(ui.cmbConnListener->currentText());
186 initListenerPropLayout(ui.cmbConnListener->currentText());
187
188 //=========== SLOTS =============
189 connect(ui.processingAlgo, SIGNAL(changedAlgorithm()), this, SLOT(changeProcessingAlgorithm()));
190 connect(ui.postAlgo, SIGNAL(changedAlgorithm()), this, SLOT(changePostProcessingAlgorithm()));
191
192 connect(ui.radProcessNone, SIGNAL(toggled(bool)), this, SLOT(radioProcessClicked()));
193 connect(ui.radProcessAlgorithm, SIGNAL(toggled(bool)), this, SLOT(radioProcessClicked()));
194 connect(ui.radProcessScript, SIGNAL(toggled(bool)), this, SLOT(radioProcessClicked()));
195
196 connect(ui.radPostProcessNone, SIGNAL(toggled(bool)), this, SLOT(radioPostProcessClicked()));
197 connect(ui.radPostProcessAlgorithm, SIGNAL(toggled(bool)), this, SLOT(radioPostProcessClicked()));
198 connect(ui.radPostProcessScript, SIGNAL(toggled(bool)), this, SLOT(radioPostProcessClicked()));
199
200 connect(ui.radNow, SIGNAL(toggled(bool)), this, SLOT(radioTimeClicked()));
201 connect(ui.radStartOfRun, SIGNAL(toggled(bool)), this, SLOT(radioTimeClicked()));
202 connect(ui.radAbsoluteTime, SIGNAL(toggled(bool)), this, SLOT(radioTimeClicked()));
203
204 connect(ui.chkPreserveEvents, SIGNAL(toggled(bool)), this, SLOT(chkPreserveEventsToggled()));
205
206 connect(ui.cmbConnListener, SIGNAL(currentIndexChanged(const QString &)), this,
207 SLOT(setDefaultAccumulationMethod(const QString &)));
208 connect(ui.cmbConnListener, SIGNAL(currentIndexChanged(const QString &)), this,
209 SLOT(initListenerPropLayout(const QString &)));
210 connect(ui.cmbInstrument, SIGNAL(currentIndexChanged(const QString &)), this,
211 SLOT(updateUiElements(const QString &)));
212 connect(ui.cmbInstrument, SIGNAL(currentIndexChanged(const QString &)), this,
213 SLOT(updateConnectionChoices(const QString &)));
214
215 connect(ui.cmbConnection, SIGNAL(currentIndexChanged(const QString &)), this,
216 SLOT(updateConnectionDetails(const QString &)));
217
218 QLayout *buttonLayout = this->createDefaultButtonLayout();
219 ui.mainLayout->addLayout(buttonLayout);
220}
221
222//------------------------------------------------------------------------------
225 storePropertyValue("Instrument", ui.cmbInstrument->currentText());
226
227 // "Connection" property does not need to be set, since these override it
228 storePropertyValue("Listener", ui.cmbConnListener->currentText());
229 storePropertyValue("Address", ui.edtConnAddress->text());
230
231 storePropertyValue("AccumulationMethod", ui.cmbAccumulationMethod->currentText());
232
233 storePropertyValue("AccumulationWorkspace", ui.editAccumulationWorkspace->text());
235 storePropertyValue("AccumulationWorkspace", "");
236
237 storePropertyValue("OutputWorkspace", ui.editOutputWorkspace->text());
238
239 storePropertyValue("ProcessingAlgorithm", "");
240 storePropertyValue("ProcessingProperties", "");
241 storePropertyValue("ProcessingScript", "");
243 storePropertyValue("ProcessingAlgorithm", ui.processingAlgo->getSelectedAlgorithm());
244 std::string props;
245 props = m_processingAlg->asString(false);
246 storePropertyValue("ProcessingProperties", QString::fromStdString(props));
247 } else if (m_useProcessScript)
248 storePropertyValue("ProcessingScript", ui.processingAlgo->getScriptText());
249
250 storePropertyValue("PostProcessingAlgorithm", "");
251 storePropertyValue("PostProcessingProperties", "");
252 storePropertyValue("PostProcessingScript", "");
254 storePropertyValue("PostProcessingAlgorithm", ui.postAlgo->getSelectedAlgorithm());
255 std::string props;
256 props = m_postProcessingAlg->asString(false);
257 storePropertyValue("PostProcessingProperties", QString::fromStdString(props));
258 } else if (m_usePostProcessScript)
259 storePropertyValue("PostProcessingScript", ui.postAlgo->getScriptText());
260
261 // Save to QSettings
262 ui.processingAlgo->saveInput();
263 ui.postAlgo->saveInput();
264}
265
266//------------------------------------------------------------------------------
269 m_useProcessAlgo = ui.radProcessAlgorithm->isChecked();
270 ui.processingAlgo->algoVisible(m_useProcessAlgo);
271 m_useProcessScript = ui.radProcessScript->isChecked();
272 ui.processingAlgo->editorVisible(m_useProcessScript);
273}
274
275//------------------------------------------------------------------------------
278 m_usePostProcessAlgo = ui.radPostProcessAlgorithm->isChecked();
279 ui.postAlgo->algoVisible(m_usePostProcessAlgo);
280 m_usePostProcessScript = ui.radPostProcessScript->isChecked();
281 ui.postAlgo->editorVisible(m_usePostProcessScript);
282 // Disable the AccumulationWorkspace widget unless it is needed
283 ui.editAccumulationWorkspace->setEnabled(m_usePostProcessAlgo || m_usePostProcessScript);
284 ui.lblAccumulationWorkspace->setEnabled(m_usePostProcessAlgo || m_usePostProcessScript);
285}
286
287//------------------------------------------------------------------------------
289void StartLiveDataDialog::radioTimeClicked() { ui.dateTimeEdit->setEnabled(ui.radAbsoluteTime->isChecked()); }
290
293 ui.lblPreserveEventsWarning->setVisible(ui.chkPreserveEvents->isChecked());
294}
295
296//------------------------------------------------------------------------------
300 Algorithm_sptr alg = ui.processingAlgo->getAlgorithm();
301 if (!alg)
302 return;
303 m_processingAlg = alg;
304}
305
306//------------------------------------------------------------------------------
310 Algorithm_sptr alg = ui.postAlgo->getAlgorithm();
311 if (!alg)
312 return;
314}
315
316//------------------------------------------------------------------------------
322 if (listener.isEmpty())
323 return;
324 try {
325 // Make sure 'Add' is enabled ahead of the check (the check may throw)
326 int addIndex = ui.cmbAccumulationMethod->findText("Add");
327 ui.cmbAccumulationMethod->setItemData(addIndex, QVariant(Qt::ItemIsSelectable | Qt::ItemIsEnabled),
328 Qt::UserRole - 1);
329
330 // Check whether this listener will give back events. If not, disable 'Add'
331 // as an option
332 // The 'false' 2nd argument means don't connect the created listener
333 Mantid::Kernel::LiveListenerInfo info(listener.toStdString());
334 if (!Mantid::API::LiveListenerFactory::Instance().create(info, false)->buffersEvents()) {
335 // If 'Add' is currently selected, select 'Replace' instead
336 if (ui.cmbAccumulationMethod->currentIndex() == addIndex) {
337 int replaceIndex = ui.cmbAccumulationMethod->findText("Replace");
338 ui.cmbAccumulationMethod->setCurrentIndex(replaceIndex);
339 }
340 // Disable the 'Add' option in the combobox. It just wouldn't make sense.
341 ui.cmbAccumulationMethod->setItemData(addIndex, false, Qt::UserRole - 1);
342 }
343 }
344 // If an exception is thrown, just swallow it and do nothing
345 // getInstrument can throw, particularly while we allow listener names to be
346 // passed in directly
348 }
349}
350
351//------------------------------------------------------------------------------
358void StartLiveDataDialog::updateUiElements(const QString &inst) {
359 if (inst.isEmpty())
360 return;
361 try {
362 if (inst == "TOPAZ") {
363 ui.groupBox->setEnabled(false);
364 ui.radNow->setChecked(true);
365 } else {
366 ui.groupBox->setEnabled(true);
367 }
368 }
369 // If an exception is thrown, just swallow it and do nothing
370 // getInstrument can throw, particularly while we allow listener names to be
371 // passed in directly
373 }
374}
375
377 // Now manually set the StartTime property as there's a computation needed
378 DateAndTime startTime = DateAndTime::getCurrentTime() - ui.dateTimeEdit->value() * 60.0;
379 std::string starttime = startTime.toISO8601String();
380 // Store the value to property value map: property value can be only set from the map to m_algorithm
381 // as the last step before executing
382 QString propertyname = QString::fromStdString("StartTime");
383 QString propertyvalue = QString::fromStdString(starttime);
384 this->storePropertyValue(propertyname, propertyvalue);
385
386 // Call base class
387 AlgorithmDialog::accept(); // accept executes the algorithm
388}
389
394void StartLiveDataDialog::initListenerPropLayout(const QString &listener) {
395 // remove previous listener's properties
396 auto props = m_algorithm->getPropertiesInGroup("ListenerProperties");
397 for (const auto &prop : props) {
398 QString propName = QString::fromStdString((*prop).name());
399 if (m_algProperties.contains(propName)) {
400 m_algProperties.removeAll(propName);
401 }
402 }
403
404 // update algorithm's properties
405 if (ui.cmbInstrument->currentText().toStdString() != "") {
406 // create or clear the layout
407 QLayout *layout = ui.listenerProps->layout();
408 if (!layout) {
409 QGridLayout *listenerPropLayout = new QGridLayout(ui.listenerProps);
410 layout = listenerPropLayout;
411 } else {
412 QLayoutItem *child;
413 while ((child = layout->takeAt(0)) != nullptr) {
414 child->widget()->close();
415 delete child;
416 }
417 }
418
419 // set the instrument and listener properties early to get the listener's properties
420 // this will be overriden by the same values in parseInput() function
421 m_algorithm->setPropertyValue("Instrument", ui.cmbInstrument->currentText().toStdString());
422 m_algorithm->setPropertyValue("Listener", listener.toStdString());
423 // find the listener's properties
424 props = m_algorithm->getPropertiesInGroup("ListenerProperties");
425
426 // no properties - don't show the box
427 if (props.empty()) {
428 ui.listenerProps->setVisible(false);
429 return;
430 }
431
432 auto gridLayout = static_cast<QGridLayout *>(layout);
433 // add widgets for the listener's properties
434 for (auto prop = props.begin(); prop != props.end(); ++prop) {
435 int row = static_cast<int>(std::distance(props.begin(), prop));
436 QString propName = QString::fromStdString((**prop).name());
437 gridLayout->addWidget(new QLabel(propName), row, 0);
438 QLineEdit *propWidget = new QLineEdit();
439 gridLayout->addWidget(propWidget, row, 1);
440 if (!m_algProperties.contains(propName)) {
441 m_algProperties.append(propName);
442 }
443 tie(propWidget, propName, gridLayout);
444 }
445 ui.listenerProps->setVisible(true);
446 }
447}
448
454void StartLiveDataDialog::updateConnectionChoices(const QString &inst_name) {
455 // Reset the connections listed
456 ui.cmbConnection->clear();
457 ui.cmbConnection->addItem(CUSTOM_CONNECTION);
458
459 // Add available LiveListenerInfo names based on selected instrument
460 const auto &inst = ConfigService::Instance().getInstrument(inst_name.toStdString());
461 for (const auto &listener : inst.liveListenerInfoList()) {
462 ui.cmbConnection->addItem(QString::fromStdString(listener.name()));
463 }
464
465 // Select default item
466 try {
467 auto selectName = QString::fromStdString(inst.liveListenerInfo().name());
468 auto index = ui.cmbConnection->findText(selectName);
469 ui.cmbConnection->setCurrentIndex(index);
470 } catch (std::runtime_error &) {
471 // the default instrument has no live listener, don't make a selection
472 }
473}
474
480void StartLiveDataDialog::updateConnectionDetails(const QString &connection) {
481 // Custom connections just enable editting connection parameters
482 if (connection == CUSTOM_CONNECTION) {
483 ui.cmbConnListener->setEnabled(true);
484 ui.edtConnAddress->setEnabled(true);
485 return;
486 }
487
488 // User shouldn't be able to edit values loaded from Facilities.xml
489 ui.cmbConnListener->setEnabled(false);
490 ui.edtConnAddress->setEnabled(false);
491
492 // Get live listener for select instrument and connection
493 const auto &inst = ConfigService::Instance().getInstrument(ui.cmbInstrument->currentText().toStdString());
494 const auto &info = inst.liveListenerInfo(connection.toStdString());
495
496 // Select correct listener
497 auto listener = QString::fromStdString(info.listener());
498 auto index = ui.cmbConnListener->findText(listener);
499 ui.cmbConnListener->setCurrentIndex(index);
500
501 // Set address text box
502 auto address = QString::fromStdString(info.address());
503 ui.edtConnAddress->setText(address);
504 ui.edtConnAddress->home(false); // display long lines from beginning, not end
505}
506} // 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 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.
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
Mantid::Kernel::SingletonHolder< ConfigServiceImpl > ConfigService
Policy class controlling creation of the singleton Implementation classes should mark their default c...