19#include "moc_qgstaskmanagerwidget.cpp" 
   27#include <QProgressBar> 
   41  QVBoxLayout *vLayout = 
new QVBoxLayout();
 
   42  vLayout->setContentsMargins( 0, 0, 0, 0 );
 
   43  mTreeView = 
new QTreeView();
 
   44  mModel = 
new QgsTaskManagerModel( manager, 
this );
 
   45  mTreeView->setModel( mModel );
 
   46  connect( mModel, &QgsTaskManagerModel::rowsInserted, 
this, &QgsTaskManagerWidget::modelRowsInserted );
 
   47  mTreeView->setHeaderHidden( 
true );
 
   48  mTreeView->setRootIsDecorated( 
false );
 
   49  mTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
 
   51  const int progressColWidth = 
static_cast<int>( fontMetrics().horizontalAdvance( 
'X' ) * 10 * 
Qgis::UI_SCALE_FACTOR );
 
   52  mTreeView->setColumnWidth( QgsTaskManagerModel::Progress, progressColWidth );
 
   54  const int statusColWidth = 
static_cast<int>( fontMetrics().horizontalAdvance( 
'X' ) * 2 * 
Qgis::UI_SCALE_FACTOR );
 
   55  mTreeView->setColumnWidth( QgsTaskManagerModel::Status, statusColWidth );
 
   56  mTreeView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
 
   57  mTreeView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
 
   58  mTreeView->header()->setStretchLastSection( 
false );
 
   59  mTreeView->header()->setSectionResizeMode( QgsTaskManagerModel::Description, QHeaderView::Stretch );
 
   61  connect( mTreeView, &QTreeView::clicked, 
this, &QgsTaskManagerWidget::clicked );
 
   63  vLayout->addWidget( mTreeView );
 
 
   74void QgsTaskManagerWidget::modelRowsInserted( 
const QModelIndex &, 
int start, 
int end )
 
   76  for ( 
int row = start; row <= end; ++row )
 
   78    QgsTask *task = mModel->indexToTask( mModel->index( row, 1 ) );
 
   82    QProgressBar *progressBar = 
new QProgressBar();
 
   83    progressBar->setAutoFillBackground( 
true );
 
   84    progressBar->setRange( 0, 0 );
 
   89        progressBar->setMaximum( 100 );
 
   90        progressBar->setValue( 
static_cast<int>( progress ) );
 
   93        progressBar->setMaximum( 0 );
 
   95    mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Progress ), progressBar );
 
   97    QgsTaskStatusWidget *statusWidget = 
new QgsTaskStatusWidget( 
nullptr, task->
status(), task->
canCancel() );
 
   98    statusWidget->setAutoFillBackground( 
true );
 
  100    connect( statusWidget, &QgsTaskStatusWidget::cancelClicked, task, &
QgsTask::cancel );
 
  101    mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Status ), statusWidget );
 
  105void QgsTaskManagerWidget::clicked( 
const QModelIndex &index )
 
  107  QgsTask *task = mModel->indexToTask( index );
 
  119QgsTaskManagerModel::QgsTaskManagerModel( 
QgsTaskManager *manager, QObject *parent )
 
  120  : QAbstractItemModel( parent )
 
  121  , mManager( manager )
 
  123  Q_ASSERT( mManager );
 
  126  const auto constTasks = mManager->
tasks();
 
  127  for ( 
QgsTask *task : constTasks )
 
  129    mRowToTaskIdList << mManager->
