2016-04-19 12 views
3

Я начал расширять qGet DownloadManager, чтобы исправить прогресс TransferItem, так что я могу подключиться к нему. Я вставляю данные прогресса в ячейку модели TableView для отображения с Delegate, и, наконец, делегат рисует индикатор выполнения. Это работает в теории, но я бегу в следующемКак обновить TableView с данными прогресса для нескольких ProgressBars?

Проблема: при наличии нескольких загрузок параллельно, то я получить прогресс обновления от обоих сигналов в как клетки!

enter image description here

Оба прогресса столбики показывают данные о ходе работы, но сигнал вид смешивают и не является уникальным для текущего индекса (QModelIndex index/index.row()).

(Пожалуйста, игнорируйте небольшую проблему перехода между UserRoles (после нажатия кнопки загрузки «ActionCell» отображается, а затем «Install», до появления «ProgressBar».) Это не основная проблема. относится к проблеме индекса.) Текст «112» и «113» - это int index.row.

Вопросы:

  • Как обновить TableView с данными о ходе работы для нескольких индикаторов выполнения?
  • Что я должен изменить, чтобы отобразить индикатор выполнения для каждой загрузки?

Источник

Испустите прогресс загрузки

я добавил следующие вещи переизлучались сигнал через классы, пока это не пузырится к наверх, где он становится подключаемым из GUI.

  1. соединение с 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(); 
    } 
    
  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*). (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*))); 
    } 
    
  4. Наконец, я вновь испускать прогресс загрузки еще раз:

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

Теперь TableView с делегатом, doDownload (индекс) и ProgressBarUpdater

  1. QTableView
  2. с добавлением QSortFilterProxyModel (для случая-нечувствительности)
  3. с добавлением ColumnDelegate, что делает DownloadButton и ProgressBar на основе пользовательских UserRoles. Делегат обрабатывает нажатие кнопки: СИГНАЛ 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))); 
    
  4. 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); 
    } 
    
  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 !! Я последовал за ваши предложения и установил его:

enter image description here

+0

Ваша реализация излишне сложна, неудивительно, что вещи перепутались. Вы пытались на самом деле отлаживать или по крайней мере ставить несколько 'qDebug()' s, чтобы определить, что и где идет не так? – dtech

+0

Я умышленно удалил строки отладки. «Ваша реализация излишне сложна». Не могли бы вы объяснить, как это упростить? –

+2

Я в основном согласен с ddriver. Невозможно понять, что происходит не так, если посмотреть на этот большой незавершенный фрагмент кода. Вы должны предоставить минимальный полный пример или просто упростить его. Удалите делегат, задайте текст элемента непосредственно из объекта обновления, добавьте некоторую отладку, чтобы проверить, обновлены ли правильные элементы и т. Д. ... – hank

ответ

1

DownloadManager отслеживает ход всех передач, и хранить данные каждой передачи элемента в соответствующем TransferItem.

Логическая вещь ИМО должна заключаться в подключении от каждого TransferItem к соответствующему ProgressBarUpdater и испускать из передаточного элемента.

Однако в вашем случае вы сообщаете прогресс не от каждого отдельного элемента передачи, а от диспетчера загрузки. Таким образом, каждый раз, когда вы излучаете прогресс, вы излучаете прогресс для определенного предмета переноса на все индикаторы выполнения.

connect(&downloadManager, SIGNAL(signalProgress(QMap<QString, QVariant>)), 
      progressBar, SLOT(updateProgress(QMap<QString, QVariant>))); 

Так вместо

TransferItem --progress--> CorrespondingUI

у вас есть:

TransferItem --transferItem--> DownloadManager --progress--> AllUIs

Это приводит к тому, один сингл и различный прогресс для всех баров прогресса, что соответствует последняя загрузка, которая будет сообщать о прогрессе перед обновлением пользовательского интерфейса. Вот почему вы не получаете больше изменений после того, как первая загрузка будет завершена, поскольку менеджер только обновляет прогресс для второго.

Наконец, я вновь испускать прогресс загрузки еще раз:

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

