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