Make use of nice new tech for the new spatialite layer dialog:

- Use a provider connection combo box to list connections
- Use database provider connection functions to store a
new connection
This commit is contained in:
nirvn 2020-03-16 11:46:01 +07:00 committed by Mathieu Pellerin
parent 2cbdcaf926
commit 35c11288a0
7 changed files with 99 additions and 59 deletions

View File

@ -33,6 +33,17 @@ The QgsProviderConnectionComboBox class is a combo box which displays the list o
%Docstring %Docstring
Constructor for QgsProviderConnectionComboBox, for the specified ``provider``. 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:: .. warning::
The provider must support the connection API methods in its QgsProviderMetadata implementation The provider must support the connection API methods in its QgsProviderMetadata implementation

View File

@ -22,12 +22,16 @@
#include "qgis.h" #include "qgis.h"
#include "qgsapplication.h" #include "qgsapplication.h"
#include "qgsproviderregistry.h" #include "qgsabstractdatabaseproviderconnection.h"
#include "qgisapp.h" // <- for theme icons #include "qgisapp.h" // <- for theme icons
#include "qgsvectorlayer.h" #include "qgsvectorlayer.h"
#include "qgsproject.h" #include "qgsproject.h"
#include "qgscoordinatereferencesystem.h" #include "qgscoordinatereferencesystem.h"
#include "qgsfileutils.h"
#include "qgsprojectionselectiondialog.h" #include "qgsprojectionselectiondialog.h"
#include "qgsproviderconnectionmodel.h"
#include "qgsprovidermetadata.h"
#include "qgsproviderregistry.h"
#include "qgsspatialiteutils.h" #include "qgsspatialiteutils.h"
#include "qgslogger.h" #include "qgslogger.h"
#include "qgssettings.h" #include "qgssettings.h"
@ -51,7 +55,7 @@ QgsNewSpatialiteLayerDialog::QgsNewSpatialiteLayerDialog( QWidget *parent, Qt::W
connect( mGeometryTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewSpatialiteLayerDialog::mGeometryTypeBox_currentIndexChanged ); 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( mTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewSpatialiteLayerDialog::mTypeBox_currentIndexChanged );
connect( pbnFindSRID, &QPushButton::clicked, this, &QgsNewSpatialiteLayerDialog::pbnFindSRID_clicked ); 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::accepted, this, &QgsNewSpatialiteLayerDialog::buttonBox_accepted );
connect( buttonBox, &QDialogButtonBox::rejected, this, &QgsNewSpatialiteLayerDialog::buttonBox_rejected ); connect( buttonBox, &QDialogButtonBox::rejected, this, &QgsNewSpatialiteLayerDialog::buttonBox_rejected );
@ -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( "/mIconFieldInteger.svg" ) ), tr( "Whole number" ), "integer" );
mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldFloat.svg" ) ), tr( "Decimal number" ), "real" ); mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldFloat.svg" ) ), tr( "Decimal number" ), "real" );
// Populate the database list from the stored connections mDatabaseComboBox->setProvider( QStringLiteral( "spatialite" ) );
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();
mOkButton = buttonBox->button( QDialogButtonBox::Ok ); mOkButton = buttonBox->button( QDialogButtonBox::Ok );
mOkButton->setEnabled( false ); mOkButton->setEnabled( false );
@ -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 QString QgsNewSpatialiteLayerDialog::selectedType() const
{ {
return mGeometryTypeBox->currentData( Qt::UserRole ).toString(); return mGeometryTypeBox->currentData( Qt::UserRole ).toString();
@ -206,11 +177,14 @@ void QgsNewSpatialiteLayerDialog::mRemoveAttributeButton_clicked()
void QgsNewSpatialiteLayerDialog::pbnFindSRID_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 // first get list of supported SRID from the selected SpatiaLite database
// to build filter for projection selector // to build filter for projection selector
sqlite3_database_unique_ptr database; sqlite3_database_unique_ptr database;
bool status = true; 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 ) if ( rc != SQLITE_OK )
{ {
QMessageBox::warning( this, tr( "SpatiaLite Database" ), tr( "Unable to open the database" ) ); QMessageBox::warning( this, tr( "SpatiaLite Database" ), tr( "Unable to open the database" ) );
@ -274,10 +248,15 @@ void QgsNewSpatialiteLayerDialog::selectionChanged()
bool QgsNewSpatialiteLayerDialog::createDb() 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() ) if ( dbPath.isEmpty() )
return false; return false;
QgsFileUtils::ensureFileNameHasExtension( dbPath, QStringList() << QStringLiteral( ".sqlite" ) << QLatin1String( ".db" ) << QLatin1String( ".sqlite3" )
<< QLatin1String( ".db3" ) << QLatin1String( ".s3db" ) );
QFile newDb( dbPath ); QFile newDb( dbPath );
if ( newDb.exists() ) if ( newDb.exists() )
{ {
@ -316,24 +295,21 @@ bool QgsNewSpatialiteLayerDialog::createDb()
if ( !fi.exists() ) if ( !fi.exists() )
{ {
pbnFindSRID->setEnabled( false ); pbnFindSRID->setEnabled( false );
return false;
} }
else
QString key = "/SpatiaLite/connections/" + fi.fileName() + "/sqlitepath";
QgsSettings settings;
if ( !settings.contains( key ) )
{ {
settings.setValue( QStringLiteral( "SpatiaLite/connections/selected" ), fi.fileName() + tr( "@" ) + fi.canonicalFilePath() ); QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "spatialite" ) ) };
settings.setValue( key, fi.canonicalFilePath() ); std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn( static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( QStringLiteral( "dbname='%1'" ).arg( dbPath ), QVariantMap() ) ) );
if ( conn )
// Reload connections to refresh browser panel {
QgisApp::instance()->reloadConnections(); md->saveConnection( conn.get(), fi.fileName() );
mDatabaseComboBox->setConnection( fi.fileName() );
pbnFindSRID->setEnabled( true );
return true;
}
} }
pbnFindSRID->setEnabled( true ); return false;
return true;
} }
void QgsNewSpatialiteLayerDialog::buttonBox_accepted() void QgsNewSpatialiteLayerDialog::buttonBox_accepted()
@ -349,6 +325,9 @@ void QgsNewSpatialiteLayerDialog::buttonBox_rejected()
bool QgsNewSpatialiteLayerDialog::apply() bool QgsNewSpatialiteLayerDialog::apply()
{ {
const QgsDataSourceUri dbUri = mDatabaseComboBox->currentConnectionUri();
const QString dbPath = dbUri.database();
// Build up the sql statement for creating the table // Build up the sql statement for creating the table
QString sql = QStringLiteral( "create table %1(" ).arg( quotedIdentifier( leLayerName->text() ) ); QString sql = QStringLiteral( "create table %1(" ).arg( quotedIdentifier( leLayerName->text() ) );
QString delim; QString delim;
@ -369,16 +348,16 @@ bool QgsNewSpatialiteLayerDialog::apply()
// complete the create table statement // complete the create table statement
sql += ')'; sql += ')';
QgsDebugMsg( QStringLiteral( "Creating table in database %1" ).arg( mDatabaseComboBox->currentText() ) ); QgsDebugMsg( QStringLiteral( "Creating table in database %1" ).arg( dbPath ) );
QgsDebugMsg( sql ); QgsDebugMsg( sql );
spatialite_database_unique_ptr database; spatialite_database_unique_ptr database;
int rc = database.open( mDatabaseComboBox->currentText() ); int rc = database.open( dbPath );
if ( rc != SQLITE_OK ) if ( rc != SQLITE_OK )
{ {
QMessageBox::warning( this, QMessageBox::warning( this,
tr( "SpatiaLite Database" ), tr( "SpatiaLite Database" ),
tr( "Unable to open the database: %1" ).arg( mDatabaseComboBox->currentText() ) ); tr( "Unable to open the database: %1" ).arg( dbPath ) );
return false; return false;
} }
@ -433,8 +412,10 @@ bool QgsNewSpatialiteLayerDialog::apply()
} }
const QgsVectorLayer::LayerOptions options { QgsProject::instance()->transformContext() }; const QgsVectorLayer::LayerOptions options { QgsProject::instance()->transformContext() };
QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "dbname='%1' table='%2'%3 sql=" ) const QString uri = QStringLiteral( "dbname='%1' table='%2'%3 sql=" ).arg( dbPath, leLayerName->text(),
.arg( mDatabaseComboBox->currentText(), mGeometryTypeBox->currentIndex() != 0 ? QStringLiteral( "(%1)" ).arg( leGeometryColumn->text() ) : QString() );
QgsVectorLayer *layer = new QgsVectorLayer( QStringLiteral( "%1 table='%2'%3 sql=" )
.arg( mDatabaseComboBox->currentConnectionUri(),
leLayerName->text(), leLayerName->text(),
mGeometryTypeBox->currentIndex() != 0 ? QStringLiteral( "(%1)" ).arg( leGeometryColumn->text() ) : QString() ), mGeometryTypeBox->currentIndex() != 0 ? QStringLiteral( "(%1)" ).arg( leGeometryColumn->text() ) : QString() ),
leLayerName->text(), QStringLiteral( "spatialite" ), options ); leLayerName->text(), QStringLiteral( "spatialite" ), options );

