QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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"
24#include "qgsproviderregistry.h"
26#include "qgsvectorfilewriter.h"
27#include "qgssettings.h"
28#include "qgsogrprovider.h"
29#include "qgsgui.h"
30#include "qgsiconutils.h"
31#include "qgsfileutils.h"
32#include "qgsvariantutils.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
51 mAddAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewAttribute.svg" ) ) );
52 mRemoveAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteAttribute.svg" ) ) );
53
54 mTypeBox->addItem( QgsFields::iconForFieldType( QVariant::String ), QgsVariantUtils::typeToDisplayString( QVariant::String ), "String" );
55 mTypeBox->addItem( QgsFields::iconForFieldType( QVariant::Int ), QgsVariantUtils::typeToDisplayString( QVariant::Int ), "Integer" );
56 mTypeBox->addItem( QgsFields::iconForFieldType( QVariant::Double ), QgsVariantUtils::typeToDisplayString( QVariant::Double ), "Real" );
57 mTypeBox->addItem( QgsFields::iconForFieldType( QVariant::Date ), QgsVariantUtils::typeToDisplayString( QVariant::Date ), "Date" );
58
59 mWidth->setValidator( new QIntValidator( 1, 255, this ) );
60 mPrecision->setValidator( new QIntValidator( 0, 15, this ) );
61
62 const QgsWkbTypes::Type geomTypes[] =
63 {
69 };
70
71 for ( const auto type : geomTypes )
72 mGeometryTypeBox->addItem( QgsIconUtils::iconForWkbType( type ), QgsWkbTypes::translatedDisplayString( type ), type );
73 mGeometryTypeBox->setCurrentIndex( -1 );
74
75 mOkButton = buttonBox->button( QDialogButtonBox::Ok );
76 mOkButton->setEnabled( false );
77
78 mFileFormatComboBox->addItem( tr( "ESRI Shapefile" ), "ESRI Shapefile" );
79#if 0
80 // Disabled until provider properly supports editing the created file formats
81 // When enabling this, adapt the window-title of the dialog and the title of all actions showing this dialog.
82 mFileFormatComboBox->addItem( tr( "Comma Separated Value" ), "Comma Separated Value" );
83 mFileFormatComboBox->addItem( tr( "GML" ), "GML" );
84 mFileFormatComboBox->addItem( tr( "Mapinfo File" ), "Mapinfo File" );
85#endif
86 if ( mFileFormatComboBox->count() == 1 )
87 {
88 mFileFormatComboBox->setVisible( false );
89 mFileFormatLabel->setVisible( false );
90 }
91
92 mCrsSelector->setShowAccuracyWarnings( true );
93
94 mFileFormatComboBox->setCurrentIndex( 0 );
95
96 mFileEncoding->addItems( QgsVectorDataProvider::availableEncodings() );
97
98 // Use default encoding if none supplied
99 const QString enc = QgsSettings().value( QStringLiteral( "/UI/encoding" ), "System" ).toString();
100
101 // The specified decoding is added if not existing already, and then set current.
102 // This should select it.
103 int encindex = mFileEncoding->findText( enc );
104 if ( encindex < 0 )
105 {
106 mFileEncoding->insertItem( 0, enc );
107 encindex = 0;
108 }
109 mFileEncoding->setCurrentIndex( encindex );
110
111 mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << QStringLiteral( "id" ) << QStringLiteral( "Integer" ) << QStringLiteral( "10" ) << QString() ) );
112 connect( mNameEdit, &QLineEdit::textChanged, this, &QgsNewVectorLayerDialog::nameChanged );
113 connect( mAttributeView, &QTreeWidget::itemSelectionChanged, this, &QgsNewVectorLayerDialog::selectionChanged );
114 connect( mGeometryTypeBox, static_cast<void( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ]( int )
115 {
116 updateExtension();
117 checkOk();
118 } );
119
120 mAddAttributeButton->setEnabled( false );
121 mRemoveAttributeButton->setEnabled( false );
122
123 mFileName->setStorageMode( QgsFileWidget::SaveFile );
124 mFileName->setFilter( QgsVectorFileWriter::filterForDriver( mFileFormatComboBox->currentData( Qt::UserRole ).toString() ) );
125 mFileName->setConfirmOverwrite( false );
126 mFileName->setDialogTitle( tr( "Save Layer As" ) );
127 const QgsSettings settings;
128 mFileName->setDefaultRoot( settings.value( QStringLiteral( "UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
129 connect( mFileName, &QgsFileWidget::fileChanged, this, [ = ]
130 {
131 QgsSettings settings;
132 const QFileInfo tmplFileInfo( mFileName->filePath() );
133 settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
134 checkOk();
135 } );
136}
137
138void QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged( int index )
139{
140 Q_UNUSED( index )
141 if ( mFileFormatComboBox->currentText() == tr( "ESRI Shapefile" ) )
142 mNameEdit->setMaxLength( 10 );
143 else
144 mNameEdit->setMaxLength( 32767 );
145}
146
147void QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged( int index )
148{
149 // FIXME: sync with providers/ogr/qgsogrprovider.cpp
150 switch ( index )
151 {
152 case 0: // Text data
153 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 255 )
154 mWidth->setText( QStringLiteral( "80" ) );
155 mPrecision->setEnabled( false );
156 mWidth->setValidator( new QIntValidator( 1, 255, this ) );
157 break;
158
159 case 1: // Whole number
160 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 10 )
161 mWidth->setText( QStringLiteral( "10" ) );
162 mPrecision->setEnabled( false );
163 mWidth->setValidator( new QIntValidator( 1, 10, this ) );
164 break;
165
166 case 2: // Decimal number
167 if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 20 )
168 mWidth->setText( QStringLiteral( "20" ) );
169 if ( mPrecision->text().toInt() < 1 || mPrecision->text().toInt() > 15 )
170 mPrecision->setText( QStringLiteral( "6" ) );
171
172 mPrecision->setEnabled( true );
173 mWidth->setValidator( new QIntValidator( 1, 20, this ) );
174 break;
175
176 default:
177 QgsDebugMsg( QStringLiteral( "unexpected index" ) );
178 break;
179 }
180}
181
183{
185 wkbType = static_cast<QgsWkbTypes::Type>
186 ( 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 checkOk();
216 mNameEdit->clear();
217}
218
219void QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked()
220{
221 delete mAttributeView->currentItem();
222 checkOk();
223}
224
225void QgsNewVectorLayerDialog::attributes( QList< QPair<QString, QString> > &at ) const
226{
227 QTreeWidgetItemIterator it( mAttributeView );
228 while ( *it )
229 {
230 QTreeWidgetItem *item = *it;
231 const QString type = QStringLiteral( "%1;%2;%3" ).arg( item->text( 1 ), item->text( 2 ), item->text( 3 ) );
232 at.push_back( qMakePair( item->text( 0 ), type ) );
233 QgsDebugMsg( QStringLiteral( "appending %1//%2" ).arg( item->text( 0 ), type ) );
234 ++it;
235 }
236}
237
239{
240 //use userrole to avoid translated type string
241 QString myType = mFileFormatComboBox->currentData( Qt::UserRole ).toString();
242 return myType;
243}
244
246{
247 return mFileEncoding->currentText();
248}
249
250void QgsNewVectorLayerDialog::nameChanged( const QString &name )
251{
252 mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
253}
254
255void QgsNewVectorLayerDialog::selectionChanged()
256{
257 mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
258}
259
261{
262 return mFileName->filePath();
263}
264
265void QgsNewVectorLayerDialog::setFilename( const QString &filename )
266{
267 mFileName->setFilePath( filename );
268}
269
270void QgsNewVectorLayerDialog::checkOk()
271{
272 const bool ok = ( !mFileName->filePath().isEmpty() && mAttributeView->topLevelItemCount() > 0 && mGeometryTypeBox->currentIndex() != -1 );
273 mOkButton->setEnabled( ok );
274}
275
276// this is static
277QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs, const QString &initialPath )
278{
279 QString error;
280 QString res = execAndCreateLayer( error, parent, initialPath, pEnc, crs );
281 if ( res.isEmpty() && error.isEmpty() )
282 res = QString( "" ); // maintain gross earlier API compatibility
283 return res;
284}
285
286void QgsNewVectorLayerDialog::updateExtension()
287{
288 QString fileName = filename();
289 const QString fileformat = selectedFileFormat();
290 const QgsWkbTypes::Type geometrytype = selectedType();
291 if ( fileformat == QLatin1String( "ESRI Shapefile" ) )
292 {
293 if ( geometrytype != QgsWkbTypes::NoGeometry )
294 {
295 fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".dbf" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".shp" ) );
296 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { QStringLiteral( "shp" ) } );
297 }
298 else
299 {
300 fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".shp" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".dbf" ) );
301 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, { QStringLiteral( "dbf" ) } );
302 }
303 }
304 setFilename( fileName );
305
306}
307
309{
310 updateExtension();
311
312 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?" ),
313 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel ) != QMessageBox::Yes )
314 return;
315
316 QDialog::accept();
317}
318
319QString QgsNewVectorLayerDialog::execAndCreateLayer( QString &errorMessage, QWidget *parent, const QString &initialPath, QString *encoding, const QgsCoordinateReferenceSystem &crs )
320{
321 errorMessage.clear();
322 QgsNewVectorLayerDialog geomDialog( parent );
323 geomDialog.setCrs( crs );
324 if ( !initialPath.isEmpty() )
325 geomDialog.setFilename( initialPath );
326 if ( geomDialog.exec() == QDialog::Rejected )
327 {
328 return QString();
329 }
330
331 const QString fileformat = geomDialog.selectedFileFormat();
332 const QgsWkbTypes::Type geometrytype = geomDialog.selectedType();
333 QString fileName = geomDialog.filename();
334
335 const QString enc = geomDialog.selectedFileEncoding();
336 QgsDebugMsg( QStringLiteral( "New file format will be: %1" ).arg( fileformat ) );
337
338 QList< QPair<QString, QString> > attributes;
339 geomDialog.attributes( attributes );
340
341 QgsSettings settings;
342 settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( fileName ).absolutePath() );
343 settings.setValue( QStringLiteral( "UI/encoding" ), enc );
344
345 //try to create the new layer with OGRProvider instead of QgsVectorFileWriter
346 if ( geometrytype != QgsWkbTypes::Unknown )
347 {
348 const QgsCoordinateReferenceSystem srs = geomDialog.crs();
349 const bool success = QgsOgrProviderUtils::createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs, errorMessage );
350 if ( !success )
351 {
352 return QString();
353 }
354 }
355 else
356 {
357 errorMessage = QObject::tr( "Geometry type not recognised" );
358 QgsDebugMsg( errorMessage );
359 return QString();
360 }
361
362 if ( encoding )
363 *encoding = enc;
364
365 return fileName;
366}
367
368void QgsNewVectorLayerDialog::showHelp()
369{
370 QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
371}
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(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns an icon corresponding to a field type.
Definition: qgsfields.cpp:294
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.
Definition: qgsfilewidget.h:71
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:178
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(QgsWkbTypes::Type 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.
QgsWkbTypes::Type selectedType() const
Returns the selected geometry type.
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.
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:62
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(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
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(Type type) SIP_HOLDGIL
Returns a translated display string type for a WKB type, e.g., the geometry name used in WKT geometry...
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1176
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1201
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs