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