Skip to content

Commit 4ac4a5f

Browse files
committed
added enum wrapper classes derived from python int, so that enums are distinguishable from normal python integers. This will allow better overload handling e.g. of QColor constructors in the future
1 parent 37f19f9 commit 4ac4a5f

7 files changed

Lines changed: 141 additions & 21 deletions

src/PythonQt.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,30 @@ PythonQtClassWrapper* PythonQtPrivate::createNewPythonQtClassWrapper(PythonQtCla
431431
return result;
432432
}
433433

434+
PyObject* PythonQtPrivate::createNewPythonQtEnumWrapper(const char* enumName, PyObject* parentObject) {
435+
PyObject* result;
436+
437+
PyObject* className = PyString_FromString(enumName);
438+
439+
PyObject* baseClasses = PyTuple_New(1);
440+
PyTuple_SET_ITEM(baseClasses, 0, (PyObject*)&PyInt_Type);
441+
442+
PyObject* module = PyObject_GetAttrString(parentObject, "__module__");
443+
PyObject* typeDict = PyDict_New();
444+
PyDict_SetItemString(typeDict, "__module__", module);
445+
446+
PyObject* args = Py_BuildValue("OOO", className, baseClasses, typeDict);
447+
448+
// create the new int derived type object by calling the core type
449+
result = PyObject_Call((PyObject *)&PyType_Type, args, NULL);
450+
451+
Py_DECREF(baseClasses);
452+
Py_DECREF(typeDict);
453+
Py_DECREF(args);
454+
Py_DECREF(className);
455+
456+
return result;
457+
}
434458

435459
PythonQtSignalReceiver* PythonQt::getSignalReceiver(QObject* obj)
436460
{

src/PythonQt.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,9 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
446446
//! helper method that creates a PythonQtClassWrapper object
447447
PythonQtClassWrapper* createNewPythonQtClassWrapper(PythonQtClassInfo* info, const char* package = NULL);
448448

449+
//! helper that creates a new int derived class that represents the enum of the given name
450+
PyObject* createNewPythonQtEnumWrapper(const char* enumName, PyObject* parentObject);
451+
449452
//! helper method that creates a PythonQtInstanceWrapper object and registers it in the object map
450453
PythonQtInstanceWrapper* createNewPythonQtInstanceWrapper(QObject* obj, PythonQtClassInfo* info, void* wrappedPtr = NULL);
451454

src/PythonQtClassInfo.cpp

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
#include "PythonQtMethodInfo.h"
4444
#include "PythonQt.h"
4545
#include <QMetaMethod>
46+
#include <QMetaObject>
47+
#include <QMetaEnum>
4648

4749
QHash<QByteArray, int> PythonQtMethodInfo::_parameterTypeDict;
4850

@@ -56,6 +58,7 @@ PythonQtClassInfo::PythonQtClassInfo() {
5658
_shellSetInstanceWrapperCB = NULL;
5759
_metaTypeId = -1;
5860
_isQObject = false;
61+
_enumsCreated = false;
5962
}
6063

6164
PythonQtClassInfo::~PythonQtClassInfo()
@@ -250,13 +253,23 @@ bool PythonQtClassInfo::lookForEnumAndCache(const QMetaObject* meta, const char*
250253
QMetaEnum e = meta->enumerator(i);
251254
for (int j=0; j < e.keyCount(); j++) {
252255
if (qstrcmp(e.key(j), memberName)==0) {
253-
PythonQtMemberInfo newInfo(e.value(j));
254-
_cachedMembers.insert(memberName, newInfo);
256+
PyObject* enumType = findEnumWrapper(e.name());
257+
if (enumType) {
258+
PyObject* args = Py_BuildValue("(i)", e.value(j));
259+
PyObject* enumValue = PyObject_Call(enumType, args, NULL);
260+
Py_DECREF(args);
261+
PythonQtObjectPtr enumValuePtr;
262+
enumValuePtr.setNewRef(enumValue);
263+
PythonQtMemberInfo newInfo(enumValuePtr);
264+
_cachedMembers.insert(memberName, newInfo);
255265
#ifdef PYTHONQT_DEBUG
256-
std::cout << "caching enum " << memberName << " on " << meta->className() << std::endl;
266+
std::cout << "caching enum " << memberName << " on " << meta->className() << std::endl;
257267
#endif
258-
found = true;
259-
break;
268+
found = true;
269+
break;
270+
} else {
271+
std::cout << "enum " << e.name() << " not found on " << className() << std::endl;
272+
}
260273
}
261274
}
262275
}
@@ -294,6 +307,15 @@ PythonQtMemberInfo PythonQtClassInfo::member(const char* memberName)
294307
}
295308
}
296309
}
310+
if (!found) {
311+
PyObject* p = findEnumWrapper(memberName);
312+
if (p) {
313+
info._type = PythonQtMemberInfo::EnumWrapper;
314+
info._enumWrapper = p;
315+
_cachedMembers.insert(memberName, info);
316+
found = true;
317+
}
318+
}
297319
if (!found) {
298320
// we store a NotFound member, so that we get a quick result for non existing members (e.g. operator_equal lookup)
299321
info._type = PythonQtMemberInfo::NotFound;
@@ -470,6 +492,7 @@ QStringList PythonQtClassInfo::memberList(bool metaOnly)
470492
foreach(const QMetaObject* meta, enumMetaObjects) {
471493
for (int i = 0; i<meta->enumeratorCount(); i++) {
472494
QMetaEnum e = meta->enumerator(i);
495+
l << e.name();
473496
for (int j=0; j < e.keyCount(); j++) {
474497
l << QString(e.key(j));
475498
}
@@ -661,6 +684,9 @@ QObject* PythonQtClassInfo::decorator()
661684
PythonQt::priv()->addDecorators(_decoratorProvider, PythonQtPrivate::ConstructorDecorator | PythonQtPrivate::DestructorDecorator);
662685
}
663686
}
687+
if (!_enumsCreated) {
688+
createEnumWrappers();
689+
}
664690
return _decoratorProvider;
665691
}
666692

