QGIS API Documentation 3.29.0-Master (006c3c0232)
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
36 QgsBrowserGuiModel *browserModel,
37 bool setFilterByLayerType,
38 QgsMapLayerType layerType,
39 QWidget *parent )
40 : QgsPanelWidget( parent )
41{
42 if ( ! browserModel )
43 {
44 mBrowserModel = new QgsBrowserGuiModel( this );
45 mBrowserModel->initialize();
46 }
47 else
48 {
49 mBrowserModel = browserModel;
50 mBrowserModel->initialize();
51 }
52
53 setupUi( this );
54
55 mBrowserProxyModel.setBrowserModel( mBrowserModel );
56 mBrowserTreeView->setHeaderHidden( true );
57
58 if ( setFilterByLayerType )
59 {
60 // This will also set the (proxy) model
61 setLayerTypeFilter( layerType );
62 }
63 else
64 {
65 mBrowserTreeView->setModel( &mBrowserProxyModel );
66 mBrowserTreeView->setBrowserModel( mBrowserModel );
67 setValid( false );
68 }
69
70 mBrowserTreeView->setBrowserModel( mBrowserModel );
71
72 mWidgetFilter->hide();
73 mLeFilter->setPlaceholderText( tr( "Type here to filter visible items…" ) );
74 // icons from http://www.fatcow.com/free-icons License: CC Attribution 3.0
75
76 QMenu *menu = new QMenu( this );
77 menu->setSeparatorsCollapsible( false );
78 mBtnFilterOptions->setMenu( menu );
79 QAction *action = new QAction( tr( "Case Sensitive" ), menu );
80 action->setData( "case" );
81 action->setCheckable( true );
82 action->setChecked( false );
83 connect( action, &QAction::toggled, this, &QgsDataSourceSelectWidget::setCaseSensitive );
84 menu->addAction( action );
85 QActionGroup *group = new QActionGroup( menu );
86 action = new QAction( tr( "Filter Pattern Syntax" ), group );
87 action->setSeparator( true );
88 menu->addAction( action );
89 action = new QAction( tr( "Normal" ), group );
90 action->setData( QgsBrowserProxyModel::Normal );
91 action->setCheckable( true );
92 action->setChecked( true );
93 menu->addAction( action );
94 action = new QAction( tr( "Wildcard(s)" ), group );
95 action->setData( QgsBrowserProxyModel::Wildcards );
96 action->setCheckable( true );
97 menu->addAction( action );
98 action = new QAction( tr( "Regular Expression" ), group );
100 action->setCheckable( true );
101 menu->addAction( action );
102
103 connect( mActionRefresh, &QAction::triggered, this, [ = ] { refreshModel( QModelIndex() ); } );
104 connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, &QgsDataSourceSelectWidget::onLayerSelected );
105 connect( mBrowserTreeView, &QgsBrowserTreeView::doubleClicked, this, &QgsDataSourceSelectWidget::itemDoubleClicked );
106 connect( mActionCollapse, &QAction::triggered, mBrowserTreeView, &QgsBrowserTreeView::collapseAll );
107 connect( mActionShowFilter, &QAction::triggered, this, &QgsDataSourceSelectWidget::showFilterWidget );
108 connect( mLeFilter, &QgsFilterLineEdit::returnPressed, this, &QgsDataSourceSelectWidget::setFilter );
110 connect( mLeFilter, &QgsFilterLineEdit::textChanged, this, &QgsDataSourceSelectWidget::setFilter );
111 connect( group, &QActionGroup::triggered, this, &QgsDataSourceSelectWidget::setFilterSyntax );
112
113 mBrowserToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
114
115 if ( QgsSettings().value( QStringLiteral( "datasourceSelectFilterVisible" ), false, QgsSettings::Section::Gui ).toBool() )
116 {
117 mActionShowFilter->trigger();
118 }
119}
120
122
124{
125 QgsPanelWidget::showEvent( e );
126 const QString lastSelectedPath( QgsSettings().value( QStringLiteral( "datasourceSelectLastSelectedItem" ),
127 QString(), QgsSettings::Section::Gui ).toString() );
128 if ( ! lastSelectedPath.isEmpty() )
129 {
130 const QModelIndexList items = mBrowserProxyModel.match(
131 mBrowserProxyModel.index( 0, 0 ),
133 QVariant::fromValue( lastSelectedPath ),
134 1,
135 Qt::MatchRecursive );
136 if ( items.count( ) > 0 )
137 {
138 const QModelIndex expandIndex = items.at( 0 );
139 if ( expandIndex.isValid() )
140 {
141 mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::ScrollHint::PositionAtTop );
142 mBrowserTreeView->expand( expandIndex );
143 }
144 }
145 }
146}
147
149{
150 QgsSettings().setValue( QStringLiteral( "datasourceSelectFilterVisible" ), visible, QgsSettings::Section::Gui );
151 mWidgetFilter->setVisible( visible );
152 if ( ! visible )
153 {
154 mLeFilter->setText( QString() );
155 setFilter();
156 }
157 else
158 {
159 mLeFilter->setFocus();
160 }
161}
162
163void QgsDataSourceSelectWidget::setDescription( const QString &description )
164{
165 if ( !description.isEmpty() )
166 {
167 if ( !mDescriptionLabel )
168 {
169 mDescriptionLabel = new QLabel();
170 mDescriptionLabel->setWordWrap( true );
171 mDescriptionLabel->setMargin( 4 );
172 mDescriptionLabel->setTextInteractionFlags( Qt::TextBrowserInteraction );
173 connect( mDescriptionLabel, &QLabel::linkActivated, this, [ = ]( const QString & link )
174 {
175 const QUrl url( link );
176 const QFileInfo file( url.toLocalFile() );
177 if ( file.exists() && !file.isDir() )
178 QgsGui::nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
179 else
180 QDesktopServices::openUrl( url );
181 } );
182 verticalLayout->insertWidget( 1, mDescriptionLabel );
183 }
184 mDescriptionLabel->setText( description );
185 }
186 else
187 {
188 if ( mDescriptionLabel )
189 {
190 verticalLayout->removeWidget( mDescriptionLabel );
191 delete mDescriptionLabel;
192 mDescriptionLabel = nullptr;
193 }
194 }
195}
196
197void QgsDataSourceSelectWidget::expandPath( const QString &path )
198{
199 mBrowserTreeView->expandPath( path );
200}
201
203{
204 const QString filter = mLeFilter->text();
205 mBrowserProxyModel.setFilterString( filter );
206}
207
208
209void QgsDataSourceSelectWidget::refreshModel( const QModelIndex &index )
210{
211
212 QgsDataItem *item = mBrowserModel->dataItem( index );
213 if ( item )
214 {
215 QgsDebugMsgLevel( "path = " + item->path(), 2 );
216 }
217 else
218 {
219 QgsDebugMsg( QStringLiteral( "invalid item" ) );
220 }
221
222 if ( item && ( item->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
223 {
224 mBrowserModel->refresh( index );
225 }
226
227 for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
228 {
229 const QModelIndex idx = mBrowserModel->index( i, 0, index );
230 const QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
231 QgsDataItem *child = mBrowserModel->dataItem( idx );
232
233 // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
234 // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
235 if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & Qgis::BrowserItemCapability::Fast ) )
236 {
237 refreshModel( idx );
238 }
239 else
240 {
241 if ( child && ( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
242 {
243 child->depopulate();
244 }
245 }
246 }
247}
248
249void QgsDataSourceSelectWidget::setValid( bool valid )
250{
251 const bool prev = mIsValid;
252 mIsValid = valid;
253 if ( prev != mIsValid )
254 emit validationChanged( mIsValid );
255
256}
257
258
260{
261 if ( !action )
262 return;
263 mBrowserProxyModel.setFilterSyntax( static_cast< QgsBrowserProxyModel::FilterSyntax >( action->data().toInt() ) );
264}
265
267{
268 mBrowserProxyModel.setFilterCaseSensitivity( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
269}
270
272{
273 mBrowserProxyModel.setFilterByLayerType( true );
274 mBrowserProxyModel.setLayerType( layerType );
275 // reset model and button
276 mBrowserTreeView->setModel( &mBrowserProxyModel );
277 mBrowserTreeView->setBrowserModel( mBrowserModel );
278 setValid( false );
279}
280
282{
283 return mUri;
284}
285
286void QgsDataSourceSelectWidget::onLayerSelected( const QModelIndex &index )
287{
288 bool isLayerCompatible = false;
289 mUri = QgsMimeDataUtils::Uri();
290 if ( index.isValid() )
291 {
292 const QgsDataItem *dataItem( mBrowserProxyModel.dataItem( index ) );
293 if ( dataItem )
294 {
295 const QgsLayerItem *layerItem = qobject_cast<const QgsLayerItem *>( dataItem );
296 if ( layerItem && ( ! mBrowserProxyModel.filterByLayerType() ||
297 ( layerItem->mapLayerType() == mBrowserProxyModel.layerType() ) ) )
298 {
299 isLayerCompatible = true;
300 mUri = layerItem->mimeUris().isEmpty() ? QgsMimeDataUtils::Uri() : layerItem->mimeUris().first();
301 // Store last viewed item
302 QgsSettings().setValue( QStringLiteral( "datasourceSelectLastSelectedItem" ), mBrowserProxyModel.data( index, QgsBrowserGuiModel::PathRole ).toString(), QgsSettings::Section::Gui );
303 }
304 }
305 }
306 setValid( isLayerCompatible );
307 emit selectionChanged();
308}
309
310void QgsDataSourceSelectWidget::itemDoubleClicked( const QModelIndex &index )
311{
312 onLayerSelected( index );
313 if ( mIsValid )
314 emit itemTriggered( uri() );
315}
316
317//
318// QgsDataSourceSelectDialog
319//
320
321QgsDataSourceSelectDialog::QgsDataSourceSelectDialog( QgsBrowserGuiModel *browserModel, bool setFilterByLayerType, QgsMapLayerType layerType, QWidget *parent )
322 : QDialog( parent )
323{
324 setWindowTitle( tr( "Select a Data Source" ) );
325 setObjectName( QStringLiteral( "QgsDataSourceSelectDialog" ) );
327
328 mWidget = new QgsDataSourceSelectWidget( browserModel, setFilterByLayerType, layerType );
329
330 QVBoxLayout *vl = new QVBoxLayout();
331 vl->addWidget( mWidget, 1 );
332 vl->setContentsMargins( 4, 4, 4, 4 );
333 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
334 connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
335 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
336 buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
337 connect( mWidget, &QgsDataSourceSelectWidget::validationChanged, buttonBox->button( QDialogButtonBox::Ok ), &QWidget::setEnabled );
338 connect( mWidget, &QgsDataSourceSelectWidget::itemTriggered, this, &QDialog::accept );
339
340 // pressing escape should reject the dialog
341 connect( mWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
342
343 vl->addWidget( buttonBox );
344 setLayout( vl );
345}
346
348{
349 mWidget->setLayerTypeFilter( layerType );
350}
351
352void QgsDataSourceSelectDialog::setDescription( const QString &description )
353{
354 mWidget->setDescription( description );
355}
356
357void QgsDataSourceSelectDialog::expandPath( const QString &path )
358{
359 mWidget->expandPath( path );
360}
361
363{
364 return mWidget->uri();
365}
366
368{
369 mWidget->showFilterWidget( visible );
370}
371
373{
374 mWidget->setFilterSyntax( syntax );
375}
376
378{
379 mWidget->setCaseSensitive( caseSensitive );
380}
381
383{
384 mWidget->setFilter();
385
386}
@ 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,...
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
@ PathRole
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 setFilterCaseSensitivity(Qt::CaseSensitivity sensitivity)
Sets whether item filtering should be case sensitive.
void setFilterSyntax(FilterSyntax syntax)
Sets the filter syntax.
QgsMapLayerType layerType() const
Returns the layer type to filter the model by.
void setLayerType(QgsMapLayerType type)
Sets the layer type to filter the model by.
bool filterByLayerType() const
Returns true if the model is filtered by map layer type.
void setBrowserModel(QgsBrowserModel *model)
Sets the underlying browser model.
Base class for all items in the model.
Definition: qgsdataitem.h:46
QString path() const
Definition: qgsdataitem.h:354
virtual Qgis::BrowserItemCapabilities capabilities2() const
Returns the capabilities for the data item.
Definition: qgsdataitem.h:303
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 expandPath(const QString &path)
Expands out a file path in the view.
void setCaseSensitive(bool caseSensitive)
Sets filter case sensitivity.
QgsDataSourceSelectDialog(QgsBrowserGuiModel *browserModel=nullptr, bool setFilterByLayerType=false, QgsMapLayerType layerType=QgsMapLayerType::VectorLayer, QWidget *parent=nullptr)
Constructs a QgsDataSourceSelectDialog, optionally filtering by layer type.
void setFilterSyntax(QAction *)
Sets filter syntax.
QgsMimeDataUtils::Uri uri() const
Returns the (possibly invalid) uri of the selected data source.
void setDescription(const QString &description)
Sets a description label.
void setLayerTypeFilter(QgsMapLayerType 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 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.
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.
void setLayerTypeFilter(QgsMapLayerType layerType)
Sets layer type filter to layerType and activates the filtering.
~QgsDataSourceSelectWidget() override
QgsDataSourceSelectWidget(QgsBrowserGuiModel *browserModel=nullptr, bool setFilterByLayerType=false, QgsMapLayerType layerType=QgsMapLayerType::VectorLayer, QWidget *parent=nullptr)
Constructs a QgsDataSourceSelectWidget, optionally filtering by layer type.
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)
Expands out a file path in the view.
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:178
static QgsNative * nativePlatformInterface()
Returns the global native interface, which offers abstraction to the host OS's underlying public inte...
Definition: qgsgui.cpp:73
Item that represents a layer that can be opened with one of the providers.
Definition: qgslayeritem.h:31
QgsMapLayerType mapLayerType() const
Returns QgsMapLayerType.
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:62
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38