28#include <QProgressBar>
33#include "moc_qgstaskmanagerwidget.cpp"
35using namespace Qt::StringLiterals;
47 QVBoxLayout *vLayout =
new QVBoxLayout();
48 vLayout->setContentsMargins( 0, 0, 0, 0 );
49 mTreeView =
new QTreeView();
50 mModel =
new QgsTaskManagerModel( manager,
this );
51 mTreeView->setModel( mModel );
52 connect( mModel, &QgsTaskManagerModel::rowsInserted,
this, &QgsTaskManagerWidget::modelRowsInserted );
53 mTreeView->setHeaderHidden(
true );
54 mTreeView->setRootIsDecorated(
false );
55 mTreeView->setSelectionBehavior( QAbstractItemView::SelectRows );
57 const int progressColWidth =
static_cast<int>( fontMetrics().horizontalAdvance(
'X' ) * 10 *
Qgis::UI_SCALE_FACTOR );
58 mTreeView->setColumnWidth( QgsTaskManagerModel::Progress, progressColWidth );
60 const int statusColWidth =
static_cast<int>( fontMetrics().horizontalAdvance(
'X' ) * 2 *
Qgis::UI_SCALE_FACTOR );
61 mTreeView->setColumnWidth( QgsTaskManagerModel::Status, statusColWidth );
62 mTreeView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
63 mTreeView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
64 mTreeView->header()->setStretchLastSection(
false );
65 mTreeView->header()->setSectionResizeMode( QgsTaskManagerModel::Description, QHeaderView::Stretch );
67 connect( mTreeView, &QTreeView::clicked,
this, &QgsTaskManagerWidget::clicked );
69 vLayout->addWidget( mTreeView );
78void QgsTaskManagerWidget::modelRowsInserted(
const QModelIndex &,
int start,
int end )
80 for (
int row = start; row <= end; ++row )
82 QgsTask *task = mModel->indexToTask( mModel->index( row, 1 ) );
86 QProgressBar *progressBar =
new QProgressBar();
87 progressBar->setAutoFillBackground(
true );
88 progressBar->setRange( 0, 0 );
93 progressBar->setMaximum( 100 );
94 progressBar->setValue(
static_cast<int>( progress ) );
97 progressBar->setMaximum( 0 );
99 mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Progress ), progressBar );
101 QgsTaskStatusWidget *statusWidget =
new QgsTaskStatusWidget(
nullptr, task->
status(), task->
canCancel() );
102 statusWidget->setAutoFillBackground(
true );
104 connect( statusWidget, &QgsTaskStatusWidget::cancelClicked, task, &
QgsTask::cancel );
105 mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Status ), statusWidget );
109void QgsTaskManagerWidget::clicked(
const QModelIndex &index )
111 QgsTask *task = mModel->indexToTask( index );
115 mManager->triggerTask( task );
123QgsTaskManagerModel::QgsTaskManagerModel(
QgsTaskManager *manager, QObject *parent )
124 : QAbstractItemModel( parent )
125 , mManager( manager )
127 Q_ASSERT( mManager );
130 const auto constTasks = mManager->tasks();
131 for ( QgsTask *task : constTasks )
133 mRowToTaskIdList << mManager->taskId( task );
141QModelIndex QgsTaskManagerModel::index(
int row,
int column,
const QModelIndex &parent )
const
143 if ( column < 0 || column >= columnCount() )
146 return QModelIndex();
149 if ( !parent.isValid() && row >= 0 && row < mRowToTaskIdList.count() )
152 return createIndex( row, column );
156 return QModelIndex();
159QModelIndex QgsTaskManagerModel::parent(
const QModelIndex &index )
const
164 return QModelIndex();
167int QgsTaskManagerModel::rowCount(
const QModelIndex &parent )
const
169 if ( !parent.isValid() )
171 return mRowToTaskIdList.count();
180int QgsTaskManagerModel::columnCount(
const QModelIndex &parent )
const
186QVariant QgsTaskManagerModel::data(
const QModelIndex &index,
int role )
const
188 if ( !index.isValid() )
191 QgsTask *task = indexToTask( index );
196 case Qt::DisplayRole:
198 switch ( index.column() )
211 case static_cast<int>( CustomRole::Status ):
212 return static_cast<int>( task->status() );
214 case Qt::ToolTipRole:
215 switch ( index.column() )
218 return createTooltip( task, ToolTipDescription );
220 return createTooltip( task, ToolTipProgress );
222 return createTooltip( task, ToolTipStatus );
236Qt::ItemFlags QgsTaskManagerModel::flags(
const QModelIndex &index )
const
238 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
240 if ( !index.isValid() )
245 QgsTask *task = indexToTask( index );
246 if ( index.column() == Status )
249 flags = flags | Qt::ItemIsEditable;
251 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
254bool QgsTaskManagerModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
258 if ( !index.isValid() )
261 QgsTask *task = indexToTask( index );
265 switch ( index.column() )
269 if ( value.toBool() && task->
canCancel() )
279void QgsTaskManagerModel::taskAdded(
long id )
281 beginInsertRows( QModelIndex(), mRowToTaskIdList.count(), mRowToTaskIdList.count() );
282 mRowToTaskIdList << id;
286void QgsTaskManagerModel::taskDeleted(
long id )
288 for (
int row = 0; row < mRowToTaskIdList.count(); ++row )
290 if ( mRowToTaskIdList.at( row ) ==
id )
292 beginRemoveRows( QModelIndex(), row, row );
293 mRowToTaskIdList.removeAt( row );
300void QgsTaskManagerModel::progressChanged(
long id,
double progress )
304 const QModelIndex index = idToIndex(
id, Progress );
305 if ( !index.isValid() )
310 emit dataChanged( index, index );
313void QgsTaskManagerModel::statusChanged(
long id,
int status )
321 const QModelIndex index = idToIndex(
id, Status );
322 if ( !index.isValid() )
327 emit dataChanged( index, index );
331QgsTask *QgsTaskManagerModel::indexToTask(
const QModelIndex &index )
const
333 if ( !index.isValid() || index.parent().isValid() )
336 const long id = index.row() >= 0 && index.row() < mRowToTaskIdList.count() ? mRowToTaskIdList.at( index.row() ) : -1;
338 return mManager->task(
id );
343int QgsTaskManagerModel::idToRow(
long id )
const
345 for (
int row = 0; row < mRowToTaskIdList.count(); ++row )
347 if ( mRowToTaskIdList.at( row ) ==
id )
355QModelIndex QgsTaskManagerModel::idToIndex(
long id,
int column )
const
357 const int row = idToRow(
id );
359 return QModelIndex();
361 return index( row, column );
364QString QgsTaskManagerModel::createTooltip(
QgsTask *task, ToolTipType type )
370 case ToolTipDescription:
374 case ToolTipProgress:
379 return tr(
"Queued" );
381 return tr(
"On hold" );
384 if ( type == ToolTipStatus && !task->
canCancel() )
385 return tr(
"Running (cannot cancel)" );
387 return tr(
"Running" );
390 return tr(
"Complete" );
392 return tr(
"Terminated" );
398 QString formattedTime;
405 const qint64 msRemain =
static_cast<qint64
>( elapsed * 100.0 / task->
progress() - elapsed );
406 if ( msRemain > 120 * 1000 )
408 const long long minutes = msRemain / 1000 / 60;
409 const int seconds = ( msRemain / 1000 ) % 60;
410 formattedTime = tr(
"%1:%2 minutes" ).arg( minutes ).arg( seconds, 2, 10, QChar(
'0' ) );
413 formattedTime = tr(
"%1 seconds" ).arg( msRemain / 1000 );
415 formattedTime = tr(
"Estimated time remaining: %1" ).arg( formattedTime );
417 const QTime estimatedEnd = QTime::currentTime().addMSecs( msRemain );
418 formattedTime += tr(
" (%1)" ).arg( QLocale::system().toString( estimatedEnd, QLocale::ShortFormat ) );
422 if ( elapsed > 120 * 1000 )
424 const long long minutes = elapsed / 1000 / 60;
425 const int seconds = ( elapsed / 1000 ) % 60;
426 formattedTime = tr(
"%1:%2 minutes" ).arg( minutes ).arg( seconds, 2, 10, QChar(
'0' ) );
429 formattedTime = tr(
"%1 seconds" ).arg( elapsed / 1000 );
431 formattedTime = tr(
"Time elapsed: %1" ).arg( formattedTime );
436 case ToolTipDescription:
437 return tr(
"%1<br>%2" ).arg( task->
description(), formattedTime );
440 case ToolTipProgress:
445 return tr(
"Queued" );
447 return tr(
"On hold" );
451 if ( type == ToolTipStatus && !task->
canCancel() )
452 statusDesc = tr(
"Running (cannot cancel)" );
454 statusDesc = tr(
"Running" );
455 return tr(
"%1<br>%2" ).arg( statusDesc, formattedTime );
458 return tr(
"Complete" );
460 return tr(
"Terminated" );
473QgsTaskStatusWidget::QgsTaskStatusWidget( QWidget *parent,
QgsTask::TaskStatus status,
bool canCancel )
475 , mCanCancel( canCancel )
478 setMouseTracking(
true );
481QSize QgsTaskStatusWidget::sizeHint()
const
483 return QSize( 32, 32 );
486void QgsTaskStatusWidget::setStatus(
int status )
492void QgsTaskStatusWidget::paintEvent( QPaintEvent *e )
494 QWidget::paintEvent( e );
524 icon.paint( &p, 1, height() / 2 - 12, 24, 24 );
528void QgsTaskStatusWidget::mousePressEvent( QMouseEvent * )
531 emit cancelClicked();
534void QgsTaskStatusWidget::mouseMoveEvent( QMouseEvent * )
543void QgsTaskStatusWidget::leaveEvent( QEvent * )
572QgsTaskManagerFloatingWidget::QgsTaskManagerFloatingWidget(
QgsTaskManager *manager, QWidget *parent )
575 setLayout(
new QVBoxLayout() );
578 const int minWidth =
static_cast<int>( fontMetrics().horizontalAdvance(
'X' ) * 60 *
Qgis::UI_SCALE_FACTOR );
580 setMinimumSize( minWidth, minHeight );
581 layout()->addWidget( w );
583 ".QgsTaskManagerFloatingWidget { border-top-left-radius: 8px;"
584 "border-top-right-radius: 8px; background-color: rgba(0, 0, 0, 70%); }"
589QgsTaskManagerStatusBarWidget::QgsTaskManagerStatusBarWidget(
QgsTaskManager *manager, QWidget *parent )
590 : QToolButton( parent )
591 , mManager( manager )
593 setAutoRaise(
true );
594 setSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding );
595 setLayout(
new QVBoxLayout() );
597 mProgressBar =
new QProgressBar();
598 mProgressBar->setMinimum( 0 );
599 mProgressBar->setMaximum( 0 );
600 layout()->setContentsMargins( 5, 5, 5, 5 );
601 layout()->addWidget( mProgressBar );
603 mFloatingWidget =
new QgsTaskManagerFloatingWidget( manager, parent ? parent->window() :
nullptr );
604 mFloatingWidget->setAnchorWidget(
this );
607 mFloatingWidget->hide();
608 connect(
this, &QgsTaskManagerStatusBarWidget::clicked,
this, &QgsTaskManagerStatusBarWidget::toggleDisplay );
620QSize QgsTaskManagerStatusBarWidget::sizeHint()
const
622 const int width =
static_cast<int>( fontMetrics().horizontalAdvance(
'X' ) * 20 *
Qgis::UI_SCALE_FACTOR );
623 const int height = QToolButton::sizeHint().height();
624 return QSize( width, height );
627void QgsTaskManagerStatusBarWidget::changeEvent( QEvent *event )
629 QToolButton::changeEvent( event );
631 if ( event->type() == QEvent::FontChange )
633 mProgressBar->setFont( font() );
637void QgsTaskManagerStatusBarWidget::toggleDisplay()
639 if ( mFloatingWidget->isVisible() )
640 mFloatingWidget->hide();
643 mFloatingWidget->show();
644 mFloatingWidget->raise();
648void QgsTaskManagerStatusBarWidget::overallProgressChanged(
double progress )
650 mProgressBar->setValue(
static_cast<int>( progress ) );
652 mProgressBar->setMaximum( 0 );
653 else if ( mProgressBar->maximum() == 0 )
654 mProgressBar->setMaximum( 100 );
655 setToolTip( QgsTaskManagerModel::createTooltip( mManager->activeTasks().at( 0 ), QgsTaskManagerModel::ToolTipDescription ) );
658void QgsTaskManagerStatusBarWidget::countActiveTasksChanged(
int count )
662 mProgressBar->setMaximum( 0 );
663 setToolTip( tr(
"%n active task(s) running",
nullptr, count ) );
667void QgsTaskManagerStatusBarWidget::allFinished()
669 mFloatingWidget->hide();
672 mProgressBar->setMaximum( 0 );
673 mProgressBar->setValue( 0 );
676void QgsTaskManagerStatusBarWidget::showButton()
680 mProgressBar->setMaximum( 0 );
681 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.
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.
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 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).