您的线程不会更新MainWindow
,但它确实会在每次迭代时创建一个全新的QApplication
和MainWindow
。您的线程应该卡在QApplication::exec
之内,直到您退出应用程序(例如关闭窗口)。只有这样你的线程循环才能取得进一步的进展。
通常,从主线程外部进行更新时必须非常小心,因为通常GUI操作必须在主线程内执行。
想想使用QThread
,它已经有了它自己的事件循环,您可以使用它来使用相应的插槽通知/更新窗口。
没有关于您实际尝试实现什么的进一步细节,不可能给您进一步的指示。至少,我建议您在主线程内创建您的QApplication
和MainWindow
(例如main
)。那么这取决于你想要“更新”什么。如果您需要处理一些数据,那么您可以在第二个线程中执行此操作,并使用信号插槽将结果发送到您的MainWindow
实例。如果你需要在窗口上绘图,那么这个任务必须直接在主线程中完成,否则你可能会找到一种方法从你的线程中渲染到一个单独的缓冲区(即QImage
),然后将这个缓冲区发送到主将其绘制到窗口中的线程。
我试图描绘一下如何做这样的事情。但是请注意,它既不完整也不可编译,而仅仅是一个轮廓。
首先,你有你的MainWindow
并加上signal
,通知所有观察者开始工作(一会儿就会变得清晰)。此外,您还可以添加slots
,只要其中一个值发生更改,就会调用slots
。在主线程中那些slots
运行(并且是MainWindow
的成员),因此可以更新窗口然而,他们需要:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
// constructors and stuff
void startWorking()
{
emit startWorkers();
}
public slots:
void onModeChanged(Modes m)
{
// update your window with new mode
}
void onDistanceChanged(float distance)
{
// update your window with new distance
}
signals:
void startWorkers();
};
接下来,建立一个Worker
类,封装了所有的“后台工作”你喜欢做的(基本上是你的线程在原密码一样):
class Worker : public QObject
{
Q_OBJECT
public:
// constructors and stuff
public slots:
void doWork()
{
while(!done)
{
// do stuff ...
Modes m = // change mode
emit modeModified(m);
// do stuff ...
float distance = // compute distance
emit distanceModified(distance);
// do stuff ...
}
}
signals:
void modeModified(Modes m);
void distanceModified(float distance);
};
注意,那Worker
必须继承QObject
和你doWork
方法必须是一个public slot
。此外,您还可以为每个喜欢MainWindow
的值添加一个signal
。由于它是由Qt MOC(元对象编译器)生成的,因此不需要实现它们。每当其中一个值发生变化时,只需emit
对应的signal
并传递新值。
最后,你把一切融合在一起:
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow window;
// create a worker object
Worker* worker = new Worker;
// connect signals and slots between worker and main window
QObject::connect(worker, &Worker::modeModified,
&window, &MainWindow::onModeChanged);
QObject::connect(worker, &Worker::distanceModified,
&window, &MainWindow::onDistanceChanged);
QObject::connect(&window, &MainWindow::startWorkers,
worker, &Worker::doWork);
// create a new thread
QThread* thread = new QThread;
// send worker to work inside this new thread
worker->moveToThread(thread);
thread->start();
// show window and start doing work
window.show();
window.startWorking();
// start main loop
int result = app.exec();
// join worker thread and perform cleanup
return result;
}
好吧,让我们通过它去。首先,在主线程中创建您的QApplication
和MainWindow
。接下来,创建一个Worker
对象的实例(可以在此处创建多个实例)。然后您将worker
的信号添加到window
的插槽中,反之亦然。一旦这些连接建立起来,每当你有一个信号时,连接的时隙就会被Qt调用(并且传送的值被传送)。注意,这个连接可以跨越线程边界。每当信号从与接收对象的线程不同的线程发出时,Qt将发送一条消息,在接收对象的线程中处理该消息。
然后,您告诉Qt,您希望您的worker
使用QObject::moveToThread
在另一个线程内生存。有关如何正确使用QThread
及其内部对象的详细说明,请参见here。
其余的就很简单。 show
您的window
并开始处理。这里可能有不同的方式。我只是在这里调用startWorking
方法,然后emit
为startWorkers
信号,它连接到doWork
的方法,使得doWork
将在另一个线程接收到该信号之后开始执行。
然后调用QApplication::exec
运行主线程的事件循环,所有这些信号都由Qt处理。一旦您的应用程序关闭(例如通过拨打quit
或关闭主窗口)exec
方法将返回并且您返回main
。请注意,您需要正确关闭线程(例如,通过发送一个停止while
循环的加法信号)并加入它。你也应该删除所有分配的对象(worker
,thread
)。为了简化代码示例,我在此省略了这一点。
回答你的问题
我有很多的功能,例如,updateClips和mavReceive应定期调用,相互独立运行。我应该为每个函数创建一个不同的Worker类,因为每个函数都有不同的信号,并且每个函数都有一个QThread对象,对吗?我不需要startTimer()了吗?如果是的话,我怎么能控制每个函数的调用间隔(使用与startTimer()
从注释工作要做:
答案在很大程度上取决于究竟你的意思是“应该被称为定期“,谁应该给他们打电话?用户?或者他们是否应该定期执行?
所以原则上,你可以在一个线程中有多个工作者,但是如果他们应该一直工作(在一个while循环中旋转)它是没有意义的,因为一个正在运行并且所有其他的都被阻塞了。在这种情况下,每个worker都会有一个线程。
如果我理解正确,您有兴趣定期更新某些内容(例如,每500ms)。在这种情况下,我强烈建议使用QTimer
。您可以设置一个时间间隔,然后启动它。定时器将定期emit
timeout
信号,您可以连接到任何您想要执行的功能(更确切地说slot
)。
的Worker
的更新版本看起来是这样的:
class Worker : public QObject
{
Q_OBJECT
public:
Worker()
{
QObject::connect(&modeTimer_, &QTimer::timeout,
this, &Worker::onModeTimerTimeout);
QObject::connect(&distanceTimer_, &QTimer::timeout,
this, &Worker::onDistanceTimerTimeout);
modeTimer_.start(500); // emit timeout() every 500ms
distanceTimer_.start(100); // emit timeout() every 100ms
}
public slots:
void onModeTimerTimeout()
{
// recompute mode
Modes m = // ...
emit modeModified(m);
}
void onDistanceTimerTimeout()
{
// recompute distance
float distance = // ...
emit distanceModified(distance);
}
signals:
void modeModified(Modes m);
void distanceModified(float distance);
private:
QTimer modeTimer_;
QTimer distanceTimer_;
};
通知,确立了在构造函数中的连接。只要其中一个定时器超时,就会调用连接的slot
。然后该槽可以计算它需要的任何值,然后使用与之前相同的signal
将结果发送回主线程中的MainWindow
。因此,正如你所看到的,你可以在一个Worker
(也就是一个线程)内有多个定时器/重新计算/更新信号。然而,实现的关键点是计算需要多长时间。如果它们花费很长时间(例如几乎与区间一样长),那么您应该考虑使用多个线程来加速计算(意思是:在每个线程中执行一次计算)。当我慢慢地看到你想要获得的更清晰的图像时,我想知道是否仅仅是关于这些定期更新,你在你的问题中“滥用”了线索。如果确实如此,那么你根本不需要那个线程和Worker
。然后只需将定时器添加到MainWindow
,并将它们的timeout
信号直接连接到MainWindow
的相应slot
即可。
乍一看,你的代码似乎在很多方面都被破坏了...... –
你想用计时器实现什么?为什么不直接从主窗口启动窗口? – SPlatten
感谢您的回复。 @nh_我是qt的新手。你能指出我的代码中最重要的问题吗? @SPlatten我的代码主要运行在线程中,每个线程执行不同的功能并以不同的时间间隔调用。在主函数中,我只为不同的函数调用'timerStart'。当我从main启动窗口时,它冻结而其他线程运行。 –