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 );
90 progressBar->setMaximum( 100 );
91 progressBar->setValue(
static_cast< int >( std::round( progress ) ) );
94 progressBar->setMaximum( 0 );
97 mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Progress ), progressBar );
99 QgsTaskStatusWidget *statusWidget =
new QgsTaskStatusWidget(
nullptr, task->
status(), task->
canCancel() );
100 statusWidget->setAutoFillBackground(
true );
102 connect( statusWidget, &QgsTaskStatusWidget::cancelClicked, task, &
QgsTask::cancel );
103 mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Status ), statusWidget );
107void QgsTaskManagerWidget::clicked(
const QModelIndex &index )
109 QgsTask *task = mModel->indexToTask( index );
121QgsTaskManagerModel::QgsTaskManagerModel(
QgsTaskManager *manager, QObject *parent )
122 : QAbstractItemModel( parent )
123 , mManager( manager )
125 Q_ASSERT( mManager );
128 const auto constTasks = mManager->
tasks();
129 for (
QgsTask *task : constTasks )
131 mRowToTaskIdList << mManager->
taskId( task );
139QModelIndex QgsTaskManagerModel::index(
int row,
int column,
const QModelIndex &parent )
const
141 if ( column < 0 || column >= columnCount() )
144 return QModelIndex();
147 if ( !parent.isValid() && row >= 0 && row < mRowToTaskIdList.count() )
150 return createIndex( row, column );
154 return QModelIndex();
158QModelIndex QgsTaskManagerModel::parent(
const QModelIndex &index )
const
163 return QModelIndex();
166int QgsTaskManagerModel::rowCount(
const QModelIndex &parent )
const
168 if ( !parent.isValid() )
170 return mRowToTaskIdList.count();
179int QgsTaskManagerModel::columnCount(
const QModelIndex &parent )
const
185QVariant QgsTaskManagerModel::data(
const QModelIndex &index,
int role )
const
187 if ( !index.isValid() )
190 QgsTask *task = indexToTask( index );
195 case Qt::DisplayRole:
197 switch ( index.column() )
210 case static_cast< int >( CustomRole::Status ):
211 return static_cast<int>( task->status() );
213 case Qt::ToolTipRole:
214 switch ( index.column() )
217 return createTooltip( task, ToolTipDescription );
219 return createTooltip( task, ToolTipProgress );
221 return createTooltip( task, ToolTipStatus );
235Qt::ItemFlags QgsTaskManagerModel::flags(
const QModelIndex &index )
const
237 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
239 if ( ! index.isValid() )
244 QgsTask *task = indexToTask( index );
245 if ( index.column() == Status )
248 flags = flags | Qt::ItemIsEditable;
250 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
253bool QgsTaskManagerModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
257 if ( !index.isValid() )
260 QgsTask *task = indexToTask( index );
264 switch ( index.column() )
268 if ( value.toBool() && task->
canCancel() )
278void QgsTaskManagerModel::taskAdded(
long id )
280 beginInsertRows( QModelIndex(), mRowToTaskIdList.count(),
281 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 );
582 setStyleSheet(
".QgsTaskManagerFloatingWidget { border-top-left-radius: 8px;"
583 "border-top-right-radius: 8px; background-color: rgba(0, 0, 0, 70%); }" );
587QgsTaskManagerStatusBarWidget::QgsTaskManagerStatusBarWidget(
QgsTaskManager *manager, QWidget *parent )
588 : QToolButton( parent )
589 , mManager( manager )
591 setAutoRaise(
true );
592 setSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding );
593 setLayout(
new QVBoxLayout() );
595 mProgressBar =
new QProgressBar();
596 mProgressBar->setMinimum( 0 );
597 mProgressBar->setMaximum( 0 );
598 layout()->setContentsMargins( 5, 5, 5, 5 );
599 layout()->addWidget( mProgressBar );
601 mFloatingWidget =
new QgsTaskManagerFloatingWidget( manager, parent ? parent->window() : nullptr );
602 mFloatingWidget->setAnchorWidget(
this );
605 mFloatingWidget->hide();
606 connect(
this, &QgsTaskManagerStatusBarWidget::clicked,
this, &QgsTaskManagerStatusBarWidget::toggleDisplay );
618QSize QgsTaskManagerStatusBarWidget::sizeHint()
const
620 const int width =
static_cast< int >( fontMetrics().horizontalAdvance(
'X' ) * 20 *
Qgis::UI_SCALE_FACTOR );
621 const int height = QToolButton::sizeHint().height();
622 return QSize( width, height );
625void QgsTaskManagerStatusBarWidget::changeEvent( QEvent *event )
627 QToolButton::changeEvent( event );
629 if ( event->type() == QEvent::FontChange )
631 mProgressBar->setFont( font() );
635void QgsTaskManagerStatusBarWidget::toggleDisplay()
637 if ( mFloatingWidget->isVisible() )
638 mFloatingWidget->hide();
641 mFloatingWidget->show();
642 mFloatingWidget->raise();
646void QgsTaskManagerStatusBarWidget::overallProgressChanged(
double progress )
648 mProgressBar->setValue(
static_cast< int >( std::round( progress ) ) );
650 mProgressBar->setMaximum( 0 );
651 else if ( mProgressBar->maximum() == 0 )
652 mProgressBar->setMaximum( 100 );
653 setToolTip( QgsTaskManagerModel::createTooltip( mManager->activeTasks().at( 0 ), QgsTaskManagerModel::ToolTipDescription ) );
656void QgsTaskManagerStatusBarWidget::countActiveTasksChanged(
int count )
660 mProgressBar->setMaximum( 0 );
661 setToolTip( tr(
"%n active task(s) running",
nullptr, count ) );
665void QgsTaskManagerStatusBarWidget::allFinished()
667 mFloatingWidget->hide();
670 mProgressBar->setMaximum( 0 );
671 mProgressBar->setValue( 0 );
674void QgsTaskManagerStatusBarWidget::showButton()
678 mProgressBar->setMaximum( 0 );
679 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)