QGIS API Documentation 3.41.0-Master (af5edcb665c)
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)
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, [=] {
102 mTableName = mNewTableName->text();
103 emit tableNameChanged( mTableName );
104 updateUri();
105 validate();
106 } );
107
108 connect( mActionRefresh, &QAction::triggered, this, [=] {
109 refreshModel( QModelIndex() );
110 } );
111
112 connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, [=]( const QModelIndex &index ) {
113 if ( index.isValid() )
114 {
115 if ( const QgsDataItem *dataItem = mBrowserProxyModel.dataItem( index ) )
116 {
117 if ( const QgsDataCollectionItem *collectionItem = qobject_cast<const QgsDataCollectionItem *>( dataItem ) )
118 {
119 const QString providerKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( dataItem->providerKey() ) };
120 bool validationRequired { false };
121 const QString oldSchema { mSchemaName };
122
123 if ( mDataProviderKey != providerKey )
124 {
125 mSchemaName.clear();
126 mDataProviderKey = providerKey;
127 emit providerKeyChanged( providerKey );
128 validationRequired = true;
129 }
130
131 if ( collectionItem->layerCollection() )
132 {
133 mIsFilePath = FILESYSTEM_BASED_DATAITEM_PROVIDERS.contains( collectionItem->providerKey() );
134 // Data items for filesystem based items are in the form gpkg://path/to/file.gpkg
135 mSchemaName = mIsFilePath ? collectionItem->path().remove( QRegularExpression( QStringLiteral( "^[A-z]+:/" ) ) ) : collectionItem->name(); // it may be cleared
136 mConnectionName = mIsFilePath ? collectionItem->name() : collectionItem->parent()->name();
137 if ( oldSchema != mSchemaName )
138 {
139 emit schemaNameChanged( mSchemaName );
140 // Store last viewed item
141 QgsSettings().setValue( QStringLiteral( "newDatabaseTableNameWidgetLastSelectedItem" ), mBrowserProxyModel.data( index, static_cast<int>( QgsBrowserModel::CustomRole::Path ) ).toString(), QgsSettings::Section::Gui );
142 validationRequired = true;
143 }
144 }
145
146 if ( validationRequired )
147 {
148 updateUri();
149 validate();
150 }
151 }
152 }
153 }
154 } );
155
156 connect( this, &QgsNewDatabaseTableNameWidget::validationChanged, mOkButton, &QWidget::setEnabled );
157 connect( mOkButton, &QPushButton::clicked, this, &QgsNewDatabaseTableNameWidget::accepted );
158
159 validate();
160}
161
163{
164 mOkButton->setVisible( visible );
165}
166
167void QgsNewDatabaseTableNameWidget::refreshModel( const QModelIndex &index )
168{
169 QgsDataItem *item = mBrowserModel->dataItem( index );
170
171 if ( item && ( item->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
172 {
173 mBrowserModel->refresh( index );
174 }
175
176 for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
177 {
178 const QModelIndex idx = mBrowserModel->index( i, 0, index );
179 const QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
180 QgsDataItem *child = mBrowserModel->dataItem( idx );
181
182 // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
183 // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
184 if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & Qgis::BrowserItemCapability::Fast ) )
185 {
186 refreshModel( idx );
187 }
188 else
189 {
190 if ( child && ( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
191 {
192 child->depopulate();
193 }
194 }
195 }
196}
197
198void QgsNewDatabaseTableNameWidget::updateUri()
199{
200 const QString oldUri { mUri };
201 QgsProviderMetadata *dataProviderMetadata { QgsProviderRegistry::instance()->providerMetadata( mDataProviderKey ) };
202 if ( dataProviderMetadata )
203 {
204 QgsAbstractProviderConnection *conn { dataProviderMetadata->findConnection( mConnectionName ) };
205 if ( conn )
206 {
207 QVariantMap uriParts = dataProviderMetadata->decodeUri( conn->uri() );
208 uriParts[QStringLiteral( "layerName" )] = mTableName;
209 uriParts[QStringLiteral( "schema" )] = mSchemaName;
210 uriParts[QStringLiteral( "table" )] = mTableName;
211 if ( mIsFilePath )
212 {
213 uriParts[QStringLiteral( "dbname" )] = mSchemaName;
214 }
215 mUri = dataProviderMetadata->encodeUri( uriParts );
216 }
217 else
218 {
219 mUri = QString();
220 }
221 }
222 else
223 {
224 mUri = QString();
225 }
226
227 if ( mUri != oldUri )
228 {
229 emit uriChanged( mUri );
230 }
231}
232
234{
235 return mSchemaName;
236}
237
239{
240 return mUri;
241}
242
244{
245 return mTableName;
246}
247
249{
250 return mDataProviderKey;
251}
252
253void QgsNewDatabaseTableNameWidget::validate()
254{
255 const bool wasValid { mIsValid };
256 // Check table uniqueness
257 mIsValid = !mDataProviderKey.isEmpty() && mShownProviders.contains( mDataProviderKey ) && !mSchemaName.isEmpty() && !mTableName.isEmpty() && !tableNames().contains( mTableName );
258
259 mValidationError.clear();
260
261 // Whether to show it red
262 bool isError { false };
263
264 if ( !mIsValid )
265 {
266 if ( mTableName.isEmpty() && mSchemaName.isEmpty() )
267 {
268 mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
269 }
270 else if ( !mTableName.isEmpty() && !mSchemaName.isEmpty() && tableNames().contains( mTableName ) )
271 {
272 isError = true;
273 mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
274 }
275 else if ( mSchemaName.isEmpty() )
276 {
277 mValidationError = tr( "Select a database schema" );
278 }
279 else if ( mTableName.isEmpty() )
280 {
281 mValidationError = tr( "Enter a unique name for the new table" );
282 }
283 else if ( tableNames().contains( mTableName ) )
284 {
285 mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
286 }
287 else
288 {
289 mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
290 }
291 }
292
293 mValidationResults->setStyleSheet( isError ? QStringLiteral( "* { color: red; }" ) : QString() );
294
295 mValidationResults->setText( mValidationError );
296 mValidationResults->setVisible( !mIsValid );
297 if ( wasValid != mIsValid )
298 {
299 emit validationChanged( mIsValid );
300 }
301}
302
303QStringList QgsNewDatabaseTableNameWidget::tableNames()
304{
305 QStringList tableNames;
306 const QModelIndex index { mBrowserTreeView->currentIndex() };
307 if ( index.isValid() )
308 {
309 QgsDataItem *dataItem { mBrowserProxyModel.dataItem( index ) };
310 if ( dataItem )
311 {
312 const QString dataProviderKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( dataItem->providerKey() ) };
313 if ( !dataProviderKey.isEmpty() )
314 {
316 if ( metadata )
317 {
318 QgsDataItem *parentDataItem { mIsFilePath ? dataItem : dataItem->parent() };
319 if ( parentDataItem )
320 {
321 QgsAbstractProviderConnection *conn { metadata->findConnection( parentDataItem->name() ) };
322 if ( conn )
323 {
324 const QString cacheKey { conn->uri() + dataItem->name() };
325 if ( mTableNamesCache.contains( cacheKey ) )
326 {
327 tableNames = mTableNamesCache.value( cacheKey );
328 }
329 else if ( conn && static_cast<QgsAbstractDatabaseProviderConnection *>( conn ) )
330 {
331 const auto tables { static_cast<QgsAbstractDatabaseProviderConnection *>( conn )->tables( dataItem->name() ) };
332 for ( const auto &tp : tables )
333 {
334 tableNames.push_back( tp.tableName() );
335 }
336 mTableNamesCache[cacheKey] = tableNames;
337 }
338 }
339 }
340 }
341 }
342 }
343 }
344 return tableNames;
345}
346
348{
349 return mIsValid;
350}
351
353{
354 return mValidationError;
355}
356
358{
359 QWidget::showEvent( e );
360 const QString lastSelectedPath( QgsSettings().value( QStringLiteral( "newDatabaseTableNameWidgetLastSelectedItem" ), QString(), QgsSettings::Section::Gui ).toString() );
361 if ( !lastSelectedPath.isEmpty() )
362 {
363 const QModelIndexList items = mBrowserProxyModel.match(
364 mBrowserProxyModel.index( 0, 0 ),
365 static_cast<int>( QgsBrowserModel::CustomRole::Path ),
366 QVariant::fromValue( lastSelectedPath ),
367 1,
368 Qt::MatchRecursive
369 );
370 if ( items.count() > 0 )
371 {
372 const QModelIndex expandIndex = items.at( 0 );
373 if ( expandIndex.isValid() )
374 {
375 mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::ScrollHint::PositionAtTop );
376 mBrowserTreeView->expand( expandIndex );
377 }
378 }
379 }
380}
381
382//
383// QgsNewDatabaseTableNameDialog
384//
385QgsNewDatabaseTableNameDialog::QgsNewDatabaseTableNameDialog( QgsBrowserGuiModel *browserModel, const QStringList &providersFilter, QWidget *parent )
386 : QDialog( parent )
387{
388 mWidget = new QgsNewDatabaseTableNameWidget( browserModel, providersFilter );
389 QVBoxLayout *vl = new QVBoxLayout();
390 vl->addWidget( mWidget, 1 );
391 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
392 connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
393 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
394 buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
395 connect( mWidget, &QgsNewDatabaseTableNameWidget::validationChanged, buttonBox->button( QDialogButtonBox::Ok ), &QWidget::setEnabled );
396 vl->addWidget( buttonBox );
397 setLayout( vl );
398}
399
401{
402 return mWidget->schema();
403}
404
406{
407 return mWidget->uri();
408}
409
411{
412 return mWidget->table();
413}
414
416{
417 return mWidget->dataProviderKey();
418}
419
421{
422 return mWidget->isValid();
423}
424
426{
427 return mWidget->validationError();
428}
@ 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.