QGIS API Documentation  3.6.0-Noosa (5873452)
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 
29 #include <QPushButton>
30 #include <QComboBox>
31 #include <QLibrary>
32 #include <QFileDialog>
33 #include <QMessageBox>
34 
35 QgsNewVectorLayerDialog::QgsNewVectorLayerDialog( QWidget *parent, Qt::WindowFlags fl )
36  : QDialog( parent, fl )
37 {
38  setupUi( this );
39 
40  connect( mAddAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mAddAttributeButton_clicked );
41  connect( mRemoveAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked );
42  connect( mFileFormatComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged );
43  connect( mTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged );
44  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsNewVectorLayerDialog::showHelp );
45 
46  QgsSettings settings;
47  restoreGeometry( settings.value( QStringLiteral( "Windows/NewVectorLayer/geometry" ) ).toByteArray() );
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  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 
65  mOkButton = buttonBox->button( QDialogButtonBox::Ok );
66  mOkButton->setEnabled( false );
67 
68  mFileFormatComboBox->addItem( tr( "ESRI Shapefile" ), "ESRI Shapefile" );
69 #if 0
70  // Disabled until provider properly supports editing the created file formats
71  // When enabling this, adapt the window-title of the dialog and the title of all actions showing this dialog.
72  mFileFormatComboBox->addItem( tr( "Comma Separated Value" ), "Comma Separated Value" );
73  mFileFormatComboBox->addItem( tr( "GML" ), "GML" );
74  mFileFormatComboBox->addItem( tr( "Mapinfo File" ), "Mapinfo File" );
75 #endif
76  if ( mFileFormatComboBox->count() == 1 )
77  {
78  mFileFormatComboBox->setVisible( false );
79  mFileFormatLabel->setVisible( false );
80  }
81 
82  mFileFormatComboBox->setCurrentIndex( 0 );
83 
84  mFileEncoding->addItems( QgsVectorDataProvider::availableEncodings() );
85 
86  // Use default encoding if none supplied
87  QString enc = QgsSettings().value( QStringLiteral( "/UI/encoding" ), "System" ).toString();
88 
89  // The specified decoding is added if not existing already, and then set current.
90  // This should select it.
91  int encindex = mFileEncoding->findText( enc );
92  if ( encindex < 0 )
93  {
94  mFileEncoding->insertItem( 0, enc );
95  encindex = 0;
96  }
97  mFileEncoding->setCurrentIndex( encindex );
98 
99  mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << QStringLiteral( "id" ) << QStringLiteral( "Integer" ) << QStringLiteral( "10" ) << QString() ) );
100  connect( mNameEdit, &QLineEdit::textChanged, this, &QgsNewVectorLayerDialog::nameChanged );
101  connect( mAttributeView, &QTreeWidget::itemSelectionChanged, this, &QgsNewVectorLayerDialog::selectionChanged );
102 
103  mAddAttributeButton->setEnabled( false );
104  mRemoveAttributeButton->setEnabled( false );
105 
106  mFileName->setStorageMode( QgsFileWidget::SaveFile );
107  mFileName->setFilter( QgsVectorFileWriter::filterForDriver( mFileFormatComboBox->currentData( Qt::UserRole ).toString() ) );
108  mFileName->setConfirmOverwrite( false );
109  mFileName->setDialogTitle( tr( "Save Layer As" ) );
110  mFileName->setDefaultRoot( settings.value( QStringLiteral( "UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
111  connect( mFileName, &QgsFileWidget::fileChanged, this, [ = ]
112  {
113  QgsSettings settings;
114  QFileInfo tmplFileInfo( mFileName->filePath() );
115  settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
116  checkOk();
117  } );
118 }
119 
121 {
122  QgsSettings settings;
123  settings.setValue( QStringLiteral( "Windows/NewVectorLayer/geometry" ), saveGeometry() );
124 }
125 
126 void QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged( int index )
127 {
128  Q_UNUSED( index );
129  if ( mFileFormatComboBox->currentText() == tr( "ESRI Shapefile" ) )
130  mNameEdit->setMaxLength( 10 );
131  else
132  mNameEdit->setMaxLength( 32767 );
133 }
134 
135 void QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged( int index )
136 {
137  // FIXME: sync with providers/ogr/qgsogrprovider.cpp
138  switch ( index )
139  {
140  case 0: // Text data
141  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 255 )
142  mWidth->setText( QStringLiteral( "80" ) );
143  mPrecision->setEnabled( false );
144  mWidth->setValidator( new QIntValidator( 1, 255, this ) );
145  break;
146 
147  case 1: // Whole number
148  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 10 )
149  mWidth->setText( QStringLiteral( "10" ) );
150  mPrecision->setEnabled( false );
151  mWidth->setValidator( new QIntValidator( 1, 10, this ) );
152  break;
153 
154  case 2: // Decimal number
155  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 20 )
156  mWidth->setText( QStringLiteral( "20" ) );
157  mPrecision->setEnabled( true );
158  mWidth->setValidator( new QIntValidator( 1, 20, this ) );
159  break;
160 
161  default:
162  QgsDebugMsg( QStringLiteral( "unexpected index" ) );
163  break;
164  }
165 }
166 
168 {
170  wkbType = static_cast<QgsWkbTypes::Type>
171  ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
172 
173  if ( mGeometryWithZRadioButton->isChecked() )
174  wkbType = QgsWkbTypes::addZ( wkbType );
175 
176  if ( mGeometryWithMRadioButton->isChecked() )
177  wkbType = QgsWkbTypes::addM( wkbType );
178 
179  return wkbType;
180 }
181 
183 {
184  return mCrsSelector->crs();
185 }
186 
188 {
189  mCrsSelector->setCrs( crs );
190 }
191 
192 void QgsNewVectorLayerDialog::mAddAttributeButton_clicked()
193 {
194  QString myName = mNameEdit->text();
195  QString myWidth = mWidth->text();
196  QString myPrecision = mPrecision->isEnabled() ? mPrecision->text() : QString();
197  //use userrole to avoid translated type string
198  QString myType = mTypeBox->currentData( Qt::UserRole ).toString();
199  mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType << myWidth << myPrecision ) );
200  checkOk();
201  mNameEdit->clear();
202 }
203 
204 void QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked()
205 {
206  delete mAttributeView->currentItem();
207  checkOk();
208 }
209 
210 void QgsNewVectorLayerDialog::attributes( QList< QPair<QString, QString> > &at ) const
211 {
212  QTreeWidgetItemIterator it( mAttributeView );
213  while ( *it )
214  {
215  QTreeWidgetItem *item = *it;
216  QString type = QStringLiteral( "%1;%2;%3" ).arg( item->text( 1 ), item->text( 2 ), item->text( 3 ) );
217  at.push_back( qMakePair( item->text( 0 ), type ) );
218  QgsDebugMsg( QStringLiteral( "appending %1//%2" ).arg( item->text( 0 ), type ) );
219  ++it;
220  }
221 }
222 
224 {
225  //use userrole to avoid translated type string
226  QString myType = mFileFormatComboBox->currentData( Qt::UserRole ).toString();
227  return myType;
228 }
229 
231 {
232  return mFileEncoding->currentText();
233 }
234 
235 void QgsNewVectorLayerDialog::nameChanged( const QString &name )
236 {
237  mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
238 }
239 
240 void QgsNewVectorLayerDialog::selectionChanged()
241 {
242  mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
243 }
244 
246 {
247  return mFileName->filePath();
248 }
249 
251 {
252  mFileName->setFilePath( filename );
253 }
254 
255 void QgsNewVectorLayerDialog::checkOk()
256 {
257  bool ok = ( !mFileName->filePath().isEmpty() && mAttributeView->topLevelItemCount() > 0 );
258  mOkButton->setEnabled( ok );
259 }
260 
261 // this is static
262 QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs, const QString &initialPath )
263 {
264  QString error;
265  QString res = execAndCreateLayer( error, parent, initialPath, pEnc, crs );
266  if ( res.isEmpty() && error.isEmpty() )
267  res = QString( "" ); // maintain gross earlier API compatibility
268  return res;
269 }
270 
271 QString QgsNewVectorLayerDialog::execAndCreateLayer( QString &errorMessage, QWidget *parent, const QString &initialPath, QString *encoding, const QgsCoordinateReferenceSystem &crs )
272 {
273  errorMessage.clear();
274  QgsNewVectorLayerDialog geomDialog( parent );
275  geomDialog.setCrs( crs );
276  if ( !initialPath.isEmpty() )
277  geomDialog.setFilename( initialPath );
278  if ( geomDialog.exec() == QDialog::Rejected )
279  {
280  return QString();
281  }
282 
283  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?" ),
284  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel ) != QMessageBox::Yes )
285  return QString();
286 
287  QgsWkbTypes::Type geometrytype = geomDialog.selectedType();
288  QString fileformat = geomDialog.selectedFileFormat();
289  QString enc = geomDialog.selectedFileEncoding();
290  QgsDebugMsg( QStringLiteral( "New file format will be: %1" ).arg( fileformat ) );
291 
292  QList< QPair<QString, QString> > attributes;
293  geomDialog.attributes( attributes );
294 
295  QgsSettings settings;
296  QString fileName = geomDialog.filename();
297  if ( fileformat == QLatin1String( "ESRI Shapefile" ) && !fileName.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) )
298  fileName += QLatin1String( ".shp" );
299 
300  settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( fileName ).absolutePath() );
301  settings.setValue( QStringLiteral( "UI/encoding" ), enc );
302 
303  //try to create the new layer with OGRProvider instead of QgsVectorFileWriter
305  QString ogrlib = pReg->library( QStringLiteral( "ogr" ) );
306  // load the data provider
307  QLibrary *myLib = new QLibrary( ogrlib );
308  bool loaded = myLib->load();
309  if ( loaded )
310  {
311  QgsDebugMsg( QStringLiteral( "ogr provider loaded" ) );
312 
313  typedef bool ( *createEmptyDataSourceProc )( const QString &, const QString &, const QString &, QgsWkbTypes::Type,
314  const QList< QPair<QString, QString> > &, const QgsCoordinateReferenceSystem &, QString & );
315  createEmptyDataSourceProc createEmptyDataSource = ( createEmptyDataSourceProc ) cast_to_fptr( myLib->resolve( "createEmptyDataSource" ) );
316  if ( createEmptyDataSource )
317  {
318  if ( geometrytype != QgsWkbTypes::Unknown )
319  {
320  QgsCoordinateReferenceSystem srs = geomDialog.crs();
321  if ( !createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs, errorMessage ) )
322  {
323  return QString();
324  }
325  }
326  else
327  {
328  errorMessage = QObject::tr( "Geometry type not recognised" );
329  QgsDebugMsg( errorMessage );
330  return QString();
331  }
332  }
333  else
334  {
335  errorMessage = QObject::tr( "Resolving newEmptyDataSource(...) failed" );
336  QgsDebugMsg( errorMessage );
337  return QString();
338  }
339  }
340 
341  if ( encoding )
342  *encoding = enc;
343 
344  return fileName;
345 }
346 
347 void QgsNewVectorLayerDialog::showHelp()
348 {
349  QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
350 }
QString selectedFileEncoding() const
Returns the file format for storage.
void fileChanged(const QString &)
emitted as soon as the current file or directory is changed
QString selectedFileFormat() const
Returns the file format for storage.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QString library(const QString &providerKey) const
Returns path for the library of the provider.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void attributes(QList< QPair< QString, QString > > &at) const
Appends the chosen attribute names and types to at.
void saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
static Type addM(Type type)
Adds the m dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:891
QString filename() const
Returns the name for the new layer.
void setFilename(const QString &filename)
Sets the initial file name to show in the dialog.
static Type addZ(Type type)
Adds the z dimension to a WKB type and returns the new type.
Definition: qgswkbtypes.h:866
#define cast_to_fptr(f)
Definition: qgis.h:158
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs value for the new layer in the dialog.
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.
A registry / canonical manager of data providers.
QgsNewVectorLayerDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
New dialog constructor.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QgsCoordinateReferenceSystem crs() const
Returns the selected CRS for the new layer.
This class represents a coordinate reference system (CRS).
Select multiple files.
Definition: qgsfilewidget.h:68
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
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.
QgsWkbTypes::Type selectedType() const
Returns the selected geometry type.
static QString filterForDriver(const QString &driverName)
Creates a filter for an OGR driver key.
static QStringList availableEncodings()
Returns a list of available encodings.