22 #include <QMouseEvent> 
   26 #include <QProgressBar> 
   28 #include <QHeaderView> 
   40   QVBoxLayout *vLayout = 
new QVBoxLayout();
 
   41   vLayout->setContentsMargins( 0, 0, 0, 0 );
 
   42   mTreeView = 
new QTreeView();
 
   43   mModel = 
new QgsTaskManagerModel( manager, 
this );
 
   44   mTreeView->setModel( mModel );
 
   45   connect( mModel, &QgsTaskManagerModel::rowsInserted, 
this, &QgsTaskManagerWidget::modelRowsInserted );
 
   46   mTreeView->setHeaderHidden( 
true );
 
   47   mTreeView->setRootIsDecorated( 
false );
 
   48   mTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
 
   50   int progressColWidth = 
static_cast< int >( fontMetrics().horizontalAdvance( 
'X' ) * 10 * 
Qgis::UI_SCALE_FACTOR );
 
   51   mTreeView->setColumnWidth( QgsTaskManagerModel::Progress, progressColWidth );
 
   53   int statusColWidth = 
static_cast< int >( fontMetrics().horizontalAdvance( 
'X' ) * 2 * 
Qgis::UI_SCALE_FACTOR );
 
   54   mTreeView->setColumnWidth( QgsTaskManagerModel::Status, statusColWidth );
 
   55   mTreeView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
 
   56   mTreeView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
 
   57   mTreeView->header()->setStretchLastSection( 
false );
 
   58   mTreeView->header()->setSectionResizeMode( QgsTaskManagerModel::Description, QHeaderView::Stretch );
 
   60   connect( mTreeView, &QTreeView::clicked, 
this, &QgsTaskManagerWidget::clicked );
 
   62   vLayout->addWidget( mTreeView );
 
   73 void QgsTaskManagerWidget::modelRowsInserted( 
const QModelIndex &, 
int start, 
int end )
 
   75   for ( 
int row = start; row <= end; ++row )
 
   77     QgsTask *task = mModel->indexToTask( mModel->index( row, 1 ) );
 
   81     QProgressBar *progressBar = 
new QProgressBar();
 
   82     progressBar->setAutoFillBackground( 
true );
 
   83     progressBar->setRange( 0, 0 );
 
   89         progressBar->setMaximum( 100 );
 
   90         progressBar->setValue( 
static_cast< int >( std::round( progress ) ) );
 
   93         progressBar->setMaximum( 0 );
 
   96     mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Progress ), progressBar );
 
   98     QgsTaskStatusWidget *statusWidget = 
new QgsTaskStatusWidget( 
nullptr, task->
status(), task->
canCancel() );
 
   99     statusWidget->setAutoFillBackground( 
true );
 
  101     connect( statusWidget, &QgsTaskStatusWidget::cancelClicked, task, &
QgsTask::cancel );
 
  102     mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Status ), statusWidget );
 
  106 void QgsTaskManagerWidget::clicked( 
const QModelIndex &index )
 
  108   QgsTask *task = mModel->indexToTask( index );
 
  120 QgsTaskManagerModel::QgsTaskManagerModel( 
QgsTaskManager *manager, QObject *parent )
 
  121   : QAbstractItemModel( parent )
 
  122   , mManager( manager )
 
  124   Q_ASSERT( mManager );
 
  127   const auto constTasks = mManager->
tasks();
 
  128   for ( 
QgsTask *task : constTasks )
 
  130     mRowToTaskIdList << mManager->
