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