Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
New Shapefile Layer dialog: avoid warning message on user cancel
Also add better error message reporting, and deprecate horrible API.

(cherry picked from commit 9a723d9)
  • Loading branch information
nyalldawson committed Feb 1, 2019
1 parent dbd7512 commit a6cad4d
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 24 deletions.
46 changes: 45 additions & 1 deletion python/gui/auto_generated/qgsnewvectorlayerdialog.sip.in
Expand Up @@ -17,14 +17,46 @@ class QgsNewVectorLayerDialog: QDialog
%End
public:

static QString runAndCreateLayer( QWidget *parent = 0, QString *enc = 0, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );
static QString runAndCreateLayer( QWidget *parent = 0, QString *enc = 0, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem(),
const QString &initialPath = QString() ) /Deprecated/;
%Docstring
Runs the dialog and creates a layer matching the dialog parameters.

If the ``initialPath`` argument is specified, then the dialog will default to the specified filename.

:return: fileName on success, empty string use aborted, QString() if creation failed

.. deprecated:: in QGIS 3.4.5 - use execAndCreateLayer() instead.
%End

static QString execAndCreateLayer( QString &errorMessage /Out/, QWidget *parent = 0, const QString &initialPath = QString(), QString *encoding /Out/ = 0, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );
%Docstring
Runs the dialog and creates a layer matching the dialog parameters.

If the ``initialPath`` argument is specified, then the dialog will default to the specified filename.

Returns a filename if the dialog was accepted, or an empty string if the dialog was canceled.
If the dialog was accepted but an error occurred while creating the file, then the function will
return an empty string and ``errorMessage`` will contain the error message.

If ``encoding`` is specified, it will be set to the encoding of the created file.

:param errorMessage: will be set to any error message encountered during layer creation
:param parent: parent widget for dialog
:param initialPath: initial file path to show in dialog
:param encoding: if specified, will be set to file encoding of created layer
:param crs: default layer CRS to show in dialog

:return: Newly created file name, or an empty string if user canceled or an error occurred.


.. versionadded:: 3.4.5
%End

QgsNewVectorLayerDialog( QWidget *parent /TransferThis/ = 0, Qt::WindowFlags fl = QgsGuiUtils::ModalDialogFlags );
%Docstring
New dialog constructor.
%End
~QgsNewVectorLayerDialog();
QgsWkbTypes::Type selectedType() const;
%Docstring
Expand All @@ -42,9 +74,21 @@ Returns the file format for storage
%Docstring
Returns the file format for storage
%End

QString filename() const;
%Docstring
Returns the name for the new layer

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

void setFilename( const QString &filename );
%Docstring
Sets the initial file name to show in the dialog.

.. seealso:: :py:func:`filename`

.. versionadded:: 3.4.5
%End

QgsCoordinateReferenceSystem crs() const;
Expand Down
9 changes: 5 additions & 4 deletions src/app/qgisapp.cpp
Expand Up @@ -5602,7 +5602,8 @@ void QgisApp::fileNewFromTemplateAction( QAction *qAction )
void QgisApp::newVectorLayer()
{
QString enc;
QString fileName = QgsNewVectorLayerDialog::runAndCreateLayer( this, &enc, QgsProject::instance()->defaultCrsForNewLayers() );
QString error;
QString fileName = QgsNewVectorLayerDialog::execAndCreateLayer( error, this, QString(), &enc, QgsProject::instance()->defaultCrsForNewLayers() );

if ( !fileName.isEmpty() )
{
Expand All @@ -5612,12 +5613,12 @@ void QgisApp::newVectorLayer()
//todo: the last parameter will change accordingly to layer type
addVectorLayers( fileNames, enc, QStringLiteral( "file" ) );
}
else if ( fileName.isNull() )
else if ( !error.isEmpty() )
{
QLabel *msgLabel = new QLabel( tr( "Layer creation failed. Please check the <a href=\"#messageLog\">message log</a> for further information." ), messageBar() );
QLabel *msgLabel = new QLabel( tr( "Layer creation failed: %1" ).arg( error ), messageBar() );
msgLabel->setWordWrap( true );
connect( msgLabel, &QLabel::linkActivated, mLogDock, &QWidget::show );
QgsMessageBarItem *item = new QgsMessageBarItem( msgLabel, Qgis::Warning );
QgsMessageBarItem *item = new QgsMessageBarItem( msgLabel, Qgis::Critical );
messageBar()->pushItem( item );
}
}
Expand Down
41 changes: 33 additions & 8 deletions src/gui/qgsnewvectorlayerdialog.cpp
Expand Up @@ -30,7 +30,7 @@
#include <QComboBox>
#include <QLibrary>
#include <QFileDialog>

