QGIS API Documentation  2.7.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] < to ) 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  cboGraduatedColorRamp->populate( mStyle );
401 
402  spinPrecision->setMinimum( QgsRendererRangeV2LabelFormat::MinPrecision );
403  spinPrecision->setMaximum( QgsRendererRangeV2LabelFormat::MaxPrecision );
404 
405  // set project default color ramp
406  QString defaultColorRamp = QgsProject::instance()->readEntry( "DefaultStyles", "/ColorRamp", "" );
407  if ( defaultColorRamp != "" )
408  {
409  int index = cboGraduatedColorRamp->findText( defaultColorRamp, Qt::MatchCaseSensitive );
410  if ( index >= 0 )
411  cboGraduatedColorRamp->setCurrentIndex( index );
412  }
413 
414 
415  viewGraduated->setStyle( new QgsGraduatedSymbolRendererV2ViewStyle( viewGraduated->style() ) );
416 
418 
419  connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( graduatedColumnChanged( QString ) ) );
420  connect( viewGraduated, SIGNAL( doubleClicked( const QModelIndex & ) ), this, SLOT( rangesDoubleClicked( const QModelIndex & ) ) );
421  connect( viewGraduated, SIGNAL( clicked( const QModelIndex & ) ), this, SLOT( rangesClicked( const QModelIndex & ) ) );
422  connect( viewGraduated, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
423 
424  connect( btnGraduatedClassify, SIGNAL( clicked() ), this, SLOT( classifyGraduated() ) );
425  connect( btnChangeGraduatedSymbol, SIGNAL( clicked() ), this, SLOT( changeGraduatedSymbol() ) );
426  connect( btnGraduatedDelete, SIGNAL( clicked() ), this, SLOT( deleteClasses() ) );
427  connect( btnDeleteAllClasses, SIGNAL( clicked() ), this, SLOT( deleteAllClasses() ) );
428  connect( btnGraduatedAdd, SIGNAL( clicked() ), this, SLOT( addClass() ) );
429  connect( cbxLinkBoundaries, SIGNAL( toggled( bool ) ), this, SLOT( toggleBoundariesLink( bool ) ) );
430 
432 
433  // initialize from previously set renderer
435 
436  // menus for data-defined rotation/size
437  QMenu* advMenu = new QMenu;
438 
439  advMenu->addAction( tr( "Symbol levels..." ), this, SLOT( showSymbolLevels() ) );
440 
443  connect( mDataDefinedMenus, SIGNAL( rotationFieldChanged( QString ) ), this, SLOT( rotationFieldChanged( QString ) ) );
444  connect( mDataDefinedMenus, SIGNAL( sizeScaleFieldChanged( QString ) ), this, SLOT( sizeScaleFieldChanged( QString ) ) );
446  btnAdvanced->setMenu( advMenu );
447 }
448 
450 {
451  delete mRenderer;
452  delete mModel;
453 }
454 
456 {
457  return mRenderer;
458 }
459 
460 // Connect/disconnect event handlers which trigger updating renderer
461 
463 {
464  connect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ), this, SLOT( classifyGraduated() ) );
465  connect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classifyGraduated() ) );
466  connect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( reapplyColorRamp() ) );
467  connect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( reapplyColorRamp() ) );
468  connect( spinPrecision, SIGNAL( valueChanged( int ) ), this, SLOT( labelFormatChanged() ) );
469  connect( cbxTrimTrailingZeroes, SIGNAL( toggled( bool ) ), this, SLOT( labelFormatChanged() ) );
470  connect( txtFormat, SIGNAL( textChanged( QString ) ), this, SLOT( labelFormatChanged() ) );
471 
472  connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
473  connect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( modelDataChanged() ) );
474 }
475 
476 // Connect/disconnect event handlers which trigger updating renderer
477 
479 {
480  disconnect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ), this, SLOT( classifyGraduated() ) );
481  disconnect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classifyGraduated() ) );
482  disconnect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ), this, SLOT( reapplyColorRamp() ) );
483  disconnect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ), this, SLOT( reapplyColorRamp() ) );
484  disconnect( spinPrecision, SIGNAL( valueChanged( int ) ), this, SLOT( labelFormatChanged() ) );
485  disconnect( cbxTrimTrailingZeroes, SIGNAL( toggled( bool ) ), this, SLOT( labelFormatChanged() ) );
486  disconnect( txtFormat, SIGNAL( textChanged( QString ) ), this, SLOT( labelFormatChanged() ) );
487 
488  disconnect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
489  disconnect( mModel, SIGNAL( dataChanged( QModelIndex, QModelIndex ) ), this, SLOT( modelDataChanged() ) );
490 }
491 
493 {
495 
497 
498  // update UI from the graduated renderer (update combo boxes, view)
499  if ( mRenderer->mode() < cboGraduatedMode->count() )
500  cboGraduatedMode->setCurrentIndex( mRenderer->mode() );
501 
502  // Only update class count if different - otherwise typing value gets very messy
503  int nclasses = mRenderer->ranges().count();
504  if ( nclasses && updateCount )
505  spinGraduatedClasses->setValue( mRenderer->ranges().count() );
506 
507  // set column
508  QString attrName = mRenderer->classAttribute();
509  mExpressionWidget->setField( attrName );
510 
511  // set source symbol
512  if ( mRenderer->sourceSymbol() )
513  {
514  delete mGraduatedSymbol;
517  }
518 
519  // set source color ramp
520  if ( mRenderer->sourceColorRamp() )
521  {
522  cboGraduatedColorRamp->setSourceColorRamp( mRenderer->sourceColorRamp() );
523  cbxInvertedColorRamp->setChecked( mRenderer->invertedColorRamp() );
524  }
525 
527  txtFormat->setText( labelFormat.format() );
528  spinPrecision->setValue( labelFormat.precision() );
529  cbxTrimTrailingZeroes->setChecked( labelFormat.trimTrailingZeroes() );
530 
532  viewGraduated->setModel( mModel );
533  viewGraduated->resizeColumnToContents( 0 );
534  viewGraduated->resizeColumnToContents( 1 );
535  viewGraduated->resizeColumnToContents( 2 );
536 
538 }
539 
541 {
542  mRenderer->setClassAttribute( field );
543 }
544 
546 {
547  QString attrName = mExpressionWidget->currentField();
548 
549  int nclasses = spinGraduatedClasses->value();
550 
551  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
552 
553  if ( ramp == NULL )
554  {
555  if ( cboGraduatedColorRamp->count() == 0 )
556  QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
557  else
558  QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
559  return;
560  }
561 
563  if ( cboGraduatedMode->currentIndex() == 0 )
565  else if ( cboGraduatedMode->currentIndex() == 2 )
567  else if ( cboGraduatedMode->currentIndex() == 3 )
569  else if ( cboGraduatedMode->currentIndex() == 4 )
571  else // default should be quantile for now
573 
574 
575  // Jenks is n^2 complexity, warn for big dataset (more than 50k records)
576  // and give the user the chance to cancel
578  && mLayer->featureCount() > 50000 )
579  {
580  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 ) ) return;
581  }
582 
583  // create and set new renderer
584 
585  mRenderer->setClassAttribute( attrName );
586  mRenderer->setMode( mode );
587  mRenderer->setSourceColorRamp( ramp->clone() );
588  QApplication::setOverrideCursor( Qt::WaitCursor );
589  mRenderer->updateClasses( mLayer, mode, nclasses );
591  QApplication::restoreOverrideCursor();
592  // PrettyBreaks and StdDev calculation don't generate exact
593  // number of classes - leave user interface unchanged for these
594  updateUiFromRenderer( false );
595 }
596 
598 {
599  QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
600  if ( ramp == NULL )
601  return;
602 
603  mRenderer->updateColorRamp( ramp, cbxInvertedColorRamp->isChecked() );
605 }
606 
608 {
609  // Change the selected symbols alone if anything is selected
610  QItemSelectionModel* m = viewGraduated->selectionModel();
611  QModelIndexList i = m->selectedRows();
612  if ( m && i.size() > 0 )
613  {
615  return;
616  }
617 
618  // Otherwise change the base mGraduatedSymbol
619  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
620 
621  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
622  if ( !dlg.exec() )
623  {
624  delete newSymbol;
625  return;
626  }
627 
628  mGraduatedSymbol = newSymbol;
629 
633 }
634 
636 {
637  QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mGraduatedSymbol, btnChangeGraduatedSymbol->iconSize() );
638  btnChangeGraduatedSymbol->setIcon( icon );
639 }
640 
641 #if 0
642 int QgsRendererV2PropertiesDialog::currentRangeRow()
643 {
644  QModelIndex idx = viewGraduated->selectionModel()->currentIndex();
645  if ( !idx.isValid() )
646  return -1;
647  return idx.row();
648 }
649 #endif
650 
652 {
653  QList<int> rows;
654  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
655 
656  foreach ( QModelIndex r, selectedRows )
657  {
658  if ( r.isValid() )
659  {
660  rows.append( r.row() );
661  }
662  }
663  return rows;
664 }
665 
667 {
669  QModelIndexList selectedRows = viewGraduated->selectionModel()->selectedRows();
670  QModelIndexList::const_iterator sIt = selectedRows.constBegin();
671 
672  for ( ; sIt != selectedRows.constEnd(); ++sIt )
673  {
674  selectedRanges.append( mModel->rendererRange( *sIt ) );
675  }
676  return selectedRanges;
677 }
678 
680 {
681  if ( idx.isValid() && idx.column() == 0 )
682  changeRangeSymbol( idx.row() );
683  if ( idx.isValid() && idx.column() == 1 )
684  changeRange( idx.row() );
685 }
686 
688 {
689  if ( !idx.isValid() )
690  mRowSelected = -1;
691  else
692  mRowSelected = idx.row();
693 }
694 
696 {
697  QItemSelectionModel* m = viewGraduated->selectionModel();
698  QModelIndexList selectedIndexes = m->selectedRows( 1 );
699  if ( m && selectedIndexes.size() > 0 )
700  {
701  QgsSymbolV2* newSymbol = mGraduatedSymbol->clone();
702  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
703  if ( !dlg.exec() )
704  {
705  delete newSymbol;
706  return;
707  }
708 
709  foreach ( QModelIndex idx, selectedIndexes )
710  {
711  if ( idx.isValid() )
712  {
713  int rangeIdx = idx.row();
714  QgsSymbolV2* newRangeSymbol = newSymbol->clone();
715  newRangeSymbol->setColor( mRenderer->ranges()[rangeIdx].symbol()->color() );
716  mRenderer->updateRangeSymbol( rangeIdx, newRangeSymbol );
717  }
718  }
719  }
721 }
722 
724 {
725  QgsSymbolV2* newSymbol = mRenderer->ranges()[rangeIdx].symbol()->clone();
726 
727  QgsSymbolV2SelectorDialog dlg( newSymbol, mStyle, mLayer, this );
728  if ( !dlg.exec() )
729  {
730  delete newSymbol;
731  return;
732  }
733 
734  mRenderer->updateRangeSymbol( rangeIdx, newSymbol );
735 }
736 
738 {
739  QgsLUDialog dialog( this );
740 
741  const QgsRendererRangeV2& range = mRenderer->ranges()[rangeIdx];
742  // Add arbitrary 2 to number of decimal places to retain a bit extra.
743  // Ensures users can see if legend is not completely honest!
744  int decimalPlaces = mRenderer->labelFormat().precision() + 2;
745  if ( decimalPlaces < 0 ) decimalPlaces = 0;
746  dialog.setLowerValue( QString::number( range.lowerValue(), 'f', decimalPlaces ) );
747  dialog.setUpperValue( QString::number( range.upperValue(), 'f', decimalPlaces ) );
748 
749  if ( dialog.exec() == QDialog::Accepted )
750  {
751  double lowerValue = dialog.lowerValue().toDouble();
752  double upperValue = dialog.upperValue().toDouble();
753  mRenderer->updateRangeUpperValue( rangeIdx, upperValue );
754  mRenderer->updateRangeLowerValue( rangeIdx, lowerValue );
755 
756  //If the boundaries have to stay linked, we update the ranges above and below, as well as their label if needed
757  if ( cbxLinkBoundaries->isChecked() )
758  {
759  if ( rangeIdx > 0 )
760  {
761  mRenderer->updateRangeUpperValue( rangeIdx - 1, lowerValue );
762  }
763 
764  if ( rangeIdx < mRenderer->ranges().size() - 1 )
765  {
766  mRenderer->updateRangeLowerValue( rangeIdx + 1, upperValue );
767  }
768  }
769  }
770 }
771 
773 {
775 }
776 
778 {
779  QList<int> classIndexes = selectedClasses();
780  mModel->deleteRows( classIndexes );
781 }
782 
784 {
786 }
787 
789 {
790  const QgsRangeList &ranges = mRenderer->ranges();
791  bool ordered = true;
792  for ( int i = 1;i < ranges.size();++i )
793  {
794  if ( ranges[i] < ranges[i-1] )
795  {
796  ordered = false;
797  break;
798  }
799  }
800  return ordered;
801 }
802 
804 {
805  //If the checkbox controlling the link between boundaries was unchecked and we check it, we have to link the boundaries
806  //This is done by updating all lower ranges to the upper value of the range above
807  if ( linked )
808  {
809  if ( ! rowsOrdered() )
810  {
811  int result = QMessageBox::warning(
812  this,
813  tr( "Linked range warning" ),
814  tr( "Rows will be reordered before linking boundaries. Continue?" ),
815  QMessageBox::Ok | QMessageBox::Cancel );
816  if ( result != QMessageBox::Ok )
817  {
818  cbxLinkBoundaries->setChecked( false );
819  return;
820  }
822  }
823 
824  // Ok to proceed
825  for ( int i = 1;i < mRenderer->ranges().size();++i )
826  {
827  mRenderer->updateRangeLowerValue( i, mRenderer->ranges()[i-1].upperValue() );
828  }
830  }
831 }
832 
834 {
835  if ( item->column() == 2 )
836  {
837  QString label = item->text();
838  int idx = item->row();
839  mRenderer->updateRangeLabel( idx, label );
840  }
841 }
842 
844 {
845  mRenderer->setRotationField( fldName );
846 }
847 
849 {
850  mRenderer->setSizeScaleField( fldName );
851 }
852 
854 {
855  mRenderer->setScaleMethod( scaleMethod );
856 }
857 
859 {
861  txtFormat->text(),
862  spinPrecision->value(),
863  cbxTrimTrailingZeroes->isChecked() );
864  mRenderer->setLabelFormat( labelFormat, true );
865  mModel->updateLabels();
866 }
867 
868 
870 {
871  QList<QgsSymbolV2*> selectedSymbols;
872 
873  QItemSelectionModel* m = viewGraduated->selectionModel();
874  QModelIndexList selectedIndexes = m->selectedRows( 1 );
875  if ( m && selectedIndexes.size() > 0 )
876  {
877  const QgsRangeList& ranges = mRenderer->ranges();
878  QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
879  for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
880  {
881  QStringList list = m->model()->data( *indexIt ).toString().split( " " );
882  if ( list.size() < 3 )
883  {
884  continue;
885  }
886 
887  double lowerBound = list.at( 0 ).toDouble();
888  double upperBound = list.at( 2 ).toDouble();
889  QgsSymbolV2* s = findSymbolForRange( lowerBound, upperBound, ranges );
890  if ( s )
891  {
892  selectedSymbols.append( s );
893  }
894  }
895  }
896  return selectedSymbols;
897 }
898 
899 QgsSymbolV2* QgsGraduatedSymbolRendererV2Widget::findSymbolForRange( double lowerBound, double upperBound, const QgsRangeList& ranges ) const
900 {
901  for ( QgsRangeList::const_iterator it = ranges.begin(); it != ranges.end(); ++it )
902  {
903  //range string has been created with option 'f',4
904  if ( qgsDoubleNear( lowerBound, it->lowerValue(), 0.0001 ) && qgsDoubleNear( upperBound, it->upperValue(), 0.0001 ) )
905  {
906  return it->symbol();
907  }
908  }
909  return 0;
910 }
911 
913 {
914  if ( mModel )
915  {
917  }
918 }
919 
921 {
923 }
924 
926 {
927  viewGraduated->selectionModel()->clear();
928  if ( ! rowsOrdered() )
929  {
930  cbxLinkBoundaries->setChecked( false );
931  }
932 }
933 
935 {
936 }
937 
939 {
940  if ( !event )
941  {
942  return;
943  }
944 
945  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
946  {
947  mCopyBuffer.clear();
949  }
950  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
951  {
952  QgsRangeList::const_iterator rIt = mCopyBuffer.constBegin();
953  for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
954  {
955  mModel->addClass( *rIt );
956  }
957  }
958 }
void addClass()
Adds a class manually to the classification.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
void showSymbolLevelsDialog(QgsFeatureRendererV2 *r)
show a dialog with renderer's symbol level settings
QList< QgsRendererRangeV2 > QgsRangeList
static unsigned index
QVariant data(const QModelIndex &index, int role) const
void sort(int column, Qt::SortOrder order=Qt::AscendingOrder)
void setLabelFormat(const QgsRendererRangeV2LabelFormat &labelFormat, bool updateRanges=false)
Set the label format used to generate default classification labels.
QString rotationField() const
return rotation field name (or empty string if not set or not supported by renderer) ...
int rowCount(const QModelIndex &parent=QModelIndex()) const
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 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 QgsSymbolV2 * clone() const =0
virtual QgsFeatureRendererV2 * renderer()
return pointer to the renderer (no transfer of ownership)
bool updateRangeRenderState(int rangeIndex, bool render)
void setSizeScaleField(QString fieldOrExpression)
QList< QgsSymbolV2 * > selectedSymbols()
Subclasses may provide the capability of changing multiple symbols at once by implementing the follow...
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.
void scaleMethodChanged(QgsSymbolV2::ScaleMethod scaleMethod)
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
static QIcon icon(QString icon)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:325
void setRenderer(QgsGraduatedSymbolRendererV2 *renderer)
int columnCount(const QModelIndex &=QModelIndex()) const
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
QVariant headerData(int section, Qt::Orientation orientation, int role) const
QgsSymbolV2 * findSymbolForRange(double lowerBound, double upperBound, const QgsRangeList &ranges) const
void setRotationField(QString fieldOrExpression)
sets rotation field of renderer (if supported by the renderer)
void setColor(const QColor &color)
Utility class for providing GUI for data-defined rendering.
static QIcon symbolPreviewIcon(QgsSymbolV2 *symbol, QSize size)
QgsVectorLayer * mLayer
QgsSymbolV2::ScaleMethod scaleMethod() const
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
virtual QgsVectorColorRampV2 * clone() const =0
QgsRendererRangeV2 rendererRange(const QModelIndex &index)
QModelIndex parent(const QModelIndex &index) const
QgsSymbolV2 * symbol() const
bool updateRangeLowerValue(int rangeIndex, double value)
QGis::GeometryType geometryType() const
Returns point, line or polygon.
static QgsRendererV2Widget * create(QgsVectorLayer *layer, QgsStyleV2 *style, QgsFeatureRendererV2 *renderer)
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.
void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget=0) const
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=0) const
static QgsSymbolV2 * defaultSymbol(QGis::GeometryType geomType)
return new default symbol for specified geometry type
Qt::ItemFlags flags(const QModelIndex &index) const
const QgsRangeList & ranges() const
bool setData(const QModelIndex &index, const QVariant &value, int role)
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:362
bool updateRangeSymbol(int rangeIndex, QgsSymbolV2 *symbol)
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
Base class for renderer settings widgets.
bool updateRangeUpperValue(int rangeIndex, double value)
QMimeData * mimeData(const QModelIndexList &indexes) const
Represents a vector layer which manages a vector based data sets.
double size
Definition: qgssvgcache.cpp:77
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)