aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp
diff options
context:
space:
mode:
authorBrett Stottlemyer <bstottle@ford.com>2024-12-18 10:33:56 -0500
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2025-03-13 16:28:42 +0100
commit19abd816e73bebdd489408d0a3b7676822bff39c (patch)
tree8459ae9401f5e190995b3e24b6ae6968cf457baf /sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp
parent3c66c456aeab597b7cb046f81c7f015433bb57a4 (diff)
Make Remote Objects usable beyond Models
While present, the Qt Remote Objects bindings to Python have not been very useful. The only usable components were those based on QAbstractItemModel, due to the lack of a way to interpret .rep files from Python. This addresses that limitation. Fixes: PYSIDE-862 Change-Id: Ice57c0c64f11c3c7e74d50ce3c48617bd9b422a3 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> Reviewed-by: Brett Stottlemyer <brett.stottlemyer@gmail.com>
Diffstat (limited to 'sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp')
-rw-r--r--sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp158
1 files changed, 158 insertions, 0 deletions
diff --git a/sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp b/sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp
new file mode 100644
index 000000000..1f92224f5
--- /dev/null
+++ b/sources/pyside6/libpysideremoteobjects/pysidedynamicenum.cpp
@@ -0,0 +1,158 @@
+// Copyright (C) 2025 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "pysidedynamicenum_p.h"
+#include "pysidedynamiccommon_p.h"
+
+#include <autodecref.h>
+#include <sbkconverter.h>
+#include <sbkenum.h>
+
+#include <QtCore/qmetaobject.h>
+
+using namespace Shiboken;
+
+// Remote Objects transfer enums as integers of the underlying type.
+#define CREATE_ENUM_CONVERSION_FUNCTIONS(SUFFIX, INT_TYPE, PY_TYPE) \
+static void pythonToCpp_PyEnum_QEnum_##SUFFIX(PyObject *pyIn, void *cppOut) \
+{ \
+ Enum::EnumValueType value = Enum::getValue(pyIn); \
+ INT_TYPE val(value); \
+ *reinterpret_cast<INT_TYPE *>(cppOut) = val; \
+} \
+static PythonToCppFunc is_PyEnum_PythonToCpp_QEnum_##SUFFIX##_Convertible(PyObject *pyIn) \
+{ \
+ if (Enum::check(pyIn)) \
+ return pythonToCpp_PyEnum_QEnum_##SUFFIX; \
+ return {}; \
+} \
+static PyObject *cppToPython_QEnum_##SUFFIX##_PyEnum(const void *cppIn) \
+{ \
+ auto convertedCppIn = *reinterpret_cast<const INT_TYPE *>(cppIn); \
+ return PY_TYPE(convertedCppIn); \
+}
+
+CREATE_ENUM_CONVERSION_FUNCTIONS(I8, int8_t, PyLong_FromLong)
+CREATE_ENUM_CONVERSION_FUNCTIONS(I16, int16_t, PyLong_FromLong)
+CREATE_ENUM_CONVERSION_FUNCTIONS(I32, int32_t, PyLong_FromLong)
+CREATE_ENUM_CONVERSION_FUNCTIONS(U8, uint8_t, PyLong_FromUnsignedLong)
+CREATE_ENUM_CONVERSION_FUNCTIONS(U16, uint16_t, PyLong_FromUnsignedLong)
+CREATE_ENUM_CONVERSION_FUNCTIONS(U32, uint32_t, PyLong_FromUnsignedLong)
+CREATE_ENUM_CONVERSION_FUNCTIONS(I64, int64_t, PyLong_FromLongLong)
+CREATE_ENUM_CONVERSION_FUNCTIONS(U64, uint64_t, PyLong_FromUnsignedLongLong)
+
+PyTypeObject *createEnumType(QMetaEnum *metaEnum)
+{
+ static const auto namePrefix = QByteArrayLiteral("2:PySide6.QtRemoteObjects.DynamicEnum.");
+ auto fullName = namePrefix + metaEnum->scope() + "." + metaEnum->enumName();
+
+ AutoDecRef args(PyList_New(0));
+ auto *pyEnumItems = args.object();
+ auto metaType = metaEnum->metaType();
+ auto underlyingType = metaType.underlyingType();
+ bool isUnsigned = underlyingType.flags().testFlag(QMetaType::IsUnsignedEnumeration);
+ for (int idx = 0; idx < metaEnum->keyCount(); ++idx) {
+ auto *key = PyUnicode_FromString(metaEnum->key(idx));
+ auto *key_value = PyTuple_New(2);
+ PyTuple_SetItem(key_value, 0, key);
+ // Value should only return a nullopt if there is no metaObject or the index is not valid
+ auto valueOpt = metaEnum->value64(idx);
+ if (!valueOpt) {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to get value64 from enum");
+ return nullptr;
+ }
+ if (isUnsigned) {
+ auto *value = PyLong_FromUnsignedLongLong(*valueOpt);
+ PyTuple_SetItem(key_value, 1, value);
+ } else {
+ auto *value = PyLong_FromLongLong(*valueOpt);
+ PyTuple_SetItem(key_value, 1, value);
+ }
+ PyList_Append(pyEnumItems, key_value);
+ }
+
+ PyTypeObject *newType{};
+ if (metaEnum->isFlag())
+ newType = Enum::createPythonEnum(fullName.constData(), pyEnumItems, "Flag");
+ else
+ newType = Enum::createPythonEnum(fullName.constData(), pyEnumItems);
+
+ SbkConverter *converter = nullptr;
+ switch (underlyingType.sizeOf()) {
+ case 1:
+ if (isUnsigned) {
+ converter = Conversions::createConverter(newType,
+ cppToPython_QEnum_U8_PyEnum);
+ Conversions::addPythonToCppValueConversion(converter,
+ pythonToCpp_PyEnum_QEnum_U8,
+ is_PyEnum_PythonToCpp_QEnum_U8_Convertible);
+ } else {
+ converter = Conversions::createConverter(newType,
+ cppToPython_QEnum_I8_PyEnum);
+ Conversions::addPythonToCppValueConversion(converter,
+ pythonToCpp_PyEnum_QEnum_I8,
+ is_PyEnum_PythonToCpp_QEnum_I8_Convertible);
+ }
+ break;
+ case 2:
+ if (isUnsigned) {
+ converter = Conversions::createConverter(newType,
+ cppToPython_QEnum_U16_PyEnum);
+ Conversions::addPythonToCppValueConversion(converter,
+ pythonToCpp_PyEnum_QEnum_U16,
+ is_PyEnum_PythonToCpp_QEnum_U16_Convertible);
+ } else {
+ converter = Conversions::createConverter(newType,
+ cppToPython_QEnum_I16_PyEnum);
+ Conversions::addPythonToCppValueConversion(converter,
+ pythonToCpp_PyEnum_QEnum_I16,
+ is_PyEnum_PythonToCpp_QEnum_I16_Convertible);
+ }
+ break;
+ case 4:
+ if (isUnsigned) {
+ converter = Conversions::createConverter(newType,
+ cppToPython_QEnum_U32_PyEnum);
+ Conversions::addPythonToCppValueConversion(converter,
+ pythonToCpp_PyEnum_QEnum_U32,
+ is_PyEnum_PythonToCpp_QEnum_U32_Convertible);
+ } else {
+ converter = Conversions::createConverter(newType,
+ cppToPython_QEnum_I32_PyEnum);
+ Conversions::addPythonToCppValueConversion(converter,
+ pythonToCpp_PyEnum_QEnum_I32,
+ is_PyEnum_PythonToCpp_QEnum_I32_Convertible);
+ }
+ break;
+ case 8:
+ if (isUnsigned) {
+ converter = Conversions::createConverter(newType,
+ cppToPython_QEnum_U64_PyEnum);
+ Conversions::addPythonToCppValueConversion(converter,
+ pythonToCpp_PyEnum_QEnum_U64,
+ is_PyEnum_PythonToCpp_QEnum_U64_Convertible);
+ } else {
+ converter = Conversions::createConverter(newType,
+ cppToPython_QEnum_I64_PyEnum);
+ Conversions::addPythonToCppValueConversion(converter,
+ pythonToCpp_PyEnum_QEnum_I64,
+ is_PyEnum_PythonToCpp_QEnum_I64_Convertible);
+ }
+ break;
+ default:
+ PyErr_SetString(PyExc_RuntimeError, "Unsupported enum underlying type");
+ return nullptr;
+ }
+ auto scopedName = QByteArray(metaEnum->scope()) + "::" + metaEnum->enumName();
+ Conversions::registerConverterName(converter, scopedName.constData());
+ Conversions::registerConverterName(converter, metaEnum->enumName());
+ // createConverter increases the ref count of type, but that will create a
+ // circular reference when we add the capsule with the converter's pointer
+ // to the type's attributes. So we need to decrease the ref count on the
+ // type after calling createConverter.
+ Py_DECREF(newType);
+ if (set_cleanup_capsule_attr_for_pointer(newType, "_converter_capsule", converter) < 0)
+ return nullptr;
+
+ return newType;
+}