QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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 <QTreeWidgetItemIterator>
33
34#include "moc_qgsnewdatabasetablenamewidget.cpp"
35
36// List of data item provider keys that are filesystem based
37QStringList QgsNewDatabaseTableNameWidget::FILESYSTEM_BASED_DATAITEM_PROVIDERS { QStringLiteral( "GPKG" ), QStringLiteral( "spatialite" ) };
38
40 QgsBrowserGuiModel *browserModel,
41 const QStringList &providersFilter,
42 QWidget *parent
43)
44 : QgsPanelWidget( parent )
45{
46 // Initialize the browser
47 if ( !browserModel )
48 {
49 mBrowserModel = new QgsBrowserGuiModel( this );
50 mBrowserModel->initialize();
51 }
52 else
53 {
54 mBrowserModel = browserModel;
55 mBrowserModel->initialize();
56 }
57
58 setupUi( this );
59
60 mOkButton->hide();
61 mOkButton->setEnabled( false );
62
63 QStringList shownDataItemProvidersFilter;
64
65 const auto providerList { QgsApplication::dataItemProviderRegistry()->providers() };
66 for ( const auto &provider : providerList )
67 {
68 if ( provider->dataProviderKey().isEmpty() )
69 {
70 continue;
71 }
72 if ( !QgsProviderRegistry::instance()->providerMetadata( provider->dataProviderKey() ) )
73 {
74 continue;
75 }
76 if ( provider->capabilities() & Qgis::DataItemProviderCapability::Databases )
77 {
78 if ( providersFilter.isEmpty() || providersFilter.contains( provider->dataProviderKey() ) )
79 {
80 mShownProviders.insert( provider->dataProviderKey() );
81 shownDataItemProvidersFilter.push_back( provider->name() );
82 }
83 }
84 }
85
86 mBrowserToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
87
88 mBrowserProxyModel.setBrowserModel( mBrowserModel );
89 // If a filter was specified but the data provider could not be found
90 // this makes sure no providers are shown instead of ALL of them
91 if ( !providersFilter.isEmpty() && shownDataItemProvidersFilter.isEmpty() )
92 {
93 shownDataItemProvidersFilter = providersFilter;
94 }
95 mBrowserProxyModel.setShownDataItemProviderKeyFilter( shownDataItemProvidersFilter );
96 mBrowserProxyModel.setShowLayers( false );
97 mBrowserTreeView->setHeaderHidden( true );
98 mBrowserTreeView->setModel( &mBrowserProxyModel );
99 mBrowserTreeView->setBrowserModel( mBrowserModel );
100
101 // Connections
102 connect( mNewTableName, &QLineEdit::textChanged, this, [this] {
103 mTableName = mNewTableName->text();
104 emit tableNameChanged( mTableName );
105 updateUri();
106 validate();
107 } );
108
109 connect( mActionRefresh, &QAction::triggered, this, [this] {
110 refreshModel( QModelIndex() );
111 } );
112
113 connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, [this]( const QModelIndex &index ) {
114 if ( index.isValid() )
115 {
116 if ( const QgsDataItem *dataItem = mBrowserProxyModel.dataItem( index ) )
117 {
118 if ( const QgsDataCollectionItem *collectionItem = qobject_cast<const QgsDataCollectionItem *>( dataItem ) )
119 {
120 const QString providerKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( dataItem->providerKey() ) };
121 bool validationRequired { false };
122 const QString oldSchema { mSchemaName };
123
124 if ( mDataProviderKey != providerKey )
125 {
126 mSchemaName.clear();
127 mDataProviderKey = providerKey;
128 emit providerKeyChanged( providerKey );
129 validationRequired = true;
130 }
131
132 if ( collectionItem->layerCollection() )
133 {
134 mIsFilePath = FILESYSTEM_BASED_DATAITEM_PROVIDERS.contains( collectionItem->providerKey() );
135 // Data items for filesystem based items are in the form gpkg://path/to/file.gpkg
136 mSchemaName = mIsFilePath ? collectionItem->path().remove( QRegularExpression( QStringLiteral( "^[A-z]+:/" ) ) ) : collectionItem->name(); // it may be cleared
137 mConnectionName = mIsFilePath ? collectionItem->name() : collectionItem->parent()->name();
138 if ( oldSchema != mSchemaName )
139 {
140 emit schemaNameChanged( mSchemaName );
141 // Store last viewed item
142 QgsSettings().setValue( QStringLiteral( "newDatabaseTableNameWidgetLastSelectedItem" ), mBrowserProxyModel.data( index, static_cast<int>( QgsBrowserModel::CustomRole::Path ) ).toString(), QgsSettings::Section::Gui );
143 validationRequired = true;
144 }
145 }
146
147 if ( validationRequired )
148 {
149 updateUri();
150 validate();
151 }
152 }
153 }
154 }
155 } );
156
157 connect( this, &QgsNewDatabaseTableNameWidget::validationChanged, mOkButton, &QWidget::setEnabled );
158 connect( mOkButton, &QPushButton::clicked, this, &QgsNewDatabaseTableNameWidget::accepted );
159
160 validate();
161}
162
164{
165 mOkButton->setVisible( visible );
166}
167
168void QgsNewDatabaseTableNameWidget::refreshModel( const QModelIndex &index )
169{
170 QgsDataItem *item = mBrowserModel->dataItem( index );
171
172 if ( item && ( item->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
173 {
174 mBrowserModel->refresh( index );
175 }
176
177 for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
178 {
179 const QModelIndex idx = mBrowserModel->index( i, 0, index );
180 const QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
181 QgsDataItem *child = mBrowserModel->dataItem( idx );
182
183 // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
184 // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
185 if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & Qgis::BrowserItemCapability::Fast ) )
186 {
187 refreshModel( idx );
188 }
189 else
190 {
191 if ( child && ( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
192 {
193 child->depopulate();
194 }
195 }
196 }
197}
198
199void QgsNewDatabaseTableNameWidget::updateUri()
200{
201 const QString oldUri { mUri };
202 QgsProviderMetadata *dataProviderMetadata { QgsProviderRegistry::instance()->providerMetadata( mDataProviderKey ) };
203 if ( dataProviderMetadata )
204 {
205 QgsAbstractProviderConnection *conn { dataProviderMetadata->findConnection( mConnectionName ) };
206 if ( conn )
207 {
208 QVariantMap uriParts = dataProviderMetadata->decodeUri( conn->uri() );
209 uriParts[QStringLiteral( "layerName" )] = mTableName;
210 uriParts[QStringLiteral( "schema" )] = mSchemaName;
211 uriParts[QStringLiteral( "table" )] = mTableName;
212 if ( mIsFilePath )
213 {
214 uriParts[QStringLiteral( "dbname" )] = mSchemaName;
215 }
216 mUri = dataProviderMetadata->encodeUri( uriParts );
217 }
218 else
219 {
220 mUri = QString();
221 }
222 }
223 else
224 {
225 mUri = QString();
226 }
227
228 if ( mUri != oldUri )
229 {
230 emit uriChanged( mUri );
231 }
232}
233
235{
236 return mSchemaName;
237}
238
240{
241 return mUri;
242}
243
245{
246 return mTableName;
247}
248
250{
251 return mDataProviderKey;
252}
253
254void QgsNewDatabaseTableNameWidget::validate()
255{
256 const bool wasValid { mIsValid };
257 // Check table uniqueness
258 mIsValid = !mDataProviderKey.isEmpty() && mShownProviders.contains( mDataProviderKey ) && !mSchemaName.isEmpty() && !mTableName.isEmpty() && !tableNames().contains( mTableName );
259
260 mValidationError.clear();
261
262 // Whether to show it red
263 bool isError { false };
264
265 if ( !mIsValid )
266 {
267 if ( mTableName.isEmpty() && mSchemaName.isEmpty() )
268 {
269 mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
270 }
271 else if ( !mTableName.isEmpty() && !mSchemaName.isEmpty() && tableNames().contains( mTableName ) )
272 {
273 isError = true;
274 mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
275 }
276 else if ( mSchemaName.isEmpty() )
277 {
278 mValidationError = tr( "Select a database schema" );
279 }
280 else if ( mTableName.isEmpty() )
281 {
282 mValidationError = tr( "Enter a unique name for the new table" );
283 }
284 else if ( tableNames().contains( mTableName ) )
285 {
286 mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
287 }
288 else
289 {
290 mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
291 }
292 }
293
294 mValidationResults->setStyleSheet( isError ? QStringLiteral( "* { color: red; }" ) : QString() );
295
296 mValidationResults->setText( mValidationError );
297 mValidationResults->setVisible( !mIsValid );
298 if ( wasValid != mIsValid )
299 {
300 emit validationChanged( mIsValid );
301 }
302}
303
304QStringList QgsNewDatabaseTableNameWidget::tableNames()
305{
306 QStringList tableNames;
307 const QModelIndex index { mBrowserTreeView->currentIndex() };
308 if ( index.isValid() )
309 {
310 QgsDataItem *dataItem { mBrowserProxyModel.dataItem( index ) };
311 if ( dataItem )
312 {
314 if ( !dataProviderKey.isEmpty() )
315 {
316 QgsProviderMetadata *metadata { QgsProviderRegistry::instance()->providerMetadata( dataProviderKey ) };
317 if ( metadata )
318 {
319 QgsDataItem *parentDataItem { mIsFilePath ? dataItem : dataItem->parent() };
320 if ( parentDataItem )
321 {
322 QgsAbstractProviderConnection *conn { metadata->findConnection( parentDataItem->name() ) };
323 if ( conn )
324 {
325 const QString cacheKey { conn->uri() + dataItem->name() };
326 if ( mTableNamesCache.contains( cacheKey ) )
327 {
328 tableNames = mTableNamesCache.value( cacheKey );
329 }
330 else if ( conn && static_cast<QgsAbstractDatabaseProviderConnection *>( conn ) )
331 {
332 const auto tables { static_cast<QgsAbstractDatabaseProviderConnection *>( conn )->tables( dataItem->name() ) };
333 for ( const auto &tp : tables )
334 {
335 tableNames.push_back( tp.tableName() );
336 }
337 mTableNamesCache[cacheKey] = tableNames;
338 }
339 }
340 }
341 }
342 }
343 }
344 }
345 return tableNames;
346}
347
349{
350 return mIsValid;
351}
352
354{
355 return mValidationError;
356}
357
359{
360 QWidget::showEvent( e );
361 const QString lastSelectedPath( QgsSettings().value( QStringLiteral( "newDatabaseTableNameWidgetLastSelectedItem" ), QString(), QgsSettings::Section::Gui ).toString() );
362 if ( !lastSelectedPath.isEmpty() )
363 {
364 const QModelIndexList items = mBrowserProxyModel.match(
365 mBrowserProxyModel.index( 0, 0 ),
366 static_cast<int>( QgsBrowserModel::CustomRole::Path ),
367 QVariant::fromValue( lastSelectedPath ),
368 1,
369 Qt::MatchRecursive
370 );
371 if ( items.count() > 0 )
372 {
373 const QModelIndex expandIndex = items.at( 0 );
374 if ( expandIndex.isValid() )
375 {
376 mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::ScrollHint::PositionAtTop );
377 mBrowserTreeView->expand( expandIndex );
378 }
379 }
380 }
381}
382
383//
384// QgsNewDatabaseTableNameDialog
385//
386QgsNewDatabaseTableNameDialog::QgsNewDatabaseTableNameDialog( QgsBrowserGuiModel *browserModel, const QStringList &providersFilter, QWidget *parent )
387 : QDialog( parent )
388{
389 mWidget = new QgsNewDatabaseTableNameWidget( browserModel, providersFilter );
390 QVBoxLayout *vl = new QVBoxLayout();
391 vl->addWidget( mWidget, 1 );
392 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
393 connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
394 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
395 buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
396 connect( mWidget, &QgsNewDatabaseTableNameWidget::validationChanged, buttonBox->button( QDialogButtonBox::Ok ), &QWidget::setEnabled );
397 vl->addWidget( buttonBox );
398 setLayout( vl );
399}
400
402{
403 return mWidget->schema();
404}
405
407{
408 return mWidget->uri();
409}
410
412{
413 return mWidget->table();
414}
415
417{
418 return mWidget->dataProviderKey();
419}
420
422{
423 return mWidget->isValid();
424}
425
427{
428 return mWidget->validationError();
429}
@ Databases
Can provides items which corresponds to databases.
Definition qgis.h:980
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
Definition qgis.h:954
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
Definition qgis.h:955
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:47
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:65
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.