QGIS API Documentation  3.6.0-Noosa (5873452)
qgsgraduatedsymbolrendererwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgraduatedsymbolrendererwidget.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
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  ***************************************************************************/
16 #include "qgspanelwidget.h"
17 
20 #include "qgssymbol.h"
21 #include "qgssymbollayerutils.h"
22 #include "qgscolorramp.h"
23 #include "qgscolorrampbutton.h"
24 #include "qgsstyle.h"
26 
27 #include "qgsvectorlayer.h"
28 
31 #include "qgslogger.h"
32 
33 #include "qgsludialog.h"
34 
35 #include "qgsproject.h"
36 #include "qgsmapcanvas.h"
37 
38 #include <QKeyEvent>
39 #include <QMenu>
40 #include <QMessageBox>
41 #include <QStandardItemModel>
42 #include <QStandardItem>
43 #include <QPen>
44 #include <QPainter>
45 
46 // ------------------------------ Model ------------------------------------
47 
49 
50 QgsGraduatedSymbolRendererModel::QgsGraduatedSymbolRendererModel( QObject *parent ) : QAbstractItemModel( parent )
51  , mMimeFormat( QStringLiteral( "application/x-qgsgraduatedsymbolrendererv2model" ) )
52 {
53 }
54 
55 void QgsGraduatedSymbolRendererModel::setRenderer( QgsGraduatedSymbolRenderer *renderer )
56 {
57  if ( mRenderer )
58  {
59  if ( mRenderer->ranges().size() )
60  {
61  beginRemoveRows( QModelIndex(), 0, mRenderer->ranges().size() - 1 );
62  mRenderer = nullptr;
63  endRemoveRows();
64  }
65  else
66  {
67  mRenderer = nullptr;
68  }
69  }
70  if ( renderer )
71  {
72  if ( renderer->ranges().size() )
73  {
74  beginInsertRows( QModelIndex(), 0, renderer->ranges().size() - 1 );
75  mRenderer = renderer;
76  endInsertRows();
77  }
78  else
79  {
80  mRenderer = renderer;
81  }
82  }
83 }
84 
85 void QgsGraduatedSymbolRendererModel::addClass( QgsSymbol *symbol )
86 {
87  if ( !mRenderer ) return;
88  int idx = mRenderer->ranges().size();
89  beginInsertRows( QModelIndex(), idx, idx );
90  mRenderer->addClass( symbol );
91  endInsertRows();
92 }
93 
94 void QgsGraduatedSymbolRendererModel::addClass( const QgsRendererRange &range )
95 {
96  if ( !mRenderer )
97  {
98  return;
99  }
100  int idx = mRenderer->ranges().size();
101  beginInsertRows( QModelIndex(), idx, idx );
102  mRenderer->addClass( range );
103  endInsertRows();
104 }
105 
106 QgsRendererRange QgsGraduatedSymbolRendererModel::rendererRange( const QModelIndex &index )
107 {
108  if ( !index.isValid() || !mRenderer || mRenderer->ranges().size() <= index.row() )
109  {
110  return QgsRendererRange();
111  }
112 
113  return mRenderer->ranges().value( index.row() );
114 }
115 
116 Qt::ItemFlags QgsGraduatedSymbolRendererModel::flags( const QModelIndex &index ) const
117 {
118  if ( !index.isValid() )
119  {
120  return Qt::ItemIsDropEnabled;
121  }
122 
123  Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
124 
125  if ( index.column() == 2 )
126  {
127  flags |= Qt::ItemIsEditable;
128  }
129 
130  return flags;
131 }
132 
133 Qt::DropActions QgsGraduatedSymbolRendererModel::supportedDropActions() const
134 {
135  return Qt::MoveAction;
136 }
137 
138 QVariant QgsGraduatedSymbolRendererModel::data( const QModelIndex &index, int role ) const
139 {
140  if ( !index.isValid() || !mRenderer ) return QVariant();
141 
142  const QgsRendererRange range = mRenderer->ranges().value( index.row() );
143 
144  if ( role == Qt::CheckStateRole && index.column() == 0 )
145  {
146  return range.renderState() ? Qt::Checked : Qt::Unchecked;
147  }
148  else if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
149  {
150  switch ( index.column() )
151  {
152  case 1:
153  {
154  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
155  if ( decimalPlaces < 0 ) decimalPlaces = 0;
156  return QLocale().toString( range.lowerValue(), 'f', decimalPlaces ) + " - " + QLocale().toString( range.upperValue(), 'f', decimalPlaces );
157  }
158  case 2:
159  return range.label();
160  default:
161  return QVariant();
162  }
163  }
164  else if ( role == Qt::DecorationRole && index.column() == 0 && range.symbol() )
165  {
166  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
167  return QgsSymbolLayerUtils::symbolPreviewIcon( range.symbol(), QSize( iconSize, iconSize ) );
168  }
169  else if ( role == Qt::TextAlignmentRole )
170  {
171  return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
172  }
173  else if ( role == Qt::EditRole )
174  {
175  switch ( index.column() )
176  {
177  // case 1: return rangeStr;
178  case 2:
179  return range.label();
180  default:
181  return QVariant();
182  }
183  }
184 
185  return QVariant();
186 }
187 
188 bool QgsGraduatedSymbolRendererModel::setData( const QModelIndex &index, const QVariant &value, int role )
189 {
190  if ( !index.isValid() )
191  return false;
192 
193  if ( index.column() == 0 && role == Qt::CheckStateRole )
194  {
195  mRenderer->updateRangeRenderState( index.row(), value == Qt::Checked );
196  emit dataChanged( index, index );
197  return true;
198  }
199 
200  if ( role != Qt::EditRole )
201  return false;
202 
203  switch ( index.column() )
204  {
205  case 1: // range
206  return false; // range is edited in popup dialog
207  case 2: // label
208  mRenderer->updateRangeLabel( index.row(), value.toString() );
209  break;
210  default:
211  return false;
212  }
213 
214  emit dataChanged( index, index );
215  return true;
216 }
217 
218 QVariant QgsGraduatedSymbolRendererModel::headerData( int section, Qt::Orientation orientation, int role ) const
219 {
220  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
221  {
222  QStringList lst;
223  lst << tr( "Symbol" ) << tr( "Values" ) << tr( "Legend" );
224  return lst.value( section );
225  }
226  return QVariant();
227 }
228 
229 int QgsGraduatedSymbolRendererModel::rowCount( const QModelIndex &parent ) const
230 {
231  if ( parent.isValid() || !mRenderer )
232  {
233  return 0;
234  }
235  return mRenderer->ranges().size();
236 }
237 
238 int QgsGraduatedSymbolRendererModel::columnCount( const QModelIndex &index ) const
239 {
240  Q_UNUSED( index );
241  return 3;
242 }
243 
244 QModelIndex QgsGraduatedSymbolRendererModel::index( int row, int column, const QModelIndex &parent ) const
245 {
246  if ( hasIndex( row, column, parent ) )
247  {
248  return createIndex( row, column );
249  }
250  return QModelIndex();
251 }
252 
253 QModelIndex QgsGraduatedSymbolRendererModel::parent( const QModelIndex &index ) const
254 {
255  Q_UNUSED( index );
256  return QModelIndex();
257 }
258 
259 QStringList QgsGraduatedSymbolRendererModel::mimeTypes() const
260 {
261  QStringList types;
262  types << mMimeFormat;
263  return types;
264 }
265 
266 QMimeData *QgsGraduatedSymbolRendererModel::mimeData( const QModelIndexList &indexes ) const
267 {
268  QMimeData *mimeData = new QMimeData();
269  QByteArray encodedData;
270 
271  QDataStream stream( &encodedData, QIODevice::WriteOnly );
272 
273  // Create list of rows
274  Q_FOREACH ( const QModelIndex &index, indexes )
275  {
276  if ( !index.isValid() || index.column() != 0 )
277  continue;
278 
279  stream << index.row();
280  }
281  mimeData->setData( mMimeFormat, encodedData );
282  return mimeData;
283 }
284 
285 bool QgsGraduatedSymbolRendererModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
286 {
287  Q_UNUSED( row );
288  Q_UNUSED( column );
289  if ( action != Qt::MoveAction ) return true;
290 
291  if ( !data->hasFormat( mMimeFormat ) ) return false;
292 
293  QByteArray encodedData = data->data( mMimeFormat );
294  QDataStream stream( &encodedData, QIODevice::ReadOnly );
295 
296  QVector<int> rows;
297  while ( !stream.atEnd() )
298  {
299  int r;
300  stream >> r;
301  rows.append( r );
302  }
303 
304  int to = parent.row();
305  // to is -1 if dragged outside items, i.e. below any item,
306  // then move to the last position
307  if ( to == -1 ) to = mRenderer->ranges().size(); // out of rang ok, will be decreased
308  for ( int i = rows.size() - 1; i >= 0; i-- )
309  {
310  QgsDebugMsg( QStringLiteral( "move %1 to %2" ).arg( rows[i] ).arg( to ) );
311  int t = to;
312  // moveCategory first removes and then inserts
313  if ( rows[i] < t ) t--;
314  mRenderer->moveClass( rows[i], t );
315  // current moved under another, shift its index up
316  for ( int j = 0; j < i; j++ )
317  {
318  if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
319  }
320  // removed under 'to' so the target shifted down
321  if ( rows[i] < to ) to--;
322  }
323  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
324  emit rowsMoved();
325  return false;
326 }
327 
328 void QgsGraduatedSymbolRendererModel::deleteRows( QList<int> rows )
329 {
330  for ( int i = rows.size() - 1; i >= 0; i-- )
331  {
332  beginRemoveRows( QModelIndex(), rows[i], rows[i] );
333  mRenderer->deleteClass( rows[i] );
334  endRemoveRows();
335  }
336 }
337 
338 void QgsGraduatedSymbolRendererModel::removeAllRows()
339 {
340  beginRemoveRows( QModelIndex(), 0, mRenderer->ranges().size() - 1 );
341  mRenderer->deleteAllClasses();
342  endRemoveRows();
343 }
344 
345 void QgsGraduatedSymbolRendererModel::sort( int column, Qt::SortOrder order )
346 {
347  if ( column == 0 )
348  {
349  return;
350  }
351  if ( column == 1 )
352  {
353  mRenderer->sortByValue( order );
354  }
355  else if ( column == 2 )
356  {
357  mRenderer->sortByLabel( order );
358  }
359  emit rowsMoved();
360  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
361 }
362 
363 void QgsGraduatedSymbolRendererModel::updateSymbology( bool resetModel )
364 {
365  if ( resetModel )
366  {
367  reset();
368  }
369  else
370  {
371  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
372  }
373 }
374 
375 void QgsGraduatedSymbolRendererModel::updateLabels()
376 {
377  emit dataChanged( createIndex( 0, 2 ), createIndex( mRenderer->ranges().size(), 2 ) );
378 }
379 
380 // ------------------------------ View style --------------------------------
381 QgsGraduatedSymbolRendererViewStyle::QgsGraduatedSymbolRendererViewStyle( QWidget *parent )
382  : QgsProxyStyle( parent )
383 {}
384 
385 void QgsGraduatedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget ) const
386 {
387  if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
388  {
389  QStyleOption opt( *option );
390  opt.rect.setLeft( 0 );
391  // draw always as line above, because we move item to that index
392  opt.rect.setHeight( 0 );
393  if ( widget ) opt.rect.setRight( widget->width() );
394  QProxyStyle::drawPrimitive( element, &opt, painter, widget );
395  return;
396  }
397  QProxyStyle::drawPrimitive( element, option, painter, widget );
398 }
399 
401 
402 // ------------------------------ Widget ------------------------------------
403 
405 {
406  return new QgsGraduatedSymbolRendererWidget( layer, style, renderer );
407 }
408 
409 QgsExpressionContext QgsGraduatedSymbolRendererWidget::createExpressionContext() const
410 {
411  QgsExpressionContext expContext;
415 
416  if ( mContext.mapCanvas() )
417  {
418  expContext << QgsExpressionContextUtils::mapSettingsScope( mContext.mapCanvas()->mapSettings() )
419  << new QgsExpressionContextScope( mContext.mapCanvas()->expressionContextScope() );
420  }
421  else
422  {
424  }
425 
426  if ( vectorLayer() )
427  expContext << QgsExpressionContextUtils::layerScope( vectorLayer() );
428 
429  // additional scopes
430  Q_FOREACH ( const QgsExpressionContextScope &scope, mContext.additionalExpressionContextScopes() )
431  {
432  expContext.appendScope( new QgsExpressionContextScope( scope ) );
433  }
434 
435  return expContext;
436 }
437 
439  : QgsRendererWidget( layer, style )
440 {
441  // try to recognize the previous renderer
442  // (null renderer means "no previous renderer")
443  if ( renderer )
444  {
445  mRenderer.reset( QgsGraduatedSymbolRenderer::convertFromRenderer( renderer ) );
446  }
447  if ( !mRenderer )
448  {
449  mRenderer = qgis::make_unique< QgsGraduatedSymbolRenderer >( QString(), QgsRangeList() );
450  }
451 
452  // setup user interface
453  setupUi( this );
454 
455  cboGraduatedMode->addItem( tr( "Equal Interval" ), QgsGraduatedSymbolRenderer::EqualInterval );
456  cboGraduatedMode->addItem( tr( "Quantile (Equal Count)" ), QgsGraduatedSymbolRenderer::Quantile );
457  cboGraduatedMode->addItem( tr( "Natural Breaks (Jenks)" ), QgsGraduatedSymbolRenderer::Jenks );
458  cboGraduatedMode->addItem( tr( "Standard Deviation" ), QgsGraduatedSymbolRenderer::StdDev );
459  cboGraduatedMode->addItem( tr( "Pretty Breaks" ), QgsGraduatedSymbolRenderer::Pretty );
460 
461  connect( methodComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsGraduatedSymbolRendererWidget::methodComboBox_currentIndexChanged );
462  this->layout()->setContentsMargins( 0, 0, 0, 0 );
463 
464  mModel = new QgsGraduatedSymbolRendererModel( this );
465 
466  mExpressionWidget->setFilters( QgsFieldProxyModel::Numeric | QgsFieldProxyModel::Date );
467  mExpressionWidget->setLayer( mLayer );
468 
471 
472  spinPrecision->setMinimum( QgsRendererRangeLabelFormat::MIN_PRECISION );
473  spinPrecision->setMaximum( QgsRendererRangeLabelFormat::MAX_PRECISION );
474 
475  btnColorRamp->setShowRandomColorRamp( true );
476 
477  // set project default color ramp
478  QString defaultColorRamp = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ), QString() );
479  if ( !defaultColorRamp.isEmpty() )
480  {
481  btnColorRamp->setColorRampFromName( defaultColorRamp );
482  }
483  else
484  {
485  QgsColorRamp *ramp = new QgsGradientColorRamp( QColor( 255, 255, 255 ), QColor( 255, 0, 0 ) );
486  btnColorRamp->setColorRamp( ramp );
487  delete ramp;
488  }
489 
490 
491  viewGraduated->setStyle( new QgsGraduatedSymbolRendererViewStyle( viewGraduated ) );
492 
493  mGraduatedSymbol.reset( QgsSymbol::defaultSymbol( mLayer->geometryType() ) );
494 
495  methodComboBox->blockSignals( true );
496  methodComboBox->addItem( QStringLiteral( "Color" ) );
497  if ( mGraduatedSymbol->type() == QgsSymbol::Marker )
498  {
499  methodComboBox->addItem( QStringLiteral( "Size" ) );
500  minSizeSpinBox->setValue( 1 );
501  maxSizeSpinBox->setValue( 8 );
502  }
503  else if ( mGraduatedSymbol->type() == QgsSymbol::Line )
504  {
505  methodComboBox->addItem( QStringLiteral( "Size" ) );
506  minSizeSpinBox->setValue( .1 );
507  maxSizeSpinBox->setValue( 2 );
508  }
509  methodComboBox->blockSignals( false );
510 
511  connect( mExpressionWidget, static_cast < void ( QgsFieldExpressionWidget::* )( const QString & ) >( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsGraduatedSymbolRendererWidget::graduatedColumnChanged );
512  connect( viewGraduated, &QAbstractItemView::doubleClicked, this, &QgsGraduatedSymbolRendererWidget::rangesDoubleClicked );
513  connect( viewGraduated, &QAbstractItemView::clicked, this, &QgsGraduatedSymbolRendererWidget::rangesClicked );
514  connect( viewGraduated, &QTreeView::customContextMenuRequested, this, &QgsGraduatedSymbolRendererWidget::contextMenuViewCategories );
515 
516  connect( btnGraduatedClassify, &QAbstractButton::clicked, this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
517  connect( btnChangeGraduatedSymbol, &QAbstractButton::clicked, this, &QgsGraduatedSymbolRendererWidget::changeGraduatedSymbol );
518  connect( btnGraduatedDelete, &QAbstractButton::clicked, this, &QgsGraduatedSymbolRendererWidget::deleteClasses );
519  connect( btnDeleteAllClasses, &QAbstractButton::clicked, this, &QgsGraduatedSymbolRendererWidget::deleteAllClasses );
520  connect( btnGraduatedAdd, &QAbstractButton::clicked, this, &QgsGraduatedSymbolRendererWidget::addClass );
521  connect( cbxLinkBoundaries, &QAbstractButton::toggled, this, &QgsGraduatedSymbolRendererWidget::toggleBoundariesLink );
522  connect( mSizeUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsGraduatedSymbolRendererWidget::mSizeUnitWidget_changed );
523 
525 
526  // initialize from previously set renderer
528 
529  // default to collapsed symmetric group for ui simplicity
530  mGroupBoxSymmetric->setCollapsed( true ); //
531 
532  // menus for data-defined rotation/size
533  QMenu *advMenu = new QMenu( this );
534 
535  advMenu->addAction( tr( "Symbol Levels…" ), this, SLOT( showSymbolLevels() ) );
536  if ( mGraduatedSymbol->type() == QgsSymbol::Marker )
537  {
538  QAction *actionDdsLegend = advMenu->addAction( tr( "Data-defined Size Legend…" ) );
539  // only from Qt 5.6 there is convenience addAction() with new style connection
540  connect( actionDdsLegend, &QAction::triggered, this, &QgsGraduatedSymbolRendererWidget::dataDefinedSizeLegend );
541  }
542 
543  btnAdvanced->setMenu( advMenu );
544 
545  mHistogramWidget->setLayer( mLayer );
546  mHistogramWidget->setRenderer( mRenderer.get() );
548  connect( mExpressionWidget, static_cast < void ( QgsFieldExpressionWidget::* )( const QString & ) >( &QgsFieldExpressionWidget::fieldChanged ), mHistogramWidget, &QgsHistogramWidget::setSourceFieldExp );
549 
550  mExpressionWidget->registerExpressionContextGenerator( this );
551 }
552 
553 void QgsGraduatedSymbolRendererWidget::mSizeUnitWidget_changed()
554 {
555  if ( !mGraduatedSymbol ) return;
556  mGraduatedSymbol->setOutputUnit( mSizeUnitWidget->unit() );
557  mGraduatedSymbol->setMapUnitScale( mSizeUnitWidget->getMapUnitScale() );
559  mRenderer->updateSymbols( mGraduatedSymbol.get() );
561 }
562 
564 {
565  delete mModel;
566 }
567 
569 {
570  return mRenderer.get();
571 }
572 
573 // Connect/disconnect event handlers which trigger updating renderer
575 {
576  connect( spinGraduatedClasses, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
577  connect( cboGraduatedMode, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
579  connect( spinPrecision, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsGraduatedSymbolRendererWidget::labelFormatChanged );
580  connect( cbxTrimTrailingZeroes, &QAbstractButton::toggled, this, &QgsGraduatedSymbolRendererWidget::labelFormatChanged );
581  connect( txtLegendFormat, &QLineEdit::textChanged, this, &QgsGraduatedSymbolRendererWidget::labelFormatChanged );
582  connect( minSizeSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsGraduatedSymbolRendererWidget::reapplySizes );
583  connect( maxSizeSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsGraduatedSymbolRendererWidget::reapplySizes );
584 
585  connect( mModel, &QgsGraduatedSymbolRendererModel::rowsMoved, this, &QgsGraduatedSymbolRendererWidget::rowsMoved );
586  connect( mModel, &QAbstractItemModel::dataChanged, this, &QgsGraduatedSymbolRendererWidget::modelDataChanged );
587 
588  connect( mGroupBoxSymmetric, &QGroupBox::toggled, this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
589  connect( cbxAstride, &QAbstractButton::toggled, this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
590  connect( cboSymmetryPointForPretty, static_cast<void ( QComboBox::* )( int )>( &QComboBox::activated ), this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
591  connect( spinSymmetryPointForOtherMethods, static_cast<void( QgsDoubleSpinBox::* )()>( &QgsDoubleSpinBox::editingFinished ), this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
592 }
593 
594 // Connect/disconnect event handlers which trigger updating renderer
596 {
597  disconnect( spinGraduatedClasses, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
598  disconnect( cboGraduatedMode, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
600  disconnect( spinPrecision, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsGraduatedSymbolRendererWidget::labelFormatChanged );
601  disconnect( cbxTrimTrailingZeroes, &QAbstractButton::toggled, this, &QgsGraduatedSymbolRendererWidget::labelFormatChanged );
602  disconnect( txtLegendFormat, &QLineEdit::textChanged, this, &QgsGraduatedSymbolRendererWidget::labelFormatChanged );
603  disconnect( minSizeSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsGraduatedSymbolRendererWidget::reapplySizes );
604  disconnect( maxSizeSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsGraduatedSymbolRendererWidget::reapplySizes );
605 
606  disconnect( mModel, &QgsGraduatedSymbolRendererModel::rowsMoved, this, &QgsGraduatedSymbolRendererWidget::rowsMoved );
607  disconnect( mModel, &QAbstractItemModel::dataChanged, this, &QgsGraduatedSymbolRendererWidget::modelDataChanged );
608 
609  disconnect( mGroupBoxSymmetric, &QGroupBox::toggled, this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
610  disconnect( cbxAstride, &QAbstractButton::toggled, this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
611  disconnect( cboSymmetryPointForPretty, static_cast<void ( QComboBox::* )( int )>( &QComboBox::activated ), this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
612  disconnect( spinSymmetryPointForOtherMethods, static_cast<void( QgsDoubleSpinBox::* )()>( &QgsDoubleSpinBox::editingFinished ), this, &QgsGraduatedSymbolRendererWidget::classifyGraduated );
613 }
614 
616 {
619  spinSymmetryPointForOtherMethods->setShowClearButton( false );
620 
621  // update UI from the graduated renderer (update combo boxes, view)
622  if ( cboGraduatedMode->findData( mRenderer->mode() ) >= 0 )
623  {
624  cboGraduatedMode->setCurrentIndex( cboGraduatedMode->findData( mRenderer->mode() ) );
625  }
626 
627  // symmetric classification
628  const QgsGraduatedSymbolRenderer::Mode cboMode = static_cast< QgsGraduatedSymbolRenderer::Mode >( cboGraduatedMode->currentData().toInt() );
629  switch ( cboMode )
630  {
633  {
634  mGroupBoxSymmetric->setVisible( true );
635  cbxAstride->setVisible( true );
636  cboSymmetryPointForPretty->setVisible( false );
637  spinSymmetryPointForOtherMethods->setVisible( true );
638  spinSymmetryPointForOtherMethods->setValue( mRenderer->symmetryPoint() );
639  break;
640  }
641 
643  {
644  mGroupBoxSymmetric->setVisible( true );
645  cbxAstride->setVisible( true );
646  spinSymmetryPointForOtherMethods->setVisible( false );
647  cboSymmetryPointForPretty->setVisible( true );
648  cboSymmetryPointForPretty->clear();
649  cboSymmetryPointForPretty->addItems( mRenderer->listForCboPrettyBreaks() );
650  // replace the combobox on the good old value
651  cboSymmetryPointForPretty->setCurrentText( QString::number( mRenderer->symmetryPoint(), 'f', 2 ) );
652  break;
653  }
654 
658  {
659  mGroupBoxSymmetric->setVisible( false );
660  cbxAstride->setVisible( false );
661  cboSymmetryPointForPretty->setVisible( false );
662  spinSymmetryPointForOtherMethods->setVisible( false );
663  spinSymmetryPointForOtherMethods->setValue( mRenderer->symmetryPoint() );
664  break;
665  }
666  }
667 
668  if ( mRenderer->useSymmetricMode() )
669  {
670  mGroupBoxSymmetric->setChecked( true );
671  spinSymmetryPointForOtherMethods->setEnabled( true );
672  cbxAstride->setEnabled( true );
673  cboSymmetryPointForPretty->setEnabled( true );
674  }
675  else
676  {
677  mGroupBoxSymmetric->setChecked( false );
678  spinSymmetryPointForOtherMethods->setEnabled( false );
679  cbxAstride->setEnabled( false );
680  cboSymmetryPointForPretty->setEnabled( false );
681  }
682 
683  if ( mRenderer->astride() )
684  cbxAstride->setChecked( true );
685  else
686  cbxAstride->setChecked( false );
687 
688  // Only update class count if different - otherwise typing value gets very messy
689  int nclasses = mRenderer->ranges().count();
690  if ( nclasses && updateCount )
691  spinGraduatedClasses->setValue( mRenderer->ranges().count() );
692 
693  // set column
694  QString attrName = mRenderer->classAttribute();
695  mExpressionWidget->setField( attrName );
696  mHistogramWidget->setSourceFieldExp( attrName );
697 
698  // set source symbol
699  if ( mRenderer->sourceSymbol() )
700  {
701  mGraduatedSymbol.reset( mRenderer->sourceSymbol()->clone() );
703  }
704 
705  mModel->setRenderer( mRenderer.get() );
706  viewGraduated->setModel( mModel );
707 
708  if ( mGraduatedSymbol )
709  {
710  mSizeUnitWidget->blockSignals( true );
711  mSizeUnitWidget->setUnit( mGraduatedSymbol->outputUnit() );
712  mSizeUnitWidget->setMapUnitScale( mGraduatedSymbol->mapUnitScale() );
713  mSizeUnitWidget->blockSignals( false );
714  }
715 
716  // set source color ramp
717  methodComboBox->blockSignals( true );
718  if ( mRenderer->graduatedMethod() == QgsGraduatedSymbolRenderer::GraduatedColor )
719  {
720  methodComboBox->setCurrentIndex( 0 );
721  if ( mRenderer->sourceColorRamp() )
722  {
723  btnColorRamp->setColorRamp( mRenderer->sourceColorRamp() );
724  }
725  }
726  else
727  {
728  methodComboBox->setCurrentIndex( 1 );
729  if ( !mRenderer->ranges().isEmpty() ) // avoid overriding default size with zeros
730  {
731  minSizeSpinBox->setValue( mRenderer->minSymbolSize() );
732  maxSizeSpinBox->setValue( mRenderer->maxSymbolSize() );
733  }
734  }
735  toggleMethodWidgets( methodComboBox->currentIndex() );
736  methodComboBox->blockSignals( false );
737 
738  QgsRendererRangeLabelFormat labelFormat = mRenderer->labelFormat();
739  txtLegendFormat->setText( labelFormat.format() );
740  spinPrecision->setValue( labelFormat.precision() );
741  cbxTrimTrailingZeroes->setChecked( labelFormat.trimTrailingZeroes() );
742 
743  viewGraduated->resizeColumnToContents( 0 );
744  viewGraduated->resizeColumnToContents( 1 );
745  viewGraduated->resizeColumnToContents( 2 );
746 
747  mHistogramWidget->refresh();
748 
750  emit widgetChanged();
751 }
752 
754 {
755  mRenderer->setClassAttribute( field );
756 }
757 
758 void QgsGraduatedSymbolRendererWidget::methodComboBox_currentIndexChanged( int idx )
759 {
760  toggleMethodWidgets( idx );
761  if ( idx == 0 )
762  {
763  mRenderer->setGraduatedMethod( QgsGraduatedSymbolRenderer::GraduatedColor );
764  QgsColorRamp *ramp = btnColorRamp->colorRamp();
765 
766  if ( !ramp )
767  {
768  QMessageBox::critical( this, tr( "Select Method" ), tr( "No color ramp defined." ) );
769  return;
770  }
771  mRenderer->setSourceColorRamp( ramp );
773  }
774  else
775  {
776  lblColorRamp->setVisible( false );
777  btnColorRamp->setVisible( false );
778  lblSize->setVisible( true );
779  minSizeSpinBox->setVisible( true );
780  lblSize->setVisible( true );
781  maxSizeSpinBox->setVisible( true );
782  mSizeUnitWidget->setVisible( true );
783 
784  mRenderer->setGraduatedMethod( QgsGraduatedSymbolRenderer::GraduatedSize );
785  reapplySizes();
786  }
787 }
788 
789 void QgsGraduatedSymbolRendererWidget::toggleMethodWidgets( int idx )
790 {
791  if ( idx == 0 )
792  {
793  lblColorRamp->setVisible( true );
794  btnColorRamp->setVisible( true );
795  lblSize->setVisible( false );
796  minSizeSpinBox->setVisible( false );
797  lblSizeTo->setVisible( false );
798  maxSizeSpinBox->setVisible( false );
799  mSizeUnitWidget->setVisible( false );
800  }
801  else
802  {
803  lblColorRamp->setVisible( false );
804  btnColorRamp->setVisible( false );
805  lblSize->setVisible( true );
806  minSizeSpinBox->setVisible( true );
807  lblSizeTo->setVisible( true );
808  maxSizeSpinBox->setVisible( true );
809  mSizeUnitWidget->setVisible( true );
810  }
811 }
812 
814 {
815  if ( !mModel )
816  return;
817 
818  mModel->updateSymbology( reset );
819  emit widgetChanged();
820 }
821 
822 void QgsGraduatedSymbolRendererWidget::cleanUpSymbolSelector( QgsPanelWidget *container )
823 {
824  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( container );
825  if ( !dlg )
826  return;
827 
828  delete dlg->symbol();
829 }
830 
831 void QgsGraduatedSymbolRendererWidget::updateSymbolsFromWidget()
832 {
833  QgsSymbolSelectorWidget *dlg = qobject_cast<QgsSymbolSelectorWidget *>( sender() );
834  mGraduatedSymbol.reset( dlg->symbol()->clone() );
835 
837 }
838 
840 {
841  mSizeUnitWidget->blockSignals( true );
842  mSizeUnitWidget->setUnit( mGraduatedSymbol->outputUnit() );
843  mSizeUnitWidget->setMapUnitScale( mGraduatedSymbol->mapUnitScale() );
844  mSizeUnitWidget->blockSignals( false );
845 
846  QItemSelectionModel *m = viewGraduated->selectionModel();
847  QModelIndexList selectedIndexes = m->selectedRows( 1 );
848  if ( m && !selectedIndexes.isEmpty() )
849  {
850  Q_FOREACH ( const QModelIndex &idx, selectedIndexes )
851  {
852  if ( idx.isValid() )
853  {
854  int rangeIdx = idx.row();
855  QgsSymbol *newRangeSymbol = mGraduatedSymbol->clone();
856  if ( selectedIndexes.count() > 1 )
857  {
858  //if updating multiple ranges, retain the existing range colors
859  newRangeSymbol->setColor( mRenderer->ranges().at( rangeIdx ).symbol()->color() );
860  }
861  mRenderer->updateRangeSymbol( rangeIdx, newRangeSymbol );
862  }
863  }
864  }
865  else
866  {
867  mRenderer->updateSymbols( mGraduatedSymbol.get() );
868  }
869 
871  emit widgetChanged();
872 }
873 
874 
876 {
877  QString attrName = mExpressionWidget->currentField();
878  int nclasses = spinGraduatedClasses->value();
879 
880  std::unique_ptr<QgsColorRamp> ramp( btnColorRamp->colorRamp() );
881  if ( !ramp )
882  {
883  QMessageBox::critical( this, tr( "Apply Classification" ), tr( "No color ramp defined." ) );
884  return;
885  }
886 
888  bool useSymmetricMode = false;
889  bool astride = false;
890 
891  int attrNum = mLayer->fields().lookupField( attrName );
892  double minimum = mLayer->minimumValue( attrNum ).toDouble();
893  double maximum = mLayer->maximumValue( attrNum ).toDouble();
894  spinSymmetryPointForOtherMethods->setMinimum( minimum );
895  spinSymmetryPointForOtherMethods->setMaximum( maximum );
896  spinSymmetryPointForOtherMethods->setDecimals( spinPrecision->value() );
897 
898  double symmetryPoint = spinSymmetryPointForOtherMethods->value();
899 
900  const QgsGraduatedSymbolRenderer::Mode cboMode = static_cast< QgsGraduatedSymbolRenderer::Mode >( cboGraduatedMode->currentData().toInt() );
901  switch ( cboMode )
902  {
904  {
906  // knowing that spinSymmetryPointForOtherMethods->value() is automatically put at minimum when out of min-max
907  // using "(maximum-minimum)/100)" to avoid direct comparison of doubles
908  if ( spinSymmetryPointForOtherMethods->value() < ( minimum + ( maximum - minimum ) / 100. ) || spinSymmetryPointForOtherMethods->value() > ( maximum - ( maximum - minimum ) / 100. ) )
909  spinSymmetryPointForOtherMethods->setValue( minimum + ( maximum - minimum ) / 2. );
910 
911  if ( mGroupBoxSymmetric->isChecked() )
912  {
913  useSymmetricMode = true;
914  symmetryPoint = spinSymmetryPointForOtherMethods->value();
915  astride = cbxAstride->isChecked();
916  }
917  break;
918  }
919 
921  {
923  break;
924  }
925 
927  {
929  // knowing that spinSymmetryPointForOtherMethods->value() is automatically put at minimum when out of min-max
930  // using "(maximum-minimum)/100)" to avoid direct comparison of doubles
931  if ( spinSymmetryPointForOtherMethods->value() < ( minimum + ( maximum - minimum ) / 100. ) || spinSymmetryPointForOtherMethods->value() > ( maximum - ( maximum - minimum ) / 100. ) )
932  spinSymmetryPointForOtherMethods->setValue( minimum + ( maximum - minimum ) / 2. );
933 
934  if ( mGroupBoxSymmetric->isChecked() )
935  {
936  useSymmetricMode = true;
937  symmetryPoint = spinSymmetryPointForOtherMethods->value();
938  astride = cbxAstride->isChecked();
939  }
940  break;
941  }
942 
944  {
946  if ( mGroupBoxSymmetric->isChecked() )
947  {
948  useSymmetricMode = true;
949  astride = cbxAstride->isChecked();
950  symmetryPoint = cboSymmetryPointForPretty->currentText().toDouble(); //selected number
951  }
952  break;
953  }
954 
957  {
958  // default should be quantile for now
959  mode = QgsGraduatedSymbolRenderer::Quantile; // Quantile
960  break;
961  }
962  }
963 
964  // Jenks is n^2 complexity, warn for big dataset (more than 50k records)
965  // and give the user the chance to cancel
966  if ( QgsGraduatedSymbolRenderer::Jenks == mode && mLayer->featureCount() > 50000 )
967  {
968  if ( QMessageBox::Cancel == QMessageBox::question( this, tr( "Apply Classification" ), tr( "Natural break classification (Jenks) is O(n2) complexity, your classification may take a long time.\nPress cancel to abort breaks calculation or OK to continue." ), QMessageBox::Cancel, QMessageBox::Ok ) )
969  return;
970  }
971  // create and set new renderer
972  mRenderer->setClassAttribute( attrName );
973  mRenderer->setMode( mode );
974  mRenderer->setUseSymmetricMode( useSymmetricMode );
975  mRenderer->setSymmetryPoint( symmetryPoint );
976  mRenderer->setAstride( astride );
977 
978  if ( methodComboBox->currentIndex() == 0 )
979  {
980  if ( !ramp )
981  {
982  QMessageBox::critical( this, tr( "Apply Classification" ), tr( "No color ramp defined." ) );
983  return;
984  }
985  mRenderer->setSourceColorRamp( ramp.release() );
986  }
987  else
988  {
989  mRenderer->setSourceColorRamp( nullptr );
990  }
991 
992  QApplication::setOverrideCursor( Qt::WaitCursor );
993 
994  mRenderer->updateClasses( mLayer, mode, nclasses, useSymmetricMode, symmetryPoint, astride );
995 
996  if ( methodComboBox->currentIndex() == 1 )
997  mRenderer->setSymbolSizes( minSizeSpinBox->value(), maxSizeSpinBox->value() );
998 
999  mRenderer->calculateLabelPrecision();
1000  QApplication::restoreOverrideCursor();
1001  // PrettyBreaks and StdDev calculation don't generate exact
1002  // number of classes - leave user interface unchanged for these
1003  updateUiFromRenderer( false );
1004 }
1005 
1007 {
1008  std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
1009  if ( !ramp )
1010  return;
1011 
1012  mRenderer->updateColorRamp( ramp.release() );
1013  mRenderer->updateSymbols( mGraduatedSymbol.get() );
1015 }
1016 
1018 {
1019  mRenderer->setSymbolSizes( minSizeSpinBox->value(), maxSizeSpinBox->value() );
1020  mRenderer->updateSymbols( mGraduatedSymbol.get() );
1022 }
1023 
1025 {
1027  std::unique_ptr< QgsSymbol > newSymbol( mGraduatedSymbol->clone() );
1028  if ( panel && panel->dockMode() )
1029  {
1030  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( newSymbol.release(), mStyle, mLayer, panel );
1031  dlg->setContext( mContext );
1032 
1033  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsGraduatedSymbolRendererWidget::updateSymbolsFromWidget );
1034  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsGraduatedSymbolRendererWidget::cleanUpSymbolSelector );
1036  panel->openPanel( dlg );
1037  }
1038  else
1039  {
1040  QgsSymbolSelectorDialog dlg( newSymbol.get(), mStyle, mLayer, panel );
1041  if ( !dlg.exec() || !newSymbol )
1042  {
1043  return;
1044  }
1045 
1046  mGraduatedSymbol = std::move( newSymbol );
1049  }
1050 }
1051 
1053 {
1054  if ( !mGraduatedSymbol )
1055  return;
1056 
1057  QIcon icon = QgsSymbolLayerUtils::symbolPreviewIcon( mGraduatedSymbol.get(), btnChangeGraduatedSymbol->iconSize() );
1058  btnChangeGraduatedSymbol->setIcon( icon );
1059 }
1060 
1061 #if 0
1062 int QgsRendererPropertiesDialog::currentRangeRow()
1063 {
1064  QModelIndex idx = viewGraduated->selectionModel()->currentIndex();
1065  if ( !idx.isValid() )
1066  return -1;
1067  return idx.row();
1068 }
1069 #endif
1070 
1072 {
1073  QList<int> rows;
1074  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
1075 
1076  Q_FOREACH ( const QModelIndex &r, selectedRows )
1077  {
1078  if ( r.isValid() )
1079  {
1080  rows.append( r.row() );
1081  }
1082  }
1083  return rows;
1084 }
1085 
1087 {
1089  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
1090  QModelIndexList::const_iterator sIt = selectedRows.constBegin();
1091 
1092  for ( ; sIt != selectedRows.constEnd(); ++sIt )
1093  {
1094  selectedRanges.append( mModel->rendererRange( *sIt ) );
1095  }
1096  return selectedRanges;
1097 }
1098 
1100 {
1101  if ( idx.isValid() && idx.column() == 0 )
1102  changeRangeSymbol( idx.row() );
1103  if ( idx.isValid() && idx.column() == 1 )
1104  changeRange( idx.row() );
1105 }
1106 
1108 {
1109  if ( !idx.isValid() )
1110  mRowSelected = -1;
1111  else
1112  mRowSelected = idx.row();
1113 }
1114 
1116 {
1117 }
1118 
1120 {
1121  const QgsRendererRange &range = mRenderer->ranges()[rangeIdx];
1122  std::unique_ptr< QgsSymbol > newSymbol( range.symbol()->clone() );
1124  if ( panel && panel->dockMode() )
1125  {
1126  // bit tricky here - the widget doesn't take ownership of the symbol. So we need it to last for the duration of the
1127  // panel's existence. Accordingly, just kinda give it ownership here, and clean up in cleanUpSymbolSelector
1128  QgsSymbolSelectorWidget *dlg = new QgsSymbolSelectorWidget( newSymbol.release(), mStyle, mLayer, panel );
1129  dlg->setContext( mContext );
1130  dlg->setPanelTitle( range.label() );
1131  connect( dlg, &QgsPanelWidget::widgetChanged, this, &QgsGraduatedSymbolRendererWidget::updateSymbolsFromWidget );
1132  connect( dlg, &QgsPanelWidget::panelAccepted, this, &QgsGraduatedSymbolRendererWidget::cleanUpSymbolSelector );
1133  openPanel( dlg );
1134  }
1135  else
1136  {
1137  QgsSymbolSelectorDialog dlg( newSymbol.get(), mStyle, mLayer, panel );
1138  dlg.setContext( mContext );
1139  if ( !dlg.exec() || !newSymbol )
1140  {
1141  return;
1142  }
1143 
1144  mGraduatedSymbol = std::move( newSymbol );
1146  }
1147 }
1148 
1150 {
1151  QgsLUDialog dialog( this );
1152 
1153  const QgsRendererRange &range = mRenderer->ranges()[rangeIdx];
1154  // Add arbitrary 2 to number of decimal places to retain a bit extra.
1155  // Ensures users can see if legend is not completely honest!
1156  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
1157  if ( decimalPlaces < 0 ) decimalPlaces = 0;
1158  dialog.setLowerValue( QLocale().toString( range.lowerValue(), 'f', decimalPlaces ) );
1159  dialog.setUpperValue( QLocale().toString( range.upperValue(), 'f', decimalPlaces ) );
1160 
1161  if ( dialog.exec() == QDialog::Accepted )
1162  {
1163  bool ok = false;
1164  double lowerValue = qgsPermissiveToDouble( dialog.lowerValue(), ok );
1165  if ( ! ok )
1166  lowerValue = 0.0;
1167  double upperValue = qgsPermissiveToDouble( dialog.upperValue(), ok );
1168  if ( ! ok )
1169  upperValue = 0.0;
1170  mRenderer->updateRangeUpperValue( rangeIdx, upperValue );
1171  mRenderer->updateRangeLowerValue( rangeIdx, lowerValue );
1172 
1173  //If the boundaries have to stay linked, we update the ranges above and below, as well as their label if needed
1174  if ( cbxLinkBoundaries->isChecked() )
1175  {
1176  if ( rangeIdx > 0 )
1177  {
1178  mRenderer->updateRangeUpperValue( rangeIdx - 1, lowerValue );
1179  }
1180 
1181  if ( rangeIdx < mRenderer->ranges().size() - 1 )
1182  {
1183  mRenderer->updateRangeLowerValue( rangeIdx + 1, upperValue );
1184  }
1185  }
1186  }
1187  mHistogramWidget->refresh();
1188  emit widgetChanged();
1189 }
1190 
1192 {
1193  mModel->addClass( mGraduatedSymbol.get() );
1194  mHistogramWidget->refresh();
1195 }
1196 
1198 {
1199  QList<int> classIndexes = selectedClasses();
1200  mModel->deleteRows( classIndexes );
1201  mHistogramWidget->refresh();
1202 }
1203 
1205 {
1206  mModel->removeAllRows();
1207  mHistogramWidget->refresh();
1208 }
1209 
1211 {
1212  const QgsRangeList &ranges = mRenderer->ranges();
1213  bool ordered = true;
1214  for ( int i = 1; i < ranges.size(); ++i )
1215  {
1216  if ( ranges[i] < ranges[i - 1] )
1217  {
1218  ordered = false;
1219  break;
1220  }
1221  }
1222  return ordered;
1223 }
1224 
1226 {
1227  //If the checkbox controlling the link between boundaries was unchecked and we check it, we have to link the boundaries
1228  //This is done by updating all lower ranges to the upper value of the range above
1229  if ( linked )
1230  {
1231  if ( ! rowsOrdered() )
1232  {
1233  int result = QMessageBox::warning(
1234  this,
1235  tr( "Link Class Boundaries" ),
1236  tr( "Rows will be reordered before linking boundaries. Continue?" ),
1237  QMessageBox::Ok | QMessageBox::Cancel );
1238  if ( result != QMessageBox::Ok )
1239  {
1240  cbxLinkBoundaries->setChecked( false );
1241  return;
1242  }
1243  mRenderer->sortByValue();
1244  }
1245 
1246  // Ok to proceed
1247  for ( int i = 1; i < mRenderer->ranges().size(); ++i )
1248  {
1249  mRenderer->updateRangeLowerValue( i, mRenderer->ranges()[i - 1].upperValue() );
1250  }
1252  }
1253 }
1254 
1256 {
1257  if ( item->column() == 2 )
1258  {
1259  QString label = item->text();
1260  int idx = item->row();
1261  mRenderer->updateRangeLabel( idx, label );
1262  }
1263 }
1264 
1266 {
1268  txtLegendFormat->text(),
1269  spinPrecision->value(),
1270  cbxTrimTrailingZeroes->isChecked() );
1271  mRenderer->setLabelFormat( labelFormat, true );
1272  mModel->updateLabels();
1273 }
1274 
1275 
1277 {
1278  QList<QgsSymbol *> selectedSymbols;
1279 
1280  QItemSelectionModel *m = viewGraduated->selectionModel();
1281  QModelIndexList selectedIndexes = m->selectedRows( 1 );
1282  if ( m && !selectedIndexes.isEmpty() )
1283  {
1284  const QgsRangeList &ranges = mRenderer->ranges();
1285  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1286  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1287  {
1288  QStringList list = m->model()->data( *indexIt ).toString().split( ' ' );
1289  if ( list.size() < 3 )
1290  {
1291  continue;
1292  }
1293  // Not strictly necessary because the range should have been sanitized already
1294  // after user input, but being permissive never hurts
1295  bool ok = false;
1296  double lowerBound = qgsPermissiveToDouble( list.at( 0 ), ok );
1297  if ( ! ok )
1298  lowerBound = 0.0;
1299  double upperBound = qgsPermissiveToDouble( list.at( 2 ), ok );
1300  if ( ! ok )
1301  upperBound = 0.0;
1302  QgsSymbol *s = findSymbolForRange( lowerBound, upperBound, ranges );
1303  if ( s )
1304  {
1305  selectedSymbols.append( s );
1306  }
1307  }
1308  }
1309  return selectedSymbols;
1310 }
1311 
1312 QgsSymbol *QgsGraduatedSymbolRendererWidget::findSymbolForRange( double lowerBound, double upperBound, const QgsRangeList &ranges ) const
1313 {
1314  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
1315  if ( decimalPlaces < 0 )
1316  decimalPlaces = 0;
1317  double precision = 1.0 / std::pow( 10, decimalPlaces );
1318 
1319  for ( QgsRangeList::const_iterator it = ranges.begin(); it != ranges.end(); ++it )
1320  {
1321  if ( qgsDoubleNear( lowerBound, it->lowerValue(), precision ) && qgsDoubleNear( upperBound, it->upperValue(), precision ) )
1322  {
1323  return it->symbol();
1324  }
1325  }
1326  return nullptr;
1327 }
1328 
1330 {
1331  if ( mModel )
1332  {
1333  mModel->updateSymbology();
1334  }
1335  mHistogramWidget->refresh();
1336  emit widgetChanged();
1337 }
1338 
1340 {
1341  showSymbolLevelsDialog( mRenderer.get() );
1342 }
1343 
1345 {
1346  viewGraduated->selectionModel()->clear();
1347  if ( ! rowsOrdered() )
1348  {
1349  cbxLinkBoundaries->setChecked( false );
1350  }
1351  emit widgetChanged();
1352 }
1353 
1355 {
1356  emit widgetChanged();
1357 }
1358 
1360 {
1361  if ( !event )
1362  {
1363  return;
1364  }
1365 
1366  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1367  {
1368  mCopyBuffer.clear();
1369  mCopyBuffer = selectedRanges();
1370  }
1371  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1372  {
1373  QgsRangeList::const_iterator rIt = mCopyBuffer.constBegin();
1374  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1375  {
1376  mModel->addClass( *rIt );
1377  }
1378  emit widgetChanged();
1379  }
1380 }
1381 
1382 void QgsGraduatedSymbolRendererWidget::dataDefinedSizeLegend()
1383 {
1384  QgsMarkerSymbol *s = static_cast<QgsMarkerSymbol *>( mGraduatedSymbol.get() ); // this should be only enabled for marker symbols
1385  QgsDataDefinedSizeLegendWidget *panel = createDataDefinedSizeLegendWidget( s, mRenderer->dataDefinedSizeLegend() );
1386  if ( panel )
1387  {
1388  connect( panel, &QgsPanelWidget::widgetChanged, this, [ = ]
1389  {
1390  mRenderer->setDataDefinedSizeLegend( panel->dataDefinedSizeLegend() );
1391  emit widgetChanged();
1392  } );
1393  openPanel( panel ); // takes ownership of the panel
1394  }
1395 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value...
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
const QgsRendererRangeLabelFormat & labelFormat() const
Returns the label format used to generate default classification labels.
static QgsGraduatedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsGraduatedSymbolRenderer from an existing renderer.
The QgsFieldExpressionWidget class reates a widget to choose fields and edit expressions It contains ...
int precision
bool dockMode()
Returns the dock mode state.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
QList< QgsRendererRange > QgsRangeList
QgsSymbol * symbol()
Returns the symbol that is currently active in the widget.
void showSymbolLevelsDialog(QgsFeatureRenderer *r)
show a dialog with renderer&#39;s symbol level settings
void colorRampChanged()
Emitted whenever a new color ramp is set for the button.
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly...
Base class for renderer settings widgets.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:265
double qgsPermissiveToDouble(QString string, bool &ok)
Converts a string to a double in a permissive way, e.g., allowing for incorrect numbers of digits bet...
Definition: qgis.cpp:97
Abstract base class for color ramps.
Definition: qgscolorramp.h:31
QgsVectorLayer * mLayer
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0)
Returns an icon preview for a color ramp.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Base class for any widget that can be shown as a inline panel.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
Line symbol.
Definition: qgssymbol.h:86
A QProxyStyle subclass which correctly sets the base style to match the QGIS application style...
Definition: qgsproxystyle.h:30
void setLowerValue(const QString &val)
Definition: qgsludialog.cpp:37
void rangesModified(bool rangesAdded)
Emitted when the user modifies the graduated ranges using the histogram widget.
void panelAccepted(QgsPanelWidget *panel)
Emitted when the panel is accepted by the user.
QList< QgsUnitTypes::RenderUnit > RenderUnitList
List of render units.
Definition: qgsunittypes.h:184
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:732
static QgsRendererWidget * create(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
The QgsMapSettings class contains configuration for rendering of the map.
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:277
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Date or datetime fields.
QgsGraduatedSymbolRendererWidget(QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer)
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget...
Widget for configuration of appearance of legend for marker symbols with data-defined size...
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void applyChangeToSymbol()
Applies current symbol to selected ranges, or to all ranges if none is selected.
void setSourceFieldExp(const QString &fieldOrExp)
Sets the source field or expression to use for values in the histogram.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
points (e.g., for font sizes)
Definition: qgsunittypes.h:118
QList< int > selectedClasses()
Returns a list of indexes for the classes under selection.
QgsDataDefinedSizeLegendWidget * createDataDefinedSizeLegendWidget(const QgsMarkerSymbol *symbol, const QgsDataDefinedSizeLegend *ddsLegend)
Creates widget to setup data-defined size legend.
Symbol selector widget that can be used to select and build a symbol.
QgsSymbol * symbol() const
QgsFeatureRenderer * renderer() override
Returns pointer to the renderer (no transfer of ownership)
QVariant minimumValue(int index) const FINAL
Returns the minimum value for an attribute column or an invalid variant in case of error...
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addClass()
Adds a class manually to the classification.
void widgetChanged()
Emitted when the widget state changes.
void moveClass(int from, int to)
Moves the category at index position from to index position to.
void fieldChanged(const QString &fieldName)
the signal is emitted when the currently selected field changes
Marker symbol.
Definition: qgssymbol.h:85
QList< QgsSymbol * > selectedSymbols() override
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
static QgsExpressionContextScope * atlasScope(QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
QgsSymbolWidgetContext mContext
Context in which widget is shown.
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration as set up in the dialog (may be null). Ownership is passed to the caller...
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:430
void toggleBoundariesLink(bool linked)
Toggle the link between classes boundaries.
void contextMenuViewCategories(QPoint p)
void deleteAllClasses()
Removes all classes from the classification.
void setUpperValue(const QString &val)
Definition: qgsludialog.cpp:42
bool updateRangeRenderState(int rangeIndex, bool render)
QString lowerValue() const
Definition: qgsludialog.cpp:27
bool updateRangeLabel(int rangeIndex, const QString &label)
QString upperValue() const
Definition: qgsludialog.cpp:32
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:139
QVariant maximumValue(int index) const FINAL
Returns the maximum value for an attribute column or an invalid variant in case of error...
Represents a vector layer which manages a vector based data sets.
void setPanelTitle(const QString &panelTitle)
Set the title of the panel when shown in the interface.
const QgsRangeList & ranges() const
QgsSymbol * findSymbolForRange(double lowerBound, double upperBound, const QgsRangeList &ranges) const
void deleteClasses()
Removes currently selected classes.
QgsVectorLayer * layer()
Returns the layer currently associated with the widget.
void setColor(const QColor &color)
Sets the color for the symbol.
Definition: qgssymbol.cpp:452