[PyKDE] Extraction of Qt objects from a PyObject

Albert Strasheim 13640887 at sun.ac.za
Sun Feb 8 08:31:01 GMT 2004


Hello PyKDE mailing list,

I am writing some code that intercepts the events received by a Qt program
by installing an event filter on the QApplication instance.

When events are intercepted, they are passed on to another object via a call
to a function. This function is pure virtual, so you can derive a C++ or
Python class from it to specify a function to do with the events what you
want. Alternatively, the event filter can call another function which
returns an object, the class of which is derived from QEvent, to inject new
events which the program must handle.

I'm using Boost.Python to wrap the "event handler" base class, but I would
like to use the existing SIP Qt bindings instead of writing my own. I'm
currently having a problem extracting the events (i.e. instances of the
classes derived from QEvent) from the PyObject* returned by the latter
function mentioned above.

The Python code looks like this:

from qt import *
from pyqttest import *

class PyEventHandler(EventHandlerBase):
    def __init__(self):
        EventHandlerBase.__init__(self)
        pass
    def inject(self):
        return QKeyEvent(QEvent.KeyRelease, 0, 32, 0, 'h', False, 0)

def main():
    eh = PyEventHandler()
    myQtAppTest = MyQtAppTest(TestType.INJECT, eh)

The C++ wrapper around the base class that PyEventHandler is deriving from
looks like this:

struct EventHandlerBaseWrap : MyUnitTest_t::EventHandlerBase_t
{
   EventHandlerBaseWrap(PyObject* self_) :
      self(self_)
   {
   }

   virtual QEvent* inject()
   {
      try
      {
         // Get a Python object
         boost::python::object o = call_method<object>(self, "inject");

         // more code here...
      }
      catch (...)
      {
         // something has gone badly wrong...
      }
   }
}

MyUnitTest_t::EventHandlerBase_t::inject() is a pure virtual function.

boost::python::object is a Boost.Python type that wraps around a PyObject*.
You can get at the underlying PyObject* by calling the object's ptr()
method.

If I print the QKeyEvent instantiated in the Python inject() above (i.e. the
__repr__ method is called?), the address displayed corresponds to the
address contained in the pointer returned by object's ptr() method, so I'm
pretty sure the return is working. Now I need to get at the event in the
PyObject so that I can copy it and return the address of the copy:

boost::python::object o = call_method<object>(self, "inject");
int iserrp = 0;
PyObject* attr_name = PyString_FromString("__class__");
PyObject* baseclass = PyObject_GetAttr(o.ptr(), attr_name);
delete attr_name;
attr_name = 0;

const QEvent* e = reinterpret_cast<const QEvent*>(
    sipConvertToCpp(o.ptr(), baseclass, &iserrp));

I obtained this way of figuring out the base class from a thread on this
mailing list in July 2003. I'm not sure this is exactly what I want to do
here, though. Now that we have the QEvent (or maybe it's a sipQEvent? of
course, the reinterpret_cast isn't going to care), we call the type() method
to figure out the derived type so that we may copy all its information:

switch ( e->type() )
{
    case QEvent::KeyPress:
    case QEvent::KeyRelease:
    {
        std::cout << "QEvent* = " << e << std::endl;
        std::cout << typeid(e).name() << std::endl;
        std::cout << typeid(*e).name() << std::endl;
        const QKeyEvent* k = dynamic_cast<const QKeyEvent*>(e);
        std::cout << "casted const QKeyEvent* = " << k << std::endl;
    }

    // ...
}

When run, this code prints out the following:

QEvent* = 0x923c108
PK6QEvent
12sipQKeyEvent
casted const QKeyEvent* = 0

The interesting one is the third line. Apparently the underlying object is a
sipQKeyEvent, and not a QKeyEvent. Also, the dynamic_cast fails, so
apparently sipQKeyEvent is not derived from QKeyEvent. From what I can see,
sipQKeyEvent is not declared in any public headers.

How do I go about extracting the underlying QKeyEvent so that I can do with
it what I want? Am I going about the whole the wrong way? Is
sipConvertToCpp() the function I want to call here? Am I using the right
value for baseclass?

Cheers,

Albert Strasheim




More information about the PyQt mailing list