QGIS API Documentation  2.14.0-Essen
qgsgraduatedsymbolrendererv2widget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgraduatedsymbolrendererv2widget.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 
17 #include "qgssymbolv2.h"
18 #include "qgssymbollayerv2utils.h"
19 #include "qgsvectorcolorrampv2.h"
20 #include "qgsstylev2.h"
21 
22 #include "qgsvectorlayer.h"
23 
26 
27 #include "qgsludialog.h"
28 
29 #include "qgsproject.h"
30 #include "qgsmapcanvas.h"
31 
32 #include <QKeyEvent>
33 #include <QMenu>
34 #include <QMessageBox>
35 #include <QStandardItemModel>
36 #include <QStandardItem>
37 #include <QPen>
38 #include <QPainter>
39 
40 // ------------------------------ Model ------------------------------------
41 
43 
44 QgsGraduatedSymbolRendererV2Model::QgsGraduatedSymbolRendererV2Model( QObject * parent ) : QAbstractItemModel( parent )
45  , mRenderer( nullptr )
46  , mMimeFormat( "application/x-qgsgraduatedsymbolrendererv2model" )
47 {
48 }
49 
50 void QgsGraduatedSymbolRendererV2Model::setRenderer( QgsGraduatedSymbolRendererV2* renderer )
51 {
52  if ( mRenderer )
53  {
54  beginRemoveRows( QModelIndex(), 0, mRenderer->ranges().size() - 1 );
55  mRenderer = nullptr;
56  endRemoveRows();
57  }
58  if ( renderer )
59  {
60  beginInsertRows( QModelIndex(), 0, renderer->ranges().size() - 1 );
61  mRenderer = renderer;
62  endInsertRows();
63  }
64 }
65 
66 void QgsGraduatedSymbolRendererV2Model::addClass( QgsSymbolV2* symbol )
67 {
68  if ( !mRenderer ) return;
69  int idx = mRenderer->ranges().size();
70  beginInsertRows( QModelIndex(), idx, idx );
71  mRenderer->addClass( symbol );
72  endInsertRows();
73 }
74 
75 void QgsGraduatedSymbolRendererV2Model::addClass( const QgsRendererRangeV2& range )
76 {
77  if ( !mRenderer )
78  {
79  return;
80  }
81  int idx = mRenderer->ranges().size();
82  beginInsertRows( QModelIndex(), idx, idx );
83  mRenderer->addClass( range );
84  endInsertRows();
85 }
86 
87 QgsRendererRangeV2 QgsGraduatedSymbolRendererV2Model::rendererRange( const QModelIndex &index )
88 {
89  if ( !index.isValid() || !mRenderer || mRenderer->ranges().size() <= index.row() )
90  {
91  return QgsRendererRangeV2();
92  }
93 
94  return mRenderer->ranges().value( index.row() );
95 }
96 
97 Qt::ItemFlags QgsGraduatedSymbolRendererV2Model::flags( const QModelIndex & index ) const
98 {
99  if ( !index.isValid() )
100  {
101  return Qt::ItemIsDropEnabled;
102  }
103 
104  Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
105 
106  if ( index.column() == 2 )
107  {
108  flags |= Qt::ItemIsEditable;
109  }
110 
111  return flags;
112 }
113 
114 Qt::DropActions QgsGraduatedSymbolRendererV2Model::supportedDropActions() const
115 {
116  return Qt::MoveAction;
117 }
118 
119 QVariant QgsGraduatedSymbolRendererV2Model::data( const QModelIndex &index, int role ) const
120 {
121  if ( !index.isValid() || !mRenderer ) return QVariant();
122 
123  const QgsRendererRangeV2 range = mRenderer->ranges().value( index.row() );
124 
125  if ( role == Qt::CheckStateRole && index.column() == 0 )
126  {
127  return range.renderState() ? Qt::Checked : Qt::Unchecked;
128  }
129  else if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
130  {
131  switch ( index.column() )
132  {
133  case 1:
134  {
135  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
136  if ( decimalPlaces < 0 ) decimalPlaces = 0;
137  return QString::number( range.lowerValue(), 'f', decimalPlaces ) + " - " + QString::number( range.upperValue(), 'f', decimalPlaces );
138  }
139  case 2:
140  return range.label();
141  default:
142  return QVariant();
143  }
144  }
145  else if ( role == Qt::DecorationRole && index.column() == 0 && range.symbol() )
146  {
147  return QgsSymbolLayerV2Utils::symbolPreviewIcon( range.symbol(), QSize( 16, 16 ) );
148  }
149  else if ( role == Qt::TextAlignmentRole )
150  {
151  return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
152  }
153  else if ( role == Qt::EditRole )
154  {
155  switch ( index.column() )
156  {
157  // case 1: return rangeStr;
158  case 2:
159  return range.label();
160  default:
161  return QVariant();
162  }
163  }
164 
165  return QVariant();
166 }
167 
168 bool QgsGraduatedSymbolRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role )
169 {
170  if ( !index.isValid() )
171  return false;
172 
173  if ( index.column() == 0 && role == Qt::CheckStateRole )
174  {
175  mRenderer->updateRangeRenderState( index.row(), value == Qt::Checked );
176  emit dataChanged( index, index );
177  return true;
178  }
179 
180  if ( role != Qt::EditRole )
181  return false;
182 
183  switch ( index.column() )
184  {
185  case 1: // range
186  return false; // range is edited in popup dialog
187  case 2: // label
188  mRenderer->updateRangeLabel( index.row(), value.toString() );
189  break;
190  default:
191  return false;
192  }
193 
194  emit dataChanged( index, index );
195  return true;
196 }
197 
198 QVariant QgsGraduatedSymbolRendererV2Model::headerData( int section, Qt::Orientation orientation, int role ) const
199 {
200  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
201  {
202  QStringList lst;
203  lst << tr( "Symbol" ) << tr( "Values" ) << tr( "Legend" );
204  return lst.value( section );
205  }
206  return QVariant();
207 }
208 
209 int QgsGraduatedSymbolRendererV2Model::rowCount( const QModelIndex &parent ) const
210 {
211  if ( parent.isValid() || !mRenderer )
212  {
213  return 0;
214  }
215  return mRenderer->ranges().size();
216 }
217 
218 int QgsGraduatedSymbolRendererV2Model::columnCount( const QModelIndex & index ) const
219 {
220  Q_UNUSED( index );
221  return 3;
222 }
223 
224 QModelIndex QgsGraduatedSymbolRendererV2Model::index( int row, int column, const QModelIndex &parent ) const
225 {
226  if ( hasIndex( row, column, parent ) )
227  {
228  return createIndex( row, column );
229  }
230  return QModelIndex();
231 }
232 
233 QModelIndex QgsGraduatedSymbolRendererV2Model::parent( const QModelIndex &index ) const
234 {
235  Q_UNUSED( index );
236  return QModelIndex();
237 }
238 
239 QStringList QgsGraduatedSymbolRendererV2Model::mimeTypes() const
240 {
241  QStringList types;
242  types << mMimeFormat;
243  return types;
244 }
245 
246 QMimeData *QgsGraduatedSymbolRendererV2Model::mimeData( const QModelIndexList &indexes ) const
247 {
248  QMimeData *mimeData = new QMimeData();
249  QByteArray encodedData;
250 
251  QDataStream stream( &encodedData, QIODevice::WriteOnly );
252 
253  // Create list of rows
254  Q_FOREACH ( const QModelIndex &index, indexes )
255  {
256  if ( !index.isValid() || index.column() != 0 )
257  continue;
258 
259  stream << index.row();
260  }
261  mimeData->setData( mMimeFormat, encodedData );
262  return mimeData;
263 }
264 
265 bool QgsGraduatedSymbolRendererV2Model::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
266 {
267  Q_UNUSED( row );
268  Q_UNUSED( column );
269  if ( action != Qt::MoveAction ) return true;
270 
271  if ( !data->hasFormat( mMimeFormat ) ) return false;
272 
273  QByteArray encodedData = data->data( mMimeFormat );
274  QDataStream stream( &encodedData, QIODevice::ReadOnly );
275 
276  QVector<int> rows;
277  while ( !stream.atEnd() )
278  {
279  int r;
280  stream >> r;
281  rows.append( r );
282  }
283 
284  int to = parent.row();
285  // to is -1 if dragged outside items, i.e. below any item,
286  // then move to the last position
287  if ( to == -1 ) to = mRenderer->ranges().size(); // out of rang ok, will be decreased
288  for ( int i = rows.size() - 1; i >= 0; i-- )
289  {
290  QgsDebugMsg( QString( "move %1 to %2" ).arg( rows[i] ).arg( to ) );
291  int t = to;
292  // moveCategory first removes and then inserts
293  if ( rows[i] < t ) t--;
294  mRenderer->moveClass( rows[i], t );
295  // current moved under another, shift its index up
296  for ( int j = 0; j < i; j++ )
297  {
298  if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
299  }
300  // removed under 'to' so the target shifted down
301  if ( rows[i] < to ) to--;
302  }
303  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
304  emit rowsMoved();
305  return false;
306 }
307 
308 void QgsGraduatedSymbolRendererV2Model::deleteRows( QList<int> rows )
309 {
310  for ( int i = rows.size() - 1; i >= 0; i-- )
311  {
312  beginRemoveRows( QModelIndex(), rows[i], rows[i] );
313  mRenderer->deleteClass( rows[i] );
314  endRemoveRows();
315  }
316 }
317 
318 void QgsGraduatedSymbolRendererV2Model::removeAllRows()
319 {
320  beginRemoveRows( QModelIndex(), 0, mRenderer->ranges().size() - 1 );
321  mRenderer->deleteAllClasses();
322  endRemoveRows();
323 }
324 
325 void QgsGraduatedSymbolRendererV2Model::sort( int column, Qt::SortOrder order )
326 {
327  QgsDebugMsg( "Entered" );
328  if ( column == 0 )
329  {
330  return;
331  }
332  if ( column == 1 )
333  {
334  mRenderer->sortByValue( order );
335  }
336  else if ( column == 2 )
337  {
338  mRenderer->sortByLabel( order );
339  }
340  emit rowsMoved();
341  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
342  QgsDebugMsg( "Done" );
343 }
344 
345 void QgsGraduatedSymbolRendererV2Model::updateSymbology( bool resetModel )
346 {
347  if ( resetModel )
348  {
349  reset();
350  }
351  else
352  {
353  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
354  }
355 }
356 
357 void QgsGraduatedSymbolRendererV2Model::updateLabels()
358 {
359  emit dataChanged( createIndex( 0, 2 ), createIndex( mRenderer->ranges().size(), 2 ) );
360 }
361 
362 // ------------------------------ View style --------------------------------
363 QgsGraduatedSymbolRendererV2ViewStyle::QgsGraduatedSymbolRendererV2ViewStyle( QStyle* style )
364  : QProxyStyle( style )
365 {}
366 
367 void QgsGraduatedSymbolRendererV2ViewStyle::drawPrimitive( PrimitiveElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget ) const
368 {
369  if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
370  {
371  QStyleOption opt( *option );
372  opt.rect.setLeft( 0 );
373  // draw always as line above, because we move item to that index
374  opt.rect.setHeight( 0 );
375  if ( widget ) opt.rect.setRight( widget->width() );
376  QProxyStyle::drawPrimitive( element, &opt, painter, widget );
377  return;
378  }
379  QProxyStyle::drawPrimitive( element, option, painter, widget );
380 }
381 
383 
384 // ------------------------------ Widget ------------------------------------
385 
387 {
388  return new QgsGraduatedSymbolRendererV2Widget( layer, style, renderer );
389 }
390 
391 static QgsExpressionContext _getExpressionContext( const void* context )
392 {
394 
395  QgsExpressionContext expContext;
399 
400  if ( widget->mapCanvas() )
401  {
404  }
405  else
406  {
408  }
409 
410  if ( widget->vectorLayer() )
411  expContext << QgsExpressionContextUtils::layerScope( widget->vectorLayer() );
412 
413  return expContext;
414 }
415 
417  : QgsRendererV2Widget( layer, style )
418  , mRenderer( nullptr )
419  , mModel( nullptr )
420 {
421 
422 
423  // try to recognize the previous renderer
424  // (null renderer means "no previous renderer")
425  if ( renderer )
426  {
428  }
429  if ( !mRenderer )
430  {
432  }
433 
434  // setup user interface
435  setupUi( this );
436  mModel = new QgsGraduatedSymbolRendererV2Model( this );
437 
438  mExpressionWidget->setFilters( QgsFieldProxyModel::Numeric | QgsFieldProxyModel::Date );
439  mExpressionWidget->setLayer( mLayer );
440 
442 
443  cboGraduatedColorRamp->populate( mStyle );
444 
445  spinPrecision->setMinimum( QgsRendererRangeV2LabelFormat::MinPrecision );
446  spinPrecision->setMaximum( QgsRendererRangeV2LabelFormat::MaxPrecision );
447 
448  // set project default color ramp
449  QString defaultColorRamp = QgsProject::instance()->readEntry( "DefaultStyles", "/ColorRamp", "" );
450  if ( defaultColorRamp != "" )
451  {
452  int index = cboGraduatedColorRamp->findText( defaultColorRamp, Qt::MatchCaseSensitive );
453  if ( index >= 0 )
454  cboGraduatedColorRamp->setCurrentIndex( index );
455  }
456 
457 
458  viewGraduated->setStyle( new QgsGraduatedSymbolRendererV2ViewStyle( viewGraduated->style() ) );
459 
461 
462  methodComboBox->blockSignals( true );
463  methodComboBox->addItem( "Color" );
465  {
466  methodComboBox->addItem( "Size" );
467  minSizeSpinBox->setValue( 1 );
468  maxSizeSpinBox->setValue( 8 );
469  }
470  else if ( mGraduatedSymbol->type() == QgsSymbolV2::Line )
471  {
472  methodComboBox->addItem( "Size" );
473  minSizeSpinBox->setValue( .1 );
474  maxSizeSpinBox->setValue( 2 );
475  }
476  methodComboBox->blockSignals( false );
477 
478  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( graduatedColumnChanged( QString ) ) );
479  connect( viewGraduated, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( rangesDoubleClicked( const QModelIndex & ) ) );
480  connect( viewGraduated, SIGNAL( clicked( const QModelIndex & ) ), this, SLOT( rangesClicked( const QModelIndex & ) ) );
481  connect( viewGraduated, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
482 
483  connect( btnGraduatedClassify, SIGNAL( clicked() ), this, SLOT( classifyGraduated() ) );
484  connect( btnChangeGraduatedSymbol, SIGNAL( clicked() ), this, SLOT( changeGraduatedSymbol() ) );
485  connect( btnGraduatedDelete, SIGNAL( clicked() ), this, SLOT( deleteClasses() ) );
486  connect( btnDeleteAllClasses, SIGNAL( clicked() ), this, SLOT( deleteAllClasses() ) );
487  connect( btnGraduatedAdd, SIGNAL( clicked() ), this, SLOT( addClass() ) );
488  connect( cbxLinkBoundaries, SIGNAL( toggled( bool ) ), this, SLOT( toggleBoundariesLink( bool ) ) );
489 
490  connect( mSizeUnitWidget, SIGNAL( changed() ), this, SLOT( on_mSizeUnitWidget_changed() ) );
491 
493 
494  // initialize from previously set renderer
496 
497  // menus for data-defined rotation/size
498  QMenu* advMenu = new QMenu;
499 
500  advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );
501 
502  btnAdvanced->setMenu( advMenu );
503 
504  mHistogramWidget->setLayer( mLayer );
505  mHistogramWidget->setRenderer( mRenderer );
506  connect( mHistogramWidget, SIGNAL( rangesModified( bool ) ), this, SLOT( refreshRanges( bool ) ) );
507  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), mHistogramWidget, SLOT( setSourceFieldExp( QString ) ) );
508 
509  mExpressionWidget->registerGetExpressionContextCallback( &_getExpressionContext, this );
510 }
511 
513 {
514  if ( !mGraduatedSymbol ) return;
515  mGraduatedSymbol->setOutputUnit( mSizeUnitWidget->unit() );
516  mGraduatedSymbol->setMapUnitScale( mSizeUnitWidget->getMapUnitScale() );
520 }
521 
523 {
524  delete mRenderer;
525  delete mModel;
526  delete mGraduatedSymbol;
527 }
528 
530 {
531  return mRenderer;
532 }
533 
534 // Connect/disconnect event handlers which trigger updating renderer
535 
537 {
538  connect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ), this, SLOT( classifyGraduated() ) );
539  connect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classifyGraduated() ) );
540  connect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( reapplyColorRamp() ) );
541  connect( cboGraduatedColorRamp, SIGNAL( sourceRampEdited() ), this, SLOT( reapplyColorRamp() ) );
542  connect( mButtonEditRamp, SIGNAL( clicked() ), cboGraduatedColorRamp, SLOT( editSourceRamp() ) );
543  connect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( reapplyColorRamp() ) );
544  connect( spinPrecision, SIGNAL( valueChanged( int ) ), this, SLOT( labelFormatChanged() ) );
545  connect( cbxTrimTrailingZeroes, SIGNAL( toggled( bool ) ), this, SLOT( labelFormatChanged() ) );
546  connect( txtLegendFormat, SIGNAL( textChanged( QString ) ), this, SLOT( labelFormatChanged() ) );
547  connect( minSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
548  connect( maxSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
549 
550  connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
551  connect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( modelDataChanged() ) );
552 }
553 
554 // Connect/disconnect event handlers which trigger updating renderer
555 
557 {
558  disconnect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ), this, SLOT( classifyGraduated() ) );
559  disconnect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classifyGraduated() ) );
560  disconnect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( reapplyColorRamp() ) );
561  disconnect( cboGraduatedColorRamp, SIGNAL( sourceRampEdited() ), this, SLOT( reapplyColorRamp() ) );
562  disconnect( mButtonEditRamp, SIGNAL( clicked() ), cboGraduatedColorRamp, SLOT( editSourceRamp() ) );
563  disconnect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( reapplyColorRamp() ) );
564  disconnect( spinPrecision, SIGNAL( valueChanged( int ) ), this, SLOT( labelFormatChanged() ) );
565  disconnect( cbxTrimTrailingZeroes, SIGNAL( toggled( bool ) ), this, SLOT( labelFormatChanged() ) );
566  disconnect( txtLegendFormat, SIGNAL( textChanged( QString ) ), this, SLOT( labelFormatChanged() ) );
567  disconnect( minSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
568  disconnect( maxSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
569 
570  disconnect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
571  disconnect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( modelDataChanged() ) );
572 }
573 
575 {
577 
579 
580  // update UI from the graduated renderer (update combo boxes, view)
581  if ( mRenderer->mode() < cboGraduatedMode->count() )
582  cboGraduatedMode->setCurrentIndex( mRenderer->mode() );
583 
584  // Only update class count if different - otherwise typing value gets very messy
585  int nclasses = mRenderer->ranges().count();
586  if ( nclasses && updateCount )
587  spinGraduatedClasses->setValue( mRenderer->ranges().count() );
588 
589  // set column
590  QString attrName = mRenderer->classAttribute();
591  mExpressionWidget->setField( attrName );
592  mHistogramWidget->setSourceFieldExp( attrName );
593 
594  // set source symbol
595  if ( mRenderer->sourceSymbol() )
596  {
597  delete mGraduatedSymbol;
600  }
601 
602  mModel->setRenderer( mRenderer );
603  viewGraduated->setModel( mModel );
604 
605  if ( mGraduatedSymbol )
606  {
607  mSizeUnitWidget->blockSignals( true );
608  mSizeUnitWidget->setUnit( mGraduatedSymbol->outputUnit() );
609  mSizeUnitWidget->setMapUnitScale( mGraduatedSymbol->mapUnitScale() );
610  mSizeUnitWidget->blockSignals( false );
611  }
612 
613  // set source color ramp
614  methodComboBox->blockSignals( true );
616  {
617  methodComboBox->setCurrentIndex( 0 );
618  if ( mRenderer->sourceColorRamp() )
619  cboGraduatedColorRamp->setSourceColorRamp( mRenderer->sourceColorRamp() );
620  cbxInvertedColorRamp->setChecked( mRenderer->invertedColorRamp() );
621  }
622  else
623  {
624  methodComboBox->setCurrentIndex( 1 );
625  if ( !mRenderer->ranges().isEmpty() ) // avoid overiding default size with zeros
626  {
627  minSizeSpinBox->setValue( mRenderer->minSymbolSize() );
628  maxSizeSpinBox->setValue( mRenderer->maxSymbolSize() );
629  }
630  }
631  mMethodStackedWidget->setCurrentIndex( methodComboBox->currentIndex() );
632  methodComboBox->blockSignals( false );
633 
635  txtLegendFormat->setText( labelFormat.format() );
636  spinPrecision->setValue( labelFormat.precision() );
637  cbxTrimTrailingZeroes->setChecked( labelFormat.trimTrailingZeroes() );
638 
639  viewGraduated->resizeColumnToContents( 0 );
640  viewGraduated->resizeColumnToContents( 1 );
641  viewGraduated->resizeColumnToContents( 2 );
642 
643  mHistogramWidget->refresh();
644 
646 }
647 
649 {
650  mRenderer->setClassAttribute( field );
651 }
652 
654 {
655  mMethodStackedWidget->setCurrentIndex( idx );
656  if ( idx == 0 )
657  {
659  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
660 
661  if ( !ramp )
662  {
663  if ( cboGraduatedColorRamp->count() == 0 )
664  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
665  else
666  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
667  return;
668  }
669  mRenderer->setSourceColorRamp( ramp );
671  }
672  else
673  {
675  reapplySizes();
676  }
677 }
678 
680 {
681  if ( !mModel )
682  return;
683 
684  mModel->updateSymbology( reset );
685 }
686 
688 {
689  QString attrName = mExpressionWidget->currentField();
690 
691  int nclasses = spinGraduatedClasses->value();
692 
693  QSharedPointer<QgsVectorColorRampV2> ramp( cboGraduatedColorRamp->currentColorRamp() );
694  if ( !ramp )
695  {
696  if ( cboGraduatedColorRamp->count() == 0 )
697  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
698  else
699  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
700  return;
701  }
702 
704  if ( cboGraduatedMode->currentIndex() == 0 )
706  else if ( cboGraduatedMode->currentIndex() == 2 )
708  else if ( cboGraduatedMode->currentIndex() == 3 )
710  else if ( cboGraduatedMode->currentIndex() == 4 )
712  else // default should be quantile for now
714 
715  // Jenks is n^2 complexity, warn for big dataset (more than 50k records)
716  // and give the user the chance to cancel
717  if ( QgsGraduatedSymbolRendererV2::Jenks == mode && mLayer->featureCount() > 50000 )
718  {
719  if ( QMessageBox::Cancel == QMessageBox::question( this, tr( "Warning" ), 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 ) )
720  return;
721  }
722 
723  // create and set new renderer
724 
725  mRenderer->setClassAttribute( attrName );
726  mRenderer->setMode( mode );
727 
728  if ( methodComboBox->currentIndex() == 0 )
729  {
730  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
731 
732  if ( !ramp )
733  {
734  if ( cboGraduatedColorRamp->count() == 0 )
735  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
736  else
737  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
738  return;
739  }
740  mRenderer->setSourceColorRamp( ramp );
741  }
742  else
743  {
744  mRenderer->setSourceColorRamp( nullptr );
745  }
746 
747  QApplication::setOverrideCursor( Qt::WaitCursor );
748  mRenderer->updateClasses( mLayer, mode, nclasses );
749 
750  if ( methodComboBox->currentIndex() == 1 )
751  mRenderer->setSymbolSizes( minSizeSpinBox->value(), maxSizeSpinBox->value() );
752 
755  // PrettyBreaks and StdDev calculation don't generate exact
756  // number of classes - leave user interface unchanged for these
757  updateUiFromRenderer( false );
758 }
759 
761 {
762  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
763  if ( !ramp )
764  return;
765 
766  mRenderer->updateColorRamp( ramp, cbxInvertedColorRamp->isChecked() );
769 }
770 
772 {
773  mRenderer->setSymbolSizes( minSizeSpinBox->value(), maxSizeSpinBox->value() );
776 }
777 
779 {
780  // Change the selected symbols alone if anything is selected
781  QItemSelectionModel* m = viewGraduated->selectionModel();
782  QModelIndexList i = m->selectedRows();
783  if ( m && !i.isEmpty() )
784  {
786  return;
787  }
788 
789  // Otherwise change the base mGraduatedSymbol
790  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
791 
792  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
793  dlg.setMapCanvas( mMapCanvas );
794  if ( !dlg.exec() )
795  {
796  delete newSymbol;
797  return;
798  }
799 
800  delete mGraduatedSymbol;
801  mGraduatedSymbol = newSymbol;
802 
803  mSizeUnitWidget->blockSignals( true );
804  mSizeUnitWidget->setUnit( mGraduatedSymbol->outputUnit() );
805  mSizeUnitWidget->setMapUnitScale( mGraduatedSymbol->mapUnitScale() );
806  mSizeUnitWidget->blockSignals( false );
807 
811 }
812 
814 {
815  if ( !mGraduatedSymbol )
816  return;
817 
818  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mGraduatedSymbol, btnChangeGraduatedSymbol->iconSize() );
819  btnChangeGraduatedSymbol->setIcon( icon );
820 }
821 
822 #if 0
823 int QgsRendererV2PropertiesDialog::currentRangeRow()
824 {
825  QModelIndex idx = viewGraduated->selectionModel()->currentIndex();
826  if ( !idx.isValid() )
827  return -1;
828  return idx.row();
829 }
830 #endif
831 
833 {
834  QList<int> rows;
835  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
836 
837  Q_FOREACH ( const QModelIndex& r, selectedRows )
838  {
839  if ( r.isValid() )
840  {
841  rows.append( r.row() );
842  }
843  }
844  return rows;
845 }
846 
848 {
850  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
851  QModelIndexList::const_iterator sIt = selectedRows.constBegin();
852 
853  for ( ; sIt != selectedRows.constEnd(); ++sIt )
854  {
855  selectedRanges.append( mModel->rendererRange( *sIt ) );
856  }
857  return selectedRanges;
858 }
859 
861 {
862  if ( idx.isValid() && idx.column() == 0 )
863  changeRangeSymbol( idx.row() );
864  if ( idx.isValid() && idx.column() == 1 )
865  changeRange( idx.row() );
866 }
867 
869 {
870  if ( !idx.isValid() )
871  mRowSelected = -1;
872  else
873  mRowSelected = idx.row();
874 }
875 
877 {
878  QItemSelectionModel* m = viewGraduated->selectionModel();
879  QModelIndexList selectedIndexes = m->selectedRows( 1 );
880  if ( m && !selectedIndexes.isEmpty() )
881  {
882  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
883  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
884  dlg.setMapCanvas( mMapCanvas );
885  if ( !dlg.exec() )
886  {
887  delete newSymbol;
888  return;
889  }
890 
891  Q_FOREACH ( const QModelIndex& idx, selectedIndexes )
892  {
893  if ( idx.isValid() )
894  {
895  int rangeIdx = idx.row();
896  QgsSymbolV2* newRangeSymbol = newSymbol->clone();
897  newRangeSymbol->setColor( mRenderer->ranges()[rangeIdx].symbol()->color() );
898  mRenderer->updateRangeSymbol( rangeIdx, newRangeSymbol );
899  }
900  }
901  }
903 }
904 
906 {
907  QgsSymbolV2* newSymbol = mRenderer->ranges()[rangeIdx].symbol()->clone();
908 
909  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
910  dlg.setMapCanvas( mMapCanvas );
911  if ( !dlg.exec() )
912  {
913  delete newSymbol;
914  return;
915  }
916 
917  mRenderer->updateRangeSymbol( rangeIdx, newSymbol );
918  mHistogramWidget->refresh();
919 }
920 
922 {
923  QgsLUDialog dialog( this );
924 
925  const QgsRendererRangeV2& range = mRenderer->ranges()[rangeIdx];
926  // Add arbitrary 2 to number of decimal places to retain a bit extra.
927  // Ensures users can see if legend is not completely honest!
928  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
929  if ( decimalPlaces < 0 ) decimalPlaces = 0;
930  dialog.setLowerValue( QString::number( range.lowerValue(), 'f', decimalPlaces ) );
931  dialog.setUpperValue( QString::number( range.upperValue(), 'f', decimalPlaces ) );
932 
933  if ( dialog.exec() == QDialog::Accepted )
934  {
935  double lowerValue = dialog.lowerValue().toDouble();
936  double upperValue = dialog.upperValue().toDouble();
937  mRenderer->updateRangeUpperValue( rangeIdx, upperValue );
938  mRenderer->updateRangeLowerValue( rangeIdx, lowerValue );
939 
940  //If the boundaries have to stay linked, we update the ranges above and below, as well as their label if needed
941  if ( cbxLinkBoundaries->isChecked() )
942  {
943  if ( rangeIdx > 0 )
944  {
945  mRenderer->updateRangeUpperValue( rangeIdx - 1, lowerValue );
946  }
947 
948  if ( rangeIdx < mRenderer->ranges().size() - 1 )
949  {
950  mRenderer->updateRangeLowerValue( rangeIdx + 1, upperValue );
951  }
952  }
953  }
954  mHistogramWidget->refresh();
955 }
956 
958 {
959  mModel->addClass( mGraduatedSymbol );
960  mHistogramWidget->refresh();
961 }
962 
964 {
965  QList<int> classIndexes = selectedClasses();
966  mModel->deleteRows( classIndexes );
967  mHistogramWidget->refresh();
968 }
969 
971 {
972  mModel->removeAllRows();
973  mHistogramWidget->refresh();
974 }
975 
977 {
978  const QgsRangeList &ranges = mRenderer->ranges();
979  bool ordered = true;
980  for ( int i = 1;i < ranges.size();++i )
981  {
982  if ( ranges[i] < ranges[i-1] )
983  {
984  ordered = false;
985  break;
986  }
987  }
988  return ordered;
989 }
990 
992 {
993  //If the checkbox controlling the link between boundaries was unchecked and we check it, we have to link the boundaries
994  //This is done by updating all lower ranges to the upper value of the range above
995  if ( linked )
996  {
997  if ( ! rowsOrdered() )
998  {
999  int result = QMessageBox::warning(
1000  this,
1001  tr( "Linked range warning" ),
1002  tr( "Rows will be reordered before linking boundaries. Continue?" ),
1003  QMessageBox::Ok | QMessageBox::Cancel );
1004  if ( result != QMessageBox::Ok )
1005  {
1006  cbxLinkBoundaries->setChecked( false );
1007  return;
1008  }
1010  }
1011 
1012  // Ok to proceed
1013  for ( int i = 1;i < mRenderer->ranges().size();++i )
1014  {
1015  mRenderer->updateRangeLowerValue( i, mRenderer->ranges()[i-1].upperValue() );
1016  }
1018  }
1019 }
1020 
1022 {
1023  if ( item->column() == 2 )
1024  {
1025  QString label = item->text();
1026  int idx = item->row();
1027  mRenderer->updateRangeLabel( idx, label );
1028  }
1029 }
1030 
1032 {
1033  mRenderer->setSizeScaleField( fldName );
1034 }
1035 
1037 {
1038  mRenderer->setScaleMethod( scaleMethod );
1039 }
1040 
1042 {
1044  txtLegendFormat->text(),
1045  spinPrecision->value(),
1046  cbxTrimTrailingZeroes->isChecked() );
1047  mRenderer->setLabelFormat( labelFormat, true );
1048  mModel->updateLabels();
1049 }
1050 
1051 
1053 {
1055 
1056  QItemSelectionModel* m = viewGraduated->selectionModel();
1057  QModelIndexList selectedIndexes = m->selectedRows( 1 );
1058  if ( m && !selectedIndexes.isEmpty() )
1059  {
1060  const QgsRangeList& ranges = mRenderer->ranges();
1061  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1062  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1063  {
1064  QStringList list = m->model()->data( *indexIt ).toString().split( ' ' );
1065  if ( list.size() < 3 )
1066  {
1067  continue;
1068  }
1069 
1070  double lowerBound = list.at( 0 ).toDouble();
1071  double upperBound = list.at( 2 ).toDouble();
1072  QgsSymbolV2* s = findSymbolForRange( lowerBound, upperBound, ranges );
1073  if ( s )
1074  {
1075  selectedSymbols.append( s );
1076  }
1077  }
1078  }
1079  return selectedSymbols;
1080 }
1081 
1082 QgsSymbolV2* QgsGraduatedSymbolRendererV2Widget::findSymbolForRange( double lowerBound, double upperBound, const QgsRangeList& ranges ) const
1083 {
1084  for ( QgsRangeList::const_iterator it = ranges.begin(); it != ranges.end(); ++it )
1085  {
1086  //range string has been created with option 'f',4
1087  if ( qgsDoubleNear( lowerBound, it->lowerValue(), 0.0001 ) && qgsDoubleNear( upperBound, it->upperValue(), 0.0001 ) )
1088  {
1089  return it->symbol();
1090  }
1091  }
1092  return nullptr;
1093 }
1094 
1096 {
1097  if ( mModel )
1098  {
1099  mModel->updateSymbology();
1100  }
1101  mHistogramWidget->refresh();
1102 }
1103 
1105 {
1107 }
1108 
1110 {
1111  viewGraduated->selectionModel()->clear();
1112  if ( ! rowsOrdered() )
1113  {
1114  cbxLinkBoundaries->setChecked( false );
1115  }
1116 }
1117 
1119 {
1120 }
1121 
1123 {
1124  if ( !event )
1125  {
1126  return;
1127  }
1128 
1129  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1130  {
1131  mCopyBuffer.clear();
1133  }
1134  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1135  {
1137  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1138  {
1139  mModel->addClass( *rIt );
1140  }
1141  }
1142 }
void customContextMenuRequested(const QPoint &pos)
void addClass()
Adds a class manually to the classification.
void showSymbolLevelsDialog(QgsFeatureRendererV2 *r)
show a dialog with renderer&#39;s symbol level settings
QList< QgsRendererRangeV2 > QgsRangeList
void clear()
static unsigned index
void setSymbolSizes(double minSize, double maxSize)
set varying symbol size for classes
void setupUi(QWidget *widget)
QByteArray data(const QString &mimeType) const
void setLabelFormat(const QgsRendererRangeV2LabelFormat &labelFormat, bool updateRanges=false)
Set the label format used to generate default classification labels.
static QgsGraduatedSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsGraduatedSymbolRendererV2 from an existing renderer.
QgsGraduatedSymbolRendererV2Widget(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
void updateClasses(QgsVectorLayer *vlayer, Mode mode, int nclasses)
Recalculate classes for a layer.
QString upperValue() const
Definition: qgsludialog.cpp:37
void append(const T &value)
SymbolType type() const
Definition: qgssymbolv2.h:104
static QgsExpressionContextScope * atlasScope(const QgsAtlasComposition *atlas)
Creates a new scope which contains variables and functions relating to a QgsAtlasComposition.
void toggleBoundariesLink(bool linked)
Toggle the link between classes boundaries.
void updateSymbols(QgsSymbolV2 *sym)
Update all the symbols but leave breaks and colors.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
virtual bool hasFormat(const QString &mimeType) const
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
virtual QgsSymbolV2 * clone() const =0
QStyle * style() const
bool updateRangeRenderState(int rangeIndex, bool render)
The output shall be in pixels.
Definition: qgssymbolv2.h:67
const T & at(int i) const
void addAction(QAction *action)
void setSizeScaleField(const QString &fieldOrExpression)
QgsMapCanvas * mMapCanvas
QgsMapUnitScale mapUnitScale() const
double minSymbolSize() const
return the min symbol size when graduated by size
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
int exec()
void updateColorRamp(QgsVectorColorRampV2 *ramp=nullptr, bool inverted=false)
Update the color ramp used.
Line symbol.
Definition: qgssymbolv2.h:79
void calculateLabelPrecision(bool updateRanges=true)
Reset the label decimal places to a numberbased on the minimum class interval.
const QPixmap * icon() const
virtual QgsFeatureRendererV2 * renderer() override
return pointer to the renderer (no transfer of ownership)
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
void scaleMethodChanged(QgsSymbolV2::ScaleMethod scaleMethod)
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
void setLowerValue(const QString &val)
Definition: qgsludialog.cpp:42
double toDouble(bool *ok) const
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QString tr(const char *sourceText, const char *disambiguation, int n)
QString text() const
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
QgsVectorColorRampV2 * sourceColorRamp()
Returns the source color ramp, from which each classes&#39; color is derived.
Marker symbol.
Definition: qgssymbolv2.h:78
int size() const
void setMapUnitScale(const QgsMapUnitScale &scale)
T value(int i) const
The QgsMapSettings class contains configuration for rendering of the map.
QgsSymbolV2 * findSymbolForRange(double lowerBound, double upperBound, const QgsRangeList &ranges) const
long featureCount(QgsSymbolV2 *symbol)
Number of features rendered with specified symbol.
QSize size() const
void setColor(const QColor &color)
bool isValid() const
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=nullptr) const
The output shall be in millimeters.
Definition: qgssymbolv2.h:64
QString number(int n, int base)
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
int count(const T &value) const
QgsVectorLayer * mLayer
void setGraduatedMethod(GraduatedMethod method)
set the method used for graduation (either size or color)
void append(const T &value)
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
StandardButton question(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
bool isEmpty() const
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QgsExpressionContext _getExpressionContext(const void *context)
const QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
QModelIndexList selectedRows(int column) const
int row() const
const QgsVectorLayer * vectorLayer() const
Returns the vector layer associated with the widget.
QgsSymbolV2 * sourceSymbol()
Returns the renderer&#39;s source symbol, which is the base symbol used for the each classes&#39; symbol befo...
QgsSymbolV2 * symbol() const
GraduatedMethod graduatedMethod() const
return the method used for graduation (either size or color)
The output shall be in map unitx.
Definition: qgssymbolv2.h:65
void setOverrideCursor(const QCursor &cursor)
bool updateRangeLowerValue(int rangeIndex, double value)
void restoreOverrideCursor()
QGis::GeometryType geometryType() const
Returns point, line or polygon.
virtual QVariant data(const QModelIndex &index, int role) const =0
static QgsRendererV2Widget * create(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
Single scope for storing variables and functions for use within a QgsExpressionContext.
iterator end()
int key() const
const QgsRendererRangeV2LabelFormat & labelFormat() const
Return the label format used to generate default classification labels.
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
void contextMenuViewCategories(QPoint p)
void deleteClasses()
Removes currently selected classes.
void moveClass(int from, int to)
Moves the category at index position from to index position to.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
QgsExpressionContextScope & expressionContextScope()
Returns a reference to the expression context scope for the map canvas.
Definition: qgsmapcanvas.h:443
static QgsSymbolV2 * defaultSymbol(QGis::GeometryType geomType)
return new default symbol for specified geometry type
ScaleMethod
Scale method.
Definition: qgssymbolv2.h:87
typedef DropActions
double maxSymbolSize() const
return the max symbol size when graduated by size
const QgsRangeList & ranges() const
void setClassAttribute(const QString &attr)
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:381
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the dialog.
void setUpperValue(const QString &val)
Definition: qgsludialog.cpp:47
bool updateRangeSymbol(int rangeIndex, QgsSymbolV2 *symbol)
QgsSymbolV2::OutputUnit outputUnit() const
StandardButton critical(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
int column() const
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
Sets the source color ramp.
int column() const
Base class for renderer settings widgets.
bool updateRangeUpperValue(int rangeIndex, double value)
StandardButton warning(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
void setData(const QString &mimeType, const QByteArray &data)
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
const_iterator constEnd() const
const_iterator constBegin() const
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
int size() const
Represents a vector layer which manages a vector based data sets.
QList< QgsSymbolV2 * > selectedSymbols() override
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
QString toString() const
virtual bool event(QEvent *event)
const QAbstractItemModel * model() const
void setOutputUnit(QgsSymbolV2::OutputUnit u)
int row() const
iterator begin()
QString lowerValue() const
Definition: qgsludialog.cpp:32
QgsVectorLayer * layer()
Returns the layer currently associated with the widget.
void deleteAllClasses()
Removes all classes from the classification.
bool updateRangeLabel(int rangeIndex, const QString &label)
QList< int > selectedClasses()
return a list of indexes for the classes under selection
typedef ItemFlags