QGIS API Documentation 3.41.0-Master (cea29feecf2)
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[] = {
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 updateExtension();
117 checkOk();
118 } );
119
120 mAddAttributeButton->setEnabled( false );
121 mRemoveAttributeButton->setEnabled( false );
122 mButtonUp->setEnabled( false );
123 mButtonDown->setEnabled( false );
124
125 mFileName->setStorageMode( QgsFileWidget::SaveFile );
126 mFileName->setFilter( QgsVectorFileWriter::filterForDriver( mFileFormatComboBox->currentData( Qt::UserRole ).toString() ) );
127 mFileName->setConfirmOverwrite( false );
128 mFileName->setDialogTitle( tr( "Save Layer As" ) );
129 const QgsSettings settings;
130 mFileName->setDefaultRoot( settings.value( QStringLiteral( "UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
131 connect( mFileName, &QgsFileWidget::fileChanged, this, [=] {
132 QgsSettings settings;
133 const QFileInfo tmplFileInfo( mFileName->filePath() );
134 settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
135 checkOk();
136 } );
137}
138
139void QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged( int index )
140{
141 Q_UNUSED( index )
142 if ( mFileFormatComboBox->currentText() == tr( "ESRI Shapefile" ) )
143 mNameEdit->setMaxLength( 10 );
144 else
145 mNameEdit->setMaxLength( 32767 );
146}
147
148void QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged( int index )
149{
150 // FIXME: sync with providers/ogr/qgsogrprovider.cpp
151 switch ( index )
152 {
153 case 0: // Text data
154 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 255 )
155 mWidth->setText( QStringLiteral( "80" ) );
156 mPrecision->setEnabled( false );
157 mWidth->setValidator( new QIntValidator( 1, 255, this ) );
158 break;
159
160 case 1: // Whole number
161 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 10 )
162 mWidth->setText( QStringLiteral( "10" ) );
163 mPrecision->setEnabled( false );
164 mWidth->setValidator( new QIntValidator( 1, 10, this ) );
165 break;
166
167 case 2: // Decimal number
168 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 20 )
169 mWidth->setText( QStringLiteral( "20" ) );
170 if ( mPrecision->text().toInt() < 1 || mPrecision->text().toInt() > 15 )
171 mPrecision->setText( QStringLiteral( "6" ) );
172
173 mPrecision->setEnabled( true );
174 mWidth->setValidator( new QIntValidator( 1, 20, this ) );
175 break;
176
177 default:
178 QgsDebugError( QStringLiteral( "unexpected index" ) );
179 break;
180 }
181}
182
184{
186 wkbType = static_cast<Qgis::WkbType>( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
187
188 if ( mGeometryWithZRadioButton->isChecked() )
189 wkbType = QgsWkbTypes::addZ( wkbType );
190
191 if ( mGeometryWithMRadioButton->isChecked() )
192 wkbType = QgsWkbTypes::addM( wkbType );
193
194 return wkbType;
195}
196
198{
199 return mCrsSelector->crs();
200}
201
203{
204 mCrsSelector->setCrs( crs );
205}
206
207void QgsNewVectorLayerDialog::mAddAttributeButton_clicked()
208{
209 const QString myName = mNameEdit->text();
210 const QString myWidth = mWidth->text();
211 const QString myPrecision = mPrecision->isEnabled() ? mPrecision->text() : QString();
212 //use userrole to avoid translated type string
213 const QString myType = mTypeBox->currentData( Qt::UserRole ).toString();
214 mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType << myWidth << myPrecision ) );
215
216 checkOk();
217
218 mNameEdit->clear();
219
220 if ( !mNameEdit->hasFocus() )
221 {
222 mNameEdit->setFocus();
223 }
224}
225
226void QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked()
227{
228 delete mAttributeView->currentItem();
229 checkOk();
230}
231
232void QgsNewVectorLayerDialog::attributes( QList<QPair<QString, QString>> &at ) const
233{
234 QTreeWidgetItemIterator it( mAttributeView );
235 while ( *it )
236 {
237 QTreeWidgetItem *item = *it;
238 const QString type = QStringLiteral( "%1;%2;%3" ).arg( item->text( 1 ), item->text( 2 ), item->text( 3 ) );
239 at.push_back( qMakePair( item->text( 0 ), type ) );
240 QgsDebugMsgLevel( QStringLiteral( "appending %1//%2" ).arg( item->text( 0 ), type ), 2 );
241 ++it;
242 }
243}
244
246{
247 //use userrole to avoid translated type string
248 QString myType = mFileFormatComboBox->currentData( Qt::UserRole ).toString();
249 return myType;
250}
251
253{
254 return mFileEncoding->currentText();
255}
256
257void QgsNewVectorLayerDialog::nameChanged( const QString &name )
258{
259 mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
260}
261
262void QgsNewVectorLayerDialog::selectionChanged()
263{
264 mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
265 mButtonUp->setDisabled( mAttributeView->selectedItems().isEmpty() );
266 mButtonDown->setDisabled( mAttributeView->selectedItems().isEmpty() );
267}
268
269void QgsNewVectorLayerDialog::moveFieldsUp()
270{
271 int currentRow = mAttributeView->currentIndex().row();
272 if ( currentRow == 0 )
273 return;
274
275 mAttributeView->insertTopLevelItem( currentRow - 1, mAttributeView->takeTopLevelItem( currentRow ) );
276 mAttributeView->setCurrentIndex( mAttributeView->model()->index( currentRow - 1, 0 ) );
277}
278
279void QgsNewVectorLayerDialog::moveFieldsDown()
280{
281 int currentRow = mAttributeView->currentIndex().row();
282 if ( currentRow == mAttributeView->topLevelItemCount() - 1 )
283 return;
284
285 mAttributeView->insertTopLevelItem( currentRow + 1, mAttributeView->takeTopLevelItem( currentRow ) );
286 mAttributeView->setCurrentIndex( mAttributeView->model()->index( currentRow + 1, 0 ) );
287}
288
290{
291 return mFileName->filePath();
292}
293
294void QgsNewVectorLayerDialog::setFilename( const QString &filename )
295{
296 mFileName->setFilePath( filename );
297}
298
299void QgsNewVectorLayerDialog::checkOk()
300{
301 const bool ok = ( !mFileName->filePath().isEmpty() && mAttributeView->topLevelItemCount() > 0 && mGeometryTypeBox->currentIndex() != -1 );
302 mOkButton->setEnabled( ok );
303}
304
305// this is static
306QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs, const QString &initialPath )
307{
308 QString error;
309 QString res = execAndCreateLayer( error, parent, initialPath, pEnc, crs );
310 if ( res.isEmpty() && error.isEmpty() )
311 res = QString( "" ); // maintain gross earlier API compatibility
312 return res;
313}
314
315void QgsNewVectorLayerDialog::updateExtension()
316{
317 QString fileName = filename();
318 const QString fileformat = selectedFileFormat();
319 const Qgis::WkbType geometrytype = selectedType();
320 if ( fileformat == QLatin1String( "ESRI Shapefile" ) )
321 {
322 if ( geometrytype != Qgis::WkbType::NoGeometry )
323 {
324 fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".dbf" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".shp" ) );
325 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { QStringLiteral( "shp" ) } );
326 }
327 else
328 {
329 fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".shp" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".dbf" ) );
330 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { QStringLiteral( "dbf" ) } );
331 }
332 }
333 setFilename( fileName );
334}
335
337{
338 if ( !mNameEdit->text().trimmed().isEmpty() )
339 {
340 const QString currentFieldName = mNameEdit->text();
341 bool currentFound = false;
342 QTreeWidgetItemIterator it( mAttributeView );
343 while ( *it )
344 {
345 QTreeWidgetItem *item = *it;
346 if ( item->text( 0 ) == currentFieldName )
347 {
348 currentFound = true;
349 break;
350 }
351 ++it;
352 }
353
354 if ( !currentFound )
355 {
356 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 )
357 {
358 return;
359 }
360 }
361 }
362
363 updateExtension();
364
365 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 )
366 return;
367
368 QDialog::accept();
369}
370
371QString QgsNewVectorLayerDialog::execAndCreateLayer( QString &errorMessage, QWidget *parent, const QString &initialPath, QString *encoding, const QgsCoordinateReferenceSystem &crs )
372{
373 errorMessage.clear();
374 QgsNewVectorLayerDialog geomDialog( parent );
375 geomDialog.setCrs( crs );
376 if ( !initialPath.isEmpty() )
377 geomDialog.setFilename( initialPath );
378 if ( geomDialog.exec() == QDialog::Rejected )
379 {
380 return QString();
381 }
382
383 const QString fileformat = geomDialog.selectedFileFormat();
384 const Qgis::WkbType geometrytype = geomDialog.selectedType();
385 QString fileName = geomDialog.filename();
386
387 const QString enc = geomDialog.selectedFileEncoding();
388 QgsDebugMsgLevel( QStringLiteral( "New file format will be: %1" ).arg( fileformat ), 2 );
389
390 QList<QPair<QString, QString>> attributes;
391 geomDialog.attributes( attributes );
392
393 QgsSettings settings;
394 settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( fileName ).absolutePath() );
395 settings.setValue( QStringLiteral( "UI/encoding" ), enc );
396
397 //try to create the new layer with OGRProvider instead of QgsVectorFileWriter
398 if ( geometrytype != Qgis::WkbType::Unknown )
399 {
400 const QgsCoordinateReferenceSystem srs = geomDialog.crs();
401 const bool success = QgsOgrProviderUtils::createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs, errorMessage );
402 if ( !success )
403 {
404 return QString();
405 }
406 }
407 else
408 {
409 errorMessage = QObject::tr( "Geometry type not recognised" );
410 QgsDebugError( errorMessage );
411 return QString();
412 }
413
414 if ( encoding )
415 *encoding = enc;
416
417 return fileName;
418}
419
420void QgsNewVectorLayerDialog::showHelp()
421{
422 QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
423}
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:210
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