@@ -768,3 +794,43 @@ bool PythonQtClassInfo::hasEnum(const QByteArray& name)
768794
return found;
769795
}
770796

797+
void PythonQtClassInfo::createEnumWrappers(const QMetaObject* meta)
798+
{
799+
for (int i = meta->enumeratorOffset();i<meta->enumeratorCount();i++) {
800+
QMetaEnum e = meta->enumerator(i);
801+
PythonQtObjectPtr p;
802+
p.setNewRef(PythonQt::priv()->createNewPythonQtEnumWrapper(e.name(), _pythonQtClassWrapper));
803+
_enumWrappers.append(p);
804+
}
805+
}
806+
807+
void PythonQtClassInfo::createEnumWrappers()
808+
{
809+
if (!_enumsCreated) {
810+
_enumsCreated = true;
811+
if (_meta) {
812+
createEnumWrappers(_meta);
813+
}
814+
if (decorator()) {
815+
createEnumWrappers(decorator()->metaObject());
816+
}
817+
foreach(const ParentClassInfo& info, _parentClasses) {
818+
info._parent->createEnumWrappers();
819+
}
820+
}
821+
}
822+
823+
PyObject* PythonQtClassInfo::findEnumWrapper(const char* name) {
824+
foreach(const PythonQtObjectPtr& p, _enumWrappers) {
825+
const char* className = ((PyTypeObject*)p.object())->tp_name;
826+
if (qstrcmp(className, name)==0) {
827+
return p.object();
828+
}
829+
}
830+
foreach(const ParentClassInfo& info, _parentClasses) {
831+
PyObject* p = info._parent->findEnumWrapper(name);
832+
if (p) return p;
833+
}
834+
return NULL;
835+
}
836+

src/PythonQtClassInfo.h

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,35 +44,38 @@ class PythonQtSlotInfo;
4444

