QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 #include <QMimeData>
29 
30 QgsValueMapConfigDlg::QgsValueMapConfigDlg( QgsVectorLayer *vl, int fieldIdx, QWidget *parent )
31  : QgsEditorConfigWidget( vl, fieldIdx, parent )
32 {
33  setupUi( this );
34 
35  tableWidget->insertRow( 0 );
36 
37  tableWidget->horizontalHeader()->setSectionsClickable( true );
38  tableWidget->setSortingEnabled( true );
39 
40  connect( addNullButton, &QAbstractButton::clicked, this, &QgsValueMapConfigDlg::addNullButtonPushed );
41  connect( removeSelectedButton, &QAbstractButton::clicked, this, &QgsValueMapConfigDlg::removeSelectedButtonPushed );
42  connect( loadFromLayerButton, &QAbstractButton::clicked, this, &QgsValueMapConfigDlg::loadFromLayerButtonPushed );
43  connect( loadFromCSVButton, &QAbstractButton::clicked, this, &QgsValueMapConfigDlg::loadFromCSVButtonPushed );
44  connect( tableWidget, &QTableWidget::cellChanged, this, &QgsValueMapConfigDlg::vCellChanged );
45  tableWidget->installEventFilter( this );
46 }
47 
49 {
50  QList<QVariant> valueList;
51 
52  //store data to map
53  for ( int i = 0; i < tableWidget->rowCount() - 1; i++ )
54  {
55  QTableWidgetItem *ki = tableWidget->item( i, 0 );
56  QTableWidgetItem *vi = tableWidget->item( i, 1 );
57 
58  if ( !ki )
59  continue;
60 
61  QString ks = ki->text();
62  if ( ( ks == QgsApplication::nullRepresentation() ) && !( ki->flags() & Qt::ItemIsEditable ) )
64 
65  QVariantMap value;
66 
67  if ( !vi || vi->text().isNull() )
68  {
69  value.insert( ks, ks );
70  }
71  else
72  {
73  value.insert( vi->text(), ks );
74  }
75  valueList.append( value );
76  }
77 
78  QVariantMap cfg;
79  cfg.insert( QStringLiteral( "map" ), valueList );
80  return cfg;
81 }
82 
83 void QgsValueMapConfigDlg::setConfig( const QVariantMap &config )
84 {
85  tableWidget->clearContents();
86  for ( int i = tableWidget->rowCount() - 1; i > 0; i-- )
87  {
88  tableWidget->removeRow( i );
89  }
90 
91  QList<QVariant> valueList = config.value( QStringLiteral( "map" ) ).toList();
92 
93  if ( valueList.count() > 0 )
94  {
95  for ( int i = 0, row = 0; i < valueList.count(); i++, row++ )
96  {
97  setRow( row, valueList[i].toMap().constBegin().value().toString(), valueList[i].toMap().constBegin().key() );
98  }
99  }
100  else
101  {
102  int row = 0;
103  QVariantMap values = config.value( QStringLiteral( "map" ) ).toMap();
104  for ( QVariantMap::ConstIterator mit = values.constBegin(); mit != values.constEnd(); mit++, row++ )
105  {
106  if ( mit.value().isNull() )
107  setRow( row, mit.key(), QString() );
108  else
109  setRow( row, mit.value().toString(), mit.key() );
110  }
111  }
112 }
113 
114 void QgsValueMapConfigDlg::vCellChanged( int row, int column )
115 {
116  Q_UNUSED( column )
117  if ( row == tableWidget->rowCount() - 1 )
118  {
119  tableWidget->insertRow( row + 1 );
120  } //else check type
121 
122  emit changed();
123 }
124 
125 void QgsValueMapConfigDlg::removeSelectedButtonPushed()
126 {
127  QList<QTableWidgetItem *> list = tableWidget->selectedItems();
128  QSet<int> rowsToRemove;
129  int removed = 0;
130  int i;
131  for ( i = 0; i < list.size(); i++ )
132  {
133  if ( list[i]->column() == 0 )
134  {
135  int row = list[i]->row();
136  if ( !rowsToRemove.contains( row ) )
137  {
138  rowsToRemove.insert( row );
139  }
140  }
141  }
142  for ( i = 0; i < rowsToRemove.size(); i++ )
143  {
144  tableWidget->removeRow( rowsToRemove.values().at( i ) - removed );
145  removed++;
146  }
147  emit changed();
148 }
149 
150 void QgsValueMapConfigDlg::updateMap( const QMap<QString, QVariant> &map, bool insertNull )
151 {
152  QList<QPair<QString, QVariant>> orderedMap;
153  auto end = map.constEnd();
154  for ( auto it = map.constBegin(); it != end; ++it )
155  {
156  orderedMap.append( qMakePair( it.key(), it.value() ) );
157  }
158 
159  updateMap( orderedMap, insertNull );
160 }
161 
162 void QgsValueMapConfigDlg::updateMap( const QList<QPair<QString, QVariant>> &list, bool insertNull )
163 {
164  tableWidget->clearContents();
165  for ( int i = tableWidget->rowCount() - 1; i > 0; i-- )
166  {
167  tableWidget->removeRow( i );
168  }
169  int row = 0;
170 
171  if ( insertNull )
172  {
173  setRow( row, QgsValueMapFieldFormatter::NULL_VALUE, QStringLiteral( "<NULL>" ) );
174  ++row;
175  }
176 
177  for ( const auto &pair : list )
178  {
179  if ( pair.second.isNull() )
180  setRow( row, pair.first, QString() );
181  else
182  setRow( row, pair.first, pair.second.toString() );
183  ++row;
184  }
185 }
186 
187 void QgsValueMapConfigDlg::populateComboBox( QComboBox *comboBox, const QVariantMap &config, bool skipNull )
188 {
189  const QList<QVariant> valueList = config.value( QStringLiteral( "map" ) ).toList();
190 
191  if ( !valueList.empty() )
192  {
193  for ( const QVariant &value : valueList )
194  {
195  const QVariantMap valueMap = value.toMap();
196 
197  if ( skipNull && valueMap.constBegin().value() == QgsValueMapFieldFormatter::NULL_VALUE )
198  continue;
199 
200  comboBox->addItem( valueMap.constBegin().key(), valueMap.constBegin().value() );
201  }
202  }
203  else
204  {
205  const QVariantMap map = config.value( QStringLiteral( "map" ) ).toMap();
206  for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
207  {
208  if ( skipNull && it.value() == QgsValueMapFieldFormatter::NULL_VALUE )
209  continue;
210 
211  comboBox->addItem( it.key(), it.value() );
212  }
213  }
214 }
215 
216 bool QgsValueMapConfigDlg::eventFilter( QObject *watched, QEvent *event )
217 {
218  Q_UNUSED( watched )
219  if ( event->type() == QEvent::KeyPress )
220  {
221  QKeyEvent *keyEvent = static_cast<QKeyEvent *>( event );
222  if ( keyEvent->matches( QKeySequence::Copy ) )
223  {
224  copySelectionToClipboard();
225  event->accept();
226  return true;
227  }
228  }
229  return false;
230 }
231 
232 void QgsValueMapConfigDlg::setRow( int row, const QString &value, const QString &description )
233 {
234  QgsSettings settings;
235  QTableWidgetItem *valueCell = nullptr;
236  QTableWidgetItem *descriptionCell = new QTableWidgetItem( description );
237  tableWidget->insertRow( row );
239  {
240  QFont cellFont;
241  cellFont.setItalic( true );
242  valueCell = new QTableWidgetItem( QgsApplication::nullRepresentation() );
243  valueCell->setFont( cellFont );
244  valueCell->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
245  descriptionCell->setFont( cellFont );
246  }
247  else
248  {
249  valueCell = new QTableWidgetItem( value );
250  }
251  tableWidget->setItem( row, 0, valueCell );
252  tableWidget->setItem( row, 1, descriptionCell );
253 }
254 
255 void QgsValueMapConfigDlg::copySelectionToClipboard()
256 {
257  QAbstractItemModel *model = tableWidget->model();
258  QItemSelectionModel *selection = tableWidget->selectionModel();
259  const QModelIndexList indexes = selection->selectedIndexes();
260 
261  QString clipboardText;
262  QModelIndex previous = indexes.first();
263  std::unique_ptr<QMimeData> mimeData = std::make_unique<QMimeData>();
264  for ( const QModelIndex &current : indexes )
265  {
266  const QString text = model->data( current ).toString();
267  if ( current.row() != previous.row() )
268  {
269  clipboardText.append( '\n' );
270  }
271  else if ( current.column() != previous.column() )
272  {
273  clipboardText.append( '\t' );
274  }
275  clipboardText.append( text );
276  previous = current;
277  }
278  mimeData->setData( QStringLiteral( "text/plain" ), clipboardText.toUtf8() );
279  QApplication::clipboard()->setMimeData( mimeData.release() );
280 }
281 
282 void QgsValueMapConfigDlg::addNullButtonPushed()
283 {
284  setRow( tableWidget->rowCount() - 1, QgsValueMapFieldFormatter::NULL_VALUE, QStringLiteral( "<NULL>" ) );
285 }
286 
287 void QgsValueMapConfigDlg::loadFromLayerButtonPushed()
288 {
289  QgsAttributeTypeLoadDialog layerDialog( layer() );
290  if ( !layerDialog.exec() )
291  return;
292 
293  updateMap( layerDialog.valueMap(), layerDialog.insertNull() );
294 }
295 
296 void QgsValueMapConfigDlg::loadFromCSVButtonPushed()
297 {
298  QgsSettings settings;
299 
300  QString fileName = QFileDialog::getOpenFileName( nullptr, tr( "Load Value Map from File" ), QDir::homePath() );
301  if ( fileName.isNull() )
302  return;
303 
304  QFile f( fileName );
305 
306  if ( !f.open( QIODevice::ReadOnly ) )
307  {
308  QMessageBox::information( nullptr,
309  tr( "Load Value Map from File" ),
310  tr( "Could not open file %1\nError was: %2" ).arg( fileName, f.errorString() ),
311  QMessageBox::Cancel );
312  return;
313  }
314 
315  QTextStream s( &f );
316  s.setAutoDetectUnicode( true );
317 
318  QRegExp re0( "^([^;]*);(.*)$" );
319  re0.setMinimal( true );
320  QRegExp re1( "^([^,]*),(.*)$" );
321  re1.setMinimal( true );
322 
323  QList<QPair<QString, QVariant>> map;
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 }
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
This class should be subclassed for every configurable editor widget type.
QgsVectorLayer * layer()
Returns the layer for which this configuration widget applies.
void changed()
Emitted when the configuration of the widget is changed.
void setConfig(const QVariantMap &config) override
Update the configuration widget to represent the given configuration.
bool eventFilter(QObject *watched, QEvent *event) override
QgsValueMapConfigDlg(QgsVectorLayer *vl, int fieldIdx, QWidget *parent)
void updateMap(const QMap< QString, QVariant > &map, bool insertNull)
Updates the displayed table with the values from map.
static void populateComboBox(QComboBox *comboBox, const QVariantMap &configuration, bool skipNull)
Populates a comboBox with the appropriate entries based on a value map configuration.
QVariantMap config() override
Create a configuration from the current GUI state.
static const QString NULL_VALUE
Will be saved in the configuration when a value is NULL.
Represents a vector layer which manages a vector based data sets.