4

接続できるように、 qGetDownloadManagerを拡張してaの進行状況を出力し始めましTransferItemた。TableViewで表示するモデルのセルに進行状況データを挿入していDelegateます。最後にデリゲートが進行状況バーを描画します。それは理論的には機能しますが、私は次のことに遭遇しています

問題:複数のダウンロードが並行して行われている場合、両方のシグナルから両方のセルへの進行状況の更新を取得します。

ここに画像の説明を入力

QModelIndex indexどちらの進行状況バーにも進行状況データが表示されますが、シグナルは混合されており、現在のインデックス ( / )に固有のものではありませんindex.row()

(UserRoles 間の小さな遷移の問題は無視してください (ダウンロード ボタンをクリックした後、"ActionCell" が表示され、"ProgressBar" が表示される前に "Install" が表示されます)。それはここでの主な問題ではありません。私の質問はインデックスに関するものです。問題です。) テキスト "112" と "113" は intindex.rowです。

質問:

  • 複数の ProgressBars の進行状況データで TableView を更新する方法は?
  • ダウンロードごとに進行状況バーを表示するには、何を変更する必要がありますか?

ソース

ダウンロードの進行状況を出力する

クラスを介して信号を再発信するために、次のことを追加しました。信号が上部にバブルアップし、GUI から接続可能になります。

  1. -からQNetworkReply- downloadProgress(qint64,qint64)への接続TransferItemupdateDownloadProgress(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();
    }
    
  2. 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);
    }
    
  3. 新しいダウンロードがキューに入れられる と、発行さdownloadProgress(this)れた を Slot DownloadManager-に接続していdownloadProgress(TransferItem*)ます。(dlDownloadItem拡張する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*)));
    }
    
  4. 最後に、ダウンロードの進行状況をもう一度再発行しています。

    void DownloadManager::downloadProgress(TransferItem *item)
    {
        emit signalProgress(item->progress);
    }
    

Delegate、doDownload(index)、および ProgressBarUpdater を含む TableView

  1. QTableView
  2. を追加QSortFilterProxyModel(大文字と小文字を区別しないため)
  3. カスタムUserRolesColumnDelegateに基づいて DownloadButton と ProgressBar をレンダリングします。デリゲートはボタンのクリックを処理しますdownloadButtonClicked(index)。SIGNAL はeditorEvent(event, model, option, index)メソッドから発行されます。

    actionDelegate = new Updater::ActionColumnItemDelegate;
    ui->tableView->setItemDelegateForColumn(Columns::Action, actionDelegate);
    
    connect(actionDelegate, SIGNAL(downloadButtonClicked(QModelIndex)), this, SLOT(doDownload(QModelIndex)));
    
  4. doDownloadメソッドは を受け取り、indexモデルからダウンロード URL を取得します。次に、URL が DownloadManager に追加され、ProgressBarUpdater オブジェクトを設定して、指定されたインデックスのモデルに進行状況データを設定しています。最後に、に接続downloadManager::signalProgressprogressBar::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);
    }
    
  5. モデルの更新部分: 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);
    }
    
  6. レンダリング部分: デリゲートから偽の 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さん、ありがとうございました!! 私はあなたの提案に従い、それを修正しました:

ここに画像の説明を入力

4

1 に答える 1

2

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 が更新される前に進行状況を報告するために発生した最後のダウンロードに対応する、すべての進行状況バーに対して 1 つの異なる進行状況が表示されます。これが、マネージャーが 2 番目の進行状況のみを更新するため、最初のダウンロードが完了した後に変化が得られない理由です。

最後に、ダウンロードの進行状況をもう一度再発行しています。

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 *)、本体をdownloadProgressemit signalProgress(item);変更しvoid ProgressBarUpdater::updateProgress(TransferItem *)、 に変更し、本体で、転送アイテムのリクエストの URL を のモデルの URL と比較し、同じ場合にcurrentIndexRow限ります。model-setData()このソリューションは、1 つを変更するだけですべての進行状況アップデーターに送信されるため、あまり効率的ではありません。

  • 私が最初から提案してきた仲介者を切り取り、その本体で作成された/DownloadManager ::get()へのポインターを返すようにします。次に、転送アイテムを適切な進行状況アップデーターに直接接続できるため、必要なくなりますシグナルの場合は、シグナルの署名をに変更して、アイテムではなく進行状況を発行するだけです。これは実際には最も効率的なソリューションです。不要なものを削除するだけで、余分なものは何も必要ありません。DownloadItemTransferItemUpdaterDialog::doDownload()DownloadManager::downloadProgress()signalProgressTransferItemvoid downloadProgress(QMap<QString, QVariant>);

于 2016-04-22T09:50:37.977 に答える