Skip to content

Commit

Permalink
Add API to QgsProviderRegistry to obtain user friendly warning messages
Browse files Browse the repository at this point in the history
for when a URI cannot be opened in a QGIS install, but the URI
matches a format which could potentially be opened in a different QGIS
install.

This can be used to show user-friendly warning messages advising user why
a particular uri cannot be opened on their QGIS install. For example, if
a QGIS install is built without the PDAL library then las/laz files are
unusable, and this method can be used to retrieve a user-friendly warning
as to why the las/laz files cannot be used on their QGIS build.

Instead of just showing the user a generic "not a valid or recognized data
source" error, we can use this api to show more helpful URIs about the
specific uri.

Initially intended to help advise users why LAS/LAZ files cannot be
opened on their QGIS install, but also could be used e.g. by mdal
to advise users about missing third party libraries preventing
them from opening certain mesh files, etc....
  • Loading branch information
nyalldawson committed Mar 1, 2021
1 parent 4ada839 commit 4240e20
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 0 deletions.
120 changes: 120 additions & 0 deletions python/core/auto_generated/qgsproviderregistry.sip.in
Expand Up @@ -12,6 +12,7 @@




class QgsProviderRegistry
{
%Docstring
Expand Down Expand Up @@ -337,6 +338,125 @@ all of these providers will be returned.
.. seealso:: :py:func:`shouldDeferUriForOtherProviders`

.. versionadded:: 3.18
%End

class UnusableUriDetails
{
%Docstring

Contains information about unusable URIs which aren't handled by any registered providers.

For example, if a QGIS install is built without the PDAL library then las/laz files are unusable.
This class can then be used to construct friendly warnings to users advising them why the las/laz
files cannot be used on their QGIS build.

.. versionadded:: 3.18.1
%End

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

UnusableUriDetails( const QString &uri = QString(), const QString &warning = QString(), const QList< QgsMapLayerType > &layerTypes = QList< QgsMapLayerType >() );
%Docstring
Constructor for UnusableUriDetails for the given ``uri``, with the specified user-friendly, translated ``warning``.

The optional ``layerTypes`` argument can be used to specify layer types which are usually valid
options for opening the URI.
%End

QString uri;

QString warning;

QString detailedWarning;

QList<QgsMapLayerType> layerTypes;

SIP_PYOBJECT __repr__();
%MethodCode
QString str = QStringLiteral( "<QgsProviderRegistry.UnusableUriDetails: %1>" ).arg( sipCpp->warning );
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
%End

};

class UnusableUriHandlerInterface
{
%Docstring

An interface used to handle unusable URIs which aren't handled by any registered providers, and construct
user-friendly warnings as to why the URI is unusable.

For example, if a QGIS install is built without the PDAL library then las/laz files are unusable.
This class can then be used to construct friendly warnings to users advising them why the las/laz
files cannot be used on their QGIS build.

.. versionadded:: 3.18.1
%End

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

virtual ~UnusableUriHandlerInterface();

virtual bool matchesUri( const QString &uri ) const = 0;
%Docstring
Returns ``True`` if the handle is an unusable URI handler for the specified ``uri``.
%End

virtual UnusableUriDetails details( const QString &uri ) const = 0;
%Docstring
Returns the details for advising the user why the ``uri`` is not usable.
%End

};

bool registerUnusableUriHandler( UnusableUriHandlerInterface *handler /Transfer/ );
%Docstring
Registers an unusable URI ``handler``, used to handle unusable URIs which aren't
handled by any registered providers, and construct user-friendly warnings as to why the URI is unusable.

:return: ``True`` on success

.. note::

ownership of the UnusableUriHandlerInterface instance is transferred to the registry


.. versionadded:: 3.18.1
%End

bool handleUnusableUri( const QString &uri, UnusableUriDetails &details /Out/ ) const;
%Docstring
Returns ``True`` if the specified ``uri`` can potentially be handled by QGIS, if additional
dependancies or build-time requirements are present.

This can be used to show user-friendly warning messages advising them why a particular
``uri`` cannot be opened on their QGIS install. For example, if a QGIS install is built
without the PDAL library then las/laz files are unusable, and this method can be used
to retrieve a user-friendly warning as to why the las/laz files cannot be used on their
QGIS build.

.. warning::

This method does not perform the test to actually determine if the given ``uri``
can be handled by any registered provider. It is assumed that prior to calling this method
the caller has already determined in advance that the ``uri`` could not be handled.

:param uri: URI to test


:return: - ``True`` if the ``uri`` was matched to a registered QgsProviderRegistry.UnusableUriHandlerInterface.
- details: will be populated with details allowing construction of a user-friendly warning message


.. seealso:: :py:func:`registerUnusableUriHandler`

.. versionadded:: 3.18.1
%End

bool shouldDeferUriForOtherProviders( const QString &uri, const QString &providerKey ) const;
Expand Down
14 changes: 14 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -7631,6 +7631,20 @@ bool QgisApp::openLayer( const QString &fileName, bool allowInteractive )
ok = static_cast< bool >( addMeshLayerPrivate( fileName, fileInfo.completeBaseName(), QStringLiteral( "mdal" ), false ) );
}

