Here is working code(though not in compilable form) that allows registering 
python classes(really any python object that has .__name__ attribute) as a 
QMetaType. Then you use qvariantFromPyObject to construct a qvariant from an 
instance of the registered type.  You use pyObjectFromQVariant to get the 
python object back.

This code could be modified to provide a single QMetaType for any PyObject.  
This would allow storing any python object without having a specific type 
registered.  Specific type registration is useful for thing like 
QItemEditorFactory, so the ability to register custom types should remain.

This code could quite easily be integrated into the QVariant .sip bindings to 
provide a QVariant ctor that takes any PyObject, and a QVariant function that 
returns the PyObject.

Phil, are you interested in having this functionality directly in PyQt?  I 
would be willing to integrate but don't really have time to do so at the 
moment. (It would only take a few minutes to integrate, but I would have to 
update my sip and pyqt to the latest versions first)

btw. The code i pasted is written to be compiled outside of a sip module, 
therefore uses a bit of hackery to get at the sip api.  This would not be 
needed if it was integrated into PyQt.


>>> class A:
...     pass
...
>>> metaTypeId = registerPythonQMetaType(A)
>>> print metaTypeId
424
>>> qv = qvariantFromPyObject(A())
>>> qv
<PyQt4.QtCore.QVariant object at 0xb7d2ec2c>
>>> qv.userType()
424
>>> print qv.typeName()
A
>>> pyObjectFromQVariant(qv)
<__main__.A instance at 0xb65391ec>

Matt
.sip

int registerPythonQMetaType( SIP_PYOBJECT type ) /HoldGIL/;
SIP_PYOBJECT qvariantFromPyObject( SIP_PYOBJECT object ) /HoldGIL/;
SIP_PYOBJECT pyObjectFromQVariant( SIP_PYOBJECT py_qvariant ) /HoldGIL/;

.h

extern "C" {
	#include <Python.h>
	#include <sip.h>
	#include <node.h>
	#include <compile.h>
	#include <import.h>
	#include <marshal.h>
	#include <graminit.h>
	#include <eval.h>
};

int registerPythonQMetaType( PyObject * type );
PyObject * qvariantFromPyObject( PyObject * object );
PyObject * pyObjectFromQVariant( PyObject * py_qvariant );

.cpp

#include <qmetatype.h>

static inline void ensurePythonInitialized()
{
	if( ! Py_IsInitialized() )
		Py_Initialize();
}

const sipAPIDef * getSipAPI()
{
	ensurePythonInitialized();

	static const sipAPIDef * api = 0;
	if( api ) return api;

	/* Import the SIP module and get it's API.
	 * libsip does not provide a symbol for accessing the sipAPIDef object
	 * it must be retrieved through sip's python module's dictionary
	 */
    SIP_BLOCK_THREADS
	PyObject * sip_sipmod = PyImport_ImportModule((char *)"sip");
	if (sip_sipmod == NULL) {
		LOG_3( "getSipAPI: Error importing sip module" );
	} else {

		PyObject * sip_capiobj = PyDict_GetItemString(PyModule_GetDict(sip_sipmod),"_C_API");
	
		if (sip_capiobj == NULL || !PyCObject_Check(sip_capiobj))
			LOG_3( "getSipAPI: Unable to find _C_API object from sip modules dictionary" );
		else
			api = reinterpret_cast<const sipAPIDef *>(PyCObject_AsVoidPtr(sip_capiobj));
	}
    SIP_UNBLOCK_THREADS
	return api;
}

sipExportedModuleDef * getSipModule( const char * name )
{
	const sipAPIDef * api = getSipAPI();
	if( !api ) return 0;

	sipExportedModuleDef * module = api->api_find_module( name );
	if( !module )
		LOG_5( "getSipModule: Unable to lookup module " + QString::fromLatin1(name) + " using api_find_module" );
	return module;
}

