A few UX enhancements for error reporting on GPKGs

This fixes #36574 by showing an error in the data
items when the layer cannot be opened.

In the same way, similar errors now bubble up
to the message bar from data source select dialog.
This commit is contained in:
Alessandro Pasotti 2020-06-07 12:53:34 +02:00 committed by Nyall Dawson
parent 62bd026743
commit cd4785de9e
9 changed files with 157 additions and 77 deletions

View File

@ -138,6 +138,13 @@ Emitted when a progress dialog is shown by the provider dialog
Emitted when the ok/add buttons should be enabled/disabled Emitted when the ok/add buttons should be enabled/disabled
%End %End
void pushMessage( const QString &title, const QString &message, const Qgis::MessageLevel level = Qgis::MessageLevel::Info );
%Docstring
Emitted when a ``message`` with ``title`` and ``level`` must be shown to the user using the parent visible message bar
.. versionadded:: 3.14
%End
protected: protected:

View File

@ -15865,7 +15865,7 @@ void QgisApp::onLayerError( const QString &msg )
Q_ASSERT( layer ); Q_ASSERT( layer );
mInfoBar->pushCritical( tr( "Layer %1" ).arg( layer->name() ), msg ); visibleMessageBar()->pushCritical( tr( "Layer %1" ).arg( layer->name() ), msg );
} }
bool QgisApp::gestureEvent( QGestureEvent *event ) bool QgisApp::gestureEvent( QGestureEvent *event )

View File

@ -112,6 +112,8 @@ QgsGeoPackageCollectionItem::QgsGeoPackageCollectionItem( QgsDataItem *parent, c
QVector<QgsDataItem *> QgsGeoPackageCollectionItem::createChildren() QVector<QgsDataItem *> QgsGeoPackageCollectionItem::createChildren()
{ {
QVector<QgsDataItem *> children; QVector<QgsDataItem *> children;
try
{
const auto layers = QgsOgrLayerItem::subLayers( mPath.remove( QLatin1String( "gpkg:/" ) ), QStringLiteral( "GPKG" ) ); const auto layers = QgsOgrLayerItem::subLayers( mPath.remove( QLatin1String( "gpkg:/" ) ), QStringLiteral( "GPKG" ) );
for ( const QgsOgrDbLayerInfo *info : layers ) for ( const QgsOgrDbLayerInfo *info : layers )
{ {
@ -135,6 +137,11 @@ QVector<QgsDataItem *> QgsGeoPackageCollectionItem::createChildren()
children.append( new QgsProjectItem( this, projectName, QgsGeoPackageProjectStorage::encodeUri( projectUri ) ) ); children.append( new QgsProjectItem( this, projectName, QgsGeoPackageProjectStorage::encodeUri( projectUri ) ) );
} }
} }
}
catch ( QgsOgrLayerNotValidException &ex )
{
children.append( new QgsErrorItem( this, ex.what(), mPath + "/error" ) );
}
return children; return children;
} }

View File

