QGIS API Documentation  2.99.0-Master (19b062c)
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 
19 #include "qgsapplication.h"
20 #include "qgis.h"
21 #include "qgslogger.h"
23 #include "qgsproviderregistry.h"
24 #include "qgsvectordataprovider.h"
25 #include "qgsvectorfilewriter.h"
26 #include "qgssettings.h"
27 
28 #include <QPushButton>
29 #include <QComboBox>
30 #include <QLibrary>
31 #include <QFileDialog>
32 
33 
34 QgsNewVectorLayerDialog::QgsNewVectorLayerDialog( QWidget *parent, Qt::WindowFlags fl )
35  : QDialog( parent, fl )
36 {
37  setupUi( this );
38  connect( mAddAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mAddAttributeButton_clicked );
39  connect( mRemoveAttributeButton, &QToolButton::clicked, this, &QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked );
40  connect( mFileFormatComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged );
41  connect( mTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged );
42  connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsNewVectorLayerDialog::showHelp );
43 
44  QgsSettings settings;
45  restoreGeometry( settings.value( QStringLiteral( "Windows/NewVectorLayer/geometry" ) ).toByteArray() );
46 
47  mAddAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewAttribute.svg" ) ) );
48  mRemoveAttributeButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteAttribute.svg" ) ) );
49  mTypeBox->addItem( tr( "Text data" ), "String" );
50  mTypeBox->addItem( tr( "Whole number" ), "Integer" );
51  mTypeBox->addItem( tr( "Decimal number" ), "Real" );
52  mTypeBox->addItem( tr( "Date" ), "Date" );
53 
54  mWidth->setValidator( new QIntValidator( 1, 255, this ) );
55  mPrecision->setValidator( new QIntValidator( 0, 15, this ) );
56 
57  mPointRadioButton->setChecked( true );
58  mFileFormatComboBox->addItem( tr( "ESRI Shapefile" ), "ESRI Shapefile" );
59 #if 0
60  // Disabled until provider properly supports editing the created file formats
61  // When enabling this, adapt the window-title of the dialog and the title of all actions showing this dialog.
62  mFileFormatComboBox->addItem( tr( "Comma Separated Value" ), "Comma Separated Value" );
63  mFileFormatComboBox->addItem( tr( "GML" ), "GML" );
64  mFileFormatComboBox->addItem( tr( "Mapinfo File" ), "Mapinfo File" );
65 #endif
66  if ( mFileFormatComboBox->count() == 1 )
67  {
68  mFileFormatComboBox->setVisible( false );
69  mFileFormatLabel->setVisible( false );
70  }
71 
72  mFileFormatComboBox->setCurrentIndex( 0 );
73 
74  mFileEncoding->addItems( QgsVectorDataProvider::availableEncodings() );
75 
76  // Use default encoding if none supplied
77  QString enc = QgsSettings().value( QStringLiteral( "/UI/encoding" ), "System" ).toString();
78 
79  // The specified decoding is added if not existing alread, and then set current.
80  // This should select it.
81  int encindex = mFileEncoding->findText( enc );
82  if ( encindex < 0 )
83  {
84  mFileEncoding->insertItem( 0, enc );
85  encindex = 0;
86  }
87  mFileEncoding->setCurrentIndex( encindex );
88 
89  mOkButton = buttonBox->button( QDialogButtonBox::Ok );
90 
91  mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << QStringLiteral( "id" ) << QStringLiteral( "Integer" ) << QStringLiteral( "10" ) << QLatin1String( "" ) ) );
92  connect( mNameEdit, &QLineEdit::textChanged, this, &QgsNewVectorLayerDialog::nameChanged );
93  connect( mAttributeView, &QTreeWidget::itemSelectionChanged, this, &QgsNewVectorLayerDialog::selectionChanged );
94 
95  mAddAttributeButton->setEnabled( false );
96  mRemoveAttributeButton->setEnabled( false );
97 }
98 
100 {
101  QgsSettings settings;
102  settings.setValue( QStringLiteral( "Windows/NewVectorLayer/geometry" ), saveGeometry() );
103 }
104 
105 void QgsNewVectorLayerDialog::mFileFormatComboBox_currentIndexChanged( int index )
106 {
107  Q_UNUSED( index );
108  if ( mFileFormatComboBox->currentText() == tr( "ESRI Shapefile" ) )
109  mNameEdit->setMaxLength( 10 );
110  else
111  mNameEdit->setMaxLength( 32767 );
112 }
113 
114 void QgsNewVectorLayerDialog::mTypeBox_currentIndexChanged( int index )
115 {
116  // FIXME: sync with providers/ogr/qgsogrprovider.cpp
117  switch ( index )
118  {
119  case 0: // Text data
120  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 255 )
121  mWidth->setText( QStringLiteral( "80" ) );
122  mPrecision->setEnabled( false );
123  mWidth->setValidator( new QIntValidator( 1, 255, this ) );
124  break;
125 
126  case 1: // Whole number
127  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 10 )
128  mWidth->setText( QStringLiteral( "10" ) );
129  mPrecision->setEnabled( false );
130  mWidth->setValidator( new QIntValidator( 1, 10, this ) );
131  break;
132 
133  case 2: // Decimal number
134  if ( mWidth->text().toInt() < 1 || mWidth->text().toInt() > 20 )
135  mWidth->setText( QStringLiteral( "20" ) );
136  mPrecision->setEnabled( true );
137  mWidth->setValidator( new QIntValidator( 1, 20, this ) );
138  break;
139 
140  default:
141  QgsDebugMsg( "unexpected index" );
142  break;
143  }
144 }
145 
147 {
149  if ( mPointRadioButton->isChecked() )
150  {
151  wkbType = QgsWkbTypes::Point;
152  }
153  else if ( mLineRadioButton->isChecked() )
154  {
155  wkbType = QgsWkbTypes::LineString;
156  }
157  else if ( mPolygonRadioButton->isChecked() )
158  {
159  wkbType = QgsWkbTypes::Polygon;
160  }
161 
162  if ( mGeometryWithZCheckBox->isChecked() && wkbType != QgsWkbTypes::Unknown )
163  wkbType = QgsWkbTypes::to25D( wkbType );
164 
165  return wkbType;
166 }
167 
169 {
170  return mCrsSelector->crs();
171 }
172 
174 {
175  mCrsSelector->setCrs( crs );
176 }
177 
178 void QgsNewVectorLayerDialog::mAddAttributeButton_clicked()
179 {
180  QString myName = mNameEdit->text();
181  QString myWidth = mWidth->text();
182  QString myPrecision = mPrecision->isEnabled() ? mPrecision->text() : QLatin1String( "" );
183  //use userrole to avoid translated type string
184  QString myType = mTypeBox->currentData( Qt::UserRole ).toString();
185  mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType << myWidth << myPrecision ) );
186  if ( mAttributeView->topLevelItemCount() > 0 )
187  {
188  mOkButton->setEnabled( true );
189  }
190  mNameEdit->clear();
191 }
192 
193 void QgsNewVectorLayerDialog::mRemoveAttributeButton_clicked()
194 {
195  delete mAttributeView->currentItem();
196  if ( mAttributeView->topLevelItemCount() == 0 )
197  {
198  mOkButton->setEnabled( false );
199  }
200 }
201 
202 void QgsNewVectorLayerDialog::attributes( QList< QPair<QString, QString> > &at ) const
203 {
204  QTreeWidgetItemIterator it( mAttributeView );
205  while ( *it )
206  {
207  QTreeWidgetItem *item = *it;
208  QString type = QStringLiteral( "%1;%2;%3" ).arg( item->text( 1 ), item->text( 2 ), item->text( 3 ) );
209  at.push_back( qMakePair( item->text( 0 ), type ) );
210  QgsDebugMsg( QString( "appending %1//%2" ).arg( item->text( 0 ), type ) );
211  ++it;
212  }
213 }
214 
216 {
217  //use userrole to avoid translated type string
218  QString myType = mFileFormatComboBox->currentData( Qt::UserRole ).toString();
219  return myType;
220 }
221 
223 {
224  return mFileEncoding->currentText();
225 }
226 
227 void QgsNewVectorLayerDialog::nameChanged( const QString &name )
228 {
229  mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
230 }
231 
232 void QgsNewVectorLayerDialog::selectionChanged()
233 {
234  mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
235 }
236 
237 
238 // this is static
239 QString QgsNewVectorLayerDialog::runAndCreateLayer( QWidget *parent, QString *pEnc, const QgsCoordinateReferenceSystem &crs )
240 {
241  QgsNewVectorLayerDialog geomDialog( parent );
242  geomDialog.setCrs( crs );
243  if ( geomDialog.exec() == QDialog::Rejected )
244  {
245  return QLatin1String( "" );
246  }
247 
248  QgsWkbTypes::Type geometrytype = geomDialog.selectedType();
249  QString fileformat = geomDialog.selectedFileFormat();
250  QString enc = geomDialog.selectedFileEncoding();
251  QgsDebugMsg( QString( "New file format will be: %1" ).arg( fileformat ) );
252 
253  QList< QPair<QString, QString> > attributes;
254  geomDialog.attributes( attributes );
255 
256  QgsSettings settings;
257  QString lastUsedDir = settings.value( QStringLiteral( "UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString();
258  QString filterString = QgsVectorFileWriter::filterForDriver( fileformat );
259  QString fileName = QFileDialog::getSaveFileName( nullptr, tr( "Save layer as..." ), lastUsedDir, filterString );
260  if ( fileName.isNull() )
261  {
262  return QLatin1String( "" );
263  }
264 
265  if ( fileformat == QLatin1String( "ESRI Shapefile" ) && !fileName.endsWith( QLatin1String( ".shp" ), Qt::CaseInsensitive ) )
266  fileName += QLatin1String( ".shp" );
267 
268  settings.setValue( QStringLiteral( "UI/lastVectorFileFilterDir" ), QFileInfo( fileName ).absolutePath() );
269  settings.setValue( QStringLiteral( "UI/encoding" ), enc );
270 
271  //try to create the new layer with OGRProvider instead of QgsVectorFileWriter
273  QString ogrlib = pReg->library( QStringLiteral( "ogr" ) );
274  // load the data provider
275  QLibrary *myLib = new QLibrary( ogrlib );
276  bool loaded = myLib->load();
277  if ( loaded )
278  {
279  QgsDebugMsg( "ogr provider loaded" );
280 
281  typedef bool ( *createEmptyDataSourceProc )( const QString &, const QString &, const QString &, QgsWkbTypes::Type,
282  const QList< QPair<QString, QString> > &, const QgsCoordinateReferenceSystem & );
283  createEmptyDataSourceProc createEmptyDataSource = ( createEmptyDataSourceProc ) cast_to_fptr( myLib->resolve( "createEmptyDataSource" ) );
284  if ( createEmptyDataSource )
285  {
286  if ( geometrytype != QgsWkbTypes::Unknown )
287  {
288  QgsCoordinateReferenceSystem srs = geomDialog.crs();
289  if ( !createEmptyDataSource( fileName, fileformat, enc, geometrytype, attributes, srs ) )
290  {
291  return QString();
292  }
293  }
294  else
295  {
296  QgsDebugMsg( "geometry type not recognised" );
297  return QString();
298  }
299  }
300  else
301  {
302  QgsDebugMsg( "Resolving newEmptyDataSource(...) failed" );
303  return QString();
304  }
305  }
306 
307  if ( pEnc )
308  *pEnc = enc;
309 
310  return fileName;
311 }
312 
313 void QgsNewVectorLayerDialog::showHelp()
314 {
315  QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#creating-a-new-shapefile-layer" ) );
316 }
QString selectedFileEncoding() const
Returns the file format for storage.
static Type to25D(Type type)
Will convert the 25D version of the flat type if supported or Unknown if not supported.
Definition: qgswkbtypes.h:954
QString selectedFileFormat() const
Returns the file format for storage.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:55
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
QString library(const QString &providerKey) const
Return 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 setValue(const QString &key, const QVariant &value, const QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
#define cast_to_fptr(f)
Definition: qgis.h:146
static QString runAndCreateLayer(QWidget *parent=nullptr, QString *enc=nullptr, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem())
Runs the dialog and creates a layer matching the dialog parameters.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs value for the new layer in the dialog.
A registry / canonical manager of data providers.
QgsNewVectorLayerDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
QgsCoordinateReferenceSystem crs() const
Returns the selected CRS for the new layer.
This class represents a coordinate reference system (CRS).
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:34
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.