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 algoProps = AlgorithmInputHistory::Instance().previousInput("StartLiveData", prefix + "Properties");
144 QString script = AlgorithmInputHistory::Instance().previousInput("StartLiveData", prefix + "Script");
145
146 if (!post) {
147 if (!algo.isEmpty())
148 ui.radProcessAlgorithm->setChecked(true);
149 else if (!script.isEmpty())
150 ui.radProcessScript->setChecked(true);
151 else
152 ui.radProcessNone->setChecked(true);
154 // Set the script to the previous value
155 ui.processingAlgo->setScriptText(script);
156 ui.processingAlgo->setSelectedAlgorithm(algo);
158 } else {
159 if (!algo.isEmpty())
160 ui.radPostProcessAlgorithm->setChecked(true);
161 else if (!script.isEmpty())
162 ui.radPostProcessScript->setChecked(true);
163 else
164 ui.radPostProcessNone->setChecked(true);
166 // Set the script to the previous value
167 ui.postAlgo->setScriptText(script);
168 ui.postAlgo->setSelectedAlgorithm(algo);
170 }
171 }
172
173 //=========== Load Listener Class Names =============
174 // Add available listeners to combo box
175 ui.cmbConnListener->clear();
176 std::vector<std::string> listeners = Mantid::API::LiveListenerFactory::Instance().getKeys();
177 for (const auto &listener : listeners) {
178 ui.cmbConnListener->addItem(QString::fromStdString(listener));
179 }
180
181 //=========== Update UI Elements =============
183 updateUiElements(ui.cmbInstrument->currentText());
184 updateConnectionChoices(ui.cmbInstrument->currentText());
185 updateConnectionDetails(ui.cmbConnection->currentText());
186 setDefaultAccumulationMethod(ui.cmbConnListener->currentText());
188
189 //=========== SLOTS =============
190 connect(ui.processingAlgo, SIGNAL(changedAlgorithm()), this, SLOT(changeProcessingAlgorithm()));
191 connect(ui.postAlgo, SIGNAL(changedAlgorithm()), this, SLOT(changePostProcessingAlgorithm()));
192
193 connect(ui.radProcessNone, SIGNAL(toggled(bool)), this, SLOT(radioProcessClicked()));
194 connect(ui.radProcessAlgorithm, SIGNAL(toggled(bool)), this, SLOT(radioProcessClicked()));
195 connect(ui.radProcessScript, SIGNAL(toggled(bool)), this, SLOT(radioProcessClicked()));
196
197 connect(ui.radPostProcessNone, SIGNAL(toggled(bool)), this, SLOT(radioPostProcessClicked()));
198 connect(ui.radPostProcessAlgorithm, SIGNAL(toggled(bool)), this, SLOT(radioPostProcessClicked()));
199 connect(ui.radPostProcessScript, SIGNAL(toggled(bool)), this, SLOT(radioPostProcessClicked()));
200
201 connect(ui.radNow, SIGNAL(toggled(bool)), this, SLOT(radioTimeClicked()));
202 connect(ui.radStartOfRun, SIGNAL(toggled(bool)), this, SLOT(radioTimeClicked()));
203 connect(ui.radAbsoluteTime, SIGNAL(toggled(bool)), this, SLOT(radioTimeClicked()));
204
205 connect(ui.chkPreserveEvents, SIGNAL(toggled(bool)), this, SLOT(chkPreserveEventsToggled()));
206
207 connect(ui.cmbConnListener, SIGNAL(currentIndexChanged(const QString &)), this,
208 SLOT(setDefaultAccumulationMethod(const QString &)));
209 connect(ui.cmbConnListener, SIGNAL(currentIndexChanged(const QString &)), this,
210 SLOT(initListenerPropLayout(const QString &)));
211 connect(ui.cmbInstrument, SIGNAL(currentIndexChanged(const QString &)), this,
212 SLOT(updateUiElements(const QString &)));
213 connect(ui.cmbInstrument, SIGNAL(currentIndexChanged(const QString &)), this,
214 SLOT(updateConnectionChoices(const QString &)));
215
216 connect(ui.cmbConnection, SIGNAL(currentIndexChanged(const QString &)), this,
217 SLOT(updateConnectionDetails(const QString &)));
218
219 QLayout *buttonLayout = this->createDefaultButtonLayout();
220 ui.mainLayout->addLayout(buttonLayout);
221}
222
223//------------------------------------------------------------------------------
226 storePropertyValue("Instrument", ui.cmbInstrument->currentText());
227
228 // "Connection" property does not need to be set, since these override it
229 storePropertyValue("Listener", ui.cmbConnListener->currentText());
230 storePropertyValue("Address", ui.edtConnAddress->text());
231
232 storePropertyValue("AccumulationMethod", ui.cmbAccumulationMethod->currentText());
233
234 storePropertyValue("AccumulationWorkspace", ui.editAccumulationWorkspace->text());
236 storePropertyValue("AccumulationWorkspace", "");
237
238 storePropertyValue("OutputWorkspace", ui.editOutputWorkspace->text());
239
240 storePropertyValue("ProcessingAlgorithm", "");
241 storePropertyValue("ProcessingProperties", "");
242 storePropertyValue("ProcessingScript", "");
244 storePropertyValue("ProcessingAlgorithm", ui.processingAlgo->getSelectedAlgorithm());
245 std::string props;
246 props = m_processingAlg->asString(false);
247 storePropertyValue("ProcessingProperties", QString::fromStdString(props));
248 } else if (m_useProcessScript)
249 storePropertyValue("ProcessingScript", ui.processingAlgo->getScriptText());
250
251 storePropertyValue("PostProcessingAlgorithm", "");
252 storePropertyValue("PostProcessingProperties", "");
253 storePropertyValue("PostProcessingScript", "");
255 storePropertyValue("PostProcessingAlgorithm", ui.postAlgo->getSelectedAlgorithm());
256 std::string props;
257 props = m_postProcessingAlg->asString(false);
258 storePropertyValue("PostProcessingProperties", QString::fromStdString(props));
259 } else if (m_usePostProcessScript)
260 storePropertyValue("PostProcessingScript", ui.postAlgo->getScriptText());
261
262 // Save to QSettings
263 ui.processingAlgo->saveInput();
264 ui.postAlgo->saveInput();
265}
266
267//------------------------------------------------------------------------------
270 m_useProcessAlgo = ui.radProcessAlgorithm->isChecked();
271 ui.processingAlgo->algoVisible(m_useProcessAlgo);
272 m_useProcessScript = ui.radProcessScript->isChecked();
273 ui.processingAlgo->editorVisible(m_useProcessScript);
274}
275
276//------------------------------------------------------------------------------
279 m_usePostProcessAlgo = ui.radPostProcessAlgorithm->isChecked();
280 ui.postAlgo->algoVisible(m_usePostProcessAlgo);
281 m_usePostProcessScript = ui.radPostProcessScript->isChecked();
282 ui.postAlgo->editorVisible(m_usePostProcessScript);
283 // Disable the AccumulationWorkspace widget unless it is needed
284 ui.editAccumulationWorkspace->setEnabled(m_usePostProcessAlgo || m_usePostProcessScript);
285 ui.lblAccumulationWorkspace->setEnabled(m_usePostProcessAlgo || m_usePostProcessScript);
286}
287
288//------------------------------------------------------------------------------
290void StartLiveDataDialog::radioTimeClicked() { ui.dateTimeEdit->setEnabled(ui.radAbsoluteTime->isChecked()); }
291
294 ui.lblPreserveEventsWarning->setVisible(ui.chkPreserveEvents->isChecked());
295}
296
297//------------------------------------------------------------------------------
301 Algorithm_sptr alg = ui.processingAlgo->getAlgorithm();
302 if (!alg)
303 return;
304 m_processingAlg = alg;
305}
306
307//------------------------------------------------------------------------------
311 Algorithm_sptr alg = ui.postAlgo->getAlgorithm();
312 if (!alg)
313 return;
315}
316
317//------------------------------------------------------------------------------
323 if (listener.isEmpty())
324 return;
325 try {
326 // Make sure 'Add' is enabled ahead of the check (the check may throw)
327 int addIndex = ui.cmbAccumulationMethod->findText("Add");
328 ui.cmbAccumulationMethod->setItemData(addIndex, QVariant(Qt::ItemIsSelectable | Qt::ItemIsEnabled),
329 Qt::UserRole - 1);
330
331 // Check whether this listener will give back events. If not, disable 'Add'
332 // as an option
333 // The 'false' 2nd argument means don't connect the created listener
334 Mantid::Kernel::LiveListenerInfo info(listener.toStdString());
335 if (!Mantid::API::LiveListenerFactory::Instance().create(info, false)->buffersEvents()) {
336 // If 'Add' is currently selected, select 'Replace' instead
337 if (ui.cmbAccumulationMethod->currentIndex() == addIndex) {
338 int replaceIndex = ui.cmbAccumulationMethod->findText("Replace");
339 ui.cmbAccumulationMethod->setCurrentIndex(replaceIndex);
340 }
341 // Disable the 'Add' option in the combobox. It just wouldn't make sense.
342 ui.cmbAccumulationMethod->setItemData(addIndex, false, Qt::UserRole - 1);
343 }
344 }
345 // If an exception is thrown, just swallow it and do nothing
346 // getInstrument can throw, particularly while we allow listener names to be
347 // passed in directly
349 }
350}
351
352//------------------------------------------------------------------------------
359void StartLiveDataDialog::updateUiElements(const QString &inst) {
360 if (inst.isEmpty())
361 return;
362 try {
363 if (inst == "TOPAZ") {
364 ui.groupBox->setEnabled(false);
365 ui.radNow->setChecked(true);
366 } else {
367 ui.groupBox->setEnabled(true);
368 }
369 }
370 // If an exception is thrown, just swallow it and do nothing
371 // getInstrument can throw, particularly while we allow listener names to be
372 // passed in directly
374 }
375}
376
378 // Now manually set the StartTime property as there's a computation needed
379 DateAndTime startTime = DateAndTime::getCurrentTime() - ui.dateTimeEdit->value() * 60.0;
380 std::string starttime = startTime.toISO8601String();
381 // Store the value to property value map: property value can be only set from the map to m_algorithm
382 // as the last step before executing
383 QString propertyname = QString::fromStdString("StartTime");
384 QString propertyvalue = QString::fromStdString(starttime);
385 this->storePropertyValue(propertyname, propertyvalue);
386
387 // Call base class
388 AlgorithmDialog::accept(); // accept executes the algorithm
389}
390
396 // remove previous listener's properties
397 auto props = m_algorithm->getPropertiesInGroup("ListenerProperties");
398 for (auto &prop : props) {
399 QString propName = QString::fromStdString((*prop).name());
400 if (m_algProperties.contains(propName)) {
401 m_algProperties.removeAll(propName);
402 }
403 }
404
405 // update algorithm's properties
406 if (ui.cmbInstrument->currentText().toStdString() != "") {
407 // create or clear the layout
408 QLayout *layout = ui.listenerProps->layout();
409 if (!layout) {
410 QGridLayout *listenerPropLayout = new QGridLayout(ui.listenerProps);
411 layout = listenerPropLayout;
412 } else {
413 QLayoutItem *child;
414 while ((child = layout->takeAt(0)) != nullptr) {
415 child->widget()->close();
416 delete child;
417 }
418 }
419
420 // find the listener's properties
421 props = m_algorithm->getPropertiesInGroup("ListenerProperties");
422
423 // no properties - don't show the box
424 if (props.empty()) {
425 ui.listenerProps->setVisible(false);
426 return;
427 }
428
429 auto gridLayout = static_cast<QGridLayout *>(layout);
430 // add widgets for the listener's properties
431 for (auto prop = props.begin(); prop != props.end(); ++prop) {
432 int row = static_cast<int>(std::distance(props.begin(), prop));
433 QString propName = QString::fromStdString((**prop).name());
434 gridLayout->addWidget(new QLabel(propName), row, 0);
435 QLineEdit *propWidget = new QLineEdit();
436 gridLayout->addWidget(propWidget, row, 1);
437 if (!m_algProperties.contains(propName)) {
438 m_algProperties.append(propName);
439 }
440 tie(propWidget, propName, gridLayout);
441 }
442 ui.listenerProps->setVisible(true);
443 }
444}
445
451void StartLiveDataDialog::updateConnectionChoices(const QString &inst_name) {
452 // Reset the connections listed
453 ui.cmbConnection->clear();
454 ui.cmbConnection->addItem(CUSTOM_CONNECTION);
455
456 // Add available LiveListenerInfo names based on selected instrument
457 const auto &inst = ConfigService::Instance().getInstrument(inst_name.toStdString());
458 for (const auto &listener : inst.liveListenerInfoList()) {
459 ui.cmbConnection->addItem(QString::fromStdString(listener.name()));
460 }
461
462 // Select default item
463 try {
464 auto selectName = QString::fromStdString(inst.liveListenerInfo().name());
465 auto index = ui.cmbConnection->findText(selectName);
466 ui.cmbConnection->setCurrentIndex(index);
467 } catch (std::runtime_error &) {
468 // the default instrument has no live listener, don't make a selection
469 }
470}
471
477void StartLiveDataDialog::updateConnectionDetails(const QString &connection) {
478 // Custom connections just enable editting connection parameters
479 if (connection == CUSTOM_CONNECTION) {
480 ui.cmbConnListener->setEnabled(true);
481 ui.edtConnAddress->setEnabled(true);
482 return;
483 }
484
485 // User shouldn't be able to edit values loaded from Facilities.xml
486 ui.cmbConnListener->setEnabled(false);
487 ui.edtConnAddress->setEnabled(false);
488
489 // Get live listener for select instrument and connection
490 const auto &inst = ConfigService::Instance().getInstrument(ui.cmbInstrument->currentText().toStdString());
491 const auto &info = inst.liveListenerInfo(connection.toStdString());
492
493 // Select correct listener
494 auto listener = QString::fromStdString(info.listener());
495 auto index = ui.cmbConnListener->findText(listener);
496 ui.cmbConnListener->setCurrentIndex(index);
497
498 // Set address text box
499 auto address = QString::fromStdString(info.address());
500 ui.edtConnAddress->setText(address);
501 ui.edtConnAddress->home(false); // display long lines from beginning, not end
502}
503} // namespace MantidQt::CustomDialogs
#define DECLARE_DIALOG(classname)
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
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 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 initListenerPropLayout()
Update the Listener Properties group box for the current LiveListener.
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.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition: Algorithm.h:61
Policy class controlling creation of the singleton Implementation classes should mark their default c...