QGIS API Documentation 3.99.0-Master (e9821da5c6b)
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
43 QgsBrowserGuiModel *browserModel,
44 const QStringList &providersFilter,
45 QWidget *parent
46)
47 : QgsPanelWidget( parent )
48{
49 // Initialize the browser
50 if ( !browserModel )
51 {
52 mBrowserModel = new QgsBrowserGuiModel( this );
53 mBrowserModel->initialize();
54 }
55 else
56 {
57 mBrowserModel = browserModel;
58 mBrowserModel->initialize();
59 }
60
61 setupUi( this );
62
63 mOkButton->hide();
64 mOkButton->setEnabled( false );
65
66 QStringList shownDataItemProvidersFilter;
67
68 const auto providerList { QgsApplication::dataItemProviderRegistry()->providers() };
69 for ( const auto &provider : providerList )
70 {
71 if ( provider->dataProviderKey().isEmpty() )
72 {
73 continue;
74 }
75 if ( !QgsProviderRegistry::instance()->providerMetadata( provider->dataProviderKey() ) )
76 {
77 continue;
78 }
79 if ( provider->capabilities() & Qgis::DataItemProviderCapability::Databases )
80 {
81 if ( providersFilter.isEmpty() || providersFilter.contains( provider->dataProviderKey() ) )
82 {
83 mShownProviders.insert( provider->dataProviderKey() );
84 shownDataItemProvidersFilter.push_back( provider->name() );
85 }
86 }
87 }
88
89 mBrowserToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
90
91 mBrowserProxyModel.setBrowserModel( mBrowserModel );
92 // If a filter was specified but the data provider could not be found
93 // this makes sure no providers are shown instead of ALL of them
94 if ( !providersFilter.isEmpty() && shownDataItemProvidersFilter.isEmpty() )
95 {
96 shownDataItemProvidersFilter = providersFilter;
97 }
98 mBrowserProxyModel.setShownDataItemProviderKeyFilter( shownDataItemProvidersFilter );
99 mBrowserProxyModel.setShowLayers( false );
100 mBrowserTreeView->setHeaderHidden( true );
101 mBrowserTreeView->setModel( &mBrowserProxyModel );
102 mBrowserTreeView->setBrowserModel( mBrowserModel );
103
104 // Connections
105 connect( mNewTableName, &QLineEdit::textChanged, this, [this] {
106 mTableName = mNewTableName->text();
107 emit tableNameChanged( mTableName );
108 updateUri();
109 validate();
110 } );
111
112 connect( mActionRefresh, &QAction::triggered, this, [this] {
113 refreshModel( QModelIndex() );
114 } );
115
116 connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, [this]( const QModelIndex &index ) {
117 if ( index.isValid() )
118 {
119 if ( const QgsDataItem *dataItem = mBrowserProxyModel.dataItem( index ) )
120 {
121 if ( const QgsDataCollectionItem *collectionItem = qobject_cast<const QgsDataCollectionItem *>( dataItem ) )
122 {
123 const QString providerKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( dataItem->providerKey() ) };
124 bool validationRequired { false };
125 const QString oldSchema { mSchemaName };
126
127 if ( mDataProviderKey != providerKey )
128 {
129 mSchemaName.clear();
130 mDataProviderKey = providerKey;
131 emit providerKeyChanged( providerKey );
132 validationRequired = true;
133 }
134
135 if ( collectionItem->layerCollection() )
136 {
137 mIsFilePath = FILESYSTEM_BASED_DATAITEM_PROVIDERS.contains( collectionItem->providerKey() );
138 // Data items for filesystem based items are in the form gpkg://path/to/file.gpkg
139 mSchemaName = mIsFilePath ? collectionItem->path().remove( QRegularExpression( u"^[A-z]+:/"_s ) ) : collectionItem->name(); // it may be cleared
140 mConnectionName = mIsFilePath ? collectionItem->name() : collectionItem->parent()->name();
141 if ( oldSchema != mSchemaName )
142 {
143 emit schemaNameChanged( mSchemaName );
144 // Store last viewed item
145 QgsSettings().setValue( u"newDatabaseTableNameWidgetLastSelectedItem"_s, 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 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
202void 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[u"layerName"_s] = mTableName;
213 uriParts[u"schema"_s] = mSchemaName;
214 uriParts[u"table"_s] = mTableName;
215 if ( mIsFilePath )
216 {
217 uriParts[u"dbname"_s] = 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
257void QgsNewDatabaseTableNameWidget::validate()
258{
259 const bool wasValid { mIsValid };
260 // Check table uniqueness
261 mIsValid = !mDataProviderKey.isEmpty() && mShownProviders.contains( mDataProviderKey ) && !mSchemaName.isEmpty() && !mTableName.isEmpty() && !tableNames().contains( mTableName );
262
263 mValidationError.clear();
264
265 // Whether to show it red
266 bool isError { false };
267
268 if ( !mIsValid )
269 {
270 if ( mTableName.isEmpty() && mSchemaName.isEmpty() )
271 {
272 mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
273 }
274 else if ( !mTableName.isEmpty() && !mSchemaName.isEmpty() && tableNames().contains( mTableName ) )
275 {
276 isError = true;
277 mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
278 }
279 else if ( mSchemaName.isEmpty() )
280 {
281 mValidationError = tr( "Select a database schema" );
282 }
283 else if ( mTableName.isEmpty() )
284 {
285 mValidationError = tr( "Enter a unique name for the new table" );
286 }
287 else if ( tableNames().contains( mTableName ) )
288 {
289 mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
290 }
291 else
292 {
293 mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
294 }
295 }
296
297 mValidationResults->setStyleSheet( isError ? u"* { color: red; }"_s : QString() );
298
299 mValidationResults->setText( mValidationError );
300 mValidationResults->setVisible( !mIsValid );
301 if ( wasValid != mIsValid )
302 {
303 emit validationChanged( mIsValid );
304 }
305}
306
307QStringList QgsNewDatabaseTableNameWidget::tableNames()
308{
309 QStringList tableNames;
310 const QModelIndex index { mBrowserTreeView->currentIndex() };
311 if ( index.isValid() )
312 {
313 QgsDataItem *dataItem { mBrowserProxyModel.dataItem( index ) };
314 if ( dataItem )
315 {
317 if ( !dataProviderKey.isEmpty() )
318 {
319 QgsProviderMetadata *metadata { QgsProviderRegistry::instance()->providerMetadata( dataProviderKey ) };
320 if ( metadata )
321 {
322 QgsDataItem *parentDataItem { mIsFilePath ? dataItem : dataItem->parent() };
323 if ( parentDataItem )
324 {
325 QgsAbstractProviderConnection *conn { metadata->findConnection( parentDataItem->name() ) };
326 if ( conn )
327 {
328 const QString cacheKey { conn->uri() + dataItem->name() };
329 if ( mTableNamesCache.contains( cacheKey ) )
330 {
331 tableNames = mTableNamesCache.value( cacheKey );
332 }
333 else if ( conn && static_cast<QgsAbstractDatabaseProviderConnection *>( conn ) )
334 {
335 const auto tables { static_cast<QgsAbstractDatabaseProviderConnection *>( conn )->tables( dataItem->name() ) };
336 for ( const auto &tp : tables )
337 {
338 tableNames.push_back( tp.tableName() );
339 }
340 mTableNamesCache[cacheKey] = tableNames;
341 }
342 }
343 }
344 }
345 }
346 }
347 }
348 return tableNames;
349}
350
352{
353 return mIsValid;
354}
355
357{
358 return mValidationError;
359}
360
362{
363 QWidget::showEvent( e );
364 const QString lastSelectedPath( QgsSettings().value( u"newDatabaseTableNameWidgetLastSelectedItem"_s, QString(), QgsSettings::Section::Gui ).toString() );
365 if ( !lastSelectedPath.isEmpty() )
366 {
367 const QModelIndexList items = mBrowserProxyModel.match(
368 mBrowserProxyModel.index( 0, 0 ),
369 static_cast<int>( QgsBrowserModel::CustomRole::Path ),
370 QVariant::fromValue( lastSelectedPath ),
371 1,
372 Qt::MatchRecursive
373 );
374 if ( items.count() > 0 )
375 {
376 const QModelIndex expandIndex = items.at( 0 );
377 if ( expandIndex.isValid() )
378 {
379 mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::ScrollHint::PositionAtTop );
380 mBrowserTreeView->expand( expandIndex );
381 }
382 }
383 }
384}
385
386//
387// QgsNewDatabaseTableNameDialog
388//
389QgsNewDatabaseTableNameDialog::QgsNewDatabaseTableNameDialog( QgsBrowserGuiModel *browserModel, const QStringList &providersFilter, QWidget *parent )
390 : QDialog( parent )
391{
392 mWidget = new QgsNewDatabaseTableNameWidget( browserModel, providersFilter );
393 QVBoxLayout *vl = new QVBoxLayout();
394 vl->addWidget( mWidget, 1 );
395 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
396 connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
397 connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
398 buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
399 connect( mWidget, &QgsNewDatabaseTableNameWidget::validationChanged, buttonBox->button( QDialogButtonBox::Ok ), &QWidget::setEnabled );
400 vl->addWidget( buttonBox );
401 setLayout( vl );
402}
403
405{
406 return mWidget->schema();
407}
408
410{
411 return mWidget->uri();
412}
413
415{
416 return mWidget->table();
417}
418
420{
421 return mWidget->dataProviderKey();
422}
423
425{
426 return mWidget->isValid();
427}
428
430{
431 return mWidget->validationError();
432}
@ Databases
Can provides items which corresponds to databases.
Definition qgis.h:999
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
Definition qgis.h:973
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
Definition qgis.h:974
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)