Создание пользовательского сообщения / события с помощью Qt

#c #qt #postmessage

#c #qt #postmessage

Вопрос:

У меня есть поток RPC, который перезванивает мне из этого потока. Мне нужно каким-то образом сообщить Qt, что ему необходимо выполнить вызов функции из основного потока. В обычной Windows я мог бы сделать это, используя пользовательское сообщение, а затем отправив это сообщение в очередь сообщений, например, я мог бы создать WM_CALLFUNCTION сообщение и передать указатель на функцию через wParam и параметр (указатель класса) через lParam .

Есть ли у кого-нибудь идеи, как я мог бы сделать это с помощью Qt? Я сталкивался с QCustomEvent , но я понятия не имею, как это использовать или как это обработать. Любая помощь была бы чрезвычайно признательна!

Редактировать:

В итоге я выбрал QMetaObject::invokeMethod, который работает идеально.

Комментарии:

1. почему бы вместо QCustomeEvent не использовать механизм сигналов / слотов?

2. Каким образом… Как бы я вызвал это из потока B, чтобы поток A выполнил функцию …?

3. Механизм сигналов / слотов Qt потокобезопасен и может использоваться в разных потоках. Посмотрите руководства по сигналам / слотам, если вы не знаете, как это сделать.

Ответ №1:

Использование пользовательских событий обычно включает в себя создание вашего собственного подкласса QEvent, переопределение CustomEvent() в классе QObject, который получит событие (часто класс main window) и некоторый код, который «отправляет» событие из вашего потока получателю.

Мне нравится реализовывать код отправки события как метод класса receiver. Таким образом, вызывающий должен знать только об объекте recevier, а не о каких-либо особенностях «Qt». Вызывающий вызовет этот метод, который затем, по сути, отправит сообщение самому себе. Надеюсь, приведенный ниже код сделает это более понятным.

 // 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);
  

customData1 и customData2 приведены для демонстрации того, как вы могли бы передавать некоторые данные в своем событии. Они не обязательно должны быть int s.

 // 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.
}
  

Надеюсь, я ничего не упустил. С учетом этого коду в каком-либо другом потоке просто нужно получить указатель на MainWindow объект (давайте назовем его mainWindow ) и вызвать

 mainWindow->postMyCustomEvent(1,2);
  

где, просто для нашего примера, 1 и 2 могут быть любые целочисленные данные.

Комментарии:

1. QApplication::postEvent(this, new StatisticEvent(p, dltotal, dlnow, uptotal, upnow)); Я получаю сообщение об ошибке, /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)); и я не уверен, что делать. Может кто-нибудь, пожалуйста, помочь? Это было бы с благодарностью.

Ответ №2:

В Qt 3 обычным способом связи с потоком GUI из потока, отличного от GUI, была публикация пользовательского события в QObject в потоке GUI. В Qt 4 это все еще работает и может быть обобщено на случай, когда одному потоку необходимо взаимодействовать с любым другим потоком, у которого есть цикл событий.

Для упрощения программирования Qt 4 также позволяет устанавливать соединения сигнал-слот между потоками. За кулисами эти соединения реализуются с использованием события. Если у сигнала есть какие-либо параметры, они также сохраняются в событии. Как и ранее, если отправитель и получатель находятся в одном потоке, Qt выполняет прямой вызов функции.

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

Комментарии:

1. Приветствия! Но как именно вы устанавливаете соединение с сигнальным слотом между потоками?

2. Во-первых… нужно ли мне для этого запускаться из потока Qt? Потому что поток, из которого мне нужно выполнить обратный вызов, не является потоком QT…

3. Графический интерфейс должен выполняться в главном потоке. Если вы не имеете дело с виджетами, то вам нужно создать подкласс QObject и реализовать свои сигналы / слоты.

4. @Goz Для установления соединения сигнала / слота между потоками просто используйте обычную функцию connect. Qt сам догадывается, как реализовать соединение, посмотрев, в каком потоке был отправлен сигнал и в каком потоке находится получатель. Вы также можете установить блокирующее соединение, когда один поток асинхронно отправляет сигнал другому потоку, но ожидает его выполнения до тех пор, пока не вернется слот (который выполняется в другом потоке), что очень удобно при синхронизации рабочих потоков с потоком пользовательского интерфейса.

5. Приветствия! В конце концов я обнаружил QMetaObject::invokeMethod и пошел с этим. Собираюсь принять ваш ответ в любом случае, хотя 🙂