diff --git a/MantidQt/API/CMakeLists.txt b/MantidQt/API/CMakeLists.txt index e7b70d31c4289eec451cd545ded0b501879b0086..7e6530283747fc52142c496a94b196ed6756c97c 100644 --- a/MantidQt/API/CMakeLists.txt +++ b/MantidQt/API/CMakeLists.txt @@ -28,9 +28,9 @@ src/PropertyWidgetFactory.cpp src/PythonRunner.cpp src/PythonThreading.cpp + src/QScienceSpinBox.cpp src/QtSignalChannel.cpp src/QwtRasterDataMD.cpp - src/QwtRasterDataMW.cpp src/QwtWorkspaceBinData.cpp src/QwtWorkspaceSpectrumData.cpp src/RepoModel.cpp @@ -62,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 @@ -99,8 +100,8 @@ set ( INC_FILES inc/MantidQtAPI/PropertyWidgetFactory.h inc/MantidQtAPI/PythonSystemHeader.h inc/MantidQtAPI/PythonThreading.h + inc/MantidQtAPI/QScienceSpinBox.h inc/MantidQtAPI/QwtRasterDataMD.h - inc/MantidQtAPI/QwtRasterDataMW.h inc/MantidQtAPI/QwtWorkspaceBinData.h inc/MantidQtAPI/QwtWorkspaceSpectrumData.h inc/MantidQtAPI/ScaleEngine.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/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h index 57793adb998a926f4f6c09de514c0d90c3ce3f60..ff45740be0b8782202b9445f1509ccbb6aa90de8 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.h @@ -8,7 +8,7 @@ namespace MantidQt { namespace MantidWidgets { - class MWSpectrogram; + class MWView; } namespace CustomInterfaces { namespace DynamicPDF { @@ -44,22 +44,16 @@ public: ~SliceSelector(); private slots: - /// Opens the Qt help page for the interface void showHelp(); - /// Load file or workspace containing energy slices void loadSlices(const QString &workspaceName); - /// Update all child widgets void updateSelectedSlice(const int &newSelectedIndex); void updatePlotSelectedSlice(); - /// Launch (initialize and/or update) the background removal tool void launchBackgroundRemover(); private: - /// Initialize the ui form and connect SIGNALS to SLOTS void initLayout(); /// The form generated by Qt Designer Ui::SliceSelector m_uiForm; - MantidQt::MantidWidgets::MWSpectrogram* m_plot2D; boost::shared_ptr<WorkspaceRecord> m_loadedWorkspace; size_t m_selectedWorkspaceIndex; /// The child dialog to remove the multiphonon background diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.ui b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.ui index ffb7f469a51bbad08f906c1b7223dee1899ba638..54352c828084ea77d1a82b8010d516ef601b7b4e 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.ui +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/DynamicPDF/SliceSelector.ui @@ -95,7 +95,7 @@ </widget> </item> <item> - <widget class="MantidQt::MantidWidgets::MWSpectrogram" name="slices2DPlot"/> + <widget class="MantidQt::MantidWidgets::MWView" name="slices2DPlot"/> </item> </layout> </item> @@ -194,9 +194,9 @@ <container>1</container> </customwidget> <customwidget> - <class>MantidQt::MantidWidgets::MWSpectrogram</class> - <extends>QwtPlot</extends> - <header>MantidQtMantidWidgets/MWSpectrogram.h</header> + <class>MantidQt::MantidWidgets::MWView</class> + <extends>Qwidget</extends> + <header>MantidQtMantidWidgets/MWView.h</header> </customwidget> <customwidget> <class>MantidQt::MantidWidgets::DataSelector</class> diff --git a/MantidQt/CustomInterfaces/src/DynamicPDF/SliceSelector.cpp b/MantidQt/CustomInterfaces/src/DynamicPDF/SliceSelector.cpp index 03094760b56a122cee8fb04d224c8ba7eda23089..8130ebc96d64256b7c643f327e53ea6c49b333a9 100644 --- a/MantidQt/CustomInterfaces/src/DynamicPDF/SliceSelector.cpp +++ b/MantidQt/CustomInterfaces/src/DynamicPDF/SliceSelector.cpp @@ -1,11 +1,11 @@ #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" -// includes for the 2D view -#include "MantidQtMantidWidgets/SafeQwtPlot.h" #include <qwt_plot_spectrogram.h> //#include "MantidQtCustomInterfaces/DynamicPDF/BackgroundRemover.h" @@ -41,8 +41,7 @@ DECLARE_SUBWINDOW(SliceSelector) // SliceSelector::SliceSelector(QWidget *parent) : UserSubWindow{parent}, // m_loadedWorkspace{nullptr}, m_BackgroundRemover{nullptr} { SliceSelector::SliceSelector(QWidget *parent) - : UserSubWindow{parent}, m_plot2D{nullptr}, - m_loadedWorkspace{nullptr} {} + : UserSubWindow{parent}, m_loadedWorkspace{nullptr} {} SliceSelector::~SliceSelector() { m_selectedWorkspaceIndex = 0; } @@ -60,7 +59,7 @@ void SliceSelector::initLayout() { SLOT(launchBackgroundRemover())); } -/// +/// Load file or workspace containing energy slices void SliceSelector::loadSlices(const QString &workspaceName) { m_loadedWorkspace = boost::make_shared<WorkspaceRecord>(workspaceName.toStdString()); @@ -84,10 +83,8 @@ void SliceSelector::loadSlices(const QString &workspaceName) { m_uiForm.sliderSelectSlice->setMaximum(maximumWorkspaceIndex); m_uiForm.spinboxSliceSelector->setValue(0); - /// initialize the 2D view of the histogram; - m_plot2D = m_uiForm.slices2DPlot; - m_plot2D->setWorkspace(m_loadedWorkspace->m_ws); - m_plot2D->updateDisplay(); + /// initialize the 2D view of the slices; + /// initialize the preview plot updatePlotSelectedSlice(); @@ -124,10 +121,10 @@ void SliceSelector::launchBackgroundRemover() { //} // m_BackgroundRemover->refreshSlice(m_loadedWorkspace, // m_selectedWorkspaceIndex); - std::cout << "Hello world"; + g_log.error("Not implemented...yet"); } -/// Qt-help page +/// Opens the Qt help page for the interface void SliceSelector::showHelp() { MantidQt::API::HelpWindow::showCustomInterface( NULL, QString("Dynamic PDF Calculator")); diff --git a/MantidQt/MantidWidgets/CMakeLists.txt b/MantidQt/MantidWidgets/CMakeLists.txt index 0f3f1ec512460654050d42a2057382562e9a81b7..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 @@ -102,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 @@ -234,6 +236,7 @@ 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 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..45d737159e830d3285ad33625708503dfe6d7505 --- /dev/null +++ b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/ColorBarWidget.ui @@ -0,0 +1,95 @@ +<?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="margin"> + <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="autoScale"> + <property name="text"> + <string>Autoscale</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 index 7c61cc9feaec41be3ced238792c77aadb5b9201d..0ee3a80308a6377819807490edb14f65f63eca7a 100644 --- a/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.h +++ b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.h @@ -1,25 +1,31 @@ #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 "WidgetDllOption.h" - -#include <qwt_plot.h> -#include <qpainter.h> -#include "qwt_text.h" -#include <qwt_plot_spectrogram.h> -#include <qwt_raster_data.h> +#include "MantidAPI/IMDWorkspace.h" +namespace Mantid{ +namespace API{ +class MWDimension; +} +} namespace MantidQt { -namespace API { -class QwtRasterDataMW; +namespace API{ +class QwtRasterDataMD; +class MdSettings; } namespace MantidWidgets { - +class ColorBarWidget; /** A viewer for a Matrix Workspace. * @@ -56,11 +62,38 @@ 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); -protected: +signals: + /// Signal emitted when someone uses setWorkspace() on MWView + void workspaceChanged(); -private: +public slots: + void colorRangeChangedSlot(); + void loadColorMapSlot(); + void setTransparentZerosSlot(bool transparent); +private: + void initLayout(); + void loadSettings(); + void saveSettings(); + void updateDisplay(); + void checkRangeLimits(); + + 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; }; diff --git a/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.ui b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.ui index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9efd8d068c878bf36d9da5dc7622d6e6ee27554e 100644 --- a/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.ui +++ b/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MWView.ui @@ -0,0 +1,56 @@ +<?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>941</width> + <height>718</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>911</width> + <height>691</height> + </rect> + </property> + <layout class="QHBoxLayout" name="viewer"> + <item> + <widget class="MantidQt::MantidWidgets::SafeQwtPlot" name="plot2D"/> + </item> + <item> + <widget class="MantidQt::MantidWidgets::ColorBarWidget" name="colorBar" native="true"/> + </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 index ea3a6bbb10f74c4f29f57d4385291ebc15dc9c4e..eb1e97bb9251ffd8de986cbd438dcd6c1712c748 100644 --- a/MantidQt/MantidWidgets/src/MWView.cpp +++ b/MantidQt/MantidWidgets/src/MWView.cpp @@ -1,16 +1,181 @@ #include "MantidQtMantidWidgets/MWView.h" +// includes for workspace handling #include "MantidAPI/MatrixWorkspace.h" -#include "MantidKernel/System.h" -#include "MantidKernel/ReadLock.h" - +#include "MantidGeometry/MDGeometry/MDTypes.h" +// includes for interface development #include "MantidQtAPI/QwtRasterDataMD.h" #include "MantidQtAPI/MantidColorMap.h" #include <qwt_color_map.h> +namespace { +Mantid::Kernel::Logger g_log("MWView"); +} + namespace MantidQt { namespace MantidWidgets { +// ++++++++++++++++++++++++++++++++ +// ++++++++ Public members ++++++++ +// ++++++++++++++++++++++++++++++++ + +MWView::MWView(QWidget *parent) + : QWidget(parent), m_mdSettings{nullptr}, m_workspace{nullptr} { + m_spect = new QwtPlotSpectrogram(); + m_data = new MantidQt::API::QwtRasterDataMD(); + 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(); + m_data->setWorkspace(ws); + m_uiForm.plot2D->setWorkspace(ws); + emit workspaceChanged(); +} + + +// ++++++++++++++++++++++++++++++++ +// ++++++++ 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)); +} + +void MWView::updateDisplay() { + if (!m_workspace) + return; + m_data->setRange(m_uiForm.colorBar->getViewRange()); + m_spect->setData(*m_data); + m_spect->itemChanged(); + m_uiForm.plot2D->replot(); +} +// 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()); + } +} } // namespace MantidQt } // namespace MantidWidgets