QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsnewvectorlayerdialog.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsnewvectorlayerdialog.cpp - description
3 -------------------
4 begin : October 2004
5 copyright : (C) 2004 by Marco Hugentobler
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19#include "qgsapplication.h"
20#include "qgsfilewidget.h"
21#include "qgis.h"
22#include "qgslogger.h"
25#include "qgsvectorfilewriter.h"
26#include "qgssettings.h"
27#include "qgsgui.h"
28#include "qgsiconutils.h"
29#include "qgsfileutils.h"
30#include "qgsvariantutils.h"
31#include "qgsogrproviderutils.h"
32
33#include <QPushButton>
34#include <QComboBox>
35#include <QFileDialog>
36#include <QMessageBox>
37
38QgsNewVectorLayerDialog::QgsNewVectorLayerDialog( QWidget *parent, Qt::WindowFlags fl )
39 : QDialog( parent, fl )
40{
41 setupUi( this );
43
44 connect( mAddAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mAddAttributeButton_clicked );
45 connect( mRemoveAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked );
46 connect( mFileFormatComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged );
47 connect( mTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged );
48 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsNewVectorLayerDialog::showHelp );
49
50 mAddAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewAttribute.svg" ) ) );
51 mRemoveAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteAttribute.svg" ) ) );
52
53 mTypeBox->addItem( QgsFields::iconForFieldType( QVariant::String ), QgsVariantUtils::typeToDisplayString( QVariant::String ), "String" );
54 mTypeBox->addItem( QgsFields::iconForFieldType( QVariant::Int ), QgsVariantUtils::typeToDisplayString( QVariant::Int ), "Integer" );
55 mTypeBox->addItem( QgsFields::iconForFieldType( QVariant::Double ), QgsVariantUtils::typeToDisplayString( QVariant::Double ), "Real" );
56 mTypeBox->addItem( QgsFields::iconForFieldType( QVariant::Date ), QgsVariantUtils::typeToDisplayString( QVariant::Date ), "Date" );
57
58 mWidth->setValidator( new QIntValidator( 1, 255, this ) );
59 mPrecision->setValidator( new QIntValidator( 0, 15, this ) );
60
61 const Qgis::WkbType geomTypes[] =
62 {
68 };
69
70 for ( const auto type : geomTypes )
71 mGeometryTypeBox->addItem( QgsIconUtils::iconForWkbType( type ), QgsWkbTypes::translatedDisplayString( type ), static_cast<quint32>( type ) );
72 mGeometryTypeBox->setCurrentIndex( -1 );
73
74 mOkButton = buttonBox->button( QDialogButtonBox::Ok );
75 mOkButton->setEnabled( false );
76
77 mFileFormatComboBox->addItem( tr( "ESRI Shapefile" ), "ESRI Shapefile" );
78#if 0
79 // Disabled until provider properly supports editing the created file formats
80 // When enabling this, adapt the window-title of the dialog and the title of all actions showing this dialog.
81 mFileFormatComboBox->addItem( tr( "Comma Separated Value" ), "Comma Separated Value" );
82 mFileFormatComboBox->addItem( tr( "GML" ), "GML" );
83 mFileFormatComboBox->addItem( tr( "Mapinfo File" ), "Mapinfo File" );
84#endif
85 if ( mFileFormatComboBox->count() == 1 )
86 {
87 mFileFormatComboBox->setVisible( false );
88 mFileFormatLabel->setVisible( false );
89 }
90
91 mCrsSelector->setShowAccuracyWarnings( true );
92
93 mFileFormatComboBox->setCurrentIndex( 0 );
94
95 mFileEncoding->addItems( QgsVectorDataProvider::availableEncodings() );
96
97 // Use default encoding if none supplied
98 const QString enc = QgsSettings().value( QStringLiteral( "/UI/encoding" ), "System" ).toString();
99
100 // The specified decoding is added if not existing already, and then set current.
101 // This should select it.
102 int encindex = mFileEncoding->findText( enc );
103 if ( encindex < 0 )
104 {
105 mFileEncoding->insertItem( 0, enc );
106 encindex = 0;
107 }
108 mFileEncoding->setCurrentIndex( encindex );
109
110 mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << QStringLiteral( "id" ) << QStringLiteral( "Integer" ) << QStringLiteral( "10" ) << QString() ) );
111 connect( mNameEdit, &QLineEdit::textChanged, this, &QgsNewVectorLayerDialog::nameChanged );
112 connect( mAttributeView, &QTreeWidget::itemSelectionChanged, this, &QgsNewVectorLayerDialog::selectionChanged );
113 connect( mGeometryTypeBox, static_cast<void( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ]( int )
114 {
115 updateExtension();
116 checkOk();
117 } );
118
119 mAddAttributeButton->setEnabled( false );
120 mRemoveAttributeButton->setEnabled( false );
121
122 mFileName->setStorageMode( QgsFileWidget::SaveFile );
123 mFileName->setFilter( QgsVectorFileWriter::filterForDriver( mFileFormatComboBox->currentData( Qt::UserRole ).toString() ) );
124 mFileName->setConfirmOverwrite( false );
125 mFileName->setDialogTitle( tr( "Save Layer As" ) );
126 const QgsSettings settings;
127 mFileName->setDefaultRoot( settings.value( QStringLiteral( "UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
128 connect( mFileName, &QgsFileWidget::fileChanged, this, [ = ]
129 {
130 QgsSettings settings;
131 const QFileInfo tmplFileInfo( mFileName->filePath() );
132 settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
133 checkOk();
134 } );
135}
136
137void QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged( int index )
138{
139 Q_UNUSED( index )
140 if ( mFileFormatComboBox->currentText() == tr( "ESRI Shapefile" ) )
141 mNameEdit->setMaxLength( 10 );
142 else
143 mNameEdit->setMaxLength( 32767 );
144}
145
146void QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged( int index )
147{
148 // FIXME: sync with providers/ogr/qgsogrprovider.cpp
149 switch ( index )
150 {
151 case 0: // Text data
152 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 255 )
153 mWidth->setText( QStringLiteral( "80" ) );
154 mPrecision->setEnabled( false );
155 mWidth->setValidator( new QIntValidator( 1, 255, this ) );
156 break;
157
158 case 1: // Whole number
159 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 10 )
160 mWidth->setText( QStringLiteral( "10" ) );
161 mPrecision->setEnabled( false );
162 mWidth->setValidator( new QIntValidator( 1, 10, this ) );
163 break;
164
165 case 2: // Decimal number
166 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 20 )
167 mWidth->setText( QStringLiteral( "20" ) );
168 if ( mPrecision->text().toInt() < 1 || mPrecision->text().toInt() > 15 )
169 mPrecision->setText( QStringLiteral( "6" ) );
170
171 mPrecision->setEnabled( true );
172 mWidth->setValidator( new QIntValidator( 1, 20, this ) );
173 break;
174
175 default:
176 QgsDebugError( QStringLiteral( "unexpected index" ) );
177 break;
178 }
179}
180
182{
184 wkbType = static_cast<Qgis::WkbType>
185 ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
186
187 if ( mGeometryWithZRadioButton->isChecked() )
188 wkbType = QgsWkbTypes::addZ( wkbType );
189
190 if ( mGeometryWithMRadioButton->isChecked() )
191 wkbType = QgsWkbTypes::addM( wkbType );
192
193 return wkbType;
194}
195
197{
198 return mCrsSelector->crs();
199}
200
202{
203 mCrsSelector->setCrs( crs );
204}
205
206void QgsNewVectorLayerDialog::mAddAttributeButton_clicked()
207{
208 const QString myName = mNameEdit->text();
209 const QString myWidth = mWidth->text();
210 const QString myPrecision = mPrecision->isEnabled() ? mPrecision->text() : QString();
211 //use userrole to avoid translated type string
212 const QString myType = mTypeBox->currentData( Qt::UserRole ).toString();
213 mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType << myWidth << myPrecision ) );
214 checkOk();
215 mNameEdit->clear();
216}
217
218void QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked()
219{
220 delete mAttributeView->currentItem();
221 checkOk();
222}
223
224void QgsNewVectorLayerDialog::attributes( QList< QPair<QString, QString> > &at ) const
225{
226 QTreeWidgetItemIterator it( mAttributeView );
227 while ( *it )
228 {
229 QTreeWidgetItem *item = *it;
230 const QString type = QStringLiteral( "%1;%2;%3" ).arg( item->text( 1 ), item->text( 2 ), item->text( 3 ) );
231 at.push_back( qMakePair( item->text( 0 ), type ) );
232 QgsDebugMsgLevel( QStringLiteral( "appending %1//%2" ).arg( item->text( 0 ), type ), 2 );
233 ++it;
234 }
235}
236
238{
239 //use userrole to avoid translated type string
240 QString myType = mFileFormatComboBox->currentData( Qt::UserRole ).toString();
241 return myType;
242}
243
245{
246 return mFileEncoding->currentText();
247}
248
249void QgsNewVectorLayerDialog::nameChanged( const QString &name )
250{
251 mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
252}
253
254void QgsNewVectorLayerDialog::selectionChanged()
255{
256 mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
257}
258
260{
261 return mFileName->filePath();
262}
263
264void QgsNewVectorLayerDialog::setFilename( const QString &filename )
265{
266 mFileName->setFilePath( filename );
267}
268
269void QgsNewVectorLayerDialog::checkOk()
270{
271 const bool ok = ( !mFileName->filePath().isEmpty() && mAttributeView->topLevelItemCount() > 0 && mGeometryTypeBox->currentIndex() != -1 );
272 mOkButton->setEnabled( ok );
273}
274
275// this is static
276QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs, const QString &initialPath )
277{
278 QString error;
279 QString res = execAndCreateLayer( error, parent, initialPath, pEnc, crs );
280 if ( res.isEmpty() && error.isEmpty() )
281 res = QString( "" ); // maintain gross earlier API compatibility
282 return res;
283}
284
285void QgsNewVectorLayerDialog::updateExtension()
286{
287 QString fileName = filename();
288 const QString fileformat = selectedFileFormat();
289 const Qgis::WkbType geometrytype = selectedType();
290 if ( fileformat == QLatin1String( "ESRI Shapefile" ) )
291 {
292 if ( geometrytype != Qgis::WkbType::NoGeometry )
293 {
294 fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".dbf" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".shp" ) );
295 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { QStringLiteral( "shp" ) } );
296 }
297 else
298 {
299 fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".shp" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".dbf" ) );
300 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { QStringLiteral( "dbf" ) } );
301 }
302 }
303 setFilename( fileName );
304
305}
306
308{
309 if ( !mNameEdit->text().trimmed().isEmpty() )
310 {
311 const QString currentFieldName = mNameEdit->text();
312 bool currentFound = false;
313 QTreeWidgetItemIterator it( mAttributeView );
314 while ( *it )
315 {
316 QTreeWidgetItem *item = *it;
317 if ( item->text( 0 ) == currentFieldName )
318 {
319 currentFound = true;
320 break;
321 }
322 ++it;
323 }
324
325 if ( !currentFound )
326 {
327 if ( QMessageBox::question( this, windowTitle(),
328 tr( "The field “%1” has not been added to the fields list. Are you sure you want to proceed and discard this field?" ).arg( currentFieldName ),
329 QMessageBox::Ok | QMessageBox::Cancel ) != QMessageBox::Ok )
330 {
331 return;
332 }
333 }
334 }
335
336 updateExtension();
337
338 if ( QFile::exists( filename() ) && QMessageBox::warning( this, tr( "New ShapeFile Layer" ), tr( "The layer already exists. Are you sure you want to overwrite the existing file?" ),
339 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel ) != QMessageBox::Yes )
340 return;
341
342 QDialog::accept();
343}
344
345QString QgsNewVectorLayerDialog::execAndCreateLayer( QString &errorMessage, QWidget *parent, const QString &initialPath, QString *encoding, const QgsCoordinateReferenceSystem &crs )
346{
347 errorMessage.clear();
348 QgsNewVectorLayerDialog geomDialog( parent );
349 geomDialog.setCrs( crs );
350 if ( !initialPath.isEmpty() )
351 geomDialog.setFilename( initialPath );
352 if ( geomDialog.exec() == QDialog::Rejected )
353 {
354 return QString();
355 }
356
357 const QString fileformat = geomDialog.selectedFileFormat();
358 const Qgis::WkbType geometrytype = geomDialog.selectedType();
359 QString fileName = geomDialog.filename();
360
361 const QString enc = geomDialog.selectedFileEncoding();
362 QgsDebugMsgLevel( QStringLiteral( "New file format will be: %1" ).arg( fileformat ), 2 );
363
364 QList< QPair<QString, QString> > attributes;
365 geomDialog.attributes( attributes );
366
367 QgsSettings settings;
368 settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( fileName ).absolutePath() );
369 settings.setValue( QStringLiteral( "UI/encoding" ), enc );
370
371 //try to create the new layer with OGRProvider instead of QgsVectorFileWriter
372 if ( geometrytype != Qgis::WkbType::Unknown )
373 {
374 const QgsCoordinateReferenceSystem srs = geomDialog.crs();
375 const bool success = QgsOgrProviderUtils::createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs, errorMessage );
376 if ( !success )
377 {
378 return QString();
379 }
380 }
381 else
382 {
383 errorMessage = QObject::tr( "Geometry type not recognised" );
384 QgsDebugError( errorMessage );
385 return QString();
386 }
387
388 if ( encoding )
389 *encoding = enc;
390
391 return fileName;
392}
393
394void QgsNewVectorLayerDialog::showHelp()
395{
396 QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
397}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ LineString
LineString.
@ MultiPoint
MultiPoint.
@ Polygon
Polygon.
@ NoGeometry
No geometry.
@ Unknown
Unknown.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
This class represents a coordinate reference system (CRS).
static QIcon iconForFieldType(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid, const QString &typeString=QString())
Returns an icon corresponding to a field type.
Definition: qgsfields.cpp:294
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
@ SaveFile
Select a single new or pre-existing file.
Definition: qgsfilewidget.h:71
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:194
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:39
static QIcon iconForWkbType(Qgis::WkbType type)
Returns the icon for a vector layer whose geometry type is provided.
static Q_DECL_DEPRECATED QString runAndCreateLayer(QWidget *parent=nullptr, QString *enc=nullptr, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), const QString &initialPath=QString())
Runs the dialog and creates a layer matching the dialog parameters.
void attributes(QList< QPair< QString, QString > > &at) const
Appends the chosen attribute names and types to at.
QgsCoordinateReferenceSystem crs() const
Returns the selected CRS for the new layer.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs value for the new layer in the dialog.
QString filename() const
Returns the name for the new layer.
QString selectedFileFormat() const
Returns the file format for storage.
QString selectedFileEncoding() const
Returns the file format for storage.
QgsNewVectorLayerDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
New dialog constructor.
static QString execAndCreateLayer(QString &errorMessage, QWidget *parent=nullptr, const QString &initialPath=QString(), QString *encoding=nullptr, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Runs the dialog and creates a layer matching the dialog parameters.
Qgis::WkbType selectedType() const
Returns the selected geometry type.
void setFilename(const QString &filename)
Sets the initial file name to show in the dialog.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static QString typeToDisplayString(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns a user-friendly translated string representing a QVariant type.
static QStringList availableEncodings()
Returns a list of available encodings.
static QString filterForDriver(const QString &driverName)
Creates a filter for an OGR driver key.
static QString translatedDisplayString(Qgis::WkbType type)
Returns a translated display string type for a WKB type, e.g., the geometry name used in WKT geometry...
static Qgis::WkbType addM(Qgis::WkbType type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1092
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1068
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs