SetProperty : Cannot send events to objects owned by a different thread



  • I set some dynamic properties on QObjects from different threads using QObject->setProperty()
    I was under the impression this is executed by posting a QDynamicPropertyChangeEvent so I didn't have to worry about what thread it was sent from.

    This seemed to work on linux, but on windows I get the following assert :

    @ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread.@

    What would be the best approach to set Dynamic properties in a multi threaded application?



  • AFAIK, the event is there to enable you to act on the change, but they are not used to make the actual change. An inspection of the sources can tell you the details.

    I guess I'd create a slot on the class as a thin wrapper for setProperty. That would enable you to use the cross-thread signal/slot mechanism (either via a signal or by using QMetaObject) to set the properties.



  • I suppose you are right about the event. Thanks for clearing that up :)
    Are you suggesting to create a wrapper for every QObject derived class I want to use to set a dynamic property? Since I use a lot of the QWidgets, this seems like an impossible job to maintain.

    Any idea why this behaviour only manifests on windows and not on linux?



  • Hi dvdk,

    Though all functions in class QObject are reentrant, but only connect() and disconnect() are thread-safe. And you should not use not-thread-safe method in this way.

    You can using signals and slots between threads(with auto connection or queued connection), which is easy and safe.

    Regards,

    Debao



  • I understand I should use cross-thread signaling, but I fail to see how I can achieve this in a simple way.

    Say I want to do a setProperty on a QLabel object, how can I achieve this with signals without writing a wrapper round QLabel (and every other qwidget I like to use)? setProperty is not a slot.



  • You did not say that the QObject you mentioned is QWidget before.

    [quote author="dvdk" date="1333131332"]
    Say I want to do a setProperty on a QLabel object, how can I achieve this with signals without writing a wrapper round QLabel (and every other qwidget I like to use)? setProperty is not a slot.[/quote]

    In fact, You cannot! The manual says:

    bq. Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread.

    By the way, if you want to call slots or signals or invoked methods, you can use
    @
    QMetaObject::invokeMethod()
    @



  • Well, that's an issue, indeed.

    If your issue is to set properties from different threads to your GUI thread (you're talking about widgets, after all), then I think I'd create a QObject derived class, of which you create an instance in your main tread. You can use that class as a proxy to your widget. Simply pass the pointer to the widget, as well as the property name and the QVariant value to that class via the threaded signal/slot mechanism, and let that class then do the actual property setting.



  • Thanks Andre, your solutions looks very clean. I will implement the proxy class.

    1+1=2 :
    Just out of curiosity I wanted to try your hint about InvokeMethod(), but I can't figure out how to use it properly.

    I tried the following

    @QMetaObject::invokeMethod(pObj, "setProperty", Qt::AutoConnection,
    Q_ARG(const char*, "somevar"), Q_ARG(const QVariant, "somevalue"));@

    It compiles fine, but on execution I get :

    @QMetaObject::invokeMethod: No such method QLabel::setProperty(const char*,const QVariant)@

    Any idea what I did wrong? Documentation on Q_ARG is a bit thin, I probably misinterpreted something.



  • What you are doing wrong, is that on QLabel, setProperty is not declared as a a slot, or at least as Q_INVOKABLE. This functionality is basically a 'raw' way of using the signal/slot mechanism, and it relies on the same mechanisms.



  • Thanks for the answer. So the QMetaObject::invokeMethod cannot be used in this case.



  • well, it can be used through the proxy object that I suggested. You can use QMetaObject::invokeMethod on the slot you define there. That slot can then use a normal method invocation on the object you pass for the actual property setting. The invokeMethod should use the Qt::QueuedConnection connection type.

    The proxy object could be quite simple, I guess something like this would suffice:

    @
    class PropertySetProxy: public QObject
    {
    Q_OBJECT

    public:
    PropertySetProxy(QObject* parent = 0): QObject(parent) {}

    setProperty(const QObject* target, const char* property, QVariant value);
    

    };

    //implementation
    PropertySetProxy::setProperty(const QObject* target, const char* property, QVariant value)
    {
    //check if target object lives in the same thread as we do
    if (thread() != target->thread()) {
    qWarning() << "Can't set property of object living in different thread!";
    return;
    }

    target->setProperty(property, value);
    }
    @

    Note: brain to forum editor, code is not tested and to be understood as an example only.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.