taskId( task );
 
  138 QModelIndex QgsTaskManagerModel::index( 
int row, 
int column, 
const QModelIndex &parent )
 const 
  140   if ( column < 0 || column >= columnCount() )
 
  143     return QModelIndex();
 
  146   if ( !parent.isValid() && row >= 0 && row < mRowToTaskIdList.count() )
 
  149     return createIndex( row, column );
 
  153   return QModelIndex();
 
  157 QModelIndex QgsTaskManagerModel::parent( 
const QModelIndex &index )
 const 
  162   return QModelIndex();
 
  165 int QgsTaskManagerModel::rowCount( 
const QModelIndex &parent )
 const 
  167   if ( !parent.isValid() )
 
  169     return mRowToTaskIdList.count();
 
  178 int QgsTaskManagerModel::columnCount( 
const QModelIndex &parent )
 const 
  184 QVariant QgsTaskManagerModel::data( 
const QModelIndex &index, 
int role )
 const 
  186   if ( !index.isValid() )
 
  189   QgsTask *task = indexToTask( index );
 
  194       case Qt::DisplayRole:
 
  196         switch ( index.column() )
 
  210         return static_cast<int>( task->
status() );
 
  212       case Qt::ToolTipRole:
 
  213         switch ( index.column() )
 
  216             return createTooltip( task, ToolTipDescription );
 
  218             return createTooltip( task, ToolTipProgress );
 
  220             return createTooltip( task, ToolTipStatus );
 
  234 Qt::ItemFlags QgsTaskManagerModel::flags( 
const QModelIndex &index )
 const 
  236   Qt::ItemFlags flags = QAbstractItemModel::flags( index );
 
  238   if ( ! index.isValid() )
 
  243   QgsTask *task = indexToTask( index );
 
  244   if ( index.column() == Status )
 
  247       flags = flags | Qt::ItemIsEditable;
 
  249   return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
 
  252 bool QgsTaskManagerModel::setData( 
const QModelIndex &index, 
const QVariant &value, 
int role )
 
  256   if ( !index.isValid() )
 
  259   QgsTask *task = indexToTask( index );
 
  263   switch ( index.column() )
 
  267       if ( value.toBool() && task->
canCancel() )
 
  277 void QgsTaskManagerModel::taskAdded( 
long id )
 
  279   beginInsertRows( QModelIndex(), mRowToTaskIdList.count(),
 
  280                    mRowToTaskIdList.count() );
 
  281   mRowToTaskIdList << id;
 
  285 void QgsTaskManagerModel::taskDeleted( 
long id )
 
  287   for ( 
int row = 0; row < mRowToTaskIdList.count(); ++row )
 
  289     if ( mRowToTaskIdList.at( row ) == 
id )
 
  291       beginRemoveRows( QModelIndex(), row, row );
 
  292       mRowToTaskIdList.removeAt( row );
 
  299 void QgsTaskManagerModel::progressChanged( 
long id, 
double progress )
 
  303   QModelIndex index = idToIndex( 
id, Progress );
 
  304   if ( !index.isValid() )
 
  309   emit dataChanged( index, index );
 
  312 void QgsTaskManagerModel::statusChanged( 
long id, 
int status )
 
  320     QModelIndex index = idToIndex( 
id, Status );
 
  321     if ( !index.isValid() )
 
  326     emit dataChanged( index, index );
 
  330 QgsTask *QgsTaskManagerModel::indexToTask( 
const QModelIndex &index )
 const 
  332   if ( !index.isValid() || index.parent().isValid() )
 
  335   long id = index.row() >= 0 && index.row() < mRowToTaskIdList.count() ? mRowToTaskIdList.at( index.row() ) : -1;
 
  337     return mManager->task( 
id );
 
  342 int QgsTaskManagerModel::idToRow( 
long id )
 const 
  344   for ( 
int row = 0; row < mRowToTaskIdList.count(); ++row )
 
  346     if ( mRowToTaskIdList.at( row ) == 
id )
 
  354 QModelIndex QgsTaskManagerModel::idToIndex( 
long id, 
int column )
 const 
  356   int row = idToRow( 
id );
 
  358     return QModelIndex();
 
  360   return index( row, column );
 
  363 QString QgsTaskManagerModel::createTooltip( 
QgsTask *task, ToolTipType type )
 
  369       case ToolTipDescription:
 
  373       case ToolTipProgress:
 
  378             return tr( 
"Queued" );
 
  380             return tr( 
"On hold" );
 
  383             if ( type == ToolTipStatus && !task->
canCancel() )
 
  384               return tr( 
"Running (cannot cancel)" );
 
  386               return tr( 
"Running" );
 
  389             return tr( 
"Complete" );
 
  391             return tr( 
"Terminated" );
 
  397   QString formattedTime;
 
  404     qint64 msRemain = 
static_cast< qint64 
>( elapsed * 100.0 / task->
progress() - elapsed );
 
  405     if ( msRemain > 120 * 1000 )
 
  407       long long minutes = msRemain / 1000 / 60;
 
  408       int seconds = ( msRemain / 1000 ) % 60;
 
  409       formattedTime = tr( 
"%1:%2 minutes" ).arg( minutes ).arg( seconds, 2, 10, QChar( 
'0' ) );
 
  412       formattedTime = tr( 
"%1 seconds" ).arg( msRemain / 1000 );
 
  414     formattedTime = tr( 
"Estimated time remaining: %1" ).arg( formattedTime );
 
  416     QTime estimatedEnd = QTime::currentTime().addMSecs( msRemain );
 
  417     formattedTime += tr( 
" (%1)" ).arg( QLocale::system().toString( estimatedEnd, QLocale::ShortFormat ) );
 
  421     if ( elapsed > 120 * 1000 )
 
  423       long long minutes = elapsed / 1000 / 60;
 
  424       int seconds = ( elapsed / 1000 ) % 60;
 
  425       formattedTime = tr( 
"%1:%2 minutes" ).arg( minutes ).arg( seconds, 2, 10, QChar( 
'0' ) );
 
  428       formattedTime = tr( 
"%1 seconds" ).arg( elapsed / 1000 );
 
  430     formattedTime = tr( 
"Time elapsed: %1" ).arg( formattedTime );
 
  435     case ToolTipDescription:
 
  436       return tr( 
"%1<br>%2" ).arg( task->
description(), formattedTime );
 
  439     case ToolTipProgress:
 
  444           return tr( 
"Queued" );
 
  446           return tr( 
"On hold" );
 
  450           if ( type == ToolTipStatus && !task->
canCancel() )
 
  451             statusDesc = tr( 
"Running (cannot cancel)" );
 
  453             statusDesc = tr( 
"Running" );
 
  454           return tr( 
"%1<br>%2" ).arg( statusDesc, formattedTime );
 
  457           return tr( 
"Complete" );
 
  459           return tr( 
"Terminated" );
 
  472 QgsTaskStatusWidget::QgsTaskStatusWidget( QWidget *parent, 
QgsTask::TaskStatus status, 
bool canCancel )
 
  474   , mCanCancel( canCancel )
 
  477   setMouseTracking( 
true );
 
  480 QSize QgsTaskStatusWidget::sizeHint()
 const 
  482   return QSize( 32, 32 );
 
  485 void QgsTaskStatusWidget::setStatus( 
int status )
 
  491 void QgsTaskStatusWidget::paintEvent( QPaintEvent *e )
 
  493   QWidget::paintEvent( e );
 
  523   icon.paint( &p, 1, height() / 2 - 12, 24, 24 );
 
  527 void QgsTaskStatusWidget::mousePressEvent( QMouseEvent * )
 
  530     emit cancelClicked();
 
  533 void QgsTaskStatusWidget::mouseMoveEvent( QMouseEvent * )
 
  542 void QgsTaskStatusWidget::leaveEvent( QEvent * )
 
  571 QgsTaskManagerFloatingWidget::QgsTaskManagerFloatingWidget( 
QgsTaskManager *manager, QWidget *parent )
 
  574   setLayout( 
new QVBoxLayout() );
 
  577   int minWidth = 
