QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 
32 #include <QPushButton>
33 #include <QComboBox>
34 #include <QFileDialog>
35 #include <QMessageBox>
36 
37 QgsNewVectorLayerDialog::QgsNewVectorLayerDialog( QWidget *parent, Qt::WindowFlags fl )
38  : QDialog( parent, fl )
39 {
40  setupUi( this );
42 
43  connect( mAddAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mAddAttributeButton_clicked );
44  connect( mRemoveAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked );
45  connect( mFileFormatComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged );
46  connect( mTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged );
47  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsNewVectorLayerDialog::showHelp );
48 
49  mAddAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewAttribute.svg" ) ) );
50  mRemoveAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteAttribute.svg" ) ) );
51 
52  mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldText.svg" ) ), tr( "Text Data" ), "String" );
53  mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldInteger.svg" ) ), tr( "Whole Number" ), "Integer" );
54  mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldFloat.svg" ) ), tr( "Decimal Number" ), "Real" );
55  mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldDate.svg" ) ), tr( "Date" ), "Date" );
56 
57  mWidth->setValidator( new QIntValidator( 1, 255, this ) );
58  mPrecision->setValidator( new QIntValidator( 0, 15, this ) );
59 
60  const QgsWkbTypes::Type geomTypes[] =
61  {
67  };
68 
69  for ( const auto type : geomTypes )
70  mGeometryTypeBox->addItem( QgsIconUtils::iconForWkbType( type ), QgsWkbTypes::translatedDisplayString( type ), type );
71  mGeometryTypeBox->setCurrentIndex( -1 );
72 
73  mOkButton = buttonBox->button( QDialogButtonBox::Ok );
74  mOkButton->setEnabled( false );
75 
76  mFileFormatComboBox->addItem( tr( "ESRI Shapefile" ), "ESRI Shapefile" );
77 #if 0
78  // Disabled until provider properly supports editing the created file formats
79  // When enabling this, adapt the window-title of the dialog and the title of all actions showing this dialog.
80  mFileFormatComboBox->addItem( tr( "Comma Separated Value" ), "Comma Separated Value" );
81  mFileFormatComboBox->addItem( tr( "GML" ), "GML" );
82  mFileFormatComboBox->addItem( tr( "Mapinfo File" ), "Mapinfo File" );
83 #endif
84  if ( mFileFormatComboBox->count() == 1 )
85  {
86  mFileFormatComboBox->setVisible( false );
87  mFileFormatLabel->setVisible( false );
88  }
89 
90  mCrsSelector->setShowAccuracyWarnings( true );
91 
92  mFileFormatComboBox->setCurrentIndex( 0 );
93 
94  mFileEncoding->addItems( QgsVectorDataProvider::availableEncodings() );
95 
96  // Use default encoding if none supplied
97  QString enc = QgsSettings().value( QStringLiteral( "/UI/encoding" ), "System" ).toString();
98 
99  // The specified decoding is added if not existing already, and then set current.
100  // This should select it.
101  int encindex = mFileEncoding->findText( enc );
102  if ( encindex < 0 )
103  {
104  mFileEncoding->insertItem( 0, enc );
105  encindex = 0;
106  }
107  mFileEncoding->setCurrentIndex( encindex );
108 
109  mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << QStringLiteral( "id" ) << QStringLiteral( "Integer" ) << QStringLiteral( "10" ) << QString() ) );
110  connect( mNameEdit, &QLineEdit::textChanged, this, &QgsNewVectorLayerDialog::nameChanged );
111  connect( mAttributeView, &QTreeWidget::itemSelectionChanged, this, &QgsNewVectorLayerDialog::selectionChanged );
112  connect( mGeometryTypeBox, static_cast<void( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ]( int index )
113  {
114  QString fileName = mFileName->filePath();
115  if ( !fileName.isEmpty() )
116  {
117  if ( index == 0 )
118  {
119  fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".shp" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".dbf" ) );
120  }
121  else
122  {
123  fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".dbf" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".shp" ) );
124  }
125  mFileName->setFilePath( fileName );
126  }
127  checkOk();
128  } );
129 
130  mAddAttributeButton->setEnabled( false );
131  mRemoveAttributeButton->setEnabled( false );
132 
133  mFileName->setStorageMode( QgsFileWidget::SaveFile );
134  mFileName->setFilter( QgsVectorFileWriter::filterForDriver( mFileFormatComboBox->currentData( Qt::UserRole ).toString() ) );
135  mFileName->setConfirmOverwrite( false );
136  mFileName->setDialogTitle( tr( "Save Layer As" ) );
137  QgsSettings settings;
138  mFileName->setDefaultRoot( settings.value( QStringLiteral( "UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
139  connect( mFileName, &QgsFileWidget::fileChanged, this, [ = ]
140  {
141  QgsSettings settings;
142  QFileInfo tmplFileInfo( mFileName->filePath() );
143  settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
144  checkOk();
145  } );
146 }
147 
148 void QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged( int index )
149 {
150  Q_UNUSED( index )
151  if ( mFileFormatComboBox->currentText() == tr( "ESRI Shapefile" ) )
152  mNameEdit->setMaxLength( 10 );
153  else
154  mNameEdit->setMaxLength( 32767 );
155 }
156 
157 void QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged( int index )
158 {
159  // FIXME: sync with providers/ogr/qgsogrprovider.cpp
160  switch ( index )
161  {
162  case 0: // Text data
163  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 255 )
164  mWidth->setText( QStringLiteral( "80" ) );
165  mPrecision->setEnabled( false );
166  mWidth->setValidator( new QIntValidator( 1, 255, this ) );
167  break;
168 
169  case 1: // Whole number
170  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 10 )
171  mWidth->setText( QStringLiteral( "10" ) );
172  mPrecision->setEnabled( false );
173  mWidth->setValidator( new QIntValidator( 1, 10, this ) );
174  break;
175 
176  case 2: // Decimal number
177  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 20 )
178  mWidth->setText( QStringLiteral( "20" ) );
179  if ( mPrecision->text().toInt() < 1 || mPrecision->text().toInt() > 15 )
180  mPrecision->setText( QStringLiteral( "6" ) );
181 
182  mPrecision->setEnabled( true );
183  mWidth->setValidator( new QIntValidator( 1, 20, this ) );
184  break;
185 
186  default:
187  QgsDebugMsg( QStringLiteral( "unexpected index" ) );
188  break;
189  }
190 }
191 
193 {
195  wkbType = static_cast<QgsWkbTypes::Type>
196  ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
197 
198  if ( mGeometryWithZRadioButton->isChecked() )
199  wkbType = QgsWkbTypes::addZ( wkbType );
200 
201  if ( mGeometryWithMRadioButton->isChecked() )
202  wkbType = QgsWkbTypes::addM( wkbType );
203 
204  return wkbType;
205 }
206 
208 {
209  return mCrsSelector->crs();
210 }
211 
213 {
214  mCrsSelector->setCrs( crs );
215 }
216 
217 void QgsNewVectorLayerDialog::mAddAttributeButton_clicked()
218 {
219  QString myName = mNameEdit->text();
220  QString myWidth = mWidth->text();
221  QString myPrecision = mPrecision->isEnabled() ? mPrecision->text() : QString();
222  //use userrole to avoid translated type string
223  QString myType = mTypeBox->currentData( Qt::UserRole ).toString();
224  mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType << myWidth << myPrecision ) );
225  checkOk();
226  mNameEdit->clear();
227 }
228 
229 void QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked()
230 {
231  delete mAttributeView->currentItem();
232  checkOk();
233 }
234 
235 void QgsNewVectorLayerDialog::attributes( QList< QPair<QString, QString> > &at ) const
236 {
237  QTreeWidgetItemIterator it( mAttributeView );
238  while ( *it )
239  {
240  QTreeWidgetItem *item = *it;
241  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  QgsDebugMsg( QStringLiteral( "appending %1//%2" ).arg( item->text( 0 ), type ) );
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 
260 void QgsNewVectorLayerDialog::nameChanged( const QString &name )
261 {
262  mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
263 }
264 
265 void QgsNewVectorLayerDialog::selectionChanged()
266 {
267  mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
268 }
269 
271 {
272  return mFileName->filePath();
273 }
274 
275 void QgsNewVectorLayerDialog::setFilename( const QString &filename )
276 {
277  mFileName->setFilePath( filename );
278 }
279 
280 void QgsNewVectorLayerDialog::checkOk()
281 {
282  bool ok = ( !mFileName->filePath().isEmpty() && mAttributeView->topLevelItemCount() > 0 && mGeometryTypeBox->currentIndex() != -1 );
283  mOkButton->setEnabled( ok );
284 }
285 
286 // this is static
287 QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs, const QString &initialPath )
288 {
289  QString error;
290  QString res = execAndCreateLayer( error, parent, initialPath, pEnc, crs );
291  if ( res.isEmpty() && error.isEmpty() )
292  res = QString( "" ); // maintain gross earlier API compatibility
293  return res;
294 }
295 
296 QString QgsNewVectorLayerDialog::execAndCreateLayer( QString &errorMessage, QWidget *parent, const QString &initialPath, QString *encoding, const QgsCoordinateReferenceSystem &crs )
297 {
298  errorMessage.clear();
299  QgsNewVectorLayerDialog geomDialog( parent );
300  geomDialog.setCrs( crs );
301  if ( !initialPath.isEmpty() )
302  geomDialog.setFilename( initialPath );
303  if ( geomDialog.exec() == QDialog::Rejected )
304  {
305  return QString();
306  }
307 
308  if ( QFile::exists( geomDialog.filename() ) && QMessageBox::warning( parent, tr( "New ShapeFile Layer" ), tr( "The layer already exists. Are you sure you want to overwrite the existing file?" ),
309  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel ) != QMessageBox::Yes )
310  return QString();
311 
312  QgsWkbTypes::Type geometrytype = geomDialog.selectedType();
313  QString fileformat = geomDialog.selectedFileFormat();
314  QString enc = geomDialog.selectedFileEncoding();
315  QgsDebugMsg( QStringLiteral( "New file format will be: %1" ).arg( fileformat ) );
316 
317  QList< QPair<QString, QString> > attributes;
318  geomDialog.attributes( attributes );
319 
320  QgsSettings settings;
321  QString fileName = geomDialog.filename();
322  if ( fileformat == QLatin1String( "ESRI Shapefile" ) && ( geometrytype != QgsWkbTypes::NoGeometry && !fileName.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) ) )
323  fileName += QLatin1String( ".shp" );
324  else if ( fileformat == QLatin1String( "ESRI Shapefile" ) && ( geometrytype == QgsWkbTypes::NoGeometry && !fileName.endsWith( QLatin1String( ".dbf" ), Qt::CaseInsensitive ) ) )
325  {
326  if ( fileName.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) )
327  fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".shp" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".dbf" ) );
328  else
329  fileName += QLatin1String( ".dbf" );
330  }
331 
332  settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( fileName ).absolutePath() );
333  settings.setValue( QStringLiteral( "UI/encoding" ), enc );
334 
335  //try to create the new layer with OGRProvider instead of QgsVectorFileWriter
336  if ( geometrytype != QgsWkbTypes::Unknown )
337  {
338  QgsCoordinateReferenceSystem srs = geomDialog.crs();
339  bool success = QgsOgrProviderUtils::createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs, errorMessage );
340  if ( !success )
341  {
342  return QString();
343  }
344  }
345  else
346  {
347  errorMessage = QObject::tr( "Geometry type not recognised" );
348  QgsDebugMsg( errorMessage );
349  return QString();
350  }
351 
352  if ( encoding )
353  *encoding = enc;
354 
355  return fileName;
356 }
357 
358 void QgsNewVectorLayerDialog::showHelp()
359 {
360  QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
361 }
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).
@ SaveFile
Select a single new or pre-existing file.
Definition: qgsfilewidget.h:69
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:65
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:156
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
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.
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:1146
static Type addM(Type type) SIP_HOLDGIL
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:1171
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs