From 1ec2a74a073957e458308ffea73fbc4fe59b07fd 2019-03-27 20:16:25 From: Alexis Jeandet Date: 2019-03-27 20:16:25 Subject: [PATCH] Added ultra preliminary version of python providers Signed-off-by: Alexis Jeandet --- diff --git a/core b/core index a0c89a7..68c0115 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit a0c89a70c83c407d12cf22096a0e7ffae4763e00 +Subproject commit 68c01155acd227b01fa3c9f7926215e8cedd70df diff --git a/plugins/python_providers/CMakeLists.txt b/plugins/python_providers/CMakeLists.txt index 20a7962..7ea499f 100644 --- a/plugins/python_providers/CMakeLists.txt +++ b/plugins/python_providers/CMakeLists.txt @@ -1,26 +1,42 @@ include_directories(include) -FILE (GLOB_RECURSE python_providers - include/*.h - src/*.cpp - resources/*.qrc +FILE (GLOB_RECURSE python_provider_srcs + include/python_providers.h + src/python_providers.cpp + resources/python_providers.qrc ) +FILE (GLOB_RECURSE python_interpreter_srcs + include/python_interpreter.h + src/python_interpreter.cpp + ) + +add_library(python_interpreter ${python_interpreter_srcs}) +target_link_libraries(python_interpreter PRIVATE pybind11::embed) +target_link_libraries(python_interpreter PUBLIC sciqlopcore) +target_compile_definitions(python_interpreter PRIVATE QT_NO_KEYWORDS) +SET_TARGET_PROPERTIES(python_interpreter PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) + add_definitions(-DQT_PLUGIN) add_definitions(-DSCIQLOP_PLUGIN_JSON_FILE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/python_providers.json") if(NOT BUILD_SHARED_LIBS) add_definitions(-DQT_STATICPLUGIN) endif() -add_library(python_providers ${python_providers}) +add_library(python_providers ${python_provider_srcs}) SET_TARGET_PROPERTIES(python_providers PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) target_link_libraries(python_providers PUBLIC sciqlopgui) -target_link_libraries(python_providers PRIVATE pybind11::embed) -ADD_DEFINITIONS(-DQT_NO_KEYWORDS) +target_link_libraries(python_providers PRIVATE python_interpreter) + install(TARGETS python_providers ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/SciQlop LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/SciQlop RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS python_interpreter + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/SciQlop + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/SciQlop + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + include(sciqlop_tests) diff --git a/plugins/python_providers/include/python_interpreter.h b/plugins/python_providers/include/python_interpreter.h new file mode 100644 index 0000000..9ce9a96 --- /dev/null +++ b/plugins/python_providers/include/python_interpreter.h @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include + + +class PythonInterpreter +{ +public: + PythonInterpreter(); + void add_register_callback(std::function&, + std::function(std::string&, double, double)>)> + callback); + ~PythonInterpreter(); + void eval(const std::string& file); + +private: +}; diff --git a/plugins/python_providers/include/python_providers.h b/plugins/python_providers/include/python_providers.h index d9a5a94..42afa5d 100644 --- a/plugins/python_providers/include/python_providers.h +++ b/plugins/python_providers/include/python_providers.h @@ -2,9 +2,10 @@ #define PYTHON_PROVIDERS_H #include - +#include #include +#include #ifndef SCIQLOP_PLUGIN_JSON_FILE_PATH #define SCIQLOP_PLUGIN_JSON_FILE_PATH "python_providers.json" @@ -21,6 +22,12 @@ public: /// @sa IPlugin::initialize() void initialize() override; ~PythonProviders(); + +private: + void register_product(const std::vector& path_list, + std::function(std::string& name, double, double)> + f); + PythonInterpreter _interpreter; }; #endif // PYTHON_PROVIDERS_H diff --git a/plugins/python_providers/src/python_interpreter.cpp b/plugins/python_providers/src/python_interpreter.cpp new file mode 100644 index 0000000..50429f6 --- /dev/null +++ b/plugins/python_providers/src/python_interpreter.cpp @@ -0,0 +1,47 @@ +#include "python_interpreter.h" +#include +#include +#include +#include +#include +#include +#include + +namespace py = pybind11; + + +PYBIND11_EMBEDDED_MODULE(PythonProviders, m) {} +static pybind11::gil_scoped_release* _rel = nullptr; + +PythonInterpreter::PythonInterpreter() +{ + py::initialize_interpreter(false); +} + +void PythonInterpreter::add_register_callback(std::function&, + std::function(std::string&, double, double)>)> + callback) +{ + py::module PythonProviders = py::module::import("PythonProviders"); + PythonProviders.attr("register_product") = callback; +} + +PythonInterpreter::~PythonInterpreter() +{ + if (_rel) + delete _rel; + py::finalize_interpreter(); +} + +void PythonInterpreter::eval(const std::string& file) +{ + try + { + py::eval_file(file); + } + catch (py::error_already_set const& pythonErr) + { + std::cout << pythonErr.what(); + } + _rel = new py::gil_scoped_release(); +} diff --git a/plugins/python_providers/src/python_providers.cpp b/plugins/python_providers/src/python_providers.cpp index 881b823..4de21d8 100644 --- a/plugins/python_providers/src/python_providers.cpp +++ b/plugins/python_providers/src/python_providers.cpp @@ -1,17 +1,158 @@ #include "python_providers.h" -#include -namespace py = pybind11; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +const auto DATA_SOURCE_NAME = QStringLiteral("PythonProviders"); + +struct noop_deleter +{ + void operator()(TimeSeries::ITimeSerie*) {} +}; + +class PythonProvider : public IDataProvider +{ +public: + PythonProvider( + std::function(std::string&, double, double)> f) + : _pythonFunction { f } + { + } + + PythonProvider(const PythonProvider& other) : _pythonFunction { other._pythonFunction } {} + + std::shared_ptr clone() const override + { + return std::make_shared(*this); + } + virtual TimeSeries::ITimeSerie* getData(const DataProviderParameters& parameters) override + { + auto product = parameters.m_Data.value("PRODUCT", "").toString().toStdString(); + auto range = parameters.m_Range; + auto result = _pythonFunction(product, range.m_TStart, range.m_TEnd); + if (auto ts = std::dynamic_pointer_cast(result)) + { + return new VectorTimeSerie(*ts); + } + if (auto ts = std::dynamic_pointer_cast(result)) + { + return new ScalarTimeSerie(*ts); + } + if (auto ts = std::dynamic_pointer_cast(result)) + { + return new SpectrogramTimeSerie(*ts); + } + return nullptr; + } + +private: + std::function(std::string&, double, double)> + _pythonFunction; +}; + void PythonProviders::initialize() { - py::initialize_interpreter(false); - py::print("Hello, World!"); - py::print("Hello, World!"); - py::print("Hello, World!"); - py::print("Hello, World!"); + _interpreter.add_register_callback( + [this](const std::vector& path_list, + std::function(std::string&, double, double)> + f) { this->register_product(path_list, f); }); + + for (const auto& path : QStandardPaths::standardLocations(QStandardPaths::AppLocalDataLocation)) + { + auto dir = QDir(path + "/python"); + if (dir.exists()) + { + for (const auto& entry : + dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name)) + { + if (entry.isFile() && entry.suffix() == "py") + { + _interpreter.eval(entry.absoluteFilePath().toStdString()); + } + } + } + } +} + +PythonProviders::~PythonProviders() {} + +std::unique_ptr make_folder_item(const QString& name) +{ + return std::make_unique(DataSourceItemType::NODE, name); +} + +template +DataSourceItem* make_path_items( + const T& path_list_begin, const T& path_list_end, DataSourceItem* root) +{ + std::for_each(path_list_begin, path_list_end, [&root](const auto& folder_name) mutable { + auto folder_ptr = root->findItem(folder_name); + if (folder_ptr == nullptr) + { + auto folder = make_folder_item(folder_name); + folder_ptr = folder.get(); + root->appendChild(std::move(folder)); + } + root = folder_ptr; + }); + return root; +} + +std::unique_ptr make_product_item( + const QVariantHash& metaData, const QUuid& dataSourceUid) +{ + auto result = std::make_unique(DataSourceItemType::PRODUCT, metaData); + + // Adds plugin name to product metadata + result->setData(DataSourceItem::PLUGIN_DATA_KEY, DATA_SOURCE_NAME); + result->setData(DataSourceItem::ID_DATA_KEY, metaData.value(DataSourceItem::NAME_DATA_KEY)); + + auto productName = metaData.value(DataSourceItem::NAME_DATA_KEY).toString(); + + // Add action to load product from DataSourceController + result->addAction( + std::make_unique(QObject::tr("Load %1 product").arg(productName), + [productName, dataSourceUid](DataSourceItem& item) { + if (auto app = sqpApp) + { + app->dataSourceController().loadProductItem(dataSourceUid, item); + } + })); + + return result; } -PythonProviders::~PythonProviders() +void PythonProviders::register_product(const std::vector& path_list, + std::function(std::string&, double, double)> f) { - py::finalize_interpreter(); + auto& dataSourceController = sqpApp->dataSourceController(); + auto id = dataSourceController.registerDataSource(DATA_SOURCE_NAME); + auto root = make_folder_item(DATA_SOURCE_NAME); + std::for_each( + std::cbegin(path_list), std::cend(path_list), [id, f, root = root.get()](const auto& path) { + auto path_list = QString::fromStdString(path).split('/'); + auto name = *(std::cend(path_list) - 1); + auto path_item + = make_path_items(std::cbegin(path_list), std::cend(path_list) - 1, root); + path_item->appendChild( + make_product_item({ { DataSourceItem::NAME_DATA_KEY, name } }, id)); + }); + dataSourceController.setDataSourceItem(id, std::move(root)); + dataSourceController.setDataProvider(id, std::make_unique(f)); + std::cout << "Gone there" << std::endl; }