QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 : marco.hugentobler@autoform.ch
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 
18 #include "qgsdataitem.h"
20 #include "qgsapplication.h"
21 #include "qgsfilewidget.h"
22 #include "qgis.h"
23 #include "qgslogger.h"
25 #include "qgsproviderregistry.h"
26 #include "qgsvectordataprovider.h"
27 #include "qgsvectorfilewriter.h"
28 #include "qgssettings.h"
29 #include "qgsogrprovider.h"
30 #include "qgsgui.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( QgsLayerItem::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  mFileFormatComboBox->setCurrentIndex( 0 );
91 
92  mFileEncoding->addItems( QgsVectorDataProvider::availableEncodings() );
93 
94  // Use default encoding if none supplied
95  QString enc = QgsSettings().value( QStringLiteral( "/UI/encoding" ), "System" ).toString();
96 
97  // The specified decoding is added if not existing already, and then set current.
98  // This should select it.
99  int encindex = mFileEncoding->findText( enc );
100  if ( encindex < 0 )
101  {
102  mFileEncoding->insertItem( 0, enc );
103  encindex = 0;
104  }
105  mFileEncoding->setCurrentIndex( encindex );
106 
107  mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << QStringLiteral( "id" ) << QStringLiteral( "Integer" ) << QStringLiteral( "10" ) << QString() ) );
108  connect( mNameEdit, &QLineEdit::textChanged, this, &QgsNewVectorLayerDialog::nameChanged );
109  connect( mAttributeView, &QTreeWidget::itemSelectionChanged, this, &QgsNewVectorLayerDialog::selectionChanged );
110  connect( mGeometryTypeBox, static_cast<void( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ]( int index )
111  {
112  QString fileName = mFileName->filePath();
113  if ( !fileName.isEmpty() )
114  {
115  if ( index == 0 )
116  {
117  fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".shp" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".dbf" ) );
118  }
119  else
120  {
121  fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".dbf" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".shp" ) );
122  }
123  mFileName->setFilePath( fileName );
124  }
125  checkOk();
126  } );
127 
128  mAddAttributeButton->setEnabled( false );
129  mRemoveAttributeButton->setEnabled( false );
130 
131  mFileName->setStorageMode( QgsFileWidget::SaveFile );
132  mFileName->setFilter( QgsVectorFileWriter::filterForDriver( mFileFormatComboBox->currentData( Qt::UserRole ).toString() ) );
133  mFileName->setConfirmOverwrite( false );
134  mFileName->setDialogTitle( tr( "Save Layer As" ) );
135  QgsSettings settings;
136  mFileName->setDefaultRoot( settings.value( QStringLiteral( "UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
137  connect( mFileName, &QgsFileWidget::fileChanged, this, [ = ]
138  {
139  QgsSettings settings;
140  QFileInfo tmplFileInfo( mFileName->filePath() );
141  settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
142  checkOk();
143  } );
144 }
145 
146 void QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged( int index )
147 {
148  Q_UNUSED( index )
149  if ( mFileFormatComboBox->currentText() == tr( "ESRI Shapefile" ) )
150  mNameEdit->setMaxLength( 10 );
151  else
152  mNameEdit->setMaxLength( 32767 );
153 }
154 
155 void QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged( int index )
156 {
157  // FIXME: sync with providers/ogr/qgsogrprovider.cpp
158  switch ( index )
159  {
160  case 0: // Text data
161  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 255 )
162  mWidth->setText( QStringLiteral( "80" ) );
163  mPrecision->setEnabled( false );
164  mWidth->setValidator( new QIntValidator( 1, 255, this ) );
165  break;
166 
167  case 1: // Whole number
168  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 10 )
169  mWidth->setText( QStringLiteral( "10" ) );
170  mPrecision->setEnabled( false );
171  mWidth->setValidator( new QIntValidator( 1, 10, this ) );
172  break;
173 
174  case 2: // Decimal number
175  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 20 )
176  mWidth->setText( QStringLiteral( "20" ) );
177  if ( mPrecision->text().toInt() < 1 || mPrecision->text().toInt() > 15 )
178  mPrecision->setText( QStringLiteral( "6" ) );
179 
180  mPrecision->setEnabled( true );
181  mWidth->setValidator( new QIntValidator( 1, 20, this ) );
182  break;
183 
184  default:
185  QgsDebugMsg( QStringLiteral( "unexpected index" ) );
186  break;
187  }
188 }
189 
191 {
193  wkbType = static_cast<QgsWkbTypes::Type>
194  ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
195 
196  if ( mGeometryWithZRadioButton->isChecked() )
197  wkbType = QgsWkbTypes::addZ( wkbType );
198 
199  if ( mGeometryWithMRadioButton->isChecked() )
200  wkbType = QgsWkbTypes::addM( wkbType );
201 
202  return wkbType;
203 }
204 
206 {
207  return mCrsSelector->crs();
208 }
209 
211 {
212  mCrsSelector->setCrs( crs );
213 }
214 
215 void QgsNewVectorLayerDialog::mAddAttributeButton_clicked()
216 {
217  QString myName = mNameEdit->text();
218  QString myWidth = mWidth->text();
219  QString myPrecision = mPrecision->isEnabled() ? mPrecision->text() : QString();
220  //use userrole to avoid translated type string
221  QString myType = mTypeBox->currentData( Qt::UserRole ).toString();
222  mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType << myWidth << myPrecision ) );
223  checkOk();
224  mNameEdit->clear();
225 }
226 
227 void QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked()
228 {
229  delete mAttributeView->currentItem();
230  checkOk();
231 }
232 
233 void QgsNewVectorLayerDialog::attributes( QList< QPair<QString, QString> > &at ) const
234 {
235  QTreeWidgetItemIterator it( mAttributeView );
236  while ( *it )
237  {
238  QTreeWidgetItem *item = *it;
239  QString type = QStringLiteral( "%1;%2;%3" ).arg( item->text( 1 ), item->text( 2 ), item->text( 3 ) );
240  at.push_back( qMakePair( item->text( 0 ), type ) );
241  QgsDebugMsg( QStringLiteral( "appending %1//%2" ).arg( item->text( 0 ), type ) );
242  ++it;
243  }
244 }
245 
247 {
248  //use userrole to avoid translated type string
249  QString myType = mFileFormatComboBox->currentData( Qt::UserRole ).toString();
250  return myType;
251 }
252 
254 {
255  return mFileEncoding->currentText();
256 }
257 
258 void QgsNewVectorLayerDialog::nameChanged( const QString &name )
259 {
260  mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
261 }
262 
263 void QgsNewVectorLayerDialog::selectionChanged()
264 {
265  mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
266 }
267 
269 {
270  return mFileName->filePath();
271 }
272 
273 void QgsNewVectorLayerDialog::setFilename( const QString &filename )
274 {
275  mFileName->setFilePath( filename );
276 }
277 
278 void QgsNewVectorLayerDialog::checkOk()
279 {
280  bool ok = ( !mFileName->filePath().isEmpty() && mAttributeView->topLevelItemCount() > 0 && mGeometryTypeBox->currentIndex() != -1 );
281  mOkButton->setEnabled( ok );
282 }
283 
284 // this is static
285 QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs, const QString &initialPath )
286 {
287  QString error;
288  QString res = execAndCreateLayer( error, parent, initialPath, pEnc, crs );
289  if ( res.isEmpty() && error.isEmpty() )
290  res = QString( "" ); // maintain gross earlier API compatibility
291  return res;
292 }
293 
294 QString QgsNewVectorLayerDialog::execAndCreateLayer( QString &errorMessage, QWidget *parent, const QString &initialPath, QString *encoding, const QgsCoordinateReferenceSystem &crs )
295 {
296  errorMessage.clear();
297  QgsNewVectorLayerDialog geomDialog( parent );
298  geomDialog.setCrs( crs );
299  if ( !initialPath.isEmpty() )
300  geomDialog.setFilename( initialPath );
301  if ( geomDialog.exec() == QDialog::Rejected )
302  {
303  return QString();
304  }
305 
306  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?" ),
307  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel ) != QMessageBox::Yes )
308  return QString();
309 
310  QgsWkbTypes::Type geometrytype = geomDialog.selectedType();
311  QString fileformat = geomDialog.selectedFileFormat();
312  QString enc = geomDialog.selectedFileEncoding();
313  QgsDebugMsg( QStringLiteral( "New file format will be: %1" ).arg( fileformat ) );
314 
315  QList< QPair<QString, QString> > attributes;
316  geomDialog.attributes( attributes );
317 
318  QgsSettings settings;
319  QString fileName = geomDialog.filename();
320  if ( fileformat == QLatin1String( "ESRI Shapefile" ) && ( geometrytype != QgsWkbTypes::NoGeometry && !fileName.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) ) )
321  fileName += QLatin1String( ".shp" );
322  else if ( fileformat == QLatin1String( "ESRI Shapefile" ) && ( geometrytype == QgsWkbTypes::NoGeometry && !fileName.endsWith( QLatin1String( ".dbf" ), Qt::CaseInsensitive ) ) )
323  {
324  if ( fileName.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) )
325  fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".shp" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".dbf" ) );
326  else
327  fileName += QLatin1String( ".dbf" );
328  }
329 
330  settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( fileName ).absolutePath() );
331  settings.setValue( QStringLiteral( "UI/encoding" ), enc );
332 
333  //try to create the new layer with OGRProvider instead of QgsVectorFileWriter
334  if ( geometrytype != QgsWkbTypes::Unknown )
335  {
336  QgsCoordinateReferenceSystem srs = geomDialog.crs();
337  bool success = QgsOgrProviderUtils::createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs, errorMessage );
338  if ( !success )
339  {
340  return QString();
341  }
342  }
343  else
344  {
345  errorMessage = QObject::tr( "Geometry type not recognised" );
346  QgsDebugMsg( errorMessage );
347  return QString();
348  }
349 
350  if ( encoding )
351  *encoding = enc;
352 
353  return fileName;
354 }
355 
356 void QgsNewVectorLayerDialog::showHelp()
357 {
358  QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
359 }
static QIcon getThemeIcon(const QString &name)
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.
Definition: qgsdataitem.cpp:57
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 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