QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
qgsdatasourceselectdialog.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsdatasourceselectdialog.cpp - QgsDataSourceSelectDialog
3
4 ---------------------
5 begin : 1.11.2018
6 copyright : (C) 2018 by Alessandro Pasotti
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
18#include "moc_qgsdatasourceselectdialog.cpp"
19
20#include "qgis.h"
21#include "qgsbrowsermodel.h"
22#include "qgsgui.h"
23#include "qgsguiutils.h"
24#include "qgssettings.h"
25#include "qgsnative.h"
26#include "qgslayeritem.h"
27
28#include <QPushButton>
29#include <QMenu>
30#include <QDesktopServices>
31#include <QDialogButtonBox>
32#include <QFileInfo>
33#include <QUrl>
34#include <QActionGroup>
35#include <QDir>
36
38 QgsBrowserGuiModel *browserModel,
39 bool setFilterByLayerType,
40 Qgis::LayerType layerType,
41 QWidget *parent )
42 : QgsPanelWidget( parent )
43{
44 if ( ! browserModel )
45 {
46 mBrowserModel = new QgsBrowserGuiModel( this );
47 mBrowserModel->initialize();
48 }
49 else
50 {
51 mBrowserModel = browserModel;
52 mBrowserModel->initialize();
53 }
54
55 setupUi( this );
56
57 mBrowserProxyModel.setBrowserModel( mBrowserModel );
58 mBrowserTreeView->setHeaderHidden( true );
59
60 if ( setFilterByLayerType )
61 {
62 // This will also set the (proxy) model
63 setLayerTypeFilter( layerType );
64 }
65 else
66 {
67 mBrowserTreeView->setModel( &mBrowserProxyModel );
68 mBrowserTreeView->setBrowserModel( mBrowserModel );
69 setValid( false );
70 }
71
72 mBrowserTreeView->setBrowserModel( mBrowserModel );
73
74 mWidgetFilter->hide();
75 mLeFilter->setPlaceholderText( tr( "Type here to filter visible items…" ) );
76 // icons from http://www.fatcow.com/free-icons License: CC Attribution 3.0
77
78 QMenu *menu = new QMenu( this );
79 menu->setSeparatorsCollapsible( false );
80 mBtnFilterOptions->setMenu( menu );
81 QAction *action = new QAction( tr( "Case Sensitive" ), menu );
82 action->setData( "case" );
83 action->setCheckable( true );
84 action->setChecked( false );
85 connect( action, &QAction::toggled, this, &QgsDataSourceSelectWidget::setCaseSensitive );
86 menu->addAction( action );
87 QActionGroup *group = new QActionGroup( menu );
88 action = new QAction( tr( "Filter Pattern Syntax" ), group );
89 action->setSeparator( true );
90 menu->addAction( action );
91 action = new QAction( tr( "Normal" ), group );
92 action->setData( QgsBrowserProxyModel::Normal );
93 action->setCheckable( true );
94 action->setChecked( true );
95 menu->addAction( action );
96 action = new QAction( tr( "Wildcard(s)" ), group );
97 action->setData( QgsBrowserProxyModel::Wildcards );
98 action->setCheckable( true );
99 menu->addAction( action );
100 action = new QAction( tr( "Regular Expression" ), group );
102 action->setCheckable( true );
103 menu->addAction( action );
104
105 connect( mActionRefresh, &QAction::triggered, this, [ = ] { refreshModel( QModelIndex() ); } );
106 connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, &QgsDataSourceSelectWidget::onLayerSelected );
107 connect( mBrowserTreeView, &QgsBrowserTreeView::doubleClicked, this, &QgsDataSourceSelectWidget::itemDoubleClicked );
108 connect( mActionCollapse, &QAction::triggered, mBrowserTreeView, &QgsBrowserTreeView::collapseAll );
109 connect( mActionShowFilter, &QAction::triggered, this, &QgsDataSourceSelectWidget::showFilterWidget );
110 connect( mLeFilter, &QgsFilterLineEdit::returnPressed, this, &QgsDataSourceSelectWidget::setFilter );
112 connect( mLeFilter, &QgsFilterLineEdit::textChanged, this, &QgsDataSourceSelectWidget::setFilter );
113 connect( group, &QActionGroup::triggered, this, &QgsDataSourceSelectWidget::setFilterSyntax );
114
115 mBrowserToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
116
117 if ( QgsSettings().value( QStringLiteral( "datasourceSelectFilterVisible" ), false, QgsSettings::Section::Gui ).toBool() )
118 {
119 mActionShowFilter->trigger();
120 }
121
122 setAcceptDrops( true );
123}
124
126
128{
129 QgsPanelWidget::showEvent( e );
130 const QString lastSelectedPath( QgsSettings().value( QStringLiteral( "datasourceSelectLastSelectedItem" ),
131 QString(), QgsSettings::Section::Gui ).toString() );
132 if ( ! lastSelectedPath.isEmpty() )
133 {
134 const QModelIndexList items = mBrowserProxyModel.match(
135 mBrowserProxyModel.index( 0, 0 ),
136 static_cast< int >( QgsBrowserModel::CustomRole::Path ),
137 QVariant::fromValue( lastSelectedPath ),
138 1,
139 Qt::MatchRecursive );
140 if ( items.count( ) > 0 )
141 {
142 const QModelIndex expandIndex = items.at( 0 );
143 if ( expandIndex.isValid() )
144 {
145 mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::ScrollHint::PositionAtTop );
146 mBrowserTreeView->expand( expandIndex );
147 }
148 }
149 }
150}
151
152QString QgsDataSourceSelectWidget::acceptableFilePath( QDropEvent *event ) const
153{
154 if ( event->mimeData()->hasUrls() )
155 {
156 const QList< QUrl > urls = event->mimeData()->urls();
157 for ( const QUrl &url : urls )
158 {
159 const QString local = url.toLocalFile();
160 if ( local.isEmpty() )
161 continue;
162
163 if ( QFile::exists( local ) )
164 {
165 return local;
166 }
167 }
168 }
169 return QString();
170}
171
172void QgsDataSourceSelectWidget::dragEnterEvent( QDragEnterEvent *event )
173{
174 const QString filePath = acceptableFilePath( event );
175 if ( !filePath.isEmpty() )
176 {
177 event->acceptProposedAction();
178 }
179 else
180 {
181 event->ignore();
182 }
183}
184
186{
187 const QString filePath = acceptableFilePath( event );
188 if ( !filePath.isEmpty() )
189 {
190 event->acceptProposedAction();
191
192 const QFileInfo fi( filePath );
193 if ( fi.isDir() )
194 {
195 expandPath( filePath, true );
196 }
197 else
198 {
199 expandPath( fi.dir().path(), true );
200 }
201 }
202}
203
205{
206 QgsSettings().setValue( QStringLiteral( "datasourceSelectFilterVisible" ), visible, QgsSettings::Section::Gui );
207 mWidgetFilter->setVisible( visible );
208 if ( ! visible )
209 {
210 mLeFilter->setText( QString() );
211 setFilter();
212 }
213 else
214 {
215 mLeFilter->setFocus();
216 }
217}
218
219void QgsDataSourceSelectWidget::setDescription( const QString &description )
220{
221 if ( !description.isEmpty() )
222 {
223 if ( !mDescriptionLabel )
224 {
225 mDescriptionLabel = new QLabel();
226 mDescriptionLabel->setWordWrap( true );
227 mDescriptionLabel->setMargin( 4 );
228 mDescriptionLabel->setTextInteractionFlags( Qt::TextBrowserInteraction );
229 connect( mDescriptionLabel, &QLabel::linkActivated, this, [ = ]( const QString & link )
230 {
231 const QUrl url( link );
232 const QFileInfo file( url.toLocalFile() );
233 if ( file.exists() && !file.isDir() )
234 QgsGui::nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
235 else
236 QDesktopServices::openUrl( url );
237 } );
238 verticalLayout->insertWidget( 1, mDescriptionLabel );
239 }
240 mDescriptionLabel->setText( description );
241 }
242 else
243 {
244 if ( mDescriptionLabel )
245 {
246 verticalLayout->removeWidget( mDescriptionLabel );
247 delete mDescriptionLabel;
248 mDescriptionLabel = nullptr;
249 }
250 }
251}
252
253void QgsDataSourceSelectWidget::expandPath( const QString &path, bool selectPath )
254{
255 mBrowserTreeView->expandPath( path, selectPath );
256}
257
259{
260 const QString filter = mLeFilter->text();
261 mBrowserProxyModel.setFilterString( filter );
262}
263
264void QgsDataSourceSelectWidget::refreshModel( const QModelIndex &index )
265{
266
267 QgsDataItem *item = mBrowserModel->dataItem( index );
268 if ( item )
269 {
270 QgsDebugMsgLevel( "path = " + item->path(), 2 );
271 }
272 else
273 {
274 QgsDebugMsgLevel( QStringLiteral( "invalid item" ), 2 );
275 }
276
277 if ( item && ( item->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
278 {
279 mBrowserModel->refresh( index );
280 }
281
282 for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
283 {
284 const QModelIndex idx = mBrowserModel->index( i, 0, index );
285 const QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
286 QgsDataItem *child = mBrowserModel->dataItem( idx );
287
288 // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
289 // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
290 if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & Qgis::BrowserItemCapability::Fast ) )
291 {
292 refreshModel( idx );
293 }
294 else
295 {
296 if ( child && ( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
297 {
298 child->depopulate();
299 }
300 }
301 }
302}
303
304void QgsDataSourceSelectWidget::setValid( bool valid )
305{
306 const bool prev = mIsValid;
307 mIsValid = valid;
308 if ( prev != mIsValid )
309 emit validationChanged( mIsValid );
310
311}
312
314{
315 if ( !action )
316 return;
317 mBrowserProxyModel.setFilterSyntax( static_cast< QgsBrowserProxyModel::FilterSyntax >( action->data().toInt() ) );
318}
319
321{
322 mBrowserProxyModel.setFilterCaseSensitivity( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
323}
324
326{
327 mBrowserProxyModel.setFilterByLayerType( true );
328 mBrowserProxyModel.setLayerType( layerType );
329 // reset model and button
330 mBrowserTreeView->setModel( &mBrowserProxyModel );
331 mBrowserTreeView->setBrowserModel( mBrowserModel );
332 setValid( false );
333}
334
336{
337 return mUri;
338}
339
340void QgsDataSourceSelectWidget::onLayerSelected( const QModelIndex &index )
341{
342 bool isLayerCompatible = false;
343 mUri = QgsMimeDataUtils::Uri();
344 if ( index.isValid() )
345 {
346 const QgsDataItem *dataItem( mBrowserProxyModel.dataItem( index ) );
347 if ( dataItem )
348 {
349 const QgsLayerItem *layerItem = qobject_cast<const QgsLayerItem *>( dataItem );
350 if ( layerItem && ( ! mBrowserProxyModel.filterByLayerType() ||
351 ( layerItem->mapLayerType() == mBrowserProxyModel.layerType() ) ) )
352 {
353 isLayerCompatible = true;
354 mUri = layerItem->mimeUris().isEmpty() ? QgsMimeDataUtils::Uri() : layerItem->mimeUris().first();
355 // Store last viewed item
356 QgsSettings().setValue( QStringLiteral( "datasourceSelectLastSelectedItem" ), mBrowserProxyModel.data( index, static_cast< int >( QgsBrowserModel::CustomRole::Path ) ).toString(), QgsSettings::Section::Gui );
357 }
358 }
359 }
360 setValid( isLayerCompatible );
361 emit selectionChanged();
362}
363
364void QgsDataSourceSelectWidget::itemDoubleClicked( const QModelIndex &index )
365{
366 onLayerSelected( index );
367 if ( mIsValid )
368 emit itemTriggered( uri() );
369}
370
371//
372// QgsDataSourceSelectDialog
373//
374
375QgsDataSourceSelectDialog::QgsDataSourceSelectDialog( QgsBrowserGuiModel *browserModel, bool setFilterByLayerType, Qgis::LayerType layerType, QWidget *parent )
376 : QDialog( parent )
377{
378 setWindowTitle( tr( "Select a Data Source" ) );
379 setObjectName( QStringLiteral( "QgsDataSourceSelectDialog" ) );
381
382 mWidget = new QgsDataSourceSelectWidget( browserModel, setFilterByLayerType, layerType );
383
384 QVBoxLayout *vl = new QVBoxLayout();
385 vl->addWidget( mWidget, 1 );
386 vl->setContentsMargins( 4, 4, 4, 4 );
387 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
388 connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
389 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
390 buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
391 connect( mWidget, &QgsDataSourceSelectWidget::validationChanged, buttonBox->button( QDialogButtonBox::Ok ), &QWidget::setEnabled );
392 connect( mWidget, &QgsDataSourceSelectWidget::itemTriggered, this, &QDialog::accept );
393
394 // pressing escape should reject the dialog
395 connect( mWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
396
397 vl->addWidget( buttonBox );
398 setLayout( vl );
399}
400
402{
403 mWidget->setLayerTypeFilter( layerType );
404}
405
406void QgsDataSourceSelectDialog::setDescription( const QString &description )
407{
408 mWidget->setDescription( description );
409}
410
411void QgsDataSourceSelectDialog::expandPath( const QString &path, bool selectPath )
412{
413 mWidget->expandPath( path, selectPath );
414}
415
417{
418 return mWidget->uri();
419}
420
422{
423 mWidget->showFilterWidget( visible );
424}
425
427{
428 mWidget->setFilterSyntax( syntax );
429}
430
432{
433 mWidget->setCaseSensitive( caseSensitive );
434}
435
437{
438 mWidget->setFilter();
439
440}
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
LayerType
Types of layers that can be added to a map.
Definition qgis.h:169
A model for showing available data sources and other items in a structured tree.
QgsDataItem * dataItem(const QModelIndex &idx) const
Returns the data item at the specified index, or nullptr if no item exists at the index.
void refresh(const QString &path)
Refresh item specified by path.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void initialize()
Delayed initialization, needed because the provider registry must be already populated.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
@ Path
Item path used to access path in the tree, see QgsDataItem::mPath.
FilterSyntax
Filter syntax options.
@ RegularExpression
Regular expression filtering.
@ Wildcards
Wildcard filtering.
@ Normal
Standard string filtering.
void setFilterByLayerType(bool enabled)
Sets whether the model is filtered by map layer type.
void setFilterString(const QString &filter)
Sets the filter string to use when filtering items in the model.
QgsDataItem * dataItem(const QModelIndex &index) const
Returns the data item at the specified proxy index, or nullptr if no item exists at the index.
void setLayerType(Qgis::LayerType type)
Sets the layer type to filter the model by.
void setFilterCaseSensitivity(Qt::CaseSensitivity sensitivity)
Sets whether item filtering should be case sensitive.
void setFilterSyntax(FilterSyntax syntax)
Sets the filter syntax.
bool filterByLayerType() const
Returns true if the model is filtered by map layer type.
Qgis::LayerType layerType() const
Returns the layer type to filter the model by.
void setBrowserModel(QgsBrowserModel *model)
Sets the underlying browser model.
Base class for all items in the model.
Definition qgsdataitem.h:46
QString path() const
virtual Qgis::BrowserItemCapabilities capabilities2() const
Returns the capabilities for the data item.
virtual void depopulate()
Remove children recursively and set as not populated. This is used when refreshing collapsed items.
void setFilter()
Apply filter to the model.
void showFilterWidget(bool visible)
Show/hide filter widget.
void setCaseSensitive(bool caseSensitive)
Sets filter case sensitivity.
void setFilterSyntax(QAction *)
Sets filter syntax.
QgsMimeDataUtils::Uri uri() const
Returns the (possibly invalid) uri of the selected data source.
void expandPath(const QString &path, bool selectPath=false)
Expands out a file path in the view.
void setDescription(const QString &description)
Sets a description label.
QgsDataSourceSelectDialog(QgsBrowserGuiModel *browserModel=nullptr, bool setFilterByLayerType=false, Qgis::LayerType layerType=Qgis::LayerType::Vector, QWidget *parent=nullptr)
Constructs a QgsDataSourceSelectDialog, optionally filtering by layer type.
void setLayerTypeFilter(Qgis::LayerType layerType)
Sets layer type filter to layerType and activates the filtering.
The QgsDataSourceSelectWidget class embeds the browser view to select an existing data source.
void expandPath(const QString &path, bool selectPath=false)
Expands out a file path in the view.
void selectionChanged()
Emitted when the current selection changes in the widget.
void itemTriggered(const QgsMimeDataUtils::Uri &uri)
Emitted when an item is triggered, e.g.
void setFilter()
Apply filter to the model.
QgsDataSourceSelectWidget(QgsBrowserGuiModel *browserModel=nullptr, bool setFilterByLayerType=false, Qgis::LayerType layerType=Qgis::LayerType::Vector, QWidget *parent=nullptr)
Constructs a QgsDataSourceSelectWidget, optionally filtering by layer type.
void dropEvent(QDropEvent *event) override
void showFilterWidget(bool visible)
Show/hide filter widget.
void setDescription(const QString &description)
Sets a description label.
void validationChanged(bool isValid)
This signal is emitted whenever the validation status of the widget changes.
void showEvent(QShowEvent *e) override
Scroll to last selected index and expand it's children.
~QgsDataSourceSelectWidget() override
void setCaseSensitive(bool caseSensitive)
Sets filter case sensitivity.
void dragEnterEvent(QDragEnterEvent *event) override
void setFilterSyntax(QAction *)
Sets filter syntax.
QgsMimeDataUtils::Uri uri() const
Returns the (possibly invalid) uri of the selected data source.
void setLayerTypeFilter(Qgis::LayerType layerType)
Sets layer type filter to layerType and activates the filtering.
void cleared()
Emitted when the widget is cleared.
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...
Definition qgsgui.cpp:209
static QgsNative * nativePlatformInterface()
Returns the global native interface, which offers abstraction to the host OS's underlying public inte...
Definition qgsgui.cpp:84
Item that represents a layer that can be opened with one of the providers.
Qgis::LayerType mapLayerType() const
Returns the associated map layer type.
QgsMimeDataUtils::UriList mimeUris() const override
Returns mime URIs for the data item, most data providers will only return a single URI but some data ...
Base class for any widget that can be shown as a inline panel.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39