QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
20#include <gdal.h>
21
22#include "qgis.h"
23#include "qgsapplication.h"
25#include "qgsfileutils.h"
26#include "qgsfilewidget.h"
27#include "qgsgui.h"
28#include "qgsiconutils.h"
29#include "qgslogger.h"
30#include "qgsogrproviderutils.h"
31#include "qgssettings.h"
32#include "qgsvariantutils.h"
34#include "qgsvectorfilewriter.h"
35
36#include <QComboBox>
37#include <QFileDialog>
38#include <QMessageBox>
39#include <QPushButton>
40
41#include "moc_qgsnewvectorlayerdialog.cpp"
42
43QgsNewVectorLayerDialog::QgsNewVectorLayerDialog( QWidget *parent, Qt::WindowFlags fl )
44 : QDialog( parent, fl )
45{
46 setupUi( this );
48
49 connect( mAddAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mAddAttributeButton_clicked );
50 connect( mRemoveAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked );
51 connect( mFileFormatComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged );
52 connect( mTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged );
53 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsNewVectorLayerDialog::showHelp );
54 connect( mButtonUp, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::moveFieldsUp );
55 connect( mButtonDown, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::moveFieldsDown );
56
57 mAddAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewAttribute.svg" ) ) );
58 mRemoveAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteAttribute.svg" ) ) );
59
60 mTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::QString ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::QString ), "String" );
61 mTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::Int ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::Int ), "Integer" );
62 mTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::Double ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::Double ), "Real" );
63 mTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::QDate ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDate ), "Date" );
64#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 9, 0 )
65 mTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::Bool ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::Bool ), "bool" );
66#endif
67
68 mWidth->setValidator( new QIntValidator( 1, 255, this ) );
69 mPrecision->setValidator( new QIntValidator( 0, 15, this ) );
70
71 const Qgis::WkbType geomTypes[] = {
77 };
78
79 for ( const auto type : geomTypes )
80 mGeometryTypeBox->addItem( QgsIconUtils::iconForWkbType( type ), QgsWkbTypes::translatedDisplayString( type ), static_cast<quint32>( type ) );
81 mGeometryTypeBox->setCurrentIndex( -1 );
82
83 mOkButton = buttonBox->button( QDialogButtonBox::Ok );
84 mOkButton->setEnabled( false );
85
86 mFileFormatComboBox->addItem( tr( "ESRI Shapefile" ), "ESRI Shapefile" );
87#if 0
88 // Disabled until provider properly supports editing the created file formats
89 // When enabling this, adapt the window-title of the dialog and the title of all actions showing this dialog.
90 mFileFormatComboBox->addItem( tr( "Comma Separated Value" ), "Comma Separated Value" );
91 mFileFormatComboBox->addItem( tr( "GML" ), "GML" );
92 mFileFormatComboBox->addItem( tr( "Mapinfo File" ), "Mapinfo File" );
93#endif
94 if ( mFileFormatComboBox->count() == 1 )
95 {
96 mFileFormatComboBox->setVisible( false );
97 mFileFormatLabel->setVisible( false );
98 }
99
100 mCrsSelector->setShowAccuracyWarnings( true );
101
102 mFileFormatComboBox->setCurrentIndex( 0 );
103
104 mFileEncoding->addItems( QgsVectorDataProvider::availableEncodings() );
105
106 // Use default encoding if none supplied
107 const QString enc = QgsSettings().value( QStringLiteral( "/UI/encoding" ), "System" ).toString();
108
109 // The specified decoding is added if not existing already, and then set current.
110 // This should select it.
111 int encindex = mFileEncoding->findText( enc );
112 if ( encindex < 0 )
113 {
114 mFileEncoding->insertItem( 0, enc );
115 encindex = 0;
116 }
117 mFileEncoding->setCurrentIndex( encindex );
118
119 mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << QStringLiteral( "id" ) << QStringLiteral( "Integer" ) << QStringLiteral( "10" ) << QString() ) );
120 connect( mNameEdit, &QLineEdit::textChanged, this, &QgsNewVectorLayerDialog::nameChanged );
121 connect( mAttributeView, &QTreeWidget::itemSelectionChanged, this, &QgsNewVectorLayerDialog::selectionChanged );
122 connect( mGeometryTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [this]( int ) {
123 updateExtension();
124 checkOk();
125 } );
126
127 mAddAttributeButton->setEnabled( false );
128 mRemoveAttributeButton->setEnabled( false );
129 mButtonUp->setEnabled( false );
130 mButtonDown->setEnabled( false );
131
132 mFileName->setStorageMode( QgsFileWidget::SaveFile );
133 mFileName->setFilter( QgsVectorFileWriter::filterForDriver( mFileFormatComboBox->currentData( Qt::UserRole ).toString() ) );
134 mFileName->setConfirmOverwrite( false );
135 mFileName->setDialogTitle( tr( "Save Layer As" ) );
136 const QgsSettings settings;
137 mFileName->setDefaultRoot( settings.value( QStringLiteral( "UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
138 connect( mFileName, &QgsFileWidget::fileChanged, this, [this] {
139 QgsSettings settings;
140 const QFileInfo tmplFileInfo( mFileName->filePath() );
141 settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
142 checkOk();
143 } );
144}
145
146void QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged( int index )
147{
148 Q_UNUSED( index )
149 if ( mFileFormatComboBox->currentText() == tr( "ESRI Shapefile" ) )
150 mNameEdit->setMaxLength( 10 );
151 else
152 mNameEdit->setMaxLength( 32767 );
153}
154
155void QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged( int index )
156{
157 // FIXME: sync with providers/ogr/qgsogrprovider.cpp
158 switch ( index )
159 {
160 case 0: // Text data
161 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 255 )
162 mWidth->setText( QStringLiteral( "80" ) );
163 mPrecision->setEnabled( false );
164 mWidth->setEnabled( true );
165 mWidth->setValidator( new QIntValidator( 1, 255, this ) );
166 break;
167
168 case 1: // Whole number
169 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 10 )
170 mWidth->setText( QStringLiteral( "10" ) );
171 mPrecision->setEnabled( false );
172 mWidth->setEnabled( true );
173 mWidth->setValidator( new QIntValidator( 1, 10, this ) );
174 break;
175
176 case 2: // Decimal number
177 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 20 )
178 mWidth->setText( QStringLiteral( "20" ) );
179 if ( mPrecision->text().toInt() < 1 || mPrecision->text().toInt() > 15 )
180 mPrecision->setText( QStringLiteral( "6" ) );
181
182 mPrecision->setEnabled( true );
183 mWidth->setEnabled( true );
184 mWidth->setValidator( new QIntValidator( 1, 20, this ) );
185 break;
186
187 default:
188 mPrecision->setEnabled( false );
189 mWidth->setEnabled( false );
190 mWidth->clear();
191 mPrecision->clear();
192 break;
193 }
194}
195
197{
199 wkbType = static_cast<Qgis::WkbType>( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
200
201 if ( mGeometryWithZRadioButton->isChecked() )
202 wkbType = QgsWkbTypes::addZ( wkbType );
203
204 if ( mGeometryWithMRadioButton->isChecked() )
205 wkbType = QgsWkbTypes::addM( wkbType );
206
207 return wkbType;
208}
209
211{
212 return mCrsSelector->crs();
213}
214
216{
217 mCrsSelector->setCrs( crs );
218}
219
220void QgsNewVectorLayerDialog::mAddAttributeButton_clicked()
221{
222 const QString myName = mNameEdit->text();
223 const QString myWidth = mWidth->text();
224 const QString myPrecision = mPrecision->isEnabled() ? mPrecision->text() : QString();
225 //use userrole to avoid translated type string
226 const QString myType = mTypeBox->currentData( Qt::UserRole ).toString();
227 mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType << myWidth << myPrecision ) );
228
229 checkOk();
230
231 mNameEdit->clear();
232
233 if ( !mNameEdit->hasFocus() )
234 {
235 mNameEdit->setFocus();
236 }
237}
238
239void QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked()
240{
241 delete mAttributeView->currentItem();
242 checkOk();
243}
244
245void QgsNewVectorLayerDialog::attributes( QList<QPair<QString, QString>> &at ) const
246{
247 QTreeWidgetItemIterator it( mAttributeView );
248 while ( *it )
249 {
250 QTreeWidgetItem *item = *it;
251 const QString type = QStringLiteral( "%1;%2;%3" ).arg( item->text( 1 ), item->text( 2 ), item->text( 3 ) );
252 at.push_back( qMakePair( item->text( 0 ), type ) );
253 QgsDebugMsgLevel( QStringLiteral( "appending %1//%2" ).arg( item->text( 0 ), type ), 2 );
254 ++it;
255 }
256}
257
259{
260 //use userrole to avoid translated type string
261 QString myType = mFileFormatComboBox->currentData( Qt::UserRole ).toString();
262 return myType;
263}
264
266{
267 return mFileEncoding->currentText();
268}
269
270void QgsNewVectorLayerDialog::nameChanged( const QString &name )
271{
272 mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
273}
274
275void QgsNewVectorLayerDialog::selectionChanged()
276{
277 mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
278 mButtonUp->setDisabled( mAttributeView->selectedItems().isEmpty() );
279 mButtonDown->setDisabled( mAttributeView->selectedItems().isEmpty() );
280}
281
282void QgsNewVectorLayerDialog::moveFieldsUp()
283{
284 int currentRow = mAttributeView->currentIndex().row();
285 if ( currentRow == 0 )
286 return;
287
288 mAttributeView->insertTopLevelItem( currentRow - 1, mAttributeView->takeTopLevelItem( currentRow ) );
289 mAttributeView->setCurrentIndex( mAttributeView->model()->index( currentRow - 1, 0 ) );
290}
291
292void QgsNewVectorLayerDialog::moveFieldsDown()
293{
294 int currentRow = mAttributeView->currentIndex().row();
295 if ( currentRow == mAttributeView->topLevelItemCount() - 1 )
296 return;
297
298 mAttributeView->insertTopLevelItem( currentRow + 1, mAttributeView->takeTopLevelItem( currentRow ) );
299 mAttributeView->setCurrentIndex( mAttributeView->model()->index( currentRow + 1, 0 ) );
300}
301
303{
304 return mFileName->filePath();
305}
306
308{
309 mFileName->setFilePath( filename );
310}
311
312void QgsNewVectorLayerDialog::checkOk()
313{
314 const bool ok = ( !mFileName->filePath().isEmpty() && mAttributeView->topLevelItemCount() > 0 && mGeometryTypeBox->currentIndex() != -1 );
315 mOkButton->setEnabled( ok );
316}
317
318// this is static
319QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs, const QString &initialPath )
320{
321 QString error;
322 QString res = execAndCreateLayer( error, parent, initialPath, pEnc, crs );
323 if ( res.isEmpty() && error.isEmpty() )
324 res = QString( "" ); // maintain gross earlier API compatibility
325 return res;
326}
327
328void QgsNewVectorLayerDialog::updateExtension()
329{
330 QString fileName = filename();
331 const QString fileformat = selectedFileFormat();
332 const Qgis::WkbType geometrytype = selectedType();
333 if ( fileformat == QLatin1String( "ESRI Shapefile" ) )
334 {
335 if ( geometrytype != Qgis::WkbType::NoGeometry )
336 {
337 fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".dbf" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".shp" ) );
338 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { QStringLiteral( "shp" ) } );
339 }
340 else
341 {
342 fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".shp" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".dbf" ) );
343 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { QStringLiteral( "dbf" ) } );
344 }
345 }
346 setFilename( fileName );
347}
348
350{
351 if ( !mNameEdit->text().trimmed().isEmpty() )
352 {
353 const QString currentFieldName = mNameEdit->text();
354 bool currentFound = false;
355 QTreeWidgetItemIterator it( mAttributeView );
356 while ( *it )
357 {
358 QTreeWidgetItem *item = *it;
359 if ( item->text( 0 ) == currentFieldName )
360 {
361 currentFound = true;
362 break;
363 }
364 ++it;
365 }
366
367 if ( !currentFound )
368 {
369 if ( QMessageBox::question( this, windowTitle(), 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 ), QMessageBox::Ok | QMessageBox::Cancel ) != QMessageBox::Ok )
370 {
371 return;
372 }
373 }
374 }
375
376 updateExtension();
377
378 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?" ), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel ) != QMessageBox::Yes )
379 return;
380
381 QDialog::accept();
382}
383
384QString QgsNewVectorLayerDialog::execAndCreateLayer( QString &errorMessage, QWidget *parent, const QString &initialPath, QString *encoding, const QgsCoordinateReferenceSystem &crs )
385{
386 errorMessage.clear();
387 QgsNewVectorLayerDialog geomDialog( parent );
388 geomDialog.setCrs( crs );
389 if ( !initialPath.isEmpty() )
390 geomDialog.setFilename( initialPath );
391 if ( geomDialog.exec() == QDialog::Rejected )
392 {
393 return QString();
394 }
395
396 const QString fileformat = geomDialog.selectedFileFormat();
397 const Qgis::WkbType geometrytype = geomDialog.selectedType();
398 QString fileName = geomDialog.filename();
399
400 const QString enc = geomDialog.selectedFileEncoding();
401 QgsDebugMsgLevel( QStringLiteral( "New file format will be: %1" ).arg( fileformat ), 2 );
402
403 QList<QPair<QString, QString>> attributes;
404 geomDialog.attributes( attributes );
405
406 QgsSettings settings;
407 settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( fileName ).absolutePath() );
408 settings.setValue( QStringLiteral( "UI/encoding" ), enc );
409
410 //try to create the new layer with OGRProvider instead of QgsVectorFileWriter
411 if ( geometrytype != Qgis::WkbType::Unknown )
412 {
413 const QgsCoordinateReferenceSystem srs = geomDialog.crs();
414 const bool success = QgsOgrProviderUtils::createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs, errorMessage );
415 if ( !success )
416 {
417 return QString();
418 }
419 }
420 else
421 {
422 errorMessage = QObject::tr( "Geometry type not recognised" );
423 QgsDebugError( errorMessage );
424 return QString();
425 }
426
427 if ( encoding )
428 *encoding = enc;
429
430 return fileName;
431}
432
433void QgsNewVectorLayerDialog::showHelp()
434{
435 QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
436}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:277
@ Point
Point.
Definition qgis.h:279
@ LineString
LineString.
Definition qgis.h:280
@ MultiPoint
MultiPoint.
Definition qgis.h:283
@ Polygon
Polygon.
Definition qgis.h:281
@ NoGeometry
No geometry.
Definition qgis.h:294
@ Unknown
Unknown.
Definition qgis.h:278
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Represents a coordinate reference system (CRS).
static QIcon iconForFieldType(QMetaType::Type type, QMetaType::Type subType=QMetaType::Type::UnknownType, const QString &typeString=QString())
Returns an icon corresponding to a field type.
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.
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:221
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:38
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.
Stores settings for use within QGIS.
Definition qgssettings.h:65
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(QMetaType::Type type, QMetaType::Type subType=QMetaType::Type::UnknownType)
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 Q_INVOKABLE 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.
static Qgis::WkbType addZ(Qgis::WkbType type)
Adds the z dimension to a WKB type and returns the new type.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57