4545
struct PythonQtMemberInfo {
4646
enum Type {
47-
Invalid, Slot, EnumValue, Property, NotFound
47+
Invalid, Slot, EnumValue, EnumWrapper, Property, NotFound
4848
};
4949

50-
PythonQtMemberInfo():_type(Invalid),_slot(NULL),_enumValue(0) { }
50+
PythonQtMemberInfo():_type(Invalid),_slot(NULL),_enumWrapper(NULL),_enumValue(0) { }
5151

5252
PythonQtMemberInfo(PythonQtSlotInfo* info) {
5353
_type = Slot;
5454
_slot = info;
55-
_enumValue = 0;
55+
_enumValue = NULL;
5656
}
5757

58-
PythonQtMemberInfo(unsigned int enumValue) {
58+
PythonQtMemberInfo(const PythonQtObjectPtr& enumValue) {
5959
_type = EnumValue;
6060
_slot = NULL;
6161
_enumValue = enumValue;
62+
_enumWrapper = NULL;
6263
}
6364

6465
PythonQtMemberInfo(const QMetaProperty& prop) {
6566
_type = Property;
6667
_slot = NULL;
67-
_enumValue = 0;
68+
_enumValue = NULL;
6869
_property = prop;
70+
_enumWrapper = NULL;
6971
}
7072

7173
Type _type;
7274

7375
// TODO: this could be a union...
7476
PythonQtSlotInfo* _slot;
75-
unsigned int _enumValue;
77+
PyObject* _enumWrapper;
78+
PythonQtObjectPtr _enumValue;
7679
QMetaProperty _property;
7780
};
7881

@@ -194,7 +197,11 @@ class PythonQtClassInfo {
194197
//! returns if the localScope has an enum of that type name or if the enum contains a :: scope, if that class contails the enum
195198
static bool hasEnum(const QByteArray& name, PythonQtClassInfo* localScope);
196199

197-
private:
200+
private:
201+
void createEnumWrappers();
202+
void createEnumWrappers(const QMetaObject* meta);
203+
PyObject* findEnumWrapper(const char* name);
204+
198205
//! checks if the enum is part of this class (without any leading scope!)
199206
bool hasEnum(const QByteArray& name);
200207

@@ -223,6 +230,8 @@ class PythonQtClassInfo {
223230
PythonQtSlotInfo* _destructor;
224231
QList<PythonQtSlotInfo*> _decoratorSlots;
225232

233+
QList<PythonQtObjectPtr> _enumWrappers;
234+
226235
const QMetaObject* _meta;
227236

228237
QByteArray _wrappedClassName;
@@ -240,6 +249,7 @@ class PythonQtClassInfo {
240249
int _metaTypeId;
241250

242251
bool _isQObject;
252+
bool _enumsCreated;
243253

244254
};
245255

src/PythonQtClassWrapper.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,14 @@ static PyObject *PythonQtClassWrapper_getattro(PyObject *obj, PyObject *name)
183183
if (wrapper->classInfo()) {
184184
PythonQtMemberInfo member = wrapper->classInfo()->member(attributeName);
185185
if (member._type == PythonQtMemberInfo::EnumValue) {
186-
return PyInt_FromLong(member._enumValue);
187-
} else
188-
if (member._type == PythonQtMemberInfo::Slot) {
186+
PyObject* enumValue = member._enumValue;
187+
Py_INCREF(enumValue);
188+
return enumValue;
189+
} else if (member._type == PythonQtMemberInfo::EnumWrapper) {
190+
PyObject* enumWrapper = member._enumWrapper;
191+
Py_INCREF(enumWrapper);
192+
return enumWrapper;
193+
} else if (member._type == PythonQtMemberInfo::Slot) {
189194
// we return all slots, even the instance slots, since they are callable as unbound slots with self argument
190195
return PythonQtSlotFunction_New(member._slot, obj, NULL);
191196
}

src/PythonQtConversion.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,10 @@ int PythonQtConv::PyObjGetInt(PyObject* val, bool strict, bool &ok) {
611611
if (val->ob_type == &PyInt_Type) {
612612
d = PyInt_AS_LONG(val);
613613
} else if (!strict) {
614-
if (val->ob_type == &PyFloat_Type) {
614+
if (PyObject_TypeCheck(val, &PyInt_Type)) {
615+
// support for derived int classes, e.g. for our enums
616+
d = PyInt_AS_LONG(val);
617+
} else if (val->ob_type == &PyFloat_Type) {
615618
d = floor(PyFloat_AS_DOUBLE(val));
616619
} else if (val->ob_type == &PyLong_Type) {
617620
// handle error on overflow!
@@ -632,7 +635,7 @@ int PythonQtConv::PyObjGetInt(PyObject* val, bool strict, bool &ok) {
632635
qint64 PythonQtConv::PyObjGetLongLong(PyObject* val, bool strict, bool &ok) {
633636
qint64 d = 0;
634637
ok = true;
635-
if (val->ob_type == &PyInt_Type) {
638+
if (PyObject_TypeCheck(val, &PyInt_Type)) {
636639
d = PyInt_AS_LONG(val);
637640
} else if (val->ob_type == &PyLong_Type) {
638641
d = PyLong_AsLongLong(val);
@@ -655,7 +658,7 @@ qint64 PythonQtConv::PyObjGetLongLong(PyObject* val, bool strict, bool &ok) {
655658
quint64 PythonQtConv::PyObjGetULongLong(PyObject* val, bool strict, bool &ok) {
656659
quint64 d = 0;
657660
ok = true;
658-
if (val->ob_type == &PyInt_Type) {
661+
if (PyObject_TypeCheck(val, &PyInt_Type)) {
659662
d = PyInt_AS_LONG(val);
660663
} else if (val->ob_type == &PyLong_Type) {
661664
d = PyLong_AsLongLong(val);
@@ -681,7 +684,7 @@ double PythonQtConv::PyObjGetDouble(PyObject* val, bool strict, bool &ok) {
681684
if (val->ob_type == &PyFloat_Type) {
682685
d = PyFloat_AS_DOUBLE(val);
683686
} else if (!strict) {
684-
if (val->ob_type == &PyInt_Type) {
687+
if (PyObject_TypeCheck(val, &PyInt_Type)) {
685688
d = PyInt_AS_LONG(val);
686689
} else if (val->ob_type == &PyLong_Type) {
687690
d = PyLong_AsLong(val);
@@ -707,7 +710,7 @@ QVariant PythonQtConv::PyObjToQVariant(PyObject* val, int type)
707710
// no special type requested
708711
if (val->ob_type==&PyString_Type || val->ob_type==&PyUnicode_Type) {
709712
type = QVariant::String;
710-
} else if (val->ob_type==&PyInt_Type) {
713+
} else if (PyObject_TypeCheck(val, &PyInt_Type)) {
711714
type = QVariant::Int;
712715
} else if (val->ob_type==&PyLong_Type) {
713716
type = QVariant::LongLong;

src/PythonQtInstanceWrapper.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,14 @@ static PyObject *PythonQtInstanceWrapper_getattro(PyObject *obj,PyObject *name)
277277
return PythonQtSlotFunction_New(member._slot, obj, NULL);
278278
break;
279279
case PythonQtMemberInfo::EnumValue:
280-
return PyInt_FromLong(member._enumValue);
280+
PyObject* enumValue = member._enumValue;
281+
Py_INCREF(enumValue);
282+
return enumValue;
283+
break;
284+
case PythonQtMemberInfo::EnumWrapper:
285+
PyObject* enumWrapper = member._enumWrapper;
286+
Py_INCREF(enumWrapper);
287+
return enumWrapper;
281288
break;
282289
default:
283290
// is an invalid type, go on
@@ -353,6 +360,8 @@ static int PythonQtInstanceWrapper_setattro(PyObject *obj,PyObject *name,PyObjec
353360
error = QString("Slot '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object";
354361
} else if (member._type == PythonQtMemberInfo::EnumValue) {
355362
error = QString("EnumValue '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object";
363+
} else if (member._type == PythonQtMemberInfo::EnumWrapper) {
364+
error = QString("Enum '") + attributeName + "' can not be overwritten on " + obj->ob_type->tp_name + " object";
356365
} else if (member._type == PythonQtMemberInfo::NotFound) {
357366
// if we are a derived python class, we allow setting attributes.
358367
// if we are a direct CPP wrapper, we do NOT allow it, since

0 commit comments

Comments
 (0)