Skip to content

Commit

Permalink
Make use of nice new tech for the new spatialite layer dialog:
Browse files Browse the repository at this point in the history
- Use a provider connection combo box to list connections
- Use database provider connection functions to store a
new connection
  • Loading branch information
nirvn committed Mar 16, 2020
1 parent 2cbdcaf commit 35c1128
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 59 deletions.
11 changes: 11 additions & 0 deletions python/gui/auto_generated/qgsproviderconnectioncombobox.sip.in
Expand Up @@ -33,6 +33,17 @@ The QgsProviderConnectionComboBox class is a combo box which displays the list o
%Docstring
Constructor for QgsProviderConnectionComboBox, for the specified ``provider``.

.. warning::

The provider must support the connection API methods in its QgsProviderMetadata implementation
in order for the model to work correctly.
%End


void setProvider( const QString &provider );
%Docstring
Sets the provider to be used.

.. warning::

The provider must support the connection API methods in its QgsProviderMetadata implementation
Expand Down
95 changes: 38 additions & 57 deletions src/app/qgsnewspatialitelayerdialog.cpp
Expand Up @@ -22,12 +22,16 @@

#include "qgis.h"
#include "qgsapplication.h"
#include "qgsproviderregistry.h"
#include "qgsabstractdatabaseproviderconnection.h"
#include "qgisapp.h" // <- for theme icons
#include "qgsvectorlayer.h"
#include "qgsproject.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsfileutils.h"
#include "qgsprojectionselectiondialog.h"
#include "qgsproviderconnectionmodel.h"
#include "qgsprovidermetadata.h"
#include "qgsproviderregistry.h"
#include "qgsspatialiteutils.h"
#include "qgslogger.h"
#include "qgssettings.h"
Expand All @@ -51,7 +55,7 @@ QgsNewSpatialiteLayerDialog::QgsNewSpatialiteLayerDialog( QWidget *parent, Qt::W
connect( mGeometryTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewSpatialiteLayerDialog::mGeometryTypeBox_currentIndexChanged );
connect( mTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewSpatialiteLayerDialog::mTypeBox_currentIndexChanged );
connect( pbnFindSRID, &QPushButton::clicked, this, &QgsNewSpatialiteLayerDialog::pbnFindSRID_clicked );
connect( toolButtonNewDatabase, &QToolButton::clicked, this, &QgsNewSpatialiteLayerDialog::toolButtonNewDatabase_clicked );
connect( toolButtonNewDatabase, &QToolButton::clicked, this, &QgsNewSpatialiteLayerDialog::createDb );
connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsNewSpatialiteLayerDialog::buttonBox_accepted );
connect( buttonBox, &QDialogButtonBox::rejected, this, &QgsNewSpatialiteLayerDialog::buttonBox_rejected );

Expand All @@ -75,20 +79,7 @@ QgsNewSpatialiteLayerDialog::QgsNewSpatialiteLayerDialog( QWidget *parent, Qt::W
mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldInteger.svg" ) ), tr( "Whole number" ), "integer" );
mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldFloat.svg" ) ), tr( "Decimal number" ), "real" );

// Populate the database list from the stored connections
QgsSettings settings;
settings.beginGroup( QStringLiteral( "SpatiaLite/connections" ) );
QStringList keys = settings.childGroups();
QStringList::Iterator it = keys.begin();
mDatabaseComboBox->clear();
while ( it != keys.end() )
{
// retrieving the SQLite DB name and full path
QString text = settings.value( *it + "/sqlitepath", "###unknown###" ).toString();
mDatabaseComboBox->addItem( text );
++it;
}
settings.endGroup();
mDatabaseComboBox->setProvider( QStringLiteral( "spatialite" ) );

mOkButton = buttonBox->button( QDialogButtonBox::Ok );
mOkButton->setEnabled( false );
Expand Down Expand Up @@ -132,26 +123,6 @@ void QgsNewSpatialiteLayerDialog::mTypeBox_currentIndexChanged( int index )
}
}

void QgsNewSpatialiteLayerDialog::toolButtonNewDatabase_clicked()
{
QString fileName = QFileDialog::getSaveFileName( this, tr( "New SpatiaLite Database File" ),
QDir::homePath(),
tr( "SpatiaLite" ) + " (*.sqlite *.db *.sqlite3 *.db3 *.s3db)", nullptr, QFileDialog::DontConfirmOverwrite );

if ( fileName.isEmpty() )
return;

if ( !fileName.endsWith( QLatin1String( ".sqlite" ), Qt::CaseInsensitive ) && !fileName.endsWith( QLatin1String( ".db" ), Qt::CaseInsensitive ) )
{
fileName += QLatin1String( ".sqlite" );
}

mDatabaseComboBox->insertItem( 0, fileName );
mDatabaseComboBox->setCurrentIndex( 0 );

createDb();
}