#include <QMessageBox>

QgsNewVectorLayerDialog::QgsNewVectorLayerDialog( QWidget *parent, Qt::WindowFlags fl )
: QDialog( parent, fl )
Expand Down Expand Up @@ -105,6 +105,7 @@ QgsNewVectorLayerDialog::QgsNewVectorLayerDialog( QWidget *parent, Qt::WindowFla

mFileName->setStorageMode( QgsFileWidget::SaveFile );
mFileName->setFilter( QgsVectorFileWriter::filterForDriver( mFileFormatComboBox->currentData( Qt::UserRole ).toString() ) );
mFileName->setConfirmOverwrite( false );
mFileName->setDialogTitle( tr( "Save Layer As" ) );
mFileName->setDefaultRoot( settings.value( QStringLiteral( "UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
connect( mFileName, &QgsFileWidget::fileChanged, this, [ = ]
Expand Down Expand Up @@ -246,22 +247,43 @@ QString QgsNewVectorLayerDialog::filename() const
return mFileName->filePath();
}

void QgsNewVectorLayerDialog::setFilename( const QString &filename )
{
mFileName->setFilePath( filename );
}

void QgsNewVectorLayerDialog::checkOk()
{
bool ok = ( !mFileName->filePath().isEmpty() && mAttributeView->topLevelItemCount() > 0 );
mOkButton->setEnabled( ok );
}

// this is static
QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs )
QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs, const QString &initialPath )
{
QString error;
QString res = execAndCreateLayer( error, parent, initialPath, pEnc, crs );
if ( res.isEmpty() && error.isEmpty() )
res = QString( "" ); // maintain gross earlier API compatibility
return res;
}

QString QgsNewVectorLayerDialog::execAndCreateLayer( QString &errorMessage, QWidget *parent, const QString &initialPath, QString *encoding, const QgsCoordinateReferenceSystem &crs )
{
errorMessage.clear();
QgsNewVectorLayerDialog geomDialog( parent );
geomDialog.setCrs( crs );
if ( !initialPath.isEmpty() )
geomDialog.setFilename( initialPath );
if ( geomDialog.exec() == QDialog::Rejected )
{
return QString();
}

if ( QFile::exists( geomDialog.filename() ) && QMessageBox::warning( parent, tr( "New ShapeFile Layer" ), tr( "The layer already exists. Are you sure you want to overwrite the existing file?" ),
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel ) != QMessageBox::Yes )
return QString();

QgsWkbTypes::Type geometrytype = geomDialog.selectedType();
QString fileformat = geomDialog.selectedFileFormat();
QString enc = geomDialog.selectedFileEncoding();
Expand Down Expand Up @@ -289,33 +311,35 @@ QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pE
QgsDebugMsg( QStringLiteral( "ogr provider loaded" ) );

typedef bool ( *createEmptyDataSourceProc )( const QString &, const QString &, const QString &, QgsWkbTypes::Type,
const QList< QPair<QString, QString> > &, const QgsCoordinateReferenceSystem & );
const QList< QPair<QString, QString> > &, const QgsCoordinateReferenceSystem &, QString & );
createEmptyDataSourceProc createEmptyDataSource = ( createEmptyDataSourceProc ) cast_to_fptr( myLib->resolve( "createEmptyDataSource" ) );
if ( createEmptyDataSource )
{
if ( geometrytype != QgsWkbTypes::Unknown )
{
QgsCoordinateReferenceSystem srs = geomDialog.crs();
if ( !createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs ) )
if ( !createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs, errorMessage ) )
{
return QString();
}
}
else
{
QgsDebugMsg( QStringLiteral( "geometry type not recognised" ) );
errorMessage = QObject::tr( "Geometry type not recognised" );
QgsDebugMsg( errorMessage );
return QString();
}
}
else
{
QgsDebugMsg( QStringLiteral( "Resolving newEmptyDataSource(...) failed" ) );
errorMessage = QObject::tr( "Resolving newEmptyDataSource(...) failed" );
QgsDebugMsg( errorMessage );
return QString();
}
}

