我是学习Qt的初学者,并试图理解Qt提供的用于下载操作的example。在downloadmanager.cpp中,成员函数如下:
void DownloadManager::append(const QUrl &url)
{
if (downloadQueue.isEmpty())
QTimer::singleShot(0, this, SLOT(startNextDownload()));
downloadQueue.enqueue(url);
++totalCount;
}
downloadQueue
为空,则需要在添加网址之前激活 startNextDownload()
。 (注意:如果startNextDownload()
为空则downloadQueue
结束程序)QTimer::singleShot(x, y, z)
已经被使用过。据我了解,这是一个延迟 0 毫秒激活插槽的计时器。我是初学者,例如:
statement1;
statement2;
我习惯于看到
statement1
运行并完成,然后再继续处理 statement2
。但是尝试学习 Qt 并阅读给定的示例,我看到 SLOT(startNextDownload())
在 downloadQueue.enqueue(url);
发生后被激活。我试图理解为什么这有效。
这会将回调放入消息队列中。
定时器立即到期,一条消息被发布到消息队列中。当进程下次到达主循环时,将调用
startNextDownload()
函数。此时,该 URL 已经在队列中了。
startNextDownload()
函数是从调度上下文中调用的,可以安全地更改窗口内容。这样,可以在多线程应用程序中使用 DownloadManager
类,其中启动下载的线程可能与 Paint 事件的处理程序同时运行。通过从处理 Paint 事件的同一线程调用它,您可以确保没有处理此类事件,并且您可以安全地更新小部件。
如果之后需要重新绘制某个小部件,它会要求重新绘制,并且如果该小部件当前可见,操作系统将发送一个 Paint 事件。
对
QTimer::singleShot(...)
的每次调用都在调用它的线程的 事件循环中执行 **。如果从主线程调用,它将是以 app.exec()
开始的事件循环。
根据 Qt-Network-Manager-Example,此函数在网络管理器填充 URL 后调用,因此将在队列完全填满后处理单次。不幸的是,qt 文档对此主题还不太清楚,因此有关事件处理等的更多信息,请查看here。
旧问题标题的答案在开始之前,计时器用于在额外的线程中进行下载。因此 GUI 保持响应。 完整的
downloadNext()
方法是递归的。它只会被调用一次,直到队列为空为止。 看看这个:
void DownloadManager::append(const QStringList &urlList)
{
foreach (QString url, urlList)
append(QUrl::fromEncoded(url.toLocal8Bit())); //Call for only one URL
...
}
void DownloadManager::append(const QUrl &url)
{
if (downloadQueue.isEmpty())
//I'm only called if the queue is empty! And I will be called after the next line. Not instantly!
QTimer::singleShot(0, this, SLOT(startNextDownload()));
downloadQueue.enqueue(url);
++totalCount;
}
队列为空后,每个方法都会返回,并且至少会打印下载完成的消息。那么为什么这会起作用?
请参阅下面的第一章。