diff --git a/MantidQt/CustomInterfaces/CMakeLists.txt b/MantidQt/CustomInterfaces/CMakeLists.txt index ada8021d700646e914d4bbe412a8e155037d3058..be571e64c996a33e8e2b1b6535783bbb32c3a6e6 100644 --- a/MantidQt/CustomInterfaces/CMakeLists.txt +++ b/MantidQt/CustomInterfaces/CMakeLists.txt @@ -90,7 +90,11 @@ set ( SRC_FILES src/SANSPlotSpecial.cpp src/SANSRunWindow.cpp src/StepScan.cpp + src/Tomography/ImageROIPresenter.cpp + src/Tomography/ImageROIViewQtWidget.cpp + src/Tomography/ImageStackPreParams.cpp src/Tomography/SavuConfigDialog.cpp + src/Tomography/StackOfImagesDirs.cpp src/Tomography/TomographyIfaceModel.cpp src/Tomography/TomographyIfacePresenter.cpp src/Tomography/TomographyIfaceViewQtGUI.cpp @@ -205,8 +209,14 @@ set ( INC_FILES inc/MantidQtCustomInterfaces/SANSPlotSpecial.h inc/MantidQtCustomInterfaces/SANSRunWindow.h inc/MantidQtCustomInterfaces/StepScan.h + inc/MantidQtCustomInterfaces/Tomography/IImageROIPresenter.h + inc/MantidQtCustomInterfaces/Tomography/IImageROIView.h + inc/MantidQtCustomInterfaces/Tomography/ImageROIPresenter.h + inc/MantidQtCustomInterfaces/Tomography/ImageROIViewQtWidget.h + inc/MantidQtCustomInterfaces/Tomography/ImageStackPreParams.h inc/MantidQtCustomInterfaces/Tomography/ITomographyIfacePresenter.h inc/MantidQtCustomInterfaces/Tomography/ITomographyIfaceView.h + inc/MantidQtCustomInterfaces/Tomography/StackOfImagesDirs.h inc/MantidQtCustomInterfaces/Tomography/TomographyIfacePresenter.h inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceViewQtGUI.h inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceModel.h @@ -308,6 +318,7 @@ set ( MOC_FILES inc/MantidQtCustomInterfaces/Background.h inc/MantidQtCustomInterfaces/SANSEventSlicing.h inc/MantidQtCustomInterfaces/SANSDiagnostics.h inc/MantidQtCustomInterfaces/StepScan.h + inc/MantidQtCustomInterfaces/Tomography/ImageROIViewQtWidget.h inc/MantidQtCustomInterfaces/Tomography/TomographyIfacePresenter.h inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceViewQtGUI.h inc/MantidQtCustomInterfaces/Tomography/TomoToolConfigDialog.h @@ -370,6 +381,7 @@ set ( UI_FILES inc/MantidQtCustomInterfaces/DataComparison.ui inc/MantidQtCustomInterfaces/SANSRunWindow.ui inc/MantidQtCustomInterfaces/SANSEventSlicing.ui inc/MantidQtCustomInterfaces/StepScan.ui + inc/MantidQtCustomInterfaces/Tomography/ImageSelectCoRAndRegions.ui inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceQtGUI.ui inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceQtTabSetup.ui inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceQtTabRun.ui @@ -386,12 +398,14 @@ set ( TEST_FILES ALCPeakFittingModelTest.h ALCPeakFittingPresenterTest.h EnggDiffractionPresenterTest.h + ImageROIPresenterTest.h IO_MuonGroupingTest.h MuonAnalysisHelperTest.h ParseKeyValueStringTest.h ReflGenerateNotebookTest.h ReflLegacyTransferStrategyTest.h ReflMainViewPresenterTest.h + StackOfImagesDirsTest.h TomographyIfacePresenterTest.h UserInputValidatorTest.h ) diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/IImageROIPresenter.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/IImageROIPresenter.h new file mode 100644 index 0000000000000000000000000000000000000000..581accf1509477c8aa554e2bd2d128822391906e --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/IImageROIPresenter.h @@ -0,0 +1,77 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IIMAGEROIPRESENTER_H_ +#define MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IIMAGEROIPRESENTER_H_ + +namespace MantidQt { +namespace CustomInterfaces { + +/** +Presenter for the widget that handles the selection of the center of +rotation, region of interest, region for normalization, etc. from an +image or stack of images. This is the abstract base class / interface +for the presenter (in the sense of the MVP pattern). The name +ImageROI refers to the Center-of-Rotation, which is the most basic +parameter that users can select via this widget. This class is +QtGUI-free as it uses the interface of the view. The model is simply +the ImageStackPreParams class which holds coordinates selected by the +user. + +Copyright © 2015 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 IImageROIPresenter { + +public: + IImageROIPresenter(){}; + virtual ~IImageROIPresenter(){}; + + /// These are user actions, triggered from the (passive) view, that need + /// handling by the presenter + enum Notification { + Init, ///< interface is initing (set, defaults, etc.) + BrowseImgOrStack, ///< User browses for an image file or stack + NewImgOrStack, ///< A new image or stack needs to be loaded + UpdateImgIndex, ///< Sliding/scrolling through the stack + SelectCoR, ///< Start picking of the center of rotation + SelectROI, ///< Start selection of the region of interest + SelectNormalization, ///< Start selection of the normalization region + FinishedCoR, ///< A CoR has been picked + FinishedROI, ///< The ROI is selected + FinishedNormalization,///< The normalization region is selected + ResetCoR, ///< Reset CoR to default/none/middle + ResetROI, ///< Reset ROI to default/empty + ResetNormalization, ///< Reet the normalization region to default/empty + ShutDown ///< The widget is being closed/destroyed + }; + + /** + * Notifications sent through the presenter when something changes + * in the view. This plays the role of signals emitted by the view + * to this presenter. + * + * @param notif Type of notification to process. + */ + virtual void notify(IImageROIPresenter::Notification notif) = 0; +}; + +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif // MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IIMAGEROIPRESENTER_H_ diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/IImageROIView.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/IImageROIView.h new file mode 100644 index 0000000000000000000000000000000000000000..592dc79a3a1d4e828bf35f1c7c6311257c7d4bb7 --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/IImageROIView.h @@ -0,0 +1,216 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IIMAGEROIVIEW_H_ +#define MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IIMAGEROIVIEW_H_ + +#include "MantidAPI/WorkspaceGroup_fwd.h" +#include "MantidQtCustomInterfaces/Tomography/ImageStackPreParams.h" + +namespace MantidQt { +namespace CustomInterfaces { + +/** +Widget to handle the selection of the center of rotation, region of +interest, region for normalization, etc. from an image or stack of +images. This is the abstract base class / interface for the view of +this widget (in the sense of the MVP pattern). The name ImageROI +refers to the Center-of-Rotation, which is the most basic parameter +that users can select via this widget. This class is Qt-free. Qt +specific functionality and dependencies are added in a class derived +from this. + +Copyright © 2015 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 IImageROIView { + +public: + IImageROIView(){}; + virtual ~IImageROIView(){}; + + // Selection states + enum SelectionState { + SelectNone, ///< Init, or after any reset + SelectCoR, + SelectROIFirst, + SelectROISecond, + SelectNormAreaFirst, + SelectNormAreaSecond + }; + + /** + * Sets the user selection. This should guarantee that all widgets + * are updated (including spin boxes, image, slider through the + * image stack, etc. + * + * @param params all user-modifyable parameters (coordinates for the + * CoR, ROI and area for normalization). + * + */ + virtual void setParams(ImageStackPreParams ¶ms) = 0; + + /** + * Provides the current user selection. + * + * @return parameters as set/edited by the user. + */ + virtual ImageStackPreParams userSelection() const = 0; + + /** + * The current selection state. For example: nothin/initialized, + * selecting CoR, selecting second corner of the normalization area, + * selecting first corner of the ROI. + * + * @return current state + */ + virtual SelectionState selectionState() const = 0; + + /** + * Update to a new state (for example select CoR). + * + * @param state new state we're transitioning into. + */ + virtual void changeSelectionState(const SelectionState& state) = 0; + + /** + * Display a special case of stack of images: individual image, from + * a path to a recognized directory structure (sample/dark/white) or + * image format. Here recognized format means something that is + * supported natively by the widgets library, in practice + * Qt. Normally you can expect that .tiff and .png images are + * supported. + * + * @param path path to the stack (directory) or individual image file. + */ + virtual void showStack(const std::string &path) = 0; + + /** + * Display a stack of images (or individual image as a particular + * case), from a workspace group containing matrix workspaces. It + * assumes that the workspace contains an image in the form in which + * LoadFITS loads FITS images (or spectrum per row, all of them with + * the same number of data points (columns)). + * + * @param ws Workspace group where every workspace is a FITS or + * similar image that has been loaded with LoadFITS or similar + * algorithm. + */ + virtual void showStack(Mantid::API::WorkspaceGroup_sptr &ws) = 0; + + /** + * Get the stack of images currently being displayed (it has been + * shown using showStack()), as a workspace group. + * + * @return workspace group containing the individual images, which + * can be empty if no stack has been loaded. + */ + virtual const Mantid::API::WorkspaceGroup_sptr stack() const = 0; + + /** + * Normally one image (projection for tomography stacks) will be + * shown on a 2D display. Show there a particular projection from a + * stack contained in a workspace group. + * + * @param wsg workspace holding a stack of images + * + * @param idx index (in the group) of the image to show + * + */ + virtual void showProjection(const Mantid::API::WorkspaceGroup_sptr &wsg, + size_t idx) = 0; + + /** + * Display a warning to the user (for example as a pop-up window). + * + * @param warn warning title, should be short and would normally be + * shown as the title of the window or a big banner. + * + * @param description longer, free form description of the issue. + */ + virtual void userWarning(const std::string &warn, + const std::string &description) = 0; + + /** + * Display an error message (for example as a pop-up window). + * + * @param err Error title, should be short and would normally be + * shown as the title of the window or a big banner. + * + * @param description longer, free form description of the issue. + */ + virtual void userError(const std::string &err, + const std::string &description) = 0; + + /** + * The index of the image currently shown (from the current stack if there's + * any). + * + * @return index from 0 to the total number of images in the + * stack-1, as used for example when indexing workspaces in + * workspacegroups + */ + virtual size_t currentImgIndex() const = 0; + + /** + * Display now this image (idx) from the stack. + * + * @param idx index of the image to display. + */ + virtual void updateImgWithIndex(size_t idx) = 0; + + /** + * Get the path/location of a stack of images (or single image as a + * particular case) that the user is requesting to display. The + * path would be expected to point to a recognized directory + * structure (sample/dark/white) or image file (as a particular + * case). + * + * @return location (can be a directory, file, etc.) that needs to + * be figured out elsewhere. + */ + virtual std::string askImgOrStackPath() = 0; + + /** + * Save settings (normally when closing this widget). + */ + virtual void saveSettings() const = 0; + + /** + * Forget the current center-of-rotation selection and set to + * default. + */ + virtual void resetCoR() = 0; + + /** + * Forget the current region-of-interest selection and set to + * default (all). + */ + virtual void resetROI() = 0; + + /** + * Forget the current selection of region-for-normalization and set + * to default (none). + */ + virtual void resetNormArea() = 0; +}; + +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif // MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IIMAGEROIVIEW_H_ diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ITomographyIfacePresenter.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ITomographyIfacePresenter.h index 77f46747a077d353afc005a2a3bbe7461230e8e3..be9585bc25ee1d01ede65b98fdd416566c7aa4d4 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ITomographyIfacePresenter.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ITomographyIfacePresenter.h @@ -9,7 +9,7 @@ Interface for what the presenter of the tomography GUI needs to implement. Here the term presenter is used as in the MVP (Model-View-Presenter) pattern. The (passive) view will use this. -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ITomographyIfaceView.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ITomographyIfaceView.h index 3802a4b2b8cece79b1dc96f4b9dc12c18349e24c..0130f027370db9e29626b780bc78c2029c46b804 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ITomographyIfaceView.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ITomographyIfaceView.h @@ -15,7 +15,7 @@ Tomography GUI. Base class / interface for the view of the tomo GUI specific functionality/dependencies are added in a class derived from this. -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageROIPresenter.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageROIPresenter.h new file mode 100644 index 0000000000000000000000000000000000000000..7aaeac9147ec95b82725d83a6b0bef36ccac27ae --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageROIPresenter.h @@ -0,0 +1,95 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IMAGEROIPRESENTER_H_ +#define MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IMAGEROIPRESENTER_H_ + +#include "MantidAPI/WorkspaceGroup_fwd.h" +#include "MantidQtCustomInterfaces/Tomography/IImageROIPresenter.h" +#include "MantidQtCustomInterfaces/Tomography/IImageROIView.h" +#include "MantidQtCustomInterfaces/Tomography/ImageStackPreParams.h" +#include "MantidQtCustomInterfaces/Tomography/StackOfImagesDirs.h" + +#include <boost/scoped_ptr.hpp> + +namespace MantidQt { +namespace CustomInterfaces { + +/** +Presenter for the image center of rotation (and other parameters) +selection widget. In principle, in a strict MVP setup, signals from +the model should always be handled through this presenter and never go +directly to the view, and viceversa. + +Copyright © 2015 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 DLLExport ImageROIPresenter : public IImageROIPresenter { + +public: + /// Default constructor - normally used from the concrete view + ImageROIPresenter(IImageROIView *view); + virtual ~ImageROIPresenter(); + + void notify(IImageROIPresenter::Notification notif); + +protected: + void initialize(); + + /// clean shut down of model, view, etc. + void cleanup(); + + void processInit(); + void processBrowseImg(); + void processNewStack(); + void processUpdateImgIndex(); + void processSelectCoR(); + void processSelectROI(); + void processSelectNormalization(); + void processFinishedCoR(); + void processFinishedROI(); + void processFinishedNormalization(); + void processResetCoR(); + void processResetROI(); + void processResetNormalization(); + void processShutDown(); + +private: + StackOfImagesDirs checkInputStack(const std::string &path); + + /// loads a list of images from a stack, from their individual paths + Mantid::API::WorkspaceGroup_sptr + loadFITSStack(const std::vector<std::string> &imgs); + + void loadFITSImage(const std::string &path, const std::string &wsName); + + /// path to the image stack being visualized + std::string m_stackPath; + + /// Associated view for this presenter (MVP pattern) + IImageROIView *const m_view; + + /// Associated model for this presenter (MVP pattern). This is just + /// a set of coordinates + const boost::scoped_ptr<ImageStackPreParams> m_model; +}; + +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif // MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IMAGEROIPRESENTER_H_ diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageROIViewQtWidget.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageROIViewQtWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..739374aed282d5b19cf3fb1d097b4a95bc583d3e --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageROIViewQtWidget.h @@ -0,0 +1,186 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IMAGEROIVIEWQTWIDGET_H_ +#define MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IMAGEROIVIEWQTWIDGET_H_ + +#include "MantidAPI/WorkspaceGroup_fwd.h" +#include "MantidKernel/System.h" +#include "MantidQtCustomInterfaces/Tomography/IImageROIPresenter.h" +#include "MantidQtCustomInterfaces/Tomography/IImageROIView.h" + +#include "ui_ImageSelectCoRAndRegions.h" + +#include <boost/scoped_ptr.hpp> + +// forward declarations for Qt +class QWidget; +class QPixmap; + +namespace MantidQt { +namespace CustomInterfaces { + +/** +Qt-based view of the widget to handle the selection of the center of +rotation, region of interest, region for normalization, etc. from an +image or stack of images. Provides a concrete view for the graphical +interface for tomography functionality in Mantid. This view is +Qt-based and it is probably the only one that will be implemented in a +foreseeable horizon. The interface of this class is given by +IImageROIView so that it fits in the MVP (Model-View-Presenter) design +of the ImageROI widget. + +Copyright © 2015 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 DLLExport ImageROIViewQtWidget : public QWidget, public IImageROIView { + Q_OBJECT + +public: + ImageROIViewQtWidget(QWidget *parent = 0); + virtual ~ImageROIViewQtWidget(){}; + + void setParams(ImageStackPreParams ¶ms); + + ImageStackPreParams userSelection() const; + + SelectionState selectionState() const { return m_selectionState; } + + void changeSelectionState(const SelectionState& state); + + /// show a stack of images given the path to the files + void showStack(const std::string &path); + + /// show a stack of images that have been loaded into a group of workspaces + void showStack(Mantid::API::WorkspaceGroup_sptr &ws); + + const Mantid::API::WorkspaceGroup_sptr stack() const { return m_stack; } + + void showProjection(const Mantid::API::WorkspaceGroup_sptr &wsg, size_t idx); + + void userWarning(const std::string &warn, const std::string &description); + + void userError(const std::string &err, const std::string &description); + + size_t currentImgIndex() const; + + void updateImgWithIndex(size_t idx); + + std::string askImgOrStackPath(); + + void saveSettings() const; + + void resetCoR(); + void resetROI(); + void resetNormArea(); + +protected: + void initLayout(); + void showImg(); + + /// update coordinates from mouse event + void mouseUpdateCoR(int x, int y); + void mouseUpdateROICorners12(int x, int y); + void mouseUpdateROICorner2(int x, int y); + void mouseFinishROI(int x, int y); + void mouseUpdateNormAreaCorners12(int x, int y); + void mouseUpdateNormAreaCorner2(int x, int y); + void mouseFinishNormArea(int x, int y); + +private slots: + void browseImgClicked(); + + void corClicked(); + void corResetClicked(); + void roiClicked(); + void roiResetClicked(); + void normAreaClicked(); + void normAreaResetClicked(); + + void updateFromImagesSlider(int current); + + void valueUpdatedCoR(int v); + void valueUpdatedROI(int v); + void valueUpdatedNormArea(int v); + +private: + void grabCoRFromWidgets(); + void grabROIFromWidgets(); + void grabNormAreaFromWidgets(); + + void grabCoRFromMousePoint(int x, int y); + void grabROICorner1FromMousePoint(int x, int y); + void grabROICorner2FromMousePoint(int x, int y); + void grabNormAreaCorner1FromMousePoint(int x, int y); + void grabNormAreaCorner2FromMousePoint(int x, int y); + + void setupConnections(); + + void readSettings(); + + // widget closing + virtual void closeEvent(QCloseEvent *ev); + + /// enable/disable the groups with spin boxes for the center and corners + void enableParamWidgets(bool enable); + /// initialize values to defaults and set max/min for the spin boxes + void initParamWidgets(size_t maxWidth, size_t maxHeight); + + /// Set coordinates in the widgets from a params object + void setParamWidgets(ImageStackPreParams ¶ms); + + // shows the image in a widget + void showProjectionImage(const Mantid::API::WorkspaceGroup_sptr &wsg, + size_t idx); + + /// repaint the image with new positions of points and rectangles + void refreshROIetAl(); + void refreshCoR(); + void refreshROI(); + void refreshNormArea(); + + bool eventFilter(QObject *obj, QEvent *event); + + Ui::ImageSelectCoRAndRegions m_ui; + + Mantid::API::WorkspaceGroup_sptr m_stack; + + /// this holds the base image on top of which rectangles and other + /// objects are drawn + boost::scoped_ptr<QPixmap> m_basePixmap; + + /// persistent settings + static const std::string m_settingsGroup; + + /// parameters currently set by the user + ImageStackPreParams m_params; + + /// max image size for the current stack + int m_imgWidth, m_imgHeight; + + /// are we picking the CoR, or the first point of the ROI, etc. + SelectionState m_selectionState; + + // presenter as in the model-view-presenter + boost::scoped_ptr<IImageROIPresenter> m_presenter; +}; + +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif // MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IMAGEROIVIEWQTWIDGET_H_ diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageSelectCoRAndRegions.ui b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageSelectCoRAndRegions.ui new file mode 100644 index 0000000000000000000000000000000000000000..c71dbfed6a89027b1f6ea39a849f4df738db4671 --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageSelectCoRAndRegions.ui @@ -0,0 +1,669 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ImageSelectCoRAndRegions</class> + <widget class="QWidget" name="ImageSelectCoRAndRegions"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout_8"> + <item row="0" column="0"> + <widget class="QSplitter" name="splitter_main_horiz"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QSplitter" name="splitter_img_vertical"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>2</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QWidget" name="layoutWidget"> + <layout class="QGridLayout" name="gridLayout_5"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label_img_seq"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>28</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>28</height> + </size> + </property> + <property name="text"> + <string>Image:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="lineEdit_img_seq"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>100</width> + <height>28</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>100</width> + <height>28</height> + </size> + </property> + <property name="text"> + <string>0/0</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="label_img_name"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>28</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>28</height> + </size> + </property> + <property name="text"> + <string>none</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QPushButton" name="pushButton_browse_img"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>28</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>28</height> + </size> + </property> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="layoutWidget"> + <layout class="QGridLayout" name="gridLayout_7" rowstretch="0,1"> + <property name="sizeConstraint"> + <enum>QLayout::SetMaximumSize</enum> + </property> + <property name="spacing"> + <number>3</number> + </property> + <item row="0" column="0"> + <widget class="QScrollArea" name="scrollArea"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>2</horstretch> + <verstretch>2</verstretch> + </sizepolicy> + </property> + <property name="widgetResizable"> + <bool>true</bool> + </property> + <widget class="QWidget" name="scrollAreaWidgetContents"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>510</width> + <height>402</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>2</horstretch> + <verstretch>2</verstretch> + </sizepolicy> + </property> + <layout class="QGridLayout" name="gridLayout" rowstretch="0" columnstretch="0"> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>1</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label_img"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>2</horstretch> + <verstretch>2</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + </item> + <item row="1" column="0"> + <widget class="QScrollBar" name="horizontalScrollBar_img_stack"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>2</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="invertedControls"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <widget class="QWidget" name="layoutWidget_2"> + <layout class="QGridLayout" name="gridLayout_6" columnminimumwidth="0"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <property name="spacing"> + <number>4</number> + </property> + <item row="0" column="0"> + <widget class="QGroupBox" name="groupBox_cor"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>260</width> + <height>16777215</height> + </size> + </property> + <property name="title"> + <string>Center of rotation</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="margin"> + <number>3</number> + </property> + <property name="spacing"> + <number>4</number> + </property> + <item row="1" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Column:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spinBox_cor_x"> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>9999</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Row:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="spinBox_cor_y"> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>9999</number> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="pushButton_cor"> + <property name="toolTip"> + <string>After pushing this button, click on the image to select</string> + </property> + <property name="text"> + <string>Select</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>108</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_cor_reset"> + <property name="text"> + <string>Reset</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="0" column="0"> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>78</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Auto-find</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboBox"> + <property name="enabled"> + <bool>false</bool> + </property> + <item> + <property name="text"> + <string>Method:</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="1" column="0"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="0"> + <widget class="QGroupBox" name="groupBox_roi"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>260</width> + <height>16777215</height> + </size> + </property> + <property name="title"> + <string>Region of interest:</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <property name="margin"> + <number>3</number> + </property> + <property name="spacing"> + <number>2</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Corner 1</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="spinBox_roi_top_x"> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>9999</number> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QSpinBox" name="spinBox_roi_top_y"> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>9999</number> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Corner 2</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="spinBox_roi_bottom_x"> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>9999</number> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QSpinBox" name="spinBox_roi_bottom_y"> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>9999</number> + </property> + </widget> + </item> + <item row="2" column="0" colspan="3"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QPushButton" name="pushButton_roi"> + <property name="toolTip"> + <string>After pushing this button, click and drag on the image to select</string> + </property> + <property name="text"> + <string>Select</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>155</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_roi_reset"> + <property name="toolTip"> + <string>Reset to the whole field of view</string> + </property> + <property name="text"> + <string>Reset</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="3" column="0"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="4" column="0"> + <widget class="QGroupBox" name="groupBox_norm"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>260</width> + <height>16777215</height> + </size> + </property> + <property name="title"> + <string>Area for normalization:</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <property name="margin"> + <number>3</number> + </property> + <property name="spacing"> + <number>2</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Corner 1</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="spinBox_norm_top_x"> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>9999</number> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QSpinBox" name="spinBox_norm_top_y"> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>9999</number> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Corner 2</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="spinBox_norm_bottom_x"> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>9999</number> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QSpinBox" name="spinBox_norm_bottom_y"> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>9999</number> + </property> + </widget> + </item> + <item row="2" column="0" colspan="3"> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <item> + <widget class="QPushButton" name="pushButton_norm_area"> + <property name="toolTip"> + <string>After pushing this button, click and drag on the image to select</string> + </property> + <property name="text"> + <string>Select</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>98</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_norm_area_reset"> + <property name="toolTip"> + <string>Reset to empty/none</string> + </property> + <property name="text"> + <string>Reset</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="5" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>68</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageStackPreParams.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageStackPreParams.h new file mode 100644 index 0000000000000000000000000000000000000000..164a3a475f7229f2b91fe6e9a82e15f102cb7065 --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ImageStackPreParams.h @@ -0,0 +1,63 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IMAGESTACKPREPARAMS_H_ +#define MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IMAGESTACKPREPARAMS_H_ + +#include "MantidKernel/System.h" +#include "MantidKernel/V2D.h" + +#include <utility> + +namespace MantidQt { +namespace CustomInterfaces { + +/** +This holds parameters for pre-processing images or stacks of images +for tomographic reconstruction. These parameters are used in different +pre-processing steps in the tomographic reconstruction pipeline, and +also for the reconstruction algorithms (iternative methods, FBP, +etc.). + +The parameters include: +- center of rotation +- region of interest (clip from original or raw images) +- region for normalization (where the beam is not blocked by any sample + object throughout the stack of images) other parameters describing + the stack of images: + +Copyright © 2014,2015 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 ImageStackPreParams { +public: + + ImageStackPreParams(); + + typedef std::pair<Mantid::Kernel::V2D, Mantid::Kernel::V2D> Box2D; + + Mantid::Kernel::V2D cor; + Box2D roi; + Box2D normalizationRegion; //< also known as 'air' region + bool medianFilter; +}; + +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif // MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_IMAGESTACKPREPARAMS_H_ diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/StackOfImagesDirs.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/StackOfImagesDirs.h new file mode 100644 index 0000000000000000000000000000000000000000..3ef122016ea93de03b22395c15ab647d62ea9324 --- /dev/null +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/StackOfImagesDirs.h @@ -0,0 +1,86 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_STACKOFIMAGESDIR_H_ +#define MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_STACKOFIMAGESDIR_H_ + +#include "MantidKernel/System.h" + +#include <string> +#include <vector> + +namespace MantidQt { +namespace CustomInterfaces { + +/** +Represents the structure of directories where a stack of images is +stored on disk: data/flat/dark + processed + pre_filtered, etc. + +Copyright © 2015 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 DLLExport StackOfImagesDirs { +public: + /// constructor from a path (to a directory) + StackOfImagesDirs(const std::string &path); + + /// The current (simple) concept of valid is: there at least a + /// sample/data directory with at least one file in it. + bool isValid() const { return m_valid; } + + // human readable description of the expected structure of directories + std::string description() const; + + // string that describes the status/error message about this directory + std::string status() const; + + std::string sampleImagesDir() const { return m_sampleDir; }; + std::string flatImagesDir() const { return m_flatDir; }; + std::string darkImagesDir() const { return m_darkDir; }; + + std::vector<std::string> sampleFiles() const; + std::vector<std::string> flatFiles() const; + std::vector<std::string> darkFiles() const; + +private: + /// tries to find the expected data_/dark_/flat_ directories + void findStackDirs(const std::string &path); + /// finds images in a directory + std::vector<std::string> findImgFiles(const std::string &path) const; + + /// passes basic validity checks + bool m_valid; + /// string with informative messages specially when not valid + std::string m_statusDescStr; + + std::string m_sampleDir; + std::string m_flatDir; + std::string m_darkDir; + + static const std::string g_descr; + static const std::string g_sampleNamePrefix; + static const std::string g_flatNamePrefix; + static const std::string g_darkNamePrefix; + static const std::string g_processedNamePrefix; + static const std::string g_prefilteredNamePrefix; +}; + +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif // MANTIDQTCUSTOMINTERFACES_TOMOGRAPHY_STACKOFIMAGESDIR_H_ diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoPathsConfig.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoPathsConfig.h index 92400342f4a0363d6d52588065ae557fc58994c3..ee3becb0206f8910ae585ab1f4cedae98c201d29 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoPathsConfig.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoPathsConfig.h @@ -18,7 +18,7 @@ that define where the data for a particular dataset can be found, and that are normally required to run reconstruction tools but also pre- and post- processing steps. -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoRecToolConfig.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoRecToolConfig.h index 1ff91bd3933bcec2ee31c120f04e8e814e78bc12..256518618dcb17ac301ae2871bf615424cfb1f02 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoRecToolConfig.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoRecToolConfig.h @@ -12,7 +12,7 @@ is under development, and as it is not necessarily related to custom interfaces this class and some derived ones might be moved out of here. -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoReconToolsUserSettings.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoReconToolsUserSettings.h index 25857af9e1a9827251c4b52c3453429c8837cb7a..a6047a19de1292643bf844af8fa30a6357b406a6 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoReconToolsUserSettings.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoReconToolsUserSettings.h @@ -13,7 +13,7 @@ namespace CustomInterfaces { /** Settings for a set of tomographic reconstruction tools supported. -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoToolConfigDialog.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoToolConfigDialog.h index 64cfc7a36aed82bdc7e6a1585e4b6895eb15ca0b..5196ca49bef979e5268b8dc9cbd68a5e1c20aca8 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoToolConfigDialog.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomoToolConfigDialog.h @@ -15,7 +15,7 @@ namespace CustomInterfaces { Third party tool configuration dialog(s) for the tomographic reconstruction GUI. -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceModel.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceModel.h index a915f0c9ed1f7e54df8fa5b8249154eaef3410f8..4bd8001060f092397a3dec2d71d888c863d8f279 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceModel.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceModel.h @@ -20,7 +20,7 @@ Tomography GUI. Model for the interface (as in the MVP signals from this model should always be handled through the presenter and never go directly to the view. -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfacePresenter.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfacePresenter.h index f37b5569d582d5b09be32ecb775cd754c73d6b22..ec29c07091b112cb2ab2b5de01325dacdce081f0 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfacePresenter.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfacePresenter.h @@ -23,7 +23,7 @@ Tomography GUI. Presenter for the GUI (as in the MVP signals from the model should always be handled through this presenter and never go directly to the view, and viceversa. -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceQtTabRun.ui b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceQtTabRun.ui index ecfc5ff939ec2a361cae319847285f9d52c70162..ea670ee8a7158f0eb58a5f606461f3633d73f3a1 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceQtTabRun.ui +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceQtTabRun.ui @@ -9,8 +9,8 @@ <rect> <x>0</x> <y>0</y> - <width>462</width> - <height>460</height> + <width>671</width> + <height>451</height> </rect> </property> <layout class="QGridLayout" name="gridLayout"> @@ -74,7 +74,7 @@ <item row="1" column="0"> <widget class="QFrame" name="frame_image"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>2</horstretch> <verstretch>2</verstretch> </sizepolicy> @@ -105,10 +105,16 @@ <rect> <x>0</x> <y>0</y> - <width>151</width> - <height>180</height> + <width>231</width> + <height>175</height> </rect> </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <layout class="QGridLayout" name="gridLayout_5"> <property name="margin"> <number>0</number> @@ -118,6 +124,12 @@ </property> <item row="0" column="0"> <widget class="QLabel" name="label_image"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> <string/> </property> diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceViewQtGUI.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceViewQtGUI.h index 010abf23fbeaea0ce1a7fe1b47d4a7fe60bbecf7..3f78f8feafe18f03d52db0af5d4eab26556e76b1 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceViewQtGUI.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/TomographyIfaceViewQtGUI.h @@ -9,6 +9,8 @@ #include "MantidQtCustomInterfaces/Tomography/ITomographyIfacePresenter.h" #include "MantidQtCustomInterfaces/Tomography/ITomographyIfaceView.h" #include "MantidQtCustomInterfaces/Tomography/TomoToolConfigDialog.h" + +#include "ui_ImageSelectCoRAndRegions.h" #include "ui_TomographyIfaceQtGUI.h" #include "ui_TomographyIfaceQtTabSetup.h" #include "ui_TomographyIfaceQtTabRun.h" @@ -30,7 +32,7 @@ in a foreseeable horizon. The interface of this class is given by ITomographyIfaceView so that it fits in the MVP (Model-View-Presenter) design of the tomography GUI. -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. @@ -176,8 +178,9 @@ private: void processPathBrowseClick(QLineEdit *le, std::string &data); // Begin of Savu related functionality. This will grow and will need - // separation - + // separation. They should find a better place to live. + ///@name Savu related methods + ///@{ /// to load plugins (savu classification / API) void loadAvailablePlugins(); @@ -201,6 +204,7 @@ private: const std::string &name); std::string pluginParamValString(const Json::Value &jsonVal, const std::string &name); + ///@} static size_t g_nameSeqNo; @@ -213,6 +217,7 @@ private: // but they could be separate dialogs, widgets, etc. Ui::TomographyIfaceQtTabSetup m_uiTabSetup; Ui::TomographyIfaceQtTabRun m_uiTabRun; + Ui::ImageSelectCoRAndRegions m_uiTabCoR; /// Tool specific setup dialogs Ui::TomoToolConfigAstra m_uiAstra; diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigAstraToolbox.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigAstraToolbox.h index 80d2b62a64138bcac14062a62289bb451a62e20e..8e4b19d2f7cdac14401455f43f7fbcf0e8c5bc86 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigAstraToolbox.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigAstraToolbox.h @@ -15,7 +15,7 @@ Configuration of a third party tomographic reconstruction tool specialized for the Astra Toolbox tomographic reconstruction tool (C++, CUDA): Astra Toolbox <http://sourceforge.net/p/astra-toolbox/wiki/Home/ -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. @@ -38,7 +38,7 @@ Code Documentation is available at: <http://doxygen.mantidproject.org> */ class DLLExport ToolConfigAstraToolbox : public TomoRecToolConfig { public: - ToolConfigAstraToolbox() { } + ToolConfigAstraToolbox(); ToolConfigAstraToolbox(const std::string &runnable, double centerRot, double angleMin, double angleMax, @@ -47,7 +47,7 @@ public: const std::string &pathOpen, const std::string &pathSample); - ~ToolConfigAstraToolbox() { } + ~ToolConfigAstraToolbox() {} protected: virtual std::string makeCmdLineOptions() const; diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigCustom.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigCustom.h index fe02d914dc2a9d2db490682fa9daf8e9fdaa845d..98c947dc793a6e7fed3e9f3297e0fe1da67ea057 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigCustom.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigCustom.h @@ -18,7 +18,7 @@ custom interfaces might be moved out of here. Tools of other type might be added, and then this should not be a sublcass of TomoRecToolConfig but of a more general ToolConfig class. -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. diff --git a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigTomoPy.h b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigTomoPy.h index 56eec90cbd50d02bc126f34a091192f029621a78..b3e9054938af5d0c8ecdf4a94751f6ab2628d65e 100644 --- a/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigTomoPy.h +++ b/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Tomography/ToolConfigTomoPy.h @@ -14,7 +14,7 @@ Third party tomographic reconstruction tool configuration class specialized for TomoPy (Python + C++): https://www1.aps.anl.gov/Science/Scientific-Software/TomoPy -Copyright © 2014,205 ISIS Rutherford Appleton Laboratory, NScD +Copyright © 2014,2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source This file is part of Mantid. @@ -37,14 +37,14 @@ Code Documentation is available at: <http://doxygen.mantidproject.org> */ class DLLExport ToolConfigTomoPy : public TomoRecToolConfig { public: - ToolConfigTomoPy() { } + ToolConfigTomoPy(); ToolConfigTomoPy(const std::string &runnable, const std::string &pathOut, const std::string &pathDark, const std::string &pathOpen, const std::string &pathSample, double centerRot, double angleMin, double angleMax); - ~ToolConfigTomoPy() { } + ~ToolConfigTomoPy() {} protected: virtual std::string makeCmdLineOptions() const; diff --git a/MantidQt/CustomInterfaces/src/Tomography/ImageROIPresenter.cpp b/MantidQt/CustomInterfaces/src/Tomography/ImageROIPresenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f411515c231575349bb8e05fc40899892918a63 --- /dev/null +++ b/MantidQt/CustomInterfaces/src/Tomography/ImageROIPresenter.cpp @@ -0,0 +1,305 @@ +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/WorkspaceGroup.h" +#include "MantidQtCustomInterfaces/Tomography/ImageStackPreParams.h" +#include "MantidQtCustomInterfaces/Tomography/ImageROIPresenter.h" +#include "MantidQtCustomInterfaces/Tomography/IImageROIView.h" + +using namespace MantidQt::CustomInterfaces; + +namespace MantidQt { +namespace CustomInterfaces { + +ImageROIPresenter::ImageROIPresenter(IImageROIView *view) + : m_view(view), m_model(new ImageStackPreParams()) { + if (!m_view) { + throw std::runtime_error("Severe inconsistency found. Presenter created " + "with an empty/null view (tomography interface). " + "Cannot continue."); + } +} + +ImageROIPresenter::~ImageROIPresenter() { cleanup(); } + +void ImageROIPresenter::cleanup() {} + +void ImageROIPresenter::notify(Notification notif) { + + switch (notif) { + + case IImageROIPresenter::Init: + processInit(); + break; + + case IImageROIPresenter::BrowseImgOrStack: + processBrowseImg(); + break; + + case IImageROIPresenter::NewImgOrStack: + processNewStack(); + break; + + case IImageROIPresenter::UpdateImgIndex: + processUpdateImgIndex(); + break; + + case IImageROIPresenter::SelectCoR: + processSelectCoR(); + break; + + case IImageROIPresenter::SelectROI: + processSelectROI(); + break; + + case IImageROIPresenter::SelectNormalization: + processSelectNormalization(); + break; + + case IImageROIPresenter::FinishedCoR: + processFinishedCoR(); + break; + + case IImageROIPresenter::FinishedROI: + processFinishedROI(); + break; + + case IImageROIPresenter::FinishedNormalization: + processFinishedNormalization(); + break; + + case IImageROIPresenter::ResetCoR: + processResetCoR(); + break; + + case IImageROIPresenter::ResetROI: + processResetROI(); + break; + + case IImageROIPresenter::ResetNormalization: + processResetNormalization(); + break; + + case IImageROIPresenter::ShutDown: + processShutDown(); + break; + } +} + +void ImageROIPresenter::processInit() { + ImageStackPreParams p; + m_view->setParams(p); +} + +void ImageROIPresenter::processBrowseImg() { + const std::string path = m_view->askImgOrStackPath(); + + if (path.empty()) + return; + + m_stackPath = path; + processNewStack(); +} + +/** + * Validates the input stack of images (directories and files), and + * shows warning/error messages as needed. The outocome of the + * validation can be checkec via isValid() on the returned stack of + * images object. + * + * @param path user provided path to the stack of images + * + * @return a stack of images built from the path passed, not + * necessarily correct (check with isValid()) + */ +StackOfImagesDirs ImageROIPresenter::checkInputStack(const std::string &path) { + StackOfImagesDirs soid(path); + + const std::string soiPath = soid.sampleImagesDir(); + if (soiPath.empty()) { + m_view->userWarning("Error trying to find a stack of images", + "Could not find the sample images directory. The stack " + "of images is expected as: \n\n" + + soid.description()); + } else if (!soid.isValid()) { + m_view->userWarning("Error while checking/validating the stack of images", + "The stack of images could not be loaded correctly. " + + soid.status()); + } + + return soid; +} + +void ImageROIPresenter::processNewStack() { + + StackOfImagesDirs soid(""); + try { + soid = checkInputStack(m_stackPath); + } catch (std::exception &e) { + // Poco::FileNotFoundException: this should never happen, unless + // the open dir dialog misbehaves unexpectedly, or in tests + m_view->userWarning("Error trying to open directories/files", + "The path selected via the dialog cannot be openend or " + "there was a problem while trying to access it. This " + "is an unexpected inconsistency. Error details: " + + std::string(e.what())); + } + + if (!soid.isValid()) + return; + + std::vector<std::string> imgs = soid.sampleFiles(); + if (0 >= imgs.size()) { + m_view->userWarning( + "Error trying to find image/projection files in the stack directories", + "Could not find any image file in the samples subdirectory: " + + soid.sampleImagesDir()); + return; + } + + Mantid::API::WorkspaceGroup_sptr wsg = loadFITSStack(imgs); + if (!wsg) + return; + + size_t imgCount = wsg->size(); + if (0 == imgCount) { + m_view->userWarning( + "Failed to load any FITS images - directory structure issue", + "Even though a directory apparently holding a stack of images was " + "found, " + "it was not possible to load any image file correctly from: " + + m_stackPath); + return; + } + + m_view->showStack(wsg); + + // clean-up container group workspace? Not for now + if (false && wsg) + Mantid::API::AnalysisDataService::Instance().remove(wsg->getName()); +} + +void ImageROIPresenter::processUpdateImgIndex() { + m_view->updateImgWithIndex(m_view->currentImgIndex()); +} + +void ImageROIPresenter::processSelectCoR() { + m_view->changeSelectionState(IImageROIView::SelectCoR); +} + +void ImageROIPresenter::processSelectROI() { + m_view->changeSelectionState(IImageROIView::SelectROIFirst); +} + +void ImageROIPresenter::processSelectNormalization() { + m_view->changeSelectionState(IImageROIView::SelectNormAreaFirst); +} + +void ImageROIPresenter::processFinishedCoR() { + m_view->changeSelectionState(IImageROIView::SelectNone); +} + +void ImageROIPresenter::processFinishedROI() { + m_view->changeSelectionState(IImageROIView::SelectNone); +} + +void ImageROIPresenter::processFinishedNormalization() { + m_view->changeSelectionState(IImageROIView::SelectNone); +} + +void ImageROIPresenter::processResetCoR() { + m_view->resetCoR(); + m_view->changeSelectionState(IImageROIView::SelectNone); +} + +void ImageROIPresenter::processResetROI() { + m_view->resetROI(); + m_view->changeSelectionState(IImageROIView::SelectNone); +} + +void ImageROIPresenter::processResetNormalization() { + m_view->resetNormArea(); + m_view->changeSelectionState(IImageROIView::SelectNone); +} + +void ImageROIPresenter::processShutDown() { m_view->saveSettings(); } + +Mantid::API::WorkspaceGroup_sptr +ImageROIPresenter::loadFITSStack(const std::vector<std::string> &imgs) { + const std::string wsName = "__stack_fits_viewer_tomography_gui"; + auto &ads = Mantid::API::AnalysisDataService::Instance(); + if (ads.doesExist(wsName)) { + ads.remove(wsName); + } + for (size_t i = 0; i < imgs.size(); ++i) { + loadFITSImage(imgs[i], wsName); + } + + Mantid::API::WorkspaceGroup_sptr wsg; + try { + wsg = ads.retrieveWS<Mantid::API::WorkspaceGroup>(wsName); + } catch (std::exception &e) { + throw std::runtime_error( + "Could not produce a workspace group for the stack images. Cannot " + "display it. Error details: " + + std::string(e.what())); + } + + if (wsg && + Mantid::API::AnalysisDataService::Instance().doesExist(wsg->name()) && + imgs.size() == wsg->size()) { + return wsg; + } else { + return Mantid::API::WorkspaceGroup_sptr(); + } +} + +void ImageROIPresenter::loadFITSImage(const std::string &path, + const std::string &wsName) { + // get fits file into workspace and retrieve it from the ADS + auto alg = Mantid::API::AlgorithmManager::Instance().create("LoadFITS"); + try { + alg->initialize(); + alg->setPropertyValue("Filename", path); + alg->setProperty("OutputWorkspace", wsName); + // this is way faster when loading into a MatrixWorkspace + alg->setProperty("LoadAsRectImg", true); + } catch (std::exception &e) { + throw std::runtime_error("Failed to initialize the mantid algorithm to " + "load images. Error description: " + + std::string(e.what())); + } + + try { + alg->execute(); + } catch (std::exception &e) { + throw std::runtime_error( + "Failed to load image. Could not load this file as a " + "FITS image: " + + std::string(e.what())); + } + + if (!alg->isExecuted()) { + throw std::runtime_error( + "Failed to load image correctly. Note that even though " + "the image file has been loaded it seems to contain errors."); + } + + try { + Mantid::API::WorkspaceGroup_sptr wsg; + Mantid::API::MatrixWorkspace_sptr ws; + const auto &ads = Mantid::API::AnalysisDataService::Instance(); + wsg = ads.retrieveWS<Mantid::API::WorkspaceGroup>(wsName); + ws = ads.retrieveWS<Mantid::API::MatrixWorkspace>(wsg->getNames()[0]); + } catch (std::exception &e) { + throw std::runtime_error( + "Could not load image contents for file '" + path + + "'. An unrecoverable error " + "happened when trying to load the image contents. Cannot " + "display it. Error details: " + + std::string(e.what())); + } +} + +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/MantidQt/CustomInterfaces/src/Tomography/ImageROIViewQtWidget.cpp b/MantidQt/CustomInterfaces/src/Tomography/ImageROIViewQtWidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51d23189b5cc869314dadd5affa48158f7b373b0 --- /dev/null +++ b/MantidQt/CustomInterfaces/src/Tomography/ImageROIViewQtWidget.cpp @@ -0,0 +1,830 @@ +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/WorkspaceGroup.h" +#include "MantidQtAPI/AlgorithmInputHistory.h" +#include "MantidQtAPI/AlgorithmRunner.h" + +#include "MantidQtCustomInterfaces/Tomography/ImageROIViewQtWidget.h" +#include "MantidQtCustomInterfaces/Tomography/ImageROIPresenter.h" + +using namespace Mantid::API; +using namespace MantidQt::CustomInterfaces; + +#include <QCloseEvent> +#include <QFileDialog> +#include <QMessageBox> +#include <QPainter> +#include <QSettings> + +namespace MantidQt { +namespace CustomInterfaces { + +// this would be more like a CustomWidget if it's eventually moved there +const std::string ImageROIViewQtWidget::m_settingsGroup = + "CustomInterfaces/ImageROIView"; + +ImageROIViewQtWidget::ImageROIViewQtWidget(QWidget *parent) + : QWidget(parent), IImageROIView(), m_imgWidth(0), m_imgHeight(0), + m_selectionState(SelectNone), m_presenter(NULL) { + initLayout(); + + // using an event filter. might be worth refactoring into a specific + // QLabel + selection of ROI+NormArea+CoR class + // not using Qwt Pickers to avoid Qwt version issues.. + m_ui.label_img->installEventFilter(this); +} + +void ImageROIViewQtWidget::setParams(ImageStackPreParams ¶ms) { + m_params = params; + setParamWidgets(m_params); +} + +ImageStackPreParams ImageROIViewQtWidget::userSelection() const { + return m_params; +} + +void ImageROIViewQtWidget::changeSelectionState( + const IImageROIView::SelectionState &state) { + m_selectionState = state; +} + +void ImageROIViewQtWidget::showStack(const std::string & /*path*/) { + // TODO: + // a) load as proper stack of images workspace - this can only be done when + // we have a firt working version of the "lean MD workspace". This method + // would then load into one workspace of such type. + // b) load as workspace group - this is done in the overloaded method below + + // enableParamWidgets(true); +} + +void ImageROIViewQtWidget::showStack(Mantid::API::WorkspaceGroup_sptr &wsg) { + if (0 == wsg->size()) + return; + + m_stack = wsg; + + m_ui.horizontalScrollBar_img_stack->setEnabled(true); + m_ui.horizontalScrollBar_img_stack->setMinimum(0); + m_ui.horizontalScrollBar_img_stack->setMaximum( + static_cast<int>(m_stack->size() - 1)); + + size_t width = 0, height = 0; + try { + MatrixWorkspace_sptr ws = + boost::dynamic_pointer_cast<MatrixWorkspace>(wsg->getItem(0)); + if (!ws) + return; + width = ws->blocksize(); + height = ws->getNumberHistograms(); + } catch (std::exception &e) { + QMessageBox::warning(this, "Cannot load image information", + "There was a problem while " + " trying to find the size of the image: " + + QString::fromStdString(e.what())); + } + + showProjection(m_stack, 0); + initParamWidgets(width, height); + refreshROIetAl(); + enableParamWidgets(true); +} + +void ImageROIViewQtWidget::showProjection( + const Mantid::API::WorkspaceGroup_sptr &wsg, size_t idx) { + + showProjectionImage(wsg, idx); + refreshROIetAl(); + + // give name, set up scroll/slider + std::string name; + try { + MatrixWorkspace_sptr ws = + boost::dynamic_pointer_cast<MatrixWorkspace>(wsg->getItem(idx)); + if (!ws) + return; + name = ws->run().getLogData("run_title")->value(); + } catch (std::exception &e) { + QMessageBox::warning(this, "Cannot load image information", + "There was a problem while " + " trying to find the name of the image: " + + QString::fromStdString(e.what())); + } + m_ui.label_img_name->setText(QString::fromStdString(name)); + + const size_t numPics = wsg->size(); + m_ui.lineEdit_img_seq->setText( + QString::fromStdString("1/" + boost::lexical_cast<std::string>(numPics))); + m_ui.horizontalScrollBar_img_stack->setValue(static_cast<int>(idx)); +} + +void ImageROIViewQtWidget::userWarning(const std::string &err, + const std::string &description) { + QMessageBox::warning(this, QString::fromStdString(err), + QString::fromStdString(description), QMessageBox::Ok, + QMessageBox::Ok); +} + +void ImageROIViewQtWidget::userError(const std::string &err, + const std::string &description) { + QMessageBox::critical(this, QString::fromStdString(err), + QString::fromStdString(description), QMessageBox::Ok, + QMessageBox::Ok); +} + +std::string ImageROIViewQtWidget::askImgOrStackPath() { + // get path + QString fitsStr = QString("Supported formats: FITS, TIFF and PNG " + "(*.fits *.fit *.tiff *.tif *.png);;" + "FITS, Flexible Image Transport System images " + "(*.fits *.fit);;" + "TIFF, Tagged Image File Format " + "(*.tif *.tiff);;" + "PNG, Portable Network Graphics " + "(*.png);;" + "Other extensions/all files (*.*)"); + QString prevPath = + MantidQt::API::AlgorithmInputHistory::Instance().getPreviousDirectory(); + QString path(QFileDialog::getExistingDirectory( + this, tr("Open stack of images"), prevPath, QFileDialog::ShowDirsOnly)); + if (!path.isEmpty()) { + MantidQt::API::AlgorithmInputHistory::Instance().setPreviousDirectory(path); + } + + return path.toStdString(); +} + +void ImageROIViewQtWidget::saveSettings() const { + QSettings qs; + qs.beginGroup(QString::fromStdString(m_settingsGroup)); + + qs.setValue("interface-win-geometry", saveGeometry()); + qs.endGroup(); +} + +void ImageROIViewQtWidget::resetCoR() { + int midx = + (m_ui.spinBox_cor_x->minimum() + m_ui.spinBox_cor_x->maximum()) / 2; + m_ui.spinBox_cor_x->setValue(midx); + int midy = + (m_ui.spinBox_cor_y->minimum() + m_ui.spinBox_cor_y->maximum()) / 2; + m_ui.spinBox_cor_y->setValue(midy); +} + +void ImageROIViewQtWidget::resetROI() { + m_ui.spinBox_roi_top_x->setValue(0); + m_ui.spinBox_roi_top_y->setValue(0); + m_ui.spinBox_roi_bottom_x->setValue(m_ui.spinBox_roi_bottom_x->maximum()); + m_ui.spinBox_roi_bottom_y->setValue(m_ui.spinBox_roi_bottom_y->maximum()); +} + +void ImageROIViewQtWidget::resetNormArea() { + m_ui.spinBox_norm_top_x->setValue(0); + m_ui.spinBox_norm_top_y->setValue(0); + m_ui.spinBox_norm_bottom_x->setValue(0); + m_ui.spinBox_norm_bottom_y->setValue(0); +} + +void ImageROIViewQtWidget::initLayout() { + // setup container ui + m_ui.setupUi(this); + + QList<int> sizes; + sizes.push_back(1000); + sizes.push_back(100); + m_ui.splitter_main_horiz->setSizes(sizes); + + sizes.clear(); + sizes.push_back(10); + sizes.push_back(1000); + m_ui.splitter_img_vertical->setSizes(sizes); + m_ui.horizontalScrollBar_img_stack->setEnabled(false); + m_ui.lineEdit_img_seq->setText("---"); + + enableParamWidgets(false); + + setupConnections(); + + initParamWidgets(1, 1); + grabCoRFromWidgets(); + grabROIFromWidgets(); + grabNormAreaFromWidgets(); + + // presenter that knows how to handle a IImageROIView should take care + // of all the logic. Note the view needs to now the concrete presenter here + m_presenter.reset(new ImageROIPresenter(this)); + + // it will know what compute resources and tools we have available: + // This view doesn't even know the names of compute resources, etc. + m_presenter->notify(ImageROIPresenter::Init); +} + +void ImageROIViewQtWidget::setupConnections() { + + // 'browse' buttons + connect(m_ui.pushButton_browse_img, SIGNAL(released()), this, + SLOT(browseImgClicked())); + + connect(m_ui.pushButton_cor, SIGNAL(released()), this, SLOT(corClicked())); + connect(m_ui.pushButton_cor_reset, SIGNAL(released()), this, + SLOT(corResetClicked())); + + connect(m_ui.pushButton_roi, SIGNAL(released()), this, SLOT(roiClicked())); + connect(m_ui.pushButton_roi_reset, SIGNAL(released()), this, + SLOT(roiResetClicked())); + + connect(m_ui.pushButton_norm_area, SIGNAL(released()), this, + SLOT(normAreaClicked())); + connect(m_ui.pushButton_norm_area_reset, SIGNAL(released()), this, + SLOT(normAreaResetClicked())); + + // image sequence scroll/slide: + connect(m_ui.horizontalScrollBar_img_stack, SIGNAL(valueChanged(int)), this, + SLOT(updateFromImagesSlider(int))); + + // parameter (points) widgets + connect(m_ui.spinBox_cor_x, SIGNAL(valueChanged(int)), this, + SLOT(valueUpdatedCoR(int))); + connect(m_ui.spinBox_cor_y, SIGNAL(valueChanged(int)), this, + SLOT(valueUpdatedCoR(int))); + + connect(m_ui.spinBox_roi_top_x, SIGNAL(valueChanged(int)), this, + SLOT(valueUpdatedROI(int))); + connect(m_ui.spinBox_roi_top_y, SIGNAL(valueChanged(int)), this, + SLOT(valueUpdatedROI(int))); + connect(m_ui.spinBox_roi_bottom_x, SIGNAL(valueChanged(int)), this, + SLOT(valueUpdatedROI(int))); + connect(m_ui.spinBox_roi_bottom_y, SIGNAL(valueChanged(int)), this, + SLOT(valueUpdatedROI(int))); + + connect(m_ui.spinBox_norm_top_x, SIGNAL(valueChanged(int)), this, + SLOT(valueUpdatedNormArea(int))); + connect(m_ui.spinBox_norm_top_y, SIGNAL(valueChanged(int)), this, + SLOT(valueUpdatedNormArea(int))); + connect(m_ui.spinBox_norm_bottom_x, SIGNAL(valueChanged(int)), this, + SLOT(valueUpdatedNormArea(int))); + connect(m_ui.spinBox_norm_bottom_y, SIGNAL(valueChanged(int)), this, + SLOT(valueUpdatedNormArea(int))); +} + +void ImageROIViewQtWidget::valueUpdatedCoR(int) { + grabCoRFromWidgets(); + refreshROIetAl(); +} + +void ImageROIViewQtWidget::valueUpdatedROI(int) { + grabROIFromWidgets(); + refreshROIetAl(); +} + +void ImageROIViewQtWidget::valueUpdatedNormArea(int) { + grabNormAreaFromWidgets(); + refreshROIetAl(); +} + +/** + * Parameter values from spin box widgets => coordinate parameters + * data member + */ +void ImageROIViewQtWidget::grabCoRFromWidgets() { + m_params.cor = Mantid::Kernel::V2D(m_ui.spinBox_cor_x->value(), + m_ui.spinBox_cor_y->value()); +} + +void ImageROIViewQtWidget::grabROIFromWidgets() { + m_params.roi = + std::make_pair(Mantid::Kernel::V2D(m_ui.spinBox_roi_top_x->value(), + m_ui.spinBox_roi_top_y->value()), + Mantid::Kernel::V2D(m_ui.spinBox_roi_bottom_x->value(), + m_ui.spinBox_roi_bottom_y->value())); +} + +void ImageROIViewQtWidget::grabNormAreaFromWidgets() { + m_params.normalizationRegion = + std::make_pair(Mantid::Kernel::V2D(m_ui.spinBox_norm_top_x->value(), + m_ui.spinBox_norm_top_y->value()), + Mantid::Kernel::V2D(m_ui.spinBox_norm_bottom_x->value(), + m_ui.spinBox_norm_bottom_y->value())); +} + +/** + * Updates the image view with the current image index and selection + * of parameters (ROI, normalization area, CoR). This needs to be used + * for every event that modifies the current image and/or selection of + * parameters. + */ +void ImageROIViewQtWidget::refreshROIetAl() { + + const QPixmap *pp = m_ui.label_img->pixmap(); + if (!pp) + return; + + QPixmap toDisplay(*m_basePixmap.get()); + QPainter painter(&toDisplay); + + // TODO: display settings / nicer symbol? + + QPen penCoR(Qt::red); + painter.setPen(penCoR); + painter.drawLine(static_cast<int>(m_params.cor.X() - 5), + static_cast<int>(m_params.cor.Y()), + static_cast<int>(m_params.cor.X() + 5), + static_cast<int>(m_params.cor.Y())); + painter.drawLine(static_cast<int>(m_params.cor.X()), + static_cast<int>(m_params.cor.Y() - 5), + static_cast<int>(m_params.cor.X()), + static_cast<int>(m_params.cor.Y() + 5)); + + QPen penROI(Qt::green); + painter.setPen(penROI); + painter.drawRect( + static_cast<int>(m_params.roi.first.X()), + static_cast<int>(m_params.roi.first.Y()), + static_cast<int>(m_params.roi.second.X() - m_params.roi.first.X()), + static_cast<int>(m_params.roi.second.Y() - m_params.roi.first.Y())); + + QPen penNA(Qt::yellow); + painter.setPen(penNA); + painter.drawRect(static_cast<int>(m_params.normalizationRegion.first.X()), + static_cast<int>(m_params.normalizationRegion.first.Y()), + static_cast<int>(m_params.normalizationRegion.second.X() - + m_params.normalizationRegion.first.X()), + static_cast<int>(m_params.normalizationRegion.second.Y() - + m_params.normalizationRegion.first.Y())); + + m_ui.label_img->setPixmap(toDisplay); +} + +void ImageROIViewQtWidget::refreshCoR() { + const QPixmap *pp = m_ui.label_img->pixmap(); + if (!pp) + return; + + grabCoRFromWidgets(); + + QPixmap toDisplay(*m_basePixmap.get()); + QPainter painter(&toDisplay); + QPen pen(Qt::red); + painter.setPen(pen); + painter.drawLine(static_cast<int>(m_params.cor.X() - 5), + static_cast<int>(m_params.cor.Y()), + static_cast<int>(m_params.cor.X() + 5), + static_cast<int>(m_params.cor.Y())); + painter.drawLine(static_cast<int>(m_params.cor.X()), + static_cast<int>(m_params.cor.Y() - 5), + static_cast<int>(m_params.cor.X()), + static_cast<int>(m_params.cor.Y() + 5)); + m_ui.label_img->setPixmap(toDisplay); +} + +void ImageROIViewQtWidget::refreshROI() { + const QPixmap *pp = m_ui.label_img->pixmap(); + if (!pp) + return; + + grabROIFromWidgets(); + // TODO: display proper symbol + + // QPixmap const *pm = m_ui.label_img->pixmap(); + QPixmap toDisplay(*m_basePixmap.get()); + QPainter painter(&toDisplay); + QPen pen(Qt::green); + painter.setPen(pen); + painter.drawRect( + static_cast<int>(m_params.roi.first.X()), + static_cast<int>(m_params.roi.first.Y()), + static_cast<int>(m_params.roi.second.X() - m_params.roi.first.X()), + static_cast<int>(m_params.roi.second.Y() - m_params.roi.first.Y())); + m_ui.label_img->setPixmap(toDisplay); +} + +void ImageROIViewQtWidget::refreshNormArea() { + // TODO: display proper symbol + const QPixmap *pp = m_ui.label_img->pixmap(); + if (!pp) + return; + + grabNormAreaFromWidgets(); + + // QPixmap const *pm = m_ui.label_img->pixmap(); + QPixmap toDisplay(*m_basePixmap.get()); + QPainter painter(&toDisplay); + QPen pen(Qt::yellow); + painter.setPen(pen); + painter.drawRect(static_cast<int>(m_params.normalizationRegion.first.X()), + static_cast<int>(m_params.normalizationRegion.first.Y()), + static_cast<int>(m_params.normalizationRegion.second.X() - + m_params.normalizationRegion.first.X()), + static_cast<int>(m_params.normalizationRegion.second.Y() - + m_params.normalizationRegion.first.Y())); + m_ui.label_img->setPixmap(toDisplay); +} + +void ImageROIViewQtWidget::enableParamWidgets(bool enable) { + m_ui.groupBox_cor->setEnabled(enable); + m_ui.groupBox_roi->setEnabled(enable); + m_ui.groupBox_norm->setEnabled(enable); +} + +void ImageROIViewQtWidget::initParamWidgets(size_t maxWidth, size_t maxHeight) { + m_imgWidth = static_cast<int>(maxWidth); + m_imgHeight = static_cast<int>(maxHeight); + + m_ui.spinBox_cor_x->setMinimum(0); + m_ui.spinBox_cor_x->setMaximum(m_imgWidth - 1); + m_ui.spinBox_cor_y->setMinimum(0); + m_ui.spinBox_cor_y->setMaximum(m_imgHeight - 1); + resetCoR(); + + m_ui.spinBox_roi_top_x->setMinimum(0); + m_ui.spinBox_roi_top_x->setMaximum(m_imgWidth - 1); + m_ui.spinBox_roi_top_y->setMinimum(0); + m_ui.spinBox_roi_top_y->setMaximum(m_imgHeight - 1); + + m_ui.spinBox_roi_bottom_x->setMinimum(0); + m_ui.spinBox_roi_bottom_x->setMaximum(m_imgWidth - 1); + m_ui.spinBox_roi_bottom_y->setMinimum(0); + m_ui.spinBox_roi_bottom_y->setMaximum(m_imgHeight - 1); + + resetROI(); + + m_ui.spinBox_norm_top_x->setMinimum(0); + m_ui.spinBox_norm_top_x->setMaximum(m_imgWidth - 1); + m_ui.spinBox_norm_top_y->setMinimum(0); + m_ui.spinBox_norm_top_y->setMaximum(m_imgHeight - 1); + + m_ui.spinBox_norm_bottom_x->setMinimum(0); + m_ui.spinBox_norm_bottom_x->setMaximum(m_imgWidth - 1); + m_ui.spinBox_norm_bottom_y->setMinimum(0); + m_ui.spinBox_norm_bottom_y->setMaximum(m_imgHeight - 1); + + resetNormArea(); +} + +void ImageROIViewQtWidget::setParamWidgets(ImageStackPreParams ¶ms) { + m_ui.spinBox_cor_x->setValue(static_cast<int>(params.cor.X())); + m_ui.spinBox_cor_y->setValue(static_cast<int>(params.cor.Y())); + + m_ui.spinBox_roi_top_x->setValue(static_cast<int>(params.roi.first.X())); + m_ui.spinBox_roi_top_y->setValue(static_cast<int>(params.roi.first.Y())); + + m_ui.spinBox_roi_bottom_x->setValue(static_cast<int>(params.roi.second.X())); + m_ui.spinBox_roi_bottom_y->setValue(static_cast<int>(params.roi.second.Y())); + + m_ui.spinBox_norm_top_x->setValue( + static_cast<int>(params.normalizationRegion.first.X())); + m_ui.spinBox_norm_top_y->setValue( + static_cast<int>(params.normalizationRegion.first.Y())); + + m_ui.spinBox_norm_bottom_x->setValue( + static_cast<int>(params.normalizationRegion.second.X())); + m_ui.spinBox_norm_bottom_y->setValue( + static_cast<int>(params.normalizationRegion.second.Y())); +} + +void ImageROIViewQtWidget::corClicked() { + m_presenter->notify(IImageROIPresenter::SelectCoR); +} + +void ImageROIViewQtWidget::corResetClicked() { + m_presenter->notify(IImageROIPresenter::ResetCoR); + refreshROIetAl(); +} + +void ImageROIViewQtWidget::roiClicked() { + m_presenter->notify(IImageROIPresenter::SelectROI); +} + +void ImageROIViewQtWidget::roiResetClicked() { + m_presenter->notify(IImageROIPresenter::ResetROI); + refreshROIetAl(); +} + +void ImageROIViewQtWidget::normAreaClicked() { + m_presenter->notify(IImageROIPresenter::SelectNormalization); +} + +void ImageROIViewQtWidget::normAreaResetClicked() { + m_presenter->notify(IImageROIPresenter::ResetNormalization); + refreshROIetAl(); +} + +void ImageROIViewQtWidget::browseImgClicked() { + m_presenter->notify(IImageROIPresenter::BrowseImgOrStack); +} + +void ImageROIViewQtWidget::updateFromImagesSlider(int /* current */) { + m_presenter->notify(IImageROIPresenter::UpdateImgIndex); +} + +size_t ImageROIViewQtWidget::currentImgIndex() const { + return m_ui.horizontalScrollBar_img_stack->value(); +} + +void ImageROIViewQtWidget::updateImgWithIndex(size_t idx) { + int max = m_ui.horizontalScrollBar_img_stack->maximum(); + int current = m_ui.horizontalScrollBar_img_stack->value(); + + showProjection(m_stack, idx); + m_ui.lineEdit_img_seq->setText( + QString::fromStdString(boost::lexical_cast<std::string>(current + 1) + + "/" + boost::lexical_cast<std::string>(max + 1))); +} + +void ImageROIViewQtWidget::showProjectionImage( + const Mantid::API::WorkspaceGroup_sptr &wsg, size_t idx) { + + MatrixWorkspace_sptr ws; + try { + ws = boost::dynamic_pointer_cast<MatrixWorkspace>(wsg->getItem(idx)); + if (!ws) + return; + } catch (std::exception &e) { + QMessageBox::warning( + this, "Cannot load image", + "There was a problem while trying to find the image data: " + + QString::fromStdString(e.what())); + } + + const size_t MAXDIM = 2048 * 16; + size_t width; + try { + width = boost::lexical_cast<size_t>(ws->run().getLogData("Axis1")->value()); + // TODO: add a settings option for this (like max mem allocation for + // images)? + if (width >= MAXDIM) + width = MAXDIM; + } catch (std::exception &e) { + QMessageBox::critical(this, "Cannot load image", + "There was a problem while trying to " + "find the width of the image: " + + QString::fromStdString(e.what())); + return; + } + + size_t height; + try { + height = + boost::lexical_cast<size_t>(ws->run().getLogData("Axis2")->value()); + if (height >= MAXDIM) + height = MAXDIM; + } catch (std::exception &e) { + QMessageBox::critical(this, "Cannot load image", + "There was a problem while trying to " + "find the height of the image: " + + QString::fromStdString(e.what())); + return; + } + + // images are loaded as 1 histogram == 1 pixel (1 bin per histogram): + if (height != ws->getNumberHistograms() || width != ws->blocksize()) { + QMessageBox::critical( + this, "Image dimensions do not match in the input image workspace", + "Could not load the expected " + "number of rows and columns."); + return; + } + // find min and max to scale pixel values + double min = std::numeric_limits<double>::max(), + max = std::numeric_limits<double>::min(); + for (size_t i = 0; i < ws->getNumberHistograms(); ++i) { + for (size_t j = 0; j < ws->blocksize(); ++j) { + const double &v = ws->readY(i)[j]; + if (v < min) + min = v; + if (v > max) + max = v; + } + } + if (min >= max) { + QMessageBox::warning( + this, "Empty image!", + "The image could be loaded but it contains " + "effectively no information, all pixels have the same value."); + // black picture + QPixmap pix(static_cast<int>(width), static_cast<int>(height)); + pix.fill(QColor(0, 0, 0)); + m_ui.label_img->setPixmap(pix); + m_ui.label_img->show(); + m_basePixmap.reset(new QPixmap(pix)); + return; + } + + // load / transfer image into a QImage + QImage rawImg(QSize(static_cast<int>(width), static_cast<int>(height)), + QImage::Format_RGB32); + const double max_min = max - min; + const double scaleFactor = 255.0 / max_min; + for (size_t yi = 0; yi < width; ++yi) { + for (size_t xi = 0; xi < width; ++xi) { + const double &v = ws->readY(yi)[xi]; + // color the range min-max in gray scale. To apply different color + // maps you'd need to use rawImg.setColorTable() or similar. + const int scaled = static_cast<int>(scaleFactor * (v - min)); + QRgb vRgb = qRgb(scaled, scaled, scaled); + rawImg.setPixel(static_cast<int>(xi), static_cast<int>(yi), vRgb); + } + } + + // paint and show image + // direct from image + QPixmap pix = QPixmap::fromImage(rawImg); + m_ui.label_img->setPixmap(pix); + m_ui.label_img->show(); + m_basePixmap.reset(new QPixmap(pix)); + // Alternative, drawing with a painter: + // QPixmap pix(static_cast<int>(width), static_cast<int>(height)); + // QPainter painter; + // painter.begin(&pix); + // painter.drawImage(0, 0, rawImg); + // painter.end(); + // m_ui.label_img->setPixmap(pix); + // m_ui.label_img->show(); + // m_basePixmap.reset(new QPixmap(pix)); +} + +/** + * Qt events filter for the mouse click and click&drag events that are + * used to select points and rectangles. Part of the logic of the + * selection is handled here. The test on the presenter can only test + * the begin and end of the selection. For full testability (including + * the mouse interaction), this method should be implemented fully in + * terms of notifications to the presenter. This would require a bunch + * of new notifications in IImageROIPresenter, and making at least all + * the mouseUpdateCoR, mouseUpdateROICorners12, mouseXXX methods + * public in this view interface. This can be considered at a later + * stage. + * + * @param obj object concerned by the event + * @param event event received (mouse click, release, move, etc.) + **/ +bool ImageROIViewQtWidget::eventFilter(QObject *obj, QEvent *event) { + // quick ignore + if (IImageROIView::SelectNone == m_selectionState) + return false; + + if (m_ui.label_img == obj) { + + QPoint p = m_ui.label_img->mapFromGlobal(QCursor::pos()); + int x = p.x(); + int y = p.y(); + // ignore potential clicks outside of the image + if (x >= m_imgWidth || y >= m_imgHeight || x < 0 || y < 0) + return false; + + auto type = event->type(); + if (type == QEvent::MouseButtonPress) { + + if (IImageROIView::SelectCoR == m_selectionState) { + mouseUpdateCoR(x, y); + } else if (IImageROIView::SelectROIFirst == m_selectionState) { + mouseUpdateROICorners12(x, y); + } else if (IImageROIView::SelectNormAreaFirst == m_selectionState) { + mouseUpdateNormAreaCorners12(x, y); + } + } else if (type == QEvent::MouseMove) { + + if (IImageROIView::SelectROISecond == m_selectionState) { + mouseUpdateROICorner2(x, y); + } else if (IImageROIView::SelectNormAreaSecond == m_selectionState) { + mouseUpdateNormAreaCorner2(x, y); + } + } else if (type == QEvent::MouseButtonRelease) { + + if (IImageROIView::SelectROISecond == m_selectionState) { + mouseFinishROI(x, y); + } else if (IImageROIView::SelectNormAreaSecond == m_selectionState) { + mouseFinishNormArea(x, y); + } + } + } + // pass on the event up to the parent class + return false; +} + +/** + * Parameter values from mouse position (at a relevant event like + * first click, or last release) => spin box widgets, AND coordinate + * parameters data member. This grabs the Center of Rotation (CoR) + * + * @param x position on x axis (local to the image) + * @param y position on y axis (local to the image) + */ +void ImageROIViewQtWidget::grabCoRFromMousePoint(int x, int y) { + m_params.cor = Mantid::Kernel::V2D(x, y); + m_ui.spinBox_cor_x->setValue(x); + m_ui.spinBox_cor_y->setValue(y); +} + +void ImageROIViewQtWidget::grabROICorner1FromMousePoint(int x, int y) { + m_params.roi.first = Mantid::Kernel::V2D(x, y); + m_ui.spinBox_roi_top_x->setValue(x); + m_ui.spinBox_roi_top_y->setValue(y); +} + +void ImageROIViewQtWidget::grabROICorner2FromMousePoint(int x, int y) { + m_params.roi.second = Mantid::Kernel::V2D(x, y); + m_ui.spinBox_roi_bottom_x->setValue(x); + m_ui.spinBox_roi_bottom_y->setValue(y); +} + +void ImageROIViewQtWidget::grabNormAreaCorner1FromMousePoint(int x, int y) { + m_params.normalizationRegion.first = Mantid::Kernel::V2D(x, y); + m_ui.spinBox_norm_top_x->setValue(x); + m_ui.spinBox_norm_top_y->setValue(y); +} + +void ImageROIViewQtWidget::grabNormAreaCorner2FromMousePoint(int x, int y) { + m_params.normalizationRegion.second = Mantid::Kernel::V2D(x, y); + m_ui.spinBox_norm_bottom_x->setValue(x); + m_ui.spinBox_norm_bottom_y->setValue(y); +} + +/** + * This is an update and implicity a finish, as there's only one + * update for the CoR (single point-click). The coordinates count as + * usual in Qt widgets. Top-left is (0,0). + * + * @param x position on x axis (local to the image) + * @param y position on y axis (local to the image) + */ +void ImageROIViewQtWidget::mouseUpdateCoR(int x, int y) { + grabCoRFromMousePoint(x, y); + refreshROIetAl(); + + m_presenter->notify(IImageROIPresenter::FinishedCoR); +} + +/** + * Start of ROI selection (or first click after pushing "select + * ROI". The rectangle starts as a point from the mouse click. + * + * @param x position on x axis (local to the image) + * @param y position on y axis (local to the image) + */ +void ImageROIViewQtWidget::mouseUpdateROICorners12(int x, int y) { + grabROICorner1FromMousePoint(x, y); + grabROICorner2FromMousePoint(x, y); + refreshROIetAl(); + m_selectionState = IImageROIView::SelectROISecond; +} + +/** + * Change the rectangle while pressing the mouse button. The first + * corner stays at the first click, now only the second corner changes + * to the new mouse position. On release of the mouse button we'll get + * to mouseFinishROICorner2() and end the selection of the ROI. + * + * @param x position on x axis (local to the image) + * @param y position on y axis (local to the image) + */ +void ImageROIViewQtWidget::mouseUpdateROICorner2(int x, int y) { + grabROICorner2FromMousePoint(x, y); + refreshROIetAl(); +} + +/** + * End of ROI selection (or mouse button release after clicking once + * and move, all after pushing "select ROI". The second corner of the + * rectangle is set at the current position. + * + * @param x position on x axis (local to the image) + * @param y position on y axis (local to the image) + */ +void ImageROIViewQtWidget::mouseFinishROI(int x, int y) { + grabROICorner2FromMousePoint(x, y); + refreshROIetAl(); + m_presenter->notify(IImageROIPresenter::FinishedROI); +} + +void ImageROIViewQtWidget::mouseUpdateNormAreaCorners12(int x, int y) { + grabNormAreaCorner1FromMousePoint(x, y); + grabNormAreaCorner2FromMousePoint(x, y); + refreshROIetAl(); + m_selectionState = IImageROIView::SelectNormAreaSecond; +} + +void ImageROIViewQtWidget::mouseUpdateNormAreaCorner2(int x, int y) { + grabNormAreaCorner2FromMousePoint(x, y); + refreshROIetAl(); +} + +void ImageROIViewQtWidget::mouseFinishNormArea(int x, int y) { + grabNormAreaCorner2FromMousePoint(x, y); + refreshROIetAl(); + m_presenter->notify(IImageROIPresenter::FinishedNormalization); +} + +void ImageROIViewQtWidget::readSettings() { + QSettings qs; + qs.beginGroup(QString::fromStdString(m_settingsGroup)); + restoreGeometry(qs.value("interface-win-geometry").toByteArray()); + qs.endGroup(); +} + +void ImageROIViewQtWidget::closeEvent(QCloseEvent *event) { + m_presenter->notify(IImageROIPresenter::ShutDown); + event->accept(); +} + +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/MantidQt/CustomInterfaces/src/Tomography/ImageStackPreParams.cpp b/MantidQt/CustomInterfaces/src/Tomography/ImageStackPreParams.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60d8a1e2bd0dbc8a26e01cb9dcae0898e0a27395 --- /dev/null +++ b/MantidQt/CustomInterfaces/src/Tomography/ImageStackPreParams.cpp @@ -0,0 +1,13 @@ +#include "MantidQtCustomInterfaces/Tomography/ImageStackPreParams.h" + +using namespace MantidQt::CustomInterfaces; + +namespace MantidQt { +namespace CustomInterfaces { + +ImageStackPreParams::ImageStackPreParams() +{ + +} +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/MantidQt/CustomInterfaces/src/Tomography/StackOfImagesDirs.cpp b/MantidQt/CustomInterfaces/src/Tomography/StackOfImagesDirs.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d8be84c4a5db207fb1285d9888532c6e30c86fe3 --- /dev/null +++ b/MantidQt/CustomInterfaces/src/Tomography/StackOfImagesDirs.cpp @@ -0,0 +1,129 @@ +#include "MantidQtCustomInterfaces/Tomography/StackOfImagesDirs.h" + +#include <boost/algorithm/string.hpp> + +#include <Poco/DirectoryIterator.h> +#include <Poco/Path.h> + +namespace MantidQt { +namespace CustomInterfaces { + +const std::string StackOfImagesDirs::g_descr = + "A directory (folder) that contains subdirectories with names " + "starting with:\n- 'Data' (for sample images),\n- 'Flat' (for white " + "bean images),\n- 'Dark' (for dark images)\nThe first one is " + "mandatory whereas the other two are optional."; + +const std::string StackOfImagesDirs::g_sampleNamePrefix = "data"; +const std::string StackOfImagesDirs::g_flatNamePrefix = "flat"; +const std::string StackOfImagesDirs::g_darkNamePrefix = "dark"; +const std::string StackOfImagesDirs::g_processedNamePrefix = "processed"; +const std::string StackOfImagesDirs::g_prefilteredNamePrefix = "pre_filtered"; + +StackOfImagesDirs::StackOfImagesDirs(const std::string &path) + : m_valid(false), m_statusDescStr("Constructed, no checks done yet.") { + findStackDirs(path); +} + +std::string StackOfImagesDirs::description() const { return g_descr; } + +std::string StackOfImagesDirs::status() const { + if (m_valid) + return "Stack of images is correct"; + else + return "There are errors in the directories and/or files. " + + m_statusDescStr; +} + +std::vector<std::string> StackOfImagesDirs::sampleFiles() const { + return findImgFiles(m_sampleDir); +} +std::vector<std::string> StackOfImagesDirs::flatFiles() const { + return findImgFiles(m_flatDir); +} +std::vector<std::string> StackOfImagesDirs::darkFiles() const { + return findImgFiles(m_darkDir); +} + +void StackOfImagesDirs::findStackDirs(const std::string &path) { + if (path.empty()) + return; + + Poco::File dir(path); + if (!dir.isDirectory() || !dir.exists()) + return; + + Poco::DirectoryIterator end; + for (Poco::DirectoryIterator it(dir); it != end; ++it) { + if (!it->isDirectory()) { + continue; + } + + const std::string name = it.name(); + + // case insensitive comparison against expected pattersn: data_*, flat_*, + // dark_*, etc. + if (boost::iequals(name.substr(0, g_sampleNamePrefix.length()), + g_sampleNamePrefix)) { + m_sampleDir = it.path().toString(); + } else if (boost::iequals(name.substr(0, g_flatNamePrefix.length()), + g_flatNamePrefix)) { + m_flatDir = name; + } else if (boost::iequals(name.substr(0, g_darkNamePrefix.length()), + g_darkNamePrefix)) { + m_darkDir = name; + } + } + + if (m_sampleDir.empty()) { + m_statusDescStr = "The the sample images directory (" + g_sampleNamePrefix + + "...) has not been found."; + return; + } + + // can be valid only if we get here. There must be at least one entry that is + // a file + Poco::Path samplesPath(m_sampleDir); + for (Poco::DirectoryIterator it(samplesPath); it != end; ++it) { + if (it->isFile()) { + m_valid = true; + break; + } + } + + if (m_valid) { + m_statusDescStr = "all checks passed"; + } else { + m_statusDescStr = "No files were found in the sample images directory (" + + g_sampleNamePrefix + "...)."; + } +} + +std::vector<std::string> +StackOfImagesDirs::findImgFiles(const std::string &path) const { + std::vector<std::string> fnames; + Poco::File dir(path); + if (!dir.isDirectory() || !dir.exists()) + return fnames; + + // as an alternative could also use Poco::Glob to find the files + Poco::DirectoryIterator it(dir); + Poco::DirectoryIterator end; + while (it != end) { + // TODO: filter names by extension? + // const std::string name = it.name(); + if (it->isFile()) { + fnames.push_back(it.path().toString()); + } + + ++it; + } + + // this assumes the usual sorting of images of a stack (directory): a prefix, + // and a sequence number (with a fixed number of digits). + std::sort(fnames.begin(), fnames.end()); + return fnames; +} + +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/MantidQt/CustomInterfaces/src/Tomography/TomographyIfacePresenter.cpp b/MantidQt/CustomInterfaces/src/Tomography/TomographyIfacePresenter.cpp index 59ae96f3caed1ffa8f76c7fbb143fcdb94a95c34..ef2cad5f09bddc06fd38dd9c798e60f91e150e41 100644 --- a/MantidQt/CustomInterfaces/src/Tomography/TomographyIfacePresenter.cpp +++ b/MantidQt/CustomInterfaces/src/Tomography/TomographyIfacePresenter.cpp @@ -310,7 +310,6 @@ void TomographyIfacePresenter::processLogMsg() { std::vector<std::string> msgs = m_view->logMsgs(); for (size_t i = 0; i < msgs.size(); i++) { m_model->logMsg(msgs[i]); - break; } } diff --git a/MantidQt/CustomInterfaces/src/Tomography/TomographyIfaceViewQtGUI.cpp b/MantidQt/CustomInterfaces/src/Tomography/TomographyIfaceViewQtGUI.cpp index c280ce58fce36a8d851ee1ad68804b38bf4fdd47..48fef5a235172ab657810b05bc0b1f4314753bc7 100644 --- a/MantidQt/CustomInterfaces/src/Tomography/TomographyIfaceViewQtGUI.cpp +++ b/MantidQt/CustomInterfaces/src/Tomography/TomographyIfaceViewQtGUI.cpp @@ -3,6 +3,7 @@ #include "MantidQtAPI/AlgorithmInputHistory.h" #include "MantidQtAPI/AlgorithmRunner.h" #include "MantidQtAPI/HelpWindow.h" +#include "MantidQtCustomInterfaces/Tomography/ImageROIViewQtWidget.h" #include "MantidQtCustomInterfaces/Tomography/TomographyIfaceViewQtGUI.h" #include "MantidQtCustomInterfaces/Tomography/TomographyIfacePresenter.h" #include "MantidQtCustomInterfaces/Tomography/ToolConfigAstraToolbox.h" @@ -65,12 +66,27 @@ void TomographyIfaceViewQtGUI::initLayout() { // setup container ui m_ui.setupUi(this); // add tab contents and set up their ui's - QWidget *tab1w = new QWidget(m_ui.tabMain); - m_uiTabRun.setupUi(tab1w); - m_ui.tabMain->addTab(tab1w, QString("Run")); - QWidget *tab2w = new QWidget(m_ui.tabMain); - m_uiTabSetup.setupUi(tab2w); - m_ui.tabMain->addTab(tab2w, QString("Setup")); + QWidget *tabRunW = new QWidget(m_ui.tabMain); + m_uiTabRun.setupUi(tabRunW); + m_ui.tabMain->addTab(tabRunW, QString("Run")); + QWidget *tabSetupW = new QWidget(m_ui.tabMain); + m_uiTabSetup.setupUi(tabSetupW); + m_ui.tabMain->addTab(tabSetupW, QString("Setup")); + + ImageROIViewQtWidget *tabROIW = new ImageROIViewQtWidget(m_ui.tabMain); + m_ui.tabMain->addTab(tabROIW, QString("ROI etc.")); + + QWidget *tabFiltersW = new QWidget(); + m_ui.tabMain->addTab(tabFiltersW, QString("Filters")); + + QWidget *tabVizW = new QWidget(); + m_ui.tabMain->addTab(tabVizW, QString("Visualize")); + + QWidget *tabConvertW = new QWidget(); + m_ui.tabMain->addTab(tabConvertW, QString("Convert")); + + QWidget *tabEBandsW = new QWidget(); + m_ui.tabMain->addTab(tabEBandsW, QString("Energy bands")); readSettings(); diff --git a/MantidQt/CustomInterfaces/src/Tomography/ToolConfig.cpp b/MantidQt/CustomInterfaces/src/Tomography/ToolConfig.cpp index 08391894c2eca282554ed2106c4db5f3a7f05a86..feb959472f6907482fb64850c5e862ecd2cb79cb 100644 --- a/MantidQt/CustomInterfaces/src/Tomography/ToolConfig.cpp +++ b/MantidQt/CustomInterfaces/src/Tomography/ToolConfig.cpp @@ -2,12 +2,15 @@ #include "MantidQtCustomInterfaces/Tomography/ToolConfigCustom.h" #include "MantidQtCustomInterfaces/Tomography/ToolConfigTomoPy.h" - #include <boost/lexical_cast.hpp> namespace MantidQt { namespace CustomInterfaces { +ToolConfigTomoPy::ToolConfigTomoPy() + : TomoRecToolConfig(""), m_pathOut(""), m_pathDark(""), m_pathOpen(""), + m_pathSample(""), m_centerRot(.0), m_angleMin(.0), m_angleMax(180.0) {} + ToolConfigTomoPy::ToolConfigTomoPy(const std::string &runnable, const std::string &pathOut, const std::string &pathDark, @@ -28,6 +31,10 @@ std::string ToolConfigTomoPy::makeCmdLineOptions() const { boost::lexical_cast<std::string>(m_centerRot); } +ToolConfigAstraToolbox::ToolConfigAstraToolbox() + : TomoRecToolConfig(""), m_centerRot(.0), m_angleMin(.0), m_angleMax(180.0), + m_pathOut(""), m_pathDark(""), m_pathOpen(""), m_pathSample("") {} + ToolConfigAstraToolbox::ToolConfigAstraToolbox( const std::string &runnable, double centerRot, double angleMin, double angleMax, const std::string &pathOut, const std::string &pathDark, diff --git a/MantidQt/CustomInterfaces/test/ImageROIPresenterTest.h b/MantidQt/CustomInterfaces/test/ImageROIPresenterTest.h new file mode 100644 index 0000000000000000000000000000000000000000..56cb937e2c3046ab444d0fe1540a5c5601d4600d --- /dev/null +++ b/MantidQt/CustomInterfaces/test/ImageROIPresenterTest.h @@ -0,0 +1,320 @@ +#ifndef MANTID_CUSTOMINTERFACES_IMAGEROIPRESENTERTEST_H +#define MANTID_CUSTOMINTERFACES_IMAGEROIPRESENTERTEST_H + +#include "MantidAPI/FrameworkManager.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidQtCustomInterfaces/Tomography/ImageROIPresenter.h" + +#include <cxxtest/TestSuite.h> + +#include <Poco/File.h> + +#include "ImageROIViewMock.h" + +using namespace MantidQt::CustomInterfaces; +using testing::TypedEq; +using testing::Return; + +class ImageROIPresenterTest : public CxxTest::TestSuite { + +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static ImageROIPresenterTest *createSuite() { + return new ImageROIPresenterTest(); + } + + static void destroySuite(ImageROIPresenterTest *suite) { delete suite; } + + ImageROIPresenterTest() { + Mantid::API::FrameworkManager::Instance(); // make sure the framework is + // initialized + } + + void setUp() { + m_view.reset(new testing::NiceMock<MockImageROIView>()); + m_presenter.reset( + new MantidQt::CustomInterfaces::ImageROIPresenter(m_view.get())); + } + + void tearDown() { + TS_ASSERT(testing::Mock::VerifyAndClearExpectations(m_view.get())); + } + + void test_initOK() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, setParams(testing::_)).Times(1); + + // No errors/warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(ImageROIPresenter::Init); + } + + void test_initWithWrongParams() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, setParams(testing::_)).Times(1); + + // One error, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(ImageROIPresenter::Init); + } + + void xxtest_browseImg_EmptyPath() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, askImgOrStackPath()).Times(1).WillOnce(Return("")); + + // No error, no warnings, just ignored + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + // should not get there: + EXPECT_CALL(mockView, showStack(testing::An<const std::string &>())) + .Times(0); + EXPECT_CALL(mockView, + showStack(testing::An<Mantid::API::WorkspaceGroup_sptr &>())) + .Times(0); + EXPECT_CALL(mockView, updateImgWithIndex(testing::_)).Times(0); + + pres.notify(IImageROIPresenter::BrowseImgOrStack); + } + + void xxtest_newImg_EmptyPath() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, askImgOrStackPath()).Times(0); + + // No error, one warning pop-up because a stack is not found + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(1); + + // should not get there because there's no stack/img - it's just ignored: + EXPECT_CALL(mockView, showStack(testing::An<const std::string &>())) + .Times(0); + EXPECT_CALL(mockView, + showStack(testing::An<Mantid::API::WorkspaceGroup_sptr &>())) + .Times(0); + EXPECT_CALL(mockView, updateImgWithIndex(testing::_)).Times(0); + + pres.notify(IImageROIPresenter::NewImgOrStack); + } + + void test_browseImg_WrongPath() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, askImgOrStackPath()) + .Times(1) + .WillOnce(Return("dont_look_for_me_i_dont_exist")); + + // A warning + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(1); + + // should not get there because there's no stack/img + EXPECT_CALL(mockView, showStack(testing::An<const std::string &>())) + .Times(0); + EXPECT_CALL(mockView, + showStack(testing::An<Mantid::API::WorkspaceGroup_sptr &>())) + .Times(0); + EXPECT_CALL(mockView, updateImgWithIndex(testing::_)).Times(0); + + // this exception is currently handled, and a warning given + //TSM_ASSERT_THROWS("There should be an exception if there is an unexpected " + // "error with the images path", + // pres.notify(IImageROIPresenter::BrowseImgOrStack), + // Poco::FileNotFoundException); + pres.notify(IImageROIPresenter::BrowseImgOrStack); + } + + void test_updateImgIndex() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + int idx = 0; + EXPECT_CALL(mockView, currentImgIndex()).Times(1).WillOnce(Return(idx)); + + EXPECT_CALL(mockView, updateImgWithIndex(idx)).Times(1); + + // No errors, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(IImageROIPresenter::UpdateImgIndex); + } + + void test_selectCoR() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, changeSelectionState(IImageROIView::SelectCoR)) + .Times(1); + + // No errors, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(IImageROIPresenter::SelectCoR); + } + + void test_resetCoR() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, resetCoR()).Times(1); + EXPECT_CALL(mockView, changeSelectionState(IImageROIView::SelectNone)) + .Times(1); + + // just a few calls that should not happen + EXPECT_CALL(mockView, resetROI()).Times(0); + EXPECT_CALL(mockView, showStack(testing::An<const std::string &>())) + .Times(0); + EXPECT_CALL(mockView, + showStack(testing::An<Mantid::API::WorkspaceGroup_sptr &>())) + .Times(0); + EXPECT_CALL(mockView, updateImgWithIndex(testing::_)).Times(0); + + // No errors, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(IImageROIPresenter::ResetCoR); + } + + void test_selectROI() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, changeSelectionState(IImageROIView::SelectROIFirst)) + .Times(1); + + // No errors, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(IImageROIPresenter::SelectROI); + } + + void test_finishROI() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, changeSelectionState(IImageROIView::SelectNone)) + .Times(1); + + // No errors, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(IImageROIPresenter::FinishedROI); + } + + void test_resetROI() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, resetROI()).Times(1); + EXPECT_CALL(mockView, changeSelectionState(IImageROIView::SelectNone)) + .Times(1); + + // just a few calls that should not happen + EXPECT_CALL(mockView, resetCoR()).Times(0); + EXPECT_CALL(mockView, showStack(testing::An<const std::string &>())) + .Times(0); + EXPECT_CALL(mockView, + showStack(testing::An<Mantid::API::WorkspaceGroup_sptr &>())) + .Times(0); + EXPECT_CALL(mockView, updateImgWithIndex(testing::_)).Times(0); + + // No errors, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(IImageROIPresenter::ResetROI); + } + + void test_selectNormalization() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, changeSelectionState( + IImageROIView::SelectNormAreaFirst)).Times(1); + + // No errors, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(IImageROIPresenter::SelectNormalization); + } + + void test_finishNormalization() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, changeSelectionState(IImageROIView::SelectNone)) + .Times(1); + + // No errors, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(IImageROIPresenter::FinishedNormalization); + } + + void test_resetNormalization() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, resetNormArea()).Times(1); + EXPECT_CALL(mockView, changeSelectionState(IImageROIView::SelectNone)) + .Times(1); + + // just a few calls that should not happen + EXPECT_CALL(mockView, resetCoR()).Times(0); + EXPECT_CALL(mockView, resetROI()).Times(0); + EXPECT_CALL(mockView, showStack(testing::An<const std::string &>())) + .Times(0); + EXPECT_CALL(mockView, + showStack(testing::An<Mantid::API::WorkspaceGroup_sptr &>())) + .Times(0); + EXPECT_CALL(mockView, updateImgWithIndex(testing::_)).Times(0); + + // No errors, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(IImageROIPresenter::ResetNormalization); + } + + void test_shutDown() { + testing::NiceMock<MockImageROIView> mockView; + MantidQt::CustomInterfaces::ImageROIPresenter pres(&mockView); + + EXPECT_CALL(mockView, saveSettings()).Times(1); + // No errors, no warnings + EXPECT_CALL(mockView, userError(testing::_, testing::_)).Times(0); + EXPECT_CALL(mockView, userWarning(testing::_, testing::_)).Times(0); + + pres.notify(ImageROIPresenter::ShutDown); + } + +private: + // boost::shared_ptr + boost::scoped_ptr<testing::NiceMock<MockImageROIView>> m_view; + boost::scoped_ptr<MantidQt::CustomInterfaces::ImageROIPresenter> m_presenter; + + // To have one FITS, etc. + Mantid::API::MatrixWorkspace_sptr m_ws; +}; + +#endif // MANTID_CUSTOMINTERFACES_IMAGEROIPRESENTERTEST_H diff --git a/MantidQt/CustomInterfaces/test/ImageROIViewMock.h b/MantidQt/CustomInterfaces/test/ImageROIViewMock.h new file mode 100644 index 0000000000000000000000000000000000000000..6ba7ebca13e036ac6569eb64b51857e61ae79e44 --- /dev/null +++ b/MantidQt/CustomInterfaces/test/ImageROIViewMock.h @@ -0,0 +1,66 @@ +#ifndef MANTID_CUSTOMINTERFACES_IMAGEROIVIEWMOCK_H +#define MANTID_CUSTOMINTERFACES_IMAGEROIVIEWMOCK_H + +#include "MantidQtCustomInterfaces/Tomography/ITomographyIfaceView.h" + +#include <gmock/gmock.h> + +class MockImageROIView : public MantidQt::CustomInterfaces::IImageROIView { +public: + // void initParams(ImageStackPreParams ¶ms) + MOCK_METHOD1(setParams, + void(MantidQt::CustomInterfaces::ImageStackPreParams &)); + + // ImageStackPreParams userSelection() const; + MOCK_CONST_METHOD0(userSelection, + MantidQt::CustomInterfaces::ImageStackPreParams()); + + // SelectionState selectionState() const; + MOCK_CONST_METHOD0(selectionState, SelectionState()); + + // void changeSelectionState(const SelectionState state); + MOCK_METHOD1(changeSelectionState, void(const IImageROIView::SelectionState&)); + + // void showStack(const std::string &path); + MOCK_METHOD1(showStack, void(const std::string &)); + + // void showStack(const Mantid::API::WorkspaceGroup_sptr &ws); + MOCK_METHOD1(showStack, void(Mantid::API::WorkspaceGroup_sptr &)); + + // const Mantid::API::WorkspaceGroup_sptr stack() const; + MOCK_CONST_METHOD0(stack, const Mantid::API::WorkspaceGroup_sptr()); + + // void showProjection(const Mantid::API::WorkspaceGroup_sptr &wsg, size_t idx); + MOCK_METHOD2(showProjection, void(const Mantid::API::WorkspaceGroup_sptr &wsg, size_t idx)); + + // void userWarning(const std::string &warn, const std::string &description) + MOCK_METHOD2(userWarning, + void(const std::string &warn, const std::string &description)); + + // void userError(const std::string &err, const std::string &description) + MOCK_METHOD2(userError, + void(const std::string &err, const std::string &description)); + + // size_t currentImgIndex() const; + MOCK_CONST_METHOD0(currentImgIndex, size_t()); + + // void updateImgWithIndex(size_t idx) + MOCK_METHOD1(updateImgWithIndex, void(size_t)); + + // std::string askImgOrStackPath(); + MOCK_METHOD0(askImgOrStackPath, std::string()); + + // void saveSettings() const {} + MOCK_CONST_METHOD0(saveSettings, void()); + + // void resetCoR() + MOCK_METHOD0(resetCoR, void()); + + // void resetROI() + MOCK_METHOD0(resetROI, void()); + + // void resetNormArea() + MOCK_METHOD0(resetNormArea, void()); +}; + +#endif // MANTID_CUSTOMINTERFACES_IMAGEROIVIEWMOCK_H diff --git a/MantidQt/CustomInterfaces/test/StackOfImagesDirsTest.h b/MantidQt/CustomInterfaces/test/StackOfImagesDirsTest.h new file mode 100644 index 0000000000000000000000000000000000000000..641dd70eb5d6e709690c04aa1560be5b0d8a9eb6 --- /dev/null +++ b/MantidQt/CustomInterfaces/test/StackOfImagesDirsTest.h @@ -0,0 +1,111 @@ +#ifndef MANTID_CUSTOMINTERFACES_STACKOFIMAGESDIRSTEST_H +#define MANTID_CUSTOMINTERFACES_STACKOFIMAGESDIRSTEST_H + +#include "MantidAPI/FrameworkManager.h" +#include "MantidQtCustomInterfaces/Tomography/StackOfImagesDirs.h" + +#include <boost/scoped_ptr.hpp> + +#include <cxxtest/TestSuite.h> + +using namespace MantidQt::CustomInterfaces; + +class StackOfImagesDirsTest : public CxxTest::TestSuite { + +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static StackOfImagesDirsTest *createSuite() { + return new StackOfImagesDirsTest(); + } + + static void destroySuite(StackOfImagesDirsTest *suite) { delete suite; } + + StackOfImagesDirsTest() { + Mantid::API::FrameworkManager::Instance(); // make sure the framework is + // initialized + } + + void setUp() { + // just to test more dynamic allocation + m_soid.reset(new StackOfImagesDirs("")); + } + + void tearDown() {} + + void test_construct() { + StackOfImagesDirs obj(""); + + TSM_ASSERT("A stack just constructed with an empty path string should not " + "be valid", + !obj.isValid()); + } + + void test_description() { + StackOfImagesDirs obj(""); + + TS_ASSERT_THROWS_NOTHING(obj.description()); + TSM_ASSERT("A description string should be produced", + "" != obj.description()); + } + + void test_status() { + StackOfImagesDirs obj(""); + + TS_ASSERT_THROWS_NOTHING(obj.description()); + TSM_ASSERT("A status string should be produced", "" != obj.status()); + } + + void test_sampleImagesDir() { + StackOfImagesDirs obj(""); + + TS_ASSERT_THROWS_NOTHING(obj.description()); + TSM_ASSERT("The sample images directory of an empty stack should be empty", + "" == obj.sampleImagesDir()); + } + + void test_flatImagesDir() { + StackOfImagesDirs obj(""); + + TS_ASSERT_THROWS_NOTHING(obj.description()); + TSM_ASSERT("The flat images directory of an empty stack should be empty", + "" == obj.flatImagesDir()); + } + + void test_darkImagesDir() { + StackOfImagesDirs obj(""); + + TS_ASSERT_THROWS_NOTHING(obj.description()); + TSM_ASSERT("The dark images directory of an empty stack should be empty", + "" == obj.flatImagesDir()); + } + + void test_sampleFiles() { + StackOfImagesDirs obj(""); + + TS_ASSERT_THROWS_NOTHING(obj.description()); + TSM_ASSERT("There should not be any sample files in an empty stack", + 0 == obj.sampleImagesDir().size()); + } + + void test_flatFiles() { + StackOfImagesDirs obj(""); + + TS_ASSERT_THROWS_NOTHING(obj.description()); + TSM_ASSERT("There should not be any flat image files in an empty stack", + 0 == obj.flatImagesDir().size()); + } + + void test_darkFiles() { + StackOfImagesDirs obj(""); + + TS_ASSERT_THROWS_NOTHING(obj.description()); + TSM_ASSERT("There should not be any dark image files in an empty stack", + 0 == obj.darkImagesDir().size()); + } + +private: + boost::scoped_ptr<StackOfImagesDirs> m_soid; +}; + +#endif /* MANTID_CUSTOMINTERFACES_STACKOFIMAGESDIRSTEST_H */ diff --git a/docs/source/interfaces/Tomographic_Reconstruction.rst b/docs/source/interfaces/Tomographic_Reconstruction.rst index d31f16f6ad90f25f598f33de0f037bcb527bc861..10218297b0180cf65a005cfe5af11202a362303f 100644 --- a/docs/source/interfaces/Tomographic_Reconstruction.rst +++ b/docs/source/interfaces/Tomographic_Reconstruction.rst @@ -140,6 +140,43 @@ To be able to run jobs on a remote compute resource (cluster, supercomputer, etc You can monitor the status of the jobs currently running (and recently run) on remote compute resources in the same tab. +Setting common parameters for the reconstruction jobs +----------------------------------------------------- + +Several parameters can be set in the "ROI etc." section or tab. These +parameters will be used for all the reconstruction jobs, regardless of +the tool and/or reconstruction method used. + +* Region of interest (ROI) for the analysis +* Area for normalization (open beam, not blocked by sample) +* Center of rotation, for tomographic reconstruction + +At any stage during the process of selecting the regions it is also +possible to see how the selections fit different images by sliding +through the images of the stack (using the slider or scroll bar). + +The center of rotation can be selected interactively by clicking on +the select button and then clicking on an image pixel. To select the +regions of interest or the area of normalization, just click on the +respective "select" button and then click and drag with the mouse to +select a rectangle. The precise coordinates of the center and regions +can be set via the boxes of the right panel as well. + +Once you have selected or set one of the regions, or the center, they +can be selected again by pushing the respective "Select" buttons +and/or editing their coordinates manually. + +The default values, set in principle when a new stack of images is +loaded, is as follows. The region of intererest is set to cover all +the images. The regions of normalization is not set (empty), and the +center of rotation is set to the center of the image. The option to +find the center of rotation automatically is disabled at present. + +If when selection a region the mouse is moved outside of the images, +it is possible to continue the selection of the region (second corner) +by clicking again inside the image. Alternatively, any selection can +be reset at any point by using the "reset" buttons. + Running jobs locally --------------------