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 #include "register_machinery.hpp" 0008 0009 #include <utility> 0010 0011 #include <QColor> 0012 #include <QUuid> 0013 #include <QVariant> 0014 #include <QPointF> 0015 #include <QSizeF> 0016 #include <QVector2D> 0017 #include <QRectF> 0018 #include <QByteArray> 0019 #include <QDateTime> 0020 #include <QDate> 0021 #include <QTime> 0022 #include <QGradient> 0023 0024 #include "app/log/log.hpp" 0025 0026 0027 namespace app::scripting::python { 0028 0029 template<class T> const char* type_name() 0030 { 0031 #if QT_VERSION_MAJOR >= 6 0032 return QMetaType(qMetaTypeId<T>()).name(); 0033 #else 0034 return QMetaType::typeName(qMetaTypeId<T>()); 0035 #endif 0036 } 0037 template<int> struct meta_2_cpp_s; 0038 template<class> struct cpp_2_meta_s; 0039 0040 #define TYPE_NAME(Type) //template<> const char* type_name<Type>() { return #Type; } 0041 #define SETUP_TYPE(MetaInt, Type) \ 0042 TYPE_NAME(Type) \ 0043 template<> struct meta_2_cpp_s<MetaInt> { using type = Type; }; \ 0044 template<> struct cpp_2_meta_s<Type> { static constexpr const int value = MetaInt; }; 0045 0046 template<int meta_type> using meta_2_cpp = typename meta_2_cpp_s<meta_type>::type; 0047 template<class T> constexpr const int cpp_2_meta = cpp_2_meta_s<T>::value; 0048 0049 SETUP_TYPE(QMetaType::Int, int) 0050 SETUP_TYPE(QMetaType::Bool, bool) 0051 SETUP_TYPE(QMetaType::Double, double) 0052 SETUP_TYPE(QMetaType::Float, float) 0053 SETUP_TYPE(QMetaType::UInt, unsigned int) 0054 SETUP_TYPE(QMetaType::Long, long) 0055 SETUP_TYPE(QMetaType::LongLong, long long) 0056 SETUP_TYPE(QMetaType::Short, short) 0057 SETUP_TYPE(QMetaType::ULong, unsigned long) 0058 SETUP_TYPE(QMetaType::ULongLong, unsigned long long) 0059 SETUP_TYPE(QMetaType::UShort, unsigned short) 0060 SETUP_TYPE(QMetaType::QString, QString) 0061 SETUP_TYPE(QMetaType::QColor, QColor) 0062 SETUP_TYPE(QMetaType::QUuid, QUuid) 0063 SETUP_TYPE(QMetaType::QObjectStar, QObject*) 0064 SETUP_TYPE(QMetaType::QVariantList, QVariantList) 0065 SETUP_TYPE(QMetaType::QVariant, QVariant) 0066 SETUP_TYPE(QMetaType::QStringList, QStringList) 0067 SETUP_TYPE(QMetaType::QVariantMap, QVariantMap) 0068 SETUP_TYPE(QMetaType::QVariantHash, QVariantHash) 0069 SETUP_TYPE(QMetaType::QPointF, QPointF) 0070 SETUP_TYPE(QMetaType::QSizeF, QSizeF) 0071 SETUP_TYPE(QMetaType::QSize, QSize) 0072 SETUP_TYPE(QMetaType::QVector2D, QVector2D) 0073 SETUP_TYPE(QMetaType::QRectF, QRectF) 0074 SETUP_TYPE(QMetaType::QByteArray, QByteArray) 0075 SETUP_TYPE(QMetaType::QDateTime, QDateTime) 0076 SETUP_TYPE(QMetaType::QDate, QDate) 0077 SETUP_TYPE(QMetaType::QTime, QTime) 0078 SETUP_TYPE(QMetaType::QImage, QImage) 0079 // If you add stuff here, remember to add it to supported_types too 0080 0081 TYPE_NAME(std::vector<QObject*>) 0082 0083 using supported_types = std::integer_sequence<int, 0084 QMetaType::Bool, 0085 QMetaType::Int, 0086 QMetaType::Double, 0087 QMetaType::Float, 0088 QMetaType::UInt, 0089 QMetaType::Long, 0090 QMetaType::LongLong, 0091 QMetaType::Short, 0092 QMetaType::ULong, 0093 QMetaType::ULongLong, 0094 QMetaType::UShort, 0095 QMetaType::QString, 0096 QMetaType::QColor, 0097 QMetaType::QUuid, 0098 QMetaType::QObjectStar, 0099 QMetaType::QVariantList, 0100 QMetaType::QVariant, 0101 QMetaType::QStringList, 0102 QMetaType::QVariantMap, 0103 QMetaType::QVariantHash, 0104 QMetaType::QPointF, 0105 QMetaType::QSizeF, 0106 QMetaType::QSize, 0107 QMetaType::QVector2D, 0108 QMetaType::QRectF, 0109 QMetaType::QByteArray, 0110 QMetaType::QDateTime, 0111 QMetaType::QDate, 0112 QMetaType::QTime, 0113 QMetaType::QImage 0114 // Ensure new types have SETUP_TYPE 0115 >; 0116 0117 template<class T> QVariant qvariant_from_cpp(const T& t) { return QVariant::fromValue(t); } 0118 template<class T> T qvariant_to_cpp(const QVariant& v) { return v.value<T>(); } 0119 0120 template<> QVariant qvariant_from_cpp<std::string>(const std::string& t) { return QString::fromStdString(t); } 0121 template<> std::string qvariant_to_cpp<std::string>(const QVariant& v) { return v.toString().toStdString(); } 0122 0123 template<> QVariant qvariant_from_cpp<QVariant>(const QVariant& t) { return t; } 0124 template<> QVariant qvariant_to_cpp<QVariant>(const QVariant& v) { return v; } 0125 0126 0127 template<> void qvariant_to_cpp<void>(const QVariant&) {} 0128 0129 0130 template<> 0131 QVariant qvariant_from_cpp<std::vector<QObject*>>(const std::vector<QObject*>& t) 0132 { 0133 QVariantList list; 0134 for ( QObject* obj : t ) 0135 list.push_back(QVariant::fromValue(obj)); 0136 return list; 0137 0138 } 0139 template<> std::vector<QObject*> qvariant_to_cpp<std::vector<QObject*>>(const QVariant& v) 0140 { 0141 std::vector<QObject*> objects; 0142 for ( const QVariant& vi : v.toList() ) 0143 objects.push_back(vi.value<QObject*>()); 0144 return objects; 0145 } 0146 0147 template<int i, template<class FuncT> class Func, class RetT, class... FuncArgs> 0148 bool type_dispatch_impl_step(int meta_type, RetT& ret, FuncArgs&&... args) 0149 { 0150 if ( meta_type != i ) 0151 return false; 0152 0153 ret = Func<meta_2_cpp<i>>::do_the_thing(std::forward<FuncArgs>(args)...); 0154 return true; 0155 } 0156 0157 template<template<class FuncT> class Func, class RetT, class... FuncArgs, int... i> 0158 bool type_dispatch_impl(int meta_type, RetT& ret, std::integer_sequence<int, i...>, FuncArgs&&... args) 0159 { 0160 return (type_dispatch_impl_step<i, Func>(meta_type, ret, std::forward<FuncArgs>(args)...)||...); 0161 } 0162 0163 template<template<class FuncT> class Func, class RetT, class... FuncArgs> 0164 RetT type_dispatch(int meta_type, FuncArgs&&... args) 0165 { 0166 if ( meta_type >= QMetaType::User ) 0167 { 0168 if ( QMetaType(meta_type).flags() & QMetaType::IsEnumeration ) 0169 return Func<int>::do_the_thing(std::forward<FuncArgs>(args)...); 0170 return Func<QObject*>::do_the_thing(std::forward<FuncArgs>(args)...); 0171 } 0172 RetT ret; 0173 type_dispatch_impl<Func>(meta_type, ret, supported_types(), std::forward<FuncArgs>(args)...); 0174 return ret; 0175 } 0176 0177 template<template<class FuncT> class Func, class RetT, class... FuncArgs> 0178 static RetT type_dispatch_maybe_void(int meta_type, FuncArgs&&... args) 0179 { 0180 if ( meta_type == QMetaType::Void ) 0181 return Func<void>::do_the_thing(std::forward<FuncArgs>(args)...); 0182 return type_dispatch<Func, RetT>(meta_type, std::forward<FuncArgs>(args)...); 0183 } 0184 0185 std::string fix_type(QByteArray ba) 0186 { 0187 if ( ba.endsWith('*') || ba.endsWith('&') ) 0188 ba.remove(ba.size()-1, 1); 0189 0190 if ( ba.startsWith("const ") ) 0191 ba.remove(0, 6); 0192 0193 if ( ba == "QString" ) 0194 return "str"; 0195 else if ( ba == "QVariantList" ) 0196 return "list"; 0197 if ( ba == "QStringList" ) 0198 return "List[str]"; 0199 else if ( ba == "double" ) 0200 return "float"; 0201 else if ( ba == "void" ) 0202 return "None"; 0203 else if ( ba == "QImage" ) 0204 return "PIL.Image.Image"; 0205 0206 return ba.toStdString(); 0207 } 0208 0209 0210 template<class CppType> 0211 struct RegisterProperty 0212 { 0213 static PyPropertyInfo do_the_thing(const QMetaProperty& prop) 0214 { 0215 PyPropertyInfo py; 0216 py.name = prop.name(); 0217 std::string sig = "Type: " + fix_type(prop.typeName()); 0218 py.get = py::cpp_function( 0219 [prop](const QObject* o) { return qvariant_to_cpp<CppType>(prop.read(o)); }, 0220 py::return_value_policy::automatic_reference, 0221 sig.c_str() 0222 ); 0223 0224 if ( prop.isWritable() ) 0225 py.set = py::cpp_function([prop](QObject* o, const CppType& v) { 0226 prop.write(o, qvariant_from_cpp<CppType>(v)); 0227 }); 0228 return py; 0229 } 0230 }; 0231 0232 PyPropertyInfo register_property(const QMetaProperty& prop, const QMetaObject& meta) 0233 { 0234 if ( !prop.isScriptable() ) 0235 return {}; 0236 0237 PyPropertyInfo pyprop = type_dispatch<RegisterProperty, PyPropertyInfo>(prop.userType(), prop); 0238 if ( !pyprop.name ) 0239 log::LogStream("Python", "", log::Error) << "Invalid property" << meta.className() << "::" << prop.name() << "of type" << prop.userType() << prop.typeName(); 0240 return pyprop; 0241 } 0242 0243 class ArgumentBuffer 0244 { 0245 public: 0246 ArgumentBuffer(const QMetaMethod& method) : method(method) {} 0247 ArgumentBuffer(const ArgumentBuffer&) = delete; 0248 ArgumentBuffer& operator=(const ArgumentBuffer&) = delete; 0249 ~ArgumentBuffer() 0250 { 0251 for ( int i = 0; i < destructors_used; i++) 0252 { 0253 destructors[i]->destruct(); 0254 delete destructors[i]; 0255 } 0256 } 0257 0258 template<class CppType> 0259 const char* object_type_name(const CppType&) 0260 { 0261 return type_name<CppType>(); 0262 } 0263 0264 std::string object_type_name(QObject* value) 0265 { 0266 std::string s = value->metaObject()->className(); 0267 std::string target = method.parameterTypes()[arguments].toStdString(); 0268 if ( !target.empty() && target.back() == '*' ) 0269 { 0270 target.pop_back(); 0271 if ( s != target ) 0272 { 0273 for ( auto mo = value->metaObject()->superClass(); mo; mo = mo->superClass() ) 0274 { 0275 std::string moname = mo->className(); 0276 if ( moname == target ) 0277 return target + "*"; 0278 } 0279 } 0280 } 0281 return s + "*"; 0282 } 0283 0284 template<class CppType> 0285 CppType* allocate(const CppType& value) 0286 { 0287 if ( avail() < int(sizeof(CppType)) ) 0288 throw py::type_error("Cannot allocate argument"); 0289 0290 CppType* addr = new (next_mem()) CppType; 0291 buffer_used += sizeof(CppType); 0292 names[arguments] = object_type_name(value); 0293 generic_args[arguments] = { names[arguments].c_str(), addr }; 0294 ensure_destruction(addr); 0295 arguments += 1; 0296 *addr = value; 0297 return addr; 0298 } 0299 0300 template<class CppType> 0301 void allocate_return_type(const char* name) 0302 { 0303 if ( avail() < int(sizeof(CppType)) ) 0304 throw py::type_error("Cannot allocate return value"); 0305 0306 CppType* addr = new (next_mem()) CppType; 0307 buffer_used += sizeof(CppType); 0308 ret = { name, addr }; 0309 ensure_destruction(addr); 0310 ret_addr = addr; 0311 } 0312 0313 template<class CppType> 0314 CppType return_value() 0315 { 0316 return *static_cast<CppType*>(ret_addr); 0317 } 0318 0319 const QGenericArgument& arg(int i) const { return generic_args[i]; } 0320 0321 const QGenericReturnArgument& return_arg() const { return ret; } 0322 0323 private: 0324 class Destructor 0325 { 0326 public: 0327 Destructor() = default; 0328 Destructor(const Destructor&) = delete; 0329 Destructor& operator=(const Destructor&) = delete; 0330 virtual ~Destructor() = default; 0331 virtual void destruct() const = 0; 0332 }; 0333 0334 template<class CppType> 0335 struct DestructorImpl : public Destructor 0336 { 0337 DestructorImpl(CppType* addr) : addr(addr) {} 0338 0339 void destruct() const override 0340 { 0341 addr->~CppType(); 0342 } 0343 0344 CppType* addr; 0345 }; 0346 0347 int arguments = 0; 0348 int buffer_used = 0; 0349 std::array<char, 128> buffer; 0350 std::array<Destructor*, 9> destructors; 0351 std::array<QGenericArgument, 9> generic_args; 0352 std::array<std::string, 9> names; 0353 QGenericReturnArgument ret; 0354 void* ret_addr = nullptr; 0355 QMetaMethod method; 0356 int destructors_used = 0; 0357 0358 0359 int avail() { return buffer.size() - buffer_used; } 0360 void* next_mem() { return buffer.data() + buffer_used; } 0361 0362 0363 template<class CppType> 0364 std::enable_if_t<std::is_pod_v<CppType>> ensure_destruction(CppType*) {} 0365 0366 0367 template<class CppType> 0368 std::enable_if_t<!std::is_pod_v<CppType>> ensure_destruction(CppType* addr) 0369 { 0370 destructors[destructors_used] = new DestructorImpl<CppType>(addr); 0371 destructors_used++; 0372 } 0373 }; 0374 0375 template<> void ArgumentBuffer::allocate_return_type<void>(const char*){} 0376 template<> void ArgumentBuffer::return_value<void>(){} 0377 0378 0379 template<class CppType> 0380 struct ConvertArgument 0381 { 0382 static bool do_the_thing(const py::handle& val, ArgumentBuffer& buf) 0383 { 0384 buf.allocate<CppType>(val.cast<CppType>()); 0385 return true; 0386 } 0387 }; 0388 0389 bool convert_argument(int meta_type, const py::handle& value, ArgumentBuffer& buf) 0390 { 0391 return type_dispatch<ConvertArgument, bool>(meta_type, value, buf); 0392 } 0393 0394 template<class ReturnType> 0395 struct RegisterMethod 0396 { 0397 static PyMethodInfo do_the_thing(const QMetaMethod& meth, py::handle& handle) 0398 { 0399 PyMethodInfo py; 0400 py.name = meth.name().constData(); 0401 0402 std::string signature = "Signature:\n"; 0403 signature += meth.name().toStdString(); 0404 signature += "(self"; 0405 auto names = meth.parameterNames(); 0406 auto types = meth.parameterTypes(); 0407 for ( int i = 0; i < meth.parameterCount(); i++ ) 0408 { 0409 signature += ", "; 0410 signature += names[i].toStdString(); 0411 signature += ": "; 0412 signature += fix_type(types[i]); 0413 0414 if ( meth.parameterType(i) == QMetaType::UnknownType ) 0415 { 0416 auto cls = py::str(handle.attr("__name__")); 0417 log::LogStream("Python", "", log::Error) 0418 << "Invalid parameter" << QString::fromStdString(cls) << "::" << meth.name() 0419 << i << names[i] 0420 << "of type" << meth.parameterType(i) << types[i]; 0421 return {}; 0422 } 0423 } 0424 signature += ") -> "; 0425 signature += fix_type(meth.typeName()); 0426 0427 py.method = py::cpp_function( 0428 [meth](QObject* o, py::args args) -> ReturnType 0429 { 0430 int len = py::len(args); 0431 if ( len > 9 ) 0432 throw pybind11::value_error("Invalid argument count"); 0433 0434 ArgumentBuffer argbuf(meth); 0435 0436 argbuf.allocate_return_type<ReturnType>(meth.typeName()); 0437 0438 for ( int i = 0; i < len; i++ ) 0439 { 0440 if ( !convert_argument(meth.parameterType(i), args[i], argbuf) ) 0441 throw pybind11::value_error("Invalid argument"); 0442 } 0443 0444 // Calling by name from QMetaObject ensures that default arguments work correctly 0445 bool ok = QMetaObject::invokeMethod( 0446 o, 0447 meth.name().constData(), 0448 Qt::DirectConnection, 0449 argbuf.return_arg(), 0450 argbuf.arg(0), 0451 argbuf.arg(1), 0452 argbuf.arg(2), 0453 argbuf.arg(3), 0454 argbuf.arg(4), 0455 argbuf.arg(5), 0456 argbuf.arg(6), 0457 argbuf.arg(7), 0458 argbuf.arg(8), 0459 argbuf.arg(9) 0460 ); 0461 if ( !ok ) 0462 throw pybind11::value_error("Invalid method invocation"); 0463 return argbuf.return_value<ReturnType>(); 0464 }, 0465 py::name(py.name), 0466 py::is_method(handle), 0467 py::sibling(py::getattr(handle, py.name, py::none())), 0468 py::return_value_policy::automatic_reference, 0469 signature.c_str() 0470 ); 0471 0472 return py; 0473 } 0474 }; 0475 0476 PyMethodInfo register_method(const QMetaMethod& meth, py::handle& handle, const QMetaObject& cls) 0477 { 0478 if ( meth.access() != QMetaMethod::Public ) 0479 return {}; 0480 if ( meth.methodType() != QMetaMethod::Method && meth.methodType() != QMetaMethod::Slot ) 0481 return {}; 0482 0483 if ( meth.parameterCount() > 9 ) 0484 { 0485 log::LogStream("Python", "", log::Error) << "Too many arguments for method " << cls.className() << "::" << meth.name() << ": " << meth.parameterCount(); 0486 return {}; 0487 } 0488 0489 PyMethodInfo pymeth = type_dispatch_maybe_void<RegisterMethod, PyMethodInfo>(meth.returnType(), meth, handle); 0490 if ( !pymeth.name ) 0491 log::LogStream("Python", "", log::Error) << "Invalid method" << cls.className() << "::" << meth.name() << "return type" << meth.returnType() << meth.typeName(); 0492 return pymeth; 0493 0494 } 0495 0496 template<int i> 0497 bool qvariant_type_caster_load_impl(QVariant& into, const pybind11::handle& src) 0498 { 0499 auto caster = pybind11::detail::make_caster<meta_2_cpp<i>>(); 0500 if ( caster.load(src, false) ) 0501 { 0502 into = QVariant::fromValue(pybind11::detail::cast_op<meta_2_cpp<i>>(caster)); 0503 return true; 0504 } 0505 return false; 0506 } 0507 0508 template<> 0509 bool qvariant_type_caster_load_impl<QMetaType::QVariant>(QVariant&, const pybind11::handle&) { return false; } 0510 0511 template<int... i> 0512 bool qvariant_type_caster_load(QVariant& into, const pybind11::handle& src, std::integer_sequence<int, i...>) 0513 { 0514 return (qvariant_type_caster_load_impl<i>(into, src)||...); 0515 } 0516 0517 template<int i> 0518 bool qvariant_type_caster_cast_impl( 0519 pybind11::handle& into, const QVariant& src, 0520 pybind11::return_value_policy policy, const pybind11::handle& parent) 0521 { 0522 if ( src.userType() == i ) 0523 { 0524 into = pybind11::detail::make_caster<meta_2_cpp<i>>::cast(src.value<meta_2_cpp<i>>(), policy, parent); 0525 return true; 0526 } 0527 return false; 0528 } 0529 0530 template<> 0531 bool qvariant_type_caster_cast_impl<QMetaType::QVariant>( 0532 pybind11::handle&, const QVariant&, pybind11::return_value_policy, const pybind11::handle&) 0533 { 0534 return false; 0535 } 0536 0537 0538 template<int... i> 0539 bool qvariant_type_caster_cast( 0540 pybind11::handle& into, 0541 const QVariant& src, 0542 pybind11::return_value_policy policy, 0543 const pybind11::handle& parent, 0544 std::integer_sequence<int, i...> 0545 ) 0546 { 0547 return (qvariant_type_caster_cast_impl<i>(into, src, policy, parent)||...); 0548 } 0549 0550 } // namespace app::scripting::python 0551 0552 using namespace app::scripting::python; 0553 0554 0555 0556 0557 std::map<int, pybind11::detail::type_caster<QVariant>::CustomConverter> pybind11::detail::type_caster<QVariant>::custom_converters; 0558 0559 bool pybind11::detail::type_caster<QVariant>::load(handle src, bool) 0560 { 0561 if ( src.ptr() == Py_None ) 0562 { 0563 value = QVariant(); 0564 return true; 0565 } 0566 0567 if ( qvariant_type_caster_load(value, src, supported_types()) ) 0568 return true; 0569 0570 for ( const auto& p : custom_converters ) 0571 if ( p.second.load(src, value) ) 0572 return true; 0573 return false; 0574 } 0575 0576 pybind11::handle pybind11::detail::type_caster<QVariant>::cast(QVariant src, return_value_policy policy, handle parent) 0577 { 0578 if ( src.isNull() ) 0579 return pybind11::none(); 0580 0581 policy = py::return_value_policy::automatic_reference; 0582 0583 int meta_type = src.userType(); 0584 if ( meta_type >= QMetaType::User ) 0585 { 0586 if ( QMetaType(meta_type).flags() & QMetaType::IsEnumeration ) 0587 return pybind11::detail::make_caster<int>::cast(src.value<int>(), policy, parent); 0588 else if ( meta_type == qMetaTypeId<QGradientStops>() ) 0589 return pybind11::detail::make_caster<QGradientStops>::cast(src.value<QGradientStops>(), policy, parent); 0590 0591 auto it = custom_converters.find(meta_type); 0592 if ( it != custom_converters.end() ) 0593 return it->second.cast(src, policy, parent); 0594 return pybind11::detail::make_caster<QObject*>::cast(src.value<QObject*>(), policy, parent); 0595 } 0596 0597 pybind11::handle ret; 0598 qvariant_type_caster_cast(ret, src, policy, parent, supported_types()); 0599 return ret; 0600 } 0601 0602 0603 bool pybind11::detail::type_caster<QUuid>::load(handle src, bool ic) 0604 { 0605 if ( isinstance(src, pybind11::module_::import("uuid").attr("UUID")) ) 0606 src = py::str(src); 0607 type_caster<QString> stdc; 0608 if ( stdc.load(src, ic) ) 0609 { 0610 value = QUuid::fromString((const QString &)stdc); 0611 return true; 0612 } 0613 return false; 0614 } 0615 0616 pybind11::handle pybind11::detail::type_caster<QUuid>::cast(QUuid src, return_value_policy policy, handle parent) 0617 { 0618 auto str = type_caster<QString>::cast(src.toString(), policy, parent); 0619 return pybind11::module_::import("uuid").attr("UUID")(str).release(); 0620 } 0621 0622 0623 0624 bool pybind11::detail::type_caster<QImage>::load(handle src, bool) 0625 { 0626 if ( !isinstance(src, pybind11::module_::import("PIL.Image").attr("Image")) ) 0627 return false; 0628 0629 py::object obj = py::reinterpret_borrow<py::object>(src); 0630 std::string mode = obj.attr("mode").cast<std::string>(); 0631 QImage::Format format; 0632 if ( mode == "RGBA" ) 0633 { 0634 format = QImage::Format_RGBA8888; 0635 } 0636 else if ( mode == "RGB" ) 0637 { 0638 format = QImage::Format_RGB888; 0639 } 0640 else if ( mode == "RGBa" ) 0641 { 0642 format = QImage::Format_RGBA8888_Premultiplied; 0643 } 0644 else if ( mode == "RGBX" ) 0645 { 0646 format = QImage::Format_RGBX8888; 0647 } 0648 else 0649 { 0650 format = QImage::Format_RGBA8888; 0651 obj = obj.attr("convert")("RGBA"); 0652 } 0653 0654 std::string data = obj.attr("tobytes")().cast<std::string>(); 0655 0656 int width = obj.attr("width").cast<int>(); 0657 int height = obj.attr("height").cast<int>(); 0658 value = QImage(width, height, format); 0659 if ( data.size() != std::size_t(value.sizeInBytes()) ) 0660 return false; 0661 std::memcpy(value.bits(), data.data(), data.size()); 0662 return true; 0663 } 0664 0665 pybind11::handle pybind11::detail::type_caster<QImage>::cast(QImage src, return_value_policy, handle) 0666 { 0667 auto mod = pybind11::module_::import("PIL.Image"); 0668 auto frombytes = mod.attr("frombytes"); 0669 0670 const char* mode; 0671 0672 switch ( src.format() ) 0673 { 0674 case QImage::Format_Invalid: 0675 return mod.attr("Image")().release(); 0676 case QImage::Format_RGB888: 0677 mode = "RGB"; 0678 break; 0679 case QImage::Format_RGBA8888: 0680 mode = "RGBA"; 0681 break; 0682 case QImage::Format_RGBA8888_Premultiplied: 0683 mode = "RGBa"; 0684 break; 0685 case QImage::Format_RGBX8888: 0686 mode = "RGBX"; 0687 break; 0688 default: 0689 mode = "RGBA"; 0690 src = src.convertToFormat(QImage::Format_RGBA8888); 0691 break; 0692 } 0693 0694 py::tuple size(2); 0695 size[0] = py::int_(src.width()); 0696 size[1] = py::int_(src.height()); 0697 0698 auto image = frombytes( 0699 mode, 0700 size, 0701 py::bytes((const char*)src.bits(), src.sizeInBytes()) 0702 ); 0703 0704 return image.release(); 0705 }