Creating a custom message/event with Qt

12,971

Solution 1

In Qt 3, the usual way to communicate with the GUI thread from a non-GUI thread was by posting a custom event to a QObject in the GUI thread. In Qt 4, this still works and can be generalized to the case where one thread needs to communicate with any other thread that has an event loop.

To ease programming, Qt 4 also allows you to establish signal--slot connections across threads. Behind the scenes, these connections are implemented using an event. If the signal has any parameters, these are also stored in the event. Like previously, if the sender and receiver live in the same thread, Qt makes a direct function call.

-- http://doc.qt.nokia.com/qq/qq14-threading.html#signalslotconnectionsacrossthreads

Solution 2

Using custom events generally involves creating your own QEvent subclass, overriding customEvent() in the QObject class that will receive the event (often the main window class) and some code that "posts" the event from your thread to the receiver.

I like to implement the event posting code as a method of the receiver class. That way, the caller only has to know about the recevier object and not any of the "Qt" specifics. The caller will invoke this method which will then essentially post a message to itself. Hopefully the code below will make it clearer.

// MainWindow.h
...
// Define your custom event identifier
const QEvent::Type MY_CUSTOM_EVENT = static_cast<QEvent::Type>(QEvent::User + 1);

// Define your custom event subclass
class MyCustomEvent : public QEvent
{
    public:
        MyCustomEvent(const int customData1, const int customData2):
            QEvent(MY_CUSTOM_EVENT),
            m_customData1(customData1),
            m_customData2(customData2)
        {
        }

        int getCustomData1() const
        {
            return m_customData1;
        }

        int getCustomData2() const
        {
            return m_customData2;
        }

    private:
        int m_customData1;
        int m_customData2;
};

public:
void postMyCustomEvent(const int customData1, const int customData2);
....
protected:
void customEvent(QEvent *event); // This overrides QObject::customEvent()
...
private:
void handleMyCustomEvent(const MyCustomEvent *event);

The customData1 and customData2 are there to demonstrate how you might pass some data along in your event. They don't have to be ints.

// MainWindow.cpp
...
void MainWindow::postMyCustomEvent(const int customData1, const int customData2)
{   
    // This method (postMyCustomEvent) can be called from any thread

    QApplication::postEvent(this, new MyCustomEvent(customData1, customData2));   
}

void MainWindow::customEvent(QEvent * event)
{
    // When we get here, we've crossed the thread boundary and are now
    // executing in the Qt object's thread

    if(event->type() == MY_CUSTOM_EVENT)
    {
        handleMyCustomEvent(static_cast<MyCustomEvent *>(event));
    }

    // use more else ifs to handle other custom events
}

void MainWindow::handleMyCustomEvent(const MyCustomEvent *event)
{
    // Now you can safely do something with your Qt objects.
    // Access your custom data using event->getCustomData1() etc.
}

I hope I didn't leave anything out. With this in place, code in some other thread just needs to get a pointer to a MainWindow object (let's call it mainWindow) and call

mainWindow->postMyCustomEvent(1,2);

where, just for our example, 1 and 2 can be any integer data.

Share:
12,971
Goz
Author by

Goz

I am a computer programmer with 10 years experience. I worked in the games industry as a 3D rendering and low level optimisation specialist for 8 years before moving into the government contract industry where I generally work on audio DSPs and general projects. See my linked in page for more.

Updated on June 18, 2022

Comments

  • Goz
    Goz almost 2 years

    I have an RPC thread that is calling back to me from that thread. I need to somehow inform Qt that it needs to make a function call from the main thread. In straight Windows I could do this by using a custom message and then posting that message to the message queue, e.g., I could create a WM_CALLFUNCTION message and pass the function pointer through wParam and the parameter (class pointer) through lParam.

    Has anyone an idea how I could do this with Qt? I've come across QCustomEvent but I have no idea how to use it or how to process it. Any help would be hugely appreciated!

    Edit:

    In the end I went with QMetaObject::invokeMethod which works perfectly.

  • Goz
    Goz almost 13 years
    Cheers! But how exactly do you establish a signal slot connection across threads?
  • Goz
    Goz almost 13 years
    For one ... do i need to be running from a Qt thread to do that? Because the thread i need to callback from is a non QT thread ...
  • Arlen
    Arlen almost 13 years
    The GUI has to run in the main thread. If you are not dealing with widgets, then you'll need to subclass QObject and implement your signals/slots.
  • Christian Rau
    Christian Rau almost 13 years
    @Goz For establishing a signal/slot connection across threads, just use the usual connect function. Qt guesses itself, how to realize the connection by looking in what thread the signal got emitted and in what thread the receiver lives. You can also establish a blocking connectionm, where one thread emits a signal asynchronously to another thread, but waits with it's execution until the slot (that is carried out in the other thread) returns, which comes very handy when synchronizing worker threads with the ui thread.
  • Goz
    Goz almost 13 years
    Cheers! In the end I discovered QMetaObject::invokeMethod and went with that. Gonna accept your answer anyway though :)
  • rbaleksandar
    rbaleksandar over 7 years
    I know the post is old but I've just started struggling with this topic and I felt the necessity to write it here. Signals and slots are not an alternative depending on what you want to do. Note that unlike signals events can be queued, ignored etc. Also in case you want a custom QMouseEvent, QGestureEvent etc. you have no choice but to create your own and not do that via slots (in case of QGestureEvent if you don't want to use custom events you will have to rewrite a large chunk of code which is already there for events just to use slots and signals).
  • Phobos D'thorga
    Phobos D'thorga over 7 years
    QApplication::postEvent(this, new StatisticEvent(p, dltotal, dlnow, uptotal, upnow)); I'm getting the error, /home/phobos/Programming/FyreDL/src/cmnroutines.cpp:1241:29: error: cannot initialize a parameter of type 'QObject *' with an rvalue of type 'GekkoFyre::StatisticEvent *' QApplication::postEvent(this, new StatisticEvent(p, dltotal, dlnow, uptotal, upnow)); and I'm not sure what to do. Can someone please help? It'd be greatly appreciated.