if ( pEnc )
*pEnc = enc;
if ( encoding )
*encoding = enc;

return fileName;
}
Expand All @@ -324,3 +348,4 @@ void QgsNewVectorLayerDialog::showHelp()
{
QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
}

53 changes: 50 additions & 3 deletions src/gui/qgsnewvectorlayerdialog.h
Expand Up @@ -21,8 +21,9 @@
#include "qgsguiutils.h"
#include "qgshelp.h"

#include "qgis.h"
#include "qgswkbtypes.h"
#include "qgis_gui.h"
#include "qgis_sip.h"

/**
* \ingroup gui
Expand All @@ -36,10 +37,42 @@ class GUI_EXPORT QgsNewVectorLayerDialog: public QDialog, private Ui::QgsNewVect

/**
* Runs the dialog and creates a layer matching the dialog parameters.
*
* If the \a initialPath argument is specified, then the dialog will default to the specified filename.
*
* \returns fileName on success, empty string use aborted, QString() if creation failed
*
* \deprecated in QGIS 3.4.5 - use execAndCreateLayer() instead.
*/
static QString runAndCreateLayer( QWidget *parent = nullptr, QString *enc = nullptr, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );
Q_DECL_DEPRECATED static QString runAndCreateLayer( QWidget *parent = nullptr, QString *enc = nullptr, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem(),
const QString &initialPath = QString() ) SIP_DEPRECATED;

/**
* Runs the dialog and creates a layer matching the dialog parameters.
*
* If the \a initialPath argument is specified, then the dialog will default to the specified filename.
*
* Returns a filename if the dialog was accepted, or an empty string if the dialog was canceled.
* If the dialog was accepted but an error occurred while creating the file, then the function will
* return an empty string and \a errorMessage will contain the error message.
*
* If \a encoding is specified, it will be set to the encoding of the created file.
*
* \param errorMessage will be set to any error message encountered during layer creation
* \param parent parent widget for dialog
* \param initialPath initial file path to show in dialog
* \param encoding if specified, will be set to file encoding of created layer
* \param crs default layer CRS to show in dialog
*
* \returns Newly created file name, or an empty string if user canceled or an error occurred.
*
* \since QGIS 3.4.5
*/
static QString execAndCreateLayer( QString &errorMessage SIP_OUT, QWidget *parent = nullptr, const QString &initialPath = QString(), QString *encoding SIP_OUT = nullptr, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );

/**
* New dialog constructor.
*/
QgsNewVectorLayerDialog( QWidget *parent SIP_TRANSFERTHIS = nullptr, Qt::WindowFlags fl = QgsGuiUtils::ModalDialogFlags );
~QgsNewVectorLayerDialog() override;
//! Returns the selected geometry type
Expand All @@ -50,9 +83,23 @@ class GUI_EXPORT QgsNewVectorLayerDialog: public QDialog, private Ui::QgsNewVect
QString selectedFileFormat() const;
//! Returns the file format for storage
QString selectedFileEncoding() const;
//! Returns the name for the new layer

/**
* Returns the name for the new layer
*
* \see setFilename()
*/
QString filename() const;

/**
* Sets the initial file name to show in the dialog.
*
* \see filename()
*
* \since QGIS 3.4.5
*/
void setFilename( const QString &filename );

