New database table widget new signals & tests

This commit is contained in:
Alessandro Pasotti 2020-03-10 14:58:10 +01:00
parent d1404ac5ae
commit b42bdddd87
4 changed files with 170 additions and 67 deletions

View File

@ -20,6 +20,11 @@ The table name is validated for uniqueness and the selected
data item provider, schema and table names can be retrieved with
getters.
.. warning::
The data provider that originated the data item provider
must support the connections API
.. versionadded:: 3.14
%End
@ -35,8 +40,9 @@ getters.
Constructs a new QgsNewDatabaseTableNameWidget
:param browserModel: an existing browser model (typically from app), if NULL an instance will be created
:param providersFilter: optional white list of item provider names (not data providers!) that should be
shown in the widget, if not specified all providers data items with database capabilities will be shown
:param providersFilter: optional white list of data provider keys that should be
shown in the widget, if not specified all providers data items with database
capabilities will be shown
:param parent: optional parent for this widget
%End
@ -50,9 +56,9 @@ Returns the currently selected schema for the new table
Returns the current name of the new table
%End
QString dataItemProviderName();
QString dataProviderKey();
%Docstring
Returns the currently selected data item provider name (which is NOT the data provider key!) for the new table
Returns the currently selected data item provider key
%End
bool isValid() const;
@ -88,6 +94,14 @@ This signal is emitted when the user enters a table name
:param tableName: the name of the new table
%End
void providerKeyChanged( const QString &providerKey );
%Docstring
This signal is emitted when the selects a data provider or a schema name
that has a different data provider than the previously selected one.
:param providerKey: the data provider key of the selected schema
%End
};

View File

