QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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()->setSectionsClickable( 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  QList<QPair<QString, QVariant>> orderedMap;
152  auto end = map.constEnd();
153  for ( auto it = map.constBegin(); it != end; ++it )
154  {
155  orderedMap.append( qMakePair( it.key(), it.value() ) );
156  }
157 
158  updateMap( orderedMap, insertNull );
159 }
160 
161 void QgsValueMapConfigDlg::updateMap( const QList<QPair<QString, QVariant>> &list, bool insertNull )
162 {
163  tableWidget->clearContents();
164  for ( int i = tableWidget->rowCount() - 1; i > 0; i-- )
165  {
166  tableWidget->removeRow( i );
167  }
168  int row = 0;
169 
170  if ( insertNull )
171  {
172  setRow( row, QgsValueMapFieldFormatter::NULL_VALUE, QStringLiteral( "<NULL>" ) );
173  ++row;
174  }
175 
176  for ( const auto &pair : list )
177  {
178  if ( pair.second.isNull() )
179  setRow( row, pair.first, QString() );
180  else
181  setRow( row, pair.first, pair.second.toString() );
182  }
183 }
184 
185 void QgsValueMapConfigDlg::populateComboBox( QComboBox *comboBox, const QVariantMap &config, bool skipNull )
186 {
187  const QList<QVariant> valueList = config.value( QStringLiteral( "map" ) ).toList();
188 
189  if ( !valueList.empty() )
190  {
191  for ( const QVariant &value : valueList )
192  {
193  const QVariantMap valueMap = value.toMap();
194 
195  if ( skipNull && valueMap.constBegin().value() == QgsValueMapFieldFormatter::NULL_VALUE )
196  continue;
197 
198  comboBox->addItem( valueMap.constBegin().key(), valueMap.constBegin().value() );
199  }
200  }
201  else
202  {
203  const QVariantMap map = config.value( QStringLiteral( "map" ) ).toMap();
204  for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
205  {
206  if ( skipNull && it.value() == QgsValueMapFieldFormatter::NULL_VALUE )
207  continue;
208 
209  comboBox->addItem( it.key(), it.value() );
210  }
211  }
212 }
213 
214 bool QgsValueMapConfigDlg::eventFilter( QObject *watched, QEvent *event )
215 {
216  Q_UNUSED( watched )
217  if ( event->type() == QEvent::KeyPress )
218  {
219  QKeyEvent *keyEvent = static_cast<QKeyEvent *>( event );
220  if ( keyEvent->matches( QKeySequence::Copy ) )
221  {
222  copySelectionToClipboard();
223  event->accept();
224  return true;
225  }
226  }
227  return false;
228 }
229 
230 void QgsValueMapConfigDlg::setRow( int row, const QString &value, const QString &description )
231 {
232  QgsSettings settings;
233  QTableWidgetItem *valueCell = nullptr;
234  QTableWidgetItem *descriptionCell = new QTableWidgetItem( description );
235  tableWidget->insertRow( row );
237  {
238  QFont cellFont;
239  cellFont.setItalic( true );
240  valueCell = new QTableWidgetItem( QgsApplication::nullRepresentation() );
241  valueCell->setFont( cellFont );
242  valueCell->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
243  descriptionCell->setFont( cellFont );
244  }
245  else
246  {
247  valueCell = new QTableWidgetItem( value );
248  }
249  tableWidget->setItem( row, 0, valueCell );
250  tableWidget->setItem( row, 1, descriptionCell );
251 }
252 
253 void QgsValueMapConfigDlg::copySelectionToClipboard()
254 {
255  QAbstractItemModel *model = tableWidget->model();
256  QItemSelectionModel *selection = tableWidget->selectionModel();
257  const QModelIndexList indexes = selection->selectedIndexes();
258 
259  QString clipboardText;
260  QModelIndex previous = indexes.first();
261  std::unique_ptr<QMimeData> mimeData = qgis::make_unique<QMimeData>();
262  for ( const QModelIndex &current : indexes )
263  {
264  const QString text = model->data( current ).toString();
265  if ( current.row() != previous.row() )
266  {
267  clipboardText.append( '\n' );
268  }
269  else if ( current.column() != previous.column() )
270  {
271  clipboardText.append( '\t' );
272  }
273  clipboardText.append( text );
274  previous = current;
275  }
276  mimeData->setData( QStringLiteral( "text/plain" ), clipboardText.toUtf8() );
277  QApplication::clipboard()->setMimeData( mimeData.release() );
278 }
279 
280 void QgsValueMapConfigDlg::addNullButtonPushed()
281 {
282  setRow( tableWidget->rowCount() - 1, QgsValueMapFieldFormatter::NULL_VALUE, QStringLiteral( "<NULL>" ) );
283 }
284 
285 void QgsValueMapConfigDlg::loadFromLayerButtonPushed()
286 {
287  QgsAttributeTypeLoadDialog layerDialog( layer() );
288  if ( !layerDialog.exec() )
289  return;
290 
291  updateMap( layerDialog.valueMap(), layerDialog.insertNull() );
292 }
293 
294 void QgsValueMapConfigDlg::loadFromCSVButtonPushed()
295 {
296  QgsSettings settings;
297 
298  QString fileName = QFileDialog::getOpenFileName( nullptr, tr( "Select a File" ), QDir::homePath() );
299  if ( fileName.isNull() )
300  return;
301 
302  QFile f( fileName );
303 
304  if ( !f.open( QIODevice::ReadOnly ) )
305  {
306  QMessageBox::information( nullptr,
307  tr( "Load Value Map from File" ),
308  tr( "Could not open file %1\nError was: %2" ).arg( fileName, f.errorString() ),
309  QMessageBox::Cancel );
310  return;
311  }
312 
313  QTextStream s( &f );
314  s.setAutoDetectUnicode( true );
315 
316  QRegExp re0( "^([^;]*);(.*)$" );
317  re0.setMinimal( true );
318  QRegExp re1( "^([^,]*),(.*)$" );
319  re1.setMinimal( true );
320 
321  QList<QPair<QString, QVariant>> map;
322 
323  s.readLine();
324 
325  while ( !s.atEnd() )
326  {
327  QString l = s.readLine().trimmed();
328 
329  QString key;
330  QString val;
331 
332  if ( re0.indexIn( l ) >= 0 && re0.captureCount() == 2 )
333  {
334  key = re0.cap( 1 ).trimmed();
335  val = re0.cap( 2 ).trimmed();
336  }
337  else if ( re1.indexIn( l ) >= 0 && re1.captureCount() == 2 )
338  {
339  key = re1.cap( 1 ).trimmed();
340  val = re1.cap( 2 ).trimmed();
341  }
342  else
343  continue;
344 
345  if ( ( key.startsWith( '\"' ) && key.endsWith( '\"' ) ) ||
346  ( key.startsWith( '\'' ) && key.endsWith( '\'' ) ) )
347  {
348  key = key.mid( 1, key.length() - 2 );
349  }
350 
351  if ( ( val.startsWith( '\"' ) && val.endsWith( '\"' ) ) ||
352  ( val.startsWith( '\'' ) && val.endsWith( '\'' ) ) )
353  {
354  val = val.mid( 1, val.length() - 2 );
355  }
356 
357  if ( key == QgsApplication::nullRepresentation() )
359 
360  map.append( qMakePair( key, val ) );
361  }
362 
363  updateMap( map, false );
364 }
void updateMap(const QMap< QString, QVariant > &map, bool insertNull)
Updates the displayed table with the values from map.
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.