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 <QRegularExpression>
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 for (QString const &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
272
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
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 = static_cast<int>(values.size());
377 QString nCustomLogValues;
378 nCustomLogValues.setNum(numCustomLogValues);
379 int numWorkspaces = static_cast<int>(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(currentTextChanged(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(currentTextChanged(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 const &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
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 // use anchored patterns to make sure the whole string matches, not just part of it.
902 const QRegularExpression regExpSingle(QRegularExpression::anchoredPattern(patternSingle));
903 const QRegularExpression regExpRange(QRegularExpression::anchoredPattern(patternRange));
904
905 if (regExpSingle.match(intervalString).hasMatch()) {
906 int single = intervalString.toInt();
907 init(single, single);
908 } else if (regExpRange.match(intervalString).hasMatch()) {
909 QStringList range = intervalString.split("-");
910 int first = range[0].toInt();
911 int last = range[1].toInt();
912 init(first, last);
913 } else {
914 throw std::exception();
915 }
916}
917
918Interval::Interval(const Interval &copy) { init(copy.m_start, copy.m_end); }
919
920bool Interval::merge(const Interval &other) {
921 // If cant merge, return false
922 if (!canMerge(other))
923 return false;
924
925 // Else, merge - e.g "2" into "3-5" to create "2-5":
926
927 if (other.start() < m_start)
928 m_start = other.start();
929
930 if (other.end() > m_end)
931 m_end = other.end();
932
933 return true;
934}
935
936bool Interval::canMerge(const Interval &other) const {
937 return !(other.start() > m_end + 1 || other.end() + 1 < m_start);
938}
939
940int Interval::start() const { return m_start; }
941
942int Interval::end() const { return m_end; }
943
944// Note that the length of an interval with only one number is 1.
945// Ergo, "length" is defined as (1 + (end - start))
946int Interval::length() const { return 1 + m_end - m_start; }
947
948std::set<int> Interval::getIntSet() const {
949 std::set<int> intSet;
950
951 for (int i = m_start; i <= m_end; i++) {
952 intSet.insert(i);
953 }
954
955 return intSet;
956}
957
958bool Interval::contains(const Interval &other) const { return (other.m_start >= m_start && other.m_end <= m_end); }
959
960std::string Interval::toStdString() const {
961 std::string output;
962
963 if (m_start == m_end) {
964 output += boost::lexical_cast<std::string>(m_start);
965 } else {
966 output += boost::lexical_cast<std::string>(m_start) + "-";
967 output += boost::lexical_cast<std::string>(m_end);
968 }
969
970 return output;
971}
972
973QString Interval::toQString() const {
974 QString output;
975
976 if (m_start == m_end) {
977 output.append(QString("%1").arg(m_start));
978 } else {
979 output.append(QString("%1").arg(m_start));
980 output += "-";
981 output.append(QString("%1").arg(m_end));
982 }
983
984 return output;
985}
986
987//----------------------------------
988// Interval private methods
989//----------------------------------
990void Interval::init(int start, int end) {
991 if (start <= end) {
992 m_start = start;
993 m_end = end;
994 }
995 // Here we cater for the case where a user sets start to be at say 4 but
996 // end at 2. We redefine the interval to be "2-4".
997 else {
998 m_start = end;
999 m_end = start;
1000 }
1001}
1002
1003//----------------------------------
1004// IntervalList public methods
1005//----------------------------------
1006IntervalList::IntervalList(void) = default;
1007
1008IntervalList::IntervalList(const QString &intervals) { addIntervals(intervals); }
1009
1010IntervalList::IntervalList(const Interval &interval) { m_list.append(interval); }
1011
1013
1015 // Total up all the individual Interval lengths in the list:
1016 return std::accumulate(m_list.cbegin(), m_list.cend(), 0,
1017 [](int lhs, const auto &interval) { return lhs + interval.length(); });
1018}
1019
1020std::string IntervalList::toStdString(int numOfIntervals) const {
1021 std::string output;
1022
1023 if (m_list.size() <= numOfIntervals) {
1024 for (int i = 0; i < m_list.size(); i++) {
1025 if (i > 0)
1026 output += ", ";
1027
1028 output += m_list.at(i).toStdString();
1029 }
1030 }
1031 // If the number of Intervals is over the numOfIntervals, then
1032 // we only print out the first (numOfIntervals - 1) Intervals,
1033 // followed by a ", ...", followed by the final Interval.
1034 // E.g. "0,2,4,6,8,10,12,14,16,18" becomes "0,2,4,6,8,...,18"
1035 else {
1036 for (int i = 0; i < numOfIntervals - 1; i++) {
1037 if (i > 0)
1038 output += ", ";
1039
1040 output += m_list[i].toStdString();
1041 }
1042
1043 output += ", ..., ";
1044 output += m_list.back().toStdString();
1045 }
1046 return output;
1047}
1048
1049QString IntervalList::toQString(int numOfIntervals) const {
1050 QString output(toStdString(numOfIntervals).c_str());
1051
1052 return output;
1053}
1054
1056 Interval interval(single, single);
1057
1058 IntervalList::addInterval(interval);
1059}
1060
1061// Note: this is considerably more efficient in the case where intervals are
1062// added
1063// smallest first.
1065 if (m_list.size() == 0) {
1066 m_list.append(interval);
1067 return;
1068 }
1069
1070 bool added = false;
1071 QList<int> deleteList;
1072
1073 for (int i = static_cast<int>(m_list.size()) - 1; i >= 0; i--) {
1074 // if new interval is completely higher than this interval
1075 if (interval.start() > m_list.at(i).end() + 1) {
1076 // add new interval as a seperate interval
1077 m_list.append(interval);
1078 added = true;
1079 break;
1080 }
1081 // else if the new interval can be merged with this interval
1082 else if (m_list.at(i).canMerge(interval)) {
1083 // for each interval in the list before and including this one
1084 for (int j = i; j >= 0; j--) {
1085 // if it can be merged into the new interval
1086 if (m_list.at(j).canMerge(interval)) {
1087 // do it
1088 interval.merge(m_list.at(j));
1089 // then add its index to the list of intervals to be deleted
1090 deleteList.append(j);
1091 }
1092 // else if it cant, there is no need to continue checking whether
1093 // any other intervals can alse be merged
1094 else {
1095 break;
1096 }
1097 }
1098 // insert the new large interval in the correct place
1099 m_list.insert(i + 1, interval);
1100 added = true;
1101 break;
1102 }
1103 }
1104 // if deleteList has any elements, delete intervals at those indices
1105 if (deleteList.size() > 0) {
1106 using std::sort;
1107 sort(std::begin(deleteList), std::end(deleteList));
1108
1109 for (int i = static_cast<int>(deleteList.size()) - 1; i >= 0; i--) {
1110 m_list.removeAt(deleteList[i]);
1111 }
1112 }
1113 // if still not assigned, add to the beginning
1114 if (!added) {
1115 m_list.insert(0, interval);
1116 }
1117}
1118
1119void IntervalList::addInterval(int start, int end) {
1120 Interval interval(start, end);
1121
1122 IntervalList::addInterval(interval);
1123}
1124
1125void IntervalList::addIntervals(QString intervals) {
1126 // Remove whitespace
1127 intervals = intervals.simplified();
1128 intervals = intervals.replace(" ", "");
1129
1130 // Split the string, and add the intervals to the list.
1131 QStringList intervalList = intervals.split(",");
1132 for (int i = 0; i < intervalList.size(); i++) {
1133 Interval interval(intervalList[i]);
1134 addInterval(interval);
1135 }
1136}
1137
1139 const QList<Interval> &list = intervals.getList();
1140
1141 QList<Interval>::const_iterator it = list.constBegin();
1142
1143 for (; it != list.constEnd(); ++it) {
1144 addInterval((*it));
1145 }
1146}
1147
1149
1151
1152std::set<int> IntervalList::getIntSet() const {
1153 std::set<int> intSet;
1154
1155 for (const auto &i : m_list) {
1156 std::set<int> intervalSet = i.getIntSet();
1157 intSet.insert(intervalSet.begin(), intervalSet.end());
1158 }
1159
1160 return intSet;
1161}
1162
1163bool IntervalList::contains(const Interval &other) const {
1164 const auto it =
1165 std::find_if(m_list.cbegin(), m_list.cend(), [&other](const auto &interval) { return interval.contains(other); });
1166 return it != m_list.cend();
1167}
1168
1169bool IntervalList::contains(const IntervalList &other) const {
1170 const auto it = std::find_if((other.m_list).cbegin(), (other.m_list).cend(),
1171 [this](const auto &interval) { return !IntervalList::contains(interval); });
1172 return it == (other.m_list).cend();
1173}
1174
1175bool IntervalList::isParsable(const QString &input, const IntervalList &container) {
1176 try {
1177 const IntervalList test(input);
1178 return container.contains(test);
1179 } catch (std::exception &) {
1180 return false;
1181 }
1182}
1183
1184bool IntervalList::isParsable(const QString &input) {
1185 try {
1186 IntervalList interval(input);
1187 return true;
1188 } catch (std::exception &) {
1189 return false;
1190 }
1191}
1192
1194 const IntervalList bList(bInterval);
1195
1196 return IntervalList::intersect(aList, bList);
1197}
1198
1200 IntervalList output;
1201
1202 const std::set<int> aInts = a.getIntSet();
1203 const std::set<int> bInts = b.getIntSet();
1204
1205 for (const auto &aInt : aInts) {
1206 const bool inIntervalListB = bInts.find(aInt) != bInts.end();
1207 if (inIntervalListB)
1208 output.addInterval(aInt);
1209 }
1210
1211 return output;
1212}
1213
1214//----------------------------------
1215// IntervalListValidator public methods
1216//----------------------------------
1218 : QValidator(parent), m_intervalList(std::move(intervalList)) {}
1219
1220QValidator::State IntervalListValidator::validate(QString &input, int &pos) const {
1221 UNUSED_ARG(pos)
1223 return QValidator::Acceptable;
1224
1225 const QString pattern("(\\d|-|,)*");
1226 // use anchored patterns to make sure the whole string matches, not just part of it.
1227 const QRegularExpression regExp(QRegularExpression::anchoredPattern(pattern));
1228
1229 if (regExp.match(input).hasMatch())
1230 return QValidator::Intermediate;
1231
1232 return QValidator::Invalid;
1233}
1234
1236// QLineEditWithErrorMark
1239 auto *layout = new QGridLayout();
1240 _lineEdit = new QLineEdit();
1241 m_validLbl = new QLabel("*"); // make it red
1242 QPalette pal = m_validLbl->palette();
1243 pal.setColor(QPalette::WindowText, Qt::darkRed);
1244 m_validLbl->setPalette(pal);
1245 layout->addWidget(_lineEdit, 0, 0);
1246 layout->addWidget(m_validLbl, 0, 1);
1247 m_validLbl->setVisible(false);
1248 setLayout(layout);
1249}
1250
1252 if (error.isEmpty()) {
1253 m_validLbl->setVisible(false);
1254 } else {
1255 m_validLbl->setVisible(true);
1256 m_validLbl->setToolTip(error.trimmed());
1257 }
1258}
1259} // namespace MantidQt::MantidWidgets
std::string name
Definition Run.cpp:60
double value
The value of the point.
Definition FitMW.cpp:51
double error
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:44
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.
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.