diff --git a/MantidQt/API/CMakeLists.txt b/MantidQt/API/CMakeLists.txt index 035ec66032874f7af02facb3b89cb5ed6b48a9a5..7e6530283747fc52142c496a94b196ed6756c97c 100644 --- a/MantidQt/API/CMakeLists.txt +++ b/MantidQt/API/CMakeLists.txt @@ -28,8 +28,9 @@ src/PropertyWidgetFactory.cpp src/PythonRunner.cpp src/PythonThreading.cpp + src/QScienceSpinBox.cpp src/QtSignalChannel.cpp - src/QwtRasterDataMD.cpp + src/QwtRasterDataMD.cpp src/QwtWorkspaceBinData.cpp src/QwtWorkspaceSpectrumData.cpp src/RepoModel.cpp @@ -61,6 +62,7 @@ set ( MOC_FILES inc/MantidQtAPI/OptionsPropertyWidget.h inc/MantidQtAPI/PropertyWidget.h inc/MantidQtAPI/PythonRunner.h + inc/MantidQtAPI/QScienceSpinBox.h inc/MantidQtAPI/QtSignalChannel.h inc/MantidQtAPI/ScriptRepositoryView.h inc/MantidQtAPI/RepoTreeView.h @@ -82,7 +84,7 @@ set ( INC_FILES inc/MantidQtAPI/DllOption.h inc/MantidQtAPI/FileDialogHandler.h inc/MantidQtAPI/GraphOptions.h - inc/MantidQtAPI/DistributionOptions.h + inc/MantidQtAPI/DistributionOptions.h inc/MantidQtAPI/HelpWindow.h inc/MantidQtAPI/InterfaceFactory.h inc/MantidQtAPI/InterfaceManager.h @@ -98,8 +100,9 @@ set ( INC_FILES inc/MantidQtAPI/PropertyWidgetFactory.h inc/MantidQtAPI/PythonSystemHeader.h inc/MantidQtAPI/PythonThreading.h - inc/MantidQtAPI/QwtRasterDataMD.h - inc/MantidQtAPI/QwtWorkspaceBinData.h + inc/MantidQtAPI/QScienceSpinBox.h + inc/MantidQtAPI/QwtRasterDataMD.h + inc/MantidQtAPI/QwtWorkspaceBinData.h inc/MantidQtAPI/QwtWorkspaceSpectrumData.h inc/MantidQtAPI/ScaleEngine.h inc/MantidQtAPI/ScriptRepositoryView.h diff --git a/MantidQt/API/inc/MantidQtAPI/QScienceSpinBox.h b/MantidQt/API/inc/MantidQtAPI/QScienceSpinBox.h new file mode 100644 index 0000000000000000000000000000000000000000..ffb249261b9415ca23d8104507fe5bbdd3cd490f --- /dev/null +++ b/MantidQt/API/inc/MantidQtAPI/QScienceSpinBox.h @@ -0,0 +1,57 @@ +#ifndef MANTIDQT_API_QSCIENCESPINBOX_H_ +#define MANTIDQT_API_QSCIENCESPINBOX_H_ + +#include "MantidQtAPI/DllOption.h" + +#include <QtGui/QDoubleSpinBox> +#include <QtGui/QDoubleValidator> +#include <QtGui/QLineEdit> +#include <QtCore/QVariant> +#include <QtCore/QDebug> +#include <QtCore/QString> + +namespace MantidQt { +namespace API { + +class EXPORT_OPT_MANTIDQT_API QScienceSpinBox : public QDoubleSpinBox { + Q_OBJECT +public: + QScienceSpinBox(QWidget *parent = 0); + + int decimals() const; + void setDecimals(int value); + + QString textFromValue(double value) const; + double valueFromText(const QString &text) const; + + void setLogSteps(bool logSteps); + +private: + int dispDecimals; + QChar delimiter, thousand; + QDoubleValidator *v; + /// Will step in a log way (multiplicatively) + bool m_logSteps; + +private: + void initLocalValues(QWidget *parent); + bool isIntermediateValue(const QString &str) const; + QVariant validateAndInterpret(QString &input, int &pos, + QValidator::State &state) const; + QValidator::State validate(QString &text, int &pos) const; + void fixup(QString &input) const; + QString stripped(const QString &t, int *pos) const; + double round(double value) const; + void stepBy(int steps); + +public slots: + void stepDown(); + void stepUp(); + +signals: + void valueChangedFromArrows(); +}; + +} // namespace API +} // namespace MantidQt +#endif /* MANTIDQT_API_QSCIENCESPINBOX_H_ */ diff --git a/MantidQt/API/inc/MantidQtAPI/QwtRasterDataMD.h b/MantidQt/API/inc/MantidQtAPI/QwtRasterDataMD.h index 2e75960b9cd5847717a35caf9280c181fe6656a5..b392cade774109f99e013cad2183eaa5c5f68435 100644 --- a/MantidQt/API/inc/MantidQtAPI/QwtRasterDataMD.h +++ b/MantidQt/API/inc/MantidQtAPI/QwtRasterDataMD.h @@ -54,6 +54,8 @@ public: void setZerosAsNan(bool val); + bool isZerosAsNan() const; + void setNormalization(Mantid::API::MDNormalization normalization); Mantid::API::MDNormalization getNormalization() const; diff --git a/MantidQt/API/src/QScienceSpinBox.cpp b/MantidQt/API/src/QScienceSpinBox.cpp new file mode 100644 index 0000000000000000000000000000000000000000..942e0b98eb3bc3d55612ce3838898e26cf8554d9 --- /dev/null +++ b/MantidQt/API/src/QScienceSpinBox.cpp @@ -0,0 +1,543 @@ +#include "MantidQtAPI/QScienceSpinBox.h" +#include <limits> + +//#define QSPINBOX_QSBDEBUG +#ifdef QSPINBOX_QSBDEBUG +#define QSBDEBUG qDebug +#else +#define QSBDEBUG \ + if (false) \ + qDebug +#endif + +namespace MantidQt { +namespace API { + +// reimplemented function, copied from qspinbox.cpp +bool isIntermediateValueHelper(qint64 num, qint64 min, qint64 max, + qint64 *match = 0) { + QSBDEBUG("%lld %lld %lld", num, min, max); + + if (num >= min && num <= max) { + if (match) + *match = num; + QSBDEBUG("returns true 0"); + return true; + } + qint64 tmp = num; + + int numDigits = 0; + int digits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + if (tmp == 0) { + numDigits = 1; + digits[0] = 0; + } else { + tmp = qAbs(num); + for (int i = 0; tmp > 0; ++i) { + digits[numDigits++] = int(tmp) % 10; + tmp /= 10; + } + } + + int failures = 0; + qint64 number; + for (number = max; number >= min; --number) { + tmp = qAbs(number); + for (int i = 0; tmp > 0;) { + if (digits[i] == (tmp % 10)) { + if (++i == numDigits) { + if (match) + *match = number; + QSBDEBUG("returns true 1"); + return true; + } + } + tmp /= 10; + } + if (failures++ == 500000) { // upper bound + if (match) + *match = num; + QSBDEBUG("returns true 2"); + return true; + } + } + QSBDEBUG("returns false"); + return false; +} + +QScienceSpinBox::QScienceSpinBox(QWidget *parent) + : QDoubleSpinBox(parent), m_logSteps(true) { + initLocalValues(parent); + setDecimals(8); + QDoubleSpinBox::setDecimals(1000); + + // set Range to maximum possible values + double doubleMax = std::numeric_limits<double>::max(); + setRange(-doubleMax, doubleMax); + + v = new QDoubleValidator(this); + v->setDecimals(1000); // (standard anyway) + v->setNotation(QDoubleValidator::ScientificNotation); + this->lineEdit()->setValidator(v); +} + +void QScienceSpinBox::initLocalValues(QWidget *parent) { + const QString str = (parent ? parent->locale() : QLocale()).toString(4567.1); + if (str.size() == 6) { + delimiter = str.at(4); + thousand = QChar((ushort)0); + } else if (str.size() == 7) { + thousand = str.at(1); + delimiter = str.at(5); + } + Q_ASSERT(!delimiter.isNull()); +} + +int QScienceSpinBox::decimals() const { return dispDecimals; } + +void QScienceSpinBox::setDecimals(int value) { dispDecimals = value; } + +// overwritten virtual function of QAbstractSpinBox +void QScienceSpinBox::stepBy(int steps) { + if (steps < 0) + stepDown(); + else + stepUp(); +} + +void QScienceSpinBox::setLogSteps(bool logSteps) { m_logSteps = logSteps; } + +void QScienceSpinBox::stepDown() { + if (m_logSteps) + setValue(value() / this->singleStep()); + else + setValue(value() - this->singleStep()); + emit valueChangedFromArrows(); +} + +void QScienceSpinBox::stepUp() { + if (m_logSteps) + setValue(value() * this->singleStep()); + else + setValue(value() + this->singleStep()); + emit valueChangedFromArrows(); +} + +/*! + * text to be displayed in spinbox + */ +QString QScienceSpinBox::textFromValue(double value) const { + + // convert to string -> Using exponetial display with internal decimals + QString str = locale().toString(value, 'e', dispDecimals); + // remove thousand sign + if (qAbs(value) >= 1000.0) { + str.remove(thousand); + } + return str; +} + +double QScienceSpinBox::valueFromText(const QString &text) const { + QString copy = text; + int pos = this->lineEdit()->cursorPosition(); + QValidator::State state = QValidator::Acceptable; + return validateAndInterpret(copy, pos, state).toDouble(); +} + +/** + * Round + * @param value: Value to round. + * @return rounded value + */ +double QScienceSpinBox::round(double value) const { + // this function is never used...? + const QString strDbl = locale().toString(value, 'g', dispDecimals); + return locale().toDouble(strDbl); +} + +// overwritten virtual function of QAbstractSpinBox +QValidator::State QScienceSpinBox::validate(QString &text, int &pos) const { + QValidator::State state; + validateAndInterpret(text, pos, state); + return state; +} + +// overwritten virtual function of QAbstractSpinBox +void QScienceSpinBox::fixup(QString &input) const { input.remove(thousand); } + +// reimplemented function, copied from +// QDoubleSpinBoxPrivate::isIntermediateValue +bool QScienceSpinBox::isIntermediateValue(const QString &str) const { + QSBDEBUG() << "input is" << str << minimum() << maximum(); + qint64 dec = 1; + + for (int i = 0; i < decimals(); ++i) + dec *= 10; + + const QLatin1Char dot('.'); + + /*! + * determine minimum possible values on left and right of Decimal-char + */ + // I know QString::number() uses CLocale so I use dot + const QString minstr = + QString::number(minimum(), 'f', QDoubleSpinBox::decimals()); + qint64 min_left = minstr.left(minstr.indexOf(dot)).toLongLong(); + qint64 min_right = minstr.mid(minstr.indexOf(dot) + 1).toLongLong(); + + const QString maxstr = + QString::number(maximum(), 'f', QDoubleSpinBox::decimals()); + qint64 max_left = maxstr.left(maxstr.indexOf(dot)).toLongLong(); + qint64 max_right = maxstr.mid(maxstr.indexOf(dot) + 1).toLongLong(); + + /*! + * determine left and right long values (left and right of delimiter) + */ + const int dotindex = str.indexOf(delimiter); + const bool negative = maximum() < 0; + qint64 left = 0, right = 0; + bool doleft = true; + bool doright = true; + // no separator -> everthing in left + if (dotindex == -1) { + left = str.toLongLong(); + doright = false; + } + // separator on left or contains '+' + else if (dotindex == 0 || (dotindex == 1 && str.at(0) == QLatin1Char('+'))) { + // '+' at negative max + if (negative) { + QSBDEBUG() << __FILE__ << __LINE__ << "returns false"; + return false; + } + doleft = false; + right = str.mid(dotindex + 1).toLongLong(); + } + // contains '-' + else if (dotindex == 1 && str.at(0) == QLatin1Char('-')) { + // '-' at positiv max + if (!negative) { + QSBDEBUG() << __FILE__ << __LINE__ << "returns false"; + return false; + } + doleft = false; + right = str.mid(dotindex + 1).toLongLong(); + } else { + left = str.left(dotindex).toLongLong(); + if (dotindex == str.size() - 1) { // nothing right of Separator + doright = false; + } else { + right = str.mid(dotindex + 1).toLongLong(); + } + } + // left > 0, with max < 0 and no '-' + if ((left >= 0 && max_left < 0 && !str.startsWith(QLatin1Char('-'))) + // left > 0, with min > 0 + || + (left < 0 && min_left >= 0)) { + QSBDEBUG("returns false"); + return false; + } + + qint64 match = min_left; + if (doleft && !isIntermediateValueHelper(left, min_left, max_left, &match)) { + QSBDEBUG() << __FILE__ << __LINE__ << "returns false"; + return false; + } + if (doright) { + QSBDEBUG("match %lld min_left %lld max_left %lld", match, min_left, + max_left); + if (!doleft) { + if (min_left == max_left) { + const bool ret = isIntermediateValueHelper( + qAbs(left), negative ? max_right : min_right, + negative ? min_right : max_right); + QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret; + return ret; + } else if (qAbs(max_left - min_left) == 1) { + const bool ret = isIntermediateValueHelper(qAbs(left), min_right, + negative ? 0 : dec) || + isIntermediateValueHelper( + qAbs(left), negative ? dec : 0, max_right); + QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret; + return ret; + } else { + const bool ret = isIntermediateValueHelper(qAbs(left), 0, dec); + QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret; + return ret; + } + } + if (match != min_left) { + min_right = negative ? dec : 0; + } + if (match != max_left) { + max_right = negative ? 0 : dec; + } + qint64 tmpl = negative ? max_right : min_right; + qint64 tmpr = negative ? min_right : max_right; + const bool ret = isIntermediateValueHelper(right, tmpl, tmpr); + QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret; + return ret; + } + QSBDEBUG() << __FILE__ << __LINE__ << "returns true"; + return true; +} + +/*! + \internal Multi purpose function that parses input, sets state to + the appropriate state and returns the value it will be interpreted + as. + */ +// reimplemented function, copied from +// QDoubleSpinBoxPrivate::validateAndInterpret +QVariant QScienceSpinBox::validateAndInterpret(QString &input, int &pos, + QValidator::State &state) const { + /*! return 'cachedText' if + * input = cachedText, or input Empty + */ + + static QString cachedText; + static QValidator::State cachedState; + static QVariant cachedValue; + + if (cachedText == input && !input.isEmpty()) { + state = cachedState; + QSBDEBUG() << "cachedText was" + << "'" << cachedText << "'" + << "state was " << state << " and value was " << cachedValue; + return cachedValue; + } + const double max = maximum(); + const double min = minimum(); + + // removes prefix & suffix + QString copy = stripped(input, &pos); + QSBDEBUG() << "input" << input << "copy" << copy; + + int len = copy.size(); + double num = min; + const bool plus = max >= 0; + const bool minus = min <= 0; + + // Test possible 'Intermediate' reasons + switch (len) { + case 0: + // Length 0 is always 'Intermediate', except for min=max + if (max != min) { + state = QValidator::Intermediate; + } else { + state = QValidator::Invalid; + } + goto end; + case 1: + // if only char is '+' or '-' + if (copy.at(0) == delimiter || (plus && copy.at(0) == QLatin1Char('+')) || + (minus && copy.at(0) == QLatin1Char('-'))) { + state = QValidator::Intermediate; + goto end; + } + break; + case 2: + // if only chars are '+' or '-' followed by Comma seperator (delimiter) + if (copy.at(1) == delimiter && + ((plus && copy.at(0) == QLatin1Char('+')) || + (minus && copy.at(0) == QLatin1Char('-')))) { + state = QValidator::Intermediate; + goto end; + } + break; + default: + break; + } // end switch + + // First char must not be thousand-char + if (copy.at(0) == thousand) { + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + state = QValidator::Invalid; + goto end; + } + // Test possible 'Invalid' reasons + else if (len > 1) { + const int dec = copy.indexOf(delimiter); // position of delimiter + // if decimal separator (delimiter) exists + if (dec != -1) { + // not two delimiters after one other (meaning something like ',,') + if (dec + 1 < copy.size() && copy.at(dec + 1) == delimiter && + pos == dec + 1) { + copy.remove(dec + 1, + 1); // typing a delimiter when you are on the delimiter + } // should be treated as typing right arrow + // too many decimal points + if (copy.size() - dec > QDoubleSpinBox::decimals() + 1) { + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + state = QValidator::Invalid; + goto end; + } + // after decimal separator no thousand char + for (int i = dec + 1; i < copy.size(); ++i) { + if (copy.at(i).isSpace() || copy.at(i) == thousand) { + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + state = QValidator::Invalid; + goto end; + } + } + // if no decimal separator exists + } else { + const QChar &last = copy.at(len - 1); + const QChar &secondLast = copy.at(len - 2); + // group of two thousand or space chars is invalid + if ((last == thousand || last.isSpace()) && + (secondLast == thousand || secondLast.isSpace())) { + state = QValidator::Invalid; + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + goto end; + } + // two space chars is invalid + else if (last.isSpace() && + (!thousand.isSpace() || secondLast.isSpace())) { + state = QValidator::Invalid; + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + goto end; + } + } + } // end if (len > 1) + + // block of remaining test before 'end' mark + { + bool ok = false; + bool notAcceptable = false; + + // convert 'copy' to double, and check if that was 'ok' + QLocale loc(locale()); + num = loc.toDouble(copy, &ok); + QSBDEBUG() << __FILE__ << __LINE__ << loc << copy << num << ok; + + // conversion to double did fail + if (!ok) { + // maybe thousand char was responsable + if (thousand.isPrint()) { + // if no thousand sign is possible, then + // something else is responable -> Invalid + if (max < 1000 && min > -1000 && copy.contains(thousand)) { + state = QValidator::Invalid; + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + goto end; + } + + // two thousand-chars after one other are not valid + const int len = copy.size(); + for (int i = 0; i < len - 1; ++i) { + if (copy.at(i) == thousand && copy.at(i + 1) == thousand) { + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + state = QValidator::Invalid; + goto end; + } + } + + // remove thousand-chars + const int s = copy.size(); + copy.remove(thousand); + pos = qMax(0, pos - (s - copy.size())); + + num = loc.toDouble(copy, &ok); + QSBDEBUG() << thousand << num << copy << ok; + + // if conversion still not valid, then reason unknown -> Invalid + if (!ok) { + state = QValidator::Invalid; + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + goto end; + } + notAcceptable = true; // -> state = Intermediate + } // endif: (thousand.isPrint()) + } + + // no thousand sign, but still invalid for unknown reason + if (!ok) { + state = QValidator::Invalid; + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + } + // number valid and within valid range + else if (num >= min && num <= max) { + if (notAcceptable) { + state = QValidator::Intermediate; // conversion to num initially failed + } else { + state = QValidator::Acceptable; + } + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to " + << (state == QValidator::Intermediate ? "Intermediate" + : "Acceptable"); + } + // when max and min is the same the only non-Invalid input is max (or min) + else if (max == min) { + state = QValidator::Invalid; + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + } else { + // value out of valid range (coves only special cases) + if ((num >= 0 && num > max) || (num < 0 && num < min)) { + state = QValidator::Invalid; + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to Invalid"; + } else { + // invalid range, further test with 'isIntermediateValue' + if (isIntermediateValue(copy)) { + state = QValidator::Intermediate; + } else { + state = QValidator::Invalid; + } + QSBDEBUG() << __FILE__ << __LINE__ << "state is set to " + << (state == QValidator::Intermediate ? "Intermediate" + : "Acceptable"); + } + } + } + +end: + // if something went wrong, set num to something valid + if (state != QValidator::Acceptable) { + num = max > 0 ? min : max; + } + + // save (private) cache values + cachedText = prefix() + copy + suffix(); + cachedState = state; + cachedValue = QVariant(num); + // return resulting valid num + return QVariant(num); +} + +/*! + \internal + Strips any prefix/suffix from \a text. + */ +// reimplemented function, copied from QAbstractSpinBoxPrivate::stripped +QString QScienceSpinBox::stripped(const QString &t, int *pos) const { + QString text = t; + QString prefixtext = prefix(); + QString suffixtext = suffix(); + + if (specialValueText().size() == 0 || text != specialValueText()) { + int from = 0; + int size = text.size(); + bool changed = false; + if (prefixtext.size() && text.startsWith(prefixtext)) { + from += prefixtext.size(); + size -= from; + changed = true; + } + if (suffixtext.size() && text.endsWith(suffixtext)) { + size -= suffixtext.size(); + changed = true; + } + if (changed) + text = text.mid(from, size); + } + + const int s = text.size(); + text = text.trimmed(); + if (pos) + (*pos) -= (s - text.size()); + return text; +} + +} // namespace API +} // namespace MantidQt diff --git a/MantidQt/API/src/QwtRasterDataMD.cpp b/MantidQt/API/src/QwtRasterDataMD.cpp index 9766a8a8c2dd7ab275bbae325827926d10f5c656..d845f1c5756538461cdc0c92913f09c800b3ec3d 100644 --- a/MantidQt/API/src/QwtRasterDataMD.cpp +++ b/MantidQt/API/src/QwtRasterDataMD.cpp @@ -108,6 +108,10 @@ void QwtRasterDataMD::setFastMode(bool fast) { this->m_fast = fast; } */ void QwtRasterDataMD::setZerosAsNan(bool val) { this->m_zerosAsNan = val; } +bool QwtRasterDataMD::isZerosAsNan() const { + return this->m_zerosAsNan; +} + //------------------------------------------------------------------------------------------------------ /** Set how the signal is normalized * diff --git a/MantidQt/CustomInterfaces/CMakeLists.txt b/MantidQt/CustomInterfaces/CMakeLists.txt index 167b56d2670c0cc17f5409a2b9545ef347a84f41..c565d9c70b163dcf74e46cb4c59bacc0b0241b69 100644 --- a/MantidQt/CustomInterfaces/CMakeLists.txt +++ b/MantidQt/CustomInterfaces/CMakeLists.txt @@ -1,6 +1,7 @@ set ( SRC_FILES src/DataComparison.cpp src/DirectConvertToEnergy.cpp + src/DynamicPDF/SliceSelector.cpp src/EnggDiffraction/EnggDiffractionPresenter.cpp src/EnggDiffraction/EnggDiffractionViewQtGUI.cpp src/Homer.cpp @@ -94,7 +95,7 @@ set ( SRC_FILES src/SANSEventSlicing.cpp src/SANSPlotSpecial.cpp src/SANSRunWindow.cpp - src/SampleTransmission.cpp + src/SampleTransmission.cpp src/StepScan.cpp src/Tomography/ImageROIPresenter.cpp src/Tomography/ImageROIViewQtWidget.cpp @@ -115,10 +116,12 @@ set ( SRC_FILES ) # Include files aren't required, but this makes them appear in Visual Studio +# IMPORTANT: Include files are required in the MOC_FILES set. Scroll down to find it. set ( INC_FILES inc/MantidQtCustomInterfaces/Background.h inc/MantidQtCustomInterfaces/DataComparison.h inc/MantidQtCustomInterfaces/DirectConvertToEnergy.h + inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h inc/MantidQtCustomInterfaces/DllConfig.h inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffCalibSettings.h inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresWorker.h @@ -142,7 +145,7 @@ set ( INC_FILES inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.h inc/MantidQtCustomInterfaces/Indirect/IndirectBayes.h inc/MantidQtCustomInterfaces/Indirect/IndirectBayesTab.h - inc/MantidQtCustomInterfaces/Indirect/IndirectCorrections.h + inc/MantidQtCustomInterfaces/Indirect/IndirectCorrections.h inc/MantidQtCustomInterfaces/Indirect/IndirectDataAnalysis.h inc/MantidQtCustomInterfaces/Indirect/IndirectDataAnalysisTab.h inc/MantidQtCustomInterfaces/Indirect/IndirectDataReduction.h @@ -261,6 +264,7 @@ set ( SRC_UNITY_IGNORE_FILES ) set ( MOC_FILES inc/MantidQtCustomInterfaces/Background.h inc/MantidQtCustomInterfaces/DataComparison.h inc/MantidQtCustomInterfaces/DirectConvertToEnergy.h + inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresenter.h inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionPresWorker.h inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionViewQtGUI.h @@ -349,6 +353,7 @@ set ( MOC_FILES inc/MantidQtCustomInterfaces/Background.h set ( UI_FILES inc/MantidQtCustomInterfaces/DataComparison.ui inc/MantidQtCustomInterfaces/DirectConvertToEnergy.ui + inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.ui inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionQtGUI.ui inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionQtTabCalib.ui inc/MantidQtCustomInterfaces/EnggDiffraction/EnggDiffractionQtTabFocus.ui @@ -455,6 +460,7 @@ else ( ${Boost_VERSION} GREATER 104799 AND ${qt_version} VERSION_GREATER 4.7.3 ) endif ( ${Boost_VERSION} GREATER 104799 AND ${qt_version} VERSION_GREATER 4.7.3 ) qt4_wrap_cpp ( MOCCED_FILES ${MOC_FILES} OPTIONS ${extra_options} ) + qt4_add_resources ( RES_FILES icons/CustomInterfacesIcons.qrc ) set ( ALL_SRC ${SRC_FILES} ${MOCCED_FILES} )#${RES_FILES}) diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/BackgroundRemover.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/BackgroundRemover.h new file mode 100644 index 0000000000000000000000000000000000000000..8f53fa89c6dbdff43bbad0e0d67849c2f02a003e --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/BackgroundRemover.h @@ -0,0 +1,35 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_DYNAMICPDF_BACKGROUNDREMOVER_H_ +#define MANTIDQTCUSTOMINTERFACES_DYNAMICPDF_BACKGROUNDREMOVER_H_ + +#include <QDialog> +#include "ui_BackgroundRemover.h" +#include <boost/shared_ptr.hpp> + +namespace MantidQt { +namespace CustomInterfaces { +namespace DynamicPDF { + +// Forward declaration +class WorkspaceRecord; +class SliceSelector; + +class BackgroundRemover: public QDialog +{ + Q_OBJECT + +public: + + BackgroundRemover(SliceSelector *parent); + + void refreshSlice(const boost::shared_ptr<WorkspaceRecord> loadedWorkspace, + const size_t &workspaceIndex); + +private: + Ui::BackgroundRemover m_uiForm; +}; + +} +} +} + +#endif // MANTIDQTCUSTOMINTERFACES_DYNAMICPDF_BACKGROUNDREMOVER_H_ diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/BackgroundRemover.ui b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/BackgroundRemover.ui new file mode 100644 index 0000000000000000000000000000000000000000..d0bfcc4ed5ce4a1a189934036da2568f0cc907a5 --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/BackgroundRemover.ui @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BackgroundRemover</class> + <widget class="QDialog" name="BackgroundRemover"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>349</width> + <height>177</height> + </rect> + </property> + <property name="windowTitle"> + <string>Background remover tool</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>BackgroundRemover</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>BackgroundRemover</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h new file mode 100644 index 0000000000000000000000000000000000000000..ff45740be0b8782202b9445f1509ccbb6aa90de8 --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h @@ -0,0 +1,66 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_DYNAMICPDF_SLICESELECTOR_H_ +#define MANTIDQTCUSTOMINTERFACES_DYNAMICPDF_SLICESELECTOR_H_ +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidQtCustomInterfaces/DllConfig.h" +#include "MantidQtAPI/UserSubWindow.h" +#include "ui_SliceSelector.h" +#include <qwt_plot_spectrogram.h> + +namespace MantidQt { +namespace MantidWidgets { + class MWView; +} +namespace CustomInterfaces { +namespace DynamicPDF { + +// Forward Declarations +//class BackgroundRemover; + +/// Helper class containing pointer and some metadata for the loaded workspace +class WorkspaceRecord{ + +public: + WorkspaceRecord(const std::string &workspaceName); + void updateMetadata(const size_t &newIndex); + + boost::shared_ptr<Mantid::API::MatrixWorkspace> m_ws; + const std::string m_name; + double m_energy; + std::string m_label; +}; + +class MANTIDQT_CUSTOMINTERFACES_DLL SliceSelector + : public MantidQt::API::UserSubWindow { + Q_OBJECT + +public: + /// The name of the interface as registered into the factory + static std::string name() { return "Dynamic PDF Slice Selector"; } + // This interface's categories. + static QString categoryInfo() { return "DynamicPDF"; } + +public: + SliceSelector(QWidget *parent = nullptr); + ~SliceSelector(); + +private slots: + void showHelp(); + void loadSlices(const QString &workspaceName); + void updateSelectedSlice(const int &newSelectedIndex); + void updatePlotSelectedSlice(); + void launchBackgroundRemover(); + +private: + void initLayout(); + /// The form generated by Qt Designer + Ui::SliceSelector m_uiForm; + boost::shared_ptr<WorkspaceRecord> m_loadedWorkspace; + size_t m_selectedWorkspaceIndex; + /// The child dialog to remove the multiphonon background + //boost::shared_ptr<BackgroundRemover> m_BackgroundRemover; +}; + +} +} +} +#endif //MANTIDQTCUSTOMINTERFACES_DYNAMICPDF_SLICESELECTOR_H_ diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.ui b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.ui new file mode 100644 index 0000000000000000000000000000000000000000..ead6c608cb22216a299945f7d709931f639889da --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.ui @@ -0,0 +1,233 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SliceSelector</class> + <widget class="QMainWindow" name="SliceSelector"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>613</width> + <height>850</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Dynamic PDF Slice Selector</string> + </property> + <widget class="QWidget" name="centralwidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupLoadSlices"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Input</string> + </property> + <layout class="QGridLayout" name="loRangeBinning"> + <property name="topMargin"> + <number>9</number> + </property> + <property name="bottomMargin"> + <number>9</number> + </property> + <item row="0" column="0"> + <widget class="MantidQt::MantidWidgets::DataSelector" name="dataSelector" native="true"/> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="layoutSliceSelector"> + <item> + <widget class="QLabel" name="labelWorkspaceIndex"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Workspace Index</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spinboxSliceSelector"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="labelSliceEnergy"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Energy = NAN</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="layout2Dview"> + <item> + <widget class="QSlider" name="sliderSelectSlice"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + </widget> + </item> + <item> + <widget class="MantidQt::MantidWidgets::MWView" name="slices2DPlot" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QGroupBox" name="groupPlotSelectedSlice"> + <property name="title"> + <string>Plots Selected Slice</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="MantidQt::MantidWidgets::PreviewPlot" name="previewPlotSelectedSlice" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="showLegend" stdset="0"> + <bool>false</bool> + </property> + <property name="canvasColour" stdset="0"> + <color> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="layoutActionButtons"> + <item> + <widget class="QPushButton" name="pushHelp"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Opens the help page for the interface.</p></body></html></string> + </property> + <property name="text"> + <string>?</string> + </property> + </widget> + </item> + <item> + <spacer name="sActionButtons"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushLaunchBackgroundRemover"> + <property name="toolTip"> + <string><html><head/><body><p>Launch the background removal tool.</p></body></html></string> + </property> + <property name="text"> + <string>Launch Background Remover</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + <customwidgets> + <customwidget> + <class>MantidQt::MantidWidgets::DataSelector</class> + <extends>QWidget</extends> + <header>MantidQtMantidWidgets/DataSelector.h</header> + </customwidget> + <customwidget> + <class>MantidQt::MantidWidgets::PreviewPlot</class> + <extends>QWidget</extends> + <header>MantidQtMantidWidgets/PreviewPlot.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>MantidQt::MantidWidgets::MWView</class> + <extends>QWidget</extends> + <header>MantidQtMantidWidgets/MWView.h</header> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>spinboxSliceSelector</tabstop> + <tabstop>pushLaunchBackgroundRemover</tabstop> + <tabstop>pushHelp</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/MantidQt/CustomInterfaces/src/DynamicPDF/BackgroundRemover.cpp b/MantidQt/CustomInterfaces/src/DynamicPDF/BackgroundRemover.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17d1b3c8cec144fb2ac3cce8d80cbfcdafd6752d --- /dev/null +++ b/MantidQt/CustomInterfaces/src/DynamicPDF/BackgroundRemover.cpp @@ -0,0 +1,29 @@ +#include "MantidQtCustomInterfaces/DynamicPDF/BackgroundRemover.h" +#include "MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h" + +namespace +{ + Mantid::Kernel::Logger g_log("DynamicPDF"); +} + +namespace MantidQt +{ +namespace CustomInterfaces +{ +namespace DynamicPDF +{ + + BackgroundRemover::BackgroundRemover(SliceSelector *parent) : QDialog(parent) { + m_uiForm.setupUi(this); + } + + void BackgroundRemover::refreshSlice(const boost::shared_ptr<WorkspaceRecord> loadedWorkspace, + const size_t &workspaceIndex){ + UNUSED_ARG(loadedWorkspace); + UNUSED_ARG(workspaceIndex); + std::cout << "hello world"; + } + +} +} +} diff --git a/MantidQt/CustomInterfaces/src/DynamicPDF/SliceSelector.cpp b/MantidQt/CustomInterfaces/src/DynamicPDF/SliceSelector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b34f25c65e5092d5b4065cd1803085a3a0bb6f2b --- /dev/null +++ b/MantidQt/CustomInterfaces/src/DynamicPDF/SliceSelector.cpp @@ -0,0 +1,138 @@ +#include <iostream> +#include <iomanip> +// includes for workspace handling +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/MatrixWorkspace.h" +// includes for interface +#include "MantidQtAPI/HelpWindow.h" +#include "MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h" +#include <qwt_plot_spectrogram.h> + +//#include "MantidQtCustomInterfaces/DynamicPDF/BackgroundRemover.h" + +namespace { +Mantid::Kernel::Logger g_log("DynamicPDF"); +} + +namespace MantidQt { +namespace CustomInterfaces { +namespace DynamicPDF { + +WorkspaceRecord::WorkspaceRecord(const std::string &workspaceName) + : m_name{workspaceName} { + m_ws = Mantid::API::AnalysisDataService::Instance() + .retrieveWS<Mantid::API::MatrixWorkspace>(workspaceName); +} + +void WorkspaceRecord::updateMetadata(const size_t &newIndex) { + m_energy = m_ws->getAxis(1)->getValue(newIndex); + std::stringstream energyLabelStream; + energyLabelStream << std::fixed; + energyLabelStream.precision(2); + energyLabelStream << "Energy = " << m_energy; + energyLabelStream << " meV"; + m_label = energyLabelStream.str(); +} + +// Add this class to the list of specialised dialogs in this namespace +DECLARE_SUBWINDOW(SliceSelector) + +/// Constructor +// SliceSelector::SliceSelector(QWidget *parent) : UserSubWindow{parent}, +// m_loadedWorkspace{nullptr}, m_BackgroundRemover{nullptr} { +SliceSelector::SliceSelector(QWidget *parent) : + UserSubWindow{parent}, + m_loadedWorkspace() { + +} + +SliceSelector::~SliceSelector() { m_selectedWorkspaceIndex = 0; } + +/// Initialize the ui form and connect SIGNALS to SLOTS +void SliceSelector::initLayout() { + m_uiForm.setupUi(this); + connect(m_uiForm.dataSelector, SIGNAL(dataReady(const QString &)), this, + SLOT(loadSlices(const QString &))); + connect(m_uiForm.pushHelp, SIGNAL(clicked()), this, SLOT(showHelp())); + connect(m_uiForm.spinboxSliceSelector, SIGNAL(valueChanged(int)), this, + SLOT(updateSelectedSlice(int))); + connect(m_uiForm.sliderSelectSlice, SIGNAL(valueChanged(int)), this, + SLOT(updateSelectedSlice(int))); + connect(m_uiForm.pushLaunchBackgroundRemover, SIGNAL(clicked()), this, + SLOT(launchBackgroundRemover())); +} + +/// Load file or workspace containing energy slices +void SliceSelector::loadSlices(const QString &workspaceName) { + m_loadedWorkspace = + boost::make_shared<WorkspaceRecord>(workspaceName.toStdString()); + m_selectedWorkspaceIndex = 0; + m_loadedWorkspace->updateMetadata(m_selectedWorkspaceIndex); + int maximumWorkspaceIndex = + static_cast<int>(m_loadedWorkspace->m_ws->getNumberHistograms()) - 1; + + /// initialize the label displaying the energy + m_uiForm.labelSliceEnergy->setText( + QString::fromStdString(m_loadedWorkspace->m_label)); + + /// initialize the spin box that selects the energy slice + m_uiForm.spinboxSliceSelector->setMinimum(0); + m_uiForm.spinboxSliceSelector->setMaximum(maximumWorkspaceIndex); + m_uiForm.spinboxSliceSelector->setValue(0); + m_uiForm.spinboxSliceSelector->setSingleStep(1); + + /// initialize the slider next to the 2D view + m_uiForm.sliderSelectSlice->setMinimum(0); + m_uiForm.sliderSelectSlice->setMaximum(maximumWorkspaceIndex); + m_uiForm.spinboxSliceSelector->setValue(0); + + /// initialize the 2D view of the slices; + m_uiForm.slices2DPlot->setWorkspace(m_loadedWorkspace->m_ws); + m_uiForm.slices2DPlot->updateDisplay(); + + /// initialize the preview plot + updatePlotSelectedSlice(); +} + +/// +void SliceSelector::updatePlotSelectedSlice() { + m_uiForm.previewPlotSelectedSlice->clear(); + m_uiForm.previewPlotSelectedSlice->addSpectrum( + QString::fromStdString(m_loadedWorkspace->m_label), + m_loadedWorkspace->m_ws, m_selectedWorkspaceIndex); +} + +/// Update all widgets in the form with the new selected index +void SliceSelector::updateSelectedSlice(const int &newSelectedIndex) { + m_selectedWorkspaceIndex = static_cast<size_t>(newSelectedIndex); + /// Check pointer m_loadedWorkspace because the user may attemp to manipulate + /// the widgets before (s)he loads any data + if (m_loadedWorkspace) { + m_loadedWorkspace->updateMetadata(m_selectedWorkspaceIndex); + m_uiForm.labelSliceEnergy->setText( + QString::fromStdString(m_loadedWorkspace->m_label)); + m_uiForm.spinboxSliceSelector->setValue(newSelectedIndex); + m_uiForm.sliderSelectSlice->setValue(newSelectedIndex); + updatePlotSelectedSlice(); + } +} + +/// Initialize and/or update the dialog to remove the multiphonon background +void SliceSelector::launchBackgroundRemover() { + /// parent of BackgroundRemover is this main window + // if (!m_BackgroundRemover){ + // m_BackgroundRemover = boost::make_shared<BackgroundRemover>(this); + //} + // m_BackgroundRemover->refreshSlice(m_loadedWorkspace, + // m_selectedWorkspaceIndex); + g_log.error("Not implemented...yet"); +} + +/// Opens the Qt help page for the interface +void SliceSelector::showHelp() { + MantidQt::API::HelpWindow::showCustomInterface( + NULL, QString("Dynamic PDF Calculator")); +} +} +} +} diff --git a/MantidQt/DesignerPlugins/inc/MantidQtDesignerPlugins/PluginCollectionInterface.h b/MantidQt/DesignerPlugins/inc/MantidQtDesignerPlugins/PluginCollectionInterface.h index 750f699700f5448d7bfc959544fe333eeddc0f4c..db7490fb53cc17680fc3373a5ec4805dcedb24f8 100644 --- a/MantidQt/DesignerPlugins/inc/MantidQtDesignerPlugins/PluginCollectionInterface.h +++ b/MantidQt/DesignerPlugins/inc/MantidQtDesignerPlugins/PluginCollectionInterface.h @@ -16,6 +16,7 @@ #include "MantidQtSliceViewer/SliceViewer.h" #include "MantidQtSliceViewer/LineViewer.h" #include "MantidQtMantidWidgets/SafeQwtPlot.h" +#include "MantidQtMantidWidgets/MWView.h" #include "MantidQtAPI/AlgorithmPropertiesWidget.h" #include "MantidQtMantidWidgets/ProcessingAlgoWidget.h" #include "MantidQtMantidWidgets/MessageDisplay.h" @@ -132,6 +133,10 @@ DECLARE_WIDGET_PLUGIN(SafeQwtPlotPlugin, MantidQt::MantidWidgets::SafeQwtPlot, "Version of QwtPlot with workspace-level thread safety"); +DECLARE_WIDGET_PLUGIN(MWViewPlugin, + MantidQt::MantidWidgets::MWView, + "2D view of a MatrixWorkspace"); + DECLARE_WIDGET_PLUGIN(AlgorithmPropertiesWidgetPlugin, MantidQt::API::AlgorithmPropertiesWidget, "List of algorithm properties"); @@ -147,5 +152,4 @@ DECLARE_WIDGET_PLUGIN(MessageDisplayPlugin, DECLARE_WIDGET_PLUGIN(DataSelectorPlugin, MantidQt::MantidWidgets::DataSelector, "Choose a file path or workspace to work with"); - #endif diff --git a/MantidQt/DesignerPlugins/src/PluginCollectionInterface.cpp b/MantidQt/DesignerPlugins/src/PluginCollectionInterface.cpp index 9c0c20347d400675752165a278e60114bcd71b9f..da97057d28ba9beef5b7412bbba77a1da7efacca 100644 --- a/MantidQt/DesignerPlugins/src/PluginCollectionInterface.cpp +++ b/MantidQt/DesignerPlugins/src/PluginCollectionInterface.cpp @@ -21,6 +21,7 @@ PluginCollectionInterface::PluginCollectionInterface(QObject *parent) : QObject( m_widgets.append(new SliceViewerPlugin(this)); m_widgets.append(new LineViewerPlugin(this)); m_widgets.append(new SafeQwtPlotPlugin(this)); + m_widgets.append(new MWViewPlugin(this)); m_widgets.append(new AlgorithmPropertiesWidgetPlugin(this)); m_widgets.append(new ProcessingAlgoWidgetPlugin(this)); m_widgets.append(new DataSelectorPlugin(this)); diff --git a/MantidQt/MantidWidgets/CMakeLists.txt b/MantidQt/MantidWidgets/CMakeLists.txt index 40dd50bb70f502f7cf73332e4750463f0d0d5197..df7a6979379d41a38075ba6c378bc91996be036d 100644 --- a/MantidQt/MantidWidgets/CMakeLists.txt +++ b/MantidQt/MantidWidgets/CMakeLists.txt @@ -4,6 +4,7 @@ set ( SRC_FILES src/CatalogSearch.cpp src/CatalogSelector.cpp src/CheckboxHeader.cpp + src/ColorBarWidget.cpp src/DataSelector.cpp src/DiagResults.cpp src/DoubleDialogEditor.cpp @@ -23,6 +24,7 @@ set ( SRC_FILES src/MantidHelpWindow.cpp src/MWDiag.cpp src/MWRunFiles.cpp + src/MWView.cpp src/MessageDisplay.cpp src/MultifitSetupDialog.cpp src/MuonFitPropertyBrowser.cpp @@ -101,6 +103,7 @@ set ( SRC_FILES set ( MOC_FILES inc/MantidQtMantidWidgets/AlgorithmSelectorWidget.h inc/MantidQtMantidWidgets/CheckboxHeader.h + inc/MantidQtMantidWidgets/ColorBarWidget.h inc/MantidQtMantidWidgets/DataSelector.h inc/MantidQtMantidWidgets/DiagResults.h inc/MantidQtMantidWidgets/DoubleDialogEditor.h @@ -125,6 +128,7 @@ set ( MOC_FILES inc/MantidQtMantidWidgets/MuonSequentialFitDialog.h inc/MantidQtMantidWidgets/MWDiag.h inc/MantidQtMantidWidgets/MWRunFiles.h + inc/MantidQtMantidWidgets/MWView.h inc/MantidQtMantidWidgets/PeakPicker.h inc/MantidQtMantidWidgets/PeriodicTableWidget.h inc/MantidQtMantidWidgets/pqHelpWindow.h @@ -232,9 +236,11 @@ set ( UI_FILES inc/MantidQtMantidWidgets/DataSelector.ui inc/MantidQtMantidWidgets/CatalogSearch.ui inc/MantidQtMantidWidgets/CatalogSelector.ui + inc/MantidQtMantidWidgets/ColorBarWidget.ui inc/MantidQtMantidWidgets/IndirectInstrumentConfig.ui inc/MantidQtMantidWidgets/MWDiag.ui inc/MantidQtMantidWidgets/MWRunFiles.ui + inc/MantidQtMantidWidgets/MWView.ui inc/MantidQtMantidWidgets/MultifitSetupDialog.ui inc/MantidQtMantidWidgets/MuonSequentialFitDialog.ui inc/MantidQtMantidWidgets/ProcessingAlgoWidget.ui diff --git a/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/ColorBarWidget.h b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/ColorBarWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..514c467b4842a65ab37f75cf74a91ed59d021afe --- /dev/null +++ b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/ColorBarWidget.h @@ -0,0 +1,128 @@ +#ifndef MANTID_MANTIDWIDGETS_COLORBARWIDGET_H_ +#define MANTID_MANTIDWIDGETS_COLORBARWIDGET_H_ + +#include <QtGui/QWidget> +#include "ui_ColorBarWidget.h" +#include <qwt_color_map.h> +#include <qwt_scale_widget.h> +#include "MantidQtAPI/MantidColorMap.h" +#include <QKeyEvent> +#include <QtGui> +#include "MantidQtMantidWidgets/WidgetDllOption.h" + +namespace MantidQt +{ +namespace MantidWidgets +{ + +//============================================================================= +/** Extended version of QwtScaleWidget */ +class QwtScaleWidgetExtended : public QwtScaleWidget +{ + Q_OBJECT + +public: + QwtScaleWidgetExtended(QWidget *parent = NULL) + : QwtScaleWidget(parent) + { + this->setMouseTracking(true); + } + + void mouseMoveEvent(QMouseEvent * event) + { + double val = 1.0 - double(event->y()) / double(this->height()); + emit mouseMoved(event->globalPos(), val); + } + +signals: + void mouseMoved(QPoint, double); + +}; + + +//============================================================================= +/** Widget for showing a color bar, modifying its + * limits, etc. + * + * @author Janik Zikovsky + * @date Oct 31, 2011. + */ +class EXPORT_OPT_MANTIDQT_MANTIDWIDGETS ColorBarWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY(double minimum READ getMinimum WRITE setMinimum) + Q_PROPERTY(double maximum READ getMaximum WRITE setMaximum) + +public: + ColorBarWidget(QWidget *parent = 0); + ~ColorBarWidget(); + + void updateColorMap(); + + void setViewRange(double min, double max); + void setViewRange(QwtDoubleInterval range); + void setMinimum(double min); + void setMaximum(double max); + void setRenderMode(bool rendering); + + double getMinimum() const; + double getMaximum() const; + QwtDoubleInterval getViewRange() const; + MantidColorMap & getColorMap(); + + bool getLog(); + + int getScale(); + void setScale(int); + + void setExponent(double); + double getExponent(); + + void setAutoScale(bool autoscale); + bool getAutoScale() const; + +public slots: + void changedMinimum(); + void changedMaximum(); + void colorBarMouseMoved(QPoint, double); + void changedScaleType(int); + void changedExponent(double); + +signals: + /// Signal sent when the range or log mode of the color scale changes. + void changedColorRange(double min, double max, bool log); + /// When the user double-clicks the color bar (e.g. load a new color map) + void colorBarDoubleClicked(); + +private: + void setSpinBoxesSteps(); + void mouseDoubleClickEvent(QMouseEvent * event); + void updateMinMaxGUI(); + void resizeEvent(QResizeEvent * event); + + /// Auto-gen UI classes + Ui::ColorBarWidgetClass ui; + + /// The color bar widget from QWT + QwtScaleWidget * m_colorBar; + + /// Color map being displayed + MantidColorMap m_colorMap; + + /// Logarithmic scale? + bool m_log; + + /// Min value being displayed + double m_min; + + /// Min value being displayed + double m_max; + + /// Show the value tooltip (off by default) + bool m_showTooltip; +}; + +} // namespace MantidWidgets +} // namespace Mantid + +#endif // MANTID_MANTIDWIDGETS_COLORBARWIDGET_H_ diff --git a/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/ColorBarWidget.ui b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/ColorBarWidget.ui new file mode 100644 index 0000000000000000000000000000000000000000..e542aa599cf78f03bb9fbbd5dc9052786b328a73 --- /dev/null +++ b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/ColorBarWidget.ui @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ColorBarWidgetClass</class> + <widget class="QWidget" name="ColorBarWidgetClass"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>184</width> + <height>306</height> + </rect> + </property> + <property name="windowTitle"> + <string>ColorBarWidget</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>3</number> + </property> + <item> + <widget class="QComboBox" name="cmbScaleType"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="lblN"> + <property name="text"> + <string>n =</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QDoubleSpinBox" name="dspnN"/> + </item> + </layout> + </item> + <item> + <widget class="MantidQt::API::QScienceSpinBox" name="valMax"> + <property name="toolTip"> + <string>Maximum value in the color scale</string> + </property> + <property name="wrapping"> + <bool>false</bool> + </property> + <property name="buttonSymbols"> + <enum>QAbstractSpinBox::UpDownArrows</enum> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="MantidQt::API::QScienceSpinBox" name="valMin"> + <property name="toolTip"> + <string>Minimum value in the color scale</string> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="autoScaleForCurrentSlice"> + <property name="toolTip"> + <string><html><head/><body><p>When this is checked the color scale range will be automatically set to the current slice whenever the user zooms to a new peak, pans through the view or changes the slice with the slider control</p></body></html></string> + </property> + <property name="text"> + <string>Autoscale to current slice</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="autoScale"> + <property name="toolTip"> + <string><html><head/><body><p>This flag signals that the color scale range should be set automatically to the current slice range when a workspace is loaded. Note that auto scaling will be applied when a workspace is loaded for the very first time. This option is mainly relevant for live data workspaces, which are continuously being updated and reloaded.</p></body></html></string> + </property> + <property name="text"> + <string>Autoscale on load</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11"/> + <customwidgets> + <customwidget> + <class>MantidQt::API::QScienceSpinBox</class> + <extends>QDoubleSpinBox</extends> + <header>MantidQtAPI/QScienceSpinBox.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.h b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.h new file mode 100644 index 0000000000000000000000000000000000000000..7960985fa3b572fb445097659b84e629388541e7 --- /dev/null +++ b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.h @@ -0,0 +1,114 @@ +#ifndef MANTID_MANTIDWIDGETS_MWVIEW_H_ +#define MANTID_MANTIDWIDGETS_MWVIEW_H_ + +// includes for interface development +#include <QWidget> +#include <qwt_plot_spectrogram.h> +#include "ui_MWView.h" +#include "MantidQtAPI/MdSettings.h" +#include "MantidQtMantidWidgets/WidgetDllOption.h" +// includes for workspace handling +#include "MantidAPI/MatrixWorkspace_fwd.h" +#include "MantidAPI/IMDWorkspace.h" +#include "MantidGeometry/MDGeometry/MDHistoDimension.h" + +namespace Mantid { +namespace API { +class MWDimension; +} +} + +namespace MantidQt { + +namespace API { +class QwtRasterDataMD; +class MdSettings; +} + +namespace MantidWidgets { +class ColorBarWidget; + +using MWDimension_sptr = boost::shared_ptr<Mantid::API::MWDimension>; +using MWDimension_const_sptr = + boost::shared_ptr<const Mantid::API::MWDimension>; + +/** A viewer for a Matrix Workspace. + * + * Before drawing, it acquires a ReadLock to prevent + * an algorithm from modifying the underlying workspace while it is + * drawing. + * + * If no workspace is set, no drawing occurs (silently). + + @date 2016-02-05 + + Copyright © 2012 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class EXPORT_OPT_MANTIDQT_MANTIDWIDGETS MWView : public QWidget { + Q_OBJECT + +public: + MWView(QWidget *parent = 0); + ~MWView(); + void loadColorMap(QString filename = QString()); + void setWorkspace(Mantid::API::MatrixWorkspace_sptr ws); + void updateDisplay(); + +signals: + /// Signal emitted when someone uses setWorkspace() on MWView + void workspaceChanged(); + +public slots: + void colorRangeChangedSlot(); + void loadColorMapSlot(); + void setTransparentZerosSlot(bool transparent); + +private: + void initLayout(); + void loadSettings(); + void saveSettings(); + void checkRangeLimits(); + void findRangeFull(); + void setVectorDimensions(); + + Ui::MWView m_uiForm; + /// Spectrogram plot of MWView + QwtPlotSpectrogram *m_spect; + /// Data presenter + API::QwtRasterDataMD *m_data; + /// File of the last loaded color map. + QString m_currentColorMapFile; + /// Md Settings for color maps + boost::shared_ptr<MantidQt::API::MdSettings> m_mdSettings; + /// Workspace being shown + Mantid::API::MatrixWorkspace_sptr m_workspace; + /// The calculated range of values in the FULL data set + QwtDoubleInterval m_colorRangeFull; + Mantid::API::MDNormalization m_normalization; + /// Vector of the dimensions to show. + std::vector<Mantid::Geometry::MDHistoDimension_sptr> m_dimensions; +}; + +} // namespace MantidWidgets +} // namespace MantidQt + +#endif /* MANTID_MANTIDWIDGETS_MWVIEW_H_ */ diff --git a/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.ui b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.ui new file mode 100644 index 0000000000000000000000000000000000000000..43aaf6480a64ec520c34ccf3c3069ef57b2ccfad --- /dev/null +++ b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.ui @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MWView</class> + <widget class="QWidget" name="MWView"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>706</width> + <height>462</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <widget class="QWidget" name="horizontalLayoutWidget"> + <property name="geometry"> + <rect> + <x>19</x> + <y>19</y> + <width>681</width> + <height>441</height> + </rect> + </property> + <layout class="QHBoxLayout" name="viewer"> + <item> + <widget class="MantidQt::MantidWidgets::SafeQwtPlot" name="plot2D"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="MantidQt::MantidWidgets::ColorBarWidget" name="colorBar" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <customwidgets> + <customwidget> + <class>QwtPlot</class> + <extends>QFrame</extends> + <header>qwt_plot.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>MantidQt::MantidWidgets::SafeQwtPlot</class> + <extends>QwtPlot</extends> + <header>MantidQtMantidWidgets/SafeQwtPlot.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>MantidQt::MantidWidgets::ColorBarWidget</class> + <extends>QWidget</extends> + <header>MantidQtMantidWidgets/ColorBarWidget.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/MantidQt/MantidWidgets/src/ColorBarWidget.cpp b/MantidQt/MantidWidgets/src/ColorBarWidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e8bbdbbebf10d5d0289f7e1b6642fd1081d76d5 --- /dev/null +++ b/MantidQt/MantidWidgets/src/ColorBarWidget.cpp @@ -0,0 +1,390 @@ +#include "MantidQtAPI/MantidColorMap.h" +#include "MantidQtMantidWidgets/ColorBarWidget.h" +#include "MantidQtAPI/QScienceSpinBox.h" +#include "qwt_scale_div.h" +#include "MantidQtAPI/PowerScaleEngine.h" +#include <iosfwd> +#include <qwt_scale_map.h> +#include <qwt_scale_widget.h> +#include <QKeyEvent> +#include <qwt_plot.h> +#include <qwt_scale_engine.h> + +namespace MantidQt +{ +namespace MantidWidgets +{ + +//------------------------------------------------------------------------------------------------- +/** Constructor */ +ColorBarWidget::ColorBarWidget(QWidget *parent) +: QWidget(parent) +{ + ui.setupUi(this); + + // Default values. + m_min = 0; + m_max = 1000; + m_showTooltip = false; + m_log = false; + + // Scales + ui.cmbScaleType->addItem(tr("linear")); + ui.cmbScaleType->addItem(tr("logarithmic")); + ui.cmbScaleType->addItem(tr("power")); + m_colorMap.changeScaleType( GraphOptions::Linear ); + ui.dspnN->setMinimum(-100.0); + ui.dspnN->setEnabled(false); + + // Create and add the color bar + m_colorBar = new QwtScaleWidgetExtended(); + m_colorBar->setToolTip(""); + m_colorBar->setColorBarEnabled(true); + m_colorBar->setColorBarWidth(20); + m_colorBar->setAlignment(QwtScaleDraw::RightScale); + m_colorBar->setLabelAlignment( Qt::AlignRight | Qt::AlignVCenter); + //m_colorBar->setCursor(Qt::OpenHandCursor); + ui.verticalLayout->insertWidget(2,m_colorBar, 1,0 ); + + // Hook up signals + QObject::connect(ui.dspnN, SIGNAL(valueChanged(double)), this, SLOT(changedExponent(double))); + QObject::connect(ui.cmbScaleType,SIGNAL(currentIndexChanged(int)), this, SLOT(changedScaleType(int))); + QObject::connect(ui.valMin, SIGNAL(editingFinished()), this, SLOT(changedMinimum())); + QObject::connect(ui.valMax, SIGNAL(editingFinished()), this, SLOT(changedMaximum())); + QObject::connect(ui.valMin, SIGNAL(valueChangedFromArrows()), this, SLOT(changedMinimum())); + QObject::connect(ui.valMax, SIGNAL(valueChangedFromArrows()), this, SLOT(changedMaximum())); + QObject::connect(m_colorBar, SIGNAL(mouseMoved(QPoint, double)), this, SLOT(colorBarMouseMoved(QPoint, double))); + + // Initial view + this->updateColorMap(); +} + + +//------------------------------------------------------------------------------------------------- +/// @return the minimum value of the min of the color scale +double ColorBarWidget::getMinimum() const +{ return m_min; } + +/// @return the maximum value of the max of the color scale +double ColorBarWidget::getMaximum() const +{ return m_max; } + +/// @return then min/max range currently viewed +QwtDoubleInterval ColorBarWidget::getViewRange() const +{ return QwtDoubleInterval(m_min, m_max); } + +/// @return the color map in use (ref) +MantidColorMap & ColorBarWidget::getColorMap() +{ return m_colorMap; +} + +//------------------------------------------------------------------------------------------------- +/** Turn "rendering mode" on/off, where GUI widgets are hidden + * for the purposes of rendering an image. + * + * @param rendering :: true if you are going to render + */ +void ColorBarWidget::setRenderMode(bool rendering) +{ + bool visible = !rendering; + this->ui.valMin->setVisible(visible); + this->ui.valMax->setVisible(visible); + this->ui.cmbScaleType->setVisible(visible); + this->ui.lblN->setVisible(visible); + this->ui.dspnN->setVisible(visible); +} + +// Get the current colorbar scaling type +int ColorBarWidget::getScale() +{ + // Get value from GUI + return ui.cmbScaleType->currentIndex(); +} + +// Set the current colorbar scaling type +void ColorBarWidget::setScale(int type) +{ + // Set scale in GUI + ui.cmbScaleType->setCurrentIndex(type); + // Update plot + changedScaleType(type); +} + +bool ColorBarWidget::getLog() +{ + if (getScale() == 1) + { + return true; + } + else return false; +} + +// Set exponent value for power scale +void ColorBarWidget::setExponent(double nth_power) +{ + // Set value in GUI + ui.dspnN->setValue(nth_power); + // Update plot + changedExponent(nth_power); +} + +// Get exponent value for power scale +double ColorBarWidget::getExponent() +{ + // Get value from GUI + return ui.dspnN->value(); +} + +// Change the colormap to match new exponent value +void ColorBarWidget::changedExponent(double nth_power) +{ + m_colorMap.setNthPower(nth_power); + updateColorMap(); + + emit changedColorRange(m_min,m_max,m_log); +} + +//------------------------------------------------------------------------------------------------- +/** Send a double-clicked event but only when clicking the color bar */ +void ColorBarWidget::mouseDoubleClickEvent(QMouseEvent * event) +{ + if (m_colorBar->rect().contains(event->x(), event->y())) + emit colorBarDoubleClicked(); +} + +//------------------------------------------------------------------------------------------------- +/// Event called after resizing +void ColorBarWidget::resizeEvent(QResizeEvent * event) +{ + updateColorMap(); + QWidget::resizeEvent(event); +} + + +//------------------------------------------------------------------------------------------------- +/** Adjust the steps of the spin boxes for log/linear mode */ +void ColorBarWidget::setSpinBoxesSteps() +{ + // Large maximum value + ui.valMin->setMaximum( +1e100 ); + ui.valMax->setMaximum( +1e100 ); + + double step = 1.1; + if (m_log) + { + // Logarithmic color scale: move by logarithmic steps + double logRange; + double temp_min = m_min; + if (temp_min <= 0) + { + // Try to guess at a valid min range if 0 for log scale + logRange = log10(m_max); + if (logRange >= 3) temp_min = 1; + else if (logRange >= 0) temp_min = 1e-3; + // Default to 1/10000 of the max + else temp_min = pow(10., double(int(logRange))-4.); + } + logRange = log10(m_max) - log10(temp_min); + if (logRange > 6) logRange = 6; + step = pow(10., logRange/100.); + + // Small positive value for the minimum + ui.valMin->setMinimum( 1e-99 ); + ui.valMax->setMinimum( 1e-99 ); + // Limit the current min/max to positive values + if (m_min < temp_min) m_min = temp_min; + if (m_max < temp_min) m_max = temp_min; + } + else + { + // --- Linear scale ---- + // Round step that is between 1/100 to 1/1000) + int exponent = int(log10(m_max)) - 2; + step = pow(10., double(exponent)); + + // Large negative value for the minimum + ui.valMin->setMinimum( -1e100 ); + ui.valMax->setMinimum( -1e100 ); + } + + ui.valMin->setSingleStep(step); + ui.valMax->setSingleStep(step); + int dec = 2; + ui.valMin->setDecimals(dec); + ui.valMax->setDecimals(dec); + + updateMinMaxGUI(); +} + + +//------------------------------------------------------------------------------------------------- +/** Set the range of values viewed in the color bar + * + * @param min :: min value = start of the color map + * @param max :: max value = end of the color map + */ +void ColorBarWidget::setViewRange(double min, double max) +{ + m_min = min; + m_max = max; + updateMinMaxGUI(); +} + +/** Set the range of values viewed in the color bar + * + * @param min :: min value = start of the color map + */ +void ColorBarWidget::setMinimum(double min) +{ + m_min = min; + updateMinMaxGUI(); +} + +/** Set the range of values viewed in the color bar + * + * @param max :: max value = start of the color map + */ +void ColorBarWidget::setMaximum(double max) +{ + m_max = max; + updateMinMaxGUI(); +} + +void ColorBarWidget::setViewRange(QwtDoubleInterval range) +{ this->setViewRange(range.minValue(), range.maxValue()); } + +//------------------------------------------------------------------------------------------------- +/* + * Update display if different scale type is selected + */ +void ColorBarWidget::changedScaleType(int type) +{ + // If power scale option is selected, enable "n =" widget + ui.dspnN->setEnabled(type == 2); + + // Record if log scale option is selected + m_log = (type == 1); + + m_colorMap.changeScaleType( GraphOptions::ScaleType(type) ); + ui.valMin->setLogSteps( m_log ); + ui.valMax->setLogSteps( m_log ); + setSpinBoxesSteps(); + updateColorMap(); + + emit changedColorRange(m_min,m_max,m_log); +} + +//------------------------------------------------------------------------------------------------- +/** SLOT called when minValue changes */ +void ColorBarWidget::changedMinimum() +{ + m_min = ui.valMin->value(); + if (m_min > m_max) + { + m_max = m_min+0.001; + ui.valMax->setValue( m_max ); + } + updateColorMap(); + emit changedColorRange(m_min,m_max,m_log); +} + +//------------------------------------------------------------------------------------------------- +/** SLOT called when maxValue changes */ +void ColorBarWidget::changedMaximum() +{ + m_max = ui.valMax->value(); + if (m_max < m_min) + { + m_min = m_max-0.001; + ui.valMin->setValue( m_min ); + } + updateColorMap(); + emit changedColorRange(m_min,m_max,m_log); +} + +//------------------------------------------------------------------------------------------------- +/** SLOT called when the mouse moves over the color bar*/ +void ColorBarWidget::colorBarMouseMoved(QPoint globalPos, double fraction) +{ + if (m_showTooltip) + { + double val = 0; + if (m_log) + val = pow(10., fraction * (log10(m_max)-log10(m_min)) + log10(m_min)); + else + val = fraction * (m_max-m_min) + m_min; + QString tooltip = QString::number(val,'g', 4); + QToolTip::showText(globalPos, tooltip, m_colorBar); + } +} + + +//------------------------------------------------------------------------------------------------- +/** Update the widget when the color map is changed */ +void ColorBarWidget::updateColorMap() +{ + // The color bar always shows the same range. Doesn't matter since the ticks don't show up + QwtDoubleInterval range(1.0, 100.0); + m_colorBar->setColorBarEnabled(true); + m_colorBar->setColorMap( range, m_colorMap); + m_colorBar->setColorBarWidth(15); + m_colorBar->setEnabled(true); + + // Try to limit the number of steps based on the height of the color bar + int maxMajorSteps = m_colorBar->height()/15; // 15 pixels per div looked about right + //std::cout << "maxMajorSteps" << maxMajorSteps << std::endl; + if (maxMajorSteps > 10) maxMajorSteps = 10; + + // Show the scale on the right + double minValue = m_min; + double maxValue = m_max; + GraphOptions::ScaleType type = m_colorMap.getScaleType(); + if( type == GraphOptions::Linear ) + { + QwtLinearScaleEngine linScaler; + m_colorBar->setScaleDiv(linScaler.transformation(), linScaler.divideScale(minValue, maxValue, maxMajorSteps, 5)); + m_colorBar->setColorMap(QwtDoubleInterval(minValue, maxValue),m_colorMap); + } + else if ( type == GraphOptions::Power ) { + PowerScaleEngine powScaler; + m_colorBar->setScaleDiv(powScaler.transformation(), powScaler.divideScale(minValue, maxValue, maxMajorSteps, 5)); + m_colorBar->setColorMap(QwtDoubleInterval(minValue, maxValue), m_colorMap); + } + else + { + QwtLog10ScaleEngine logScaler; + m_colorBar->setScaleDiv(logScaler.transformation(), logScaler.divideScale(minValue, maxValue, maxMajorSteps, 5)); + m_colorBar->setColorMap(QwtDoubleInterval(minValue, maxValue), m_colorMap); + } + +} + +//------------------------------------------------------------------------------------------------- +/** Updatet the widget when changing min/max*/ +void ColorBarWidget::updateMinMaxGUI() +{ + ui.valMin->setValue( m_min ); + ui.valMax->setValue( m_max ); +} + +/** + * Sets the state of the "Autoscale" checkbox + * @param autoscale :: [input] Autoscale on/off + */ +void ColorBarWidget::setAutoScale(bool autoscale) { + ui.autoScale->setChecked(autoscale); + updateColorMap(); +} + +/** + * Gets the state of the "Autoscale" checkbox + * @returns Whether the box is checked or not + */ +bool ColorBarWidget::getAutoScale() const { return ui.autoScale->isChecked(); } + +ColorBarWidget::~ColorBarWidget() +{ +} + +} // namespace MantidQt +} // namespace MantidWidgets diff --git a/MantidQt/MantidWidgets/src/MWView.cpp b/MantidQt/MantidWidgets/src/MWView.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b9a83f719c656e8d70f9d07aa5bdb13ba2d2266e --- /dev/null +++ b/MantidQt/MantidWidgets/src/MWView.cpp @@ -0,0 +1,246 @@ +#include "MantidQtMantidWidgets/MWView.h" +#include <boost/math/special_functions/fpclassify.hpp> +// includes for workspace handling +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidGeometry/MDGeometry/MDTypes.h" +#include "MantidKernel/ReadLock.h" +#include "MantidQtAPI/SignalRange.h" +#include "MantidGeometry/MDGeometry/IMDDimension.h" +#include "MantidGeometry/MDGeometry/MDHistoDimension.h" +#include <boost/pointer_cast.hpp> +#include <boost/shared_ptr.hpp> +// includes for interface development +#include "MantidQtAPI/QwtRasterDataMD.h" +#include "MantidQtAPI/MantidColorMap.h" +#include <qwt_color_map.h> +#include <qwt_double_rect.h> + +namespace { +Mantid::Kernel::Logger g_log("MWView"); +} + +namespace MantidQt { +namespace MantidWidgets { + +// ++++++++++++++++++++++++++++++++ +// ++++++++ Public members ++++++++ +// ++++++++++++++++++++++++++++++++ + +MWView::MWView(QWidget *parent) + : QWidget(parent), + m_mdSettings(boost::make_shared<MantidQt::API::MdSettings>()), + m_workspace(), m_dimensions() { + m_spect = new QwtPlotSpectrogram(); + m_data = new MantidQt::API::QwtRasterDataMD(); + m_normalization = Mantid::API::NoNormalization; + this->initLayout(); + this->loadSettings(); + this->updateDisplay(); +} + +MWView::~MWView() { + saveSettings(); + delete m_data; + delete m_spect; +} + +/** Load a color map from a file + * + * @param filename :: file to open; empty to ask via a dialog box. + */ +void MWView::loadColorMap(QString filename) { + QString fileselection; + if (filename.isEmpty()) { + fileselection = MantidColorMap::loadMapDialog(m_currentColorMapFile, this); + if (fileselection.isEmpty()) + return; + } else + fileselection = filename; + m_currentColorMapFile = fileselection; + m_uiForm.colorBar->getColorMap().loadMap(fileselection); + m_spect->setColorMap(m_uiForm.colorBar->getColorMap()); + m_uiForm.colorBar->updateColorMap(); + this->updateDisplay(); +} + +void MWView::setWorkspace(Mantid::API::MatrixWorkspace_sptr ws) { + m_workspace = ws; + this->checkRangeLimits(); + this->setVectorDimensions(); + findRangeFull(); // minimum and maximum intensities in ws + m_uiForm.colorBar->setViewRange(m_colorRangeFull); + m_uiForm.colorBar->updateColorMap(); + m_data->setWorkspace(ws); + m_normalization = ws->displayNormalization(); + m_data->setNormalization(m_normalization); + m_uiForm.plot2D->setWorkspace(ws); + m_spect->setColorMap(m_uiForm.colorBar->getColorMap()); + emit workspaceChanged(); +} + +void MWView::updateDisplay() { + if (!m_workspace) + return; + m_data->setRange(m_uiForm.colorBar->getViewRange()); + std::vector<Mantid::coord_t> slicePoint{0, 0}; + constexpr size_t dimX(0); + constexpr size_t dimY(1); + Mantid::Geometry::IMDDimension_const_sptr X = m_dimensions[dimX]; + Mantid::Geometry::IMDDimension_const_sptr Y = m_dimensions[dimY]; + m_data->setSliceParams(dimX, dimY, X, Y, slicePoint); + double left{X->getMinimum()}; + double top{Y->getMinimum()}; + double width{X->getMaximum() - X->getMinimum()}; + double height{Y->getMaximum() - Y->getMinimum()}; + QwtDoubleRect bounds{left, top, width, height}; + m_data->setBoundingRect(bounds.normalized()); + m_spect->setColorMap(m_uiForm.colorBar->getColorMap()); + m_spect->setData(*m_data); + m_spect->itemChanged(); + m_uiForm.plot2D->replot(); +} + +// ++++++++++++++++++++++++++++++++ +// ++++++++ Public slots ++++++++ +// ++++++++++++++++++++++++++++++++ + +/// Slot called when the ColorBarWidget changes the range of colors +void MWView::colorRangeChangedSlot() { + m_spect->setColorMap(m_uiForm.colorBar->getColorMap()); + this->updateDisplay(); +} + +void MWView::loadColorMapSlot() { this->loadColorMap(QString()); } + +/** Set whether to display 0 signal as "transparent" color. + * + * @param transparent :: true if you want zeros to be transparent. + */ +void MWView::setTransparentZerosSlot(bool transparent) { + m_data->setZerosAsNan(transparent); + this->updateDisplay(); +} + +// ++++++++++++++++++++++++++++++++ +// ++++++++ Private members +++++++ +// ++++++++++++++++++++++++++++++++ + +/// Initialize the ui form and connect SIGNALS to SLOTS +void MWView::initLayout() { + m_uiForm.setupUi(this); + m_spect->attach(m_uiForm.plot2D); // attach the spectrogram to the plot + /// initialize the color on the bar and the data + m_uiForm.colorBar->setViewRange(1, 10); + QObject::connect(m_uiForm.colorBar, + SIGNAL(changedColorRange(double, double, bool)), this, + SLOT(colorRangeChangedSlot())); + m_spect->setColorMap(m_uiForm.colorBar->getColorMap()); + m_uiForm.plot2D->autoRefresh(); + // Signal/Slot updating the color map + QObject::connect(m_uiForm.colorBar, SIGNAL(colorBarDoubleClicked()), this, + SLOT(loadColorMapSlot())); + // initZoomer(); // TO BE IMPLEMENTED +} + +/** Load QSettings from .ini-type files */ +void MWView::loadSettings() { + QSettings settings; + settings.beginGroup("Mantid/MWView"); + // Maintain backwards compatibility with use of LogColorScale + int scaleType = settings.value("ColorScale", -1).toInt(); + if (scaleType == -1) { + scaleType = settings.value("LogColorScale", 0).toInt(); + } + double nth_power = settings.value("PowerScaleExponent", 2.0).toDouble(); + // Load Colormap. If the file is invalid the default stored colour map is + // used. + // If the user selected a unified color map for the SliceViewer and the VSI, + // then this is loaded. + if (m_mdSettings != NULL && m_mdSettings->getUsageGeneralMdColorMap()) { + m_currentColorMapFile = m_mdSettings->getGeneralMdColorMapFile(); + } else { + m_currentColorMapFile = settings.value("ColormapFile", "").toString(); + } + // Set values from settings + if (!m_currentColorMapFile.isEmpty()) + loadColorMap(m_currentColorMapFile); + m_uiForm.colorBar->setScale(scaleType); + m_uiForm.colorBar->setExponent(nth_power); + bool transparentZeros = settings.value("TransparentZeros", 1).toInt(); + this->setTransparentZerosSlot(transparentZeros); + settings.endGroup(); +} + +void MWView::saveSettings() { + QSettings settings; + settings.beginGroup("Mantid/MWView"); + settings.setValue("ColormapFile", m_currentColorMapFile); + settings.setValue("ColorScale", m_uiForm.colorBar->getScale()); + settings.setValue("PowerScaleExponent", m_uiForm.colorBar->getExponent()); + settings.setValue("TransparentZeros", (m_data->isZerosAsNan() ? 1 : 0)); +} + +// Verify workspace limits +void MWView::checkRangeLimits() { + std::ostringstream mess; + for (size_t d = 0; d < m_workspace->getNumDims(); d++) { + Mantid::coord_t min = m_workspace->getDimension(d)->getMinimum(); + Mantid::coord_t max = m_workspace->getDimension(d)->getMaximum(); + if (max < min) { + Mantid::coord_t tmp = max; + max = min; + min = tmp; + } + if (boost::math::isnan(min) || boost::math::isinf(min) || + boost::math::isnan(max) || boost::math::isinf(max)) { + mess << "Dimension " << m_workspace->getDimension(d)->getName() + << " has a bad range: ("; + mess << min << ", " << max << ")" << std::endl; + } + } + if (!mess.str().empty()) { + mess << "Bad ranges could cause memory allocation errors. Please fix the " + "workspace."; + mess << std::endl << "You can continue using Mantid."; + throw std::out_of_range(mess.str()); + } +} + +/// Find the full range of values in the workspace +void MWView::findRangeFull() { + if (!m_workspace) + return; + Mantid::API::MatrixWorkspace_sptr workspace_used = m_workspace; + + // Acquire a scoped read-only lock on the workspace, preventing it from being + // written + // while we iterate through. + Mantid::Kernel::ReadLock lock(*workspace_used); + + // Iterate through the entire workspace + m_colorRangeFull = + API::SignalRange(*workspace_used, m_normalization).interval(); + double minR = m_colorRangeFull.minValue(); + if (minR <= 0 && m_uiForm.colorBar->getScale() == 1) { + double maxR = m_colorRangeFull.maxValue(); + minR = pow(10., log10(maxR) - 10.); + m_colorRangeFull = QwtDoubleInterval(minR, maxR); + } +} + +/// Update m_dimensions with the loaded workspace +void MWView::setVectorDimensions() { + if (!m_workspace) { + return; + } + m_dimensions.clear(); + for (size_t d = 0; d < m_workspace->getNumDims(); d++) { + Mantid::Geometry::MDHistoDimension_sptr dimension( + new Mantid::Geometry::MDHistoDimension( + m_workspace->getDimension(d).get())); + m_dimensions.push_back(dimension); + } +} + +} // namespace MantidQt +} // namespace MantidWidgets