22 #include <QMouseEvent> 26 #include <QProgressBar> 28 #include <QHeaderView> 40 QVBoxLayout *vLayout =
new QVBoxLayout();
41 vLayout->setMargin( 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 );
626 QSize QgsTaskManagerStatusBarWidget::sizeHint()
const 629 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) 632 int width =
static_cast< int >( fontMetrics().horizontalAdvance(
'X' ) * 20 *
Qgis::UI_SCALE_FACTOR );
634 int height = QToolButton::sizeHint().height();
635 return QSize( width, height );
638 void QgsTaskManagerStatusBarWidget::changeEvent( QEvent *event )
640 QToolButton::changeEvent( event );
642 if ( event->type() == QEvent::FontChange )
644 mProgressBar->setFont( font() );
648 void QgsTaskManagerStatusBarWidget::toggleDisplay()
650 if ( mFloatingWidget->isVisible() )
651 mFloatingWidget->hide();
654 mFloatingWidget->show();
655 mFloatingWidget->raise();
659 void QgsTaskManagerStatusBarWidget::overallProgressChanged(
double progress )
661 mProgressBar->setValue( static_cast< int >( std::round( progress ) ) );
663 mProgressBar->setMaximum( 0 );
664 else if ( mProgressBar->maximum() == 0 )
665 mProgressBar->setMaximum( 100 );
666 setToolTip( QgsTaskManagerModel::createTooltip( mManager->
activeTasks().at( 0 ), QgsTaskManagerModel::ToolTipDescription ) );
669 void QgsTaskManagerStatusBarWidget::countActiveTasksChanged(
int count )
673 mProgressBar->setMaximum( 0 );
674 setToolTip( tr(
"%1 active tasks running" ).arg( count ) );
678 void QgsTaskManagerStatusBarWidget::allFinished()
680 mFloatingWidget->hide();
683 mProgressBar->setMaximum( 0 );
684 mProgressBar->setValue( 0 );
687 void QgsTaskManagerStatusBarWidget::showButton()
691 mProgressBar->setMaximum( 0 );
692 mProgressBar->setValue( 0 );
static const double UI_SCALE_FACTOR
UI scaling factor.
void statusChanged(int status)
Will be emitted by task when its status changes.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
qint64 elapsedTime() const
Returns the elapsed time since the task commenced, in milliseconds.
TaskStatus
Status of tasks.
QList< QgsTask * > tasks() const
Returns all tasks tracked by the manager.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void progressChanged(double progress)
Will be emitted by task when its progress changes.
Task was terminated or errored.
void countActiveTasksChanged(int count)
Emitted when the number of active tasks changes.
void triggerTask(QgsTask *task)
Triggers a task, e.g.
QString description() const
Returns the task's description.
QgsTask * task(long id) const
Returns the task with matching ID.
void progressChanged(long taskId, double progress)
Will be emitted when a task reports a progress change.
Task is queued but on hold and will not be started.
Abstract base class for long running background tasks.
bool canCancel() const
Returns true if the task can be canceled.
void statusChanged(long taskId, int status)
Will be emitted when a task reports a status change.
Task successfully completed.
Task manager for managing a set of long-running QgsTask tasks.
void taskAdded(long taskId)
Emitted when a new task has been added to the manager.
virtual void cancel()
Notifies the task that it should terminate.
void allTasksFinished()
Emitted when all tasks are complete.
Task is queued and has not begun.
double progress() const
Returns the task's progress (between 0.0 and 100.0)
QList< QgsTask *> activeTasks() const
Returns a list of the active (queued or running) tasks.
Task is currently running.
long taskId(QgsTask *task) const
Returns the unique task ID corresponding to a task managed by the class.
void finalTaskProgressChanged(double progress)
Will be emitted when only a single task remains to complete and that task has reported a progress cha...
TaskStatus status() const
Returns the current task status.