QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgsvaluemapconfigdlg.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvaluemapconfigdlg.cpp
3  --------------------------------------
4  Date : 5.1.2014
5  Copyright : (C) 2014 Matthias Kuhn
6  Email : matthias at opengis dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsvaluemapconfigdlg.h"
17 
20 #include "qgsapplication.h"
21 #include "qgssettings.h"
22 
23 #include <QFileDialog>
24 #include <QMessageBox>
25 #include <QTextStream>
26 #include <QClipboard>
27 #include <QKeyEvent>
28 
29 QgsValueMapConfigDlg::QgsValueMapConfigDlg( QgsVectorLayer *vl, int fieldIdx, QWidget *parent )
30  : QgsEditorConfigWidget( vl, fieldIdx, parent )
31 {
32  setupUi( this );
33 
34  tableWidget->insertRow( 0 );
35 
36  tableWidget->horizontalHeader()->setClickable( true );
37  tableWidget->setSortingEnabled( true );
38 
39  connect( addNullButton, &QAbstractButton::clicked, this, &QgsValueMapConfigDlg::addNullButtonPushed );
40  connect( removeSelectedButton, &QAbstractButton::clicked, this, &QgsValueMapConfigDlg::removeSelectedButtonPushed );
41  connect( loadFromLayerButton, &QAbstractButton::clicked, this, &QgsValueMapConfigDlg::loadFromLayerButtonPushed );
42  connect( loadFromCSVButton, &QAbstractButton::clicked, this, &QgsValueMapConfigDlg::loadFromCSVButtonPushed );
43  connect( tableWidget, &QTableWidget::cellChanged, this, &QgsValueMapConfigDlg::vCellChanged );
44  tableWidget->installEventFilter( this );
45 }
46 
48 {
49  QList<QVariant> valueList;
50 
51  //store data to map
52  for ( int i = 0; i < tableWidget->rowCount() - 1; i++ )
53  {
54  QTableWidgetItem *ki = tableWidget->item( i, 0 );
55  QTableWidgetItem *vi = tableWidget->item( i, 1 );
56 
57  if ( !ki )
58  continue;
59 
60  QString ks = ki->text();
61  if ( ( ks == QgsApplication::nullRepresentation() ) && !( ki->flags() & Qt::ItemIsEditable ) )
63 
64  QVariantMap value;
65 
66  if ( !vi || vi->text().isNull() )
67  {
68  value.insert( ks, ks );
69  }
70  else
71  {
72  value.insert( vi->text(), ks );
73  }
74  valueList.append( value );
75  }
76 
77  QVariantMap cfg;
78  cfg.insert( QStringLiteral( "map" ), valueList );
79  return cfg;
80 }
81 
82 void QgsValueMapConfigDlg::setConfig( const QVariantMap &config )
83 {
84  tableWidget->clearContents();
85  for ( int i = tableWidget->rowCount() - 1; i > 0; i-- )
86  {
87  tableWidget->removeRow( i );
88  }
89 
90  QList<QVariant> valueList = config.value( QStringLiteral( "map" ) ).toList();
91 
92  if ( valueList.count() > 0 )
93  {
94  for ( int i = 0, row = 0; i < valueList.count(); i++, row++ )
95  {
96  setRow( row, valueList[i].toMap().constBegin().value().toString(), valueList[i].toMap().constBegin().key() );
97  }
98  }
99  else
100  {
101  int row = 0;
102  QVariantMap values = config.value( QStringLiteral( "map" ) ).toMap();
103  for ( QVariantMap::ConstIterator mit = values.constBegin(); mit != values.constEnd(); mit++, row++ )
104  {
105  if ( mit.value().isNull() )
106  setRow( row, mit.key(), QString() );
107  else
108  setRow( row, mit.value().toString(), mit.key() );
109  }
110  }
111 }
112 
113 void QgsValueMapConfigDlg::vCellChanged( int row, int column )
114 {
115  Q_UNUSED( column )
116  if ( row == tableWidget->rowCount() - 1 )
117  {
118  tableWidget->insertRow( row + 1 );
119  } //else check type
120 
121  emit changed();
122 }
123 
124 void QgsValueMapConfigDlg::removeSelectedButtonPushed()
125 {
126  QList<QTableWidgetItem *> list = tableWidget->selectedItems();
127  QSet<int> rowsToRemove;
128  int removed = 0;
129  int i;
130  for ( i = 0; i < list.size(); i++ )
131  {
132  if ( list[i]->column() == 0 )
133  {
134  int row = list[i]->row();
135  if ( !rowsToRemove.contains( row ) )
136  {
137  rowsToRemove.insert( row );
138  }
139  }
140  }
141  for ( i = 0; i < rowsToRemove.size(); i++ )
142  {
143  tableWidget->removeRow( rowsToRemove.values().at( i ) - removed );
144  removed++;
145  }
146  emit changed();
147 }
148 
149 void QgsValueMapConfigDlg::updateMap( const QMap<QString, QVariant> &map, bool insertNull )
150 {
151  tableWidget->clearContents();
152  for ( int i = tableWidget->rowCount() - 1; i > 0; i-- )
153  {
154  tableWidget->removeRow( i );
155  }
156  int row = 0;
157 
158  if ( insertNull )
159  {
160  setRow( row, QgsValueMapFieldFormatter::NULL_VALUE, QStringLiteral( "<NULL>" ) );
161  ++row;
162  }
163 
164  for ( QMap<QString, QVariant>::const_iterator mit = map.begin(); mit != map.end(); ++mit, ++row )
165  {
166  if ( mit.value().isNull() )
167  setRow( row, mit.key(), QString() );
168  else
169  setRow( row, mit.key(), mit.value().toString() );
170  }
171 }
172 
173 void QgsValueMapConfigDlg::populateComboBox( QComboBox *comboBox, const QVariantMap &config, bool skipNull )
174 {
175  const QList<QVariant> valueList = config.value( QStringLiteral( "map" ) ).toList();
176 
177  if ( !valueList.empty() )
178  {
179  for ( const QVariant &value : valueList )
180  {
181  const QVariantMap valueMap = value.toMap();
182 
183  if ( skipNull && valueMap.constBegin().value() == QgsValueMapFieldFormatter::NULL_VALUE )
184  continue;
185 
186  comboBox->addItem( valueMap.constBegin().key(), valueMap.constBegin().value() );
187  }
188  }
189  else
190  {
191  const QVariantMap map = config.value( QStringLiteral( "map" ) ).toMap();
192  for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
193  {
194  if ( skipNull && it.value() == QgsValueMapFieldFormatter::NULL_VALUE )
195  continue;
196 
197  comboBox->addItem( it.key(), it.value() );
198  }
199  }
200 }
201 
202 bool QgsValueMapConfigDlg::eventFilter( QObject *watched, QEvent *event )
203 {
204  Q_UNUSED( watched )
205  if ( event->type() == QEvent::KeyRelease )
206  {
207  QKeyEvent *keyEvent = static_cast<QKeyEvent *>( event );
208  if ( keyEvent->matches( QKeySequence::Copy ) )
209  {
210  copySelectionToClipboard();
211  return true;
212  }
213  }
214  return false;
215 }
216 
217 void QgsValueMapConfigDlg::setRow( int row, const QString &value, const QString &description )
218 {
219  QgsSettings settings;
220  QTableWidgetItem *valueCell = nullptr;
221  QTableWidgetItem *descriptionCell = new QTableWidgetItem( description );
222  tableWidget->insertRow( row );
224  {
225  QFont cellFont;
226  cellFont.setItalic( true );
227  valueCell = new QTableWidgetItem( QgsApplication::nullRepresentation() );
228  valueCell->setFont( cellFont );
229  valueCell->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
230  descriptionCell->setFont( cellFont );
231  }
232  else
233  {
234  valueCell = new QTableWidgetItem( value );
235  }
236  tableWidget->setItem( row, 0, valueCell );
237  tableWidget->setItem( row, 1, descriptionCell );
238 }
239 
240 void QgsValueMapConfigDlg::copySelectionToClipboard()
241 {
242  QAbstractItemModel *model = tableWidget->model();
243  QItemSelectionModel *selection = tableWidget->selectionModel();
244  const QModelIndexList indexes = selection->selectedIndexes();
245 
246  QString clipboardText;
247  QModelIndex previous = indexes.first();
248  std::unique_ptr<QMimeData> mimeData = qgis::make_unique<QMimeData>();
249  for ( const QModelIndex &current : indexes )
250  {
251  const QString text = model->data( current ).toString();
252  if ( current.row() != previous.row() )
253  {
254  clipboardText.append( '\n' );
255  }
256  else if ( current.column() != previous.column() )
257  {
258  clipboardText.append( '\t' );
259  }
260  clipboardText.append( text );
261  previous = current;
262  }
263  mimeData->setData( QStringLiteral( "text/plain" ), clipboardText.toUtf8() );
264  QApplication::clipboard()->setMimeData( mimeData.release() );
265 }
266 
267 void QgsValueMapConfigDlg::addNullButtonPushed()
268 {
269  setRow( tableWidget->rowCount() - 1, QgsValueMapFieldFormatter::NULL_VALUE, QStringLiteral( "<NULL>" ) );
270 }
271 
272 void QgsValueMapConfigDlg::loadFromLayerButtonPushed()
273 {
274  QgsAttributeTypeLoadDialog layerDialog( layer() );
275  if ( !layerDialog.exec() )
276  return;
277 
278  updateMap( layerDialog.valueMap(), layerDialog.insertNull() );
279 }
280 
281 void QgsValueMapConfigDlg::loadFromCSVButtonPushed()
282 {
283  QgsSettings settings;
284 
285  QString fileName = QFileDialog::getOpenFileName( nullptr, tr( "Select a File" ), QDir::homePath() );
286  if ( fileName.isNull() )
287  return;
288 
289  QFile f( fileName );
290 
291  if ( !f.open( QIODevice::ReadOnly ) )
292  {
293  QMessageBox::information( nullptr,
294  tr( "Load Value Map from File" ),
295  tr( "Could not open file %1\nError was: %2" ).arg( fileName, f.errorString() ),
296  QMessageBox::Cancel );
297  return;
298  }
299 
300  QTextStream s( &f );
301  s.setAutoDetectUnicode( true );
302 
303  QRegExp re0( "^([^;]*);(.*)$" );
304  re0.setMinimal( true );
305  QRegExp re1( "^([^,]*),(.*)$" );
306  re1.setMinimal( true );
307  QMap<QString, QVariant> map;
308 
309  s.readLine();
310 
311  while ( !s.atEnd() )
312  {
313  QString l = s.readLine().trimmed();
314 
315  QString key, val;
316  if ( re0.indexIn( l ) >= 0 && re0.captureCount() == 2 )
317  {
318  key = re0.cap( 1 ).trimmed();
319  val = re0.cap( 2 ).trimmed();
320  }
321  else if ( re1.indexIn( l ) >= 0 && re1.captureCount() == 2 )
322  {
323  key = re1.cap( 1 ).trimmed();
324  val = re1.cap( 2 ).trimmed();
325  }
326  else
327  continue;
328 
329  if ( ( key.startsWith( '\"' ) && key.endsWith( '\"' ) ) ||
330  ( key.startsWith( '\'' ) && key.endsWith( '\'' ) ) )
331  {
332  key = key.mid( 1, key.length() - 2 );
333  }
334 
335  if ( ( val.startsWith( '\"' ) && val.endsWith( '\"' ) ) ||
336  ( val.startsWith( '\'' ) && val.endsWith( '\'' ) ) )
337  {
338  val = val.mid( 1, val.length() - 2 );
339  }
340 
341  if ( key == QgsApplication::nullRepresentation() )
343 
344  map[ key ] = val;
345  }
346 
347  updateMap( map, false );
348 }
void updateMap(const QMap< QString, QVariant > &map, bool insertNull)
QgsVectorLayer * layer()
Returns the layer for which this configuration widget applies.
This class should be subclassed for every configurable editor widget type.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QgsValueMapConfigDlg(QgsVectorLayer *vl, int fieldIdx, QWidget *parent)
bool eventFilter(QObject *watched, QEvent *event) override
void setConfig(const QVariantMap &config) override
Update the configuration widget to represent the given configuration.
QMap< QString, QVariant > & valueMap()
Returns the value map which is currently active.
QVariantMap config() override
Create a configuration from the current GUI state.
bool insertNull()
Returns true if the "Add NULL value" checkbox has been checked.
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
void changed()
Emitted when the configuration of the widget is changed.
Represents a vector layer which manages a vector based data sets.
static const QString NULL_VALUE
Will be saved in the configuration when a value is NULL.
static void populateComboBox(QComboBox *comboBox, const QVariantMap &configuration, bool skipNull)
Populates a comboBox with the appropriate entries based on a value map configuration.