QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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
6  email : [email protected]
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"
25 #include "qgsvectordataprovider.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 
39 QgsNewVectorLayerDialog::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 
138 void 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 
147 void 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 
207 void 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 
219 void QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked()
220 {
221  delete mAttributeView->currentItem();
222  checkOk();
223 }
224 
225 void 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 
250 void QgsNewVectorLayerDialog::nameChanged( const QString &name )
251 {
252  mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
253 }
254 
255 void QgsNewVectorLayerDialog::selectionChanged()
256 {
257  mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
258 }
259 
261 {
262  return mFileName->filePath();
263 }
264 
265 void QgsNewVectorLayerDialog::setFilename( const QString &filename )
266 {
267  mFileName->setFilePath( filename );
268 }
269 
270 void 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
277 QString 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 
286 void 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 
319 QString 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 
368 void QgsNewVectorLayerDialog::showHelp()
369 {
370  QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
371 }
QgsNewVectorLayerDialog::runAndCreateLayer
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.
Definition: qgsnewvectorlayerdialog.cpp:277
QgsFileWidget::fileChanged
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
QgsWkbTypes::Point
@ Point
Definition: qgswkbtypes.h:72
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
qgsgui.h
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsWkbTypes::LineString
@ LineString
Definition: qgswkbtypes.h:73
QgsWkbTypes::addZ
static Type addZ(Type type) SIP_HOLDGIL
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1176
qgis.h
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:69
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsNewVectorLayerDialog::attributes
void attributes(QList< QPair< QString, QString > > &at) const
Appends the chosen attribute names and types to at.
Definition: qgsnewvectorlayerdialog.cpp:225
QgsFileWidget::SaveFile
@ SaveFile
Select a single new or pre-existing file.
Definition: qgsfilewidget.h:71
qgsapplication.h
QgsGui::enableAutoGeometryRestore
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:180
QgsWkbTypes::addM
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1201
QgsNewVectorLayerDialog::filename
QString filename() const
Returns the name for the new layer.
Definition: qgsnewvectorlayerdialog.cpp:260
QgsWkbTypes::Unknown
@ Unknown
Definition: qgswkbtypes.h:71
qgsproviderregistry.h
QgsNewVectorLayerDialog::accept
void accept() override
Definition: qgsnewvectorlayerdialog.cpp:308
qgsvectordataprovider.h
QgsNewVectorLayerDialog::QgsNewVectorLayerDialog
QgsNewVectorLayerDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
New dialog constructor.
Definition: qgsnewvectorlayerdialog.cpp:39
qgsvariantutils.h
QgsNewVectorLayerDialog::setCrs
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs value for the new layer in the dialog.
Definition: qgsnewvectorlayerdialog.cpp:202
QgsNewVectorLayerDialog::execAndCreateLayer
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.
Definition: qgsnewvectorlayerdialog.cpp:319
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:279
qgsfileutils.h
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:211
QgsFields::iconForFieldType
static QIcon iconForFieldType(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns an icon corresponding to a field type.
Definition: qgsfields.cpp:294
QgsNewVectorLayerDialog::setFilename
void setFilename(const QString &filename)
Sets the initial file name to show in the dialog.
Definition: qgsnewvectorlayerdialog.cpp:265
QgsVectorDataProvider::availableEncodings
static QStringList availableEncodings()
Returns a list of available encodings.
Definition: qgsvectordataprovider.cpp:663
QgsVariantUtils::typeToDisplayString
static QString typeToDisplayString(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns a user-friendly translated string representing a QVariant type.
Definition: qgsvariantutils.cpp:19
QgsFileUtils::ensureFileNameHasExtension
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
Definition: qgsfileutils.cpp:111
QgsWkbTypes::NoGeometry
@ NoGeometry
Definition: qgswkbtypes.h:85
qgsnewvectorlayerdialog.h
QgsWkbTypes::translatedDisplayString
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...
Definition: qgswkbtypes.cpp:155
QgsHelp::openHelp
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
QgsIconUtils::iconForWkbType
static QIcon iconForWkbType(QgsWkbTypes::Type type)
Returns the icon for a vector layer whose geometry type is provided.
Definition: qgsiconutils.cpp:25
QgsWkbTypes::Polygon
@ Polygon
Definition: qgswkbtypes.h:74
QgsWkbTypes::MultiPoint
@ MultiPoint
Definition: qgswkbtypes.h:76
qgssettings.h
qgsiconutils.h
QgsNewVectorLayerDialog
Definition: qgsnewvectorlayerdialog.h:32
qgsfilewidget.h
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
qgslogger.h
qgsvectorfilewriter.h
QgsNewVectorLayerDialog::selectedFileEncoding
QString selectedFileEncoding() const
Returns the file format for storage.
Definition: qgsnewvectorlayerdialog.cpp:245
QgsVectorFileWriter::filterForDriver
static QString filterForDriver(const QString &driverName)
Creates a filter for an OGR driver key.
Definition: qgsvectorfilewriter.cpp:3765
QgsNewVectorLayerDialog::crs
QgsCoordinateReferenceSystem crs() const
Returns the selected CRS for the new layer.
Definition: qgsnewvectorlayerdialog.cpp:197
qgscoordinatereferencesystem.h
QgsNewVectorLayerDialog::selectedFileFormat
QString selectedFileFormat() const
Returns the file format for storage.
Definition: qgsnewvectorlayerdialog.cpp:238
QgsNewVectorLayerDialog::selectedType
QgsWkbTypes::Type selectedType() const
Returns the selected geometry type.
Definition: qgsnewvectorlayerdialog.cpp:182