@ -107,11 +107,7 @@ QList<QgsOgrDbLayerInfo *> QgsOgrLayerItem::subLayers( const QString &path, cons
// Vector layers // Vector layers
const QgsVectorLayer::LayerOptions layerOptions { QgsProject::instance()->transformContext() }; const QgsVectorLayer::LayerOptions layerOptions { QgsProject::instance()->transformContext() };
QgsVectorLayer layer( path, QStringLiteral( "ogr_tmp" ), QStringLiteral( "ogr" ), layerOptions ); QgsVectorLayer layer( path, QStringLiteral( "ogr_tmp" ), QStringLiteral( "ogr" ), layerOptions );
if ( ! layer.isValid( ) ) if ( layer.isValid( ) )
{
QgsDebugMsgLevel( QStringLiteral( "Layer is not a valid %1 Vector layer %2" ).arg( path ), 3 );
}
else
{ {
// Collect mixed-geom layers // Collect mixed-geom layers
QMultiMap<int, QStringList> subLayersMap; QMultiMap<int, QStringList> subLayersMap;
@ -188,6 +184,7 @@ QList<QgsOgrDbLayerInfo *> QgsOgrLayerItem::subLayers( const QString &path, cons
} }
} }
} }
// Raster layers // Raster layers
QgsRasterLayer::LayerOptions options; QgsRasterLayer::LayerOptions options;
options.loadDefaultStyle = false; options.loadDefaultStyle = false;
@ -200,7 +197,7 @@ QList<QgsOgrDbLayerInfo *> QgsOgrLayerItem::subLayers( const QString &path, cons
// Split on ':' since this is what comes out from the provider // Split on ':' since this is what comes out from the provider
QStringList pieces = uri.split( ':' ); QStringList pieces = uri.split( ':' );
QString name = pieces.value( pieces.length() - 1 ); QString name = pieces.value( pieces.length() - 1 );
QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Raster item %1 %2 %3" ).arg( name, uri ), 3 ); QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Raster item %1 %2" ).arg( name, uri ), 3 );
children.append( new QgsOgrDbLayerInfo( path, uri, name, QString(), QStringLiteral( "Raster" ), QgsLayerItem::LayerType::Raster ) ); children.append( new QgsOgrDbLayerInfo( path, uri, name, QString(), QStringLiteral( "Raster" ), QgsLayerItem::LayerType::Raster ) );
} }
} }
@ -238,6 +235,24 @@ QList<QgsOgrDbLayerInfo *> QgsOgrLayerItem::subLayers( const QString &path, cons
children.append( new QgsOgrDbLayerInfo( path, uri, name, QString(), QStringLiteral( "Raster" ), QgsLayerItem::LayerType::Raster ) ); children.append( new QgsOgrDbLayerInfo( path, uri, name, QString(), QStringLiteral( "Raster" ), QgsLayerItem::LayerType::Raster ) );
} }
} }
// There were problems in reading the file: throw
if ( ! layer.isValid() && ! rlayer.isValid() )
{
QString errorMessage;
// If it is file based and the file exists, there might be a permission error, let's change
// the message to give the user a hint about this possiblity.
if ( QFile::exists( path ) )
{
errorMessage = tr( "Error opening file, check file and directory permissions on\n%1" ).arg( path );
}
else
{
errorMessage = tr( "Layer is not valid (%1)" ).arg( path );
}
throw QgsOgrLayerNotValidException( errorMessage );
}
return children; return children;
} }

View File

@ -55,6 +55,20 @@ class CORE_EXPORT QgsOgrDbLayerInfo
QgsLayerItem::LayerType mLayerType = QgsLayerItem::LayerType::NoType; QgsLayerItem::LayerType mLayerType = QgsLayerItem::LayerType::NoType;
}; };
/**
* The QgsOgrLayerException class is thrown by QgsOgrLayerItem when the layer is not valid
*/
class CORE_EXPORT QgsOgrLayerNotValidException: public QgsException
{
public:
/**
* Constructor for QgsOgrLayerNotValidException, with the specified error \a message.
*/
QgsOgrLayerNotValidException( const QString &message )
: QgsException( message )
{}
};
class CORE_EXPORT QgsOgrLayerItem final: public QgsLayerItem class CORE_EXPORT QgsOgrLayerItem final: public QgsLayerItem
{ {
@ -63,7 +77,11 @@ class CORE_EXPORT QgsOgrLayerItem final: public QgsLayerItem
QgsOgrLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType, bool isSubLayer = false ); QgsOgrLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType, bool isSubLayer = false );
QString layerName() const override; QString layerName() const override;
//! Retrieve sub layers from a DB ogr layer \a path with the specified \a driver
/**
* Retrieve sub layers from a DB ogr layer \a path with the specified \a driver
* If the layer is not valid, throw a std::exception
*/
static QList<QgsOgrDbLayerInfo *> subLayers( const QString &path, const QString &driver ); static QList<QgsOgrDbLayerInfo *> subLayers( const QString &path, const QString &driver );
//! Returns a LayerType from a geometry type string //! Returns a LayerType from a geometry type string
static QgsLayerItem::LayerType layerTypeFromDb( const QString &geometryType ); static QgsLayerItem::LayerType layerTypeFromDb( const QString &geometryType );

