From 9d722e8ce9dce2f42f1fa517622e07e6db68e76e Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Fri, 8 Jan 2021 17:07:41 +0100 Subject: [PATCH] gr-fosphor: Add pybind11 bindings Part of the GNURadio 3.9 transition Signed-off-by: Sylvain Munaut --- CMakeLists.txt | 15 ++++- python/CMakeLists.txt | 2 + python/__init__.py | 3 + python/bindings/CMakeLists.txt | 66 +++++++++++++++++++ python/bindings/base_sink_c_python.cc | 93 +++++++++++++++++++++++++++ python/bindings/glfw_sink_c_python.cc | 35 ++++++++++ python/bindings/python_bindings.cc | 43 +++++++++++++ python/bindings/qt_sink_c_python.cc | 61 ++++++++++++++++++ 8 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 python/bindings/CMakeLists.txt create mode 100644 python/bindings/base_sink_c_python.cc create mode 100644 python/bindings/glfw_sink_c_python.cc create mode 100644 python/bindings/python_bindings.cc create mode 100644 python/bindings/qt_sink_c_python.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 311eb0f..da049f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,19 @@ endif (Qt5_FOUND) find_package(Doxygen) find_package(PythonLibs 3) +######################################################################## +# PyBind11 Related +######################################################################## + +find_package(pybind11 REQUIRED) + +if (pybind11_FOUND) + execute_process( + COMMAND "${PYTHON_EXECUTABLE}" -c + "try:\n import numpy\n import os\n inc_path = numpy.get_include()\n if os.path.exists(os.path.join(inc_path, 'numpy', 'arrayobject.h')):\n print(inc_path, end='')\nexcept:\n pass" + OUTPUT_VARIABLE PYTHON_NUMPY_INCLUDE_DIR) +endif (pybind11_FOUND) + ######################################################################## # Setup doxygen option ######################################################################## @@ -153,7 +166,7 @@ endif(DOXYGEN_FOUND) include(GrComponent) GR_REGISTER_COMPONENT("Python" ENABLE_PYTHON - PYTHONLIBS_FOUND + PYTHONLIBS_FOUND pybind11_FOUND ) GR_REGISTER_COMPONENT("GLFW" ENABLE_GLFW diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 57902c0..18a3532 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -13,6 +13,8 @@ if(NOT PYTHONINTERP_FOUND) return() endif() +add_subdirectory(bindings) + ######################################################################## # Install python sources ######################################################################## diff --git a/python/__init__.py b/python/__init__.py index 6ed2a1b..e01778d 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -4,3 +4,6 @@ # This file is part of gr-fosphor # # SPDX-License-Identifier: GPL-3.0-or-later + +# import pybind11 generated symbols into the iqbalance namespace +from .fosphor_python import * diff --git a/python/bindings/CMakeLists.txt b/python/bindings/CMakeLists.txt new file mode 100644 index 0000000..b7054da --- /dev/null +++ b/python/bindings/CMakeLists.txt @@ -0,0 +1,66 @@ +# Copyright 2011-2020 Free Software Foundation, Inc. +# Copyright 2013-2021 Sylvain Munaut +# +# This file is part of gr-fosphor +# +# SPDX-License-Identifier: GPL-3.0-or-later + +######################################################################## +# Check for pygccxml +######################################################################## +GR_PYTHON_CHECK_MODULE_RAW( + "pygccxml" + "import pygccxml" + PYGCCXML_FOUND + ) + +# Official module is broken, makes too many assumptions that are not true +#include(GrPybind) + +include(GrPython) + +macro(GR_PYBIND_MAKE name updir filter files) + +configure_file(${CMAKE_SOURCE_DIR}/docs/doxygen/pydoc_macros.h ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) + +pybind11_add_module(${name}_python ${files}) + +SET(MODULE_NAME ${name}) +if (${name} STREQUAL gr) + SET(MODULE_NAME "runtime") +endif() + +target_include_directories(${name}_python PUBLIC + ${CMAKE_CURRENT_BINARY_DIR} + ${PYTHON_NUMPY_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${updir}/lib + ${CMAKE_CURRENT_SOURCE_DIR}/${updir}/include + ${PYBIND11_INCLUDE_DIR} +) +target_link_libraries(${name}_python PUBLIC ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} gnuradio-${MODULE_NAME}) +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR + CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(${name}_python PRIVATE -Wno-unused-variable) # disable warnings for docstring templates +endif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR + CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + +endmacro(GR_PYBIND_MAKE) + + + +######################################################################## +# Python Bindings +######################################################################## + +list(APPEND fosphor_python_files + base_sink_c_python.cc + glfw_sink_c_python.cc + qt_sink_c_python.cc + python_bindings.cc) + +GR_PYBIND_MAKE(fosphor + ../.. + gr::fosphor + "${fosphor_python_files}") + +install(TARGETS fosphor_python DESTINATION ${GR_PYTHON_DIR}/gnuradio/fosphor COMPONENT pythonapi) diff --git a/python/bindings/base_sink_c_python.cc b/python/bindings/base_sink_c_python.cc new file mode 100644 index 0000000..b192dca --- /dev/null +++ b/python/bindings/base_sink_c_python.cc @@ -0,0 +1,93 @@ +/* + * Copyright 2013-2021 Sylvain Munaut + * + * This file is part of gr-fosphor + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include + +namespace py = pybind11; + +#include + +#define D(...) "" + +void bind_base_sink_c(py::module& m) +{ + using base_sink_c = gr::fosphor::base_sink_c; + + py::class_> sink_class (m, "base_sink_c", D(base_sink_c)); + + + py::enum_(sink_class, "ui_action") + .value("DB_PER_DIV_UP", base_sink_c::DB_PER_DIV_UP) + .value("DB_PER_DIV_DOWN", base_sink_c::DB_PER_DIV_DOWN) + .value("REF_UP", base_sink_c::REF_UP) + .value("REF_DOWN", base_sink_c::REF_DOWN) + .value("ZOOM_TOGGLE", base_sink_c::ZOOM_TOGGLE) + .value("ZOOM_WIDTH_UP", base_sink_c::ZOOM_WIDTH_UP) + .value("ZOOM_WIDTH_DOWN", base_sink_c::ZOOM_WIDTH_DOWN) + .value("ZOOM_CENTER_UP", base_sink_c::ZOOM_CENTER_UP) + .value("ZOOM_CENTER_DOWN", base_sink_c::ZOOM_CENTER_DOWN) + .value("RATIO_UP", base_sink_c::RATIO_UP) + .value("RATIO_DOWN", base_sink_c::RATIO_DOWN) + .value("FREEZE_TOGGLE", base_sink_c::FREEZE_TOGGLE) + .export_values(); + + py::enum_(sink_class, "mouse_action") + .value("CLICK", base_sink_c::CLICK) + .export_values(); + + py::implicitly_convertible(); + py::implicitly_convertible(); + + sink_class + .def("execute_ui_action", + &base_sink_c::execute_ui_action, + py::arg("action"), + D(base_sink_c,execute_ui_action) + ) + + .def("execute_mouse_action", + &base_sink_c::execute_mouse_action, + py::arg("action"), + py::arg("x"), + py::arg("y"), + D(base_sink_c,execute_mouse_action) + ) + + .def("set_frequency_range", + &base_sink_c::set_frequency_range, + py::arg("center"), + py::arg("span"), + D(base_sink_c,set_frequency_range) + ) + + .def("set_frequency_center", + &base_sink_c::set_frequency_center, + py::arg("center"), + D(base_sink_c,set_frequency_center) + ) + + .def("set_frequency_span", + &base_sink_c::set_frequency_span, + py::arg("span"), + D(base_sink_c,set_frequency_span) + ) + + .def("set_fft_window", + &base_sink_c::set_fft_window, + py::arg("win"), + D(base_sink_c,set_fft_window) + ) + + ; +} diff --git a/python/bindings/glfw_sink_c_python.cc b/python/bindings/glfw_sink_c_python.cc new file mode 100644 index 0000000..cb0f46c --- /dev/null +++ b/python/bindings/glfw_sink_c_python.cc @@ -0,0 +1,35 @@ +/* + * Copyright 2013-2021 Sylvain Munaut + * + * This file is part of gr-fosphor + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include + +namespace py = pybind11; + +#include + +#define D(...) "" + +void bind_glfw_sink_c(py::module& m) +{ + using glfw_sink_c = gr::fosphor::glfw_sink_c; + + py::class_>(m, "glfw_sink_c", D(glfw_sink_c)) + + .def(py::init(&glfw_sink_c::make), + D(glfw_sink_c,make) + ) + + ; +} diff --git a/python/bindings/python_bindings.cc b/python/bindings/python_bindings.cc new file mode 100644 index 0000000..fdfeb32 --- /dev/null +++ b/python/bindings/python_bindings.cc @@ -0,0 +1,43 @@ +/* + * Copyright 2020-2021 Free Software Foundation, Inc. + * Copyright 2013-2021 Sylvain Munaut + * + * This file is part of gr-fosphor + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include + +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include + +namespace py = pybind11; + +void bind_base_sink_c(py::module& m); +void bind_glfw_sink_c(py::module& m); +void bind_qt_sink_c(py::module& m); + +// We need this hack because import_array() returns NULL +// for newer Python versions. +// This function is also necessary because it ensures access to the C API +// and removes a warning. +void* init_numpy() +{ + import_array(); + return NULL; +} + +PYBIND11_MODULE(fosphor_python, m) +{ + // Initialize the numpy C API + // (otherwise we will see segmentation faults) + init_numpy(); + + // Allow access to base block methods + py::module::import("gnuradio.gr"); + + bind_base_sink_c(m); + bind_glfw_sink_c(m); + bind_qt_sink_c(m); +} diff --git a/python/bindings/qt_sink_c_python.cc b/python/bindings/qt_sink_c_python.cc new file mode 100644 index 0000000..c68cb9b --- /dev/null +++ b/python/bindings/qt_sink_c_python.cc @@ -0,0 +1,61 @@ +/* + * Copyright 2013-2021 Sylvain Munaut + * + * This file is part of gr-fosphor + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include + +namespace py = pybind11; + +#include + +#include + +#define D(...) "" + +void bind_qt_sink_c(py::module& m) +{ + using qt_sink_c = gr::fosphor::qt_sink_c; + + py::class_>(m, "qt_sink_c", D(qt_sink_c)) + + .def(py::init(&qt_sink_c::make), + py::arg("parent") = nullptr, + D(qt_sink_c,make) + ) + + .def("exec_", + &qt_sink_c::exec_, + D(qt_sink_c, exec_) + ) + + .def("qwidget", + &qt_sink_c::qwidget, + D(qt_sink_c, qwidget) + ) + + //.def("pyqwidget", + // &qt_sink_c::pyqwidget, + // D(qt_sink_c, pyqwidget) + //) + // For the sip conversion to python to work, the widget object + // needs to be explicitly converted to long long. + .def("pyqwidget", + [](std::shared_ptr p) { + return PyLong_AsLongLong(p->pyqwidget()); + }, + D(qt_sink_c, pyqwidget) + ) + + ; +}