QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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
18
20#include "qgsapplication.h"
22#include "qgsdataitemprovider.h"
24#include "qgsguiutils.h"
25#include "qgsprovidermetadata.h"
26#include "qgsproviderregistry.h"
27#include "qgssettings.h"
28
29#include <QDialogButtonBox>
30#include <QPushButton>
31#include <QRegularExpression>
32#include <QString>
33#include <QTreeWidgetItemIterator>
34
35#include "moc_qgsnewdatabasetablenamewidget.cpp"
36
37using namespace Qt::StringLiterals;
38
39// List of data item provider keys that are filesystem based
40QStringList QgsNewDatabaseTableNameWidget::FILESYSTEM_BASED_DATAITEM_PROVIDERS { u"GPKG"_s, u"spatialite"_s };
41
42QgsNewDatabaseTableNameWidget::QgsNewDatabaseTableNameWidget( QgsBrowserGuiModel *browserModel, const QStringList &providersFilter, QWidget *parent )
43 : QgsPanelWidget( parent )
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, [this] {
102 mTableName = mNewTableName->text();
103 emit tableNameChanged( mTableName );
104 updateUri();
105 validate();
106 } );
107
108 connect( mActionRefresh, &QAction::triggered, this, [this] { refreshModel( QModelIndex() ); } );
109
110 connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, [this]( const QModelIndex &index ) {
111 if ( index.isValid() )
112 {
113 if ( const QgsDataItem *dataItem = mBrowserProxyModel.dataItem( index ) )
114 {
115 if ( const QgsDataCollectionItem *collectionItem = qobject_cast<const QgsDataCollectionItem *>( dataItem ) )
116 {
117 const QString providerKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( dataItem->providerKey() ) };
118 bool validationRequired { false };
119 const QString oldSchema { mSchemaName };
120
121 if ( mDataProviderKey != providerKey )
122 {
123 mSchemaName.clear();
124 mDataProviderKey = providerKey;
125 emit providerKeyChanged( providerKey );
126 validationRequired = true;
127 }
128
129 if ( collectionItem->layerCollection() )
130 {
131 mIsFilePath = FILESYSTEM_BASED_DATAITEM_PROVIDERS.contains( collectionItem->providerKey() );
132 // Data items for filesystem based items are in the form gpkg://path/to/file.gpkg
133 mSchemaName = mIsFilePath ? collectionItem->path().remove( QRegularExpression( u"^[A-z]+:/"_s ) ) : collectionItem->name(); // it may be cleared
134 mConnectionName = mIsFilePath ? collectionItem->name() : collectionItem->parent()->name();
135 if ( oldSchema != mSchemaName )
136 {
137 emit schemaNameChanged( mSchemaName );
138 // Store last viewed item
139 QgsSettings().setValue( u"newDatabaseTableNameWidgetLastSelectedItem"_s, mBrowserProxyModel.data( index, static_cast<int>( QgsBrowserModel::CustomRole::Path ) ).toString(), QgsSettings::Section::Gui );
140 validationRequired = true;
141 }
142 }
143
144 if ( validationRequired )
145 {
146 updateUri();
147 validate();
148 }
149 }
150 }
151 }
152 } );
153
154 connect( this, &QgsNewDatabaseTableNameWidget::validationChanged, mOkButton, &QWidget::setEnabled );
155 connect( mOkButton, &QPushButton::clicked, this, &QgsNewDatabaseTableNameWidget::accepted );
156
157 validate();
158}
159
161{
162 mOkButton->setVisible( visible );
163}
164
165void QgsNewDatabaseTableNameWidget::refreshModel( const QModelIndex &index )
166{
167 QgsDataItem *item = mBrowserModel->dataItem( index );
168
169 if ( item && ( item->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
170 {
171 mBrowserModel->refresh( index );
172 }
173
174 for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
175 {
176 const QModelIndex idx = mBrowserModel->index( i, 0, index );
177 const QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
178 QgsDataItem *child = mBrowserModel->dataItem( idx );
179
180 // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
181 // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
182 if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & Qgis::BrowserItemCapability::Fast ) )
183 {
184 refreshModel( idx );
185 }
186 else
187 {
188 if ( child && ( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
189 {
190 child->depopulate();
191 }
192 }
193 }
194}
195
196void QgsNewDatabaseTableNameWidget::updateUri()
197{
198 const QString oldUri { mUri };
199 QgsProviderMetadata *dataProviderMetadata { QgsProviderRegistry::instance()->providerMetadata( mDataProviderKey ) };
200 if ( dataProviderMetadata )
201 {
202 QgsAbstractProviderConnection *conn { dataProviderMetadata->findConnection( mConnectionName ) };
203 if ( conn )
204 {
205 QVariantMap uriParts = dataProviderMetadata->decodeUri( conn->uri() );
206 uriParts[u"layerName"_s] = mTableName;
207 uriParts[u"schema"_s] = mSchemaName;
208 uriParts[u"table"_s] = mTableName;
209 if ( mIsFilePath )
210 {
211 uriParts[u"dbname"_s] = mSchemaName;
212 }
213 mUri = dataProviderMetadata->encodeUri( uriParts );
214 }
215 else
216 {
217 mUri = QString();
218 }
219 }
220 else
221 {
222 mUri = QString();
223 }
224
225 if ( mUri != oldUri )
226 {
227 emit uriChanged( mUri );
228 }
229}
230
232{
233 return mSchemaName;
234}
235
237{
238 return mUri;
239}
240
242{
243 return mTableName;
244}
245
247{
248 return mDataProviderKey;
249}
250
251void QgsNewDatabaseTableNameWidget::validate()
252{
253 const bool wasValid { mIsValid };
254 // Check table uniqueness
255 mIsValid = !mDataProviderKey.isEmpty() && mShownProviders.contains( mDataProviderKey ) && !mSchemaName.isEmpty() && !mTableName.isEmpty() && !tableNames().contains( mTableName );
256
257 mValidationError.clear();
258
259 // Whether to show it red
260 bool isError { false };
261
262 if ( !mIsValid )
263 {
264 if ( mTableName.isEmpty() && mSchemaName.isEmpty() )
265 {
266 mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
267 }
268 else if ( !mTableName.isEmpty() && !mSchemaName.isEmpty() && tableNames().contains( mTableName ) )
269 {
270 isError = true;
271 mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
272 }
273 else if ( mSchemaName.isEmpty() )
274 {
275 mValidationError = tr( "Select a database schema" );
276 }
277 else if ( mTableName.isEmpty() )
278 {
279 mValidationError = tr( "Enter a unique name for the new table" );
280 }
281 else if ( tableNames().contains( mTableName ) )
282 {
283 mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
284 }
285 else
286 {
287 mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
288 }
289 }
290
291 mValidationResults->setStyleSheet( isError ? u"* { color: red; }"_s : QString() );
292
293 mValidationResults->setText( mValidationError );
294 mValidationResults->setVisible( !mIsValid );
295 if ( wasValid != mIsValid )
296 {
297 emit validationChanged( mIsValid );
298 }
299}
300
301QStringList QgsNewDatabaseTableNameWidget::tableNames()
302{
303 QStringList tableNames;
304 const QModelIndex index { mBrowserTreeView->currentIndex() };
305 if ( index.isValid() )
306 {
307 QgsDataItem *dataItem { mBrowserProxyModel.dataItem( index ) };
308 if ( dataItem )
309 {
311 if ( !dataProviderKey.isEmpty() )
312 {
313 QgsProviderMetadata *metadata { QgsProviderRegistry::instance()->providerMetadata( dataProviderKey ) };
314 if ( metadata )
315 {
316 QgsDataItem *parentDataItem { mIsFilePath ? dataItem : dataItem->parent() };
317 if ( parentDataItem )
318 {
319 QgsAbstractProviderConnection *conn { metadata->findConnection( parentDataItem->name() ) };
320 if ( conn )
321 {
322 const QString cacheKey { conn->uri() + dataItem->name() };
323 if ( mTableNamesCache.contains( cacheKey ) )
324 {
325 tableNames = mTableNamesCache.value( cacheKey );
326 }
327 else if ( conn && static_cast<QgsAbstractDatabaseProviderConnection *>( conn ) )
328 {
329 const auto tables { static_cast<QgsAbstractDatabaseProviderConnection *>( conn )->tables( dataItem->name() ) };
330 for ( const auto &tp : tables )
331 {
332 tableNames.push_back( tp.tableName() );
333 }
334 mTableNamesCache[cacheKey] = tableNames;
335 }
336 }
337 }
338 }
339 }
340 }
341 }
342 return tableNames;
343}
344
346{
347 return mIsValid;
348}
349
351{
352 return mValidationError;
353}
354
356{
357 QWidget::showEvent( e );
358 const QString lastSelectedPath( QgsSettings().value( u"newDatabaseTableNameWidgetLastSelectedItem"_s, QString(), QgsSettings::Section::Gui ).toString() );
359 if ( !lastSelectedPath.isEmpty() )
360 {
361 const QModelIndexList items
362 = mBrowserProxyModel.match( mBrowserProxyModel.index( 0, 0 ), static_cast<int>( QgsBrowserModel::CustomRole::Path ), QVariant::fromValue( lastSelectedPath ), 1, Qt::MatchRecursive );
363 if ( items.count() > 0 )
364 {
365 const QModelIndex expandIndex = items.at( 0 );
366 if ( expandIndex.isValid() )
367 {
368 mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::ScrollHint::PositionAtTop );
369 mBrowserTreeView->expand( expandIndex );
370 }
371 }
372 }
373}
374
375//
376// QgsNewDatabaseTableNameDialog
377//
378QgsNewDatabaseTableNameDialog::QgsNewDatabaseTableNameDialog( QgsBrowserGuiModel *browserModel, const QStringList &providersFilter, QWidget *parent )
379 : QDialog( parent )
380{
381 mWidget = new QgsNewDatabaseTableNameWidget( browserModel, providersFilter );
382 QVBoxLayout *vl = new QVBoxLayout();
383 vl->addWidget( mWidget, 1 );
384 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
385 connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
386 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
387 buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
388 connect( mWidget, &QgsNewDatabaseTableNameWidget::validationChanged, buttonBox->button( QDialogButtonBox::Ok ), &QWidget::setEnabled );
389 vl->addWidget( buttonBox );
390 setLayout( vl );
391}
392
394{
395 return mWidget->schema();
396}
397
399{
400 return mWidget->uri();
401}
402
404{
405 return mWidget->table();
406}
407
409{
410 return mWidget->dataProviderKey();
411}
412
414{
415 return mWidget->isValid();
416}
417
419{
420 return mWidget->validationError();
421}
@ Databases
Can provides items which corresponds to databases.
Definition qgis.h:1006
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
Definition qgis.h:980
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
Definition qgis.h:981
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.
void initialize()
Delayed initialization, needed because the provider registry must be already populated.
@ Path
Item path used to access path in the tree, see QgsDataItem::mPath.
A browser item for collections of data.
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:50
QString name() const
Returns the name of the item (the displayed text for the item).
QgsDataItem * parent() const
Gets item parent.
QString providerKey() const
Returns the provider key that created this item (e.g.
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.
A widget which embeds the browser view to select a DB schema and a new table name.
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.
void providerKeyChanged(const QString &providerKey)
This signal is emitted when the selects a data provider or a schema name that has a different data pr...
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.
void schemaNameChanged(const QString &schemaName)
This signal is emitted when the user selects a schema (or file path for filesystem-based DBs like spa...
QgsPanelWidget(QWidget *parent=nullptr)
Base class for any widget that can be shown as an inline panel.
QgsAbstractProviderConnection * findConnection(const QString &name, bool cached=true)
Searches and returns a (possibly nullptr) connection from the stored provider connections.
virtual QString encodeUri(const QVariantMap &parts) const
Reassembles a provider data source URI from its component paths (e.g.
virtual QVariantMap decodeUri(const QString &uri) const
Breaks a provider data source URI into its component paths (e.g.
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.
Stores settings for use within QGIS.
Definition qgssettings.h:68
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.
const QString cacheKey(const QString &pathIn)