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