QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 
31 #include <QPushButton>
32 #include <QComboBox>
33 #include <QFileDialog>
34 #include <QMessageBox>
35 
36 QgsNewVectorLayerDialog::QgsNewVectorLayerDialog( QWidget *parent, Qt::WindowFlags fl )
37  : QDialog( parent, fl )
38 {
39  setupUi( this );
41 
42  connect( mAddAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mAddAttributeButton_clicked );
43  connect( mRemoveAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked );
44  connect( mFileFormatComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged );
45  connect( mTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged );
46  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsNewVectorLayerDialog::showHelp );
47 
48  mAddAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewAttribute.svg" ) ) );
49  mRemoveAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteAttribute.svg" ) ) );
50 
51  mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldText.svg" ) ), tr( "Text Data" ), "String" );
52  mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldInteger.svg" ) ), tr( "Whole Number" ), "Integer" );
53  mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldFloat.svg" ) ), tr( "Decimal Number" ), "Real" );
54  mTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldDate.svg" ) ), tr( "Date" ), "Date" );
55 
56  mWidth->setValidator( new QIntValidator( 1, 255, this ) );
57  mPrecision->setValidator( new QIntValidator( 0, 15, this ) );
58 
59  mGeometryTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconTableLayer.svg" ) ), tr( "No Geometry" ), QgsWkbTypes::NoGeometry );
60  mGeometryTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconPointLayer.svg" ) ), tr( "Point" ), QgsWkbTypes::Point );
61  mGeometryTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconPointLayer.svg" ) ), tr( "MultiPoint" ), QgsWkbTypes::MultiPoint );
62  mGeometryTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconLineLayer.svg" ) ), tr( "Line" ), QgsWkbTypes::LineString );
63  mGeometryTypeBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconPolygonLayer.svg" ) ), tr( "Polygon" ), QgsWkbTypes::Polygon );
64  mGeometryTypeBox->setCurrentIndex( -1 );
65 
66  mOkButton = buttonBox->button( QDialogButtonBox::Ok );
67  mOkButton->setEnabled( false );
68 
69  mFileFormatComboBox->addItem( tr( "ESRI Shapefile" ), "ESRI Shapefile" );
70 #if 0
71  // Disabled until provider properly supports editing the created file formats
72  // When enabling this, adapt the window-title of the dialog and the title of all actions showing this dialog.
73  mFileFormatComboBox->addItem( tr( "Comma Separated Value" ), "Comma Separated Value" );
74  mFileFormatComboBox->addItem( tr( "GML" ), "GML" );
75  mFileFormatComboBox->addItem( tr( "Mapinfo File" ), "Mapinfo File" );
76 #endif
77  if ( mFileFormatComboBox->count() == 1 )
78  {
79  mFileFormatComboBox->setVisible( false );
80  mFileFormatLabel->setVisible( false );
81  }
82 
83  mFileFormatComboBox->setCurrentIndex( 0 );
84 
85  mFileEncoding->addItems( QgsVectorDataProvider::availableEncodings() );
86 
87  // Use default encoding if none supplied
88  QString enc = QgsSettings().value( QStringLiteral( "/UI/encoding" ), "System" ).toString();
89 
90  // The specified decoding is added if not existing already, and then set current.
91  // This should select it.
92  int encindex = mFileEncoding->findText( enc );
93  if ( encindex < 0 )
94  {
95  mFileEncoding->insertItem( 0, enc );
96  encindex = 0;
97  }
98  mFileEncoding->setCurrentIndex( encindex );
99 
100  mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << QStringLiteral( "id" ) << QStringLiteral( "Integer" ) << QStringLiteral( "10" ) << QString() ) );
101  connect( mNameEdit, &QLineEdit::textChanged, this, &QgsNewVectorLayerDialog::nameChanged );
102  connect( mAttributeView, &QTreeWidget::itemSelectionChanged, this, &QgsNewVectorLayerDialog::selectionChanged );
103  connect( mGeometryTypeBox, static_cast<void( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ]( int index )
104  {
105  QString fileName = mFileName->filePath();
106  if ( !fileName.isEmpty() )
107  {
108  if ( index == 0 )
109  {
110  fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".shp" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".dbf" ) );
111  }
112  else
113  {
114  fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".dbf" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".shp" ) );
115  }
116  mFileName->setFilePath( fileName );
117  }
118  checkOk();
119  } );
120 
121  mAddAttributeButton->setEnabled( false );
122  mRemoveAttributeButton->setEnabled( false );
123 
124  mFileName->setStorageMode( QgsFileWidget::SaveFile );
125  mFileName->setFilter( QgsVectorFileWriter::filterForDriver( mFileFormatComboBox->currentData( Qt::UserRole ).toString() ) );
126  mFileName->setConfirmOverwrite( false );
127  mFileName->setDialogTitle( tr( "Save Layer As" ) );
128  QgsSettings settings;
129  mFileName->setDefaultRoot( settings.value( QStringLiteral( "UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
130  connect( mFileName, &QgsFileWidget::fileChanged, this, [ = ]
131  {
132  QgsSettings settings;
133  QFileInfo tmplFileInfo( mFileName->filePath() );
134  settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
135  checkOk();
136  } );
137 }
138 
139 void 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 
148 void 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  QgsDebugMsg( QStringLiteral( "unexpected index" ) );
179  break;
180  }
181 }
182 
184 {
186  wkbType = static_cast<QgsWkbTypes::Type>
187  ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
188 
189  if ( mGeometryWithZRadioButton->isChecked() )
190  wkbType = QgsWkbTypes::addZ( wkbType );
191 
192  if ( mGeometryWithMRadioButton->isChecked() )
193  wkbType = QgsWkbTypes::addM( wkbType );
194 
195  return wkbType;
196 }
197 
199 {
200  return mCrsSelector->crs();
201 }
202 
204 {
205  mCrsSelector->setCrs( crs );
206 }
207 
208 void QgsNewVectorLayerDialog::mAddAttributeButton_clicked()
209 {
210  QString myName = mNameEdit->text();
211  QString myWidth = mWidth->text();
212  QString myPrecision = mPrecision->isEnabled() ? mPrecision->text() : QString();
213  //use userrole to avoid translated type string
214  QString myType = mTypeBox->currentData( Qt::UserRole ).toString();
215  mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType << myWidth << myPrecision ) );
216  checkOk();
217  mNameEdit->clear();
218 }
219 
220 void QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked()
221 {
222  delete mAttributeView->currentItem();
223  checkOk();
224 }
225 
226 void QgsNewVectorLayerDialog::attributes( QList< QPair<QString, QString> > &at ) const
227 {
228  QTreeWidgetItemIterator it( mAttributeView );
229  while ( *it )
230  {
231  QTreeWidgetItem *item = *it;
232  QString type = QStringLiteral( "%1;%2;%3" ).arg( item->text( 1 ), item->text( 2 ), item->text( 3 ) );
233  at.push_back( qMakePair( item->text( 0 ), type ) );
234  QgsDebugMsg( QStringLiteral( "appending %1//%2" ).arg( item->text( 0 ), type ) );
235  ++it;
236  }
237 }
238 
240 {
241  //use userrole to avoid translated type string
242  QString myType = mFileFormatComboBox->currentData( Qt::UserRole ).toString();
243  return myType;
244 }
245 
247 {
248  return mFileEncoding->currentText();
249 }
250 
251 void QgsNewVectorLayerDialog::nameChanged( const QString &name )
252 {
253  mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
254 }
255 
256 void QgsNewVectorLayerDialog::selectionChanged()
257 {
258  mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
259 }
260 
262 {
263  return mFileName->filePath();
264 }
265 
266 void QgsNewVectorLayerDialog::setFilename( const QString &filename )
267 {
268  mFileName->setFilePath( filename );
269 }
270 
271 void QgsNewVectorLayerDialog::checkOk()
272 {
273  bool ok = ( !mFileName->filePath().isEmpty() && mAttributeView->topLevelItemCount() > 0 && mGeometryTypeBox->currentIndex() != -1 );
274  mOkButton->setEnabled( ok );
275 }
276 
277 // this is static
278 QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs, const QString &initialPath )
279 {
280  QString error;
281  QString res = execAndCreateLayer( error, parent, initialPath, pEnc, crs );
282  if ( res.isEmpty() && error.isEmpty() )
283  res = QString( "" ); // maintain gross earlier API compatibility
284  return res;
285 }
286 
287 QString QgsNewVectorLayerDialog::execAndCreateLayer( QString &errorMessage, QWidget *parent, const QString &initialPath, QString *encoding, const QgsCoordinateReferenceSystem &crs )
288 {
289  errorMessage.clear();
290  QgsNewVectorLayerDialog geomDialog( parent );
291  geomDialog.setCrs( crs );
292  if ( !initialPath.isEmpty() )
293  geomDialog.setFilename( initialPath );
294  if ( geomDialog.exec() == QDialog::Rejected )
295  {
296  return QString();
297  }
298 
299  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?" ),
300  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel ) != QMessageBox::Yes )
301  return QString();
302 
303  QgsWkbTypes::Type geometrytype = geomDialog.selectedType();
304  QString fileformat = geomDialog.selectedFileFormat();
305  QString enc = geomDialog.selectedFileEncoding();
306  QgsDebugMsg( QStringLiteral( "New file format will be: %1" ).arg( fileformat ) );
307 
308  QList< QPair<QString, QString> > attributes;
309  geomDialog.attributes( attributes );
310 
311  QgsSettings settings;
312  QString fileName = geomDialog.filename();
313  if ( fileformat == QLatin1String( "ESRI Shapefile" ) && ( geometrytype != QgsWkbTypes::NoGeometry && !fileName.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) ) )
314  fileName += QLatin1String( ".shp" );
315  else if ( fileformat == QLatin1String( "ESRI Shapefile" ) && ( geometrytype == QgsWkbTypes::NoGeometry && !fileName.endsWith( QLatin1String( ".dbf" ), Qt::CaseInsensitive ) ) )
316  {
317  if ( fileName.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) )
318  fileName = fileName.replace( fileName.lastIndexOf( QLatin1String( ".shp" ), -1, Qt::CaseInsensitive ), 4, QLatin1String( ".dbf" ) );
319  else
320  fileName += QLatin1String( ".dbf" );
321  }
322 
323  settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( fileName ).absolutePath() );
324  settings.setValue( QStringLiteral( "UI/encoding" ), enc );
325 
326  //try to create the new layer with OGRProvider instead of QgsVectorFileWriter
327  if ( geometrytype != QgsWkbTypes::Unknown )
328  {
329  QgsCoordinateReferenceSystem srs = geomDialog.crs();
330  bool success = QgsOgrProviderUtils::createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs, errorMessage );
331  if ( !success )
332  {
333  return QString();
334  }
335  }
336  else
337  {
338  errorMessage = QObject::tr( "Geometry type not recognised" );
339  QgsDebugMsg( errorMessage );
340  return QString();
341  }
342 
343  if ( encoding )
344  *encoding = enc;
345 
346  return fileName;
347 }
348 
349 void QgsNewVectorLayerDialog::showHelp()
350 {
351  QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
352 }
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:278
QgsFileWidget::fileChanged
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:626
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:174
qgsgui.h
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:51
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:1139
qgis.h
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
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:226
QgsFileWidget::SaveFile
@ SaveFile
Select a single new or pre-existing file.
Definition: qgsfilewidget.h:69
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:139
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:1164
QgsNewVectorLayerDialog::filename
QString filename() const
Returns the name for the new layer.
Definition: qgsnewvectorlayerdialog.cpp:261
QgsWkbTypes::Unknown
@ Unknown
Definition: qgswkbtypes.h:71
qgsproviderregistry.h
qgsvectordataprovider.h
QgsNewVectorLayerDialog::QgsNewVectorLayerDialog
QgsNewVectorLayerDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
New dialog constructor.
Definition: qgsnewvectorlayerdialog.cpp:36
QgsNewVectorLayerDialog::setCrs
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs value for the new layer in the dialog.
Definition: qgsnewvectorlayerdialog.cpp:203
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:287
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:289
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:206
QgsNewVectorLayerDialog::setFilename
void setFilename(const QString &filename)
Sets the initial file name to show in the dialog.
Definition: qgsnewvectorlayerdialog.cpp:266
QgsVectorDataProvider::availableEncodings
static QStringList availableEncodings()
Returns a list of available encodings.
Definition: qgsvectordataprovider.cpp:649
QgsWkbTypes::NoGeometry
@ NoGeometry
Definition: qgswkbtypes.h:85
QgsGui::instance
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:63
qgsnewvectorlayerdialog.h
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
QgsWkbTypes::Polygon
@ Polygon
Definition: qgswkbtypes.h:74
QgsWkbTypes::MultiPoint
@ MultiPoint
Definition: qgswkbtypes.h:76
qgssettings.h
QgsNewVectorLayerDialog
Definition: qgsnewvectorlayerdialog.h:33
qgsfilewidget.h
qgslogger.h
qgsvectorfilewriter.h
QgsNewVectorLayerDialog::selectedFileEncoding
QString selectedFileEncoding() const
Returns the file format for storage.
Definition: qgsnewvectorlayerdialog.cpp:246
QgsVectorFileWriter::filterForDriver
static QString filterForDriver(const QString &driverName)
Creates a filter for an OGR driver key.
Definition: qgsvectorfilewriter.cpp:3511
QgsNewVectorLayerDialog::crs
QgsCoordinateReferenceSystem crs() const
Returns the selected CRS for the new layer.
Definition: qgsnewvectorlayerdialog.cpp:198
qgscoordinatereferencesystem.h
QgsNewVectorLayerDialog::selectedFileFormat
QString selectedFileFormat() const
Returns the file format for storage.
Definition: qgsnewvectorlayerdialog.cpp:239
QgsNewVectorLayerDialog::selectedType
QgsWkbTypes::Type selectedType() const
Returns the selected geometry type.
Definition: qgsnewvectorlayerdialog.cpp:183