/**
* Returns the selected CRS for the new layer.
* \see setCrs()
Expand Down
5 changes: 3 additions & 2 deletions src/plugins/geometry_checker/qgsgeometrycheckerresulttab.cpp
Expand Up @@ -261,13 +261,14 @@ bool QgsGeometryCheckerResultTab::exportErrorsDo( const QString &file )
{
return false;
}
typedef bool ( *createEmptyDataSourceProc )( const QString &, const QString &, const QString &, QgsWkbTypes::Type, const QList< QPair<QString, QString> > &, const QgsCoordinateReferenceSystem & );
typedef bool ( *createEmptyDataSourceProc )( const QString &, const QString &, const QString &, QgsWkbTypes::Type, const QList< QPair<QString, QString> > &, const QgsCoordinateReferenceSystem &, QString & );
createEmptyDataSourceProc createEmptyDataSource = ( createEmptyDataSourceProc ) cast_to_fptr( ogrLib.resolve( "createEmptyDataSource" ) );
if ( !createEmptyDataSource )
{
return false;
}
if ( !createEmptyDataSource( file, driver, "UTF-8", QgsWkbTypes::Point, attributes, QgsProject::instance()->crs() ) )
QString createError;
if ( !createEmptyDataSource( file, driver, "UTF-8", QgsWkbTypes::Point, attributes, QgsProject::instance()->crs(), createError ) )
{
return false;
}
Expand Down
19 changes: 13 additions & 6 deletions src/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -3183,9 +3183,11 @@ QGISEXTERN bool createEmptyDataSource( const QString &uri,
const QString &encoding,
QgsWkbTypes::Type vectortype,
const QList< QPair<QString, QString> > &attributes,
const QgsCoordinateReferenceSystem &srs = QgsCoordinateReferenceSystem() )
const QgsCoordinateReferenceSystem &srs,
QString &errorMessage )
{
QgsDebugMsg( QStringLiteral( "Creating empty vector layer with format: %1" ).arg( format ) );
errorMessage.clear();

QgsApplication::registerOgrDrivers();
OGRSFDriverH driver = OGRGetDriverByName( format.toLatin1() );
Expand All @@ -3200,7 +3202,8 @@ QGISEXTERN bool createEmptyDataSource( const QString &uri,
{
if ( !uri.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) )
{
QgsDebugMsg( QStringLiteral( "uri %1 doesn't end with .shp" ).arg( uri ) );
errorMessage = QObject::tr( "URI %1 doesn't end with .shp" ).arg( uri );
QgsDebugMsg( errorMessage );
return false;
}

Expand All @@ -3212,7 +3215,8 @@ QGISEXTERN bool createEmptyDataSource( const QString &uri,
QString name = fldIt->first.left( 10 );
if ( fieldNames.contains( name ) )
{
QgsMessageLog::logMessage( QObject::tr( "Duplicate field (10 significant characters): %1" ).arg( name ), QObject::tr( "OGR" ) );
errorMessage = QObject::tr( "Duplicate field (10 significant characters): %1" ).arg( name );
QgsMessageLog::logMessage( errorMessage, QObject::tr( "OGR" ) );
return false;
}
fieldNames << name;
Expand All @@ -3229,7 +3233,8 @@ QGISEXTERN bool createEmptyDataSource( const QString &uri,
dataSource.reset( OGR_Dr_CreateDataSource( driver, uri.toUtf8().constData(), nullptr ) );
if ( !dataSource )
{
QgsMessageLog::logMessage( QObject::tr( "Creating the data source %1 failed: %2" ).arg( uri, QString::fromUtf8( CPLGetLastErrorMsg() ) ), QObject::tr( "OGR" ) );
errorMessage = QObject::tr( "Creating the data source %1 failed: %2" ).arg( uri, QString::fromUtf8( CPLGetLastErrorMsg() ) );
QgsMessageLog::logMessage( errorMessage, QObject::tr( "OGR" ) );
return false;
}

Expand Down Expand Up @@ -3263,7 +3268,8 @@ QGISEXTERN bool createEmptyDataSource( const QString &uri,
case QgsWkbTypes::GeometryCollectionZM:
case QgsWkbTypes::Unknown:
{
QgsMessageLog::logMessage( QObject::tr( "Unknown vector type of %1" ).arg( ( int )( vectortype ) ), QObject::tr( "OGR" ) );
errorMessage = QObject::tr( "Unknown vector type of %1" ).arg( static_cast< int >( vectortype ) );
QgsMessageLog::logMessage( errorMessage, QObject::tr( "OGR" ) );
return false;
}

Expand Down Expand Up @@ -3292,7 +3298,8 @@ QGISEXTERN bool createEmptyDataSource( const QString &uri,

if ( !layer )
{
QgsMessageLog::logMessage( QObject::tr( "Creation of OGR data source %1 failed: %2" ).arg( uri, QString::fromUtf8( CPLGetLastErrorMsg() ) ), QObject::tr( "OGR" ) );
errorMessage = QString::fromUtf8( CPLGetLastErrorMsg() );
QgsMessageLog::logMessage( errorMessage, QObject::tr( "OGR" ) );
return false;
}

Expand Down

0 comments on commit a6cad4d

Please sign in to comment.