29#include <QStandardItemModel>
30#include <QStandardItem>
35#include <QDirIterator>
37#include <QDragEnterEvent>
41QgsProcessingMultipleSelectionPanelWidget::QgsProcessingMultipleSelectionPanelWidget(
const QVariantList &availableOptions,
42 const QVariantList &selectedOptions,
45 , mValueFormatter( []( const QVariant & v )->QString
47 if ( v.userType() == QMetaType::type(
"QgsProcessingModelChildParameterSource" ) )
48 return v.value< QgsProcessingModelChildParameterSource >().staticValue().toString();
57 mSelectionList->setSelectionBehavior( QAbstractItemView::SelectRows );
58 mSelectionList->setSelectionMode( QAbstractItemView::ExtendedSelection );
59 mSelectionList->setDragDropMode( QAbstractItemView::InternalMove );
61 mButtonSelectAll =
new QPushButton( tr(
"Select All" ) );
62 mButtonBox->addButton( mButtonSelectAll, QDialogButtonBox::ActionRole );
64 mButtonClearSelection =
new QPushButton( tr(
"Clear Selection" ) );
65 mButtonBox->addButton( mButtonClearSelection, QDialogButtonBox::ActionRole );
67 mButtonToggleSelection =
new QPushButton( tr(
"Toggle Selection" ) );
68 mButtonBox->addButton( mButtonToggleSelection, QDialogButtonBox::ActionRole );
70 connect( mButtonSelectAll, &QPushButton::clicked,
this, [ = ] { selectAll(
true ); } );
71 connect( mButtonClearSelection, &QPushButton::clicked,
this, [ = ] { selectAll(
false ); } );
72 connect( mButtonToggleSelection, &QPushButton::clicked,
this, &QgsProcessingMultipleSelectionPanelWidget::toggleSelection );
74 connect( mButtonBox, &QDialogButtonBox::accepted,
this, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked );
75 populateList( availableOptions, selectedOptions );
77 connect( mModel, &QStandardItemModel::itemChanged,
this, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged );
81 connect( mModel, &QStandardItemModel::rowsRemoved,
this, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged );
84void QgsProcessingMultipleSelectionPanelWidget::setValueFormatter(
const std::function<QString(
const QVariant & )> &
formatter )
88 for (
int i = 0; i < mModel->rowCount(); ++i )
90 mModel->item( i )->setText( mValueFormatter( mModel->item( i )->data( Qt::UserRole ) ) );
94QVariantList QgsProcessingMultipleSelectionPanelWidget::selectedOptions()
const
97 options.reserve( mModel->rowCount() );
98 bool hasModelSources =
false;
99 for (
int i = 0; i < mModel->rowCount(); ++i )
101 QStandardItem *item = mModel->item( i );
107 if ( item->checkState() == Qt::Checked )
109 const QVariant option = item->data( Qt::UserRole );
111 if ( option.userType() == QMetaType::type(
"QgsProcessingModelChildParameterSource" ) )
112 hasModelSources =
true;
118 if ( hasModelSources )
121 QVariantList originalOptions = options;
123 for (
const QVariant &option : originalOptions )
125 if ( option.userType() == QMetaType::type(
"QgsProcessingModelChildParameterSource" ) )
128 options << QVariant::fromValue( QgsProcessingModelChildParameterSource::fromStaticValue( option ) );
136void QgsProcessingMultipleSelectionPanelWidget::selectAll(
const bool checked )
138 const QList<QStandardItem *> items = currentItems();
139 for ( QStandardItem *item : items )
141 item->setCheckState( checked ? Qt::Checked : Qt::Unchecked );
145void QgsProcessingMultipleSelectionPanelWidget::toggleSelection()
147 const QList<QStandardItem *> items = currentItems();
148 for ( QStandardItem *item : items )
150 item->setCheckState( item->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked );
154QList<QStandardItem *> QgsProcessingMultipleSelectionPanelWidget::currentItems()
156 QList<QStandardItem *> items;
157 const QModelIndexList selection = mSelectionList->selectionModel()->selectedIndexes();
158 if ( selection.size() > 1 )
160 items.reserve( selection.size() );
161 for (
const QModelIndex &index : selection )
163 items << mModel->itemFromIndex( index );
168 items.reserve( mModel->rowCount() );
169 for (
int i = 0; i < mModel->rowCount(); ++i )
171 items << mModel->item( i );
177void QgsProcessingMultipleSelectionPanelWidget::populateList(
const QVariantList &availableOptions,
const QVariantList &selectedOptions )
179 mModel =
new QStandardItemModel(
this );
181 QVariantList remainingOptions = availableOptions;
184 for (
const QVariant &option : selectedOptions )
190 addOption( option, mValueFormatter( option ),
true );
191 remainingOptions.removeAll( option );
194 for (
const QVariant &option : std::as_const( remainingOptions ) )
196 addOption( option, mValueFormatter( option ),
false );
199 mSelectionList->setModel( mModel );
202QList< int> QgsProcessingMultipleSelectionPanelWidget::existingMapLayerFromMimeData(
const QMimeData *data )
const
211 for (
int i = 0; i < mModel->rowCount(); ++i )
214 QString userRole = mModel->item( i )->data( Qt::UserRole ).toString();
215 if ( userRole == layer->id() || userRole == layer->source() )
225void QgsProcessingMultipleSelectionPanelWidget::dragEnterEvent( QDragEnterEvent *event )
227 if ( !( event->possibleActions() & Qt::CopyAction ) )
230 const QList< int> indexes = existingMapLayerFromMimeData( event->mimeData() );
231 if ( !indexes.isEmpty() )
234 event->setDropAction( Qt::CopyAction );
239void QgsProcessingMultipleSelectionPanelWidget::dropEvent( QDropEvent *event )
241 if ( !( event->possibleActions() & Qt::CopyAction ) )
244 const QList< int> indexes = existingMapLayerFromMimeData( event->mimeData() );
245 if ( !indexes.isEmpty() )
248 setFocus( Qt::MouseFocusReason );
249 event->setDropAction( Qt::CopyAction );
252 for (
const int i : indexes )
254 mModel->item( i )->setCheckState( Qt::Checked );
256 emit selectionChanged();
260void QgsProcessingMultipleSelectionPanelWidget::addOption(
const QVariant &value,
const QString &title,
bool selected,
bool updateExistingTitle )
263 for (
int i = 0; i < mModel->rowCount(); ++i )
265 if ( mModel->item( i )->data( Qt::UserRole ) == value ||
266 ( mModel->item( i )->data( Qt::UserRole ).userType() == QMetaType::type(
"QgsProcessingModelChildParameterSource" ) &&
267 value.userType() == QMetaType::type(
"QgsProcessingModelChildParameterSource" ) &&
268 mModel->item( i )->data( Qt::UserRole ).value< QgsProcessingModelChildParameterSource >() ==
269 value.value< QgsProcessingModelChildParameterSource >() )
272 if ( updateExistingTitle )
273 mModel->item( i )->setText( title );
278 std::unique_ptr< QStandardItem > item = std::make_unique< QStandardItem >( title );
279 item->setData( value, Qt::UserRole );
280 item->setCheckState( selected ? Qt::Checked : Qt::Unchecked );
281 item->setCheckable(
true );
282 item->setDropEnabled(
false );
283 mModel->appendRow( item.release() );
292QgsProcessingMultipleSelectionDialog::QgsProcessingMultipleSelectionDialog(
const QVariantList &availableOptions,
const QVariantList &selectedOptions, QWidget *parent, Qt::WindowFlags flags )
293 : QDialog( parent, flags )
295 setWindowTitle( tr(
"Multiple Selection" ) );
296 QVBoxLayout *vLayout =
new QVBoxLayout();
297 mWidget =
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
298 vLayout->addWidget( mWidget );
299 mWidget->buttonBox()->addButton( QDialogButtonBox::Cancel );
300 connect( mWidget->buttonBox(), &QDialogButtonBox::accepted,
this, &QDialog::accept );
301 connect( mWidget->buttonBox(), &QDialogButtonBox::rejected,
this, &QDialog::reject );
302 setLayout( vLayout );
305void QgsProcessingMultipleSelectionDialog::setValueFormatter(
const std::function<QString(
const QVariant & )> &
formatter )
310QVariantList QgsProcessingMultipleSelectionDialog::selectedOptions()
const
312 return mWidget->selectedOptions();
321 const QList<QgsProcessingModelChildParameterSource> &modelSources,
322 QgsProcessingModelAlgorithm *model, QWidget *parent )
323 : QgsProcessingMultipleSelectionPanelWidget( QVariantList(), selectedOptions, parent )
324 , mParameter( parameter )
326 QPushButton *addFileButton =
new QPushButton( tr(
"Add File(s)…" ) );
327 connect( addFileButton, &QPushButton::clicked,
this, &QgsProcessingMultipleInputPanelWidget::addFiles );
328 buttonBox()->addButton( addFileButton, QDialogButtonBox::ActionRole );
330 QPushButton *addDirButton =
new QPushButton( tr(
"Add Directory…" ) );
331 connect( addDirButton, &QPushButton::clicked,
this, &QgsProcessingMultipleInputPanelWidget::addDirectory );
332 buttonBox()->addButton( addDirButton, QDialogButtonBox::ActionRole );
333 setAcceptDrops(
true );
334 for (
const QgsProcessingModelChildParameterSource &source : modelSources )
336 addOption( QVariant::fromValue( source ), source.friendlyIdentifier( model ),
false,
true );
340void QgsProcessingMultipleInputPanelWidget::setProject(
QgsProject *project )
343 populateFromProject( project );
346void QgsProcessingMultipleInputPanelWidget::addFiles()
349 QString path = settings.
value( QStringLiteral(
"/Processing/LastInputPath" ), QDir::homePath() ).toString();
353 filter = generator->createFileFilter();
355 filter = QObject::tr(
"All files (*.*)" );
357 const QStringList filenames = QFileDialog::getOpenFileNames(
this, tr(
"Select File(s)" ), path, filter );
358 if ( filenames.empty() )
361 settings.
setValue( QStringLiteral(
"/Processing/LastInputPath" ), QFileInfo( filenames.at( 0 ) ).path() );
363 for (
const QString &file : filenames )
365 addOption( file, file,
true );
368 emit selectionChanged();
371void QgsProcessingMultipleInputPanelWidget::addDirectory()
374 const QString path = settings.
value( QStringLiteral(
"/Processing/LastInputPath" ), QDir::homePath() ).toString();
376 const QString dir = QFileDialog::getExistingDirectory(
this, tr(
"Select Directory" ), path );
380 settings.
setValue( QStringLiteral(
"/Processing/LastInputPath" ), dir );
382 QStringList nameFilters;
386 for (
const QString &extension : extensions )
388 nameFilters << QStringLiteral(
"*.%1" ).arg( extension );
389 nameFilters << QStringLiteral(
"*.%1" ).arg( extension.toUpper() );
390 nameFilters << QStringLiteral(
"*.%1" ).arg( extension.toLower() );
394 QDirIterator it( dir, nameFilters, QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories );
395 while ( it.hasNext() )
397 const QString fullPath = it.next();
398 addOption( fullPath, fullPath,
true );
400 emit selectionChanged();
403QList< int> QgsProcessingMultipleInputPanelWidget::existingMapLayerFromMimeData(
const QMimeData *data )
const
412 for (
int i = 0; i < mModel->rowCount(); ++i )
415 const QString userRole = mModel->item( i )->data( Qt::UserRole ).toString();
416 if ( userRole == layer->id() || userRole == layer->source() )
426void QgsProcessingMultipleInputPanelWidget::dragEnterEvent( QDragEnterEvent *event )
428 if ( !( event->possibleActions() & Qt::CopyAction ) )
431 const QList< int> indexes = existingMapLayerFromMimeData( event->mimeData() );
432 if ( !indexes.isEmpty() )
435 event->setDropAction( Qt::CopyAction );
440void QgsProcessingMultipleInputPanelWidget::dropEvent( QDropEvent *event )
442 if ( !( event->possibleActions() & Qt::CopyAction ) )
445 const QList< int> indexes = existingMapLayerFromMimeData( event->mimeData() );
446 if ( !indexes.isEmpty() )
449 setFocus( Qt::MouseFocusReason );
450 event->setDropAction( Qt::CopyAction );
453 for (
const int i : indexes )
455 mModel->item( i )->setCheckState( Qt::Checked );
457 emit selectionChanged();
461void QgsProcessingMultipleInputPanelWidget::populateFromProject(
QgsProject *project )
465 for (
int i = 0; i < mModel->rowCount(); ++i )
467 const QStandardItem *item = mModel->item( i );
468 if ( item->data( Qt::UserRole ) == layerId )
470 bool isChecked = ( item->checkState() == Qt::Checked );
471 mModel->removeRow( i );
474 emit selectionChanged();
484 const QString authid = layer->crs().authid();
486 if ( settings.value( QStringLiteral(
"Processing/Configuration/SHOW_CRS_DEF" ),
true ).toBool() && !authid.isEmpty() )
487 title = QStringLiteral(
"%1 [%2]" ).arg( layer->name(), authid );
489 title = layer->name();
492 QString
id = layer->id();
494 id = QStringLiteral(
"main" );
496 for (
int i = 0; i < mModel->rowCount(); ++i )
499 if ( mModel->item( i )->data( Qt::UserRole ) == layer->id() )
504 else if ( mModel->item( i )->data( Qt::UserRole ) == layer->source() )
506 id = layer->source();
511 addOption(
id, title,
false,
true );
514 switch ( mParameter->layerType() )
651 const QList< QgsProcessingModelChildParameterSource > &modelSources, QgsProcessingModelAlgorithm *model, QWidget *parent, Qt::WindowFlags flags )
652 : QDialog( parent, flags )
654 setWindowTitle( tr(
"Multiple Selection" ) );
655 QVBoxLayout *vLayout =
new QVBoxLayout();
656 mWidget =
new QgsProcessingMultipleInputPanelWidget( parameter, selectedOptions, modelSources, model );
657 vLayout->addWidget( mWidget );
658 mWidget->buttonBox()->addButton( QDialogButtonBox::Cancel );
659 connect( mWidget->buttonBox(), &QDialogButtonBox::accepted,
this, &QDialog::accept );
660 connect( mWidget->buttonBox(), &QDialogButtonBox::rejected,
this, &QDialog::reject );
661 setLayout( vLayout );
662 setAcceptDrops(
true );
665QVariantList QgsProcessingMultipleInputDialog::selectedOptions()
const
667 return mWidget->selectedOptions();
670void QgsProcessingMultipleInputDialog::setProject(
QgsProject *project )
672 mWidget->setProject( project );
Represents a map layer containing a set of georeferenced annotations, e.g.
Abstract interface for classes which generate a file filter string.
static QStringList extensionsFromFilter(const QString &filter)
Returns a list of the extensions contained within a file filter string.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Base class for all map layer types.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QList< QgsMimeDataUtils::Uri > UriList
static UriList decodeUriList(const QMimeData *data)
Base class for plugin layers.
Represents a map layer supporting display of point clouds.
A parameter for processing algorithms which accepts multiple map layers.
static QList< QgsAnnotationLayer * > compatibleAnnotationLayers(QgsProject *project, bool sort=true)
Returns a list of annotation layers from a project which are compatible with the processing framework...
static QList< QgsRasterLayer * > compatibleRasterLayers(QgsProject *project, bool sort=true)
Returns a list of raster layers from a project which are compatible with the processing framework.
static QList< QgsPluginLayer * > compatiblePluginLayers(QgsProject *project, bool sort=true)
Returns a list of plugin layers from a project which are compatible with the processing framework.
static QList< QgsVectorLayer * > compatibleVectorLayers(QgsProject *project, const QList< int > &sourceTypes=QList< int >(), bool sort=true)
Returns a list of vector layers from a project which are compatible with the processing framework.
static QList< QgsVectorTileLayer * > compatibleVectorTileLayers(QgsProject *project, bool sort=true)
Returns a list of vector tile layers from a project which are compatible with the processing framewor...
static QList< QgsPointCloudLayer * > compatiblePointCloudLayers(QgsProject *project, bool sort=true)
Returns a list of point cloud layers from a project which are compatible with the processing framewor...
static QList< QgsMeshLayer * > compatibleMeshLayers(QgsProject *project, bool sort=true)
Returns a list of mesh layers from a project which are compatible with the processing framework.
@ TypePlugin
Plugin layers.
@ TypeVectorLine
Vector line layers.
@ TypeMapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer)
@ TypeVectorPolygon
Vector polygon layers.
@ TypeFile
Files (i.e. non map layer sources, such as text files)
@ TypeAnnotation
Annotation layers.
@ TypePointCloud
Point cloud layers.
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
@ TypeRaster
Raster layers.
@ TypeVectorTile
Vector tile layers.
@ TypeVectorPoint
Vector point layers.
@ TypeVectorAnyGeometry
Any vector layer with geometry.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
QgsAnnotationLayer * mainAnnotationLayer()
Returns the main annotation layer associated with the project.
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the registry.
Represents a raster layer.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Represents a vector layer which manages a vector based data sets.
Implements a map layer that is dedicated to rendering of vector tiles.