我已经开始扩展qGet DownloadManager
以发出TransferItem
的进度,以便我可以连接到它。我将进度数据插入到TableView
模型的单元格中,以便显示Delegate
,最后委托绘制进度条。这在理论上有效,但我遇到了以下
问题:当有多个并行下载时,我会从两个信号进入两个单元格进行更新!
两个进度条都显示进度数据,但信号有点混合,并非当前索引唯一(QModelIndex index
/ index.row()
)。
(请忽略UserRoles之间的小转换问题(单击下载按钮“ActionCell”后显示,然后在“ProgressBar”出现之前显示“Install”。)。这不是主要问题。我的问题是关于索引问题。)文本“112”和“113”是int index.row
。
问题:
来源
发布下载进度
我添加了以下内容以通过类重新发出信号,直到它冒泡到顶部,从GUI可以连接到它。
从QNetworkReply
- downloadProgress(qint64,qint64)
到TransferItem
- updateDownloadProgress(qint64,qint64)
的连接
void TransferItem::startRequest()
{
reply = nam.get(request);
connect(reply, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
this, SLOT(updateDownloadProgress(qint64,qint64)));
connect(reply, SIGNAL(finished()), this, SLOT(finished()));
timer.start();
}
SLOT函数TransferItem
- updateDownloadProgress(qint64,qint64)
作为接收者计算进度并将其存储在progress
(QMap<QString, QVariant>
)中。
计算完成后,会发出downloadProgress(this)
信号。
// SLOT
void TransferItem::updateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
progress["bytesReceived"] = QString::number(bytesReceived);
progress["bytesTotal"] = QString::number(bytesTotal);
progress["size"] = getSizeHumanReadable(outputFile->size());
progress["speed"] = QString::number((double)outputFile->size()/timer.elapsed(),'f',0).append(" KB/s");
progress["time"] = QString::number((double)timer.elapsed()/1000,'f',2).append("s");
progress["percentage"] = (bytesTotal > 0) ? QString::number(bytesReceived*100/bytesTotal).append("%") : "0 %";
emit downloadProgress(this);
}
QString TransferItem::getSizeHumanReadable(qint64 bytes)
{
float num = bytes; QStringList list;
list << "KB" << "MB" << "GB" << "TB";
QStringListIterator i(list); QString unit("bytes");
while(num >= 1024.0 && i.hasNext()) {
unit = i.next(); num /= 1024.0;
}
return QString::fromLatin1("%1 %2").arg(num, 3, 'f', 1).arg(unit);
}
当新的下载入队时,我将发出的downloadProgress(this)
与插槽DownloadManager
- downloadProgress(TransferItem*)
相关联。 (dl
是DownloadItem
,TransferItem
}。
void DownloadManager::get(const QNetworkRequest &request)
{
DownloadItem *dl = new DownloadItem(request, nam);
transfers.append(dl);
FilesToDownloadCounter = transfers.count();
connect(dl, SIGNAL(downloadProgress(TransferItem*)),
SLOT(downloadProgress(TransferItem*)));
connect(dl, SIGNAL(downloadFinished(TransferItem*)),
SLOT(downloadFinished(TransferItem*)));
}
最后,我再次重新发布下载进度:
void DownloadManager::downloadProgress(TransferItem *item)
{
emit signalProgress(item->progress);
}
现在TableView with Delegate,doDownload(index)和ProgressBarUpdater
QTableView
QSortFilterProxyModel
(不区分大小写)添加了ColumnDelegate
,根据自定义UserRoles呈现DownloadButton和ProgressBar。代表处理按钮点击:SIGNAL downloadButtonClicked(index)
从editorEvent(event, model, option, index)
方法退出。
actionDelegate = new Updater::ActionColumnItemDelegate;
ui->tableView->setItemDelegateForColumn(Columns::Action, actionDelegate);
connect(actionDelegate, SIGNAL(downloadButtonClicked(QModelIndex)), this, SLOT(doDownload(QModelIndex)));
doDownload
方法接收index
并从模型中提取下载URL。然后将URL添加到DownloadManager
我正在设置ProgressBarUpdater对象,以将进度数据设置为给定索引处的模型。最后,我将downloadManager::signalProgress
连接到progressBar::updateProgress
并调用downloadManager::checkForAllDone
开始下载处理。
void UpdaterDialog::doDownload(const QModelIndex &index)
{
QUrl downloadURL = getDownloadUrl(index);
if (!validateURL(downloadURL)) return;
QNetworkRequest request(downloadURL);
downloadManager.get(request); // QueueMode is Parallel by default
ProgressBarUpdater *progressBar = new ProgressBarUpdater(this, index.row());
progressBar->setObjectName("ProgressBar_in_Row_" + QString::number(index.row()) );
connect(&downloadManager, SIGNAL(signalProgress(QMap<QString, QVariant>)),
progressBar, SLOT(updateProgress(QMap<QString, QVariant>)));
QMetaObject::invokeMethod(&downloadManager, "checkForAllDone", Qt::QueuedConnection);
}
模型更新部分:ProgressBarUpdater获取索引和进度,并应更新给定索引处的模型。
ProgressBarUpdater::ProgressBarUpdater(UpdaterDialog *parent, int currentIndexRow) :
QObject(parent), currentIndexRow(currentIndexRow)
{
model = parent->ui->tableView_1->model();
}
void ProgressBarUpdater::updateProgress(QMap<QString, QVariant> progress)
{
QModelIndex actionIndex = model->index(currentIndexRow, UpdaterDialog::Columns::Action);
// set progress to model
model->setData(actionIndex, progress, ActionColumnItemDelegate::DownloadProgressBarRole);
model->dataChanged(actionIndex, actionIndex);
}
渲染部分:我从委托中渲染假的ProgressBar;使用index.model()->data(index, DownloadProgressBarRole)
获取进度数据。
void ActionColumnItemDelegate::drawDownloadProgressBar(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionProgressBarV2 opt;
opt.initFrom(bar);
opt.rect = option.rect;
opt.rect.adjust(3,3,-3,-3);
opt.textVisible = true;
opt.textAlignment = Qt::AlignCenter;
opt.state = QStyle::State_Enabled | QStyle::State_Active;
// get progress from model
QMap<QString, QVariant> progress =
index.model()->data(index, DownloadProgressBarRole).toMap();
QString text = QString::fromLatin1(" %1 %2 %3 %4 %5 ")
.arg(QString::number(index.row()))
.arg(progress["percentage"].toString())
.arg(progress["size"].toString())
.arg(progress["speed"].toString())
.arg(progress["time"].toString());
opt.minimum = 0;
opt.maximum = progress["bytesTotal"].toFloat();
opt.progress = progress["bytesReceived"].toFloat();
opt.text = text;
bar->style()->drawControl(QStyle::CE_ProgressBar,&opt,painter,bar);
}
我已将QString::number(index.row()
添加到进度条文本中,以便每个ProgressBar获取其行号。换句话说:渲染对于行是唯一的,但传入的进度数据在某种程度上是混合的。
我现在停留在索引问题上一段时间了。提前感谢您的帮助。
更新:问题已解决!
非常感谢ddriver !!我按照你的建议修改了它:
答案 0 :(得分:1)
DownloadManager
跟踪所有转移的进度,并将每个转移项的数据保存在相应的TransferItem
中。
IMO的逻辑是将每个TransferItem
与相应的ProgressBarUpdater
建立连接,并从传输项中发出。
但是,在您的情况下,您报告的进度不是来自每个单独的传输项目,而是来自下载管理器。因此,每当您发布进度时,您都会将特定转移项目的进度发送到所有进度条。
connect(&downloadManager, SIGNAL(signalProgress(QMap<QString, QVariant>)),
progressBar, SLOT(updateProgress(QMap<QString, QVariant>)));
所以而不是
TransferItem --progress--> CorrespondingUI
TransferItem --transferItem--> DownloadManager --progress--> AllUIs
这导致所有进度条都有一个单一且不同的进度,这对应于在UI更新之前报告进度的最后一次下载。这就是为什么在第一次下载完成后你没有得到更多的变化,因为经理只更新第二次下载的进度。
最后,我再次重新发布下载进度:
void DownloadManager::downloadProgress(TransferItem *item)
{
emit signalProgress(item->progress);
}
谁确切需要匿名进展,不包含任何适用于哪种转移的信息?除了当然的错误。
你会很高兴解释,如何简化它?
昨天,当我发表评论时,我的精神状态已经结束了。在一个清醒的头脑中,它看起来并不过分,但我仍然可能会采用更精简的方式,仅涉及3个关键部件:
DownloadsManager -> DownloadController -> UI
-> DownloadController -> UI
考虑到下载是转移,有DownloadItem
然后还有TransferItem
似乎多余。
模型和视图也是完全不必要的,就像在模型中存储进度而不仅仅是将其作为进度条的成员一样。每次下载都可以使用常规小部件,并将它们放在垂直布局中。
更新
过度的,不必要的划分导致了一定程度的碎片化,这使得很难获取数据,一旦将所有内容放在一起就需要使其工作。主要问题是您无法将转移项目绑定到正确的进度条更新程序,并且由于仍尚未发布所有相关代码,因此我可以提供的最简单的解决方案涉及以下小改动:
// in DownloadManager
void signalProgress(QMap<QString, QVariant>); // this signal is unnecessary, remove
void DownloadManager::downloadProgress(TransferItem *item) // change this
{
registry[item->request.url()]->updateProgress(item->progress);
}
QMap<QUrl, ProgressBarUpdater *> registry; // add this
// in UpdaterDialog
void UpdaterDialog::doDownload(const QModelIndex &index)
{
QUrl downloadURL = getDownloadUrl(index);
if (!validateURL(downloadURL)) return;
QNetworkRequest request(downloadURL);
downloadManager.get(request); // QueueMode is Parallel by default
ProgressBarUpdater *progressBar = new ProgressBarUpdater(this, index.row());
progressBar->setObjectName("ProgressBar_in_Row_" + QString::number(index.row()) );
// remove the connection - source of the bug, instead register the updater
downloadManager.registry[downloadURL] = progressBar;
QMetaObject::invokeMethod(&downloadManager, "checkForAllDone", Qt::QueuedConnection);
}
就是这样,进度更新程序与URL相关联,而在DownloadManager::downloadProgress
而不是向所有进度更新程序发送进度,您只需查找实际对应的进度更新程序特定的下载,只更新其进度。它有点笨拙,但正如我所说,如果你的设计是正确的,那就不需要了,你首先就不会遇到问题。
还有其他解决方案:
将DownloadManager的信号更改为void signalProgress(TransferItem *)
,将downloadProgress
的正文更改为emit signalProgress(item);
,更改为void ProgressBarUpdater::updateProgress(TransferItem *)
,并在正文中比较转移项目的请求url指向currentIndexRow
模型中的那个,如果相同则仅model-setData()
。这个解决方案效率不高,因为它会向所有进度更新程序发出只是为了修改它。
切断了中间人,我从一开始就建议,让DownloadManager ::get()
返回指向其身体中创建的DownloadItem
/ TransferItem
的指针,然后是UpdaterDialog::doDownload()
您可以将转移项直接连接到相应的进度更新程序,这样您就不再需要DownloadManager::downloadProgress()
和signalProgress
信号,只需更改TransferItem
信号的签名即可{1}}到void downloadProgress(QMap<QString, QVariant>);
并发出进度而不是项目。这实际上是最有效的解决方案,因为它不需要额外的任务,只需删除不必要的东西。