37#include <QDirIterator>
38#include <QDragEnterEvent>
42#include <QStandardItem>
43#include <QStandardItemModel>
47#include "moc_qgsprocessingmultipleselectiondialog.cpp"
49using namespace Qt::StringLiterals;
53QgsProcessingMultipleSelectionPanelWidget::QgsProcessingMultipleSelectionPanelWidget(
const QVariantList &availableOptions,
const QVariantList &selectedOptions, QWidget *parent )
55 , mValueFormatter( []( const QVariant &v ) -> QString {
56 if ( v.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
57 return v.value<QgsProcessingModelChildParameterSource>().staticValue().toString();
66 mSelectionList->setSelectionBehavior( QAbstractItemView::SelectRows );
67 mSelectionList->setSelectionMode( QAbstractItemView::ExtendedSelection );
68 mSelectionList->setDragDropMode( QAbstractItemView::InternalMove );
71 mModel =
new QStandardItemModel(
this );
72 mSelectionList->setModel( mModel );
74 mButtonSelectAll =
new QPushButton( tr(
"Select All" ) );
75 mButtonBox->addButton( mButtonSelectAll, QDialogButtonBox::ActionRole );
77 mButtonClearSelection =
new QPushButton( tr(
"Clear Selection" ) );
78 mButtonBox->addButton( mButtonClearSelection, QDialogButtonBox::ActionRole );
80 mButtonToggleSelection =
new QPushButton( tr(
"Toggle Selection" ) );
81 mButtonBox->addButton( mButtonToggleSelection, QDialogButtonBox::ActionRole );
83 connect( mButtonSelectAll, &QPushButton::clicked,
this, [
this] { selectAll(
true ); } );
84 connect( mButtonClearSelection, &QPushButton::clicked,
this, [
this] { selectAll(
false ); } );
85 connect( mButtonToggleSelection, &QPushButton::clicked,
this, &QgsProcessingMultipleSelectionPanelWidget::toggleSelection );
87 connect( mButtonBox, &QDialogButtonBox::accepted,
this, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked );
88 populateList( availableOptions, selectedOptions );
90 connect( mModel, &QStandardItemModel::itemChanged,
this, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged );
94 connect( mModel, &QStandardItemModel::rowsRemoved,
this, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged );
97void QgsProcessingMultipleSelectionPanelWidget::setValueFormatter(
const std::function<QString(
const QVariant & )> &formatter )
99 mValueFormatter = formatter;
101 for (
int i = 0; i < mModel->rowCount(); ++i )
103 mModel->item( i )->setText( mValueFormatter( mModel->item( i )->data( Qt::UserRole ) ) );
107QVariantList QgsProcessingMultipleSelectionPanelWidget::selectedOptions()
const
109 QVariantList options;
110 options.reserve( mModel->rowCount() );
111 bool hasModelSources =
false;
112 for (
int i = 0; i < mModel->rowCount(); ++i )
114 QStandardItem *item = mModel->item( i );
120 if ( item->checkState() == Qt::Checked )
122 const QVariant option = item->data( Qt::UserRole );
124 if ( option.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
125 hasModelSources =
true;
131 if ( hasModelSources )
134 QVariantList originalOptions = options;
136 for (
const QVariant &option : originalOptions )
138 if ( option.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
141 options << QVariant::fromValue( QgsProcessingModelChildParameterSource::fromStaticValue( option ) );
149void QgsProcessingMultipleSelectionPanelWidget::selectAll(
const bool checked )
151 const QList<QStandardItem *> items = currentItems();
152 for ( QStandardItem *item : items )
154 item->setCheckState( checked ? Qt::Checked : Qt::Unchecked );
158void QgsProcessingMultipleSelectionPanelWidget::toggleSelection()
160 const QList<QStandardItem *> items = currentItems();
161 for ( QStandardItem *item : items )
163 item->setCheckState( item->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked );
167QList<QStandardItem *> QgsProcessingMultipleSelectionPanelWidget::currentItems()
169 QList<QStandardItem *> items;
170 const QModelIndexList selection = mSelectionList->selectionModel()->selectedIndexes();
171 if ( selection.size() > 1 )
173 items.reserve( selection.size() );
174 for (
const QModelIndex &index : selection )
176 items << mModel->itemFromIndex( index );
181 items.reserve( mModel->rowCount() );
182 for (
int i = 0; i < mModel->rowCount(); ++i )
184 items << mModel->item( i );
190void QgsProcessingMultipleSelectionPanelWidget::populateList(
const QVariantList &availableOptions,
const QVariantList &selectedOptions )
192 QVariantList remainingOptions = availableOptions;
195 for (
const QVariant &option : selectedOptions )
201 addOption( option, mValueFormatter( option ),
true );
202 remainingOptions.removeAll( option );
205 for (
const QVariant &option : std::as_const( remainingOptions ) )
207 addOption( option, mValueFormatter( option ),
false );
211QList<int> QgsProcessingMultipleSelectionPanelWidget::existingMapLayerFromMimeData(
const QMimeData *data )
const
220 for (
int i = 0; i < mModel->rowCount(); ++i )
223 QString userRole = mModel->item( i )->data( Qt::UserRole ).toString();
224 if ( userRole == layer->id() || userRole == layer->source() )
234void QgsProcessingMultipleSelectionPanelWidget::dragEnterEvent( QDragEnterEvent *event )
236 if ( !( event->possibleActions() & Qt::CopyAction ) )
239 const QList<int> indexes = existingMapLayerFromMimeData( event->mimeData() );
240 if ( !indexes.isEmpty() )
243 event->setDropAction( Qt::CopyAction );
248void QgsProcessingMultipleSelectionPanelWidget::dropEvent( QDropEvent *event )
250 if ( !( event->possibleActions() & Qt::CopyAction ) )
253 const QList<int> indexes = existingMapLayerFromMimeData( event->mimeData() );
254 if ( !indexes.isEmpty() )
257 setFocus( Qt::MouseFocusReason );
258 event->setDropAction( Qt::CopyAction );
261 for (
const int i : indexes )
263 mModel->item( i )->setCheckState( Qt::Checked );
265 emit selectionChanged();
269void QgsProcessingMultipleSelectionPanelWidget::addOption(
const QVariant &value,
const QString &title,
bool selected,
bool updateExistingTitle, QIcon icon,
const QString &tooltip )
272 for (
int i = 0; i < mModel->rowCount(); ++i )
274 if ( mModel->item( i )->data( Qt::UserRole ) == value || ( mModel->item( i )->data( Qt::UserRole ).userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() && value.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() && mModel->item( i )->data( Qt::UserRole ).value<QgsProcessingModelChildParameterSource>() == value.value<QgsProcessingModelChildParameterSource>() ) )
276 if ( updateExistingTitle )
277 mModel->item( i )->setText( title );
282 auto item = std::make_unique<QStandardItem>( title );
283 item->setData( value, Qt::UserRole );
284 item->setCheckState( selected ? Qt::Checked : Qt::Unchecked );
285 item->setCheckable(
true );
286 item->setDropEnabled(
false );
287 item->setToolTip( tooltip );
288 if ( !icon.isNull() )
289 item->setData( icon, Qt::DecorationRole );
290 mModel->appendRow( item.release() );
298QgsProcessingMultipleSelectionDialog::QgsProcessingMultipleSelectionDialog(
const QVariantList &availableOptions,
const QVariantList &selectedOptions, QWidget *parent, Qt::WindowFlags flags )
299 : QDialog( parent, flags )
301 setWindowTitle( tr(
"Multiple Selection" ) );
302 QVBoxLayout *vLayout =
new QVBoxLayout();
303 mWidget =
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
304 vLayout->addWidget( mWidget );
305 mWidget->buttonBox()->addButton( QDialogButtonBox::Cancel );
306 connect( mWidget->buttonBox(), &QDialogButtonBox::accepted,
this, &QDialog::accept );
307 connect( mWidget->buttonBox(), &QDialogButtonBox::rejected,
this, &QDialog::reject );
308 setLayout( vLayout );
311void QgsProcessingMultipleSelectionDialog::setValueFormatter(
const std::function<QString(
const QVariant & )> &formatter )
313 mWidget->setValueFormatter( formatter );
316QVariantList QgsProcessingMultipleSelectionDialog::selectedOptions()
const
318 return mWidget->selectedOptions();
326QgsProcessingMultipleInputPanelWidget::QgsProcessingMultipleInputPanelWidget(
328 const QVariantList &selectedOptions,
329 const QList<QgsProcessingModelChildParameterSource> &modelSources,
330 QgsProcessingModelAlgorithm *model,
333 : QgsProcessingMultipleSelectionPanelWidget( QVariantList(), selectedOptions, parent )
334 , mParameter( parameter )
336 QPushButton *addFileButton =
new QPushButton( tr(
"Add File(s)…" ) );
337 connect( addFileButton, &QPushButton::clicked,
this, &QgsProcessingMultipleInputPanelWidget::addFiles );
338 buttonBox()->addButton( addFileButton, QDialogButtonBox::ActionRole );
340 QPushButton *addDirButton =
new QPushButton( tr(
"Add Directory…" ) );
341 connect( addDirButton, &QPushButton::clicked,
this, &QgsProcessingMultipleInputPanelWidget::addDirectory );
342 buttonBox()->addButton( addDirButton, QDialogButtonBox::ActionRole );
343 setAcceptDrops(
true );
346 for (
const QgsProcessingModelChildParameterSource &source : modelSources )
348 addOption( QVariant::fromValue( source ), source.friendlyIdentifier( model ),
false,
true );
352void QgsProcessingMultipleInputPanelWidget::setProject(
QgsProject *project )
355 populateFromProject( project );
358void QgsProcessingMultipleInputPanelWidget::addFiles()
361 QString path = settings.
value( u
"/Processing/LastInputPath"_s, QDir::homePath() ).toString();
365 filter = generator->createFileFilter();
367 filter = QObject::tr(
"All files (*.*)" );
369 const QStringList filenames = QFileDialog::getOpenFileNames(
this, tr(
"Select File(s)" ), path, filter );
370 if ( filenames.empty() )
373 settings.
setValue( u
"/Processing/LastInputPath"_s, QFileInfo( filenames.at( 0 ) ).path() );
375 for (
const QString &file : filenames )
377 addOption( file, file,
true );
380 emit selectionChanged();
383void QgsProcessingMultipleInputPanelWidget::addDirectory()
386 const QString path = settings.
value( u
"/Processing/LastInputPath"_s, QDir::homePath() ).toString();
388 const QString dir = QFileDialog::getExistingDirectory(
this, tr(
"Select Directory" ), path );
392 settings.
setValue( u
"/Processing/LastInputPath"_s, dir );
394 QStringList nameFilters;
398 for (
const QString &extension : extensions )
400 nameFilters << u
"*.%1"_s.arg( extension );
401 nameFilters << u
"*.%1"_s.arg( extension.toUpper() );
402 nameFilters << u
"*.%1"_s.arg( extension.toLower() );
406 QDirIterator it( dir, nameFilters, QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories );
407 while ( it.hasNext() )
409 const QString fullPath = it.next();
410 if ( fullPath.endsWith(
".dbf"_L1, Qt::CaseInsensitive ) )
412 if ( QFileInfo::exists( u
"%1.shp"_s.arg( fullPath.chopped( 4 ) ) ) || QFileInfo::exists( u
"%1.SHP"_s.arg( fullPath.chopped( 4 ) ) ) )
418 else if ( fullPath.endsWith(
".aux.xml"_L1, Qt::CaseInsensitive ) || fullPath.endsWith(
".shp.xml"_L1, Qt::CaseInsensitive ) )
423 addOption( fullPath, fullPath,
true );
425 emit selectionChanged();
428QList<int> QgsProcessingMultipleInputPanelWidget::existingMapLayerFromMimeData(
const QMimeData *data,
QgsMimeDataUtils::UriList &handledUrls )
const
436 bool matched =
false;
439 for (
int i = 0; i < mModel->rowCount(); ++i )
442 const QString userRole = mModel->item( i )->data( Qt::UserRole ).toString();
443 if ( userRole == layer->id() || userRole == layer->source() )
453 handledUrls.append( u );
462 QStringList skipUrlData;
463 skipUrlData.reserve( skipUrls.size() );
466 skipUrlData.append( u.data() );
474 if ( skipUrlData.contains( u.data() ) )
487 bool acceptable =
false;
528 && u.providerKey ==
"gdal"_L1 )
532 && u.providerKey ==
"mdal"_L1 )
542 if ( !uriList.isEmpty() )
546 QStringList rawPaths;
547 if ( data->hasUrls() )
549 const QList<QUrl> urls = data->urls();
550 rawPaths.reserve( urls.count() );
551 for (
const QUrl &url : urls )
553 const QString local = url.toLocalFile();
554 if ( !rawPaths.contains( local ) )
555 rawPaths.append( local );
558 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
559 rawPaths.append( data->text() );
561 for (
const QString &path : std::as_const( rawPaths ) )
563 QFileInfo file( path );
574void QgsProcessingMultipleInputPanelWidget::dragEnterEvent( QDragEnterEvent *event )
576 if ( !( event->possibleActions() & Qt::CopyAction ) )
581 const QList<int> indexes = existingMapLayerFromMimeData( event->mimeData(), handledUris );
582 if ( !indexes.isEmpty() )
585 event->setDropAction( Qt::CopyAction );
591 const QStringList uris = compatibleUrisFromMimeData( mParameter, event->mimeData(), handledUris );
592 if ( !uris.isEmpty() )
595 event->setDropAction( Qt::CopyAction );
600void QgsProcessingMultipleInputPanelWidget::dropEvent( QDropEvent *event )
602 if ( !( event->possibleActions() & Qt::CopyAction ) )
606 const QList<int> indexes = existingMapLayerFromMimeData( event->mimeData(), handledUris );
607 if ( !indexes.isEmpty() )
610 setFocus( Qt::MouseFocusReason );
611 event->setDropAction( Qt::CopyAction );
614 for (
const int i : indexes )
616 mModel->item( i )->setCheckState( Qt::Checked );
618 emit selectionChanged();
622 const QStringList uris = compatibleUrisFromMimeData( mParameter, event->mimeData(), handledUris );
623 if ( !uris.isEmpty() )
625 for (
const QString &uri : uris )
627 addOption( uri, uri,
true );
629 emit selectionChanged();
633void QgsProcessingMultipleInputPanelWidget::populateFromProject(
QgsProject *project )
636 for (
int i = 0; i < mModel->rowCount(); ++i )
638 const QStandardItem *item = mModel->item( i );
639 if ( item->data( Qt::UserRole ) == layerId )
641 bool isChecked = ( item->checkState() == Qt::Checked );
642 mModel->removeRow( i );
645 emit selectionChanged();
654 const QString authid = layer->crs().authid();
656 if ( settings.value( u
"Processing/Configuration/SHOW_CRS_DEF"_s,
true ).toBool() && !authid.isEmpty() )
657 title = u
"%1 [%2]"_s.arg( layer->name(), authid );
659 title = layer->name();
663 QString
id = layer->id();
667 for (
int i = 0; i < mModel->rowCount(); ++i )
670 if ( mModel->item( i )->data( Qt::UserRole ) == layer->id() )
676 else if ( mModel->item( i )->data( Qt::UserRole ) == layer->source() )
678 id = layer->source();
685 addOption(
id, title,
false,
true, icon, tooltip );
688 switch ( mParameter->layerType() )
835QgsProcessingMultipleInputDialog::QgsProcessingMultipleInputDialog(
837 const QVariantList &selectedOptions,
838 const QList<QgsProcessingModelChildParameterSource> &modelSources,
839 QgsProcessingModelAlgorithm *model,
841 Qt::WindowFlags flags
843 : QDialog( parent, flags )
845 setWindowTitle( tr(
"Multiple Selection" ) );
846 QVBoxLayout *vLayout =
new QVBoxLayout();
847 mWidget =
new QgsProcessingMultipleInputPanelWidget( parameter, selectedOptions, modelSources, model,
nullptr );
848 vLayout->addWidget( mWidget );
849 mWidget->buttonBox()->addButton( QDialogButtonBox::Cancel );
850 connect( mWidget->buttonBox(), &QDialogButtonBox::accepted,
this, &QDialog::accept );
851 connect( mWidget->buttonBox(), &QDialogButtonBox::rejected,
this, &QDialog::reject );
852 setLayout( vLayout );
853 setAcceptDrops(
true );
856QVariantList QgsProcessingMultipleInputDialog::selectedOptions()
const
858 return mWidget->selectedOptions();
861void QgsProcessingMultipleInputDialog::setProject(
QgsProject *project )
863 mWidget->setProject( project );
@ File
Files (i.e. non map layer sources, such as text files).
@ TiledScene
Tiled scene layers.
@ Annotation
Annotation layers.
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
@ VectorTile
Vector tile layers.
@ MapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer).
@ VectorAnyGeometry
Any vector layer with geometry.
@ VectorPoint
Vector point layers.
@ VectorPolygon
Vector polygon layers.
@ VectorLine
Vector line layers.
@ PointCloud
Point cloud layers.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
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...
static QIcon iconForLayer(const QgsMapLayer *layer)
Returns the icon corresponding to a specified map layer.
static QString typeToString(Qgis::LayerType type)
Converts a map layer type to a string value.
static QString layerToolTip(const QgsMapLayer *layer)
Returns the consistent tooltip for the given layer.
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.
Qgis::ProcessingSourceType layerType() const
Returns the layer type for layers acceptable by the parameter.
static QList< QgsTiledSceneLayer * > compatibleTiledSceneLayers(QgsProject *project, bool sort=true)
Returns a list of tiled scene layers from a project which are compatible with the processing framewor...
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 QString encodeProviderKeyAndUri(const QString &providerKey, const QString &uri)
Encodes a provider key and layer uri to a single string, for use with decodeProviderKeyAndUri().
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.
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.
Stores settings for use within QGIS.
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 map layer supporting display of tiled scene objects.
Represents a vector layer which manages a vector based dataset.
Implements a map layer that is dedicated to rendering of vector tiles.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...