QString QgsNewSpatialiteLayerDialog::selectedType() const
{
return mGeometryTypeBox->currentData( Qt::UserRole ).toString();
Expand Down Expand Up @@ -206,11 +177,14 @@ void QgsNewSpatialiteLayerDialog::mRemoveAttributeButton_clicked()

void QgsNewSpatialiteLayerDialog::pbnFindSRID_clicked()
{
const QgsDataSourceUri dbUri = mDatabaseComboBox->currentConnectionUri();
const QString dbPath = dbUri.database();

// first get list of supported SRID from the selected SpatiaLite database
// to build filter for projection selector
sqlite3_database_unique_ptr database;
bool status = true;
int rc = database.open_v2( mDatabaseComboBox->currentText(), SQLITE_OPEN_READONLY, nullptr );
int rc = database.open_v2( dbPath, SQLITE_OPEN_READONLY, nullptr );
if ( rc != SQLITE_OK )
{
QMessageBox::warning( this, tr( "SpatiaLite Database" ), tr( "Unable to open the database" ) );
Expand Down Expand Up @@ -274,10 +248,15 @@ void QgsNewSpatialiteLayerDialog::selectionChanged()

bool QgsNewSpatialiteLayerDialog::createDb()
{
QString dbPath = mDatabaseComboBox->currentText();
QString dbPath = QFileDialog::getSaveFileName( this, tr( "New SpatiaLite Database File" ),
QDir::homePath(),
tr( "SpatiaLite" ) + " (*.sqlite *.db *.sqlite3 *.db3 *.s3db)", nullptr, QFileDialog::DontConfirmOverwrite );

if ( dbPath.isEmpty() )
return false;

QgsFileUtils::ensureFileNameHasExtension( dbPath, QStringList() << QStringLiteral( ".sqlite" ) << QLatin1String( ".db" ) << QLatin1String( ".sqlite3" )
<< QLatin1String( ".db3" ) << QLatin1String( ".s3db" ) );
QFile newDb( dbPath );
if ( newDb.exists() )
{
Expand Down Expand Up @@ -316,24 +295,21 @@ bool QgsNewSpatialiteLayerDialog::createDb()
if ( !fi.exists() )
{
pbnFindSRID->setEnabled( false );
return false;
}

QString key = "/SpatiaLite/connections/" + fi.fileName() + "/sqlitepath";

QgsSettings settings;
if ( !settings.contains( key ) )
else
{
settings.setValue( QStringLiteral( "SpatiaLite/connections/selected" ), fi.fileName() + tr( "@" ) + fi.canonicalFilePath() );
settings.setValue( key, fi.canonicalFilePath() );

// Reload connections to refresh browser panel
QgisApp::instance()->reloadConnections();
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "spatialite" ) ) };
std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn( static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( QStringLiteral( "dbname='%1'" ).arg( dbPath ), QVariantMap() ) ) );
if ( conn )
{
md->saveConnection( conn.get(), fi.fileName() );
mDatabaseComboBox->setConnection( fi.fileName() );
pbnFindSRID->setEnabled( true );
return true;
}
}

pbnFindSRID->setEnabled( true );

return true;
return false;
}

void QgsNewSpatialiteLayerDialog::buttonBox_accepted()
Expand All @@ -349,6 +325,9 @@ void QgsNewSpatialiteLayerDialog::buttonBox_rejected()

bool QgsNewSpatialiteLayerDialog::apply()
{
const QgsDataSourceUri dbUri = mDatabaseComboBox->currentConnectionUri();
const QString dbPath = dbUri.database();

// Build up the sql statement for creating the table
QString sql = QStringLiteral( "create table %1(" ).arg( quotedIdentifier( leLayerName->text() ) );
QString delim;
Expand All @@ -369,16 +348,16 @@ bool QgsNewSpatialiteLayerDialog::apply()
// complete the create table statement
sql += ')';

QgsDebugMsg( QStringLiteral( "Creating table in database %1" ).arg( mDatabaseComboBox->currentText() ) );
QgsDebugMsg( QStringLiteral( "Creating table in database %1" ).arg( dbPath ) );
QgsDebugMsg( sql );

spatialite_database_unique_ptr database;
int rc = database.open( mDatabaseComboBox->currentText() );
int rc = database.open( dbPath );
if ( rc != SQLITE_OK )
{
QMessageBox::warning( this,
tr( "SpatiaLite Database" ),
tr( "Unable to open the database: %1" ).arg( mDatabaseComboBox->currentText() ) );
tr( "Unable to open the database: %1" ).arg( dbPath ) );
return false;
}

Expand Down Expand Up @@ -433,8 +412,10 @@ bool QgsNewSpatialiteLayerDialog::apply()
}

