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