sipWrapperType * getSipWrapperType( const char * module_name, const char * typeName )
{
	sipExportedModuleDef * module = getSipModule(module_name);
	if( !module ) return 0;

	for( int i = module->em_nrtypes - 1; i >= 0; i-- ) {
		sipWrapperType * swt = module->em_types[i];
		sipTypeDef * type = swt->type;
		if( strcmp( type->td_name, typeName ) == 0 || ( type->td_cname && strcmp( type->td_cname, typeName ) == 0 ) )
			return swt;
	}

	LOG_5( "getSipWrapperType: Unabled to find " + QString::fromLatin1(typeName) + " in module " + QString::fromLatin1(module_name) );
	return 0;
}

static sipWrapperType * sipQVariantWrapper()
{ static sipWrapperType * sQVariantW = 0; if( !sQVariantW ) sQVariantW = getSipWrapperType("PyQt4.QtCore","QtCore.QVariant"); return sQVariantW; }

void * pythonMetaTypeCtor( const void * copy )
{
	if( copy ) {
		SIP_BLOCK_THREADS
		Py_INCREF( (PyObject*)copy );
		SIP_UNBLOCK_THREADS
		return const_cast<void*>(copy);
	}
	return 0;
}

void pythonMetaTypeDtor( void * pyObject )
{
	SIP_BLOCK_THREADS
	Py_DECREF((PyObject*)pyObject);
	SIP_UNBLOCK_THREADS
}

int registerPythonQMetaType( PyObject * type )
{
	int ret = 0;
	PyObject * pyname = PyObject_GetAttrString( type, "__name__" );
	if( !pyname ) {
		printf( "registerPythonQMetaType: Unabled to get attribute __name__\n" );
		return ret;
	}
	const char * typeName = PyString_AsString(pyname);
	if( typeName ) {
		ret = QMetaType::registerType( typeName, reinterpret_cast<QMetaType::Destructor>(pythonMetaTypeDtor), reinterpret_cast<QMetaType::Constructor>(pythonMetaTypeCtor) );
		// Dont return success when it's returning an already registered builtin type
		if( ret < QMetaType::User ) ret = 0;
	} else
		printf("registerPythonQMetaType: __name__ attribute is not a string\n");
	Py_DECREF(pyname);
	return ret;
}

int findQVariantType( PyObject * type )
{
	int ret = 0;
	PyObject * pyname = PyObject_GetAttrString( type, "__name__" );
	if( !pyname ) {
		printf( "findQVariantType: Unabled to get attribute __name__\n" );
		return ret;
	}
	const char * typeName = PyString_AsString(pyname);
	if( typeName ) {
		ret = QMetaType::type(typeName);
	} else
		printf("findQVariantType: __name__ attribute is not a string\n");
	Py_DECREF(pyname);
	return ret;
}

PyObject * qvariantFromPyObject( PyObject * object )
{
	PyObject * klass = PyObject_GetAttrString( object, "__class__" );
	if( klass ) {
 		int type = findQVariantType( klass );
		if( type >= QMetaType::User ) {
			QVariant * ret = new QVariant( type, (void*)object );
			return getSipAPI()->api_convert_from_new_instance(ret,sipQVariantWrapper(),0);
		} else
			printf("qvariantFromPyObject: QMetaType::Type %i is not valid\n", type);
	}
	Py_INCREF(Py_None);
	return Py_None;
}

PyObject * pyObjectFromQVariant( PyObject * py_qvariant )
{
	if( getSipAPI()->api_can_convert_to_instance( py_qvariant, sipQVariantWrapper(), 0 ) ) {
		int err = 0;
		QVariant * v = (QVariant*)getSipAPI()->api_convert_to_instance( py_qvariant, sipQVariantWrapper(), 0, 0, 0, &err );
		if( v ) {
			PyObject * object = (PyObject*)v->data();
			Py_INCREF(object);
			return object;
		} else
			printf("pyObjectFromQVariant: Unable to convert PyObject * to QVariant with sipConvertToInstance\n");
	}
	Py_INCREF(Py_None);
	return Py_None;
}
_______________________________________________
PyQt mailing list    [email protected]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

Reply via email to