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