存在许多类似的问题,但没有找到合适的答案。
我使用第三方库。
当调用lib类中的某些虚拟方法时,会从我的应用程序启动不的工作线程调用这些方法。这个帖子不是QThread,也不是QThread。
我可以从这个线程发出,但只有当我使用Qt :: DirectConnection连接插槽时。 结果是SLOT中的QObject :: sender()将始终返回NULL。 我希望调用deleteLater(),但是只能在QThread中调度。
我想我需要回到主线程,但是如何在主线程上发出信号呢?
示例:当调用以下方法时,它是在第三方库创建的线程上完成的。
/*virtual*/ bool MediaPlayer::onEof()
{
stopTransmit();
emit sigFinished(); // slots only called if bound using Qt::DirectConnection
deleteLater(); // dtor is never called
return false;
}
连接也是在非QThread上下文中进行的,如下所示:
/*virtual*/ void
SipCall::state_answer_call::onEntering(SipCall& ref)
{
...
MediaPlayer* player = new MediaPlayer;
ref.connect(player, SIGNAL(sigFinished()), SLOT(slotMediaFinished()), Qt::DirectConnection);
...
}
如果没有明确的Qt::DirectConnection
,则永远不会调用SipCall::slotMediaFinished()
。
答案 0 :(得分:2)
问题是您的MediaPlayer
实例player
是在没有活动事件循环的线程上创建的。
将代码更改为...
ref.connect(player, SIGNAL(sigFinished()), SLOT(slotMediaFinished()), Qt::QueuedConnection);
Qt基础架构会将事件发布到与player
关联的线程。由于没有事件循环来处理该事件,因此永远不会调用目标代码MediaPlayer::slotMediaFinished
。
所以问题是......你希望调用哪个线程MediaPlayer::slotMediaFinished
?如果它是您可以尝试的主应用程序线程(假设您使用的是qt5和c ++ 11)...
ref.connect(player, &MediaPlayer::sigFinished, QCoreApplication::instance(),
[&]()
{
player->slotMediaFinished();
},
Qt::QueuedConnection);
这将使用与QThread
实例关联的QCoreApplication
作为执行lambda的context。
修改强>
正如你所说,你不能使用qt5或c ++ 11我唯一可以建议的另一个选择是在你的主应用程序线程上有一个QObject
派生变量,它可以充当代理上下文player
...
class proxy: public QObject {
Q_OBJECT;
public slots:
void slotMediaFinished ()
{
if (MediaPlayer *player = dynamic_cast<MediaPlayer *>(sender()) {
player->slotMediaFinished();
}
}
};
在您的应用程序线程上创建上述实例,然后您的connect
语句将是......
ref.connect(player, SIGNAL(sigFinished()), &proxy_instance, SLOT(slotMediaFinished()), Qt::QueuedConnection)
其中proxy_instance
,呃,proxy
实例。现在,当发出sigFinished
信号时,Qt会将事件发布到proxy_instance
。反过来,我会调用proxy::slotMediaFinished
来识别MediaPlayer
感兴趣的QObject::sender
实例并调用其slotMediaFinished
成员。
答案 1 :(得分:1)
当调用lib类中的某些虚拟方法时,这些方法是从我的应用程序尚未启动的工作线程调用的。这个帖子不是QThread,也不是QThread。
错误地认为这是一个问题。它不是。发出信号的线程并不重要。它不必使用QThread
开始。
事实上,从C回调发出信号是将多线程C回调API连接到Qt的惯用方法。它意味着不需要任何努力。
我可以从这个线程发出,但只有当我使用Qt :: DirectConnection连接插槽时才会发出。
那不是真的。当信号和插槽位于不同的线程中时,如果连接的插槽/仿函数是线程安全的,则只能使用直接连接。我怀疑你的是,所以你应该不使用直接连接。自动连接将完全满足您的需求。
它不起作用,因为接收SipCall
实例并不存在于具有正在运行的事件循环的线程中。您必须将其移动到这样的线程,或者使用生成在主线程中的thunk仿函数,如the G.M.'s answer to this question中所建议的那样。
thunk仿函数的问题在于它们会混淆您调用方法的对象的线程所有权。最有可能SipCall
方法不是线程安全的 ,所以通过从主线程中调用它们,你必然会破坏它们。将播放器移动到应用程序线程是最安全的,并确保它仅在该线程中使用。那时你不需要thunk functors。
如果您想了解在给定线程中运行某些代码(例如仿函数)的方式,请参阅this question。