taskId( task );
 
  137QModelIndex QgsTaskManagerModel::index( 
int row, 
int column, 
const QModelIndex &parent )
 const 
  139  if ( column < 0 || column >= columnCount() )
 
  142    return QModelIndex();
 
  145  if ( !parent.isValid() && row >= 0 && row < mRowToTaskIdList.count() )
 
  148    return createIndex( row, column );
 
  152  return QModelIndex();
 
  155QModelIndex QgsTaskManagerModel::parent( 
const QModelIndex &index )
 const 
  160  return QModelIndex();
 
  163int QgsTaskManagerModel::rowCount( 
const QModelIndex &parent )
 const 
  165  if ( !parent.isValid() )
 
  167    return mRowToTaskIdList.count();
 
  176int QgsTaskManagerModel::columnCount( 
const QModelIndex &parent )
 const 
  182QVariant QgsTaskManagerModel::data( 
const QModelIndex &index, 
int role )
 const 
  184  if ( !index.isValid() )
 
  187  QgsTask *task = indexToTask( index );
 
  192      case Qt::DisplayRole:
 
  194        switch ( index.column() )
 
  207      case static_cast<int>( CustomRole::Status ):
 
  208        return static_cast<int>( task->status() );
 
  210      case Qt::ToolTipRole:
 
  211        switch ( index.column() )
 
  214            return createTooltip( task, ToolTipDescription );
 
  216            return createTooltip( task, ToolTipProgress );
 
  218            return createTooltip( task, ToolTipStatus );
 
  232Qt::ItemFlags QgsTaskManagerModel::flags( 
const QModelIndex &index )
 const 
  234  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
 
  236  if ( !index.isValid() )
 
  241  QgsTask *task = indexToTask( index );
 
  242  if ( index.column() == Status )
 
  245      flags = flags | Qt::ItemIsEditable;
 
  247  return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
 
  250bool QgsTaskManagerModel::setData( 
const QModelIndex &index, 
const QVariant &value, 
int role )
 
  254  if ( !index.isValid() )
 
  257  QgsTask *task = indexToTask( index );
 
  261  switch ( index.column() )
 
  265      if ( value.toBool() && task->
canCancel() )
 
  275void QgsTaskManagerModel::taskAdded( 
long id )
 
  277  beginInsertRows( QModelIndex(), mRowToTaskIdList.count(), mRowToTaskIdList.count() );
 
  278  mRowToTaskIdList << id;
 
  282void QgsTaskManagerModel::taskDeleted( 
long id )
 
  284  for ( 
int row = 0; row < mRowToTaskIdList.count(); ++row )
 
  286    if ( mRowToTaskIdList.at( row ) == 
id )
 
  288      beginRemoveRows( QModelIndex(), row, row );
 
  289      mRowToTaskIdList.removeAt( row );
 
  296void QgsTaskManagerModel::progressChanged( 
long id, 
double progress )
 
  300  const QModelIndex index = idToIndex( 
id, Progress );
 
  301  if ( !index.isValid() )
 
  306  emit dataChanged( index, index );
 
  309void QgsTaskManagerModel::statusChanged( 
long id, 
int status )
 
  317    const QModelIndex index = idToIndex( 
id, Status );
 
  318    if ( !index.isValid() )
 
  323    emit dataChanged( index, index );
 
  327QgsTask *QgsTaskManagerModel::indexToTask( 
const QModelIndex &index )
 const 
  329  if ( !index.isValid() || index.parent().isValid() )
 
  332  const long id = index.row() >= 0 && index.row() < mRowToTaskIdList.count() ? mRowToTaskIdList.at( index.row() ) : -1;
 
  334    return mManager->task( 
id );
 
  339int QgsTaskManagerModel::idToRow( 
long id )
 const 
  341  for ( 
int row = 0; row < mRowToTaskIdList.count(); ++row )
 
  343    if ( mRowToTaskIdList.at( row ) == 
id )
 
  351QModelIndex QgsTaskManagerModel::idToIndex( 
long id, 
int column )
 const 
  353  const int row = idToRow( 
id );
 
  355    return QModelIndex();
 
  357  return index( row, column );
 
  360QString QgsTaskManagerModel::createTooltip( 
QgsTask *task, ToolTipType type )
 
  366      case ToolTipDescription:
 
  370      case ToolTipProgress:
 
  375            return tr( 
"Queued" );
 
  377            return tr( 
"On hold" );
 
  380            if ( type == ToolTipStatus && !task->
canCancel() )
 
  381              return tr( 
"Running (cannot cancel)" );
 
  383              return tr( 
"Running" );
 
  386            return tr( 
"Complete" );
 
  388            return tr( 
"Terminated" );
 
  394  QString formattedTime;
 
  401    const qint64 msRemain = 