@ -20,6 +20,8 @@
#include "qgsapplication.h"
#include "qgsdataitemproviderregistry.h"
#include "qgsdataitemprovider.h"
#include "qgsproviderregistry.h"
#include "qgsprovidermetadata.h"
QgsNewDatabaseTableNameWidget::QgsNewDatabaseTableNameWidget(
QgsBrowserGuiModel *browserModel,
@ -28,7 +30,6 @@ QgsNewDatabaseTableNameWidget::QgsNewDatabaseTableNameWidget(
: QWidget( parent )
{
// Initalize the browser
if ( ! browserModel )
{
@ -43,6 +44,8 @@ QgsNewDatabaseTableNameWidget::QgsNewDatabaseTableNameWidget(
setupUi( this );
mValidationResults->setStyleSheet( QStringLiteral( "* { font-weight: bold; color: red; }" ) );
QStringList hiddenProviders
{
QStringLiteral( "special:Favorites" ),
@ -55,15 +58,26 @@ QgsNewDatabaseTableNameWidget::QgsNewDatabaseTableNameWidget(
const auto providerList { QgsApplication::dataItemProviderRegistry()->providers() };
for ( const auto &provider : providerList )
{
if ( provider->dataProviderKey().isEmpty() )
{
hiddenProviders.push_back( provider->name() );
continue;
}
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( provider->dataProviderKey() ) };
if ( ! md )
{
hiddenProviders.push_back( provider->name() );
continue;
}
if ( provider->capabilities() & QgsDataProvider::DataCapability::Database )
{
if ( ! providersFilter.isEmpty() && ! providersFilter.contains( provider->name() ) )
if ( ! providersFilter.isEmpty() && ! providersFilter.contains( provider->dataProviderKey() ) )
{
hiddenProviders.push_back( provider->name() );
}
else
{
mShownProviders.insert( provider->name() );
mShownProviders.insert( provider->dataProviderKey() );
}
}
else
@ -98,26 +112,41 @@ QgsNewDatabaseTableNameWidget::QgsNewDatabaseTableNameWidget(
const QgsDataCollectionItem *collectionItem = qobject_cast<const QgsDataCollectionItem *>( dataItem );
if ( collectionItem )
{
if ( mShownProviders.contains( collectionItem->name() ) )
const QString providerKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( dataItem->providerKey() ) };
if ( mShownProviders.contains( providerKey ) )
{
if ( mDataProviderName != collectionItem->name() )
bool validationRequired { false };
const QString oldSchema { mSchemaName };
if ( mDataProviderKey != providerKey )
{
mSchemaName.clear();
mDataProviderName = collectionItem->name();
emit providerKeyChanged( providerKey );
mDataProviderKey = providerKey;
validate();
}
if ( collectionItem->layerCollection( ) )
{
mSchemaName = collectionItem->name(); // it may be cleared
if ( oldSchema != collectionItem->name() )
{
emit schemaNameChanged( mSchemaName );
validationRequired = true;
}
}
if ( validationRequired )
{
validate();
}
}
else
{
mSchemaName = collectionItem->name();
emit schemaNameChanged( mSchemaName );
}
validate();
}
}
}
} );
mValidationResults->hide();
validate();
}
@ -131,16 +160,17 @@ QString QgsNewDatabaseTableNameWidget::table()
return mTableName;
}
QString QgsNewDatabaseTableNameWidget::dataItemProviderName()
QString QgsNewDatabaseTableNameWidget::dataProviderKey()
{
return mDataProviderName;
return mDataProviderKey;
}
void QgsNewDatabaseTableNameWidget::validate()
{
const bool wasValid { mIsValid };
// Check table uniqueness
mIsValid = ! mDataProviderName.isEmpty() &&
mShownProviders.contains( mDataProviderName ) &&
mIsValid = ! mDataProviderKey.isEmpty() &&
mShownProviders.contains( mDataProviderKey ) &&
! mSchemaName.isEmpty() &&
! mTableName.isEmpty() &&
! tableNames( ).contains( mTableName );
@ -149,27 +179,39 @@ void QgsNewDatabaseTableNameWidget::validate()
if ( ! mIsValid )
{
if ( mTableName.isEmpty() )
if ( mTableName.isEmpty() && mSchemaName.isEmpty() )
{
mValidationError = tr( "Enter a unique name for the new table" );
mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
}
else if ( ! mTableName.isEmpty() &&
! mSchemaName.isEmpty() &&
tableNames( ).contains( mTableName ) )
{
mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
}
else if ( mSchemaName.isEmpty() )
{
mValidationError = tr( "Select a database schema" );
}
else if ( mTableName.isEmpty() )
{
mValidationError = tr( "Enter a unique name for the new table" );
}
else if ( tableNames( ).contains( mTableName ) )
{
mValidationError = tr( "A table named '%1' already exists" ).arg( mTableName );
}
else
{
mValidationError = tr( "Select a schema and enter a unique name for the new table" );
mValidationError = tr( "Select a database schema and enter a unique name for the new table" );
}
}
mValidationResults->setText( mValidationError );
mValidationResults->setVisible( ! mIsValid );
emit validationChanged( mIsValid );
if ( wasValid != mIsValid )
{
emit validationChanged( mIsValid );
}
}
QStringList QgsNewDatabaseTableNameWidget::tableNames()
@ -178,16 +220,34 @@ QStringList QgsNewDatabaseTableNameWidget::tableNames()
QModelIndex index { mBrowserTreeView->currentIndex() };
if ( index.isValid() )
{
for ( int row = 0; row < mBrowserProxyModel.rowCount( ); ++row )
QgsDataItem *dataItem { mBrowserProxyModel.dataItem( index ) };
if ( dataItem )
{
// Column 1 contains the
index = mBrowserProxyModel.index( row, 1, index );
if ( index.isValid() )
const QString dataProviderKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( dataItem->providerKey() ) };
if ( ! dataProviderKey.isEmpty() )
{
const QgsDataItem *dataItem { mBrowserProxyModel.dataItem( index ) };
if ( dataItem )
QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( dataProviderKey ) };
if ( md )
{
tableNames.push_back( dataItem->name() );
QgsDataItem *parentDataItem { dataItem->parent() };
if ( parentDataItem )
{
QgsAbstractProviderConnection *conn { md->findConnection( parentDataItem->name() ) };
const QString cacheKey { conn->uri() + dataItem->name() };
if ( mTableNamesCache.contains( cacheKey ) )
{
tableNames = mTableNamesCache.value( cacheKey );
}
else if ( conn && static_cast<QgsAbstractDatabaseProviderConnection *>( conn ) )
{
const auto tables { static_cast<QgsAbstractDatabaseProviderConnection *>( conn )->tables( dataItem->name() ) };
for ( const auto &tp : tables )
{
tableNames.push_back( tp.tableName() );
}
mTableNamesCache[ cacheKey ] = tableNames;
}
}
}
}
}

View File

