17#include "moc_qgsvaluemapconfigdlg.cpp"
30#include <QRegularExpression>
37 mValueMapErrorsLabel->setVisible(
false );
38 mValueMapErrorsLabel->setStyleSheet( QStringLiteral(
"QLabel { color : red; }" ) );
40 tableWidget->insertRow( 0 );
42 tableWidget->horizontalHeader()->setSectionsClickable(
true );
43 tableWidget->setSortingEnabled(
true );
45 connect( addNullButton, &QAbstractButton::clicked,
this, &QgsValueMapConfigDlg::addNullButtonPushed );
46 connect( removeSelectedButton, &QAbstractButton::clicked,
this, &QgsValueMapConfigDlg::removeSelectedButtonPushed );
47 connect( loadFromLayerButton, &QAbstractButton::clicked,
this, &QgsValueMapConfigDlg::loadFromLayerButtonPushed );
48 connect( loadFromCSVButton, &QAbstractButton::clicked,
this, &QgsValueMapConfigDlg::loadFromCSVButtonPushed );
49 connect( tableWidget, &QTableWidget::cellChanged,
this, &QgsValueMapConfigDlg::vCellChanged );
50 tableWidget->installEventFilter(
this );
55 QList<QVariant> valueList;
58 for (
int i = 0; i < tableWidget->rowCount() - 1; i++ )
60 QTableWidgetItem *ki = tableWidget->item( i, 0 );
61 QTableWidgetItem *vi = tableWidget->item( i, 1 );
66 QString ks = ki->text();
72 if ( !vi || vi->text().isNull() )
74 value.insert( ks, ks );
78 value.insert( vi->text(), ks );
80 valueList.append( value );
84 cfg.insert( QStringLiteral(
"map" ), valueList );
90 tableWidget->clearContents();
91 for (
int i = tableWidget->rowCount() - 1; i > 0; i-- )
93 tableWidget->removeRow( i );
96 QList<QVariant> valueList =
config.value( QStringLiteral(
"map" ) ).toList();
97 QList<QPair<QString, QVariant>> orderedList;
99 if ( valueList.count() > 0 )
101 for (
int i = 0, row = 0; i < valueList.count(); i++, row++ )
103 orderedList.append( qMakePair( valueList[i].toMap().constBegin().value().toString(), valueList[i].toMap().constBegin().key() ) );
109 const QVariantMap values =
config.value( QStringLiteral(
"map" ) ).toMap();
110 for ( QVariantMap::ConstIterator mit = values.constBegin(); mit != values.constEnd(); mit++, row++ )
113 orderedList.append( qMakePair( mit.key(), QVariant() ) );
115 orderedList.append( qMakePair( mit.value().toString(), mit.key() ) );
123void QgsValueMapConfigDlg::vCellChanged(
int row,
int column )
126 if ( row == tableWidget->rowCount() - 1 )
128 tableWidget->insertRow( row + 1 );
134 QTableWidgetItem *item = tableWidget->item( row, 0 );
137 const QString validValue = checkValueLength( item->text() );
138 if ( validValue.length() != item->text().length() )
140 const QString errorMessage = tr(
"Value '%1' has been trimmed (maximum field length: %2)" )
141 .arg( item->text(), QString::number(
layer()->fields().
field(
field() ).length() ) );
142 item->setText( validValue );
143 mValueMapErrorsLabel->setVisible(
true );
144 mValueMapErrorsLabel->setText( QStringLiteral(
"%1<br>%2" ).arg( errorMessage, mValueMapErrorsLabel->text() ) );
152void QgsValueMapConfigDlg::removeSelectedButtonPushed()
154 QList<QTableWidgetItem *> list = tableWidget->selectedItems();
155 QSet<int> rowsToRemove;
158 for ( i = 0; i < list.size(); i++ )
160 if ( list[i]->column() == 0 )
162 const int row = list[i]->row();
163 if ( !rowsToRemove.contains( row ) )
165 rowsToRemove.insert( row );
169 for (
const int rowToRemoved : rowsToRemove )
171 tableWidget->removeRow( rowToRemoved - removed );
179 QList<QPair<QString, QVariant>> orderedMap;
180 const auto end = map.constEnd();
181 for (
auto it = map.constBegin(); it != end; ++it )
183 orderedMap.append( qMakePair( it.key(), it.value() ) );
191 tableWidget->clearContents();
192 mValueMapErrorsLabel->setVisible(
false );
194 for (
int i = tableWidget->rowCount() - 1; i > 0; i-- )
196 tableWidget->removeRow( i );
206 constexpr int maxOverflowErrors { 5 };
207 QStringList reportedErrors;
211 for (
const auto &pair : list )
214 setRow( row, pair.first, QString() );
217 const QString value { pair.first };
219 const QString validValue = checkValueLength( value ) ;
221 if ( validValue.length() != value.length() )
224 if ( reportedErrors.length() < maxOverflowErrors )
226 reportedErrors.push_back( tr(
"Value '%1' has been trimmed (maximum field length: %2)" )
227 .arg( value, QString::number( mappedField.length() ) ) );
229 else if ( reportedErrors.length() == maxOverflowErrors )
231 reportedErrors.push_back( tr(
"Only first %1 errors have been reported." )
232 .arg( maxOverflowErrors ) );
236 setRow( row, validValue, pair.second.toString() );
239 if ( !reportedErrors.isEmpty() )
241 mValueMapErrorsLabel->setVisible(
true );
242 mValueMapErrorsLabel->setText( reportedErrors.join( QLatin1String(
"<br>" ) ) );
249QString QgsValueMapConfigDlg::checkValueLength(
const QString &value )
254 if ( mappedField.length() > 0 && value.length() > mappedField.length() )
256 return value.mid( 0, mappedField.length() );
264 const QList<QVariant> valueList =
config.value( QStringLiteral(
"map" ) ).toList();
266 if ( !valueList.empty() )
268 for (
const QVariant &value : valueList )
270 const QVariantMap valueMap = value.toMap();
275 comboBox->addItem( valueMap.constBegin().key(), valueMap.constBegin().value() );
280 const QVariantMap map =
config.value( QStringLiteral(
"map" ) ).toMap();
281 for (
auto it = map.constBegin(); it != map.constEnd(); ++it )
286 comboBox->addItem( it.key(), it.value() );
294 if ( event->type() == QEvent::KeyPress )
296 QKeyEvent *keyEvent =
static_cast<QKeyEvent *
>( event );
297 if ( keyEvent->matches( QKeySequence::Copy ) )
299 copySelectionToClipboard();
307void QgsValueMapConfigDlg::setRow(
int row,
const QString &value,
const QString &description )
310 QTableWidgetItem *valueCell =
nullptr;
311 QTableWidgetItem *descriptionCell =
new QTableWidgetItem( description );
312 tableWidget->insertRow( row );
316 cellFont.setItalic(
true );
318 valueCell->setFont( cellFont );
319 valueCell->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
320 descriptionCell->setFont( cellFont );
324 valueCell =
new QTableWidgetItem( value );
326 tableWidget->setItem( row, 0, valueCell );
327 tableWidget->setItem( row, 1, descriptionCell );
330void QgsValueMapConfigDlg::copySelectionToClipboard()
332 QAbstractItemModel *model = tableWidget->model();
333 QItemSelectionModel *selection = tableWidget->selectionModel();
334 const QModelIndexList indexes = selection->selectedIndexes();
336 QString clipboardText;
337 QModelIndex previous = indexes.first();
338 std::unique_ptr<QMimeData> mimeData = std::make_unique<QMimeData>();
339 for (
const QModelIndex ¤t : indexes )
341 const QString text = model->data( current ).toString();
342 if ( current.row() != previous.row() )
344 clipboardText.append(
'\n' );
346 else if ( current.column() != previous.column() )
348 clipboardText.append(
'\t' );
350 clipboardText.append( text );
353 mimeData->setData( QStringLiteral(
"text/plain" ), clipboardText.toUtf8() );
354 QApplication::clipboard()->setMimeData( mimeData.release() );
357void QgsValueMapConfigDlg::addNullButtonPushed()
362void QgsValueMapConfigDlg::loadFromLayerButtonPushed()
365 if ( !layerDialog.exec() )
368 updateMap( layerDialog.valueMap(), layerDialog.insertNull() );
371void QgsValueMapConfigDlg::loadFromCSVButtonPushed()
375 const QString fileName = QFileDialog::getOpenFileName(
nullptr, tr(
"Load Value Map from File" ), QDir::homePath() );
376 if ( fileName.isNull() )
385 if ( !f.open( QIODevice::ReadOnly ) )
387 QMessageBox::information(
nullptr,
388 tr(
"Load Value Map from File" ),
389 tr(
"Could not open file %1\nError was: %2" ).arg( filePath, f.errorString() ),
390 QMessageBox::Cancel );
395 s.setAutoDetectUnicode(
true );
397 const thread_local QRegularExpression re(
"(?:^\"|[;,]\")(\"\"|[\\w\\W]*?)(?=\"[;,]|\"$)|(?:^(?!\")|[;,](?!\"))([^;,]*?)(?=$|[;,])|(\\r\\n|\\n)" );
398 QList<QPair<QString, QVariant>> map;
401 const QString l = s.readLine().trimmed();
402 QRegularExpressionMatchIterator matches = re.globalMatch( l );
404 while ( matches.hasNext() && ceils.size() < 2 )
406 const QRegularExpressionMatch match = matches.next();
407 ceils << match.capturedTexts().last().trimmed().replace( QLatin1String(
"\"\"" ), QLatin1String(
"\"" ) );
410 if ( ceils.size() != 2 )
413 QString key = ceils[0];
414 QString val = ceils[1];
417 map.append( qMakePair( key, val ) );
static QString nullRepresentation()
Returns the string used to represent the value NULL throughout QGIS.
Encapsulate a field in an attribute table or data source.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE bool exists(int i) const
Returns if a field index is valid.
This class is a composition of two QSettings instances:
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 bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based data sets.