Skip to content

Commit

Permalink
Add model for connections from a provider
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Mar 9, 2020
1 parent 2217378 commit 1fba225
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 0 deletions.
59 changes: 59 additions & 0 deletions python/core/auto_generated/qgsproviderconnectionmodel.sip.in
@@ -0,0 +1,59 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsproviderconnectionmodel.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/





class QgsProviderConnectionModel : QAbstractItemModel
{
%Docstring
A model containing registered connection names for a specific data provider.

.. warning::

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

.. versionadded:: 3.14
%End

%TypeHeaderCode
#include "qgsproviderconnectionmodel.h"
%End
public:

explicit QgsProviderConnectionModel( const QString &provider, QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsProviderConnectionModel, 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

virtual QModelIndex parent( const QModelIndex &child ) const;

virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const;

virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const;

virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const;

virtual QModelIndex index( int row, int column, const QModelIndex &parent ) const;

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsproviderconnectionmodel.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Expand Up @@ -160,6 +160,7 @@
%Include auto_generated/qgsproperty.sip
%Include auto_generated/qgspropertycollection.sip
%Include auto_generated/qgspropertytransformer.sip
%Include auto_generated/qgsproviderconnectionmodel.sip
%Include auto_generated/qgsprovidermetadata.sip
%Include auto_generated/qgsproviderregistry.sip
%Include auto_generated/qgsproxyprogresstask.sip
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -348,6 +348,7 @@ SET(QGIS_CORE_SRCS
qgsproperty.cpp
qgspropertycollection.cpp
qgspropertytransformer.cpp
qgsproviderconnectionmodel.cpp
qgsprovidermetadata.cpp
qgsproviderregistry.cpp
qgsproxyprogresstask.cpp
Expand Down Expand Up @@ -862,6 +863,7 @@ SET(QGIS_CORE_HDRS
qgsproperty.h
qgspropertycollection.h
qgspropertytransformer.h
qgsproviderconnectionmodel.h
qgsprovidermetadata.h
qgsproviderregistry.h
qgsproxyprogresstask.h
Expand Down
109 changes: 109 additions & 0 deletions src/core/qgsproviderconnectionmodel.cpp
@@ -0,0 +1,109 @@
/***************************************************************************
qgsproviderconnectionmodel.cpp
--------------------------------------
Date : March 2020
Copyright : (C) 2020 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsproviderconnectionmodel.h"
#include "qgsproviderregistry.h"
#include "qgsprovidermetadata.h"

QgsProviderConnectionModel::QgsProviderConnectionModel( const QString &provider, QObject *parent )
: QAbstractItemModel( parent )
, mProvider( provider )
, mMetadata( QgsProviderRegistry::instance()->providerMetadata( provider ) )
{
Q_ASSERT( mMetadata );

connect( mMetadata, &QgsProviderMetadata::connectionCreated, this, &QgsProviderConnectionModel::addConnection );
connect( mMetadata, &QgsProviderMetadata::connectionDeleted, this, &QgsProviderConnectionModel::removeConnection );

mConnections = mMetadata->connections().keys();
}

void QgsProviderConnectionModel::removeConnection( const QString &connection )
{
int index = mConnections.indexOf( connection );
if ( index < 0 )
return;

beginRemoveRows( QModelIndex(), index, index );
mConnections.removeAt( index );
endRemoveRows();
}

void QgsProviderConnectionModel::addConnection( const QString &connection )
{
beginInsertRows( QModelIndex(), mConnections.count(), mConnections.count() );
mConnections.append( connection );
endInsertRows();
}

QModelIndex QgsProviderConnectionModel::parent( const QModelIndex &child ) const
{
Q_UNUSED( child )
return QModelIndex();
}


int QgsProviderConnectionModel::rowCount( const QModelIndex &parent ) const
{
if ( parent.isValid() )
return 0;

return mConnections.count();
}

int QgsProviderConnectionModel::columnCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent )
return 1;
}


QVariant QgsProviderConnectionModel::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() )
return QVariant();

const QString connectionName = mConnections.value( index.row() );
switch ( role )
{
case Qt::DisplayRole:
{
return connectionName;
}

case Qt::ToolTipRole:
{
if ( const QgsAbstractProviderConnection *connection = mMetadata->findConnection( connectionName ) )
{
return connection->uri();
}
else
{
return QString();
}
}
}

return QVariant();
}

QModelIndex QgsProviderConnectionModel::index( int row, int column, const QModelIndex &parent ) const
{
if ( hasIndex( row, column, parent ) )
{
return createIndex( row, column, row );
}

return QModelIndex();
}
68 changes: 68 additions & 0 deletions src/core/qgsproviderconnectionmodel.h
@@ -0,0 +1,68 @@
/***************************************************************************
qgsproviderconnectionmodel.h
--------------------------------------
Date : March 2020
Copyright : (C) 2020 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSPROVIDERCONNECTIONMODEL_H
#define QGSPROVIDERCONNECTIONMODEL_H

#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
#include <QStringList>

#include "qgis_core.h"
#include "qgis_sip.h"

class QgsProviderMetadata;

/**
* \ingroup core
* \class QgsProviderConnectionModel
* \brief A model containing registered connection names for a specific data provider.
*
* \warning The provider must support the connection API methods in its QgsProviderMetadata implementation
* in order for the model to work correctly.
*
* \since QGIS 3.14
*/
class CORE_EXPORT QgsProviderConnectionModel : public QAbstractItemModel
{
Q_OBJECT

public:

/**
* Constructor for QgsProviderConnectionModel, for the specified \a provider.
*
* \warning The \a provider must support the connection API methods in its QgsProviderMetadata implementation
* in order for the model to work correctly.
*/
explicit QgsProviderConnectionModel( const QString &provider, QObject *parent SIP_TRANSFERTHIS = nullptr );

// QAbstractItemModel interface
QModelIndex parent( const QModelIndex &child ) const override;
int rowCount( const QModelIndex &parent = QModelIndex() ) const override;
int columnCount( const QModelIndex &parent = QModelIndex() ) const override;
QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override;
QModelIndex index( int row, int column, const QModelIndex &parent ) const override;
private slots:
void removeConnection( const QString &connection );
void addConnection( const QString &connection );

private:
QString mProvider;
QgsProviderMetadata *mMetadata = nullptr;
QStringList mConnections;
};

#endif // QGSPROVIDERCONNECTIONMODEL_H
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Expand Up @@ -185,6 +185,7 @@ ADD_PYTHON_TEST(PyQgsImportIntoPostGIS test_processing_importintopostgis.py)
ADD_PYTHON_TEST(PyQgsProjectionSelectionWidgets test_qgsprojectionselectionwidgets.py)
ADD_PYTHON_TEST(PyQgsProjectMetadata test_qgsprojectmetadata.py)
ADD_PYTHON_TEST(PyQgsPropertyOverrideButton test_qgspropertyoverridebutton.py)
ADD_PYTHON_TEST(PyQgsProviderConnectionModel test_qgsproviderconnectionmodel.py)
ADD_PYTHON_TEST(PyQgsProviderConnectionPostgres test_qgsproviderconnection_postgres.py)
ADD_PYTHON_TEST(PyQgsProviderConnectionGpkg test_qgsproviderconnection_ogr_gpkg.py)
ADD_PYTHON_TEST(TestQgsRandomMarkerSymbolLayer test_qgsrandommarkersymbollayer.py)
Expand Down
95 changes: 95 additions & 0 deletions tests/src/python/test_qgsproviderconnectionmodel.py
@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for OGR GeoPackage QgsProviderConnectionModel.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = 'Nyall Dawson'
__date__ = '07/08/2020'
__copyright__ = 'Copyright 2019, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'

import os
import shutil
import tempfile
from qgis.core import (
QgsVectorLayer,
QgsProviderRegistry,
QgsProviderConnectionModel,
)
from qgis.PyQt.QtCore import (
QModelIndex,
Qt,
QCoreApplication
)
from qgis.testing import unittest
from utilities import unitTestDataPath, start_app

TEST_DATA_DIR = unitTestDataPath()


class TestPyQgsProviderConnectionModel(unittest.TestCase):

@classmethod
def setUpClass(cls):
"""Run before all tests"""
QCoreApplication.setOrganizationName("QGIS_Test")
QCoreApplication.setOrganizationDomain(cls.__name__)
QCoreApplication.setApplicationName(cls.__name__)
start_app()

gpkg_original_path = '{}/qgis_server/test_project_wms_grouped_layers.gpkg'.format(TEST_DATA_DIR)
cls.basetestpath = tempfile.mkdtemp()
cls.gpkg_path = '{}/test_gpkg.gpkg'.format(cls.basetestpath)
shutil.copy(gpkg_original_path, cls.gpkg_path)
vl = QgsVectorLayer('{}|layername=cdb_lines'.format(cls.gpkg_path), 'test', 'ogr')
assert vl.isValid()

gpkg2_original_path = '{}/points_gpkg.gpkg'.format(TEST_DATA_DIR)
cls.gpkg_path2 = '{}/test_gpkg2.gpkg'.format(cls.basetestpath)
shutil.copy(gpkg2_original_path, cls.gpkg_path2)
vl = QgsVectorLayer('{}'.format(cls.gpkg_path2), 'test', 'ogr')
assert vl.isValid()

@classmethod
def tearDownClass(cls):
"""Run after all tests"""
os.unlink(cls.gpkg_path)
os.unlink(cls.gpkg_path2)

def test_model(self):
"""Test model functionality"""

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

model = QgsProviderConnectionModel('ogr')
self.assertEqual(model.rowCount(), 1)
self.assertEqual(model.columnCount(), 1)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), 'qgis_test1')
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.ToolTipRole), self.gpkg_path)

md.saveConnection(conn, 'qgis_test1')
self.assertEqual(model.rowCount(), 1)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), 'qgis_test1')

conn2 = md.createConnection(self.gpkg_path2, {})
md.saveConnection(conn2, 'qgis_test2')
self.assertEqual(model.rowCount(), 2)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), 'qgis_test1')
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.ToolTipRole), self.gpkg_path)
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole), 'qgis_test2')
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), Qt.ToolTipRole), self.gpkg_path2)

md.deleteConnection('qgis_test1')
self.assertEqual(model.rowCount(), 1)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), 'qgis_test2')


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

0 comments on commit 1fba225

Please sign in to comment.