View File

@ -358,6 +358,8 @@ void QgsVectorLayerSaveAsDialog::accept()
} }
} }
else if ( mActionOnExistingFile == QgsVectorFileWriter::CreateOrOverwriteFile ) else if ( mActionOnExistingFile == QgsVectorFileWriter::CreateOrOverwriteFile )
{
try
{ {
const QList<QgsOgrDbLayerInfo *> subLayers = QgsOgrLayerItem::subLayers( filename(), format() ); const QList<QgsOgrDbLayerInfo *> subLayers = QgsOgrLayerItem::subLayers( filename(), format() );
QStringList layerList; QStringList layerList;
@ -379,6 +381,14 @@ void QgsVectorLayerSaveAsDialog::accept()
return; return;
} }
} }
catch ( QgsOgrLayerNotValidException &ex )
{
QMessageBox::critical( this,
tr( "Save Vector Layer As" ),
tr( "Error opening destination file: %1" ).arg( ex.what() ) );
return;
}
}
QgsSettings settings; QgsSettings settings;
settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( filename() ).absolutePath() ); settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( filename() ).absolutePath() );

View File

@ -332,6 +332,9 @@ void QgsOgrDbSourceSelect::btnConnect_clicked()
QgsOgrDbConnection conn( subKey, ogrDriverName() ); QgsOgrDbConnection conn( subKey, ogrDriverName() );
mPath = conn.path(); mPath = conn.path();
try
{
const QList<QgsOgrDbLayerInfo *> layers = QgsOgrLayerItem::subLayers( mPath, ogrDriverName() ); const QList<QgsOgrDbLayerInfo *> layers = QgsOgrLayerItem::subLayers( mPath, ogrDriverName() );
QModelIndex rootItemIndex = mTableModel.indexFromItem( mTableModel.invisibleRootItem() ); QModelIndex rootItemIndex = mTableModel.indexFromItem( mTableModel.invisibleRootItem() );
@ -368,6 +371,11 @@ void QgsOgrDbSourceSelect::btnConnect_clicked()
QgsOgrDbConnection::setSelectedConnection( subKey, ogrDriverName() ); QgsOgrDbConnection::setSelectedConnection( subKey, ogrDriverName() );
qDeleteAll( layers ); qDeleteAll( layers );
} }
catch ( QgsOgrLayerNotValidException &ex )
{
pushMessage( tr( "Error opening layer" ), ex.what(), Qgis::MessageLevel::Critical );
}
}
void QgsOgrDbSourceSelect::setSql( const QModelIndex &index ) void QgsOgrDbSourceSelect::setSql( const QModelIndex &index )

View File

@ -142,6 +142,12 @@ class GUI_EXPORT QgsAbstractDataSourceWidget : public QDialog
//! Emitted when the ok/add buttons should be enabled/disabled //! Emitted when the ok/add buttons should be enabled/disabled
void enableButtons( bool enable ); void enableButtons( bool enable );
/**
* Emitted when a \a message with \a title and \a level must be shown to the user using the parent visible message bar
* \since QGIS 3.14
*/
void pushMessage( const QString &title, const QString &message, const Qgis::MessageLevel level = Qgis::MessageLevel::Info );
protected: protected:

View File

