QGIS API Documentation 3.39.0-Master (47f7b3a4989)
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
19#include "qgis.h"
20#include "qgsbrowsermodel.h"
21#include "qgsgui.h"
22#include "qgsguiutils.h"
23#include "qgssettings.h"
24#include "qgsnative.h"
25#include "qgslayeritem.h"
26
27#include <QPushButton>
28#include <QMenu>
29#include <QDesktopServices>
30#include <QDialogButtonBox>
31#include <QFileInfo>
32#include <QUrl>
33#include <QActionGroup>
34#include <QDir>
35
37 QgsBrowserGuiModel *browserModel,
38 bool setFilterByLayerType,
39 Qgis::LayerType layerType,
40 QWidget *parent )
41 : QgsPanelWidget( parent )
42{
43 if ( ! browserModel )
44 {
45 mBrowserModel = new QgsBrowserGuiModel( this );
46 mBrowserModel->initialize();
47 }
48 else
49 {
50 mBrowserModel = browserModel;
51 mBrowserModel->initialize();
52 }
53
54 setupUi( this );
55
56 mBrowserProxyModel.setBrowserModel( mBrowserModel );
57 mBrowserTreeView->setHeaderHidden( true );
58
59 if ( setFilterByLayerType )
60 {
61 // This will also set the (proxy) model
62 setLayerTypeFilter( layerType );
63 }
64 else
65 {
66 mBrowserTreeView->setModel( &mBrowserProxyModel );
67 mBrowserTreeView->setBrowserModel( mBrowserModel );
68 setValid( false );
69 }
70
71 mBrowserTreeView->setBrowserModel( mBrowserModel );
72
73 mWidgetFilter->hide();
74 mLeFilter->setPlaceholderText( tr( "Type here to filter visible items…" ) );
75 // icons from http://www.fatcow.com/free-icons License: CC Attribution 3.0
76
77 QMenu *menu = new QMenu( this );
78 menu->setSeparatorsCollapsible( false );
79 mBtnFilterOptions->setMenu( menu );
80 QAction *action = new QAction( tr( "Case Sensitive" ), menu );
81 action->setData( "case" );
82 action->setCheckable( true );
83 action->setChecked( false );
84 connect( action, &QAction::toggled, this, &QgsDataSourceSelectWidget::setCaseSensitive );
85 menu->addAction( action );
86 QActionGroup *group = new QActionGroup( menu );
87 action = new QAction( tr( "Filter Pattern Syntax" ), group );
88 action->setSeparator( true );
89 menu->addAction( action );
90 action = new QAction( tr( "Normal" ), group );
91 action->setData( QgsBrowserProxyModel::Normal );
92 action->setCheckable( true );
93 action->setChecked( true );
94 menu->addAction( action );
95 action = new QAction( tr( "Wildcard(s)" ), group );
96 action->setData( QgsBrowserProxyModel::Wildcards );
97 action->setCheckable( true );
98 menu->addAction( action );
99 action = new QAction( tr( "Regular Expression" ), group );
101 action->setCheckable( true );
102 menu->addAction( action );
103
104 connect( mActionRefresh, &QAction::triggered, this, [ = ] { refreshModel( QModelIndex() ); } );
105 connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, &QgsDataSourceSelectWidget::onLayerSelected );
106 connect( mBrowserTreeView, &QgsBrowserTreeView::doubleClicked, this, &QgsDataSourceSelectWidget::itemDoubleClicked );
107 connect( mActionCollapse, &QAction::triggered, mBrowserTreeView, &QgsBrowserTreeView::collapseAll );
108 connect( mActionShowFilter, &QAction::triggered, this, &QgsDataSourceSelectWidget::showFilterWidget );
109 connect( mLeFilter, &QgsFilterLineEdit::returnPressed, this, &QgsDataSourceSelectWidget::setFilter );
111 connect( mLeFilter, &QgsFilterLineEdit::textChanged, this, &QgsDataSourceSelectWidget::setFilter );
112 connect( group, &QActionGroup::triggered, this, &QgsDataSourceSelectWidget::setFilterSyntax );
113
114 mBrowserToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
115
116 if ( QgsSettings().value( QStringLiteral( "datasourceSelectFilterVisible" ), false, QgsSettings::Section::Gui ).toBool() )
117 {
118 mActionShowFilter->trigger();
119 }
120
121 setAcceptDrops( true );
122}
123
125
127{
128 QgsPanelWidget::showEvent( e );
129 const QString lastSelectedPath( QgsSettings().value( QStringLiteral( "datasourceSelectLastSelectedItem" ),
130 QString(), QgsSettings::Section::Gui ).toString() );
131 if ( ! lastSelectedPath.isEmpty() )
132 {
133 const QModelIndexList items = mBrowserProxyModel.match(
134 mBrowserProxyModel.index( 0, 0 ),
135 static_cast< int >( QgsBrowserModel::CustomRole::Path ),
136 QVariant::fromValue( lastSelectedPath ),
137 1,
138 Qt::MatchRecursive );
139 if ( items.count( ) > 0 )
140 {
141 const QModelIndex expandIndex = items.at( 0 );
142 if ( expandIndex.isValid() )
143 {
144 mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::ScrollHint::PositionAtTop );
145 mBrowserTreeView->expand( expandIndex );
146 }
147 }
148 }
149}
150
151QString QgsDataSourceSelectWidget::acceptableFilePath( QDropEvent *event ) const
152{
153 if ( event->mimeData()->hasUrls() )
154 {
155 const QList< QUrl > urls = event->mimeData()->urls();
156 for ( const QUrl &url : urls )
157 {
158 const QString local = url.toLocalFile();
159 if ( local.isEmpty() )
160 continue;
161
162 if ( QFile::exists( local ) )
163 {
164 return local;
165 }
166 }
167 }
168 return QString();
169}
170
171void QgsDataSourceSelectWidget::dragEnterEvent( QDragEnterEvent *event )
172{
173 const QString filePath = acceptableFilePath( event );
174 if ( !filePath.isEmpty() )
175 {
176 event->acceptProposedAction();
177 }
178 else
179 {
180 event->ignore();
181 }
182}
183
185{
186 const QString filePath = acceptableFilePath( event );
187 if ( !filePath.isEmpty() )
188 {
189 event->acceptProposedAction();
190
191 const QFileInfo fi( filePath );
192 if ( fi.isDir() )
193 {
194 expandPath( filePath, true );
195 }
196 else
197 {
198 expandPath( fi.dir().path(), true );
199 }
200 }
201}
202
204{
205 QgsSettings().setValue( QStringLiteral( "datasourceSelectFilterVisible" ), visible, QgsSettings::Section::Gui );
206 mWidgetFilter->setVisible( visible );
207 if ( ! visible )
208 {
209 mLeFilter->setText( QString() );
210 setFilter();
211 }
212 else
213 {
214 mLeFilter->setFocus();
215 }
216}
217
218void QgsDataSourceSelectWidget::setDescription( const QString &description )
219{
220 if ( !description.isEmpty() )
221 {
222 if ( !mDescriptionLabel )
223 {
224 mDescriptionLabel = new QLabel();
225 mDescriptionLabel->setWordWrap( true );
226 mDescriptionLabel->setMargin( 4 );
227 mDescriptionLabel->setTextInteractionFlags( Qt::TextBrowserInteraction );
228 connect( mDescriptionLabel, &QLabel::linkActivated, this, [ = ]( const QString & link )
229 {
230 const QUrl url( link );
231 const QFileInfo file( url.toLocalFile() );
232 if ( file.exists() && !file.isDir() )
233 QgsGui::nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
234 else
235 QDesktopServices::openUrl( url );
236 } );
237 verticalLayout->insertWidget( 1, mDescriptionLabel );
238 }
239 mDescriptionLabel->setText( description );
240 }
241 else
242 {
243 if ( mDescriptionLabel )
244 {
245 verticalLayout->removeWidget( mDescriptionLabel );
246 delete mDescriptionLabel;
247 mDescriptionLabel = nullptr;
248 }
249 }
250}
251
252void QgsDataSourceSelectWidget::expandPath( const QString &path, bool selectPath )
253{
254 mBrowserTreeView->expandPath( path, selectPath );
255}
256
258{
259 const QString filter = mLeFilter->text();
260 mBrowserProxyModel.setFilterString( filter );
261}
262
263void QgsDataSourceSelectWidget::refreshModel( const QModelIndex &index )
264{
265
266 QgsDataItem *item = mBrowserModel->dataItem( index );
267 if ( item )
268 {
269 QgsDebugMsgLevel( "path = " + item->path(), 2 );
270 }
271 else
272 {
273 QgsDebugMsgLevel( QStringLiteral( "invalid item" ), 2 );
274 }
275
276 if ( item && ( item->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
277 {
278 mBrowserModel->refresh( index );
279 }
280
281 for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
282 {
283 const QModelIndex idx = mBrowserModel->index( i, 0, index );
284 const QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
285 QgsDataItem *child = mBrowserModel->dataItem( idx );
286
287 // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
288 // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
289 if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & Qgis::BrowserItemCapability::Fast ) )
290 {
291 refreshModel( idx );
292 }
293 else
294 {
295 if ( child && ( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
296 {
297 child->depopulate();
298 }
299 }
300 }
301}
302
303void QgsDataSourceSelectWidget::setValid( bool valid )
304{
305 const bool prev = mIsValid;
306 mIsValid = valid;
307 if ( prev != mIsValid )
308 emit validationChanged( mIsValid );
309
310}
311
313{
314 if ( !action )
315 return;
316 mBrowserProxyModel.setFilterSyntax( static_cast< QgsBrowserProxyModel::FilterSyntax >( action->data().toInt() ) );
317}
318
320{
321 mBrowserProxyModel.setFilterCaseSensitivity( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
322}
323
325{
326 mBrowserProxyModel.setFilterByLayerType( true );
327 mBrowserProxyModel.setLayerType( layerType );
328 // reset model and button
329 mBrowserTreeView->setModel( &mBrowserProxyModel );
330 mBrowserTreeView->setBrowserModel( mBrowserModel );
331 setValid( false );
332}
333
335{
336 return mUri;
337}
338
339void QgsDataSourceSelectWidget::onLayerSelected( const QModelIndex &index )
340{
341 bool isLayerCompatible = false;
342 mUri = QgsMimeDataUtils::Uri();
343 if ( index.isValid() )
344 {
345 const QgsDataItem *dataItem( mBrowserProxyModel.dataItem( index ) );
346 if ( dataItem )
347 {
348 const QgsLayerItem *layerItem = qobject_cast<const QgsLayerItem *>( dataItem );
349 if ( layerItem && ( ! mBrowserProxyModel.filterByLayerType() ||
350 ( layerItem->mapLayerType() == mBrowserProxyModel.layerType() ) ) )
351 {
352 isLayerCompatible = true;
353 mUri = layerItem->mimeUris().isEmpty() ? QgsMimeDataUtils::Uri() : layerItem->mimeUris().first();
354 // Store last viewed item
355 QgsSettings().setValue( QStringLiteral( "datasourceSelectLastSelectedItem" ), mBrowserProxyModel.data( index, static_cast< int >( QgsBrowserModel::CustomRole::Path ) ).toString(), QgsSettings::Section::Gui );
356 }
357 }
358 }
359 setValid( isLayerCompatible );
360 emit selectionChanged();
361}
362
363void QgsDataSourceSelectWidget::itemDoubleClicked( const QModelIndex &index )
364{
365 onLayerSelected( index );
366 if ( mIsValid )
367 emit itemTriggered( uri() );
368}
369
370//
371// QgsDataSourceSelectDialog
372//
373
374QgsDataSourceSelectDialog::QgsDataSourceSelectDialog( QgsBrowserGuiModel *browserModel, bool setFilterByLayerType, Qgis::LayerType layerType, QWidget *parent )
375 : QDialog( parent )
376{
377 setWindowTitle( tr( "Select a Data Source" ) );
378 setObjectName( QStringLiteral( "QgsDataSourceSelectDialog" ) );
380
381 mWidget = new QgsDataSourceSelectWidget( browserModel, setFilterByLayerType, layerType );
382
383 QVBoxLayout *vl = new QVBoxLayout();
384 vl->addWidget( mWidget, 1 );
385 vl->setContentsMargins( 4, 4, 4, 4 );
386 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
387 connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
388 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
389 buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
390 connect( mWidget, &QgsDataSourceSelectWidget::validationChanged, buttonBox->button( QDialogButtonBox::Ok ), &QWidget::setEnabled );
391 connect( mWidget, &QgsDataSourceSelectWidget::itemTriggered, this, &QDialog::accept );
392
393 // pressing escape should reject the dialog
394 connect( mWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
395
396 vl->addWidget( buttonBox );
397 setLayout( vl );
398}
399
401{
402 mWidget->setLayerTypeFilter( layerType );
403}
404
405void QgsDataSourceSelectDialog::setDescription( const QString &description )
406{
407 mWidget->setDescription( description );
408}
409
410void QgsDataSourceSelectDialog::expandPath( const QString &path, bool selectPath )
411{
412 mWidget->expandPath( path, selectPath );
413}
414
416{
417 return mWidget->uri();
418}
419
421{
422 mWidget->showFilterWidget( visible );
423}
424
426{
427 mWidget->setFilterSyntax( syntax );
428}
429
431{
432 mWidget->setCaseSensitive( caseSensitive );
433}
434
436{
437 mWidget->setFilter();
438
439}
@ 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:114
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:194
static QgsNative * nativePlatformInterface()
Returns the global native interface, which offers abstraction to the host OS's underlying public inte...
Definition qgsgui.cpp:79
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