Mantid
Loading...
Searching...
No Matches
MantidWSIndexDialog.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 +
9#include "MantidAPI/Axis.h"
10#include "MantidAPI/Run.h"
14
15#include <QMessageBox>
16#include <QPalette>
17#include <QPushButton>
18#include <QRegExp>
19#include <QtAlgorithms>
20
21#include <algorithm>
22#include <boost/lexical_cast.hpp>
23#include <cstdlib>
24#include <exception>
25#include <numeric>
26#include <utility>
27
30const QString MantidWSIndexWidget::WORKSPACE_NAME = "Workspace name";
31const QString MantidWSIndexWidget::WORKSPACE_INDEX = "Workspace index";
32
34const QString MantidWSIndexWidget::CUSTOM = "Custom";
35
36// String for plot types
37const QString MantidWSIndexWidget::SIMPLE_PLOT = "1D Plot";
38const QString MantidWSIndexWidget::WATERFALL_PLOT = "Waterfall Plot";
39const QString MantidWSIndexWidget::SURFACE_PLOT = "Surface Plot";
40const QString MantidWSIndexWidget::CONTOUR_PLOT = "Contour Plot";
41
42//----------------------------------
43// MantidWSIndexWidget methods
44//----------------------------------
54MantidWSIndexWidget::MantidWSIndexWidget(QWidget *parent, const Qt::WindowFlags &flags, const QList<QString> &wsNames,
55 const bool showWaterfallOption, const bool showTiledOption,
56 const bool isAdvanced)
57 : QWidget(parent, flags), m_spectra(false), m_waterfall(showWaterfallOption), m_tiled(showTiledOption),
58 m_advanced(isAdvanced), m_plotOptions(), m_wsNames(wsNames), m_wsIndexIntervals(), m_spectraNumIntervals(),
59 m_wsIndexChoice(), m_spectraNumChoice() {
61 // Generate the intervals allowed to be plotted by the user.
63 if (m_spectra) {
65 }
66 init();
67}
68
74 UserInput options = UserInput();
75 options.plots = getPlots();
76 options.simple = is1DPlotSelected();
78 options.tiled = isTiledPlotSelected();
79 if (m_advanced) {
81 options.errors = isErrorBarsSelected();
83 } else {
84 options.surface = false;
85 options.errors = false;
86 options.contour = false;
87 }
88
89 // Advanced options
90 if (m_advanced && (options.simple || options.waterfall || options.surface || options.contour)) {
91 UserInputAdvanced userInputAdvanced = UserInputAdvanced();
92 if (options.surface || options.contour) {
93 userInputAdvanced.accepted = true;
94 userInputAdvanced.plotIndex = getPlotIndex();
95 userInputAdvanced.axisName = getAxisName();
96 }
97 userInputAdvanced.logName = getLogName();
98 if (userInputAdvanced.logName == WORKSPACE_NAME || userInputAdvanced.logName == WORKSPACE_INDEX) {
99 // We want default names in legend, if log is workspace name or index
100 userInputAdvanced.logName = "";
101 }
102 userInputAdvanced.workspaceNames = m_wsNames;
103 if (userInputAdvanced.logName == CUSTOM) {
104 userInputAdvanced.customLogValues = getCustomLogValues();
105 if (userInputAdvanced.customLogValues.empty()) {
106 userInputAdvanced.accepted = false;
107 }
108 }
109 options.isAdvanced = true;
110 options.advanced = std::move(userInputAdvanced);
111 } else {
112 options.isAdvanced = false; // We don't want the view to look at options.advanced.
113 }
114 return options;
115}
116
122 int spectrumIndex = 0; // default to 0
123 const auto userInput = getPlots();
124
125 if (!userInput.empty()) {
126 const auto indexList = userInput.values();
127 if (!indexList.empty()) {
128 const auto &spectrumIndexes = indexList.at(0);
129 if (!spectrumIndexes.empty()) {
130 spectrumIndex = *spectrumIndexes.begin();
131 }
132 }
133 }
134 return spectrumIndex;
135}
136
141void MantidWSIndexWidget::showPlotOptionsError(const QString &message) {
142 if (!message.isEmpty()) {
143 QMessageBox errorMessage;
144 errorMessage.setText(message);
145 errorMessage.setIcon(QMessageBox::Critical);
146 errorMessage.exec();
147 }
148}
149
157const std::set<double> MantidWSIndexWidget::getCustomLogValues() const {
158 std::set<double> logValues;
159 if (m_logSelector->currentText() == CUSTOM) {
160 QStringList values = m_logValues->lineEdit()->text().split(',');
161 foreach (QString value, values) {
162 bool ok = false;
163 double number = value.toDouble(&ok);
164 if (ok) {
165 logValues.insert(number);
166 }
167 }
168 }
169 return logValues;
170}
171
176const QString MantidWSIndexWidget::getAxisName() const { return m_axisNameEdit->lineEdit()->text(); }
177
182const QString MantidWSIndexWidget::getLogName() const { return m_logSelector->currentText(); }
183
189 // Map of workspace names to set of indices to be plotted.
191
192 // If the user typed in the wsField ...
193 if (m_wsIndexChoice.getList().size() > 0) {
194
195 for (const auto &wsName : m_wsNames) {
196 std::set<int> intSet = m_wsIndexChoice.getIntSet();
197 plots.insert(wsName, intSet);
198 }
199 }
200 // Else if the user typed in the spectraField ...
201 else if (m_spectraNumChoice.getList().size() > 0) {
202 for (const auto &wsName : m_wsNames) {
203 // Convert the spectra choices of the user into workspace indices for us
204 // to use.
205 Mantid::API::MatrixWorkspace_const_sptr ws = std::dynamic_pointer_cast<const Mantid::API::MatrixWorkspace>(
206 Mantid::API::AnalysisDataService::Instance().retrieve(wsName.toStdString()));
207 if (nullptr == ws)
208 continue;
209
210 const Mantid::spec2index_map spec2index = ws->getSpectrumToWorkspaceIndexMap();
211
212 std::set<int> origSet = m_spectraNumChoice.getIntSet();
213 std::set<int>::iterator it = origSet.begin();
214 std::set<int> convertedSet;
215
216 for (; it != origSet.end(); ++it) {
217 int origInt = (*it);
218 int convertedInt = static_cast<int>(spec2index.find(origInt)->second);
219 convertedSet.insert(convertedInt);
220 }
221
222 plots.insert(wsName, convertedSet);
223 }
224 }
225
226 return plots;
227}
228
233bool MantidWSIndexWidget::is1DPlotSelected() const { return (m_plotOptions->currentText() == SIMPLE_PLOT); }
234
240
245bool MantidWSIndexWidget::isTiledPlotSelected() const { return (m_plotOptions->currentText() == "Tiled Plot"); }
246
252
258
263bool MantidWSIndexWidget::isErrorBarsSelected() const { return m_showErrorBars->checkState(); }
264
269 m_spectraField->lineEdit()->clear();
271}
272
277 m_wsField->lineEdit()->clear();
278 m_wsField->setError("");
279}
280
286 bool acceptable = false;
287 int npos = 0;
288 QString wsText = m_wsField->lineEdit()->text();
289 QString spectraText = m_spectraField->lineEdit()->text();
290 QValidator::State wsState = m_wsField->lineEdit()->validator()->validate(wsText, npos);
291 QValidator::State spectraState = m_spectraField->lineEdit()->validator()->validate(spectraText, npos);
292 if (wsState == QValidator::Acceptable) {
296 acceptable = true;
297 }
298 // Else if the user typed in the spectraField ...
299 else if (spectraState == QValidator::Acceptable) {
302 m_usingWsIndexChoice = false;
303 acceptable = true;
304 } else {
306 m_usingWsIndexChoice = false;
307 QString error_message("Invalid input. It is not in the range available");
308 if (!wsText.isEmpty())
309 m_wsField->setError(error_message);
310 if (!spectraText.isEmpty())
311 m_spectraField->setError(error_message);
312 if (wsText.isEmpty() && spectraText.isEmpty()) {
313 m_wsField->setError("Workspace indices or spectra numbers are needed");
314 m_spectraField->setError("Spectra numbers or workspace indices are needed");
315 }
316 }
317 // To give maximum feedback to user, we validate plot options,
318 // even if intervals are not acceptable
319 return validatePlotOptions() && acceptable;
320}
321
329 return validatePlotOptions();
330}
331
337
338 // Only bother is plotting is advanced
339 if (!m_advanced)
340 return true;
341
342 bool validOptions = true;
343
344 // We only validate the custom log values and
345 // only if custom logs are selected, else it's OK.
346 if (m_logSelector->currentText() == CUSTOM) {
347 QStringList values = m_logValues->lineEdit()->text().split(',');
348 bool firstValue = true;
349 double previousValue = 0.0;
350 for (const auto &value : values) {
351 bool ok = false;
352 double currentValue = value.toDouble(&ok);
353 // Check for non-numeric value
354 if (!ok) {
355 m_logValues->setError("A custom log value is not valid: " + value);
356 validOptions = false;
357 break;
358 }
359 // Check for order
360 if (firstValue) {
361 firstValue = false;
362 previousValue = currentValue;
363 } else {
364 if (previousValue < currentValue) {
365 // cpp-check unreadVariable
366 previousValue = currentValue;
367 } else {
368 m_logValues->setError("The custom log values must be in numerical order and distinct.");
369 validOptions = false;
370 break;
371 }
372 }
373 }
374
375 if (validOptions) {
376 int numCustomLogValues = values.size();
377 QString nCustomLogValues;
378 nCustomLogValues.setNum(numCustomLogValues);
379 int numWorkspaces = m_wsNames.size();
380 if (m_plotOptions->currentText() == SURFACE_PLOT || m_plotOptions->currentText() == CONTOUR_PLOT) {
381 QString nWorkspaces;
382 nWorkspaces.setNum(numWorkspaces);
383
384 if (numCustomLogValues != numWorkspaces) {
385 m_logValues->setError("The number of custom log values (" + nCustomLogValues +
386 ") is not equal to the number of workspaces (" + nWorkspaces + ").");
387 validOptions = false;
388 }
389 } else {
390 int numSpectra = 0;
395 QString nPlots;
396 nPlots.setNum(numWorkspaces * numSpectra);
397
398 if (numCustomLogValues != numWorkspaces * numSpectra) {
399 m_logValues->setError("The number of custom log values (" + nCustomLogValues +
400 ") is not equal to the number of plots (" + nPlots + ").");
401 validOptions = false;
402 }
403 }
404 }
405 }
406
407 if (!validOptions) {
408 // Clear record of user choices, because they may change.
411 }
412
413 return validOptions;
414}
415
420 m_outer = new QVBoxLayout;
424 if (m_advanced) {
425 initLogs();
426 }
427 setLayout(m_outer);
428}
429
434 m_wsBox = new QVBoxLayout;
435 const QString wsIndices = m_wsIndexIntervals.toQString();
436 const QString label = "Enter Workspace Indices: " + wsIndices;
437 m_wsMessage = new QLabel(tr(qPrintable(label)));
439
440 m_wsField->lineEdit()->setValidator(new IntervalListValidator(this, m_wsIndexIntervals));
441 if (wsIndices == "0") { // single spectrum
442 m_wsField->lineEdit()->setEnabled(false);
443 m_wsField->lineEdit()->setText("0");
444 }
445 m_wsBox->addWidget(m_wsMessage);
446 m_wsBox->addWidget(m_wsField);
447 m_outer->addItem(m_wsBox);
448
449 connect(m_wsField->lineEdit(), SIGNAL(textEdited(const QString &)), this, SLOT(editedWsField()));
450}
451
456 m_spectraBox = new QVBoxLayout;
458 const QString label = "Enter Spectra Numbers: " + spectraNumbers;
459 m_spectraMessage = new QLabel(tr(qPrintable(label)));
461 m_orMessage = new QLabel(tr("<br>Or"));
462
464 if (spectraNumbers == "1") { // single spectrum
465 m_spectraField->lineEdit()->setEnabled(false);
466 m_spectraField->lineEdit()->setText("1");
467 }
468 m_spectraBox->addWidget(m_spectraMessage);
469 m_spectraBox->addWidget(m_spectraField);
470 m_spectraBox->addWidget(m_orMessage);
471
473 m_outer->addItem(m_spectraBox);
474
475 connect(m_spectraField->lineEdit(), SIGNAL(textEdited(const QString &)), this, SLOT(editedSpectraField()));
476}
477
482 m_optionsBox = new QVBoxLayout;
483
484 m_plotOptionLabel = new QLabel(tr("Plot Type:"));
485 if (m_waterfall || m_tiled) {
486 m_plotOptions = new QComboBox();
487 m_plotOptions->addItem(SIMPLE_PLOT);
488 if (m_waterfall) {
490 }
491 if (m_tiled) {
492 m_plotOptions->addItem(tr("Tiled Plot"));
493 }
495 m_plotOptions->addItem(SURFACE_PLOT);
496 m_plotOptions->addItem(CONTOUR_PLOT);
497 connect(m_plotOptions, SIGNAL(currentIndexChanged(const QString &)), this,
498 SLOT(onPlotOptionChanged(const QString &)));
499 }
501 m_optionsBox->addWidget(m_plotOptions);
502 }
503
504 if (m_advanced) {
505 int spacingAboveShowErrorBars = 10;
506 m_optionsBox->addSpacing(spacingAboveShowErrorBars);
507 m_showErrorBars = new QCheckBox("Show Error Bars");
508 m_optionsBox->addWidget(m_showErrorBars);
509 }
510
511 m_outer->addItem(m_optionsBox);
512}
513
515 m_logOptionsGroup = new QGroupBox(tr("Log Options"));
516 m_logBox = new QVBoxLayout;
517
518 m_logLabel = new QLabel(tr("Log value to plot against:"));
519 m_logSelector = new QComboBox();
521
522 m_customLogLabel = new QLabel(tr("<br>Custom log values:"));
524
525 m_axisLabel = new QLabel(tr("<br>Label for plot axis:"));
527 m_axisNameEdit->lineEdit()->setText(m_logSelector->currentText());
528
529 m_logBox->addWidget(m_logLabel);
530 m_logBox->addWidget(m_logSelector);
531 m_logBox->addWidget(m_customLogLabel);
532 m_logBox->addWidget(m_logValues);
533 m_logBox->addWidget(m_axisLabel);
534 m_logBox->addWidget(m_axisNameEdit);
535
536 m_logSelector->setEnabled(true);
537 m_logValues->setEnabled(false);
538 m_axisNameEdit->setEnabled(false);
539
540 m_logOptionsGroup->setLayout(m_logBox);
541
542 m_outer->addWidget(m_logOptionsGroup);
543
544 connect(m_logSelector, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(onLogSelected(const QString &)));
545}
546
554void MantidWSIndexWidget::onLogSelected(const QString &logName) {
555 m_logValues->setEnabled(logName == CUSTOM);
556 m_logValues->lineEdit()->clear();
557 m_axisNameEdit->lineEdit()->setText(logName);
558}
559
564void MantidWSIndexWidget::onPlotOptionChanged(const QString &plotOption) {
565 auto useLogNames = m_advanced && isSuitableForLogValues(plotOption);
566 auto isLogSelectorCustom = m_logSelector->currentText() == CUSTOM;
567 auto isSurfaceOrContourPlot =
568 m_plotOptions->currentText() == SURFACE_PLOT || m_plotOptions->currentText() == CONTOUR_PLOT;
569 // Enable widgets as appropriate
570 m_showErrorBars->setEnabled(!isSurfaceOrContourPlot);
571 m_logSelector->setEnabled(useLogNames);
572 m_logValues->setEnabled(useLogNames && isLogSelectorCustom);
573 m_axisNameEdit->setEnabled(isSurfaceOrContourPlot);
574 if (useLogNames) {
575 // Make sure an appropriate name is shown for the default log option.
576 if (m_plotOptions->currentText() == SURFACE_PLOT || m_plotOptions->currentText() == CONTOUR_PLOT) {
577 m_logSelector->setItemText(0, WORKSPACE_INDEX);
578 if (m_axisNameEdit->lineEdit()->text() == WORKSPACE_NAME) {
580 }
581 } else {
582 m_logSelector->setItemText(0, WORKSPACE_NAME);
583 }
584 }
585}
586
587namespace {
588struct LogTestStruct {
589 LogTestStruct() : isconstantvalue(true), value(std::numeric_limits<double>::quiet_NaN()) {}
590 LogTestStruct(bool isconstantvalue, double value) : isconstantvalue(isconstantvalue), value(value) {}
591
593 double value;
594};
595} // namespace
596
603 // First item should be "Workspace index"
605
606 // Create a map of all logs and their double represenation to compare against.
607 // Only logs that can be converted to a double and are not all equal will make
608 // the final cut
609 // it is map[logname] = (isconstantvalue, value)
610 std::map<std::string, LogTestStruct> usableLogs;
611 // add the logs that are present in the first workspace
612 auto ws = getWorkspace(m_wsNames[0]);
613 if (ws) {
614 const auto runObj = ws->run();
615 const std::vector<Mantid::Kernel::Property *> &logData = runObj.getLogData();
616 for (auto &log : logData) {
617 const std::string &name = log->name();
618 try {
619 const auto value = runObj.getLogAsSingleValue(name, Mantid::Kernel::Math::TimeAveragedMean);
620 usableLogs[name] = LogTestStruct{true, value};
621 } catch (std::invalid_argument &) {
622 // it can't be represented as a double so ignore it
623 }
624 }
625 }
626
627 // loop over all of the workspaces in the group to see that the value has
628 // changed
629 for (auto &wsName : m_wsNames) {
630 ws = getWorkspace(wsName);
631 if (ws) {
632 const auto runObj = ws->run();
633 for (auto &logItem : usableLogs) {
634 if (runObj.hasProperty(logItem.first)) {
635 // check the value if it is still considered constant
636 if (logItem.second.isconstantvalue) {
637 const auto value = runObj.getLogAsSingleValue(logItem.first, Mantid::Kernel::Math::TimeAveragedMean);
638 // set the bool to whether the value is the same
639 logItem.second.isconstantvalue = (value == logItem.second.value);
640 }
641 } else { // delete it from the list using the name
642 usableLogs.erase(logItem.first);
643 }
644 }
645 }
646 }
647
648 // Add the log names to the combo box if they appear in all workspaces
649 for (auto &logItem : usableLogs) {
650 if (!(logItem.second.isconstantvalue)) { // values are non-constant
651 m_logSelector->addItem(logItem.first.c_str());
652 }
653 }
654
655 // Add "Custom" at the end of the list
656 m_logSelector->addItem(CUSTOM);
657}
658
660 return std::dynamic_pointer_cast<const Mantid::API::MatrixWorkspace>(
661 Mantid::API::AnalysisDataService::Instance().retrieve(workspaceName.toStdString()));
662}
663
664// True if selected plot is suitable for plotting as contour of surface plot
666
667// True if selected plot is suitable for putting log values in
668bool MantidWSIndexWidget::isSuitableForLogValues(const QString &plotOption) const {
669 return (plotOption == SIMPLE_PLOT || plotOption == WATERFALL_PLOT || plotOption == SURFACE_PLOT ||
670 plotOption == CONTOUR_PLOT);
671}
672
680 m_spectra = true;
681
682 for (; it != m_wsNames.constEnd(); ++it) {
683 Mantid::API::MatrixWorkspace_const_sptr ws = std::dynamic_pointer_cast<const Mantid::API::MatrixWorkspace>(
684 Mantid::API::AnalysisDataService::Instance().retrieve((*it).toStdString()));
685 if (nullptr == ws)
686 continue;
687 bool hasSpectra = false;
688 for (int i = 0; i < ws->axes(); i++) {
689 if (ws->getAxis(i)->isSpectra())
690 hasSpectra = true;
691 }
692 if (!hasSpectra) {
693 m_spectra = false;
694 break;
695 }
696 }
697}
698
706
707 // Cycle through the workspaces ...
708 for (; it != m_wsNames.constEnd(); ++it) {
709 Mantid::API::MatrixWorkspace_const_sptr ws = std::dynamic_pointer_cast<const Mantid::API::MatrixWorkspace>(
710 Mantid::API::AnalysisDataService::Instance().retrieve((*it).toStdString()));
711 if (nullptr == ws)
712 continue;
713
714 const int endWs = static_cast<int>(ws->getNumberHistograms() - 1); //= static_cast<int> (end->first);
715
716 Interval interval(0, endWs);
717 // If no interval has been added yet, just add it ...
718 if (it == m_wsNames.constBegin())
720 // ... else set the list as the intersection of what's already there
721 // and what has just been added.
722 else
724 }
725}
726
731 bool firstWs = true;
732 for (const auto &wsName : m_wsNames) {
733 Mantid::API::MatrixWorkspace_const_sptr ws = std::dynamic_pointer_cast<const Mantid::API::MatrixWorkspace>(
734 Mantid::API::AnalysisDataService::Instance().retrieve(wsName.toStdString()));
735 if (!ws)
736 continue; // Belt and braces.
737
738 const Mantid::spec2index_map spec2index = ws->getSpectrumToWorkspaceIndexMap();
739
740 IntervalList spectraIntervalList;
741 for (const auto &pair : spec2index) {
742 spectraIntervalList.addInterval(static_cast<int>(pair.first));
743 }
744
745 if (firstWs) {
746 m_spectraNumIntervals = spectraIntervalList;
747 firstWs = false;
748 } else {
750 }
751 }
752}
753
759 return m_spectra && m_spectraNumIntervals.getList().size() > 0;
760}
761
762//----------------------------------
763// MantidWSIndexDialog public methods
764//----------------------------------
775MantidWSIndexDialog::MantidWSIndexDialog(QWidget *parent, const Qt::WindowFlags &flags, const QList<QString> &wsNames,
776 const bool showWaterfallOption, const bool showPlotAll,
777 const bool showTiledOption, const bool isAdvanced)
778 : QDialog(parent, flags), m_widget(this, flags, wsNames, showWaterfallOption, showTiledOption, isAdvanced),
779 m_plotAll(showPlotAll) {
780 // Set up UI.
781 init(isAdvanced);
782}
783
789
795
801
807
813
819
825
831
832//----------------------------------
833// MantidWSIndexDialog private slots
834//----------------------------------
839 if (m_widget.plotRequested()) {
840 accept();
841 }
842}
843
849 accept();
850 }
851}
852
853//----------------------------------
854// MantidWSIndexDialog private methods
855//----------------------------------
856void MantidWSIndexDialog::init(bool isAdvanced) {
857 m_outer = new QVBoxLayout;
858
859 if (isAdvanced) {
860 setWindowTitle(tr("Plot Advanced"));
861 } else {
862 setWindowTitle(tr("Plot Spectrum"));
863 }
864 m_outer->insertWidget(1, &m_widget);
865 initButtons();
866 setLayout(m_outer);
867}
868
870 m_buttonBox = new QHBoxLayout;
871
872 m_okButton = new QPushButton("OK");
873 m_cancelButton = new QPushButton("Cancel");
874 m_plotAllButton = new QPushButton("Plot All");
875
876 m_buttonBox->addWidget(m_okButton);
877 m_buttonBox->addWidget(m_cancelButton);
878 if (m_plotAll)
879 m_buttonBox->addWidget(m_plotAllButton);
880
881 m_outer->addItem(m_buttonBox);
882
883 connect(m_okButton, SIGNAL(clicked()), this, SLOT(plot()));
884 connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(close()));
885 if (m_plotAll)
886 connect(m_plotAllButton, SIGNAL(clicked()), this, SLOT(plotAll()));
887}
888
889//----------------------------------
890// Interval public methods
891//----------------------------------
892Interval::Interval(int single) { init(single, single); }
893
894Interval::Interval(int start, int end) { init(start, end); }
895
896Interval::Interval(const QString &intervalString) {
897 // Check to see if string is of the correct format, and then parse.
898 // An interval can either be "n" or "n-m" where n and m are integers
899 const QString patternSingle("^\\d+$"); // E.g. "2" or "712"
900 const QString patternRange("^\\d+-\\d+$"); // E.g. "2-4" or "214-200"
901 const QRegExp regExpSingle(patternSingle);
902 const QRegExp regExpRange(patternRange);
903
904 if (regExpSingle.exactMatch(intervalString)) {
905 int single = intervalString.toInt();
906 init(single, single);
907 } else if (regExpRange.exactMatch(intervalString)) {
908 QStringList range = intervalString.split("-");
909 int start = range[0].toInt();
910 int end = range[1].toInt();
911 init(start, end);
912 } else {
913 throw std::exception();
914 }
915}
916
917Interval::Interval(const Interval &copy) { init(copy.m_start, copy.m_end); }
918
919bool Interval::merge(const Interval &other) {
920 // If cant merge, return false
921 if (!canMerge(other))
922 return false;
923
924 // Else, merge - e.g "2" into "3-5" to create "2-5":
925
926 if (other.start() < m_start)
927 m_start = other.start();
928
929 if (other.end() > m_end)
930 m_end = other.end();
931
932 return true;
933}
934
935bool Interval::canMerge(const Interval &other) const {
936 return !(other.start() > m_end + 1 || other.end() + 1 < m_start);
937}
938
939int Interval::start() const { return m_start; }
940
941int Interval::end() const { return m_end; }
942
943// Note that the length of an interval with only one number is 1.
944// Ergo, "length" is defined as (1 + (end - start))
945int Interval::length() const { return 1 + m_end - m_start; }
946
947std::set<int> Interval::getIntSet() const {
948 std::set<int> intSet;
949
950 for (int i = m_start; i <= m_end; i++) {
951 intSet.insert(i);
952 }
953
954 return intSet;
955}
956
957bool Interval::contains(const Interval &other) const { return (other.m_start >= m_start && other.m_end <= m_end); }
958
959std::string Interval::toStdString() const {
960 std::string output;
961
962 if (m_start == m_end) {
963 output += boost::lexical_cast<std::string>(m_start);
964 } else {
965 output += boost::lexical_cast<std::string>(m_start) + "-";
966 output += boost::lexical_cast<std::string>(m_end);
967 }
968
969 return output;
970}
971
972QString Interval::toQString() const {
973 QString output;
974
975 if (m_start == m_end) {
976 output.append(QString("%1").arg(m_start));
977 } else {
978 output.append(QString("%1").arg(m_start));
979 output += "-";
980 output.append(QString("%1").arg(m_end));
981 }
982
983 return output;
984}
985
986//----------------------------------
987// Interval private methods
988//----------------------------------
989void Interval::init(int start, int end) {
990 if (start <= end) {
991 m_start = start;
992 m_end = end;
993 }
994 // Here we cater for the case where a user sets start to be at say 4 but
995 // end at 2. We redefine the interval to be "2-4".
996 else {
997 m_start = end;
998 m_end = start;
999 }
1000}
1001
1002//----------------------------------
1003// IntervalList public methods
1004//----------------------------------
1005IntervalList::IntervalList(void) = default;
1006
1007IntervalList::IntervalList(const QString &intervals) { addIntervals(intervals); }
1008
1009IntervalList::IntervalList(const Interval &interval) { m_list.append(interval); }
1010
1012
1014 // Total up all the individual Interval lengths in the list:
1015 return std::accumulate(m_list.cbegin(), m_list.cend(), 0,
1016 [](int lhs, const auto &interval) { return lhs + interval.length(); });
1017}
1018
1019std::string IntervalList::toStdString(int numOfIntervals) const {
1020 std::string output;
1021
1022 if (m_list.size() <= numOfIntervals) {
1023 for (int i = 0; i < m_list.size(); i++) {
1024 if (i > 0)
1025 output += ", ";
1026
1027 output += m_list.at(i).toStdString();
1028 }
1029 }
1030 // If the number of Intervals is over the numOfIntervals, then
1031 // we only print out the first (numOfIntervals - 1) Intervals,
1032 // followed by a ", ...", followed by the final Interval.
1033 // E.g. "0,2,4,6,8,10,12,14,16,18" becomes "0,2,4,6,8,...,18"
1034 else {
1035 for (int i = 0; i < numOfIntervals - 1; i++) {
1036 if (i > 0)
1037 output += ", ";
1038
1039 output += m_list[i].toStdString();
1040 }
1041
1042 output += ", ..., ";
1043 output += m_list.back().toStdString();
1044 }
1045 return output;
1046}
1047
1048QString IntervalList::toQString(int numOfIntervals) const {
1049 QString output(toStdString(numOfIntervals).c_str());
1050
1051 return output;
1052}
1053
1055 Interval interval(single, single);
1056
1057 IntervalList::addInterval(interval);
1058}
1059
1060// Note: this is considerably more efficient in the case where intervals are
1061// added
1062// smallest first.
1064 if (m_list.size() == 0) {
1065 m_list.append(interval);
1066 return;
1067 }
1068
1069 bool added = false;
1070 QList<int> deleteList;
1071
1072 for (int i = m_list.size() - 1; i >= 0; i--) {
1073 // if new interval is completely higher than this interval
1074 if (interval.start() > m_list.at(i).end() + 1) {
1075 // add new interval as a seperate interval
1076 m_list.append(interval);
1077 added = true;
1078 break;
1079 }
1080 // else if the new interval can be merged with this interval
1081 else if (m_list.at(i).canMerge(interval)) {
1082 // for each interval in the list before and including this one
1083 for (int j = i; j >= 0; j--) {
1084 // if it can be merged into the new interval
1085 if (m_list.at(j).canMerge(interval)) {
1086 // do it
1087 interval.merge(m_list.at(j));
1088 // then add its index to the list of intervals to be deleted
1089 deleteList.append(j);
1090 }
1091 // else if it cant, there is no need to continue checking whether
1092 // any other intervals can alse be merged
1093 else {
1094 break;
1095 }
1096 }
1097 // insert the new large interval in the correct place
1098 m_list.insert(i + 1, interval);
1099 added = true;
1100 break;
1101 }
1102 }
1103 // if deleteList has any elements, delete intervals at those indices
1104 if (deleteList.size() > 0) {
1105 using std::sort;
1106 sort(std::begin(deleteList), std::end(deleteList));
1107
1108 for (int i = deleteList.size() - 1; i >= 0; i--) {
1109 m_list.removeAt(deleteList[i]);
1110 }
1111 }
1112 // if still not assigned, add to the beginning
1113 if (!added) {
1114 m_list.insert(0, interval);
1115 }
1116}
1117
1118void IntervalList::addInterval(int start, int end) {
1119 Interval interval(start, end);
1120
1121 IntervalList::addInterval(interval);
1122}
1123
1124void IntervalList::addIntervals(QString intervals) {
1125 // Remove whitespace
1126 intervals = intervals.simplified();
1127 intervals = intervals.replace(" ", "");
1128
1129 // Split the string, and add the intervals to the list.
1130 QStringList intervalList = intervals.split(",");
1131 for (int i = 0; i < intervalList.size(); i++) {
1132 Interval interval(intervalList[i]);
1133 addInterval(interval);
1134 }
1135}
1136
1138 const QList<Interval> &list = intervals.getList();
1139
1140 QList<Interval>::const_iterator it = list.constBegin();
1141
1142 for (; it != list.constEnd(); ++it) {
1143 addInterval((*it));
1144 }
1145}
1146
1148
1150
1151std::set<int> IntervalList::getIntSet() const {
1152 std::set<int> intSet;
1153
1154 for (const auto &i : m_list) {
1155 std::set<int> intervalSet = i.getIntSet();
1156 intSet.insert(intervalSet.begin(), intervalSet.end());
1157 }
1158
1159 return intSet;
1160}
1161
1162bool IntervalList::contains(const Interval &other) const {
1163 const auto it =
1164 std::find_if(m_list.cbegin(), m_list.cend(), [&other](const auto &interval) { return interval.contains(other); });
1165 return it != m_list.cend();
1166}
1167
1168bool IntervalList::contains(const IntervalList &other) const {
1169 const auto it = std::find_if((other.m_list).cbegin(), (other.m_list).cend(),
1170 [this](const auto &interval) { return !IntervalList::contains(interval); });
1171 return it == (other.m_list).cend();
1172}
1173
1174bool IntervalList::isParsable(const QString &input, const IntervalList &container) {
1175 try {
1176 const IntervalList test(input);
1177 return container.contains(test);
1178 } catch (std::exception &) {
1179 return false;
1180 }
1181}
1182
1183bool IntervalList::isParsable(const QString &input) {
1184 try {
1185 IntervalList interval(input);
1186 return true;
1187 } catch (std::exception &) {
1188 return false;
1189 }
1190}
1191
1193 const IntervalList bList(bInterval);
1194
1195 return IntervalList::intersect(aList, bList);
1196}
1197
1199 IntervalList output;
1200
1201 const std::set<int> aInts = a.getIntSet();
1202 const std::set<int> bInts = b.getIntSet();
1203
1204 for (const auto &aInt : aInts) {
1205 const bool inIntervalListB = bInts.find(aInt) != bInts.end();
1206 if (inIntervalListB)
1207 output.addInterval(aInt);
1208 }
1209
1210 return output;
1211}
1212
1213//----------------------------------
1214// IntervalListValidator public methods
1215//----------------------------------
1217 : QValidator(parent), m_intervalList(std::move(intervalList)) {}
1218
1219QValidator::State IntervalListValidator::validate(QString &input, int &pos) const {
1220 UNUSED_ARG(pos)
1222 return QValidator::Acceptable;
1223
1224 const QString pattern("^(\\d|-|,)*$");
1225 const QRegExp regExp(pattern);
1226
1227 if (regExp.exactMatch(input))
1228 return QValidator::Intermediate;
1229
1230 return QValidator::Invalid;
1231}
1232
1234// QLineEditWithErrorMark
1237 auto *layout = new QGridLayout();
1238 _lineEdit = new QLineEdit();
1239 m_validLbl = new QLabel("*"); // make it red
1240 QPalette pal = m_validLbl->palette();
1241 pal.setColor(QPalette::WindowText, Qt::darkRed);
1242 m_validLbl->setPalette(pal);
1243 layout->addWidget(_lineEdit, 0, 0);
1244 layout->addWidget(m_validLbl, 0, 1);
1245 m_validLbl->setVisible(false);
1246 setLayout(layout);
1247}
1248
1250 if (error.isEmpty()) {
1251 m_validLbl->setVisible(false);
1252 } else {
1253 m_validLbl->setVisible(true);
1254 m_validLbl->setToolTip(error.trimmed());
1255 }
1256}
1257} // namespace MantidQt::MantidWidgets
double value
The value of the point.
Definition: FitMW.cpp:51
double error
Definition: IndexPeaks.cpp:133
bool hasSpectra
IntArray spectraNumbers
bool isconstantvalue
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition: System.h:64
IntervalListValidator(QObject *parent, IntervalList intervals)
Constructor - This object must know its parent QObject, as well as the IntervalList it is validating ...
State validate(QString &, int &) const override
Overriden method to validate a given QString, at a particular position.
IntervalList m_intervalList
The IntervalList against which to validate.
static bool isParsable(const QString &)
Returns true if the QString can be parsed into an IntervalList, else false.
IntervalList(void)
Constructor - with empty list.
std::set< int > getIntSet() const
Returns a set of ints that represents the interval.
void addIntervals(QString)
Attempts to parse the given string into a IntervalList to add.
const QList< Interval > & getList() const
Returns a reference to the list of Intervals.
void addIntervalList(const IntervalList &)
Adds an IntervalList to this IntervalList.
QList< Interval > m_list
A list of all the Intervals in this IntervalList.
void addInterval(int single)
Add an interval starting and ending at single.
static IntervalList intersect(const IntervalList &, const Interval &)
Returns an IntervalList which is the intersection of the given IntervalList and Interval.
std::string toStdString(int numOfIntervals=6) const
Returns a string that represents the IntervalList, of the form "0, 2-5, 8, 10-12".
int totalIntervalLength() const
Returns the combined length of all Intervals in the list.
void clear()
Clears the interval list.
bool contains(const Interval &) const
Returns true if this interval list completely contains the interval passed to it, else false.
void setIntervalList(const IntervalList &)
Replaces the current list with the list belonging to given IntervalList object.
QString toQString(int numOfIntervals=6) const
Convenience function that returns the contents of toStdString as a QString object.
QString toQString() const
Returns a string which represents the start and end of this Interval.
std::set< int > getIntSet() const
Returns a set of ints that represents the interval.
void init(int, int)
Initialise the Interval, given the specified start and end ints.
bool contains(const Interval &) const
Returns true if this interval completely contains the interval passed to it, else false.
int m_start
The start and end of the interval.
bool merge(const Interval &)
Attempts to merge the given Interval with this Interval.
int start() const
Returns the int marking the start of this Interval.
std::string toStdString() const
Returns a string which represents the start and end of this Interval.
int length() const
Returns the length of this interval.
Interval(int single)
Constructor - starting and ending at single.
bool canMerge(const Interval &) const
Returns true if it is possible to merge the given Interval with this Interval, else false.
int end() const
Returns the int marking the end of this Interval.
void plotAll()
Called when the "Plot All" button is pressed.
bool isErrorBarsSelected() const
Returns whether error bars have been selected.
bool isTiledPlotSelected() const
Returns whether the tiled plot option has been selected.
void initButtons()
Initializes the layout of the buttons.
MantidWSIndexDialog(QWidget *parent, const Qt::WindowFlags &flags, const QList< QString > &wsNames, const bool showWaterfallOption=false, const bool showPlotAll=true, const bool showTiledOption=false, const bool isAdvanced=false)
Constructor - has a list of the names of workspaces to be plotted.
bool isContourPlotSelected() const
Returns whether surface plot is selected.
bool isWaterfallPlotSelected() const
Returns whether the waterfall option has been selected.
MantidWSIndexWidget::UserInput getSelections()
Returns a structure holding all of the selected options.
void plot()
Called when the OK button is pressed.
bool m_plotAll
Do we allow the display of the "Plot all" button.
bool isSurfacePlotSelected() const
Returns whether surface plot is selected.
bool is1DPlotSelected() const
Returns whether the waterfall option has been selected.
void init(bool isAdvanced)
Initializes the layout of the dialog.
QMultiMap< QString, std::set< int > > getPlots() const
Returns the QMultiMap that contains all the workspaces that are to be plotted, mapped to the set of w...
Auxiliary class to wrap the QLineEdit allowing warning to the user for invalid inputs.
void setError(const QString &error)
if Error is not empty, it will make the * label visible and set the tooltip as the error.
QLineEditWithErrorMark(QWidget *parent=nullptr)
constructor to join together the QLineEdit and an 'invisible'label.
void init()
Initializes the layout of the dialog.
void initWorkspaceBox()
Initializes the layout of the workspace index section of the dialog.
void initOptionsBoxes()
Initialize the layout of the options check boxes.
bool isSurfacePlotSelected() const
Returns whether surface plot is selected.
MantidWSIndexWidget(QWidget *parent, const Qt::WindowFlags &flags, const QList< QString > &wsNames, const bool showWaterfallOption=false, const bool showTiledOption=false, const bool isAdvanced=false)
Constructor - same parameters as one of the parent constructors, along with a list of the names of wo...
bool is1DPlotSelected() const
Returns whether the simple 1D plot option has been selected.
QList< QString > m_wsNames
A list of names of workspaces which are to be plotted.
const std::set< double > getCustomLogValues() const
Get the set of custom log values.
void showPlotOptionsError(const QString &message)
Provide warning if there are plot errors.
bool m_waterfall
Do we allow the display of the waterfall option.
bool isContourPlotSelected() const
Returns whether contour plot is selected.
const QString getAxisName() const
Gets the axis name.
void onPlotOptionChanged(const QString &logName)
Called when the plot option has changed.
bool isSuitableForLogValues(const QString &plotOption) const
Check if workspaces are suitable for use of log values.
void editedWsField()
Called when the wsField has been edited.
void onLogSelected(const QString &logName)
Called when the log selection is changed.
QLabel * m_wsMessage
Pointers to the obligatory Qt objects:
bool validatePlotOptions()
Validate plot options when either plot or plot all is requested.
const QString getLogName() const
Gets the log name.
void checkForSpectraAxes()
Check to see if all workspaces have a spectrum axis.
bool plotRequested()
Called by dialog when plot requested.
static const QString CUSTOM
The string "Custom".
UserInput getSelections()
Returns a structure holding all of the selected options.
Mantid::API::MatrixWorkspace_const_sptr getWorkspace(const QString &workspaceName) const
Get a handle a workspace by name.
bool isErrorBarsSelected() const
Returns whether the error bars option has been selected.
bool usingSpectraNumbers() const
Whether or not there are any common spectra IDs between workspaces.
static const QString SIMPLE_PLOT
Strings for plot types.
bool isWaterfallPlotSelected() const
Returns whether the waterfall option has been selected.
void initSpectraBox()
Initializes the layout of the spectra ID section of the dialog.
void populateLogComboBox()
Populate the log combo box.
bool m_spectra
Do we allow the user to ask for a range of spectra IDs or not?
bool m_usingWsIndexChoice
Flags to indicate which one of the two interval lists above is chosen by user.
QMultiMap< QString, std::set< int > > getPlots() const
Returns the QMultiMap that contains all the workspaces that are to be plotted, mapped to the set of w...
void editedSpectraField()
Called when the spectraField has been edited.
bool isTiledPlotSelected() const
Returns whether the tiled plot option has been selected.
IntervalList m_wsIndexChoice
IntervalLists for the range of indices/numbers CHOSEN by the user.
void initLogs()
Initializes the layout of the log options.
bool isSuitableForContourOrSurfacePlot() const
Check if workspaces are suitable for contour or surface plot.
bool m_tiled
Do we allow the display of the tiled option.
void generateSpectraNumIntervals()
Generates an IntervalList which defines which spectra IDs the user can ask to plot.
bool plotAllRequested()
Called by dialog when plot all requested.
static const QString WORKSPACE_NAME
The string "Workspace index".
IntervalList m_wsIndexIntervals
IntervalLists for the range of indices/numbers AVAILABLE to the user.
void generateWsIndexIntervals()
Generates an IntervalList which defines which workspace indices the user can ask to plot.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::unordered_map< specnum_t, size_t > spec2index_map
Map with key = spectrum number, value = workspace index.
STL namespace.
Plain old data structures to hold all user-selected input.