const QgsVectorLayer::LayerOptions options { QgsProject::instance()->transformContext() };
QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "dbname='%1' table='%2'%3 sql=" )
.arg( mDatabaseComboBox->currentText(),
const QString uri = QStringLiteral( "dbname='%1' table='%2'%3 sql=" ).arg( dbPath, leLayerName->text(),
mGeometryTypeBox->currentIndex() != 0 ? QStringLiteral( "(%1)" ).arg( leGeometryColumn->text() ) : QString() );
QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "%1 table='%2'%3 sql=" )
.arg( mDatabaseComboBox->currentConnectionUri(),
leLayerName->text(),
mGeometryTypeBox->currentIndex() != 0 ? QStringLiteral( "(%1)" ).arg( leGeometryColumn->text() ) : QString() ),
leLayerName->text(), QStringLiteral( "spatialite" ), options );
Expand Down
1 change: 0 additions & 1 deletion src/app/qgsnewspatialitelayerdialog.h
Expand Up @@ -44,7 +44,6 @@ class APP_EXPORT QgsNewSpatialiteLayerDialog: public QDialog, private Ui::QgsNew
void mGeometryTypeBox_currentIndexChanged( int index );
void mTypeBox_currentIndexChanged( int index );
void pbnFindSRID_clicked();
void toolButtonNewDatabase_clicked();
void nameChanged( const QString & );
void selectionChanged();
void checkOk();
Expand Down
19 changes: 19 additions & 0 deletions src/gui/qgsproviderconnectioncombobox.cpp
Expand Up @@ -19,6 +19,25 @@
QgsProviderConnectionComboBox::QgsProviderConnectionComboBox( const QString &provider, QWidget *parent )
: QComboBox( parent )
{
setProvider( provider );
}

QgsProviderConnectionComboBox::QgsProviderConnectionComboBox( QWidget *parent )
: QComboBox( parent )
{
}

void QgsProviderConnectionComboBox::setProvider( const QString &provider )
{
if ( mSortModel )
{
disconnect( this, static_cast < void ( QComboBox::* )( int ) > ( &QComboBox::activated ), this, &QgsProviderConnectionComboBox::indexChanged );
disconnect( mSortModel, &QAbstractItemModel::rowsInserted, this, &QgsProviderConnectionComboBox::rowsChanged );
disconnect( mSortModel, &QAbstractItemModel::rowsRemoved, this, &QgsProviderConnectionComboBox::rowsChanged );
delete mSortModel;
delete mModel;
}

mModel = new QgsProviderConnectionModel( provider, this );

mSortModel = new QgsProviderConnectionComboBoxSortModel( this );
Expand Down
10 changes: 10 additions & 0 deletions src/gui/qgsproviderconnectioncombobox.h
Expand Up @@ -60,6 +60,16 @@ class GUI_EXPORT QgsProviderConnectionComboBox : public QComboBox
*/
explicit QgsProviderConnectionComboBox( const QString &provider, QWidget *parent SIP_TRANSFERTHIS = nullptr );

explicit QgsProviderConnectionComboBox( QWidget *parent = nullptr ) SIP_SKIP;

/**
* Sets the provider to be used.
*
* \warning The provider must support the connection API methods in its QgsProviderMetadata implementation
* in order for the model to work correctly.
*/
void setProvider( const QString &provider );

/**
* Sets whether an optional empty connection ("not set") option is present in the combobox.
* \see allowEmptyConnection()
Expand Down
8 changes: 7 additions & 1 deletion src/ui/qgsnewspatialitelayerdialogbase.ui
Expand Up @@ -70,7 +70,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="mDatabaseComboBox">
<widget class="QgsProviderConnectionComboBox" name="mDatabaseComboBox">
<property name="enabled">
<bool>true</bool>
</property>
Expand Down Expand Up @@ -448,6 +448,12 @@
<header>qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsProviderConnectionComboBox</class>
<extends>QWidget</extends>
<header>qgsproviderconnectioncombobox.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>scrollArea</tabstop>
Expand Down
14 changes: 14 additions & 0 deletions tests/src/python/test_qgsproviderconnectioncombobox.py
Expand Up @@ -116,6 +116,20 @@ def testCombo(self):

md.deleteConnection('aaa_qgis_test2')

def testComboSetProvider(self):
""" test combobox functionality with empty entry """
m = QgsProviderConnectionComboBox('ogr')

md = QgsProviderRegistry.instance().providerMetadata('ogr')
conn = md.createConnection(self.gpkg_path, {})
md.saveConnection(conn, 'qgis_test_zzz')

self.assertEqual(m.count(), 1)
m.setProvider('ogr')
self.assertEqual(m.count(), 1)

md.deleteConnection('qgis_test_zzz')

def testComboWithEmpty(self):
""" test combobox functionality with empty entry """
m = QgsProviderConnectionComboBox('ogr')
Expand Down

0 comments on commit 35c1128

Please sign in to comment.