static_cast<qint64
>( elapsed * 100.0 / task->
progress() - elapsed );
 
  402    if ( msRemain > 120 * 1000 )
 
  404      const long long minutes = msRemain / 1000 / 60;
 
  405      const int seconds = ( msRemain / 1000 ) % 60;
 
  406      formattedTime = tr( 
"%1:%2 minutes" ).arg( minutes ).arg( seconds, 2, 10, QChar( 
'0' ) );
 
  409      formattedTime = tr( 
"%1 seconds" ).arg( msRemain / 1000 );
 
  411    formattedTime = tr( 
"Estimated time remaining: %1" ).arg( formattedTime );
 
  413    const QTime estimatedEnd = QTime::currentTime().addMSecs( msRemain );
 
  414    formattedTime += tr( 
" (%1)" ).arg( QLocale::system().toString( estimatedEnd, QLocale::ShortFormat ) );
 
  418    if ( elapsed > 120 * 1000 )
 
  420      const long long minutes = elapsed / 1000 / 60;
 
  421      const int seconds = ( elapsed / 1000 ) % 60;
 
  422      formattedTime = tr( 
"%1:%2 minutes" ).arg( minutes ).arg( seconds, 2, 10, QChar( 
'0' ) );
 
  425      formattedTime = tr( 
"%1 seconds" ).arg( elapsed / 1000 );
 
  427    formattedTime = tr( 
"Time elapsed: %1" ).arg( formattedTime );
 
  432    case ToolTipDescription:
 
  433      return tr( 
"%1<br>%2" ).arg( task->
description(), formattedTime );
 
  436    case ToolTipProgress:
 
  441          return tr( 
"Queued" );
 
  443          return tr( 
"On hold" );
 
  447          if ( type == ToolTipStatus && !task->
canCancel() )
 
  448            statusDesc = tr( 
"Running (cannot cancel)" );
 
  450            statusDesc = tr( 
"Running" );
 
  451          return tr( 
"%1<br>%2" ).arg( statusDesc, formattedTime );
 
  454          return tr( 
"Complete" );
 
  456          return tr( 
"Terminated" );
 
  469QgsTaskStatusWidget::QgsTaskStatusWidget( QWidget *parent, 
QgsTask::TaskStatus status, 
bool canCancel )
 
  471  , mCanCancel( canCancel )
 
  474  setMouseTracking( 
true );
 
  477QSize QgsTaskStatusWidget::sizeHint()
 const 
  479  return QSize( 32, 32 );
 
  482void QgsTaskStatusWidget::setStatus( 
int status )
 
  488void QgsTaskStatusWidget::paintEvent( QPaintEvent *e )
 
  490  QWidget::paintEvent( e );
 
  520  icon.paint( &p, 1, height() / 2 - 12, 24, 24 );
 
  524void QgsTaskStatusWidget::mousePressEvent( QMouseEvent * )
 
  527    emit cancelClicked();
 
  530void QgsTaskStatusWidget::mouseMoveEvent( QMouseEvent * )
 
  539void QgsTaskStatusWidget::leaveEvent( QEvent * )
 
  568QgsTaskManagerFloatingWidget::QgsTaskManagerFloatingWidget( 
QgsTaskManager *manager, QWidget *parent )
 
  571  setLayout( 
new QVBoxLayout() );
 
  574  const int minWidth = 
static_cast<int>( fontMetrics().horizontalAdvance( 
'X' ) * 60 * 
Qgis::UI_SCALE_FACTOR );
 
  576  setMinimumSize( minWidth, minHeight );
 
  577  layout()->addWidget( w );
 
  578  setStyleSheet( 
".QgsTaskManagerFloatingWidget { border-top-left-radius: 8px;" 
  579                 "border-top-right-radius: 8px; background-color: rgba(0, 0, 0, 70%); }" );
 
  583QgsTaskManagerStatusBarWidget::QgsTaskManagerStatusBarWidget( 
QgsTaskManager *manager, QWidget *parent )
 
  584  : QToolButton( parent )
 
  585  , mManager( manager )
 
  587  setAutoRaise( 
true );
 
  588  setSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding );
 
  589  setLayout( 
new QVBoxLayout() );
 
  591  mProgressBar = 
new QProgressBar();
 
  592  mProgressBar->setMinimum( 0 );
 
  593  mProgressBar->setMaximum( 0 );
 
  594  layout()->setContentsMargins( 5, 5, 5, 5 );
 
  595  layout()->addWidget( mProgressBar );
 
  597  mFloatingWidget = 