View File

@ -44,7 +44,6 @@ class APP_EXPORT QgsNewSpatialiteLayerDialog: public QDialog, private Ui::QgsNew
void mGeometryTypeBox_currentIndexChanged( int index ); void mGeometryTypeBox_currentIndexChanged( int index );
void mTypeBox_currentIndexChanged( int index ); void mTypeBox_currentIndexChanged( int index );
void pbnFindSRID_clicked(); void pbnFindSRID_clicked();
void toolButtonNewDatabase_clicked();
void nameChanged( const QString & ); void nameChanged( const QString & );
void selectionChanged(); void selectionChanged();
void checkOk(); void checkOk();

View File

@ -19,6 +19,25 @@
QgsProviderConnectionComboBox::QgsProviderConnectionComboBox( const QString &provider, QWidget *parent ) QgsProviderConnectionComboBox::QgsProviderConnectionComboBox( const QString &provider, QWidget *parent )
: QComboBox( 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 ); mModel = new QgsProviderConnectionModel( provider, this );
mSortModel = new QgsProviderConnectionComboBoxSortModel( this ); mSortModel = new QgsProviderConnectionComboBoxSortModel( this );

View File

@ -60,6 +60,16 @@ class GUI_EXPORT QgsProviderConnectionComboBox : public QComboBox
*/ */
explicit QgsProviderConnectionComboBox( const QString &provider, QWidget *parent SIP_TRANSFERTHIS = nullptr ); 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. * Sets whether an optional empty connection ("not set") option is present in the combobox.
* \see allowEmptyConnection() * \see allowEmptyConnection()

View File

@ -70,7 +70,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="mDatabaseComboBox"> <widget class="QgsProviderConnectionComboBox" name="mDatabaseComboBox">
<property name="enabled"> <property name="enabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -448,6 +448,12 @@
<header>qgscollapsiblegroupbox.h</header> <header>qgscollapsiblegroupbox.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>QgsProviderConnectionComboBox</class>
<extends>QWidget</extends>
<header>qgsproviderconnectioncombobox.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>scrollArea</tabstop> <tabstop>scrollArea</tabstop>

View File

@ -116,6 +116,20 @@ class TestQgsProviderConnectionComboBox(unittest.TestCase):
md.deleteConnection('aaa_qgis_test2') 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): def testComboWithEmpty(self):
""" test combobox functionality with empty entry """ """ test combobox functionality with empty entry """
m = QgsProviderConnectionComboBox('ogr') m = QgsProviderConnectionComboBox('ogr')