[PyKDE] Re: How to embed Python in C++
David Boddie
david at boddie.org.uk
Sat Mar 25 18:02:36 GMT 2006
On Fri, 24 Mar 2006 17:40:00, Eric Jardim wrote:
> I found a way of getting Python extended widgets.
>
> I will post here the way I did and I would like to know if this is the
> right, easy and safer way of doing this:
I've added comments below.
> Supose this is a QWidget subclass and that I wrote a module called "
> MyWidget.py" with "MyWidget" class defined inside of it.
>
> // let's assume that I've already created my QApplication in C++ code.
> SomeQWidgetClass::someMethod()
> {
> Py_Initialize();
> PyObject* __main__ = PyImport_AddModule("__main__");
> PyObject* scope = PyObject_GetAttrString(__main__, "__dict__");
> PyRun_String("import sip", Py_single_input, scope, scope);
> PyRun_String("from PyQt4 import QtGui", Py_single_input, scope, scope);
> PyRun_String("import MyWidget", Py_single_input, scope, scope);
I usually import a module and execute code within that rather than within the
__main__ module, using a factory function to do the work. I've removed a lot
of the details from my code snippets.
> // passing this widget as parent
> QString cmd = QString(
> "w = MyWidget.MyWidget(sip.wrapinstance(%1,
> QtGui.SomeQWidgetClass))" ).arg((long)this);
> PyObject* w = PyRun_String(cmd.toStdString().c_str(), Py_single_input,
> scope, scope);
> if (!w)
> {
> PyErr_Print();
> }
>
> PyObject* id = PyRun_String("sip.unwrapinstance(w)", Py_eval_input,
> scope, scope);
It's easy to define a single function to do this:
PyRun_String("from PyQt4 import QtGui\nimport sip\n"
"def __embedded_factory__create__(parent = None):\n"
" global plugin\n"
" print \""CLASS_NAME"\"\n"
" plugin = "CLASS_NAME"(parent)\n"
" return sip.unwrapinstance(plugin)\n",
Py_file_input, pyDict, pyDict);
PyObject *pyFactory = PyDict_GetItemString(pyDict,
"__embedded_factory__create__");
This one creates a single instance of a plugin, typically an instance
of a SIP-wrapped class.
> QWidget* widget = (QWidget*) PyLong_AsLong(id);
> layout()->addWidget(widget);
> widget->show();
> }
Obviously, this code doesn't do the same sort of thing, but it shows how
I use the unwrapped instance returned by the Python function:
PyObject *pyPluginPtr = call_function(pyFactory, 0);
DesignerCustomWidgetPlugin *plugin = 0;
if (pyPluginPtr)
{
// Extract the C++ pointer from the PyObject.
plugin = static_cast<DesignerCustomWidgetPlugin*>(
PyLong_AsVoidPtr(pyPluginPtr));
// Decrement the reference count of the long value.
Py_XDECREF(pyPluginPtr);
}
else {
qDebug() << "Failed";
}
The plugin is an instance of a SIP-wrapped class, so it handles all the
ownership issues.
> Another thing I am worried about is memory management. Is there any special
> care that should I take with those Python created objects?
I'm worried about this, too, and I don't really know how to deal with
multiple instances returned to my C++ code that I pass on to the application.
I guess I should transfer ownership of them to C++. However, if I just wrap
single-instance plugins that are supposed to exist for the application's
running lifetime, this issue is mostly avoided.
A while ago, I uploaded an example of widget embedding to this page:
http://www.boddie.org.uk/david/Projects/Python/Qt/
Reviewing it recently, I'm not sure that it's really all that helpful, but
you might get some ideas from it.
David
More information about the PyQt
mailing list