[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