QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsnewdatabasetablenamewidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsnewdatabasetablenamewidget.cpp - QgsNewDatabaseTableNameWidget
3 
4  ---------------------
5  begin : 9.3.2020
6  copyright : (C) 2020 by Alessandro Pasotti
7  email : elpaso at itopen dot it
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 
17 #include <QTreeWidgetItemIterator>
18 
20 #include "qgsapplication.h"
22 #include "qgsdataitemprovider.h"
23 #include "qgsproviderregistry.h"
24 #include "qgsprovidermetadata.h"
25 #include "qgssettings.h"
26 #include "qgsguiutils.h"
27 #include "qgsdatacollectionitem.h"
29 
30 #include <QRegularExpression>
31 #include <QDialogButtonBox>
32 #include <QPushButton>
33 
34 // List of data item provider keys that are filesystem based
35 QStringList QgsNewDatabaseTableNameWidget::FILESYSTEM_BASED_DATAITEM_PROVIDERS { QStringLiteral( "GPKG" ), QStringLiteral( "spatialite" ) };
36 
38  QgsBrowserGuiModel *browserModel,
39  const QStringList &providersFilter,
40  QWidget *parent )
41  : QgsPanelWidget( parent )
42 {
43 
44  // Initialize the browser
45  if ( ! browserModel )
46  {
47  mBrowserModel = new QgsBrowserGuiModel( this );
48  mBrowserModel->initialize();
49  }
50  else
51  {
52  mBrowserModel = browserModel;
53  mBrowserModel->initialize();
54  }
55 
56  setupUi( this );
57 
58  mOkButton->hide();
59  mOkButton->setEnabled( false );
60 
61  QStringList shownDataItemProvidersFilter;
62 
63  const auto providerList { QgsApplication::dataItemProviderRegistry()->providers() };
64  for ( const auto &provider : providerList )
65  {
66  if ( provider->dataProviderKey().isEmpty() )
67  {
68  continue;
69  }
70  if ( ! QgsProviderRegistry::instance()->providerMetadata( provider->dataProviderKey() ) )
71  {
72  continue;
73  }
74  if ( provider->capabilities() & QgsDataProvider::DataCapability::Database )
75  {
76  if ( providersFilter.isEmpty() || providersFilter.contains( provider->dataProviderKey() ) )
77  {
78  mShownProviders.insert( provider->dataProviderKey() );
79  shownDataItemProvidersFilter.push_back( provider->name() );
80  }
81  }
82  }
83 
84  mBrowserToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
85 
86  mBrowserProxyModel.setBrowserModel( mBrowserModel );
87  // If a filter was specified but the data provider could not be found
88  // this makes sure no providers are shown instead of ALL of them
89  if ( ! providersFilter.isEmpty() && shownDataItemProvidersFilter.isEmpty() )
90  {
91  shownDataItemProvidersFilter = providersFilter;
92  }
93  mBrowserProxyModel.setShownDataItemProviderKeyFilter( shownDataItemProvidersFilter );
94  mBrowserProxyModel.setShowLayers( false );
95  mBrowserTreeView->setHeaderHidden( true );
96  mBrowserTreeView->setModel( &mBrowserProxyModel );
97  mBrowserTreeView->setBrowserModel( mBrowserModel );
98 
99  // Connections
100  connect( mNewTableName, &QLineEdit::textChanged, this, [ = ]
101  {
102  mTableName = mNewTableName->text();
103  emit tableNameChanged( mTableName );
104  updateUri();
105  validate();
106  } );
107 
108  connect( mActionRefresh, &QAction::triggered, this, [ = ]
109  {
110  refreshModel( QModelIndex() );
111  } );
112 
113  connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, [ = ]( const QModelIndex & index )
114  {
115  if ( index.isValid() )
116  {
117  if ( const QgsDataItem *dataItem = mBrowserProxyModel.dataItem( index ) )
118  {
119  if ( const QgsDataCollectionItem *collectionItem = qobject_cast<const QgsDataCollectionItem *>( dataItem ) )
120  {
121  const QString providerKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( dataItem->providerKey() ) };
122  bool validationRequired { false };
123  const QString oldSchema { mSchemaName };
124 
125  if ( mDataProviderKey != providerKey )
126  {
127  mSchemaName.clear();
128  mDataProviderKey = providerKey;
129  emit providerKeyChanged( providerKey );
130  validationRequired = true;
131  }
132 
133  if ( collectionItem->layerCollection( ) )
134  {
135  mIsFilePath = FILESYSTEM_BASED_DATAITEM_PROVIDERS.contains( collectionItem->providerKey() );
136  // Data items for filesystem based items are in the form gpkg://path/to/file.gpkg
137  mSchemaName = mIsFilePath ? collectionItem->path().remove( QRegularExpression( QStringLiteral( "^[A-z]+:/" ) ) ) : collectionItem->name(); // it may be cleared
138  mConnectionName = mIsFilePath ? collectionItem->name() : collectionItem->parent()->name();
139  if ( oldSchema != mSchemaName )
140  {
141  emit schemaNameChanged( mSchemaName );
142  // Store last viewed item
143  QgsSettings().setValue( QStringLiteral( "newDatabaseTableNameWidgetLastSelectedItem" ),
144  mBrowserProxyModel.data( index, QgsBrowserGuiModel::PathRole ).toString(), QgsSettings::Section::Gui );
145  validationRequired = true;
146  }
147  }
148 
149  if ( validationRequired )
150  {
151  updateUri();
152  validate();
153  }
154  }
155  }
156  }
157  } );
158 
159  connect( this, &QgsNewDatabaseTableNameWidget::validationChanged, mOkButton, &QWidget::setEnabled );
160  connect( mOkButton, &QPushButton::clicked, this, &QgsNewDatabaseTableNameWidget::accepted );
161 
162  validate();
163 }
164 
166 {
167  mOkButton->setVisible( visible );
168 }
169 
170 void QgsNewDatabaseTableNameWidget::refreshModel( const QModelIndex &index )
171 {
172 
173  QgsDataItem *item = mBrowserModel->dataItem( index );
174 
175  if ( item && ( item->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
176  {
177  mBrowserModel->refresh( index );
178  }
179 
180  for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
181  {
182  const QModelIndex idx = mBrowserModel->index( i, 0, index );
183  const QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
184  QgsDataItem *child = mBrowserModel->dataItem( idx );
185 
186  // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
187  // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
188  if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & Qgis::BrowserItemCapability::Fast ) )
189  {
190  refreshModel( idx );
191  }
192  else
193  {
194  if ( child && ( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
195  {
196  child->depopulate();
197  }
198  }
199  }
200 }
201 
202 void QgsNewDatabaseTableNameWidget::updateUri()
203 {
204  const QString oldUri { mUri };
205  QgsProviderMetadata *dataProviderMetadata { QgsProviderRegistry::instance()->providerMetadata( mDataProviderKey ) };
206  if ( dataProviderMetadata )
207  {
208  QgsAbstractProviderConnection *conn { dataProviderMetadata->findConnection( mConnectionName ) };
209  if ( conn )
210  {
211  QVariantMap uriParts = dataProviderMetadata->decodeUri( conn->uri() );
212  uriParts[ QStringLiteral( "layerName" ) ] = mTableName;
213  uriParts[ QStringLiteral( "schema" ) ] = mSchemaName;
214  uriParts[ QStringLiteral( "table" ) ] = mTableName;
215  if ( mIsFilePath )
216  {
217  uriParts[ QStringLiteral( "dbname" ) ] = mSchemaName;
218  }
219  mUri = dataProviderMetadata->encodeUri( uriParts );
220  }
221  else
222  {
223  mUri = QString();
224  }
225  }
226  else
227  {
228  mUri = QString();
229  }
230 
231  if ( mUri != oldUri )
232  {
233  emit uriChanged( mUri );
234  }
235 }
236 
238 {
239  return mSchemaName;
240 }
241 
243 {
244  return mUri;
245 }
246 
248 {
249  return mTableName;
250 }
251 
253 {
254  return mDataProviderKey;
255 }
256 
257 void QgsNewDatabaseTableNameWidget::validate()
258 {
259  const bool wasValid { mIsValid };
260  // Check table uniqueness
261  mIsValid = ! mDataProviderKey.isEmpty() &&
262  mShownProviders.contains( mDataProviderKey ) &&
263  ! mSchemaName.isEmpty() &&
264  ! mTableName.isEmpty() &&
265  ! tableNames( ).contains( mTableName );
266 
267  mValidationError.clear();
268 
269  // Whether to show it red
270  bool isError { false };
271 
272  if ( ! mIsValid )
273  {
274  if ( mTableName.isEmpty() && mSchemaName.isEmpty() )
275  {
276  mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
277  }
278  else if ( ! mTableName.isEmpty() &&
279  ! mSchemaName.isEmpty() &&
280  tableNames( ).contains( mTableName ) )
281  {
282  isError = true;
283  mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
284  }
285  else if ( mSchemaName.isEmpty() )
286  {
287  mValidationError = tr( "Select a database schema" );
288  }
289  else if ( mTableName.isEmpty() )
290  {
291  mValidationError = tr( "Enter a unique name for the new table" );
292  }
293  else if ( tableNames( ).contains( mTableName ) )
294  {
295  mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
296  }
297  else
298  {
299  mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
300  }
301  }
302 
303  mValidationResults->setStyleSheet( isError ?
304  QStringLiteral( "* { color: red; }" ) :
305  QString() );
306 
307  mValidationResults->setText( mValidationError );
308  mValidationResults->setVisible( ! mIsValid );
309  if ( wasValid != mIsValid )
310  {
311  emit validationChanged( mIsValid );
312  }
313 }
314 
315 QStringList QgsNewDatabaseTableNameWidget::tableNames()
316 {
317  QStringList tableNames;
318  const QModelIndex index { mBrowserTreeView->currentIndex() };
319  if ( index.isValid() )
320  {
321  QgsDataItem *dataItem { mBrowserProxyModel.dataItem( index ) };
322  if ( dataItem )
323  {
324  const QString dataProviderKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( dataItem->providerKey() ) };
325  if ( ! dataProviderKey.isEmpty() )
326  {
327  QgsProviderMetadata *metadata { QgsProviderRegistry::instance()->providerMetadata( dataProviderKey ) };
328  if ( metadata )
329  {
330  QgsDataItem *parentDataItem { mIsFilePath ? dataItem : dataItem->parent() };
331  if ( parentDataItem )
332  {
333  QgsAbstractProviderConnection *conn { metadata->findConnection( parentDataItem->name() ) };
334  if ( conn )
335  {
336  const QString cacheKey { conn->uri() + dataItem->name() };
337  if ( mTableNamesCache.contains( cacheKey ) )
338  {
339  tableNames = mTableNamesCache.value( cacheKey );
340  }
341  else if ( conn && static_cast<QgsAbstractDatabaseProviderConnection *>( conn ) )
342  {
343  const auto tables { static_cast<QgsAbstractDatabaseProviderConnection *>( conn )->tables( dataItem->name() ) };
344  for ( const auto &tp : tables )
345  {
346  tableNames.push_back( tp.tableName() );
347  }
348  mTableNamesCache[ cacheKey ] = tableNames;
349  }
350  }
351  }
352  }
353  }
354  }
355  }
356  return tableNames;
357 }
358 
360 {
361  return mIsValid;
362 }
363 
365 {
366  return mValidationError;
367 }
368 
370 {
371  QWidget::showEvent( e );
372  const QString lastSelectedPath( QgsSettings().value( QStringLiteral( "newDatabaseTableNameWidgetLastSelectedItem" ),
373  QString(), QgsSettings::Section::Gui ).toString() );
374  if ( ! lastSelectedPath.isEmpty() )
375  {
376  const QModelIndexList items = mBrowserProxyModel.match(
377  mBrowserProxyModel.index( 0, 0 ),
379  QVariant::fromValue( lastSelectedPath ),
380  1,
381  Qt::MatchRecursive );
382  if ( items.count( ) > 0 )
383  {
384  const QModelIndex expandIndex = items.at( 0 );
385  if ( expandIndex.isValid() )
386  {
387  mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::ScrollHint::PositionAtTop );
388  mBrowserTreeView->expand( expandIndex );
389  }
390  }
391  }
392 }
393 
394 //
395 // QgsNewDatabaseTableNameDialog
396 //
397 QgsNewDatabaseTableNameDialog::QgsNewDatabaseTableNameDialog( QgsBrowserGuiModel *browserModel, const QStringList &providersFilter, QWidget *parent )
398  : QDialog( parent )
399 {
400  mWidget = new QgsNewDatabaseTableNameWidget( browserModel, providersFilter );
401  QVBoxLayout *vl = new QVBoxLayout();
402  vl->addWidget( mWidget, 1 );
403  QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
404  connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
405  connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
406  buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
407  connect( mWidget, &QgsNewDatabaseTableNameWidget::validationChanged, buttonBox->button( QDialogButtonBox::Ok ), &QWidget::setEnabled );
408  vl->addWidget( buttonBox );
409  setLayout( vl );
410 }
411 
413 {
414  return mWidget->schema();
415 }
416 
418 {
419  return mWidget->uri();
420 }
421 
423 {
424  return mWidget->table();
425 }
426 
428 {
429  return mWidget->dataProviderKey();
430 }
431 
433 {
434  return mWidget->isValid();
435 }
436 
438 {
439  return mWidget->validationError();
440 }
QgsBrowserProxyModel::setShowLayers
void setShowLayers(bool showLayers)
Sets show layers to showLayers.
Definition: qgsbrowserproxymodel.cpp:155
QgsNewDatabaseTableNameWidget::table
QString table() const
Returns the current name of the new table.
Definition: qgsnewdatabasetablenamewidget.cpp:247
QgsNewDatabaseTableNameDialog::dataProviderKey
QString dataProviderKey() const
Returns the currently selected data item provider key.
Definition: qgsnewdatabasetablenamewidget.cpp:427
QgsNewDatabaseTableNameWidget::tableNameChanged
void tableNameChanged(const QString &tableName)
This signal is emitted when the user enters a table name.
QgsBrowserModel::initialize
void initialize()
Delayed initialization, needed because the provider registry must be already populated.
Definition: qgsbrowsermodel.cpp:249
qgsdataitemproviderregistry.h
QgsNewDatabaseTableNameWidget::schema
QString schema() const
Returns the currently selected schema or file path (in case of filesystem-based DBs like spatialite o...
Definition: qgsnewdatabasetablenamewidget.cpp:237
QgsDataItem::refresh
virtual void refresh(const QVector< QgsDataItem * > &children)
Refresh the items from a specified list of child items.
Definition: qgsdataitem.cpp:331
QgsNewDatabaseTableNameWidget::isValid
bool isValid() const
Returns true if the widget contains a valid new table name.
Definition: qgsnewdatabasetablenamewidget.cpp:359
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsNewDatabaseTableNameWidget::accepted
void accepted()
Emitted when the OK/accept button is clicked.
QgsDataItem::depopulate
virtual void depopulate()
Remove children recursively and set as not populated. This is used when refreshing collapsed items.
Definition: qgsdataitem.cpp:279
QgsBrowserProxyModel::setBrowserModel
void setBrowserModel(QgsBrowserModel *model)
Sets the underlying browser model.
Definition: qgsbrowserproxymodel.cpp:32
QgsGuiUtils::iconSize
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
Definition: qgsguiutils.cpp:264
QgsNewDatabaseTableNameDialog::uri
QString uri() const
Returns the (possibly blank) string representation of the new table data source URI.
Definition: qgsnewdatabasetablenamewidget.cpp:417
qgsapplication.h
QgsNewDatabaseTableNameWidget::showEvent
void showEvent(QShowEvent *e) override
Scroll to last selected index and expand it's children.
Definition: qgsnewdatabasetablenamewidget.cpp:369
QgsNewDatabaseTableNameWidget::dataProviderKey
QString dataProviderKey() const
Returns the currently selected data item provider key.
Definition: qgsnewdatabasetablenamewidget.cpp:252
qgsnewdatabasetablenamewidget.h
QgsNewDatabaseTableNameWidget::validationChanged
void validationChanged(bool isValid)
This signal is emitted whenever the validation status of the widget changes.
qgsprovidermetadata.h
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:29
QgsNewDatabaseTableNameWidget::setAcceptButtonVisible
void setAcceptButtonVisible(bool visible)
Sets whether the optional "Ok"/accept button should be visible.
Definition: qgsnewdatabasetablenamewidget.cpp:165
qgsproviderregistry.h
QgsAbstractProviderConnection::uri
QString uri() const
Returns the connection data source URI string representation.
Definition: qgsabstractproviderconnection.cpp:38
QgsDataItem::parent
QgsDataItem * parent() const
Gets item parent.
Definition: qgsdataitem.h:330
QgsDataItemProviderRegistry::providers
QList< QgsDataItemProvider * > providers() const
Returns the list of available providers.
Definition: qgsdataitemproviderregistry.cpp:51
QgsApplication::dataItemProviderRegistry
static QgsDataItemProviderRegistry * dataItemProviderRegistry()
Returns the application's data item provider registry, which keeps a list of data item providers that...
Definition: qgsapplication.cpp:2335
QgsNewDatabaseTableNameDialog::schema
QString schema() const
Returns the currently selected schema or file path (in case of filesystem-based DBs like spatialite o...
Definition: qgsnewdatabasetablenamewidget.cpp:412
QgsProviderRegistry::providerMetadata
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.
Definition: qgsproviderregistry.cpp:873
QgsBrowserModel::PathRole
@ PathRole
Item path used to access path in the tree, see QgsDataItem::mPath.
Definition: qgsbrowsermodel.h:71
QgsBrowserProxyModel::setShownDataItemProviderKeyFilter
void setShownDataItemProviderKeyFilter(const QStringList &shownItemsFilter)
Sets a filter to show data items based on QgsDataItem::providerKey() associated with the item.
Definition: qgsbrowserproxymodel.cpp:270
QgsNewDatabaseTableNameWidget::validationError
QString validationError() const
Returns the validation error or an empty string is the widget status is valid.
Definition: qgsnewdatabasetablenamewidget.cpp:364
QgsNewDatabaseTableNameDialog::validationError
QString validationError() const
Returns the validation error or an empty string is the widget status is valid.
Definition: qgsnewdatabasetablenamewidget.cpp:437
QgsProviderMetadata
Holds data provider key, description, and associated shared library file or function pointer informat...
Definition: qgsprovidermetadata.h:177
QgsNewDatabaseTableNameWidget::QgsNewDatabaseTableNameWidget
QgsNewDatabaseTableNameWidget(QgsBrowserGuiModel *browserModel=nullptr, const QStringList &providersFilter=QStringList(), QWidget *parent=nullptr)
Constructs a new QgsNewDatabaseTableNameWidget.
Definition: qgsnewdatabasetablenamewidget.cpp:37
qgsdataitemprovider.h
QgsNewDatabaseTableNameDialog::isValid
bool isValid() const
Returns true if the widget contains a valid new table name.
Definition: qgsnewdatabasetablenamewidget.cpp:432
qgssettings.h
QgsDataItemProviderRegistry::dataProviderKey
QString dataProviderKey(const QString &dataItemProviderName)
Returns the (possibly blank) data provider key for a given data item provider name.
Definition: qgsdataitemproviderregistry.cpp:85
QgsNewDatabaseTableNameWidget::uri
QString uri() const
Returns the (possibly blank) string representation of the new table data source URI.
Definition: qgsnewdatabasetablenamewidget.cpp:242
qgsdatacollectionitem.h
QgsNewDatabaseTableNameDialog::table
QString table() const
Returns the current name of the new table.
Definition: qgsnewdatabasetablenamewidget.cpp:422
QgsDataItem::capabilities2
virtual Qgis::BrowserItemCapabilities capabilities2() const
Returns the capabilities for the data item.
Definition: qgsdataitem.h:303
QgsAbstractProviderConnection
The QgsAbstractProviderConnection provides an interface for data provider connections.
Definition: qgsabstractproviderconnection.h:44
qgsguiutils.h
Qgis::BrowserItemCapability::Fast
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
QgsNewDatabaseTableNameDialog::QgsNewDatabaseTableNameDialog
QgsNewDatabaseTableNameDialog(QgsBrowserGuiModel *browserModel=nullptr, const QStringList &providersFilter=QStringList(), QWidget *parent=nullptr)
Constructs a new QgsNewDatabaseTableNameDialog.
Definition: qgsnewdatabasetablenamewidget.cpp:397
QgsDataItem
Base class for all items in the model.
Definition: qgsdataitem.h:45
qgsabstractdatabaseproviderconnection.h
QgsProviderRegistry::instance
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Definition: qgsproviderregistry.cpp:73
QgsAbstractDatabaseProviderConnection
The QgsAbstractDatabaseProviderConnection class provides common functionality for DB based connection...
Definition: qgsabstractdatabaseproviderconnection.h:44
Qgis::BrowserItemCapability::Fertile
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
QgsBrowserGuiModel
A model for showing available data sources and other items in a structured tree.
Definition: qgsbrowserguimodel.h:36