Skip to content
Permalink
Browse files

Add API to show empty entries in QgsDatabaseSchemaComboBox

  • Loading branch information
nyalldawson committed Mar 10, 2020
1 parent 93c917f commit cf310c5d459d5d2df6a98fd27cd4d28d479208d5
@@ -28,6 +28,11 @@ called.
%End
public:

enum Role
{
RoleEmpty,
};

explicit QgsDatabaseSchemaModel( const QString &provider, const QString &connection, QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsDatabaseSchemaModel, for the specified ``provider`` and ``connection`` name.
@@ -56,6 +61,20 @@ Ownership of ``connection`` is transferred to the model.
virtual QModelIndex index( int row, int column, const QModelIndex &parent ) const;


void setAllowEmptySchema( bool allowEmpty );
%Docstring
Sets whether an optional empty schema ("not set") option is present in the model.

.. seealso:: :py:func:`allowEmptySchema`
%End

bool allowEmptySchema() const;
%Docstring
Returns ``True`` if the model allows the empty schema ("not set") choice.

.. seealso:: :py:func:`setAllowEmptySchema`
%End

public slots:

void refresh();
@@ -10,6 +10,7 @@




class QgsDatabaseSchemaComboBox : QWidget
{
%Docstring
@@ -43,6 +44,20 @@ Constructor for QgsDatabaseSchemaComboBox, for the specified ``provider`` and ``
Constructor for QgsDatabaseSchemaComboBox, for the specified ``connection``.

Ownership of ``connection`` is transferred to the combobox.
%End

void setAllowEmptySchema( bool allowEmpty );
%Docstring
Sets whether an optional empty schema ("not set") option is present in the combobox.

.. seealso:: :py:func:`allowEmptySchema`
%End

bool allowEmptySchema() const;
%Docstring
Returns ``True`` if the combobox allows the empty schema ("not set") choice.

.. seealso:: :py:func:`setAllowEmptySchema`
%End

QString currentSchema() const;
@@ -54,7 +54,7 @@ int QgsDatabaseSchemaModel::rowCount( const QModelIndex &parent ) const
if ( parent.isValid() )
return 0;

return mSchemas.count();
return mSchemas.count() + ( mAllowEmpty ? 1 : 0 );
}

int QgsDatabaseSchemaModel::columnCount( const QModelIndex &parent ) const
@@ -69,10 +69,22 @@ QVariant QgsDatabaseSchemaModel::data( const QModelIndex &index, int role ) cons
if ( !index.isValid() )
return QVariant();

const QString schemaName = mSchemas.value( index.row() );
if ( index.row() == 0 && mAllowEmpty )
{
if ( role == RoleEmpty )
return true;

return QVariant();
}

const QString schemaName = mSchemas.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
switch ( role )
{
case RoleEmpty:
return false;

case Qt::DisplayRole:
case Qt::EditRole:
case Qt::ToolTipRole:
{
return schemaName;
@@ -92,6 +104,25 @@ QModelIndex QgsDatabaseSchemaModel::index( int row, int column, const QModelInde
return QModelIndex();
}

void QgsDatabaseSchemaModel::setAllowEmptySchema( bool allowEmpty )
{
if ( allowEmpty == mAllowEmpty )
return;

if ( allowEmpty )
{
beginInsertRows( QModelIndex(), 0, 0 );
mAllowEmpty = true;
endInsertRows();
}
else
{
beginRemoveRows( QModelIndex(), 0, 0 );
mAllowEmpty = false;
endRemoveRows();
}
}

void QgsDatabaseSchemaModel::refresh()
{
const QStringList newSchemas = mConnection->schemas();
@@ -101,8 +132,8 @@ void QgsDatabaseSchemaModel::refresh()
{
if ( !newSchemas.contains( oldSchema ) )
{
int r = mSchemas.indexOf( oldSchema );
beginRemoveRows( QModelIndex(), r, r );
int r = mSchemas.indexOf( oldSchema ) ;
beginRemoveRows( QModelIndex(), r + ( mAllowEmpty ? 1 : 0 ), r + ( mAllowEmpty ? 1 : 0 ) );
mSchemas.removeAt( r );
endRemoveRows();
}
@@ -112,7 +143,7 @@ void QgsDatabaseSchemaModel::refresh()
{
if ( !mSchemas.contains( newSchema ) )
{
beginInsertRows( QModelIndex(), mSchemas.count(), mSchemas.count() );
beginInsertRows( QModelIndex(), mSchemas.count() + ( mAllowEmpty ? 1 : 0 ), mSchemas.count() + ( mAllowEmpty ? 1 : 0 ) );
mSchemas.append( newSchema );
endInsertRows();
}
@@ -45,6 +45,12 @@ class CORE_EXPORT QgsDatabaseSchemaModel : public QAbstractItemModel

public:

//! Model roles
enum Role
{
RoleEmpty = Qt::UserRole, //!< Entry is an empty entry
};

/**
* Constructor for QgsDatabaseSchemaModel, for the specified \a provider and \a connection name.
*
@@ -67,6 +73,18 @@ class CORE_EXPORT QgsDatabaseSchemaModel : public QAbstractItemModel
QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override;
QModelIndex index( int row, int column, const QModelIndex &parent ) const override;

/**
* Sets whether an optional empty schema ("not set") option is present in the model.
* \see allowEmptySchema()
*/
void setAllowEmptySchema( bool allowEmpty );

/**
* Returns TRUE if the model allows the empty schema ("not set") choice.
* \see setAllowEmptySchema()
*/
bool allowEmptySchema() const { return mAllowEmpty; }

public slots:

/**
@@ -78,6 +96,7 @@ class CORE_EXPORT QgsDatabaseSchemaModel : public QAbstractItemModel
void init();
std::unique_ptr< QgsAbstractDatabaseProviderConnection > mConnection;
QStringList mSchemas;
bool mAllowEmpty = false;
};

#endif // QGSDATABASESCHEMAMODEL_H
@@ -35,11 +35,21 @@ QgsDatabaseSchemaComboBox::QgsDatabaseSchemaComboBox( QgsAbstractDatabaseProvide
init();
}

void QgsDatabaseSchemaComboBox::setAllowEmptySchema( bool allowEmpty )
{
mModel->setAllowEmptySchema( allowEmpty );
}

bool QgsDatabaseSchemaComboBox::allowEmptySchema() const
{
return mModel->allowEmptySchema();
}

void QgsDatabaseSchemaComboBox::init()
{
mComboBox = new QComboBox();

mSortModel = new QSortFilterProxyModel( this );
mSortModel = new QgsDatabaseSchemaComboBoxSortModel( this );
mSortModel->setSourceModel( mModel );
mSortModel->setSortRole( Qt::DisplayRole );
mSortModel->setSortLocaleAware( true );
@@ -74,7 +84,11 @@ void QgsDatabaseSchemaComboBox::setSchema( const QString &schema )

if ( schema.isEmpty() )
{
mComboBox->setCurrentIndex( -1 );
if ( mModel->allowEmptySchema() )
mComboBox->setCurrentIndex( 0 );
else
mComboBox->setCurrentIndex( -1 );

emit schemaChanged( QString() );
return;
}
@@ -102,6 +116,7 @@ void QgsDatabaseSchemaComboBox::setConnectionName( const QString &connection, co
const QString oldSchema = currentSchema();
QgsDatabaseSchemaModel *oldModel = mModel;
mModel = new QgsDatabaseSchemaModel( mProvider, connection, this );
mModel->setAllowEmptySchema( oldModel->allowEmptySchema() );
mSortModel->setSourceModel( mModel );
oldModel->deleteLater();
if ( currentSchema() != oldSchema )
@@ -134,7 +149,7 @@ void QgsDatabaseSchemaComboBox::indexChanged( int i )

void QgsDatabaseSchemaComboBox::rowsChanged()
{
if ( mComboBox->count() == 1 )
if ( mComboBox->count() == 1 || ( mModel->allowEmptySchema() && mComboBox->count() == 2 && mComboBox->currentIndex() == 1 ) )
{
//currently selected connection item has changed
emit schemaChanged( currentSchema() );
@@ -144,3 +159,27 @@ void QgsDatabaseSchemaComboBox::rowsChanged()
emit schemaChanged( QString() );
}
}


///@cond PRIVATE
QgsDatabaseSchemaComboBoxSortModel::QgsDatabaseSchemaComboBoxSortModel( QObject *parent )
: QSortFilterProxyModel( parent )
{

}

bool QgsDatabaseSchemaComboBoxSortModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
{
// empty row is always first
if ( sourceModel()->data( left, QgsDatabaseSchemaModel::RoleEmpty ).toBool() )
return true;
else if ( sourceModel()->data( right, QgsDatabaseSchemaModel::RoleEmpty ).toBool() )
return false;

// default mode is alphabetical order
QString leftStr = sourceModel()->data( left ).toString();
QString rightStr = sourceModel()->data( right ).toString();
return QString::localeAwareCompare( leftStr, rightStr ) < 0;
}

///@endcond
@@ -20,11 +20,24 @@

#include "qgis_gui.h"
#include "qgis_sip.h"
#include <QSortFilterProxyModel>

class QgsDatabaseSchemaModel;
class QSortFilterProxyModel;
class QgsAbstractDatabaseProviderConnection;

///@cond PRIVATE
#ifndef SIP_RUN
class GUI_EXPORT QgsDatabaseSchemaComboBoxSortModel: public QSortFilterProxyModel
{
public:
explicit QgsDatabaseSchemaComboBoxSortModel( QObject *parent = nullptr );
protected:
bool lessThan( const QModelIndex &source_left, const QModelIndex &source_right ) const override;

};
#endif
///@endcond

/**
* \ingroup gui
* \brief The QgsDatabaseSchemaComboBox class is a combo box which displays the list of schemas for a specific database connection.
@@ -55,6 +68,18 @@ class GUI_EXPORT QgsDatabaseSchemaComboBox : public QWidget
*/
explicit QgsDatabaseSchemaComboBox( QgsAbstractDatabaseProviderConnection *connection SIP_TRANSFER, QWidget *parent SIP_TRANSFERTHIS = nullptr );

/**
* Sets whether an optional empty schema ("not set") option is present in the combobox.
* \see allowEmptySchema()
*/
void setAllowEmptySchema( bool allowEmpty );

/**
* Returns TRUE if the combobox allows the empty schema ("not set") choice.
* \see setAllowEmptySchema()
*/
bool allowEmptySchema() const;

/**
* Returns the name of the current schema selected in the combo box.
*/
@@ -117,6 +117,83 @@ def testCombo(self):
self.assertFalse(m.currentSchema())
self.assertFalse(spy[-1][0])

def testComboWithEmpty(self):
""" test combobox functionality with the empty row"""
conn = QgsProviderRegistry.instance().providerMetadata('postgres').createConnection(self.uri, {})
self.assertTrue(conn)

m = QgsDatabaseSchemaComboBox(conn)
spy = QSignalSpy(m.schemaChanged)
old_count = m.comboBox().count()
self.assertGreaterEqual(m.comboBox().count(), 3)
m.setAllowEmptySchema(True)
self.assertEqual(m.comboBox().count(), old_count + 1)

text = [m.comboBox().itemText(i) for i in range(m.comboBox().count())]
self.assertFalse(text[0])
self.assertIn('CamelCaseSchema', text)
self.assertIn('qgis_test', text)
self.assertLess(text.index('CamelCaseSchema'), text.index('qgis_test'))
self.assertEqual(m.currentSchema(), 'CamelCaseSchema')

m.setSchema('qgis_test')
self.assertEqual(m.currentSchema(), 'qgis_test')
self.assertEqual(len(spy), 1)
self.assertEqual(spy[-1][0], 'qgis_test')

m.setSchema('')
self.assertEqual(m.comboBox().currentIndex(), 0)
self.assertFalse(m.currentSchema())
self.assertEqual(len(spy), 2)
self.assertFalse(spy[-1][0])
m.setSchema('')
self.assertEqual(len(spy), 2)
self.assertFalse(m.currentSchema())

m.setSchema('qgis_test')
self.assertEqual(len(spy), 3)
self.assertEqual(m.currentSchema(), 'qgis_test')
self.assertEqual(spy[-1][0], 'qgis_test')

conn.createSchema('myNewSchema')
text2 = [m.comboBox().itemText(i) for i in range(m.comboBox().count())]
# schemas are not automatically refreshed
self.assertEqual(text2, text)

# but setting a new connection should fix this!
md = QgsProviderRegistry.instance().providerMetadata('postgres')
conn2 = md.createConnection(self.uri, {})
md.saveConnection(conn2, 'another')
m.setConnectionName('another', 'postgres')
# ideally there'd be no extra signal here, but it's a minor issue...
self.assertEqual(len(spy), 4)
self.assertEqual(m.currentSchema(), 'qgis_test')
self.assertEqual(spy[-1][0], 'qgis_test')

text2 = [m.comboBox().itemText(i) for i in range(m.comboBox().count())]
self.assertNotEqual(text2, text)
self.assertIn('myNewSchema', text2)
self.assertFalse(text2[0])

m.setSchema('myNewSchema')
self.assertEqual(len(spy), 5)
self.assertEqual(m.currentSchema(), 'myNewSchema')
self.assertEqual(spy[-1][0], 'myNewSchema')

# no auto drop
conn.dropSchema('myNewSchema')
self.assertEqual(len(spy), 5)
self.assertEqual(m.currentSchema(), 'myNewSchema')
self.assertEqual(spy[-1][0], 'myNewSchema')

m.refreshSchemas()
text2 = [m.comboBox().itemText(i) for i in range(m.comboBox().count())]
self.assertNotIn('myNewSchema', text2)
self.assertEqual(len(spy), 6)
self.assertFalse(m.currentSchema())
self.assertFalse(spy[-1][0])
self.assertFalse(text2[0])


if __name__ == '__main__':
unittest.main()

0 comments on commit cf310c5

Please sign in to comment.
You can’t perform that action at this time.