// maybe a known file type, which couldn't be opened due to a missing dependancy... (eg. las for a non-pdal-enabled build)
{
QgsProviderRegistry::UnusableUriDetails details;
if ( QgsProviderRegistry::instance()->handleUnusableUri( fileName, details ) )
{
ok = true;

if ( details.detailedWarning.isEmpty() )
visibleMessageBar()->pushMessage( QString(), details.warning, Qgis::Critical );
else
visibleMessageBar()->pushMessage( QString(), details.warning, details.detailedWarning, Qgis::Critical );
}
}

if ( !ok )
{
// we have no idea what this file is...
Expand Down
21 changes: 21 additions & 0 deletions src/core/qgsproviderregistry.cpp
Expand Up @@ -379,6 +379,8 @@ bool QgsProviderRegistry::exists()

QgsProviderRegistry::~QgsProviderRegistry()
{
qDeleteAll( mUnusableUriHandlers );

clean();
if ( sInstance == this )
sInstance = nullptr;
Expand Down Expand Up @@ -807,6 +809,25 @@ QList<QgsProviderRegistry::ProviderCandidateDetails> QgsProviderRegistry::prefer
return res;
}

bool QgsProviderRegistry::registerUnusableUriHandler( QgsProviderRegistry::UnusableUriHandlerInterface *handler )
{
mUnusableUriHandlers << handler;
return true;
}

bool QgsProviderRegistry::handleUnusableUri( const QString &uri, UnusableUriDetails &details ) const
{
for ( const QgsProviderRegistry::UnusableUriHandlerInterface *handler : mUnusableUriHandlers )
{
if ( handler->matchesUri( uri ) )
{
details = handler->details( uri );
return true;
}
}
return false;
}

bool QgsProviderRegistry::shouldDeferUriForOtherProviders( const QString &uri, const QString &providerKey ) const
{
const QList< ProviderCandidateDetails > providers = preferredProvidersForUri( uri );
Expand Down
131 changes: 131 additions & 0 deletions src/core/qgsproviderregistry.h
Expand Up @@ -30,6 +30,9 @@
#include "qgis_core.h"
#include "qgis_sip.h"

#include <vector>
#include <memory>

class QgsProviderMetadata;
class QgsVectorLayer;
class QgsCoordinateReferenceSystem;
Expand Down Expand Up @@ -363,6 +366,132 @@ class CORE_EXPORT QgsProviderRegistry
*/
QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProvidersForUri( const QString &uri ) const;

/**
* \ingroup core
*
* \brief Contains information about unusable URIs which aren't handled by any registered providers.
*
* For example, if a QGIS install is built without the PDAL library then las/laz files are unusable.
* This class can then be used to construct friendly warnings to users advising them why the las/laz
* files cannot be used on their QGIS build.
*
* \since QGIS 3.18.1
*/
class CORE_EXPORT UnusableUriDetails
{
public:

/**
* Constructor for UnusableUriDetails for the given \a uri, with the specified user-friendly, translated \a warning.
*
* The optional \a layerTypes argument can be used to specify layer types which are usually valid
* options for opening the URI.
*/
UnusableUriDetails( const QString &uri = QString(), const QString &warning = QString(), const QList< QgsMapLayerType > &layerTypes = QList< QgsMapLayerType >() )
: uri( uri )
, warning( warning )
, layerTypes( layerTypes )
{}

/**
* URI which could not be handled.
*/
QString uri;

/**
* Contains a short, user-friendly, translated message advising why the URI is not usable.
*/
QString warning;

/**
* Contains a longer, user-friendly, translated message advising why the URI is not usable.
*/
QString detailedWarning;

/**
* Contains a list of map layer types which are usually valid options for opening the
* target URI.
*/
QList<QgsMapLayerType> layerTypes;

#ifdef SIP_RUN
SIP_PYOBJECT __repr__();
% MethodCode
QString str = QStringLiteral( "<QgsProviderRegistry.UnusableUriDetails: %1>" ).arg( sipCpp->warning );
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
% End
#endif

};

/**
* \ingroup core
*
* \brief An interface used to handle unusable URIs which aren't handled by any registered providers, and construct
* user-friendly warnings as to why the URI is unusable.
*
* For example, if a QGIS install is built without the PDAL library then las/laz files are unusable.
* This class can then be used to construct friendly warnings to users advising them why the las/laz
* files cannot be used on their QGIS build.
*
* \since QGIS 3.18.1
*/
class CORE_EXPORT UnusableUriHandlerInterface
{

public:

virtual ~UnusableUriHandlerInterface() = default;

/**
* Returns TRUE if the handle is an unusable URI handler for the specified \a uri.
*/
virtual bool matchesUri( const QString &uri ) const = 0;

/**
* Returns the details for advising the user why the \a uri is not usable.
*/
virtual UnusableUriDetails details( const QString &uri ) const = 0;

};

/**
* \brief Registers an unusable URI \a handler, used to handle unusable URIs which aren't
* handled by any registered providers, and construct user-friendly warnings as to why the URI is unusable.
*
* \return TRUE on success
*
* \note ownership of the UnusableUriHandlerInterface instance is transferred to the registry
*
* \since QGIS 3.18.1
*/
bool registerUnusableUriHandler( UnusableUriHandlerInterface *handler SIP_TRANSFER );

/**
* Returns TRUE if the specified \a uri can potentially be handled by QGIS, if additional
* dependancies or build-time requirements are present.
*
* This can be used to show user-friendly warning messages advising them why a particular
* \a uri cannot be opened on their QGIS install. For example, if a QGIS install is built
* without the PDAL library then las/laz files are unusable, and this method can be used
* to retrieve a user-friendly warning as to why the las/laz files cannot be used on their
* QGIS build.
*
* \warning This method does not perform the test to actually determine if the given \a uri
* can be handled by any registered provider. It is assumed that prior to calling this method
* the caller has already determined in advance that the \a uri could not be handled.
*
* \param uri URI to test
* \param details will be populated with details allowing construction of a user-friendly
* warning message
*
* \returns TRUE if the \a uri was matched to a registered QgsProviderRegistry::UnusableUriHandlerInterface.
*
* \see registerUnusableUriHandler()
* \since QGIS 3.18.1
*/
bool handleUnusableUri( const QString &uri, UnusableUriDetails &details SIP_OUT ) const;

/**
* Returns TRUE if the provider with matching \a providerKey should defer handling of
* the specified \a uri to another provider.
Expand Down Expand Up @@ -557,6 +686,8 @@ class CORE_EXPORT QgsProviderRegistry
*/
QString mProtocolDrivers;

QList< UnusableUriHandlerInterface * > mUnusableUriHandlers;

/**
* Returns TRUE if registry instance exists.
*/
Expand Down

0 comments on commit 4240e20

Please sign in to comment.