QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
341 }
342 
344 {
345  emit dataChanged( createIndex( 0, 2 ), createIndex( mRenderer->ranges().size(), 2 ) );
346 }
347 
348 // ------------------------------ View style --------------------------------
350  : QProxyStyle( style )
351 {}
352 
353 void QgsGraduatedSymbolRendererV2ViewStyle::drawPrimitive( PrimitiveElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget ) const
354 {
355  if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
356  {
357  QStyleOption opt( *option );
358  opt.rect.setLeft( 0 );
359  // draw always as line above, because we move item to that index
360  opt.rect.setHeight( 0 );
361  if ( widget ) opt.rect.setRight( widget->width() );
362  QProxyStyle::drawPrimitive( element, &opt, painter, widget );
363  return;
364  }
365  QProxyStyle::drawPrimitive( element, option, painter, widget );
366 }
367 
368 // ------------------------------ Widget ------------------------------------
369 
371 {
372  return new QgsGraduatedSymbolRendererV2Widget( layer, style, renderer );
373 }
374 
376  : QgsRendererV2Widget( layer, style )
377  , mRenderer( 0 )
378  , mModel( 0 )
379 {
380 
381 
382  // try to recognize the previous renderer
383  // (null renderer means "no previous renderer")
384  if ( renderer )
385  {
387  }
388  if ( !mRenderer )
389  {
391  }
392 
393  // setup user interface
394  setupUi( this );
396 
397  mExpressionWidget->setFilters( QgsFieldProxyModel::Numeric | QgsFieldProxyModel::Date );
398  mExpressionWidget->setLayer( mLayer );
399 
400  mSizeUnitWidget->setUnits( QgsSymbolV2::OutputUnitList() << QgsSymbolV2::MM << QgsSymbolV2::MapUnit );
401 
402 
403  cboGraduatedColorRamp->populate( mStyle );
404 
405  spinPrecision->setMinimum( QgsRendererRangeV2LabelFormat::MinPrecision );
406  spinPrecision->setMaximum( QgsRendererRangeV2LabelFormat::MaxPrecision );
407 
408  // set project default color ramp
409  QString defaultColorRamp = QgsProject::instance()->readEntry( "DefaultStyles", "/ColorRamp", "" );
410  if ( defaultColorRamp != "" )
411  {
412  int index = cboGraduatedColorRamp->findText( defaultColorRamp, Qt::MatchCaseSensitive );
413  if ( index >= 0 )
414  cboGraduatedColorRamp->setCurrentIndex( index );
415  }
416 
417 
418  viewGraduated->setStyle( new QgsGraduatedSymbolRendererV2ViewStyle( viewGraduated->style() ) );
419 
421 
422  methodComboBox->blockSignals( true );
423  methodComboBox->addItem( "Color" );
425  {
426  methodComboBox->addItem( "Size" );
427  minSizeSpinBox->setValue( 1 );
428  maxSizeSpinBox->setValue( 8 );
429  }
430  else if ( mGraduatedSymbol->type() == QgsSymbolV2::Line )
431  {
432  methodComboBox->addItem( "Size" );
433  minSizeSpinBox->setValue( .1 );
434  maxSizeSpinBox->setValue( 2 );
435  }
436  methodComboBox->blockSignals( false );
437 
438  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( graduatedColumnChanged( QString ) ) );
439  connect( viewGraduated, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( rangesDoubleClicked( const QModelIndex & ) ) );
440  connect( viewGraduated, SIGNAL( clicked( const QModelIndex & ) ), this, SLOT( rangesClicked( const QModelIndex & ) ) );
441  connect( viewGraduated, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
442 
443  connect( btnGraduatedClassify, SIGNAL( clicked() ), this, SLOT( classifyGraduated() ) );
444  connect( btnChangeGraduatedSymbol, SIGNAL( clicked() ), this, SLOT( changeGraduatedSymbol() ) );
445  connect( btnGraduatedDelete, SIGNAL( clicked() ), this, SLOT( deleteClasses() ) );
446  connect( btnDeleteAllClasses, SIGNAL( clicked() ), this, SLOT( deleteAllClasses() ) );
447  connect( btnGraduatedAdd, SIGNAL( clicked() ), this, SLOT( addClass() ) );
448  connect( cbxLinkBoundaries, SIGNAL( toggled( bool ) ), this, SLOT( toggleBoundariesLink( bool ) ) );
449 
450  connect( mSizeUnitWidget, SIGNAL( changed() ), this, SLOT( on_mSizeUnitWidget_changed() ) );
451 
453 
454  // initialize from previously set renderer
456 
457  // menus for data-defined rotation/size
458  QMenu* advMenu = new QMenu;
459 
460  advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );
461 
464  connect( mDataDefinedMenus, SIGNAL( rotationFieldChanged( QString ) ), this, SLOT( rotationFieldChanged( QString ) ) );
465  connect( mDataDefinedMenus, SIGNAL( sizeScaleFieldChanged( QString ) ), this, SLOT( sizeScaleFieldChanged( QString ) ) );
467  btnAdvanced->setMenu( advMenu );
468 }
469 
471 {
472  if ( !mGraduatedSymbol ) return;
473  mGraduatedSymbol->setOutputUnit( mSizeUnitWidget->unit() );
474  mGraduatedSymbol->setMapUnitScale( mSizeUnitWidget->getMapUnitScale() );
478 }
479 
481 {
482  delete mRenderer;
483  delete mModel;
484 }
485 
487 {
488  return mRenderer;
489 }
490 
491 // Connect/disconnect event handlers which trigger updating renderer
492 
494 {
495  connect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ), this, SLOT( classifyGraduated() ) );
496  connect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classifyGraduated() ) );
497  connect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( reapplyColorRamp() ) );
498  connect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( reapplyColorRamp() ) );
499  connect( spinPrecision, SIGNAL( valueChanged( int ) ), this, SLOT( labelFormatChanged() ) );
500  connect( cbxTrimTrailingZeroes, SIGNAL( toggled( bool ) ), this, SLOT( labelFormatChanged() ) );
501  connect( txtLegendFormat, SIGNAL( textChanged( QString ) ), this, SLOT( labelFormatChanged() ) );
502  connect( minSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
503  connect( maxSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
504 
505 
506  connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
507  connect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( modelDataChanged() ) );
508 }
509 
510 // Connect/disconnect event handlers which trigger updating renderer
511 
513 {
514  disconnect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ), this, SLOT( classifyGraduated() ) );
515  disconnect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classifyGraduated() ) );
516  disconnect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( reapplyColorRamp() ) );
517  disconnect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( reapplyColorRamp() ) );
518  disconnect( spinPrecision, SIGNAL( valueChanged( int ) ), this, SLOT( labelFormatChanged() ) );
519  disconnect( cbxTrimTrailingZeroes, SIGNAL( toggled( bool ) ), this, SLOT( labelFormatChanged() ) );
520  disconnect( txtLegendFormat, SIGNAL( textChanged( QString ) ), this, SLOT( labelFormatChanged() ) );
521  disconnect( minSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
522  disconnect( maxSizeSpinBox, SIGNAL( valueChanged( double ) ), this, SLOT( reapplySizes() ) );
523 
524  disconnect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
525  disconnect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( modelDataChanged() ) );
526 }
527 
529 {
531 
533 
534  // update UI from the graduated renderer (update combo boxes, view)
535  if ( mRenderer->mode() < cboGraduatedMode->count() )
536  cboGraduatedMode->setCurrentIndex( mRenderer->mode() );
537 
538  // Only update class count if different - otherwise typing value gets very messy
539  int nclasses = mRenderer->ranges().count();
540  if ( nclasses && updateCount )
541  spinGraduatedClasses->setValue( mRenderer->ranges().count() );
542 
543  // set column
544  QString attrName = mRenderer->classAttribute();
545  mExpressionWidget->setField( attrName );
546 
547  // set source symbol
548  if ( mRenderer->sourceSymbol() )
549  {
550  delete mGraduatedSymbol;
553  }
554 
556  viewGraduated->setModel( mModel );
557 
558  if ( mGraduatedSymbol )
559  {
560  mSizeUnitWidget->blockSignals( true );
561  mSizeUnitWidget->setUnit( mGraduatedSymbol->outputUnit() );
562  mSizeUnitWidget->setMapUnitScale( mGraduatedSymbol->mapUnitScale() );
563  mSizeUnitWidget->blockSignals( false );
564  }
565 
566  // set source color ramp
567  methodComboBox->blockSignals( true );
569  {
570  methodComboBox->setCurrentIndex( 0 );
571  if ( mRenderer->sourceColorRamp() )
572  cboGraduatedColorRamp->setSourceColorRamp( mRenderer->sourceColorRamp() );
573  cbxInvertedColorRamp->setChecked( mRenderer->invertedColorRamp() );
574  }
575  else
576  {
577  methodComboBox->setCurrentIndex( 1 );
578  if ( mRenderer->ranges().count() ) // avoid overiding default size with zeros
579  {
580  minSizeSpinBox->setValue( mRenderer->minSymbolSize() );
581  maxSizeSpinBox->setValue( mRenderer->maxSymbolSize() );
582  }
583  }
584  mMethodStackedWidget->setCurrentIndex( methodComboBox->currentIndex() );
585  methodComboBox->blockSignals( false );
586 
588  txtLegendFormat->setText( labelFormat.format() );
589  spinPrecision->setValue( labelFormat.precision() );
590  cbxTrimTrailingZeroes->setChecked( labelFormat.trimTrailingZeroes() );
591 
592  viewGraduated->resizeColumnToContents( 0 );
593  viewGraduated->resizeColumnToContents( 1 );
594  viewGraduated->resizeColumnToContents( 2 );
595 
597 }
598 
600 {
601  mRenderer->setClassAttribute( field );
602 }
603 
605 {
606  mMethodStackedWidget->setCurrentIndex( idx );
607  if ( idx == 0 )
608  {
610  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
611 
612  if ( ramp == NULL )
613  {
614  if ( cboGraduatedColorRamp->count() == 0 )
615  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
616  else
617  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
618  return;
619  }
620  mRenderer->setSourceColorRamp( ramp->clone() );
622  }
623  else
624  {
626  reapplySizes();
627  }
628 }
629 
631 {
632  QString attrName = mExpressionWidget->currentField();
633 
634  int nclasses = spinGraduatedClasses->value();
635 
636  QSharedPointer<QgsVectorColorRampV2> ramp( cboGraduatedColorRamp->currentColorRamp() );
637  if ( !ramp )
638  {
639  if ( cboGraduatedColorRamp->count() == 0 )
640  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
641  else
642  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
643  return;
644  }
645 
647  if ( cboGraduatedMode->currentIndex() == 0 )
649  else if ( cboGraduatedMode->currentIndex() == 2 )
651  else if ( cboGraduatedMode->currentIndex() == 3 )
653  else if ( cboGraduatedMode->currentIndex() == 4 )
655  else // default should be quantile for now
657 
658  // Jenks is n^2 complexity, warn for big dataset (more than 50k records)
659  // and give the user the chance to cancel
660  if ( QgsGraduatedSymbolRendererV2::Jenks == mode && mLayer->featureCount() > 50000 )
661  {
662  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 ) )
663  return;
664  }
665 
666  // create and set new renderer
667 
668  mRenderer->setClassAttribute( attrName );
669  mRenderer->setMode( mode );
670 
671  if ( methodComboBox->currentIndex() == 0 )
672  {
673  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
674 
675  if ( ramp == NULL )
676  {
677  if ( cboGraduatedColorRamp->count() == 0 )
678  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
679  else
680  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
681  return;
682  }
683  mRenderer->setSourceColorRamp( ramp->clone() );
684  }
685  else
686  {
687  mRenderer->setSourceColorRamp( NULL );
688  }
689 
690  QApplication::setOverrideCursor( Qt::WaitCursor );
691  mRenderer->updateClasses( mLayer, mode, nclasses );
692 
693  if ( methodComboBox->currentIndex() == 1 )
694  mRenderer->setSymbolSizes( minSizeSpinBox->value(), maxSizeSpinBox->value() );
695 
697  QApplication::restoreOverrideCursor();
698  // PrettyBreaks and StdDev calculation don't generate exact
699  // number of classes - leave user interface unchanged for these
700  updateUiFromRenderer( false );
701 }
702 
704 {
705  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
706  if ( ramp == NULL )
707  return;
708 
709  mRenderer->updateColorRamp( ramp, cbxInvertedColorRamp->isChecked() );
712 }
713 
715 {
716  mRenderer->setSymbolSizes( minSizeSpinBox->value(), maxSizeSpinBox->value() );
719 }
720 
722 {
723  // Change the selected symbols alone if anything is selected
724  QItemSelectionModel* m = viewGraduated->selectionModel();
725  QModelIndexList i = m->selectedRows();
726  if ( m && i.size() > 0 )
727  {
729  return;
730  }
731 
732  // Otherwise change the base mGraduatedSymbol
733  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
734 
735  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
736  if ( !dlg.exec() )
737  {
738  delete newSymbol;
739  return;
740  }
741 
742  mGraduatedSymbol = newSymbol;
743 
744  mSizeUnitWidget->blockSignals( true );
745  mSizeUnitWidget->setUnit( mGraduatedSymbol->outputUnit() );
746  mSizeUnitWidget->setMapUnitScale( mGraduatedSymbol->mapUnitScale() );
747  mSizeUnitWidget->blockSignals( false );
748 
752 }
753 
755 {
756  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mGraduatedSymbol, btnChangeGraduatedSymbol->iconSize() );
757  btnChangeGraduatedSymbol->setIcon( icon );
758 }
759 
760 #if 0
761 int QgsRendererV2PropertiesDialog::currentRangeRow()
762 {
763  QModelIndex idx = viewGraduated->selectionModel()->currentIndex();
764  if ( !idx.isValid() )
765  return -1;
766  return idx.row();
767 }
768 #endif
769 
771 {
772  QList<int> rows;
773  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
774 
775  foreach ( QModelIndex r, selectedRows )
776  {
777  if ( r.isValid() )
778  {
779  rows.append( r.row() );
780  }
781  }
782  return rows;
783 }
784 
786 {
788  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
789  QModelIndexList::const_iterator sIt = selectedRows.constBegin();
790 
791  for ( ; sIt != selectedRows.constEnd(); ++sIt )
792  {
793  selectedRanges.append( mModel->rendererRange( *sIt ) );
794  }
795  return selectedRanges;
796 }
797 
799 {
800  if ( idx.isValid() && idx.column() == 0 )
801  changeRangeSymbol( idx.row() );
802  if ( idx.isValid() && idx.column() == 1 )
803  changeRange( idx.row() );
804 }
805 
807 {
808  if ( !idx.isValid() )
809  mRowSelected = -1;
810  else
811  mRowSelected = idx.row();
812 }
813 
815 {
816  QItemSelectionModel* m = viewGraduated->selectionModel();
817  QModelIndexList selectedIndexes = m->selectedRows( 1 );
818  if ( m && selectedIndexes.size() > 0 )
819  {
820  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
821  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
822  if ( !dlg.exec() )
823  {
824  delete newSymbol;
825  return;
826  }
827 
828  foreach ( QModelIndex idx, selectedIndexes )
829  {
830  if ( idx.isValid() )
831  {
832  int rangeIdx = idx.row();
833  QgsSymbolV2* newRangeSymbol = newSymbol->clone();
834  newRangeSymbol->setColor( mRenderer->ranges()[rangeIdx].symbol()->color() );
835  mRenderer->updateRangeSymbol( rangeIdx, newRangeSymbol );
836  }
837  }
838  }
840 }
841 
843 {
844  QgsSymbolV2* newSymbol = mRenderer->ranges()[rangeIdx].symbol()->clone();
845 
846  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
847  if ( !dlg.exec() )
848  {
849  delete newSymbol;
850  return;
851  }
852 
853  mRenderer->updateRangeSymbol( rangeIdx, newSymbol );
854 }
855 
857 {
858  QgsLUDialog dialog( this );
859 
860  const QgsRendererRangeV2& range = mRenderer->ranges()[rangeIdx];
861  // Add arbitrary 2 to number of decimal places to retain a bit extra.
862  // Ensures users can see if legend is not completely honest!
863  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
864  if ( decimalPlaces < 0 ) decimalPlaces = 0;
865  dialog.setLowerValue( QString::number( range.lowerValue(), 'f', decimalPlaces ) );
866  dialog.setUpperValue( QString::number( range.upperValue(), 'f', decimalPlaces ) );
867 
868  if ( dialog.exec() == QDialog::Accepted )
869  {
870  double lowerValue = dialog.lowerValue().toDouble();
871  double upperValue = dialog.upperValue().toDouble();
872  mRenderer->updateRangeUpperValue( rangeIdx, upperValue );
873  mRenderer->updateRangeLowerValue( rangeIdx, lowerValue );
874 
875  //If the boundaries have to stay linked, we update the ranges above and below, as well as their label if needed
876  if ( cbxLinkBoundaries->isChecked() )
877  {
878  if ( rangeIdx > 0 )
879  {
880  mRenderer->updateRangeUpperValue( rangeIdx - 1, lowerValue );
881  }
882 
883  if ( rangeIdx < mRenderer->ranges().size() - 1 )
884  {
885  mRenderer->updateRangeLowerValue( rangeIdx + 1, upperValue );
886  }
887  }
888  }
889 }
890 
892 {
894 }
895 
897 {
898  QList<int> classIndexes = selectedClasses();
899  mModel->deleteRows( classIndexes );
900 }
901 
903 {
905 }
906 
908 {
909  const QgsRangeList &ranges = mRenderer->ranges();
910  bool ordered = true;
911  for ( int i = 1;i < ranges.size();++i )
912  {
913  if ( ranges[i] < ranges[i-1] )
914  {
915  ordered = false;
916  break;
917  }
918  }
919  return ordered;
920 }
921 
923 {
924  //If the checkbox controlling the link between boundaries was unchecked and we check it, we have to link the boundaries
925  //This is done by updating all lower ranges to the upper value of the range above
926  if ( linked )
927  {
928  if ( ! rowsOrdered() )
929  {
930  int result = QMessageBox::warning(
931  this,
932  tr( "Linked range warning" ),
933  tr( "Rows will be reordered before linking boundaries. Continue?" ),
934  QMessageBox::Ok | QMessageBox::Cancel );
935  if ( result != QMessageBox::Ok )
936  {
937  cbxLinkBoundaries->setChecked( false );
938  return;
939  }
941  }
942 
943  // Ok to proceed
944  for ( int i = 1;i < mRenderer->ranges().size();++i )
945  {
946  mRenderer->updateRangeLowerValue( i, mRenderer->ranges()[i-1].upperValue() );
947  }
949  }
950 }
951 
953 {
954  if ( item->column() == 2 )
955  {
956  QString label = item->text();
957  int idx = item->row();
958  mRenderer->updateRangeLabel( idx, label );
959  }
960 }
961 
963 {
964  mRenderer->setRotationField( fldName );
965 }
966 
968 {
969  mRenderer->setSizeScaleField( fldName );
970 }
971 
973 {
974  mRenderer->setScaleMethod( scaleMethod );
975 }
976 
978 {
980  txtLegendFormat->text(),
981  spinPrecision->value(),
982  cbxTrimTrailingZeroes->isChecked() );
983  mRenderer->setLabelFormat( labelFormat, true );
984  mModel->updateLabels();
985 }
986 
987 
989 {
990  QList<QgsSymbolV2*> selectedSymbols;
991 
992  QItemSelectionModel* m = viewGraduated->selectionModel();
993  QModelIndexList selectedIndexes = m->selectedRows( 1 );
994  if ( m && selectedIndexes.size() > 0 )
995  {
996  const QgsRangeList& ranges = mRenderer->ranges();
997  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
998  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
999  {
1000  QStringList list = m->model()->data( *indexIt ).toString().split( " " );
1001  if ( list.size() < 3 )
1002  {
1003  continue;
1004  }
1005 
1006  double lowerBound = list.at( 0 ).toDouble();
1007  double upperBound = list.at( 2 ).toDouble();
1008  QgsSymbolV2* s = findSymbolForRange( lowerBound, upperBound, ranges );
1009  if ( s )
1010  {
1011  selectedSymbols.append( s );
1012  }
1013  }
1014  }
1015  return selectedSymbols;
1016 }
1017 
1018 QgsSymbolV2* QgsGraduatedSymbolRendererV2Widget::findSymbolForRange( double lowerBound, double upperBound, const QgsRangeList& ranges ) const
1019 {
1020  for ( QgsRangeList::const_iterator it = ranges.begin(); it != ranges.end(); ++it )
1021  {
1022  //range string has been created with option 'f',4
1023  if ( qgsDoubleNear( lowerBound, it->lowerValue(), 0.0001 ) && qgsDoubleNear( upperBound, it->upperValue(), 0.0001 ) )
1024  {
1025  return it->symbol();
1026  }
1027  }
1028  return 0;
1029 }
1030 
1032 {
1033  if ( mModel )
1034  {
1036  }
1037 }
1038 
1040 {
1042 }
1043 
1045 {
1046  viewGraduated->selectionModel()->clear();
1047  if ( ! rowsOrdered() )
1048  {
1049  cbxLinkBoundaries->setChecked( false );
1050  }
1051 }
1052 
1054 {
1055 }
1056 
1058 {
1059  if ( !event )
1060  {
1061  return;
1062  }
1063 
1064  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1065  {
1066  mCopyBuffer.clear();
1068  }
1069  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1070  {
1071  QgsRangeList::const_iterator rIt = mCopyBuffer.constBegin();
1072  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1073  {
1074  mModel->addClass( *rIt );
1075  }
1076  }
1077 }
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:85
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:59
bool updateRangeRenderState(int rangeIndex, bool render)
void setSizeScaleField(QString fieldOrExpression)
QgsMapUnitScale mapUnitScale() const
Definition: qgssymbolv2.cpp:90
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
Definition: qgssymbolv2.cpp:69
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)