И кто именно нуждается анонимный прогрессом, не содержащее никакой информации, к которой он относится передать? Кроме ошибок, конечно.

Не могли бы вы объяснить, как его упростить?

Я был в конце моего психического канате вчера, когда я заметил, на ясной головой он не выглядит все это преувеличено, но все-таки я бы, вероятно, пойти на что-то более обтекаемый, с участием только 3 ключевых компонентов :

DownloadsManager -> DownloadController -> UI 
       -> DownloadController -> UI 

это просто кажется излишним иметь DownloadItem, а затем и в TransferItem, принимая во внимание, что загрузка происходит передача.

Модель и вид совершенно не нужны, так же как и сохранение прогресса в модели, а не просто наличие ее в качестве индикатора выполнения. Вы можете использовать только обычный виджет для каждой загрузки и размещать их в вертикальном макете.

Update:

Чрезмерный, ненужно раздробленность привела к уровню фрагментации, что делает его трудно получить доступ к данным, необходимым, чтобы сделать его работу, как только вы положили все вместе. Основная проблема заключается в том, что у вас нет возможности связать элемент перевода с соответствующим обновлением программы выполнения, и поскольку вы еще не разместили весь соответствующий код, самое простое возможное решение, которое я могу предложить, включает следующие незначительные изменения:

// 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 вместо испускать прогресс в все прогресс обновления приложений, вы просто LookUp тот, который на самом деле соответствует конкретной загрузке, и только обновить его прогресс. Это несколько неуклюжий, но, как я уже сказал, если ваш дизайн правильный, он не понадобится, и у вас не будет проблемы в первую очередь.

Есть и другие решения, а также:

  • изменение сигнала в DownloadManager к void signalProgress(TransferItem *), а тело downloadProgress к emit signalProgress(item);, изменение void ProgressBarUpdater::updateProgress(TransferItem *), и в теле сравнить URL запроса на передачу предмета к той в модели на currentIndexRow, и только model-setData(), если это то же самое. Это решение не очень эффективно, так как оно будет выдавать все обновления для обновления только для его изменения.

  • вырезать посредник, что я был с предложением с самого начала, сделать DownloadManager ::get() возвращает указатель на DownloadItem/TransferItem, созданный в своем теле, то в UpdaterDialog::doDownload() вы можете подключить элемент передачи непосредственно в соответствующем прогрессе Updater , поэтому вам больше не понадобится DownloadManager::downloadProgress() и сигнал signalProgress, вам нужно только изменить сигнатуру сигнала в TransferItem на void downloadProgress(QMap<QString, QVariant>); и испустить прогресс, а не элемент. На самом деле это наиболее эффективное решение, так как оно не включает в себя ничего лишнего, jsut удаление ненужных вещей.

+0

Извините за задержку. Да, thats spot on - проблема заключается в соединении с downloadManager to progressBar. Но я все еще не уверен, как его реализовать.- «анонимный прогресс, не содержащий никакой информации, к которой применяется передача». Должен ли я передать идентификатор индекса в диспетчер загрузки, чтобы установить его в TransferItem и позволить ему объединить данные о ходе? Или испустить сам TransferItem? Если я не передаю идентификатор, я могу использовать URL-адрес, чтобы связать его со следующей строкой. Hmm ... –

+0

Что касается избыточности 'DownloadItem' +' TransferItem': Да, его можно избавиться от 'TransferItem' и работать только с объектом' DownloadItem'. Его код Qt: https://github.com/qtproject/qtbase/blob/dev/tests/manual/qnetworkaccessmanager/qget/transferitem.cpp Я предполагаю, что они использовали этот подход для разграничения между объектом Upload и Download на основе метода запроса. –

+0

Вам не требуется соединение между менеджером и индикатором выполнения, индикатор выполнения на каждый элемент, менеджер - нет, но, как правило, вы не хотите «один ко многим», а «одно к одному» - обязательно вы можете пройти по индексу и игнорировать сигнал во всех, кроме нужного индекса, но это лишняя сложность и накладные расходы. – dtech