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