Commit e7ed6c02 authored by Federico Montesino Pouzols's avatar Federico Montesino Pouzols
Browse files

Merge pull request #15690 from mantidproject/15681_ALC_emptyData

ALC: fix crash when no baseline fitted
parents 5592ac05 5394fa51
......@@ -61,17 +61,23 @@ namespace CustomInterfaces
void onDataChanged();
/// Executed when user clicks "Plot guess"
void onPlotGuess();
void onPlotGuessClicked();
// Remove plots from graph
private:
/// Plot guess on graph
bool plotGuessOnGraph();
/// Remove plots from graph
void removePlots();
private:
/// Associated view
IALCPeakFittingView* const m_view;
/// Associated model
IALCPeakFittingModel* const m_model;
/// Whether guess is currently plotted
bool m_guessPlotted;
};
......
......@@ -60,10 +60,6 @@ namespace CustomInterfaces
IPeakFunction_const_sptr peakPicker() const override;
void emitFitRequested();
public:
/// Clear guess when a fit is ready
void clearGuess() override;
public slots:
void initialize() override;
......@@ -78,6 +74,7 @@ namespace CustomInterfaces
void displayError(const QString &message) override;
void help() override;
void plotGuess() override;
void changePlotGuessState(bool plotted) override;
// -- End of IALCPeakFitting interface ---------------------------------------------------------
private:
......@@ -95,9 +92,6 @@ namespace CustomInterfaces
/// Peak picker tool - only one on the plot at any given moment
MantidWidgets::PeakPicker* m_peakPicker;
/// Whether the guess is currently plotted on the graph
bool m_guessPlotted;
};
......
......@@ -66,6 +66,9 @@ namespace CustomInterfaces
/// Signal to inform that data was set
void dataChanged();
/// Signal to inform presenter of an error with fitting
void errorInModel(const QString &message);
};
......
......@@ -56,9 +56,6 @@ namespace CustomInterfaces
/// @return A peak currently represented by the peak picker
virtual IPeakFunction_const_sptr peakPicker() const = 0;
/// Clears any guess from the plot
virtual void clearGuess() = 0;
public slots:
/// Performs any necessary initialization
virtual void initialize() = 0;
......@@ -100,9 +97,12 @@ namespace CustomInterfaces
/// Opens the Mantid Wiki web page
virtual void help() = 0;
/// Changes button text and emits signal
/// Emits signal
virtual void plotGuess() = 0;
/// Changes button state
virtual void changePlotGuessState(bool plotted) = 0;
signals:
/// Request to perform peak fitting
void fitRequested();
......@@ -117,10 +117,7 @@ namespace CustomInterfaces
void parameterChanged(const QString &funcIndex, const QString &paramName);
/// Request to plot guess
void plotGuessRequested();
/// Request to remove guess from plot
void removeGuessRequested();
void plotGuessClicked();
};
} // namespace CustomInterfaces
......
......@@ -67,7 +67,9 @@ namespace CustomInterfaces
QCoreApplication::processEvents();
}
if (!result.error().empty()) {
throw std::runtime_error(result.error());
QString msg =
"Fit algorithm failed.\n\n" + QString(result.error().c_str()) + "\n";
emit errorInModel(msg);
}
m_data = fit->getProperty("OutputWorkspace");
......
......@@ -11,35 +11,37 @@ namespace MantidQt
namespace CustomInterfaces
{
ALCPeakFittingPresenter::ALCPeakFittingPresenter(IALCPeakFittingView* view, IALCPeakFittingModel* model)
: m_view(view), m_model(model)
{}
void ALCPeakFittingPresenter::initialize()
{
m_view->initialize();
connect(m_view, SIGNAL(fitRequested()), SLOT(fit()));
connect(m_view, SIGNAL(currentFunctionChanged()), SLOT(onCurrentFunctionChanged()));
connect(m_view, SIGNAL(peakPickerChanged()), SLOT(onPeakPickerChanged()));
// We are updating the whole function anyway, so paramName if left out
connect(m_view, SIGNAL(parameterChanged(QString,QString)), SLOT(onParameterChanged(QString)));
connect(m_model, SIGNAL(fittedPeaksChanged()), SLOT(onFittedPeaksChanged()));
connect(m_model, SIGNAL(dataChanged()), SLOT(onDataChanged()));
connect(m_view, SIGNAL(plotGuessRequested()), SLOT(onPlotGuess()));
connect(m_view, SIGNAL(removeGuessRequested()), SLOT(removePlots()));
ALCPeakFittingPresenter::ALCPeakFittingPresenter(IALCPeakFittingView *view,
IALCPeakFittingModel *model)
: m_view(view), m_model(model), m_guessPlotted(false) {}
void ALCPeakFittingPresenter::initialize() {
m_view->initialize();
connect(m_view, SIGNAL(fitRequested()), SLOT(fit()));
connect(m_view, SIGNAL(currentFunctionChanged()),
SLOT(onCurrentFunctionChanged()));
connect(m_view, SIGNAL(peakPickerChanged()), SLOT(onPeakPickerChanged()));
// We are updating the whole function anyway, so paramName if left out
connect(m_view, SIGNAL(parameterChanged(QString, QString)),
SLOT(onParameterChanged(QString)));
connect(m_model, SIGNAL(fittedPeaksChanged()), SLOT(onFittedPeaksChanged()));
connect(m_model, SIGNAL(dataChanged()), SLOT(onDataChanged()));
connect(m_view, SIGNAL(plotGuessClicked()), SLOT(onPlotGuessClicked()));
connect(m_model, SIGNAL(errorInModel(const QString &)), m_view,
SLOT(displayError(const QString &)));
}
void ALCPeakFittingPresenter::fit()
{
void ALCPeakFittingPresenter::fit() {
IFunction_const_sptr func = m_view->function("");
if ( func ) {
m_view->clearGuess();
auto dataWS = m_model->data();
if (func && dataWS) {
removePlots();
m_model->fitPeaks(func);
} else {
m_view->displayError("Couldn't fit an empty function");
m_view->displayError("Couldn't fit with empty function/data");
}
}
......@@ -95,38 +97,63 @@ namespace CustomInterfaces
}
}
void ALCPeakFittingPresenter::onFittedPeaksChanged()
{
if(IFunction_const_sptr fittedPeaks = m_model->fittedPeaks())
{
auto x = m_model->data()->readX(0);
m_view->setFittedCurve(*(ALCHelper::curveDataFromFunction(fittedPeaks, x)));
void ALCPeakFittingPresenter::onFittedPeaksChanged() {
IFunction_const_sptr fittedPeaks = m_model->fittedPeaks();
auto dataWS = m_model->data();
if (fittedPeaks && dataWS) {
auto x = dataWS->readX(0);
m_view->setFittedCurve(
*(ALCHelper::curveDataFromFunction(fittedPeaks, x)));
m_view->setFunction(fittedPeaks);
}
else
{
} else {
m_view->setFittedCurve(*(ALCHelper::emptyCurveData()));
m_view->setFunction(IFunction_const_sptr());
}
}
void ALCPeakFittingPresenter::onDataChanged()
{
m_view->setDataCurve(*(ALCHelper::curveDataFromWs(m_model->data(), 0)),
ALCHelper::curveErrorsFromWs(m_model->data(), 0));
void ALCPeakFittingPresenter::onDataChanged() {
auto dataWS = m_model->data();
if (dataWS) {
m_view->setDataCurve(*(ALCHelper::curveDataFromWs(m_model->data(), 0)),
ALCHelper::curveErrorsFromWs(m_model->data(), 0));
} else {
m_view->setDataCurve(*(ALCHelper::emptyCurveData()), Mantid::MantidVec{});
}
}
/**
* Called when user clicks "Plot guess" on the view.
* Plots the current guess fit on the graph.
* Called when user clicks "Plot/Remove guess" on the view.
* Plots the current guess fit on the graph, or removes it.
*/
void ALCPeakFittingPresenter::onPlotGuess() {
if (auto func = m_view->function("")) {
auto xdata = m_model->data()->readX(0);
m_view->setFittedCurve(*(ALCHelper::curveDataFromFunction(func, xdata)));
} else {
void ALCPeakFittingPresenter::onPlotGuessClicked() {
if (m_guessPlotted) {
removePlots();
} else {
if (plotGuessOnGraph()) {
m_view->changePlotGuessState(true);
m_guessPlotted = true;
} else {
m_view->displayError("Couldn't plot with empty function/data");
removePlots();
}
}
}
/**
* Plots current guess on the graph, if possible
* Not possible if function or data are null
* @returns :: success or failure
*/
bool ALCPeakFittingPresenter::plotGuessOnGraph() {
bool plotted = false;
auto func = m_view->function("");
auto dataWS = m_model->data();
if (func && dataWS) {
auto xdata = dataWS->readX(0);
m_view->setFittedCurve(*(ALCHelper::curveDataFromFunction(func, xdata)));
plotted = true;
}
return plotted;
}
/**
......@@ -134,6 +161,8 @@ namespace CustomInterfaces
*/
void ALCPeakFittingPresenter::removePlots() {
m_view->setFittedCurve(*(ALCHelper::emptyCurveData()));
m_view->changePlotGuessState(false);
m_guessPlotted = false;
}
} // namespace CustomInterfaces
......
......@@ -15,7 +15,7 @@ namespace CustomInterfaces
ALCPeakFittingView::ALCPeakFittingView(QWidget *widget)
: m_widget(widget), m_ui(), m_dataCurve(new QwtPlotCurve()),
m_fittedCurve(new QwtPlotCurve()), m_dataErrorCurve(NULL),
m_peakPicker(NULL), m_guessPlotted(false) {}
m_peakPicker(NULL) {}
ALCPeakFittingView::~ALCPeakFittingView()
{
......@@ -150,38 +150,20 @@ void ALCPeakFittingView::displayError(const QString& message)
void ALCPeakFittingView::emitFitRequested() {
// Fit requested: reset "plot guess"
clearGuess();
emit fitRequested();
}
/**
* Clears fit guess and changes button text
* Emit signal that "plot/remove guess" has been clicked
*/
void ALCPeakFittingView::clearGuess() {
m_guessPlotted = false;
m_ui.plotGuess->setText("Plot guess");
}
void ALCPeakFittingView::plotGuess() { emit plotGuessClicked(); }
/**
* Changes the text on the "Plot guess" button and emits a signal
* to plot the guess on the graph
* Changes the text on the "Plot guess" button
* @param plotted :: [input] Whether guess is plotted or not
*/
void ALCPeakFittingView::plotGuess() {
QString buttonText("Plot guess");
if (m_guessPlotted) {
// Remove the plotted guess
emit removeGuessRequested();
m_guessPlotted = false;
} else {
// Plot the guess function, if there is one
if (function("") != nullptr) {
buttonText = "Remove guess";
emit plotGuessRequested();
m_guessPlotted = true;
}
}
m_ui.plotGuess->setText(buttonText);
void ALCPeakFittingView::changePlotGuessState(bool plotted) {
m_ui.plotGuess->setText(plotted ? "Remove guess" : "Plot guess");
}
} // namespace CustomInterfaces
......
......@@ -38,8 +38,7 @@ public:
{
emit parameterChanged(funcIndex, paramName);
}
void plotGuess() override { emit plotGuessRequested(); }
void clearGuess() override { emit removeGuessRequested(); }
void plotGuess() override { emit plotGuessClicked(); }
MOCK_CONST_METHOD1(function, IFunction_const_sptr(QString));
MOCK_CONST_METHOD0(currentFunctionIndex, boost::optional<QString>());
......@@ -54,6 +53,7 @@ public:
MOCK_METHOD3(setParameter, void(const QString&, const QString&, double));
MOCK_METHOD1(displayError, void(const QString&));
MOCK_METHOD0(help, void());
MOCK_METHOD1(changePlotGuessState, void(bool));
};
class MockALCPeakFittingModel : public IALCPeakFittingModel
......@@ -61,6 +61,7 @@ class MockALCPeakFittingModel : public IALCPeakFittingModel
public:
void changeFittedPeaks() { emit fittedPeaksChanged(); }
void changeData() { emit dataChanged(); }
void setError(const QString &message) { emit errorInModel(message); }
MOCK_CONST_METHOD0(fittedPeaks, IFunction_const_sptr());
MOCK_CONST_METHOD0(data, MatrixWorkspace_const_sptr());
......@@ -129,16 +130,22 @@ public:
presenter.initialize();
}
void test_fitEmptyFunction()
{
ON_CALL(*m_view, function(QString(""))).WillByDefault(Return(IFunction_const_sptr()));
EXPECT_CALL(*m_view, displayError(QString("Couldn't fit an empty function"))).Times(1);
void test_fitEmptyFunction() {
auto ws = WorkspaceCreationHelper::Create2DWorkspace123(1, 3);
ON_CALL(*m_model, data()).WillByDefault(Return(ws));
ON_CALL(*m_view, function(QString("")))
.WillByDefault(Return(IFunction_const_sptr()));
EXPECT_CALL(*m_view,
displayError(QString("Couldn't fit with empty function/data")))
.Times(1);
m_view->requestFit();
}
void test_fit()
{
void test_fit() {
auto ws = WorkspaceCreationHelper::Create2DWorkspace123(1, 3);
ON_CALL(*m_model, data()).WillByDefault(Return(ws));
IFunction_sptr peaks = createGaussian(1,2,3);
ON_CALL(*m_view, function(QString(""))).WillByDefault(Return(peaks));
......@@ -284,14 +291,6 @@ public:
m_view->help();
}
/**
* Test that clearing the guess from the view removes the fitted curve
*/
void test_clearGuess() {
EXPECT_CALL(*m_view, setFittedCurve(Property(&QwtData::size, 0)));
m_view->clearGuess();
}
/**
* Test that clicking "Plot guess" with no function set plots nothing
*/
......@@ -304,6 +303,18 @@ public:
m_view->plotGuess();
}
/**
* Test that clicking "Plot guess" with no data plots nothing
* (and doesn't crash)
*/
void test_plotGuess_noData() {
ON_CALL(*m_model, data()).WillByDefault(Return(nullptr));
IFunction_sptr peaks = createGaussian(1, 2, 3);
ON_CALL(*m_view, function(QString(""))).WillByDefault(Return(peaks));
EXPECT_CALL(*m_view, setFittedCurve(Property(&QwtData::size, 0)));
TS_ASSERT_THROWS_NOTHING(m_view->plotGuess());
}
/**
* Test that "Plot guess" with a function set plots a function
*/
......@@ -315,6 +326,23 @@ public:
EXPECT_CALL(*m_view, setFittedCurve(_));
m_view->plotGuess();
}
/**
* Test that plotting a guess, then clicking again, clears the guess
*/
void test_plotGuessAndThenClear() {
test_plotGuess();
EXPECT_CALL(*m_view, setFittedCurve(Property(&QwtData::size, 0)));
m_view->plotGuess(); // click again i.e. "Remove guess"
}
/**
* Test that errors coming from the model are displayed in the view
*/
void test_displayError() {
EXPECT_CALL(*m_view, displayError(QString("Test error")));
m_model->setError("Test error");
}
};
#endif /* MANTIDQT_CUSTOMINTERFACES_ALCPEAKFITTINGTEST_H_ */
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment