Асинхронно вызывать слот, не подключаясь к нему, используя четкую строку кода

Я столкнулся с довольно причудливой ошибкой - QAction::trigger вызывал появление диалогового окна блокировки, из-за чего мой сервер, который вызывал trigger, зависал (например, не мог обрабатывать сигналы сокета, пока диалог не был закрыт).

Я придумал обходной путь. Я подключаю сигнал void triggerWorkaround() к слоту QAction::trigger с помощью Qt::QueuedConnection и испускаю его:

QObject::connect(this, &HackClass::triggerWorkaround, targetAction_.data(), &QAction::trigger, Qt::QueuedConnection);
emit triggerWorkaround();
QObject::disconnect(this, nullptr, targetAction_.data(), nullptr);

Но это три строки запутанного кода. Есть ли способ сделать это без путаницы? Я нашел QMetaObject::invokeMethod, но, честно говоря, это в 10 раз более запутанно, чем мое текущее решение. Кроме того, я никогда не хочу использовать имя метода в виде строки!


person Tomáš Zato - Reinstate Monica    schedule 27.01.2017    source источник
comment
Может быть, сделать отдельный поток для цикла событий сервера.   -  person Velkan    schedule 28.01.2017


Ответы (1)


Вы можете разделить это на функцию QueuedInvoke следующим образом:

//overload for methods/slots
//the slot gets invoked in the thread where the QObject lives
template <typename Object, typename T>
void QueuedInvoke(Object* object, T (Object::* f)()){
    QObject signalSource;
    QObject::connect(&signalSource, &QObject::destroyed,
                     object, f, Qt::QueuedConnection);
}
//overload for functors
//the functor gets invoked in the thread where the contextObject lives
//or in the current thread if no contextObject is provided
template <typename Func>
void QueuedInvoke(Func&& f, QObject* contextObject = QAbstractEventDispatcher::instance()){
    QObject signalSource;
    QObject::connect(&signalSource, &QObject::destroyed, 
                     contextObject, std::forward<Func>(f), Qt::QueuedConnection);
}

Это будет использовать сигнал destroyed(), испускаемый временным QObject, для отправки события в очередь в цикл событий. Слот/функтор фактически вызывается, когда цикл событий обрабатывает это событие.

Итак, вместо 3 строк, которые вы разместили, вы можете использовать приведенную выше функцию следующим образом:

QueuedInvoke(targetAction_.data(), &QAction::trigger);

Мой ответ основан на отличном ответе о выполнении функтора в данном QThread. Вы можете обратиться к нему за более подробной информацией.

person Mike    schedule 28.01.2017
comment
@TomášZato, чтобы запустить лямбду в определенном потоке, вы можете указать контекст QObject. - person Mike; 03.03.2017
comment
Можно ли включить параметры с помощью вариативных шаблонов С++ 11? - person Silicomancer; 28.06.2017
comment
@Silicomancer, Ну, у вас определенно может быть что-то вроде это. Но я думаю, что использование лямбда-выражений и захват нужных вам переменных делает метод захвата (то есть значение или ссылку) более понятным, когда вы будете читать код позже. Как вы думаете? Вы также можете взглянуть на альтернативный подход в этом ответе. - person Mike; 29.06.2017
comment
@Silicomancer, но при использовании этого подхода можно использовать блокировку соединений в очереди, чтобы дождаться завершения функции в другом потоке и, возможно, получить возвращаемое значение в текущем потоке, что-то вроде это. - person Mike; 29.06.2017
comment
Этот ответ отличный, к сожалению, он не работает в Qt 4.8 (нет функторов в соединениях: () - person AKludges; 05.06.2018