QGIS API Documentation  2.15.0-Master (af20121)
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  emit widgetChanged();
647 }
648 
650 {
651  mRenderer->setClassAttribute( field );
652 }
653 
655 {
656  mMethodStackedWidget->setCurrentIndex( idx );
657  if ( idx == 0 )
658  {
660  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
661 
662  if ( !ramp )
663  {
664  if ( cboGraduatedColorRamp->count() == 0 )
665  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
666  else
667  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
668  return;
669  }
670  mRenderer->setSourceColorRamp( ramp );
672  }
673  else
674  {
676  reapplySizes();
677  }
678 }
679 
681 {
682  if ( !mModel )
683  return;
684 
685  mModel->updateSymbology( reset );
686  emit widgetChanged();
687 }
688 
690 {
691  QString attrName = mExpressionWidget->currentField();
692 
693  int nclasses = spinGraduatedClasses->value();
694 
695  QSharedPointer<QgsVectorColorRampV2> ramp( cboGraduatedColorRamp->currentColorRamp() );
696  if ( !ramp )
697  {
698  if ( cboGraduatedColorRamp->count() == 0 )
699  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
700  else
701  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
702  return;
703  }
704 
706  if ( cboGraduatedMode->currentIndex() == 0 )
708  else if ( cboGraduatedMode->currentIndex() == 2 )
710  else if ( cboGraduatedMode->currentIndex() == 3 )
712  else if ( cboGraduatedMode->currentIndex() == 4 )
714  else // default should be quantile for now
716 
717  // Jenks is n^2 complexity, warn for big dataset (more than 50k records)
718  // and give the user the chance to cancel
719  if ( QgsGraduatedSymbolRendererV2::Jenks == mode && mLayer->featureCount() > 50000 )
720  {
721  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 ) )
722  return;
723  }
724 
725  // create and set new renderer
726 
727  mRenderer->setClassAttribute( attrName );
728  mRenderer->setMode( mode );
729 
730  if ( methodComboBox->currentIndex() == 0 )
731  {
732  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
733 
734  if ( !ramp )
735  {
736  if ( cboGraduatedColorRamp->count() == 0 )
737  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
738  else
739  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
740  return;
741  }
742  mRenderer->setSourceColorRamp( ramp );
743  }
744  else
745  {
746  mRenderer->setSourceColorRamp( nullptr );
747  }
748 
749  QApplication::setOverrideCursor( Qt::WaitCursor );
750  mRenderer->updateClasses( mLayer, mode, nclasses );
751 
752  if ( methodComboBox->currentIndex() == 1 )
753  mRenderer->setSymbolSizes( minSizeSpinBox->value(), maxSizeSpinBox->value() );
754 
757  // PrettyBreaks and StdDev calculation don't generate exact
758  // number of classes - leave user interface unchanged for these
759  updateUiFromRenderer( false );
760 }
761 
763 {
764  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
765  if ( !ramp )
766  return;
767 
768  mRenderer->updateColorRamp( ramp, cbxInvertedColorRamp->isChecked() );
771 }
772 
774 {
775  mRenderer->setSymbolSizes( minSizeSpinBox->value(), maxSizeSpinBox->value() );
778 }
779 
781 {
782  // Change the selected symbols alone if anything is selected
783  QItemSelectionModel* m = viewGraduated->selectionModel();
784  QModelIndexList i = m->selectedRows();
785  if ( m && !i.isEmpty() )
786  {
788  return;
789  }
790 
791  // Otherwise change the base mGraduatedSymbol
792  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
793 
794  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
795  dlg.setMapCanvas( mMapCanvas );
796  if ( !dlg.exec() )
797  {
798  delete newSymbol;
799  return;
800  }
801 
802  delete mGraduatedSymbol;
803  mGraduatedSymbol = newSymbol;
804 
805  mSizeUnitWidget->blockSignals( true );
806  mSizeUnitWidget->setUnit( mGraduatedSymbol->outputUnit() );
807  mSizeUnitWidget->setMapUnitScale( mGraduatedSymbol->mapUnitScale() );
808  mSizeUnitWidget->blockSignals( false );
809 
813 }
814 
816 {
817  if ( !mGraduatedSymbol )
818  return;
819 
820  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mGraduatedSymbol, btnChangeGraduatedSymbol->iconSize() );
821  btnChangeGraduatedSymbol->setIcon( icon );
822 }
823 
824 #if 0
825 int QgsRendererV2PropertiesDialog::currentRangeRow()
826 {
827  QModelIndex idx = viewGraduated->selectionModel()->currentIndex();
828  if ( !idx.isValid() )
829  return -1;
830  return idx.row();
831 }
832 #endif
833 
835 {
836  QList<int> rows;
837  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
838 
839  Q_FOREACH ( const QModelIndex& r, selectedRows )
840  {
841  if ( r.isValid() )
842  {
843  rows.append( r.row() );
844  }
845  }
846  return rows;
847 }
848 
850 {
852  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
853  QModelIndexList::const_iterator sIt = selectedRows.constBegin();
854 
855  for ( ; sIt != selectedRows.constEnd(); ++sIt )
856  {
857  selectedRanges.append( mModel->rendererRange( *sIt ) );
858  }
859  return selectedRanges;
860 }
861 
863 {
864  if ( idx.isValid() && idx.column() == 0 )
865  changeRangeSymbol( idx.row() );
866  if ( idx.isValid() && idx.column() == 1 )
867  changeRange( idx.row() );
868 }
869 
871 {
872  if ( !idx.isValid() )
873  mRowSelected = -1;
874  else
875  mRowSelected = idx.row();
876 }
877 
879 {
880  QItemSelectionModel* m = viewGraduated->selectionModel();
881  QModelIndexList selectedIndexes = m->selectedRows( 1 );
882  if ( m && !selectedIndexes.isEmpty() )
883  {
884  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
885  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
886  dlg.setMapCanvas( mMapCanvas );
887  if ( !dlg.exec() )
888  {
889  delete newSymbol;
890  return;
891  }
892 
893  Q_FOREACH ( const QModelIndex& idx, selectedIndexes )
894  {
895  if ( idx.isValid() )
896  {
897  int rangeIdx = idx.row();
898  QgsSymbolV2* newRangeSymbol = newSymbol->clone();
899  newRangeSymbol->setColor( mRenderer->ranges()[rangeIdx].symbol()->color() );
900  mRenderer->updateRangeSymbol( rangeIdx, newRangeSymbol );
901  }
902  }
903  }
905 }
906 
908 {
909  QgsSymbolV2* newSymbol = mRenderer->ranges()[rangeIdx].symbol()->clone();
910 
911  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
912  dlg.setMapCanvas( mMapCanvas );
913  if ( !dlg.exec() )
914  {
915  delete newSymbol;
916  return;
917  }
918 
919  mRenderer->updateRangeSymbol( rangeIdx, newSymbol );
920  mHistogramWidget->refresh();
921  emit widgetChanged();
922 }
923 
925 {
926  QgsLUDialog dialog( this );
927 
928  const QgsRendererRangeV2& range = mRenderer->ranges()[rangeIdx];
929  // Add arbitrary 2 to number of decimal places to retain a bit extra.
930  // Ensures users can see if legend is not completely honest!
931  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
932  if ( decimalPlaces < 0 ) decimalPlaces = 0;
933  dialog.setLowerValue( QString::number( range.lowerValue(), 'f', decimalPlaces ) );
934  dialog.setUpperValue( QString::number( range.upperValue(), 'f', decimalPlaces ) );
935 
936  if ( dialog.exec() == QDialog::Accepted )
937  {
938  double lowerValue = dialog.lowerValue().toDouble();
939  double upperValue = dialog.upperValue().toDouble();
940  mRenderer->updateRangeUpperValue( rangeIdx, upperValue );
941  mRenderer->updateRangeLowerValue( rangeIdx, lowerValue );
942 
943  //If the boundaries have to stay linked, we update the ranges above and below, as well as their label if needed
944  if ( cbxLinkBoundaries->isChecked() )
945  {
946  if ( rangeIdx > 0 )
947  {
948  mRenderer->updateRangeUpperValue( rangeIdx - 1, lowerValue );
949  }
950 
951  if ( rangeIdx < mRenderer->ranges().size() - 1 )
952  {
953  mRenderer->updateRangeLowerValue( rangeIdx + 1, upperValue );
954  }
955  }
956  }
957  mHistogramWidget->refresh();
958  emit widgetChanged();
959 }
960 
962 {
963  mModel->addClass( mGraduatedSymbol );
964  mHistogramWidget->refresh();
965 }
966 
968 {
969  QList<int> classIndexes = selectedClasses();
970  mModel->deleteRows( classIndexes );
971  mHistogramWidget->refresh();
972 }
973 
975 {
976  mModel->removeAllRows();
977  mHistogramWidget->refresh();
978 }
979 
981 {
982  const QgsRangeList &ranges = mRenderer->ranges();
983  bool ordered = true;
984  for ( int i = 1;i < ranges.size();++i )
985  {
986  if ( ranges[i] < ranges[i-1] )
987  {
988  ordered = false;
989  break;
990  }
991  }
992  return ordered;
993 }
994 
996 {
997  //If the checkbox controlling the link between boundaries was unchecked and we check it, we have to link the boundaries
998  //This is done by updating all lower ranges to the upper value of the range above
999  if ( linked )
1000  {
1001  if ( ! rowsOrdered() )
1002  {
1003  int result = QMessageBox::warning(
1004  this,
1005  tr( "Linked range warning" ),
1006  tr( "Rows will be reordered before linking boundaries. Continue?" ),
1007  QMessageBox::Ok | QMessageBox::Cancel );
1008  if ( result != QMessageBox::Ok )
1009  {
1010  cbxLinkBoundaries->setChecked( false );
1011  return;
1012  }
1014  }
1015 
1016  // Ok to proceed
1017  for ( int i = 1;i < mRenderer->ranges().size();++i )
1018  {
1019  mRenderer->updateRangeLowerValue( i, mRenderer->ranges()[i-1].upperValue() );
1020  }
1022  }
1023 }
1024 
1026 {
1027  if ( item->column() == 2 )
1028  {
1029  QString label = item->text();
1030  int idx = item->row();
1031  mRenderer->updateRangeLabel( idx, label );
1032  }
1033 }
1034 
1036 {
1037  mRenderer->setSizeScaleField( fldName );
1038 }
1039 
1041 {
1042  mRenderer->setScaleMethod( scaleMethod );
1043 }
1044 
1046 {
1048  txtLegendFormat->text(),
1049  spinPrecision->value(),
1050  cbxTrimTrailingZeroes->isChecked() );
1051  mRenderer->setLabelFormat( labelFormat, true );
1052  mModel->updateLabels();
1053 }
1054 
1055 
1057 {
1059 
1060  QItemSelectionModel* m = viewGraduated->selectionModel();
1061  QModelIndexList selectedIndexes = m->selectedRows( 1 );
1062  if ( m && !selectedIndexes.isEmpty() )
1063  {
1064  const QgsRangeList& ranges = mRenderer->ranges();
1065  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1066  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1067  {
1068  QStringList list = m->model()->data( *indexIt ).toString().split( ' ' );
1069  if ( list.size() < 3 )
1070  {
1071  continue;
1072  }
1073 
1074  double lowerBound = list.at( 0 ).toDouble();
1075  double upperBound = list.at( 2 ).toDouble();
1076  QgsSymbolV2* s = findSymbolForRange( lowerBound, upperBound, ranges );
1077  if ( s )
1078  {
1079  selectedSymbols.append( s );
1080  }
1081  }
1082  }
1083  return selectedSymbols;
1084 }
1085 
1086 QgsSymbolV2* QgsGraduatedSymbolRendererV2Widget::findSymbolForRange( double lowerBound, double upperBound, const QgsRangeList& ranges ) const
1087 {
1088  for ( QgsRangeList::const_iterator it = ranges.begin(); it != ranges.end(); ++it )
1089  {
1090  //range string has been created with option 'f',4
1091  if ( qgsDoubleNear( lowerBound, it->lowerValue(), 0.0001 ) && qgsDoubleNear( upperBound, it->upperValue(), 0.0001 ) )
1092  {
1093  return it->symbol();
1094  }
1095  }
1096  return nullptr;
1097 }
1098 
1100 {
1101  if ( mModel )
1102  {
1103  mModel->updateSymbology();
1104  }
1105  mHistogramWidget->refresh();
1106  emit widgetChanged();
1107 }
1108 
1110 {
1112 }
1113 
1115 {
1116  viewGraduated->selectionModel()->clear();
1117  if ( ! rowsOrdered() )
1118  {
1119  cbxLinkBoundaries->setChecked( false );
1120  }
1121  emit widgetChanged();
1122 }
1123 
1125 {
1126  emit widgetChanged();
1127 }
1128 
1130 {
1131  if ( !event )
1132  {
1133  return;
1134  }
1135 
1136  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1137  {
1138  mCopyBuffer.clear();
1140  }
1141  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1142  {
1144  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1145  {
1146  mModel->addClass( *rIt );
1147  }
1148  emit widgetChanged();
1149  }
1150 }
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 widgetChanged()
Emmited when something on the widget has changed.
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)
Compare two doubles (but allow some difference)
Definition: qgis.h:352
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:456
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:388
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.
Abstract base class for color ramps.
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