26#include <QProgressBar>
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 const int progressColWidth =
static_cast< int >( fontMetrics().horizontalAdvance(
'X' ) * 10 *
Qgis::UI_SCALE_FACTOR );
51 mTreeView->setColumnWidth( QgsTaskManagerModel::Progress, progressColWidth );
53 const 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 );
73void 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 );
106void QgsTaskManagerWidget::clicked(
const QModelIndex &index )
108 QgsTask *task = mModel->indexToTask( index );
120QgsTaskManagerModel::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 );
138QModelIndex 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();
157QModelIndex QgsTaskManagerModel::parent(
const QModelIndex &index )
const
162 return QModelIndex();
165int QgsTaskManagerModel::rowCount(
const QModelIndex &parent )
const
167 if ( !parent.isValid() )
169 return mRowToTaskIdList.count();
178int QgsTaskManagerModel::columnCount(
const QModelIndex &parent )
const
184QVariant 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 );
234Qt::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;
252bool 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() )
277void QgsTaskManagerModel::taskAdded(
long id )
279 beginInsertRows( QModelIndex(), mRowToTaskIdList.count(),
280 mRowToTaskIdList.count() );
281 mRowToTaskIdList << id;
285void 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 );
299void QgsTaskManagerModel::progressChanged(
long id,
double progress )
303 const QModelIndex index = idToIndex(
id, Progress );
304 if ( !index.isValid() )
309 emit dataChanged( index, index );
312void QgsTaskManagerModel::statusChanged(
long id,
int status )
320 const QModelIndex index = idToIndex(
id, Status );
321 if ( !index.isValid() )
326 emit dataChanged( index, index );
330QgsTask *QgsTaskManagerModel::indexToTask(
const QModelIndex &index )
const
332 if ( !index.isValid() || index.parent().isValid() )
335 const long id = index.row() >= 0 && index.row() < mRowToTaskIdList.count() ? mRowToTaskIdList.at( index.row() ) : -1;
337 return mManager->task(
id );
342int QgsTaskManagerModel::idToRow(
long id )
const
344 for (
int row = 0; row < mRowToTaskIdList.count(); ++row )
346 if ( mRowToTaskIdList.at( row ) ==
id )
354QModelIndex QgsTaskManagerModel::idToIndex(
long id,
int column )
const
356 const int row = idToRow(
id );
358 return QModelIndex();
360 return index( row, column );
363QString 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 const qint64 msRemain =
static_cast< qint64
>( elapsed * 100.0 / task->
progress() - elapsed );
405 if ( msRemain > 120 * 1000 )
407 const long long minutes = msRemain / 1000 / 60;
408 const 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 const QTime estimatedEnd = QTime::currentTime().addMSecs( msRemain );
417 formattedTime += tr(
" (%1)" ).arg( QLocale::system().toString( estimatedEnd, QLocale::ShortFormat ) );
421 if ( elapsed > 120 * 1000 )
423 const long long minutes = elapsed / 1000 / 60;
424 const 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" );
472QgsTaskStatusWidget::QgsTaskStatusWidget( QWidget *parent,
QgsTask::TaskStatus status,
bool canCancel )
474 , mCanCancel( canCancel )
477 setMouseTracking(
true );
480QSize QgsTaskStatusWidget::sizeHint()
const
482 return QSize( 32, 32 );
485void QgsTaskStatusWidget::setStatus(
int status )
491void QgsTaskStatusWidget::paintEvent( QPaintEvent *e )
493 QWidget::paintEvent( e );
523 icon.paint( &p, 1, height() / 2 - 12, 24, 24 );
527void QgsTaskStatusWidget::mousePressEvent( QMouseEvent * )
530 emit cancelClicked();
533void QgsTaskStatusWidget::mouseMoveEvent( QMouseEvent * )
542void QgsTaskStatusWidget::leaveEvent( QEvent * )
571QgsTaskManagerFloatingWidget::QgsTaskManagerFloatingWidget(
QgsTaskManager *manager, QWidget *parent )
574 setLayout(
new QVBoxLayout() );
577 const 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%); }" );
586QgsTaskManagerStatusBarWidget::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 );
617QSize QgsTaskManagerStatusBarWidget::sizeHint()
const
619 const int width =
static_cast< int >( fontMetrics().horizontalAdvance(
'X' ) * 20 *
Qgis::UI_SCALE_FACTOR );
620 const int height = QToolButton::sizeHint().height();
621 return QSize( width, height );
624void QgsTaskManagerStatusBarWidget::changeEvent( QEvent *event )
626 QToolButton::changeEvent( event );
628 if ( event->type() == QEvent::FontChange )
630 mProgressBar->setFont( font() );
634void QgsTaskManagerStatusBarWidget::toggleDisplay()
636 if ( mFloatingWidget->isVisible() )
637 mFloatingWidget->hide();
640 mFloatingWidget->show();
641 mFloatingWidget->raise();
645void 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 ) );
655void QgsTaskManagerStatusBarWidget::countActiveTasksChanged(
int count )
659 mProgressBar->setMaximum( 0 );
660 setToolTip( tr(
"%n active task(s) running",
nullptr, count ) );
664void QgsTaskManagerStatusBarWidget::allFinished()
666 mFloatingWidget->hide();
669 mProgressBar->setMaximum( 0 );
670 mProgressBar->setValue( 0 );
673void 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.
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)