static_cast< int >( fontMetrics().horizontalAdvance( 
'X' ) * 60 * 
Qgis::UI_SCALE_FACTOR );
 
  579   setMinimumSize( minWidth, minHeight );
 
  580   layout()->addWidget( w );
 
  581   setStyleSheet( 
".QgsTaskManagerFloatingWidget { border-top-left-radius: 8px;" 
  582                  "border-top-right-radius: 8px; background-color: rgba(0, 0, 0, 70%); }" );
 
  586 QgsTaskManagerStatusBarWidget::QgsTaskManagerStatusBarWidget( 
QgsTaskManager *manager, QWidget *parent )
 
  587   : QToolButton( parent )
 
  588   , mManager( manager )
 
  590   setAutoRaise( 
true );
 
  591   setSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding );
 
  592   setLayout( 
new QVBoxLayout() );
 
  594   mProgressBar = 
new QProgressBar();
 
  595   mProgressBar->setMinimum( 0 );
 
  596   mProgressBar->setMaximum( 0 );
 
  597   layout()->setContentsMargins( 5, 5, 5, 5 );
 
  598   layout()->addWidget( mProgressBar );
 
  600   mFloatingWidget = 
new QgsTaskManagerFloatingWidget( manager, parent ? parent->window() : 
nullptr );
 
  601   mFloatingWidget->setAnchorWidget( 
this );
 
  604   mFloatingWidget->hide();
 
  605   connect( 
this, &QgsTaskManagerStatusBarWidget::clicked, 
this, &QgsTaskManagerStatusBarWidget::toggleDisplay );
 
  617 QSize QgsTaskManagerStatusBarWidget::sizeHint()
 const 
  619   int width = 
static_cast< int >( fontMetrics().horizontalAdvance( 
'X' ) * 20 * 
Qgis::UI_SCALE_FACTOR );
 
  620   int height = QToolButton::sizeHint().height();
 
  621   return QSize( width, height );
 
  624 void QgsTaskManagerStatusBarWidget::changeEvent( QEvent *event )
 
  626   QToolButton::changeEvent( event );
 
  628   if ( event->type() == QEvent::FontChange )
 
  630     mProgressBar->setFont( font() );
 
  634 void QgsTaskManagerStatusBarWidget::toggleDisplay()
 
  636   if ( mFloatingWidget->isVisible() )
 
  637     mFloatingWidget->hide();
 
  640     mFloatingWidget->show();
 
  641     mFloatingWidget->raise();
 
  645 void QgsTaskManagerStatusBarWidget::overallProgressChanged( 
double progress )
 
  647   mProgressBar->setValue( 
static_cast< int >( std::round( progress ) ) );
 
  649     mProgressBar->setMaximum( 0 );
 
  650   else if ( mProgressBar->maximum() == 0 )
 
  651     mProgressBar->setMaximum( 100 );
 
  652   setToolTip( QgsTaskManagerModel::createTooltip( mManager->activeTasks().at( 0 ), QgsTaskManagerModel::ToolTipDescription ) );
 
  655 void QgsTaskManagerStatusBarWidget::countActiveTasksChanged( 
int count )
 
  659     mProgressBar->setMaximum( 0 );
 
  660     setToolTip( tr( 
"%1 active tasks running" ).arg( count ) );
 
  664 void QgsTaskManagerStatusBarWidget::allFinished()
 
  666   mFloatingWidget->hide();
 
  669   mProgressBar->setMaximum( 0 );
 
  670   mProgressBar->setValue( 0 );
 
  673 void QgsTaskManagerStatusBarWidget::showButton()
 
  677     mProgressBar->setMaximum( 0 );
 
  678     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.
int countActiveTasks() const
Returns the number of active (queued or running) tasks.
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.
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)