File indexing completed on 2024-11-24 03:56:27

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #pragma once
0008 
0009 #include <cstring>
0010 
0011 #include <QMetaProperty>
0012 #include <QMetaEnum>
0013 #include <QDebug>
0014 
0015 #include "app/scripting/python/casters.hpp"
0016 #include "app/scripting/python/attribute.hpp"
0017 
0018 namespace py = pybind11;
0019 
0020 namespace app::scripting::python {
0021 
0022 struct PY_HIDDEN PyPropertyInfo
0023 {
0024     const char* name = nullptr;
0025     py::cpp_function get;
0026     py::cpp_function set;
0027 };
0028 
0029 struct PY_HIDDEN PyMethodInfo
0030 {
0031     const char* name = nullptr;
0032     py::cpp_function method;
0033 };
0034 
0035 struct PY_HIDDEN PyEnumInfo
0036 {
0037     const char* name = nullptr;
0038     py::handle enum_handle;
0039 };
0040 
0041 PyPropertyInfo register_property(const QMetaProperty& prop, const QMetaObject& cls);
0042 PyMethodInfo register_method(const QMetaMethod& meth, py::handle& handle, const QMetaObject& cls);
0043 
0044 template<class EnumT>
0045 PyEnumInfo register_enum(const QMetaEnum& meta, py::handle& scope)
0046 {
0047     py::enum_<EnumT> pyenum(scope, meta.name());
0048     for ( int i = 0; i < meta.keyCount(); i++ )
0049         pyenum.value(meta.key(i), EnumT(meta.value(i)));
0050 
0051     return {meta.name(), pyenum};
0052 }
0053 
0054 
0055 template<class... Enums>
0056 struct enums;
0057 
0058 template<class EnumT, class... Others>
0059 struct enums<EnumT, Others...>
0060     : public enums<Others...>
0061 {
0062     void process(py::handle& scope, std::vector<PyEnumInfo>& out)
0063     {
0064         out.push_back(register_enum<EnumT>(QMetaEnum::fromType<EnumT>(), scope));
0065         enums<Others...>::process(scope, out);
0066     }
0067 };
0068 
0069 template<>
0070 struct enums<>
0071 {
0072     void process(py::handle&, std::vector<PyEnumInfo>&) {}
0073 };
0074 
0075 
0076 template<class CppClass, class... Args>
0077 py::class_<CppClass, Args...> declare_from_meta(py::handle scope)
0078 {
0079     const QMetaObject& meta = CppClass::staticMetaObject;
0080     const char* name = meta.className();
0081     const char* clean_name = std::strrchr(name, ':');
0082     if ( clean_name == nullptr )
0083         clean_name = name;
0084     else
0085         clean_name++;
0086 
0087     return py::class_<CppClass, Args...> (scope, clean_name);
0088 }
0089 
0090 template<class CppClass, class... Args, class... Enums>
0091 py::class_<CppClass, Args...> register_from_meta(py::handle scope, enums<Enums...> reg_enums = {})
0092 {
0093     py::class_<CppClass, Args...> reg = declare_from_meta<CppClass, Args...>(scope);
0094     register_from_meta(reg, reg_enums);
0095     return reg;
0096 }
0097 
0098 template<class CppClass, class... Args, class... Enums>
0099 py::class_<CppClass, Args...>& register_from_meta(py::class_<CppClass, Args...>& reg, enums<Enums...> reg_enums = {})
0100 {
0101     const QMetaObject& meta = CppClass::staticMetaObject;
0102 
0103     for ( int i = meta.propertyOffset(); i < meta.propertyCount(); i++ )
0104     {
0105         PyPropertyInfo pyprop = register_property(meta.property(i), meta);
0106         if ( pyprop.name )
0107             reg.def_property(pyprop.name, pyprop.get, pyprop.set, "");
0108     }
0109 
0110     for ( int i = meta.methodOffset(); i < meta.methodCount(); i++ )
0111     {
0112         PyMethodInfo pymeth = register_method(meta.method(i), reg, meta);
0113         if ( pymeth.name )
0114             reg.attr(pymeth.name) = pymeth.method;
0115     }
0116 
0117     if ( meta.classInfoOffset() < meta.classInfoCount() )
0118     {
0119         py::dict classinfo;
0120 
0121         for ( int i = meta.classInfoOffset(); i < meta.classInfoCount(); i++ )
0122         {
0123             auto info = meta.classInfo(i);
0124             classinfo[info.name()] = info.value();
0125         }
0126 
0127         reg.attr("__classinfo__") = classinfo;
0128     }
0129 
0130     std::vector<PyEnumInfo> enum_info;
0131     reg_enums.process(reg, enum_info);
0132     for ( const auto& info : enum_info )
0133         reg.attr(info.name) = info.enum_handle;
0134 
0135     return reg;
0136 }
0137 
0138 namespace detail {
0139 
0140 template<class T>
0141 QString qdebug_operator_to_string(const T& o)
0142 {
0143     QString data;
0144     QDebug(&data) << o;
0145     return data;
0146 }
0147 
0148 } // namespace detail
0149 
0150 template<class T>
0151 auto qdebug_operator_to_string()
0152 {
0153     return &detail::qdebug_operator_to_string<T>;
0154 }
0155 
0156 } // namespace app::scripting::python