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