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