@ -34,6 +34,9 @@
* data item provider, schema and table names can be retrieved with
* getters.
*
* \warning The data provider that originated the data item provider
* must support the connections API
*
* \since QGIS 3.14
*/
class GUI_EXPORT QgsNewDatabaseTableNameWidget : public QWidget, private Ui::QgsNewDatabaseTableNameWidget
@ -46,8 +49,9 @@ class GUI_EXPORT QgsNewDatabaseTableNameWidget : public QWidget, private Ui::Qgs
* Constructs a new QgsNewDatabaseTableNameWidget
*
* \param browserModel an existing browser model (typically from app), if NULL an instance will be created
* \param providersFilter optional white list of item provider names (not data providers!) that should be
* shown in the widget, if not specified all providers data items with database capabilities will be shown
* \param providersFilter optional white list of data provider keys that should be
* shown in the widget, if not specified all providers data items with database
* capabilities will be shown
* \param parent optional parent for this widget
*/
explicit QgsNewDatabaseTableNameWidget( QgsBrowserGuiModel *browserModel = nullptr,
@ -65,9 +69,9 @@ class GUI_EXPORT QgsNewDatabaseTableNameWidget : public QWidget, private Ui::Qgs
QString table();
/**
* Returns the currently selected data item provider name (which is NOT the data provider key!) for the new table
* Returns the currently selected data item provider key
*/
QString dataItemProviderName();
QString dataProviderKey();
/**
* Returns TRUE if the widget contains a valid new table name
@ -102,6 +106,14 @@ class GUI_EXPORT QgsNewDatabaseTableNameWidget : public QWidget, private Ui::Qgs
*/
void tableNameChanged( const QString &tableName );
/**
* This signal is emitted when the selects a data provider or a schema name
* that has a different data provider than the previously selected one.
*
* \param providerKey the data provider key of the selected schema
*/
void providerKeyChanged( const QString &providerKey );
private:
@ -109,12 +121,16 @@ class GUI_EXPORT QgsNewDatabaseTableNameWidget : public QWidget, private Ui::Qgs
QgsBrowserGuiModel *mBrowserModel = nullptr;
void validate();
QStringList tableNames();
QString mDataProviderName;
QString mDataProviderKey;
QString mTableName;
QString mSchemaName;
//! List of data provider keys of shown providers
QSet<QString> mShownProviders;
bool mIsValid = false;
QString mValidationError;
//! Table names cache
QMap<QString, QStringList> mTableNamesCache;
// For testing:
friend class TestQgsNewDatabaseTableNameWidget;

View File

@ -30,8 +30,6 @@ class TestQgsNewDatabaseTableNameWidget: public QObject
public:
TestQgsNewDatabaseTableNameWidget() = default;
void testWidget();
private slots:
void initTestCase(); // will be called before the first testfunction is executed.
void cleanupTestCase(); // will be called after the last testfunction was executed.
@ -74,14 +72,14 @@ void TestQgsNewDatabaseTableNameWidget::testWidgetFilters()
QCOMPARE( w->mBrowserProxyModel.rowCount(), 0 );
std::unique_ptr<QgsNewDatabaseTableNameWidget> w2 { qgis::make_unique<QgsNewDatabaseTableNameWidget>( nullptr ) };
QVERIFY( w2->mBrowserProxyModel.rowCount() > 0 );
std::unique_ptr<QgsNewDatabaseTableNameWidget> w3 { qgis::make_unique<QgsNewDatabaseTableNameWidget>( nullptr, QStringList{ "PostGIS" } ) };
std::unique_ptr<QgsNewDatabaseTableNameWidget> w3 { qgis::make_unique<QgsNewDatabaseTableNameWidget>( nullptr, QStringList{ "postgres" } ) };
QVERIFY( w3->mBrowserProxyModel.rowCount() > 0 );
}
void TestQgsNewDatabaseTableNameWidget::testWidgetSignals()
{
std::unique_ptr<QgsNewDatabaseTableNameWidget> w { qgis::make_unique<QgsNewDatabaseTableNameWidget>( nullptr, QStringList{ "PostGIS" } ) };
std::unique_ptr<QgsNewDatabaseTableNameWidget> w { qgis::make_unique<QgsNewDatabaseTableNameWidget>( nullptr, QStringList{ "postgres" } ) };
auto index = w->mBrowserModel->findPath( QStringLiteral( "pg:/PG_1" ) );
QVERIFY( index.isValid() );
@ -93,6 +91,7 @@ void TestQgsNewDatabaseTableNameWidget::testWidgetSignals()
QSignalSpy validationSpy( w.get(), SIGNAL( validationChanged( bool ) ) );
QSignalSpy schemaSpy( w.get(), SIGNAL( schemaNameChanged( QString ) ) );
QSignalSpy tableSpy( w.get(), SIGNAL( tableNameChanged( QString ) ) );
QSignalSpy providerSpy( w.get(), SIGNAL( providerKeyChanged( QString ) ) );
index = w->mBrowserProxyModel.mapToSource( w->mBrowserProxyModel.index( 0, 0 ) );
QVERIFY( index.isValid() );
@ -103,10 +102,12 @@ void TestQgsNewDatabaseTableNameWidget::testWidgetSignals()
QVERIFY( ! w->isValid() );
QCOMPARE( validationSpy.count(), 1 );
auto arguments = validationSpy.takeLast();
QCOMPARE( arguments.at( 0 ).toBool(), false );
QCOMPARE( providerSpy.count(), 1 );
QCOMPARE( tableSpy.count(), 0 );
QCOMPARE( schemaSpy.count(), 0 );
QCOMPARE( validationSpy.count(), 0 );
auto arguments = providerSpy.takeLast();
QCOMPARE( arguments.at( 0 ).toString(), QString( "postgres" ) );
// Find qgis_test schema item
index = w->mBrowserModel->findPath( QStringLiteral( "pg:/PG_1/qgis_test" ), Qt::MatchFlag::MatchStartsWith );
@ -115,9 +116,10 @@ void TestQgsNewDatabaseTableNameWidget::testWidgetSignals()
rect = w->mBrowserTreeView->visualRect( w->mBrowserProxyModel.mapFromSource( index ) );
QVERIFY( rect.isValid() );
QTest::mouseClick( w->mBrowserTreeView->viewport(), Qt::LeftButton, 0, rect.center() );
QCOMPARE( validationSpy.count(), 1 );
arguments = validationSpy.takeLast();
QCOMPARE( arguments.at( 0 ).toBool(), false );
QVERIFY( ! w->isValid() );
QCOMPARE( validationSpy.count(), 0 );
QCOMPARE( schemaSpy.count(), 1 );
arguments = schemaSpy.takeLast();
QCOMPARE( arguments.at( 0 ).toString(), QString( "qgis_test" ) );
@ -126,38 +128,49 @@ void TestQgsNewDatabaseTableNameWidget::testWidgetSignals()
QCOMPARE( tableSpy.count(), 1 );
arguments = tableSpy.takeLast();
QCOMPARE( arguments.at( 0 ).toString(), QString( "someNewTableData" ) );
QCOMPARE( validationSpy.count(), 1 );
arguments = validationSpy.takeLast();
QCOMPARE( arguments.at( 0 ).toBool(), true );
QVERIFY( w->isValid() );
// Test unique
// Test getters
QCOMPARE( w->table(), QString( "someNewTableData" ) );
QCOMPARE( w->schema(), QString( "qgis_test" ) );
QCOMPARE( w->dataProviderKey(), QString( "postgres" ) );
// Test unique and make it invalid again so we get a status change
w->mNewTableName->setText( QStringLiteral( "someData" ) );
QVERIFY( ! w->isValid() );
QCOMPARE( tableSpy.count(), 1 );
arguments = tableSpy.takeLast();
QCOMPARE( arguments.at( 0 ).toString(), QString( "someData" ) );
QCOMPARE( validationSpy.count(), 1 );
arguments = validationSpy.takeLast();
QCOMPARE( arguments.at( 0 ).toBool(), false );
QVERIFY( ! w->isValid() );
// Now select another schema
index = w->mBrowserModel->findPath( QStringLiteral( "pg:/PG_1/public" ), Qt::MatchFlag::MatchStartsWith );
QVERIFY( index.isValid() );
w->mBrowserTreeView->scrollTo( w->mBrowserProxyModel.mapFromSource( index ) );
rect = w->mBrowserTreeView->visualRect( w->mBrowserProxyModel.mapFromSource( index ) );
QVERIFY( rect.isValid() );
QTest::mouseClick( w->mBrowserTreeView->viewport(), Qt::LeftButton, 0, rect.center() );
QCOMPARE( w->schema(), QString( "public" ) );
QVERIFY( w->isValid() );
QCOMPARE( validationSpy.count(), 1 );
arguments = validationSpy.takeLast();
QCOMPARE( arguments.at( 0 ).toBool(), true );
QCOMPARE( schemaSpy.count(), 1 );
arguments = schemaSpy.takeLast();
QCOMPARE( arguments.at( 0 ).toString(), QString( "public" ) );
// Test getters
QCOMPARE( w->table(), QString( "someData" ) );
QCOMPARE( w->schema(), QString( "qgis_test" ) );
QCOMPARE( w->dataItemProviderName(), QString( "postgres" ) );
QCOMPARE( w->schema(), QString( "public" ) );
QCOMPARE( w->dataProviderKey(), QString( "postgres" ) );
}
void TestQgsNewDatabaseTableNameWidget::testWidget()
{
QDialog d;
QVBoxLayout layout;
d.setLayout( &layout );
std::unique_ptr<QgsNewDatabaseTableNameWidget> w { qgis::make_unique<QgsNewDatabaseTableNameWidget>( nullptr, QStringList{ "PostGIS" } ) };
d.layout()->addWidget( w.get() );
d.exec();
}
QGSTEST_MAIN( TestQgsNewDatabaseTableNameWidget )
#include "testqgsnewdatabasetablewidget.moc"