@ -166,10 +166,10 @@ void QgsDataSourceManagerDialog::addProviderDialog( QgsAbstractDataSourceWidget
void QgsDataSourceManagerDialog::makeConnections( QgsAbstractDataSourceWidget *dlg, const QString &providerKey ) void QgsDataSourceManagerDialog::makeConnections( QgsAbstractDataSourceWidget *dlg, const QString &providerKey )
{ {
// DB // DB
connect( dlg, SIGNAL( addDatabaseLayers( QStringList const &, QString const & ) ), connect( dlg, &QgsAbstractDataSourceWidget::addDatabaseLayers,
this, SIGNAL( addDatabaseLayers( QStringList const &, QString const & ) ) ); this, &QgsDataSourceManagerDialog::addDatabaseLayers );
connect( dlg, SIGNAL( progressMessage( QString ) ), connect( dlg, &QgsAbstractDataSourceWidget::progressMessage,
this, SIGNAL( showStatusMessage( QString ) ) ); this, &QgsDataSourceManagerDialog::showStatusMessage );
// Vector // Vector
connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayer, this, [ = ]( const QString & vectorLayerPath, const QString & baseName, const QString & specifiedProvider ) connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayer, this, [ = ]( const QString & vectorLayerPath, const QString & baseName, const QString & specifiedProvider )
{ {
@ -179,20 +179,29 @@ void QgsDataSourceManagerDialog::makeConnections( QgsAbstractDataSourceWidget *d
); );
connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayers, connect( dlg, &QgsAbstractDataSourceWidget::addVectorLayers,
this, &QgsDataSourceManagerDialog::vectorLayersAdded ); this, &QgsDataSourceManagerDialog::vectorLayersAdded );
connect( dlg, SIGNAL( connectionsChanged() ), this, SIGNAL( connectionsChanged() ) ); connect( dlg, &QgsAbstractDataSourceWidget::connectionsChanged, this, &QgsDataSourceManagerDialog::connectionsChanged );
// Raster // Raster
connect( dlg, SIGNAL( addRasterLayer( QString const &, QString const &, QString const & ) ), connect( dlg, &QgsAbstractDataSourceWidget::addRasterLayer,
this, SIGNAL( addRasterLayer( QString const &, QString const &, QString const & ) ) ); this, [ = ]( const QString & uri, const QString & baseName, const QString & providerKey )
{
addRasterLayer( uri, baseName, providerKey );
} );
// Mesh // Mesh
connect( dlg, &QgsAbstractDataSourceWidget::addMeshLayer, this, &QgsDataSourceManagerDialog::addMeshLayer ); connect( dlg, &QgsAbstractDataSourceWidget::addMeshLayer, this, &QgsDataSourceManagerDialog::addMeshLayer );
// Vector tile // Vector tile
connect( dlg, &QgsAbstractDataSourceWidget::addVectorTileLayer, this, &QgsDataSourceManagerDialog::addVectorTileLayer ); connect( dlg, &QgsAbstractDataSourceWidget::addVectorTileLayer, this, &QgsDataSourceManagerDialog::addVectorTileLayer );
// Virtual // Virtual
connect( dlg, SIGNAL( replaceVectorLayer( QString, QString, QString, QString ) ), connect( dlg, &QgsAbstractDataSourceWidget::replaceVectorLayer,
this, SIGNAL( replaceSelectedVectorLayer( QString, QString, QString, QString ) ) ); this, &QgsDataSourceManagerDialog::replaceSelectedVectorLayer );
// Common // Common
connect( dlg, SIGNAL( connectionsChanged() ), this, SIGNAL( connectionsChanged() ) ); connect( dlg, &QgsAbstractDataSourceWidget::connectionsChanged, this, &QgsDataSourceManagerDialog::connectionsChanged );
connect( this, SIGNAL( providerDialogsRefreshRequested() ), dlg, SLOT( refresh() ) ); connect( this, &QgsDataSourceManagerDialog::providerDialogsRefreshRequested, dlg, &QgsAbstractDataSourceWidget::refresh );
// Message
connect( dlg, &QgsAbstractDataSourceWidget::pushMessage, this, [ = ]( const QString & title, const QString & message, const Qgis::MessageLevel level )
{
mMessageBar->pushMessage( title, message, level );
} );
} }
void QgsDataSourceManagerDialog::showEvent( QShowEvent *e ) void QgsDataSourceManagerDialog::showEvent( QShowEvent *e )