diff options
Diffstat (limited to 'libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details')
5 files changed, 449 insertions, 0 deletions
diff --git a/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_enum.h b/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_enum.h new file mode 100644 index 0000000..45afd0c --- /dev/null +++ b/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_enum.h @@ -0,0 +1,59 @@ +#ifndef PYTHON_PYBIND11_QT_DETAILS_ENUM_HPP +#define PYTHON_PYBIND11_QT_DETAILS_ENUM_HPP + +#include <type_traits> + +#include <pybind11/pybind11.h> + +#include "pybind11_qt_utils.h" + +namespace pybind11::detail::qt { + + // EnumData, with static members (const char[]) + // - package: name of the Python package containing the enum (e.g., + // PyQt6.QtCore) + // - name: full path to the enum, e.g. Qt.QGlobalColor + // + template <typename T> + struct EnumData; + + // template class for most Qt types that have Python equivalent (QWidget, + // etc.) + // + template <class Enum> + struct qt_enum_caster { + + public: + PYBIND11_TYPE_CASTER(Enum, EnumData<Enum>::package + const_name(".") + + EnumData<Enum>::name); + + bool load(pybind11::handle src, bool) + { + if (PyLong_Check(src.ptr())) { + value = static_cast<Enum>(PyLong_AsLong(src.ptr())); + return true; + } + + auto pyenum = + get_attr_rec(EnumData<Enum>::package.text, EnumData<Enum>::name.text); + + if (isinstance(src, pyenum)) { + value = static_cast<Enum>(src.attr("value").cast<int>()); + return true; + } + + return false; + } + + static pybind11::handle cast(Enum src, + pybind11::return_value_policy /* policy */, + pybind11::handle /* parent */) + { + auto pyenum = + get_attr_rec(EnumData<Enum>::package.text, EnumData<Enum>::name.text); + return pyenum(static_cast<int>(src)); + } + }; +} // namespace pybind11::detail::qt + +#endif diff --git a/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_qlist.h b/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_qlist.h new file mode 100644 index 0000000..e640725 --- /dev/null +++ b/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_qlist.h @@ -0,0 +1,52 @@ +#ifndef PYTHON_PYBIND11_QT_DETAILS_QLIST_HPP +#define PYTHON_PYBIND11_QT_DETAILS_QLIST_HPP + +#include <pybind11/pybind11.h> +#include <pybind11/stl.h> + +namespace pybind11::detail::qt { + + // helper class for QList to construct from any proper iterable + // + template <typename Type, typename Value> + struct qlist_caster { + using value_conv = make_caster<Value>; + + bool load(handle src, bool convert) + { + if (!isinstance<iterable>(src) || isinstance<bytes>(src) || + isinstance<str>(src)) { + return false; + } + auto s = reinterpret_borrow<iterable>(src); + value.clear(); + + if (isinstance<sequence>(src)) { + value.reserve(s.cast<sequence>().size()); + } + for (auto it : s) { + value_conv conv; + if (!conv.load(it, convert)) { + return false; + } + value.push_back(cast_op<Value&&>(std::move(conv))); + } + return true; + } + + template <typename T> + static handle cast(T&& src, return_value_policy policy, handle parent) + { + return list_caster<QList<Value>, Value>{}.cast(std::forward<T>(src), policy, + parent); + } + + // we type these as "Sequence" even if these can be constructed from Iterable, + // otherwise the return type will be typed as "Iterable" which is problematic + PYBIND11_TYPE_CASTER(Type, const_name("Sequence[") + value_conv::name + + const_name("]")); + }; + +} // namespace pybind11::detail::qt + +#endif diff --git a/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_qmap.h b/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_qmap.h new file mode 100644 index 0000000..ea743b7 --- /dev/null +++ b/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_qmap.h @@ -0,0 +1,70 @@ +#ifndef PYTHON_PYBIND11_QT_DETAILS_QMAP_HPP +#define PYTHON_PYBIND11_QT_DETAILS_QMAP_HPP + +#include <pybind11/pybind11.h> + +namespace pybind11::detail::qt { + + // helper class for QMap because QMap do not follow the standard std:: maps + // interface, for other containers, the pybind11 built-in xxx_caster works + // + // this code is basically a copy/paste from the pybind11 stl stuff with + // minor modifications + // + template <typename Type, typename Key, typename Value> + struct qmap_caster { + using key_conv = make_caster<Key>; + using value_conv = make_caster<Value>; + + bool load(handle src, bool convert) + { + if (!isinstance<dict>(src)) { + return false; + } + auto d = reinterpret_borrow<dict>(src); + value.clear(); + for (auto it : d) { + key_conv kconv; + value_conv vconv; + if (!kconv.load(it.first.ptr(), convert) || + !vconv.load(it.second.ptr(), convert)) { + return false; + } + value[cast_op<Key&&>(std::move(kconv))] = + cast_op<Value&&>(std::move(vconv)); + } + return true; + } + + template <typename T> + static handle cast(T&& src, return_value_policy policy, handle parent) + { + dict d; + return_value_policy policy_key = policy; + return_value_policy policy_value = policy; + if (!std::is_lvalue_reference<T>::value) { + policy_key = return_value_policy_override<Key>::policy(policy_key); + policy_value = + return_value_policy_override<Value>::policy(policy_value); + } + for (auto it = src.begin(); it != src.end(); ++it) { + auto key = reinterpret_steal<object>( + key_conv::cast(forward_like<T>(it.key()), policy_key, parent)); + auto value = reinterpret_steal<object>(value_conv::cast( + forward_like<T>(it.value()), policy_value, parent)); + if (!key || !value) { + return handle(); + } + d[key] = value; + } + return d.release(); + } + + PYBIND11_TYPE_CASTER(Type, const_name("Dict[") + key_conv::name + + const_name(", ") + value_conv::name + + const_name("]")); + }; + +} // namespace pybind11::detail::qt + +#endif diff --git a/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_sip.h b/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_sip.h new file mode 100644 index 0000000..556a980 --- /dev/null +++ b/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_sip.h @@ -0,0 +1,221 @@ +#ifndef PYTHON_PYBIND11_QT_DETAILS_SIP_HPP +#define PYTHON_PYBIND11_QT_DETAILS_SIP_HPP + +#include <type_traits> + +#include <pybind11/pybind11.h> + +#include <iostream> +#include <pybind11/iostream.h> + +#include "../pybind11_qt_holder.h" + +struct _sipTypeDef; +typedef struct _sipTypeDef sipTypeDef; + +struct _sipSimpleWrapper; +typedef struct _sipSimpleWrapper sipSimpleWrapper; + +struct _sipWrapper; +typedef struct _sipWrapper sipWrapper; + +namespace pybind11::detail::qt { + + // helper functions to avoid bringing <sip.h> in this header + namespace sip { + + // extract the underlying data if present from the equivalent PyQt object + void* extract_data(PyObject*); + + const sipTypeDef* api_find_type(const char* type); + int api_can_convert_to_type(PyObject* pyObj, const sipTypeDef* td, int flags); + + void api_transfer_to(PyObject* self, PyObject* owner); + void api_transfer_back(PyObject* self); + PyObject* api_convert_from_type(void* cpp, const sipTypeDef* td, + PyObject* transferObj); + } // namespace sip + + template <typename T, class = void> + struct MetaData; + + template <typename T> + struct MetaData<T, std::enable_if_t<std::is_pointer_v<T>>> + : MetaData<std::remove_pointer_t<T>> {}; + + // template class for most Qt types that have Python equivalent (QWidget, + // etc.) + // + template <class QClass> + struct qt_type_caster { + + static constexpr bool is_pointer = std::is_pointer_v<QClass>; + using pointer = std::conditional_t<is_pointer, QClass, QClass*>; + + QClass value; + + public: + static constexpr auto name = MetaData<QClass>::python_name; + + operator pointer() + { + if constexpr (is_pointer) { + return value; + } + else { + return &value; + } + } + + // pybind11 requires operator T&() & and operator T&&() && but here we want to + // use SFINAE with is_pointer so we need to template the operator + // + // having a template <class U> operator U&&() does not work since it will not + // deduce the proper return type for QClass&& or QClass& so we have two separate + // overloads, and in each one, U is actually a reference type (lvalue or rvalue) + // + + template <class U, + std::enable_if_t<std::is_same_v<std::remove_reference_t<U>, QClass> && + std::is_lvalue_reference_v<U> && !is_pointer, + int> = 0> + operator U() + { + return value; + } + + template <class U, + std::enable_if_t<std::is_same_v<std::remove_reference_t<U>, QClass> && + std::is_rvalue_reference_v<U> && !is_pointer, + int> = 0> + operator U() && + { + return std::move(value); + } + + template <typename T> + using cast_op_type = + std::conditional_t<is_pointer, QClass, movable_cast_op_type<T>>; + + bool load(pybind11::handle src, bool) + { + // special check for none for pointer classes + if constexpr (is_pointer) { + if (src.is_none()) { + value = nullptr; + return true; + } + } + + const auto* type = sip::api_find_type(MetaData<QClass>::class_name); + if (type == nullptr) { + return false; + } + if (!sip::api_can_convert_to_type(src.ptr(), type, 0)) { + return false; + } + + // this would transfer responsibility for deconstructing the + // object to C++, but pybind11 assumes l-value converters (such + // as this) don't do that instead, this should be called within + // the wrappers for functions which return deletable pointers. + // + // sipAPI()->api_transfer_to(objPtr, Py_None); + // + void* const data = sip::extract_data(src.ptr()); + + if (data) { + if constexpr (is_pointer) { + value = reinterpret_cast<QClass>(data); + + // transfer ownership + sip::api_transfer_to(src.ptr(), Py_None); + + // tie the py::object to the C++ one + new pybind11::detail::qt::qobject_holder_impl(value); + } + else { + value = *reinterpret_cast<QClass*>(data); + } + return true; + } + else { + return false; + } + } + + template < + typename T, + std::enable_if_t<std::is_same<QClass, std::remove_cv_t<T>>::value, int> = 0> + static handle cast(T* src, return_value_policy policy, handle parent) + { + // note: when QClass is a pointer type, e.g. a QWidget*, T is a + // pointer to pointer, so we can defer to the standard cast() + + if (!src) { + return none().release(); + } + + if (!is_pointer && policy == return_value_policy::take_ownership) { + auto h = cast(std::move(*src), policy, parent); + delete src; + return h; + } + return cast(*src, policy, parent); + } + + static pybind11::handle cast(QClass src, pybind11::return_value_policy policy, + pybind11::handle /* parent */) + { + if constexpr (is_pointer) { + if (!src) { + return none().release(); + } + } + + const sipTypeDef* type = sip::api_find_type(MetaData<QClass>::class_name); + if (type == nullptr) { + return Py_None; + } + + PyObject* sipObj; + void* sipData; + + if constexpr (is_pointer) { + sipData = src; + } + else if (std::is_copy_assignable_v<QClass>) { + // we send to SIP a newly allocated object, and transfer the + // owernship to it + sipData = + new QClass(policy == ::pybind11::return_value_policy::take_ownership + ? std::move(src) + : src); + } + else { + sipData = &src; + } + + sipObj = sip::api_convert_from_type(sipData, type, 0); + + if (sipObj == nullptr) { + return Py_None; + } + + // ensure Python deletes the C++ component + if constexpr (!is_pointer) { + sip::api_transfer_back(sipObj); + } + else { + if (policy == return_value_policy::take_ownership) { + sip::api_transfer_back(sipObj); + } + } + + return sipObj; + } + }; + +} // namespace pybind11::detail::qt + +#endif diff --git a/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_utils.h b/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_utils.h new file mode 100644 index 0000000..c877c15 --- /dev/null +++ b/libs/plugin_python/src/pybind11-qt/include/pybind11_qt/details/pybind11_qt_utils.h @@ -0,0 +1,47 @@ +#ifndef PYTHON_PYBIND11_QT_DETAILS_UTILS_HPP +#define PYTHON_PYBIND11_QT_DETAILS_UTILS_HPP + +#include <string> +#include <string_view> + +#include <pybind11/pybind11.h> + +namespace pybind11::detail::qt { + + /** + * @brief Convert a XXX::YYY compile time string to a XXX.YYY compile time + * string. Only one :: is allowed. + * + */ + template <size_t N> + constexpr descr<N - 2> qt_name_cpp2py(const char (&name)[N]) + { + descr<N - 2> res; + for (std::size_t i = 0, j = 0; i < N - 2; ++i) { + + res.text[i] = name[j]; + + if (res.text[i] == ':') { + res.text[i] = '.'; + j += 2; + } + else { + ++j; + } + } + return res; + } + + /** + * @brief Retrieve the class from the given package at the given path + * + * @param package Name of the module. + * @param path Path to the class/object in the module. + * + * @return the object at the given path in the given module + */ + pybind11::object get_attr_rec(std::string_view package, std::string_view path); + +} // namespace pybind11::detail::qt + +#endif |
