QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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  ++row;
183  }
184 }
185 
186 void QgsValueMapConfigDlg::populateComboBox( QComboBox *comboBox, const QVariantMap &config, bool skipNull )
187 {
188  const QList<QVariant> valueList = config.value( QStringLiteral( "map" ) ).toList();
189 
190  if ( !valueList.empty() )
191  {
192  for ( const QVariant &value : valueList )
193  {
194  const QVariantMap valueMap = value.toMap();
195 
196  if ( skipNull && valueMap.constBegin().value() == QgsValueMapFieldFormatter::NULL_VALUE )
197  continue;
198 
199  comboBox->addItem( valueMap.constBegin().key(), valueMap.constBegin().value() );
200  }
201  }
202  else
203  {
204  const QVariantMap map = config.value( QStringLiteral( "map" ) ).toMap();
205  for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
206  {
207  if ( skipNull && it.value() == QgsValueMapFieldFormatter::NULL_VALUE )
208  continue;
209 
210  comboBox->addItem( it.key(), it.value() );
211  }
212  }
213 }
214 
215 bool QgsValueMapConfigDlg::eventFilter( QObject *watched, QEvent *event )
216 {
217  Q_UNUSED( watched )
218  if ( event->type() == QEvent::KeyPress )
219  {
220  QKeyEvent *keyEvent = static_cast<QKeyEvent *>( event );
221  if ( keyEvent->matches( QKeySequence::Copy ) )
222  {
223  copySelectionToClipboard();
224  event->accept();
225  return true;
226  }
227  }
228  return false;
229 }
230 
231 void QgsValueMapConfigDlg::setRow( int row, const QString &value, const QString &description )
232 {
233  QgsSettings settings;
234  QTableWidgetItem *valueCell = nullptr;
235  QTableWidgetItem *descriptionCell = new QTableWidgetItem( description );
236  tableWidget->insertRow( row );
238  {
239  QFont cellFont;
240  cellFont.setItalic( true );
241  valueCell = new QTableWidgetItem( QgsApplication::nullRepresentation() );
242  valueCell->setFont( cellFont );
243  valueCell->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
244  descriptionCell->setFont( cellFont );
245  }
246  else
247  {
248  valueCell = new QTableWidgetItem( value );
249  }
250  tableWidget->setItem( row, 0, valueCell );
251  tableWidget->setItem( row, 1, descriptionCell );
252 }
253 
254 void QgsValueMapConfigDlg::copySelectionToClipboard()
255 {
256  QAbstractItemModel *model = tableWidget->model();
257  QItemSelectionModel *selection = tableWidget->selectionModel();
258  const QModelIndexList indexes = selection->selectedIndexes();
259 
260  QString clipboardText;
261  QModelIndex previous = indexes.first();
262  std::unique_ptr<QMimeData> mimeData = qgis::make_unique<QMimeData>();
263  for ( const QModelIndex &current : indexes )
264  {
265  const QString text = model->data( current ).toString();
266  if ( current.row() != previous.row() )
267  {
268  clipboardText.append( '\n' );
269  }
270  else if ( current.column() != previous.column() )
271  {
272  clipboardText.append( '\t' );
273  }
274  clipboardText.append( text );
275  previous = current;
276  }
277  mimeData->setData( QStringLiteral( "text/plain" ), clipboardText.toUtf8() );
278  QApplication::clipboard()->setMimeData( mimeData.release() );
279 }
280 
281 void QgsValueMapConfigDlg::addNullButtonPushed()
282 {
283  setRow( tableWidget->rowCount() - 1, QgsValueMapFieldFormatter::NULL_VALUE, QStringLiteral( "<NULL>" ) );
284 }
285 
286 void QgsValueMapConfigDlg::loadFromLayerButtonPushed()
287 {
288  QgsAttributeTypeLoadDialog layerDialog( layer() );
289  if ( !layerDialog.exec() )
290  return;
291 
292  updateMap( layerDialog.valueMap(), layerDialog.insertNull() );
293 }
294 
295 void QgsValueMapConfigDlg::loadFromCSVButtonPushed()
296 {
297  QgsSettings settings;
298 
299  QString fileName = QFileDialog::getOpenFileName( nullptr, tr( "Load Value Map from File" ), QDir::homePath() );
300  if ( fileName.isNull() )
301  return;
302 
303  QFile f( fileName );
304 
305  if ( !f.open( QIODevice::ReadOnly ) )
306  {
307  QMessageBox::information( nullptr,
308  tr( "Load Value Map from File" ),
309  tr( "Could not open file %1\nError was: %2" ).arg( fileName, f.errorString() ),
310  QMessageBox::Cancel );
311  return;
312  }
313 
314  QTextStream s( &f );
315  s.setAutoDetectUnicode( true );
316 
317  QRegExp re0( "^([^;]*);(.*)$" );
318  re0.setMinimal( true );
319  QRegExp re1( "^([^,]*),(.*)$" );
320  re1.setMinimal( true );
321 
322  QList<QPair<QString, QVariant>> map;
323 
324  while ( !s.atEnd() )
325  {
326  QString l = s.readLine().trimmed();
327 
328  QString key;
329  QString val;
330 
331  if ( re0.indexIn( l ) >= 0 && re0.captureCount() == 2 )
332  {
333  key = re0.cap( 1 ).trimmed();
334  val = re0.cap( 2 ).trimmed();
335  }
336  else if ( re1.indexIn( l ) >= 0 && re1.captureCount() == 2 )
337  {
338  key = re1.cap( 1 ).trimmed();
339  val = re1.cap( 2 ).trimmed();
340  }
341  else
342  continue;
343 
344  if ( ( key.startsWith( '\"' ) && key.endsWith( '\"' ) ) ||
345  ( key.startsWith( '\'' ) && key.endsWith( '\'' ) ) )
346  {
347  key = key.mid( 1, key.length() - 2 );
348  }
349 
350  if ( ( val.startsWith( '\"' ) && val.endsWith( '\"' ) ) ||
351  ( val.startsWith( '\'' ) && val.endsWith( '\'' ) ) )
352  {
353  val = val.mid( 1, val.length() - 2 );
354  }
355 
356  if ( key == QgsApplication::nullRepresentation() )
358 
359  map.append( qMakePair( key, val ) );
360  }
361 
362  updateMap( map, false );
363 }
QgsEditorConfigWidget
This class should be subclassed for every configurable editor widget type.
Definition: qgseditorconfigwidget.h:39
qgsattributetypeloaddialog.h
QgsValueMapFieldFormatter::NULL_VALUE
static const QString NULL_VALUE
Will be saved in the configuration when a value is NULL.
Definition: qgsvaluemapfieldformatter.h:47
QgsAttributeTypeLoadDialog
Definition: qgsattributetypeloaddialog.h:36
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QgsValueMapConfigDlg::setConfig
void setConfig(const QVariantMap &config) override
Update the configuration widget to represent the given configuration.
Definition: qgsvaluemapconfigdlg.cpp:82
qgsapplication.h
QgsValueMapConfigDlg::populateComboBox
static void populateComboBox(QComboBox *comboBox, const QVariantMap &configuration, bool skipNull)
Populates a comboBox with the appropriate entries based on a value map configuration.
Definition: qgsvaluemapconfigdlg.cpp:186
QgsEditorConfigWidget::layer
QgsVectorLayer * layer()
Returns the layer for which this configuration widget applies.
Definition: qgseditorconfigwidget.cpp:33
QgsApplication::nullRepresentation
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
Definition: qgsapplication.cpp:1851
QgsValueMapConfigDlg::updateMap
void updateMap(const QMap< QString, QVariant > &map, bool insertNull)
Updates the displayed table with the values from map.
Definition: qgsvaluemapconfigdlg.cpp:149
QgsValueMapConfigDlg::QgsValueMapConfigDlg
QgsValueMapConfigDlg(QgsVectorLayer *vl, int fieldIdx, QWidget *parent)
Definition: qgsvaluemapconfigdlg.cpp:29
qgsvaluemapfieldformatter.h
QgsValueMapConfigDlg::eventFilter
bool eventFilter(QObject *watched, QEvent *event) override
Definition: qgsvaluemapconfigdlg.cpp:215
QgsValueMapConfigDlg::config
QVariantMap config() override
Create a configuration from the current GUI state.
Definition: qgsvaluemapconfigdlg.cpp:47
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
qgssettings.h
QgsEditorConfigWidget::changed
void changed()
Emitted when the configuration of the widget is changed.
qgsvaluemapconfigdlg.h