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>( std::round( 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>( std::round( 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)