Unverified Commit 58dddea4 authored by Antti Soininen's avatar Antti Soininen Committed by GitHub
Browse files

Merge pull request #23636 from martyngigg/mplcpp-library

Add a helper library to enable plotting with matplotlib from C++
parents 465fc1f7 3c430be0
......@@ -33,6 +33,7 @@ public:
Py_intptr_t const *get_shape() const;
int get_nd() const;
void *get_data() const;
char get_typecode() const;
NDArray astype(char dtype, bool copy = true) const;
};
......
......@@ -76,6 +76,14 @@ void *NDArray::get_data() const {
return PyArray_DATA(reinterpret_cast<PyArrayObject *>(this->ptr()));
}
/**
* See https://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html
* @return The character code for the dtype of the array
*/
char NDArray::get_typecode() const {
return PyArray_DESCR(reinterpret_cast<PyArrayObject *>(this->ptr()))->type;
}
/**
* Casts (and copies if necessary) the array to the given data type
* @param dtype Character code for the numpy data types
......
......@@ -106,7 +106,7 @@ include ( PrecompiledHeaderCommands )
# CXXTEST_ADD_TEST (public macro to add unit tests)
#=============================================================
macro(CXXTEST_ADD_TEST _cxxtest_testname)
# output directory
# output directory
set (_cxxtest_output_dir ${CMAKE_CURRENT_BINARY_DIR})
if (CXXTEST_OUTPUT_DIR)
set (_cxxtest_output_dir ${CXXTEST_OUTPUT_DIR})
......
......@@ -62,6 +62,7 @@ endfunction()
# option: NO_SUFFIX If included, no suffix is added to the target name
# option: EXCLUDE_FROM_ALL If included, the target is excluded from target ALL
# keyword: TARGET_NAME The name of the target. The target will have -Qt{QT_VERSION} appended to it.
# keyword: OUTPUT_NAME An optional filename for the library
# keyword: QT_VERSION The major version of Qt to build against
# keyword: SRC .cpp files to include in the target build
# keyword: QT4_SRC .cpp files to include in a Qt4 build
......@@ -92,7 +93,7 @@ endfunction()
function (mtd_add_qt_target)
set (options LIBRARY EXECUTABLE NO_SUFFIX EXCLUDE_FROM_ALL)
set (oneValueArgs
TARGET_NAME QT_VERSION OUTPUT_DIR_BASE OUTPUT_SUBDIR
TARGET_NAME OUTPUT_NAME QT_VERSION OUTPUT_DIR_BASE OUTPUT_SUBDIR
INSTALL_DIR INSTALL_DIR_BASE PRECOMPILED)
set (multiValueArgs SRC UI MOC
NOMOC RES DEFS QT4_DEFS QT5_DEFS INCLUDE_DIRS SYSTEM_INCLUDE_DIRS LINK_LIBS
......@@ -137,9 +138,12 @@ function (mtd_add_qt_target)
if (PARSED_NO_SUFFIX)
set (_target ${PARSED_TARGET_NAME})
set (_output_name ${PARSED_OUTPUT_NAME})
else()
_append_qt_suffix (VERSION ${PARSED_QT_VERSION} OUTPUT_VARIABLE _target
${PARSED_TARGET_NAME})
_append_qt_suffix (VERSION ${PARSED_QT_VERSION} OUTPUT_VARIABLE _output_name
${PARSED_OUTPUT_NAME})
endif()
_append_qt_suffix (VERSION ${PARSED_QT_VERSION} OUTPUT_VARIABLE _mtd_qt_libs
${PARSED_MTD_QT_LINK_LIBS})
......@@ -164,6 +168,9 @@ function (mtd_add_qt_target)
endif()
# Target properties
if ( _output_name )
set_target_properties ( ${_target} PROPERTIES OUTPUT_NAME ${_output_name} )
endif ()
if (PARSED_OUTPUT_DIR_BASE)
set ( _output_dir ${PARSED_OUTPUT_DIR_BASE}/qt${PARSED_QT_VERSION} )
if (PARSED_OUTPUT_SUBDIR)
......
......@@ -3,6 +3,7 @@
###########################################################################
add_subdirectory ( common )
add_subdirectory ( legacyqwt )
add_subdirectory ( mplcpp )
add_subdirectory ( instrumentview )
add_subdirectory ( sliceviewer )
add_subdirectory ( spectrumviewer )
......
# Rules for matplotlib cpp library
set ( LIB_SRCS
src/Artist.cpp
src/Axes.cpp
src/Colors.cpp
src/Colormap.cpp
src/Figure.cpp
src/FigureCanvasQt.cpp
src/Line2D.cpp
src/ScalarMappable.cpp
)
set ( MOC_HEADERS
inc/MantidQtWidgets/MplCpp/FigureCanvasQt.h
)
set (NOMOC_HEADERS
inc/MantidQtWidgets/MplCpp/Artist.h
inc/MantidQtWidgets/MplCpp/Axes.h
inc/MantidQtWidgets/MplCpp/Colors.h
inc/MantidQtWidgets/MplCpp/Colormap.h
inc/MantidQtWidgets/MplCpp/Figure.h
inc/MantidQtWidgets/MplCpp/Line2D.h
inc/MantidQtWidgets/MplCpp/ScalarMappable.h
)
find_package ( BoostPython REQUIRED )
# Target
mtd_add_qt_library (TARGET_NAME MantidQtWidgetsMplCpp
QT_VERSION 5
SRC ${LIB_SRCS}
MOC ${MOC_HEADERS}
NOMOC ${NOMOC_HEADERS}
DEFS
IN_MANTIDQT_MPLCPP
INCLUDE_DIRS
inc
../../../Framework/PythonInterface/core/inc
${Boost_INCLUDE_DIRS}
${PYTHON_INCLUDE_PATH}
${PYTHON_NUMPY_INCLUDE_DIR}
LINK_LIBS
${TCMALLOC_LIBRARIES_LINKTIME}
${Boost_LIBRARIES}
${PYTHON_LIBRARIES}
PythonInterfaceCore
INSTALL_DIR
${LIB_DIR}
OSX_INSTALL_RPATH
@loader_path/../MacOS
@loader_path/../Libraries
LINUX_INSTALL_RPATH
"\$ORIGIN/../${LIB_DIR}"
)
# Testing
add_subdirectory ( test )
#ifndef MPLCPP_ARTIST_H
#define MPLCPP_ARTIST_H
/*
Copyright &copy; 2018 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.
*/
#include "MantidQtWidgets/MplCpp/DllConfig.h"
#include "MantidQtWidgets/MplCpp/Python/Object.h"
namespace MantidQt {
namespace Widgets {
namespace MplCpp {
/**
* Wraps a matplotlib.artist object with a C++ interface
*/
class MANTID_MPLCPP_DLL Artist : public Python::InstanceHolder {
public:
// Holds a reference to the matplotlib artist object
explicit Artist(Python::Object obj);
// Remove the artist from the canvas
void remove();
};
} // namespace MplCpp
} // namespace Widgets
} // namespace MantidQt
#endif // MPLCPP_ARTIST_H
#ifndef MPLCPP_AXES_H
#define MPLCPP_AXES_H
/*
Copyright &copy; 2018 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.
*/
#include "MantidQtWidgets/MplCpp/DllConfig.h"
#include "MantidQtWidgets/MplCpp/Line2D.h"
namespace MantidQt {
namespace Widgets {
namespace MplCpp {
class MANTID_MPLCPP_DLL Axes : public Python::InstanceHolder {
public:
explicit Axes(Python::Object obj);
/// @name Formatting
/// @{
void setXLabel(const char *label);
void setYLabel(const char *label);
void setTitle(const char *label);
/// @}
/// @name Plotting
/// @{
Line2D plot(std::vector<double> xdata, std::vector<double> ydata,
const char *format = "b-");
/// @}
};
} // namespace MplCpp
} // namespace Widgets
} // namespace MantidQt
#endif // MPLCPP_AXES_H
#ifndef MPLCPP_BACKENDQT_H
#define MPLCPP_BACKENDQT_H
/*
Copyright &copy; 2018 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.
*/
#include "MantidQtWidgets/MplCpp/Python/Object.h"
#include <QtGlobal>
/*
* Defines constants relating to the matplotlib backend
*/
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#error "Qt >= 5 required"
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) && \
QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
/// Define PyQt version that matches the matplotlib backend
const char *PYQT_MODULE = "PyQt5";
/// Define matplotlib backend that will be used to draw the canvas
const char *MPL_QT_BACKEND = "matplotlib.backends.backend_qt5agg";
#else
#error "Unknown Qt version. Cannot determine matplotlib backend."
#endif
namespace MantidQt {
namespace Widgets {
namespace MplCpp {
/// Import and return the backend module for this version of Qt
Python::Object backendModule() {
// Importing PyQt first allows matplotlib to select the correct
// backend
Python::NewRef(PyImport_ImportModule(PYQT_MODULE));
return Python::NewRef(PyImport_ImportModule(MPL_QT_BACKEND));
}
} // namespace MplCpp
} // namespace Widgets
} // namespace MantidQt
#endif // MPLCPP_BACKENDQT_H
#ifndef MPLCPP_COLORMAP_H
#define MPLCPP_COLORMAP_H
/*
Copyright &copy; 2018 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.
*/
#include "MantidQtWidgets/MplCpp/DllConfig.h"
#include "MantidQtWidgets/MplCpp/Python/Object.h"
#include <QString>
namespace MantidQt {
namespace Widgets {
namespace MplCpp {
/**
* @brief Defines a wrapper
*/
class MANTID_MPLCPP_DLL Colormap : public Python::InstanceHolder {
public:
Colormap(Python::Object obj);
};
/// Return the matplotlib.cm module
MANTID_MPLCPP_DLL Python::Object cmModule();
/// Return the named colormap if it exists
MANTID_MPLCPP_DLL Colormap getCMap(const QString &name);
} // namespace MplCpp
} // namespace Widgets
} // namespace MantidQt
#endif // MPLCPP_COLORMAP_H
#ifndef MPLCPP_COLORS_H
#define MPLCPP_COLORS_H
/*
Copyright &copy; 2018 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.
*/
#include "MantidQtWidgets/MplCpp/DllConfig.h"
#include "MantidQtWidgets/MplCpp/Python/Object.h"
/**
* @file Contains definitions of wrappers for types in
* matplotlib.colors. These types provide the ability to
* normalize data according to different scale types.
* See https://matplotlib.org/2.2.3/api/colors_api.html
*/
namespace MantidQt {
namespace Widgets {
namespace MplCpp {
/**
* @brief C++ base class for Normalize types to allow a common interface
* to distinguish from a general Python::InstanceHolder.
*/
class MANTID_MPLCPP_DLL NormalizeBase : public Python::InstanceHolder {
public:
NormalizeBase(Python::Object obj);
};
/**
* @brief The Normalize class provides a simple mapping of data in
* the internal [vmin, vmax] to the interval [0, 1].
* See
* https://matplotlib.org/2.2.3/api/_as_gen/matplotlib.colors.Normalize.html#matplotlib.colors.Normalize
*/
class MANTID_MPLCPP_DLL Normalize : public NormalizeBase {
public:
Normalize(double vmin, double vmax);
};
/**
* @brief Map data values [vmin, vmax] onto a symmetrical log scale.
* See
* https://matplotlib.org/2.2.3/api/_as_gen/matplotlib.colors.SymLogNorm.html#matplotlib.colors.SymLogNorm
*/
class MANTID_MPLCPP_DLL SymLogNorm : public NormalizeBase {
public:
SymLogNorm(double linthresh, double linscale, double vmin, double vmax);
};
/**
* @brief Map data values [vmin, vmax] onto a power law scale.
* See
* https://matplotlib.org/2.2.3/api/_as_gen/matplotlib.colors.PowerNorm.html#matplotlib.colors.PowerNorm
*/
class MANTID_MPLCPP_DLL PowerNorm : public NormalizeBase {
public:
PowerNorm(double gamma, double vmin, double vmax);
};
} // namespace MplCpp
} // namespace Widgets
} // namespace MantidQt
#endif // MPLCPP_COLORS_H
#ifndef MANTIDQT_MPLCPP_DLLCONFIG_H_
#define MANTIDQT_MPLCPP_DLLCONFIG_H_
#include "MantidKernel/System.h"
#ifdef IN_MANTIDQT_MPLCPP
#define MANTID_MPLCPP_DLL DLLExport
#else
#define MANTID_MPLCPP_DLL DLLImport
#endif /* IN_MANTIDQT_MPLCPP */
#endif // MANTIDQT_MPLCPP_DLLCONFIG_H_
#ifndef MPLCPP_FIGURE_H
#define MPLCPP_FIGURE_H
/*
Copyright &copy; 2018 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.
*/
#include "MantidQtWidgets/MplCpp/Axes.h"
#include "MantidQtWidgets/MplCpp/DllConfig.h"
#include "MantidQtWidgets/MplCpp/Python/Object.h"
namespace MantidQt {
namespace Widgets {
namespace MplCpp {
/**
* A thin C++ wrapper to create a new matplotlib figure
*/
class MANTID_MPLCPP_DLL Figure : public Python::InstanceHolder {
public:
Figure(bool tightLayout = true);
/**
* @param index The index of the axes to return
* @return The axes instance
*/
inline Axes axes(size_t index) const {
return Axes{pyobj().attr("axes")[index]};
}
Axes addAxes(double left, double bottom, double width, double height);
Axes addSubPlot(int subplotspec);
};
} // namespace MplCpp
} // namespace Widgets
} // namespace MantidQt
#endif // MPLCPP_FIGURE_H
#ifndef MPLCPP_FIGURECANVASQT_H
#define MPLCPP_FIGURECANVASQT_H
/*
Copyright &copy; 2018 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.
*/
#include "MantidQtWidgets/MplCpp/DllConfig.h"
#include "MantidQtWidgets/MplCpp/Figure.h"
#include <QWidget>
namespace MantidQt {
namespace Widgets {
namespace MplCpp {
/**
* @brief FigureCanvasQt defines a QWidget that can be embedded within
* another widget to display a matplotlib figure. It roughly follows
* the matplotlib example on embedding a matplotlib canvas:
* https://matplotlib.org/examples/user_interfaces/embedding_in_qt5.html
*/
class MANTID_MPLCPP_DLL FigureCanvasQt : public QWidget,
public Python::InstanceHolder {
Q_OBJECT
public:
FigureCanvasQt(int subplotspec, QWidget *parent = nullptr);
FigureCanvasQt(Figure fig, QWidget *parent = nullptr);
/// Non-const access to the current active axes instance.
inline Axes &gca() { return m_axes; }
private: // members
Axes m_axes;
};
} // namespace MplCpp
} // namespace Widgets
} // namespace MantidQt
#endif // MPLCPP_FIGURECANVASQT_H
#ifndef MPLCPP_LINE2D_H
#define MPLCPP_LINE2D_H
/*
Copyright &copy; 2018 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.
*/
#include "MantidQtWidgets/MplCpp/Artist.h"
#include "MantidQtWidgets/MplCpp/DllConfig.h"
#include <vector>
namespace MantidQt {
namespace Widgets {
namespace MplCpp {
/**
* @brief Line2D holds an instance of a matplotlib Line2D type.
* This type is designed to hold an existing Line2D instance that contains
* data in numpy arrays that do not own their data but have a view on to an
* existing vector of data. This object keeps the data alive.
*/
class MANTID_MPLCPP_DLL Line2D : public Artist {
public:
Line2D(Python::Object obj, std::vector<double> xdataOwner,
std::vector<double> ydataOwner);
~Line2D();
// not copyable
Line2D(const Line2D &) = delete;
Line2D &operator=(const Line2D &) = delete;
// movable
Line2D(Line2D &&) = default;
Line2D &operator=(Line2D &&) = default;
private:
// Containers that own the data making up the line
std::vector<double> m_xOwner, m_yOwner;
};
} // namespace MplCpp
} // namespace Widgets
} // namespace MantidQt
#endif // MPLCPP_LINE2D_H
#ifndef MPLCPP_PYTHON_OBJECT_H
#define MPLCPP_PYTHON_OBJECT_H
/*
Copyright &copy; 2017 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.
*/
#include <boost/python/borrowed.hpp>
#include <boost/python/object.hpp>
#include <stdexcept>