22 #include <QMouseEvent>
26 #include <QProgressBar>
28 #include <QHeaderView>
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 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
53 int progressColWidth =
static_cast< int >( fontMetrics().horizontalAdvance(
'X' ) * 10 *
Qgis::UI_SCALE_FACTOR );
55 mTreeView->setColumnWidth( QgsTaskManagerModel::Progress, progressColWidth );
57 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
60 int statusColWidth =
static_cast< int >( fontMetrics().horizontalAdvance(
'X' ) * 2 *
Qgis::UI_SCALE_FACTOR );
62 mTreeView->setColumnWidth( QgsTaskManagerModel::Status, statusColWidth );
63 mTreeView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
64 mTreeView->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
65 mTreeView->header()->setStretchLastSection(
false );
66 mTreeView->header()->setSectionResizeMode( QgsTaskManagerModel::Description, QHeaderView::Stretch );
68 connect( mTreeView, &QTreeView::clicked,
this, &QgsTaskManagerWidget::clicked );
70 vLayout->addWidget( mTreeView );
81 void QgsTaskManagerWidget::modelRowsInserted(
const QModelIndex &,
int start,
int end )
83 for (
int row = start; row <= end; ++row )
85 QgsTask *task = mModel->indexToTask( mModel->index( row, 1 ) );
89 QProgressBar *progressBar =
new QProgressBar();
90 progressBar->setAutoFillBackground(
true );
91 progressBar->setRange( 0, 0 );
97 progressBar->setMaximum( 100 );
98 progressBar->setValue(
static_cast< int >( std::round( progress ) ) );
101 progressBar->setMaximum( 0 );
104 mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Progress ), progressBar );
106 QgsTaskStatusWidget *statusWidget =
new QgsTaskStatusWidget(
nullptr, task->
status(), task->
canCancel() );
107 statusWidget->setAutoFillBackground(
true );
109 connect( statusWidget, &QgsTaskStatusWidget::cancelClicked, task, &
QgsTask::cancel );
110 mTreeView->setIndexWidget( mModel->index( row, QgsTaskManagerModel::Status ), statusWidget );
114 void QgsTaskManagerWidget::clicked(
const QModelIndex &index )
116 QgsTask *task = mModel->indexToTask( index );
128 QgsTaskManagerModel::QgsTaskManagerModel(
QgsTaskManager *manager, QObject *parent )
129 : QAbstractItemModel( parent )
130 , mManager( manager )
132 Q_ASSERT( mManager );
135 const auto constTasks = mManager->
tasks();
136 for (
QgsTask *task : constTasks )
138 mRowToTaskIdList << mManager->
taskId( task );
146 QModelIndex QgsTaskManagerModel::index(
int row,
int column,
const QModelIndex &parent )
const
148 if ( column < 0 || column >= columnCount() )
151 return QModelIndex();
154 if ( !parent.isValid() && row >= 0 && row < mRowToTaskIdList.count() )
157 return createIndex( row, column );
161 return QModelIndex();
165 QModelIndex QgsTaskManagerModel::parent(
const QModelIndex &index )
const
170 return QModelIndex();
173 int QgsTaskManagerModel::rowCount(
const QModelIndex &parent )
const
175 if ( !parent.isValid() )
177 return mRowToTaskIdList.count();
186 int QgsTaskManagerModel::columnCount(
const QModelIndex &parent )
const
192 QVariant QgsTaskManagerModel::data(
const QModelIndex &index,
int role )
const
194 if ( !index.isValid() )
197 QgsTask *task = indexToTask( index );
202 case Qt::DisplayRole:
204 switch ( index.column() )
218 return static_cast<int>( task->
status() );
220 case Qt::ToolTipRole:
221 switch ( index.column() )
224 return createTooltip( task, ToolTipDescription );
226 return createTooltip( task, ToolTipProgress );
228 return createTooltip( task, ToolTipStatus );
242 Qt::ItemFlags QgsTaskManagerModel::flags(
const QModelIndex &index )
const
244 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
246 if ( ! index.isValid() )
251 QgsTask *task = indexToTask( index );
252 if ( index.column() == Status )
255 flags = flags | Qt::ItemIsEditable;
257 return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
260 bool QgsTaskManagerModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
264 if ( !index.isValid() )
267 QgsTask *task = indexToTask( index );
271 switch ( index.column() )
275 if ( value.toBool() && task->
canCancel() )
285 void QgsTaskManagerModel::taskAdded(
long id )
287 beginInsertRows( QModelIndex(), mRowToTaskIdList.count(),
288 mRowToTaskIdList.count() );
289 mRowToTaskIdList << id;
293 void QgsTaskManagerModel::taskDeleted(
long id )
295 for (
int row = 0; row < mRowToTaskIdList.count(); ++row )
297 if ( mRowToTaskIdList.at( row ) ==
id )
299 beginRemoveRows( QModelIndex(), row, row );
300 mRowToTaskIdList.removeAt( row );
307 void QgsTaskManagerModel::progressChanged(
long id,
double progress )
311 QModelIndex index = idToIndex(
id, Progress );
312 if ( !index.isValid() )
317 emit dataChanged( index, index );
320 void QgsTaskManagerModel::statusChanged(
long id,
int status )
328 QModelIndex index = idToIndex(
id, Status );
329 if ( !index.isValid() )
334 emit dataChanged( index, index );
338 QgsTask *QgsTaskManagerModel::indexToTask(
const QModelIndex &index )
const
340 if ( !index.isValid() || index.parent().isValid() )
343 long id = index.row() >= 0 && index.row() < mRowToTaskIdList.count() ? mRowToTaskIdList.at( index.row() ) : -1;
345 return mManager->task(
id );
350 int QgsTaskManagerModel::idToRow(
long id )
const
352 for (
int row = 0; row < mRowToTaskIdList.count(); ++row )
354 if ( mRowToTaskIdList.at( row ) ==
id )
362 QModelIndex QgsTaskManagerModel::idToIndex(
long id,
int column )
const
364 int row = idToRow(
id );
366 return QModelIndex();
368 return index( row, column );
371 QString QgsTaskManagerModel::createTooltip(
QgsTask *task, ToolTipType type )
377 case ToolTipDescription:
381 case ToolTipProgress:
386 return tr(
"Queued" );
388 return tr(
"On hold" );
391 if ( type == ToolTipStatus && !task->
canCancel() )
392 return tr(
"Running (cannot cancel)" );
394 return tr(
"Running" );
397 return tr(
"Complete" );
399 return tr(
"Terminated" );
405 QString formattedTime;
412 qint64 msRemain =
static_cast< qint64
>( elapsed * 100.0 / task->
progress() - elapsed );
413 if ( msRemain > 120 * 1000 )
415 long long minutes = msRemain / 1000 / 60;
416 int seconds = ( msRemain / 1000 ) % 60;
417 formattedTime = tr(
"%1:%2 minutes" ).arg( minutes ).arg( seconds, 2, 10, QChar(
'0' ) );
420 formattedTime = tr(
"%1 seconds" ).arg( msRemain / 1000 );
422 formattedTime = tr(
"Estimated time remaining: %1" ).arg( formattedTime );
424 QTime estimatedEnd = QTime::currentTime().addMSecs( msRemain );
425 formattedTime += tr(
" (%1)" ).arg( QLocale::system().toString( estimatedEnd, QLocale::ShortFormat ) );
429 if ( elapsed > 120 * 1000 )
431 long long minutes = elapsed / 1000 / 60;
432 int seconds = ( elapsed / 1000 ) % 60;
433 formattedTime = tr(
"%1:%2 minutes" ).arg( minutes ).arg( seconds, 2, 10, QChar(
'0' ) );
436 formattedTime = tr(
"%1 seconds" ).arg( elapsed / 1000 );
438 formattedTime = tr(
"Time elapsed: %1" ).arg( formattedTime );
443 case ToolTipDescription:
444 return tr(
"%1<br>%2" ).arg( task->
description(), formattedTime );
447 case ToolTipProgress:
452 return tr(
"Queued" );
454 return tr(
"On hold" );
458 if ( type == ToolTipStatus && !task->
canCancel() )
459 statusDesc = tr(
"Running (cannot cancel)" );
461 statusDesc = tr(
"Running" );
462 return tr(
"%1<br>%2" ).arg( statusDesc, formattedTime );
465 return tr(
"Complete" );
467 return tr(
"Terminated" );
480 QgsTaskStatusWidget::QgsTaskStatusWidget( QWidget *parent,
QgsTask::TaskStatus status,
bool canCancel )
482 , mCanCancel( canCancel )
485 setMouseTracking(
true );
488 QSize QgsTaskStatusWidget::sizeHint()
const
490 return QSize( 32, 32 );
493 void QgsTaskStatusWidget::setStatus(
int status )
499 void QgsTaskStatusWidget::paintEvent( QPaintEvent *e )
501 QWidget::paintEvent( e );
531 icon.paint( &p, 1, height() / 2 - 12, 24, 24 );
535 void QgsTaskStatusWidget::mousePressEvent( QMouseEvent * )
538 emit cancelClicked();
541 void QgsTaskStatusWidget::mouseMoveEvent( QMouseEvent * )
550 void QgsTaskStatusWidget::leaveEvent( QEvent * )
579 QgsTaskManagerFloatingWidget::QgsTaskManagerFloatingWidget(
QgsTaskManager *manager, QWidget *parent )
582 setLayout(
new QVBoxLayout() );
585 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
588 int minWidth =
static_cast< int >( fontMetrics().horizontalAdvance(
'X' ) * 60 *
Qgis::UI_SCALE_FACTOR );
591 setMinimumSize( minWidth, minHeight );
592 layout()->addWidget( w );
593 setStyleSheet(
".QgsTaskManagerFloatingWidget { border-top-left-radius: 8px;"
594 "border-top-right-radius: 8px; background-color: rgba(0, 0, 0, 70%); }" );
598 QgsTaskManagerStatusBarWidget::QgsTaskManagerStatusBarWidget(
QgsTaskManager *manager, QWidget *parent )
599 : QToolButton( parent )
600 , mManager( manager )
602 setAutoRaise(
true );
603 setSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding );
604 setLayout(
new QVBoxLayout() );
606 mProgressBar =
new QProgressBar();
607 mProgressBar->setMinimum( 0 );
608 mProgressBar->setMaximum( 0 );
609 layout()->setContentsMargins( 5, 5, 5, 5 );
610 layout()->addWidget( mProgressBar );
612 mFloatingWidget =
new QgsTaskManagerFloatingWidget( manager, parent ? parent->window() :
nullptr );
613 mFloatingWidget->setAnchorWidget(
this );
616 mFloatingWidget->hide();
617 connect(
this, &QgsTaskManagerStatusBarWidget::clicked,
this, &QgsTaskManagerStatusBarWidget::toggleDisplay );
629 QSize QgsTaskManagerStatusBarWidget::sizeHint()
const
632 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
635 int width =
static_cast< int >( fontMetrics().horizontalAdvance(
'X' ) * 20 *
Qgis::UI_SCALE_FACTOR );
637 int height = QToolButton::sizeHint().height();
638 return QSize( width, height );
641 void QgsTaskManagerStatusBarWidget::changeEvent( QEvent *event )
643 QToolButton::changeEvent( event );
645 if ( event->type() == QEvent::FontChange )
647 mProgressBar->setFont( font() );
651 void QgsTaskManagerStatusBarWidget::toggleDisplay()
653 if ( mFloatingWidget->isVisible() )
654 mFloatingWidget->hide();
657 mFloatingWidget->show();
658 mFloatingWidget->raise();
662 void QgsTaskManagerStatusBarWidget::overallProgressChanged(
double progress )
664 mProgressBar->setValue(
static_cast< int >( std::round( progress ) ) );
666 mProgressBar->setMaximum( 0 );
667 else if ( mProgressBar->maximum() == 0 )
668 mProgressBar->setMaximum( 100 );
669 setToolTip( QgsTaskManagerModel::createTooltip( mManager->activeTasks().at( 0 ), QgsTaskManagerModel::ToolTipDescription ) );
672 void QgsTaskManagerStatusBarWidget::countActiveTasksChanged(
int count )
676 mProgressBar->setMaximum( 0 );
677 setToolTip( tr(
"%1 active tasks running" ).arg( count ) );
681 void QgsTaskManagerStatusBarWidget::allFinished()
683 mFloatingWidget->hide();
686 mProgressBar->setMaximum( 0 );
687 mProgressBar->setValue( 0 );
690 void QgsTaskManagerStatusBarWidget::showButton()
694 mProgressBar->setMaximum( 0 );
695 mProgressBar->setValue( 0 );
static const double UI_SCALE_FACTOR
UI scaling factor.
static QIcon getThemeIcon(const QString &name)
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.
int countActiveTasks() const
Returns the number of active (queued or running) tasks.
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.
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)