QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties 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 
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 
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 
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->refresh();
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 
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->refresh();
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->refresh();
913 }
914 
916 {
918  mHistogramWidget->refresh();
919 }
920 
922 {
923  QList<int> classIndexes = selectedClasses();
924  mModel->deleteRows( classIndexes );
925  mHistogramWidget->refresh();
926 }
927 
929 {
931  mHistogramWidget->refresh();
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 {
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  mHistogramWidget->refresh();
1065 }
1066 
1068 {
1070 }
1071 
1073 {
1074  viewGraduated->selectionModel()->clear();
1075  if ( ! rowsOrdered() )
1076  {
1077  cbxLinkBoundaries->setChecked( false );
1078  }
1079 }
1080 
1082 {
1083 }
1084 
1086 {
1087  if ( !event )
1088  {
1089  return;
1090  }
1091 
1092  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1093  {
1094  mCopyBuffer.clear();
1096  }
1097  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1098  {
1100  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1101  {
1102  mModel->addClass( *rIt );
1103  }
1104  }
1105 }
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 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
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
Qt::ItemFlags flags(const QModelIndex &index) const override
QSize size() const
void setColor(const QColor &color)
Utility class for providing GUI for data-defined rendering.
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)
QgsSymbolV2::ScaleMethod scaleMethod() const
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)
virtual QgsVectorColorRampV2 * clone() const =0
QgsRendererRangeV2 rendererRange(const QModelIndex &index)
void beginRemoveRows(const QModelIndex &parent, int first, int last)
QModelIndexList selectedRows(int column) const
int row() const
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)
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.
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
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:351
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)
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)
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