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 );
80void QgsTaskManagerWidget::modelRowsInserted(
const QModelIndex &,
int start,
int end )
82 for (
int row = start; row <= end; ++row )
84 QgsTask *task = mModel->indexToTask( mModel->index( row, 1 ) );
88 QProgressBar *progressBar =
new QProgressBar();
89 progressBar->setAutoFillBackground(
true );
90 progressBar->setRange( 0, 0 );
95 progressBar->setMaximum( 100 );
96 progressBar->setValue(
static_cast<int>( progress ) );
99 progressBar->setMaximum( 0 );
101 mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Progress ), progressBar );
103 QgsTaskStatusWidget *statusWidget =
new QgsTaskStatusWidget(
nullptr, task->
status(), task->
canCancel() );
104 statusWidget->setAutoFillBackground(
true );
106 connect( statusWidget, &QgsTaskStatusWidget::cancelClicked, task, &
QgsTask::cancel );
107 mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Status ), statusWidget );
111void QgsTaskManagerWidget::clicked(
const QModelIndex &index )
113 QgsTask *task = mModel->indexToTask( index );
117 mManager->triggerTask( task );
125QgsTaskManagerModel::QgsTaskManagerModel(
QgsTaskManager *manager, QObject *parent )
126 : QAbstractItemModel( parent )
127 , mManager( manager )
129 Q_ASSERT( mManager );
132 const auto constTasks = mManager->tasks();
133 for ( QgsTask *task : constTasks )
135 mRowToTaskIdList << mManager->taskId( task );
143QModelIndex QgsTaskManagerModel::index(
int row,
int column,
const QModelIndex &parent )
const
145 if ( column < 0 || column >= columnCount() )
148 return QModelIndex();
151 if ( !parent.isValid() && row >= 0 && row < mRowToTaskIdList.count() )
154 return createIndex( row, column );
158 return QModelIndex();
161QModelIndex QgsTaskManagerModel::parent(
const QModelIndex &index )
const
166 return QModelIndex();
169int QgsTaskManagerModel::rowCount(
const QModelIndex &parent )
const
171 if ( !parent.isValid() )
173 return mRowToTaskIdList.count();
182int QgsTaskManagerModel::columnCount(
const QModelIndex &parent )
const
188QVariant QgsTaskManagerModel::data(
const QModelIndex &index,
int role )
const
190 if ( !index.isValid() )
193 QgsTask *task = indexToTask( index );
198 case Qt::DisplayRole:
200 switch ( index.column() )
213 case static_cast<int>( CustomRole::Status ):
214 return static_cast<int>( task->status() );
216 case Qt::ToolTipRole:
217 switch ( index.column() )
220 return createTooltip( task, ToolTipDescription );
222 return createTooltip( task, ToolTipProgress );
224 return createTooltip( task, ToolTipStatus );
238Qt::ItemFlags QgsTaskManagerModel::flags(
const QModelIndex &index )
const
240 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
242 if ( !index.isValid() )
247 QgsTask *task = indexToTask( index );
248 if ( index.column() == Status )
251 flags = flags | Qt::ItemIsEditable;
253 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
256bool QgsTaskManagerModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
260 if ( !index.isValid() )
263 QgsTask *task = indexToTask( index );
267 switch ( index.column() )
271 if ( value.toBool() && task->
canCancel() )
281void QgsTaskManagerModel::taskAdded(
long id )
283 beginInsertRows( QModelIndex(), mRowToTaskIdList.count(), mRowToTaskIdList.count() );
284 mRowToTaskIdList << id;
288void QgsTaskManagerModel::taskDeleted(
long id )
290 for (
int row = 0; row < mRowToTaskIdList.count(); ++row )
292 if ( mRowToTaskIdList.at( row ) ==
id )
294 beginRemoveRows( QModelIndex(), row, row );
295 mRowToTaskIdList.removeAt( row );
302void QgsTaskManagerModel::progressChanged(
long id,
double progress )
306 const QModelIndex index = idToIndex(
id, Progress );
307 if ( !index.isValid() )
312 emit dataChanged( index, index );
315void QgsTaskManagerModel::statusChanged(
long id,
int status )
323 const QModelIndex index = idToIndex(
id, Status );
324 if ( !index.isValid() )
329 emit dataChanged( index, index );
333QgsTask *QgsTaskManagerModel::indexToTask(
const QModelIndex &index )
const
335 if ( !index.isValid() || index.parent().isValid() )
338 const long id = index.row() >= 0 && index.row() < mRowToTaskIdList.count() ? mRowToTaskIdList.at( index.row() ) : -1;
340 return mManager->task(
id );
345int QgsTaskManagerModel::idToRow(
long id )
const
347 for (
int row = 0; row < mRowToTaskIdList.count(); ++row )
349 if ( mRowToTaskIdList.at( row ) ==
id )
357QModelIndex QgsTaskManagerModel::idToIndex(
long id,
int column )
const
359 const int row = idToRow(
id );
361 return QModelIndex();
363 return index( row, column );
366QString QgsTaskManagerModel::createTooltip(
QgsTask *task, ToolTipType type )
372 case ToolTipDescription:
376 case ToolTipProgress:
381 return tr(
"Queued" );
383 return tr(
"On hold" );
386 if ( type == ToolTipStatus && !task->
canCancel() )
387 return tr(
"Running (cannot cancel)" );
389 return tr(
"Running" );
392 return tr(
"Complete" );
394 return tr(
"Terminated" );
400 QString formattedTime;
407 const qint64 msRemain =
static_cast<qint64
>( elapsed * 100.0 / task->
progress() - elapsed );
408 if ( msRemain > 120 * 1000 )
410 const long long minutes = msRemain / 1000 / 60;
411 const int seconds = ( msRemain / 1000 ) % 60;
412 formattedTime = tr(
"%1:%2 minutes" ).arg( minutes ).arg( seconds, 2, 10, QChar(
'0' ) );
415 formattedTime = tr(
"%1 seconds" ).arg( msRemain / 1000 );
417 formattedTime = tr(
"Estimated time remaining: %1" ).arg( formattedTime );
419 const QTime estimatedEnd = QTime::currentTime().addMSecs( msRemain );
420 formattedTime += tr(
" (%1)" ).arg( QLocale::system().toString( estimatedEnd, QLocale::ShortFormat ) );
424 if ( elapsed > 120 * 1000 )
426 const long long minutes = elapsed / 1000 / 60;
427 const int seconds = ( elapsed / 1000 ) % 60;
428 formattedTime = tr(
"%1:%2 minutes" ).arg( minutes ).arg( seconds, 2, 10, QChar(
'0' ) );
431 formattedTime = tr(
"%1 seconds" ).arg( elapsed / 1000 );
433 formattedTime = tr(
"Time elapsed: %1" ).arg( formattedTime );
438 case ToolTipDescription:
439 return tr(
"%1<br>%2" ).arg( task->
description(), formattedTime );
442 case ToolTipProgress:
447 return tr(
"Queued" );
449 return tr(
"On hold" );
453 if ( type == ToolTipStatus && !task->
canCancel() )
454 statusDesc = tr(
"Running (cannot cancel)" );
456 statusDesc = tr(
"Running" );
457 return tr(
"%1<br>%2" ).arg( statusDesc, formattedTime );
460 return tr(
"Complete" );
462 return tr(
"Terminated" );
475QgsTaskStatusWidget::QgsTaskStatusWidget( QWidget *parent,
QgsTask::TaskStatus status,
bool canCancel )
477 , mCanCancel( canCancel )
480 setMouseTracking(
true );
483QSize QgsTaskStatusWidget::sizeHint()
const
485 return QSize( 32, 32 );
488void QgsTaskStatusWidget::setStatus(
int status )
494void QgsTaskStatusWidget::paintEvent( QPaintEvent *e )
496 QWidget::paintEvent( e );
526 icon.paint( &p, 1, height() / 2 - 12, 24, 24 );
530void QgsTaskStatusWidget::mousePressEvent( QMouseEvent * )
533 emit cancelClicked();
536void QgsTaskStatusWidget::mouseMoveEvent( QMouseEvent * )
545void QgsTaskStatusWidget::leaveEvent( QEvent * )
574QgsTaskManagerFloatingWidget::QgsTaskManagerFloatingWidget(
QgsTaskManager *manager, QWidget *parent )
577 setLayout(
new QVBoxLayout() );
580 const int minWidth =
static_cast<int>( fontMetrics().horizontalAdvance(
'X' ) * 60 *
Qgis::UI_SCALE_FACTOR );
582 setMinimumSize( minWidth, minHeight );
583 layout()->addWidget( w );
584 setStyleSheet(
".QgsTaskManagerFloatingWidget { border-top-left-radius: 8px;"
585 "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).