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