new QgsTaskManagerFloatingWidget( manager, parent ? parent->window() : nullptr );
 
  598  mFloatingWidget->setAnchorWidget( 
this );
 
  601  mFloatingWidget->hide();
 
  602  connect( 
this, &QgsTaskManagerStatusBarWidget::clicked, 
this, &QgsTaskManagerStatusBarWidget::toggleDisplay );
 
  614QSize QgsTaskManagerStatusBarWidget::sizeHint()
 const 
  616  const int width = 
static_cast<int>( fontMetrics().horizontalAdvance( 
'X' ) * 20 * 
Qgis::UI_SCALE_FACTOR );
 
  617  const int height = QToolButton::sizeHint().height();
 
  618  return QSize( width, height );
 
  621void QgsTaskManagerStatusBarWidget::changeEvent( QEvent *event )
 
  623  QToolButton::changeEvent( event );
 
  625  if ( event->type() == QEvent::FontChange )
 
  627    mProgressBar->setFont( font() );
 
  631void QgsTaskManagerStatusBarWidget::toggleDisplay()
 
  633  if ( mFloatingWidget->isVisible() )
 
  634    mFloatingWidget->hide();
 
  637    mFloatingWidget->show();
 
  638    mFloatingWidget->raise();
 
  642void QgsTaskManagerStatusBarWidget::overallProgressChanged( 
double progress )
 
  644  mProgressBar->setValue( 
static_cast<int>( progress ) );
 
  646    mProgressBar->setMaximum( 0 );
 
  647  else if ( mProgressBar->maximum() == 0 )
 
  648    mProgressBar->setMaximum( 100 );
 
  649  setToolTip( QgsTaskManagerModel::createTooltip( mManager->activeTasks().at( 0 ), QgsTaskManagerModel::ToolTipDescription ) );
 
  652void QgsTaskManagerStatusBarWidget::countActiveTasksChanged( 
int count )
 
  656    mProgressBar->setMaximum( 0 );
 
  657    setToolTip( tr( 
"%n active task(s) running", 
nullptr, count ) );
 
  661void QgsTaskManagerStatusBarWidget::allFinished()
 
  663  mFloatingWidget->hide();
 
  666  mProgressBar->setMaximum( 0 );
 
  667  mProgressBar->setValue( 0 );
 
  670void QgsTaskManagerStatusBarWidget::showButton()
 
  674    mProgressBar->setMaximum( 0 );
 
  675    mProgressBar->setValue( 0 );
 
static const double UI_SCALE_FACTOR
UI scaling factor.
 
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
 
Task manager for managing a set of long-running QgsTask tasks.
 
QList< QgsTask * > tasks() const
Returns all tasks tracked by the manager.
 
void finalTaskProgressChanged(double progress)
Will be emitted when only a single task remains to complete and that task has reported a progress cha...
 
void statusChanged(long taskId, int status)
Will be emitted when a task reports a status change.
 
void taskAdded(long taskId)
Emitted when a new task has been added to the manager.
 
long taskId(QgsTask *task) const
Returns the unique task ID corresponding to a task managed by the class.
 
void allTasksFinished()
Emitted when all tasks are complete.
 
void progressChanged(long taskId, double progress)
Will be emitted when a task reports a progress change.
 
int countActiveTasks(bool includeHidden=true) const
Returns the number of active (queued or running) tasks.
 
void triggerTask(QgsTask *task)
Triggers a task, e.g.
 
void countActiveTasksChanged(int count)
Emitted when the number of active tasks changes.
 
Abstract base class for long running background tasks.
 
TaskStatus status() const
Returns the current task status.
 
double progress() const
Returns the task's progress (between 0.0 and 100.0)
 
void progressChanged(double progress)
Will be emitted by task when its progress changes.
 
virtual void cancel()
Notifies the task that it should terminate.
 
void statusChanged(int status)
Will be emitted by task when its status changes.
 
qint64 elapsedTime() const
Returns the elapsed time since the task commenced, in milliseconds.
 
TaskStatus
Status of tasks.
 
@ Terminated
Task was terminated or errored.
 
@ Queued
Task is queued and has not begun.
 
@ OnHold
Task is queued but on hold and will not be started.
 
@ Running
Task is currently running.
 
@ Complete
Task successfully completed.
 
QString description() const
Returns the task's description.
 
bool canCancel() const
Returns true if the task can be canceled.
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)