QGIS API Documentation  2.9.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 
41 QgsGraduatedSymbolRendererV2Model::QgsGraduatedSymbolRendererV2Model( QObject * parent ) : QAbstractItemModel( parent )
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 
94 Qt::ItemFlags QgsGraduatedSymbolRendererV2Model::flags( const QModelIndex & index ) const
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 
116 QVariant QgsGraduatedSymbolRendererV2Model::data( const QModelIndex &index, int role ) const
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 
161 bool QgsGraduatedSymbolRendererV2Model::setData( const QModelIndex & index, const QVariant & value, int role )
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 
202 int QgsGraduatedSymbolRendererV2Model::rowCount( const QModelIndex &parent ) const
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 
226 QModelIndex QgsGraduatedSymbolRendererV2Model::parent( const QModelIndex &index ) const
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 
383  : QgsRendererV2Widget( layer, style )
384  , mRenderer( 0 )
385  , mModel( 0 )
386 {
387 
388 
389  // try to recognize the previous renderer
390  // (null renderer means "no previous renderer")
391  if ( renderer )
392  {
394  }
395  if ( !mRenderer )
396  {
398  }
399 
400  // setup user interface
401  setupUi( this );
403 
404  mExpressionWidget->setFilters( QgsFieldProxyModel::Numeric | QgsFieldProxyModel::Date );
405  mExpressionWidget->setLayer( mLayer );
406 
407  mSizeUnitWidget->setUnits( QgsSymbolV2::OutputUnitList() << QgsSymbolV2::MM << QgsSymbolV2::MapUnit );
408 
409 
410  cboGraduatedColorRamp->populate( mStyle );
411 
412  spinPrecision->setMinimum( QgsRendererRangeV2LabelFormat::MinPrecision );
413  spinPrecision->setMaximum( QgsRendererRangeV2LabelFormat::MaxPrecision );
414 
415  // set project default color ramp
416  QString defaultColorRamp = QgsProject::instance()->readEntry( "DefaultStyles", "/ColorRamp", "" );
417  if ( defaultColorRamp != "" )
418  {
419  int index = cboGraduatedColorRamp->findText( defaultColorRamp, Qt::MatchCaseSensitive );
420  if ( index >= 0 )
421  cboGraduatedColorRamp->setCurrentIndex( index );
422  }
423 
424 
425  viewGraduated->setStyle( new QgsGraduatedSymbolRendererV2ViewStyle( viewGraduated->style() ) );
426 
428 
429  methodComboBox->blockSignals( true );
430  methodComboBox->addItem( "Color" );
432  {
433  methodComboBox->addItem( "Size" );
434  minSizeSpinBox->setValue( 1 );
435  maxSizeSpinBox->setValue( 8 );
436  }
437  else if ( mGraduatedSymbol->type() == QgsSymbolV2::Line )
438  {
439  methodComboBox->addItem( "Size" );
440  minSizeSpinBox->setValue( .1 );
441  maxSizeSpinBox->setValue( 2 );
442  }
443  methodComboBox->blockSignals( false );
444 
445  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( graduatedColumnChanged( QString ) ) );
446  connect( viewGraduated, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( rangesDoubleClicked( const QModelIndex & ) ) );
447  connect( viewGraduated, SIGNAL( clicked( const QModelIndex & ) ), this, SLOT( rangesClicked( const QModelIndex & ) ) );
448  connect( viewGraduated, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
449 
450  connect( btnGraduatedClassify, SIGNAL( clicked() ), this, SLOT( classifyGraduated() ) );
451  connect( btnChangeGraduatedSymbol, SIGNAL( clicked() ), this, SLOT( changeGraduatedSymbol() ) );
452  connect( btnGraduatedDelete, SIGNAL( clicked() ), this, SLOT( deleteClasses() ) );
453  connect( btnDeleteAllClasses, SIGNAL( clicked() ), this, SLOT( deleteAllClasses() ) );
454  connect( btnGraduatedAdd, SIGNAL( clicked() ), this, SLOT( addClass() ) );
455  connect( cbxLinkBoundaries, SIGNAL( toggled( bool ) ), this, SLOT( toggleBoundariesLink( bool ) ) );
456 
457  connect( mSizeUnitWidget, SIGNAL( changed() ), this, SLOT( on_mSizeUnitWidget_changed() ) );
458 
460 
461  // initialize from previously set renderer
463 
464  // menus for data-defined rotation/size
465  QMenu* advMenu = new QMenu;
466 
467  advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );
468 
471  connect( mDataDefinedMenus, SIGNAL( rotationFieldChanged( QString ) ), this, SLOT( rotationFieldChanged( QString ) ) );
472  connect( mDataDefinedMenus, SIGNAL( sizeScaleFieldChanged( QString ) ), this, SLOT( sizeScaleFieldChanged( QString ) ) );
474  btnAdvanced->setMenu( advMenu );
475 
476  mHistogramWidget->setLayer( mLayer );
477  mHistogramWidget->setRenderer( mRenderer );
478  connect( mHistogramWidget, SIGNAL( rangesModified( bool ) ), this, SLOT( refreshRanges( bool ) ) );
479  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), mHistogramWidget, SLOT( setSourceFieldExp( QString ) ) );
480 }
481 
483 {
484  if ( !mGraduatedSymbol ) return;
485  mGraduatedSymbol->setOutputUnit( mSizeUnitWidget->unit() );
486  mGraduatedSymbol->setMapUnitScale( mSizeUnitWidget->getMapUnitScale() );
490 }
491 
493 {
494  delete mRenderer;
495  delete mModel;
496 }
497 
499 {
500  return mRenderer;
501 }
502 
503 // Connect/disconnect event handlers which trigger updating renderer
504 
506 {
507  connect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ), this, SLOT( classifyGraduated() ) );
508  connect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classifyGraduated() ) );
509  connect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( reapplyColorRamp() ) );
510  connect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( reapplyColorRamp() ) );
511  connect( spinPrecision, SIGNAL( valueChanged( int ) ), this, SLOT( labelFormatChanged() ) );
512  connect( cbxTrimTrailingZeroes, SIGNAL( toggled( bool ) ), this, SLOT( labelFormatChanged() ) );
513  connect( txtLegendFormat, SIGNAL( textChanged( QString ) ), this, SLOT( labelFormatChanged() ) );
514  connect( minSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
515  connect( maxSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
516 
517  connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
518  connect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( modelDataChanged() ) );
519 }
520 
521 // Connect/disconnect event handlers which trigger updating renderer
522 
524 {
525  disconnect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ), this, SLOT( classifyGraduated() ) );
526  disconnect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classifyGraduated() ) );
527  disconnect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( reapplyColorRamp() ) );
528  disconnect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( reapplyColorRamp() ) );
529  disconnect( spinPrecision, SIGNAL( valueChanged( int ) ), this, SLOT( labelFormatChanged() ) );
530  disconnect( cbxTrimTrailingZeroes, SIGNAL( toggled( bool ) ), this, SLOT( labelFormatChanged() ) );
531  disconnect( txtLegendFormat, SIGNAL( textChanged( QString ) ), this, SLOT( labelFormatChanged() ) );
532  disconnect( minSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
533  disconnect( maxSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
534 
535  disconnect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
536  disconnect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( modelDataChanged() ) );
537 }
538 
540 {
542 
544 
545  // update UI from the graduated renderer (update combo boxes, view)
546  if ( mRenderer->mode() < cboGraduatedMode->count() )
547  cboGraduatedMode->setCurrentIndex( mRenderer->mode() );
548 
549  // Only update class count if different - otherwise typing value gets very messy
550  int nclasses = mRenderer->ranges().count();
551  if ( nclasses && updateCount )
552  spinGraduatedClasses->setValue( mRenderer->ranges().count() );
553 
554  // set column
555  QString attrName = mRenderer->classAttribute();
556  mExpressionWidget->setField( attrName );
557  mHistogramWidget->setSourceFieldExp( attrName );
558 
559  // set source symbol
560  if ( mRenderer->sourceSymbol() )
561  {
562  delete mGraduatedSymbol;
565  }
566 
568  viewGraduated->setModel( mModel );
569 
570  if ( mGraduatedSymbol )
571  {
572  mSizeUnitWidget->blockSignals( true );
573  mSizeUnitWidget->setUnit( mGraduatedSymbol->outputUnit() );
574  mSizeUnitWidget->setMapUnitScale( mGraduatedSymbol->mapUnitScale() );
575  mSizeUnitWidget->blockSignals( false );
576  }
577 
578  // set source color ramp
579  methodComboBox->blockSignals( true );
581  {
582  methodComboBox->setCurrentIndex( 0 );
583  if ( mRenderer->sourceColorRamp() )
584  cboGraduatedColorRamp->setSourceColorRamp( mRenderer->sourceColorRamp() );
585  cbxInvertedColorRamp->setChecked( mRenderer->invertedColorRamp() );
586  }
587  else
588  {
589  methodComboBox->setCurrentIndex( 1 );
590  if ( mRenderer->ranges().count() ) // avoid overiding default size with zeros
591  {
592  minSizeSpinBox->setValue( mRenderer->minSymbolSize() );
593  maxSizeSpinBox->setValue( mRenderer->maxSymbolSize() );
594  }
595  }
596  mMethodStackedWidget->setCurrentIndex( methodComboBox->currentIndex() );
597  methodComboBox->blockSignals( false );
598 
600  txtLegendFormat->setText( labelFormat.format() );
601  spinPrecision->setValue( labelFormat.precision() );
602  cbxTrimTrailingZeroes->setChecked( labelFormat.trimTrailingZeroes() );
603 
604  viewGraduated->resizeColumnToContents( 0 );
605  viewGraduated->resizeColumnToContents( 1 );
606  viewGraduated->resizeColumnToContents( 2 );
607 
608  mHistogramWidget->refreshAndRedraw();
609 
611 }
612 
614 {
615  mRenderer->setClassAttribute( field );
616 }
617 
619 {
620  mMethodStackedWidget->setCurrentIndex( idx );
621  if ( idx == 0 )
622  {
624  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
625 
626  if ( ramp == NULL )
627  {
628  if ( cboGraduatedColorRamp->count() == 0 )
629  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
630  else
631  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
632  return;
633  }
634  mRenderer->setSourceColorRamp( ramp->clone() );
636  }
637  else
638  {
640  reapplySizes();
641  }
642 }
643 
645 {
646  if ( !mModel )
647  return;
648 
649  mModel->updateSymbology( reset );
650 }
651 
653 {
654  QString attrName = mExpressionWidget->currentField();
655 
656  int nclasses = spinGraduatedClasses->value();
657 
658  QSharedPointer<QgsVectorColorRampV2> ramp( cboGraduatedColorRamp->currentColorRamp() );
659  if ( !ramp )
660  {
661  if ( cboGraduatedColorRamp->count() == 0 )
662  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
663  else
664  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
665  return;
666  }
667 
669  if ( cboGraduatedMode->currentIndex() == 0 )
671  else if ( cboGraduatedMode->currentIndex() == 2 )
673  else if ( cboGraduatedMode->currentIndex() == 3 )
675  else if ( cboGraduatedMode->currentIndex() == 4 )
677  else // default should be quantile for now
679 
680  // Jenks is n^2 complexity, warn for big dataset (more than 50k records)
681  // and give the user the chance to cancel
682  if ( QgsGraduatedSymbolRendererV2::Jenks == mode && mLayer->featureCount() > 50000 )
683  {
684  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 ) )
685  return;
686  }
687 
688  // create and set new renderer
689 
690  mRenderer->setClassAttribute( attrName );
691  mRenderer->setMode( mode );
692 
693  if ( methodComboBox->currentIndex() == 0 )
694  {
695  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
696 
697  if ( ramp == NULL )
698  {
699  if ( cboGraduatedColorRamp->count() == 0 )
700  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
701  else
702  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
703  return;
704  }
705  mRenderer->setSourceColorRamp( ramp->clone() );
706  }
707  else
708  {
709  mRenderer->setSourceColorRamp( NULL );
710  }
711 
712  QApplication::setOverrideCursor( Qt::WaitCursor );
713  mRenderer->updateClasses( mLayer, mode, nclasses );
714 
715  if ( methodComboBox->currentIndex() == 1 )
716  mRenderer->setSymbolSizes( minSizeSpinBox->value(), maxSizeSpinBox->value() );
717 
719  QApplication::restoreOverrideCursor();
720  // PrettyBreaks and StdDev calculation don't generate exact
721  // number of classes - leave user interface unchanged for these
722  updateUiFromRenderer( false );
723 }
724 
726 {
727  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
728  if ( ramp == NULL )
729  return;
730 
731  mRenderer->updateColorRamp( ramp, cbxInvertedColorRamp->isChecked() );
734 }
735 
737 {
738  mRenderer->setSymbolSizes( minSizeSpinBox->value(), maxSizeSpinBox->value() );
741 }
742 
744 {
745  // Change the selected symbols alone if anything is selected
746  QItemSelectionModel* m = viewGraduated->selectionModel();
747  QModelIndexList i = m->selectedRows();
748  if ( m && i.size() > 0 )
749  {
751  return;
752  }
753 
754  // Otherwise change the base mGraduatedSymbol
755  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
756 
757  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
758  if ( !dlg.exec() )
759  {
760  delete newSymbol;
761  return;
762  }
763 
764  mGraduatedSymbol = newSymbol;
765 
766  mSizeUnitWidget->blockSignals( true );
767  mSizeUnitWidget->setUnit( mGraduatedSymbol->outputUnit() );
768  mSizeUnitWidget->setMapUnitScale( mGraduatedSymbol->mapUnitScale() );
769  mSizeUnitWidget->blockSignals( false );
770 
774 }
775 
777 {
778  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mGraduatedSymbol, btnChangeGraduatedSymbol->iconSize() );
779  btnChangeGraduatedSymbol->setIcon( icon );
780 }
781 
782 #if 0
783 int QgsRendererV2PropertiesDialog::currentRangeRow()
784 {
785  QModelIndex idx = viewGraduated->selectionModel()->currentIndex();
786  if ( !idx.isValid() )
787  return -1;
788  return idx.row();
789 }
790 #endif
791 
793 {
794  QList<int> rows;
795  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
796 
797  foreach ( QModelIndex r, selectedRows )
798  {
799  if ( r.isValid() )
800  {
801  rows.append( r.row() );
802  }
803  }
804  return rows;
805 }
806 
808 {
810  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
811  QModelIndexList::const_iterator sIt = selectedRows.constBegin();
812 
813  for ( ; sIt != selectedRows.constEnd(); ++sIt )
814  {
815  selectedRanges.append( mModel->rendererRange( *sIt ) );
816  }
817  return selectedRanges;
818 }
819 
821 {
822  if ( idx.isValid() && idx.column() == 0 )
823  changeRangeSymbol( idx.row() );
824  if ( idx.isValid() && idx.column() == 1 )
825  changeRange( idx.row() );
826 }
827 
829 {
830  if ( !idx.isValid() )
831  mRowSelected = -1;
832  else
833  mRowSelected = idx.row();
834 }
835 
837 {
838  QItemSelectionModel* m = viewGraduated->selectionModel();
839  QModelIndexList selectedIndexes = m->selectedRows( 1 );
840  if ( m && selectedIndexes.size() > 0 )
841  {
842  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
843  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
844  if ( !dlg.exec() )
845  {
846  delete newSymbol;
847  return;
848  }
849 
850  foreach ( QModelIndex idx, selectedIndexes )
851  {
852  if ( idx.isValid() )
853  {
854  int rangeIdx = idx.row();
855  QgsSymbolV2* newRangeSymbol = newSymbol->clone();
856  newRangeSymbol->setColor( mRenderer->ranges()[rangeIdx].symbol()->color() );
857  mRenderer->updateRangeSymbol( rangeIdx, newRangeSymbol );
858  }
859  }
860  }
862 }
863 
865 {
866  QgsSymbolV2* newSymbol = mRenderer->ranges()[rangeIdx].symbol()->clone();
867 
868  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
869  if ( !dlg.exec() )
870  {
871  delete newSymbol;
872  return;
873  }
874 
875  mRenderer->updateRangeSymbol( rangeIdx, newSymbol );
876  mHistogramWidget->refreshHistogram();
877 }
878 
880 {
881  QgsLUDialog dialog( this );
882 
883  const QgsRendererRangeV2& range = mRenderer->ranges()[rangeIdx];
884  // Add arbitrary 2 to number of decimal places to retain a bit extra.
885  // Ensures users can see if legend is not completely honest!
886  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
887  if ( decimalPlaces < 0 ) decimalPlaces = 0;
888  dialog.setLowerValue( QString::number( range.lowerValue(), 'f', decimalPlaces ) );
889  dialog.setUpperValue( QString::number( range.upperValue(), 'f', decimalPlaces ) );
890 
891  if ( dialog.exec() == QDialog::Accepted )
892  {
893  double lowerValue = dialog.lowerValue().toDouble();
894  double upperValue = dialog.upperValue().toDouble();
895  mRenderer->updateRangeUpperValue( rangeIdx, upperValue );
896  mRenderer->updateRangeLowerValue( rangeIdx, lowerValue );
897 
898  //If the boundaries have to stay linked, we update the ranges above and below, as well as their label if needed
899  if ( cbxLinkBoundaries->isChecked() )
900  {
901  if ( rangeIdx > 0 )
902  {
903  mRenderer->updateRangeUpperValue( rangeIdx - 1, lowerValue );
904  }
905 
906  if ( rangeIdx < mRenderer->ranges().size() - 1 )
907  {
908  mRenderer->updateRangeLowerValue( rangeIdx + 1, upperValue );
909  }
910  }
911  }
912  mHistogramWidget->refreshHistogram();
913 }
914 
916 {
918  mHistogramWidget->refreshHistogram();
919 }
920 
922 {
923  QList<int> classIndexes = selectedClasses();
924  mModel->deleteRows( classIndexes );
925  mHistogramWidget->refreshHistogram();
926 }
927 
929 {
931  mHistogramWidget->refreshHistogram();
932 }
933 
935 {
936  const QgsRangeList &ranges = mRenderer->ranges();
937  bool ordered = true;
938  for ( int i = 1;i < ranges.size();++i )
939  {
940  if ( ranges[i] < ranges[i-1] )
941  {
942  ordered = false;
943  break;
944  }
945  }
946  return ordered;
947 }
948 
950 {
951  //If the checkbox controlling the link between boundaries was unchecked and we check it, we have to link the boundaries
952  //This is done by updating all lower ranges to the upper value of the range above
953  if ( linked )
954  {
955  if ( ! rowsOrdered() )
956  {
957  int result = QMessageBox::warning(
958  this,
959  tr( "Linked range warning" ),
960  tr( "Rows will be reordered before linking boundaries. Continue?" ),
961  QMessageBox::Ok | QMessageBox::Cancel );
962  if ( result != QMessageBox::Ok )
963  {
964  cbxLinkBoundaries->setChecked( false );
965  return;
966  }
968  }
969 
970  // Ok to proceed
971  for ( int i = 1;i < mRenderer->ranges().size();++i )
972  {
973  mRenderer->updateRangeLowerValue( i, mRenderer->ranges()[i-1].upperValue() );
974  }
976  }
977 }
978 
980 {
981  if ( item->column() == 2 )
982  {
983  QString label = item->text();
984  int idx = item->row();
985  mRenderer->updateRangeLabel( idx, label );
986  }
987 }
988 
990 {
991  mRenderer->setRotationField( fldName );
992 }
993 
995 {
996  mRenderer->setSizeScaleField( fldName );
997 }
998 
1000 {
1001  mRenderer->setScaleMethod( scaleMethod );
1002 }
1003 
1005 {
1007  txtLegendFormat->text(),
1008  spinPrecision->value(),
1009  cbxTrimTrailingZeroes->isChecked() );
1010  mRenderer->setLabelFormat( labelFormat, true );
1011  mModel->updateLabels();
1012 }
1013 
1014 
1016 {
1017  QList<QgsSymbolV2*> selectedSymbols;
1018 
1019  QItemSelectionModel* m = viewGraduated->selectionModel();
1020  QModelIndexList selectedIndexes = m->selectedRows( 1 );
1021  if ( m && selectedIndexes.size() > 0 )
1022  {
1023  const QgsRangeList& ranges = mRenderer->ranges();
1024  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1025  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1026  {
1027  QStringList list = m->model()->data( *indexIt ).toString().split( " " );
1028  if ( list.size() < 3 )
1029  {
1030  continue;
1031  }
1032 
1033  double lowerBound = list.at( 0 ).toDouble();
1034  double upperBound = list.at( 2 ).toDouble();
1035  QgsSymbolV2* s = findSymbolForRange( lowerBound, upperBound, ranges );
1036  if ( s )
1037  {
1038  selectedSymbols.append( s );
1039  }
1040  }
1041  }
1042  return selectedSymbols;
1043 }
1044 
1045 QgsSymbolV2* QgsGraduatedSymbolRendererV2Widget::findSymbolForRange( double lowerBound, double upperBound, const QgsRangeList& ranges ) const
1046 {
1047  for ( QgsRangeList::const_iterator it = ranges.begin(); it != ranges.end(); ++it )
1048  {
1049  //range string has been created with option 'f',4
1050  if ( qgsDoubleNear( lowerBound, it->lowerValue(), 0.0001 ) && qgsDoubleNear( upperBound, it->upperValue(), 0.0001 ) )
1051  {
1052  return it->symbol();
1053  }
1054  }
1055  return 0;
1056 }
1057 
1059 {
1060  if ( mModel )
1061  {
1063  }
1064 }
1065 
1067 {
1069 }
1070 
1072 {
1073  viewGraduated->selectionModel()->clear();
1074  if ( ! rowsOrdered() )
1075  {
1076  cbxLinkBoundaries->setChecked( false );
1077  }
1078 }
1079 
1081 {
1082 }
1083 
1085 {
1086  if ( !event )
1087  {
1088  return;
1089  }
1090 
1091  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1092  {
1093  mCopyBuffer.clear();
1095  }
1096  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1097  {
1098  QgsRangeList::const_iterator rIt = mCopyBuffer.constBegin();
1099  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1100  {
1101  mModel->addClass( *rIt );
1102  }
1103  }
1104 }
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
static unsigned index
void setSymbolSizes(double minSize, double maxSize)
set varying symbol size for classes
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
void setRotationField(QString fieldOrExpression) override
sets rotation field of renderer (if supported by the renderer)
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
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
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
virtual QgsSymbolV2 * clone() const =0
QList< OutputUnit > OutputUnitList
Definition: qgssymbolv2.h:60
bool updateRangeRenderState(int rangeIndex, bool render)
void setSizeScaleField(QString fieldOrExpression)
QgsMapUnitScale mapUnitScale() const
double minSymbolSize() const
return the min symbol size when graduated by size
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.
virtual QgsFeatureRendererV2 * renderer() override
return pointer to the renderer (no transfer of ownership)
void scaleMethodChanged(QgsSymbolV2::ScaleMethod scaleMethod)
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:347
void setRenderer(QgsGraduatedSymbolRendererV2 *renderer)
void setMapUnitScale(const QgsMapUnitScale &scale)
QgsSymbolV2 * findSymbolForRange(double lowerBound, double upperBound, const QgsRangeList &ranges) const
Qt::ItemFlags flags(const QModelIndex &index) const override
void setColor(const QColor &color)
Utility class for providing GUI for data-defined rendering.
void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override
QVariant data(const QModelIndex &index, int role) const override
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
QgsVectorLayer * mLayer
void setGraduatedMethod(GraduatedMethod method)
set the method used for graduation (either size or color)
QgsSymbolV2::ScaleMethod scaleMethod() const
int columnCount(const QModelIndex &=QModelIndex()) const override
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
virtual QgsVectorColorRampV2 * clone() const =0
QgsRendererRangeV2 rendererRange(const QModelIndex &index)
QString rotationField() const override
return rotation field name (or empty string if not set or not supported by renderer) ...
QgsSymbolV2 * symbol() const
GraduatedMethod graduatedMethod() const
return the method used for graduation (either size or color)
bool updateRangeLowerValue(int rangeIndex, double value)
QMimeData * mimeData(const QModelIndexList &indexes) const override
QGis::GeometryType geometryType() const
Returns point, line or polygon.
Qt::DropActions supportedDropActions() const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
static QgsRendererV2Widget * create(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
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 deleteClasses()
Removes currently selected classes.
virtual long featureCount() const
Number of features in the layer.
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
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:351
bool updateRangeSymbol(int rangeIndex, QgsSymbolV2 *symbol)
QgsSymbolV2::OutputUnit outputUnit() const
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
Base class for renderer settings widgets.
bool updateRangeUpperValue(int rangeIndex, double value)
QModelIndex parent(const QModelIndex &index) const override
Represents a vector layer which manages a vector based data sets.
double size
Definition: qgssvgcache.cpp:77
QList< QgsSymbolV2 * > selectedSymbols() override
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
void setOutputUnit(QgsSymbolV2::OutputUnit u)
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
#define tr(sourceText)