44 #include <QMessageBox> 45 #include <QStandardItemModel> 46 #include <QStandardItem> 49 #include <QFileDialog> 53 QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent ) : QAbstractItemModel( parent )
54 , mMimeFormat( QStringLiteral(
"application/x-qgscategorizedsymbolrendererv2model" ) )
62 beginRemoveRows( QModelIndex(), 0, std::max( mRenderer->categories().size() - 1, 0 ) );
71 beginInsertRows( QModelIndex(), 0, renderer->
categories().size() - 1 );
79 if ( !mRenderer )
return;
80 int idx = mRenderer->categories().size();
81 beginInsertRows( QModelIndex(), idx, idx );
82 mRenderer->addCategory( cat );
93 int row = index.row();
94 if ( row >= catList.size() )
98 return catList.at( row );
102 Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags(
const QModelIndex &index )
const 104 if ( !index.isValid() )
106 return Qt::ItemIsDropEnabled;
109 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
110 if ( index.column() == 1 || index.column() == 2 )
112 flags |= Qt::ItemIsEditable;
117 Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions()
const 119 return Qt::MoveAction;
122 QVariant QgsCategorizedSymbolRendererModel::data(
const QModelIndex &index,
int role )
const 124 if ( !index.isValid() || !mRenderer )
129 if ( role == Qt::CheckStateRole && index.column() == 0 )
131 return category.
renderState() ? Qt::Checked : Qt::Unchecked;
133 else if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
135 switch ( index.column() )
138 return category.
value().toString();
140 return category.
label();
145 else if ( role == Qt::DecorationRole && index.column() == 0 && category.
symbol() )
150 else if ( role == Qt::TextAlignmentRole )
152 return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
154 else if ( role == Qt::EditRole )
156 switch ( index.column() )
159 return category.
value();
161 return category.
label();
170 bool QgsCategorizedSymbolRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
172 if ( !index.isValid() )
175 if ( index.column() == 0 && role == Qt::CheckStateRole )
177 mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
178 emit dataChanged( index, index );
182 if ( role != Qt::EditRole )
185 switch ( index.column() )
191 switch ( mRenderer->categories().value( index.row() ).value().type() )
196 case QVariant::Double:
197 val = value.toDouble();
200 val = value.toString();
203 mRenderer->updateCategoryValue( index.row(), val );
207 mRenderer->updateCategoryLabel( index.row(), value.toString() );
213 emit dataChanged( index, index );
217 QVariant QgsCategorizedSymbolRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const 219 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
222 lst << tr(
"Symbol" ) << tr(
"Value" ) << tr(
"Legend" );
223 return lst.value( section );
228 int QgsCategorizedSymbolRendererModel::rowCount(
const QModelIndex &parent )
const 230 if ( parent.isValid() || !mRenderer )
234 return mRenderer->categories().size();
237 int QgsCategorizedSymbolRendererModel::columnCount(
const QModelIndex &index )
const 243 QModelIndex QgsCategorizedSymbolRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const 245 if ( hasIndex( row, column, parent ) )
247 return createIndex( row, column );
249 return QModelIndex();
252 QModelIndex QgsCategorizedSymbolRendererModel::parent(
const QModelIndex &index )
const 255 return QModelIndex();
258 QStringList QgsCategorizedSymbolRendererModel::mimeTypes()
const 261 types << mMimeFormat;
265 QMimeData *QgsCategorizedSymbolRendererModel::mimeData(
const QModelIndexList &indexes )
const 267 QMimeData *mimeData =
new QMimeData();
268 QByteArray encodedData;
270 QDataStream stream( &encodedData, QIODevice::WriteOnly );
273 Q_FOREACH (
const QModelIndex &index, indexes )
275 if ( !index.isValid() || index.column() != 0 )
278 stream << index.row();
280 mimeData->setData( mMimeFormat, encodedData );
284 bool QgsCategorizedSymbolRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
288 if ( action != Qt::MoveAction )
return true;
290 if ( !data->hasFormat( mMimeFormat ) )
return false;
292 QByteArray encodedData = data->data( mMimeFormat );
293 QDataStream stream( &encodedData, QIODevice::ReadOnly );
296 while ( !stream.atEnd() )
303 int to = parent.row();
306 if ( to == -1 ) to = mRenderer->categories().size();
307 for (
int i = rows.size() - 1; i >= 0; i-- )
309 QgsDebugMsg( QStringLiteral(
"move %1 to %2" ).arg( rows[i] ).arg( to ) );
312 if ( rows[i] < t ) t--;
313 mRenderer->moveCategory( rows[i], t );
315 for (
int j = 0; j < i; j++ )
317 if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
320 if ( rows[i] < to ) to--;
322 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
327 void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
329 std::sort( rows.begin(), rows.end() );
330 for (
int i = rows.size() - 1; i >= 0; i-- )
332 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
333 mRenderer->deleteCategory( rows[i] );
338 void QgsCategorizedSymbolRendererModel::removeAllRows()
340 beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
341 mRenderer->deleteAllCategories();
345 void QgsCategorizedSymbolRendererModel::sort(
int column, Qt::SortOrder order )
353 mRenderer->sortByValue( order );
355 else if ( column == 2 )
357 mRenderer->sortByLabel( order );
359 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
362 void QgsCategorizedSymbolRendererModel::updateSymbology()
364 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
368 QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
372 void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element,
const QStyleOption *option, QPainter *painter,
const QWidget *widget )
const 374 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
376 QStyleOption opt( *option );
377 opt.rect.setLeft( 0 );
379 opt.rect.setHeight( 0 );
380 if ( widget ) opt.rect.setRight( widget->width() );
381 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
384 QProxyStyle::drawPrimitive( element, option, painter, widget );
411 QString attrName =
mRenderer->classAttribute();
412 mOldClassificationAttribute = attrName;
416 this->layout()->setContentsMargins( 0, 0, 0, 0 );
418 mExpressionWidget->setLayer(
mLayer );
421 btnColorRamp->setShowRandomColorRamp(
true );
424 QString defaultColorRamp =
QgsProject::instance()->
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/ColorRamp" ), QString() );
425 if ( !defaultColorRamp.isEmpty() )
427 btnColorRamp->setColorRampFromName( defaultColorRamp );
431 btnColorRamp->setRandomColorRamp();
436 mModel =
new QgsCategorizedSymbolRendererModel(
this );
442 viewCategories->setModel(
mModel );
443 viewCategories->resizeColumnToContents( 0 );
444 viewCategories->resizeColumnToContents( 1 );
445 viewCategories->resizeColumnToContents( 2 );
447 viewCategories->setStyle(
new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
466 QMenu *advMenu =
new QMenu;
470 advMenu->addAction( tr(
"Symbol Levels…" ),
this, SLOT(
showSymbolLevels() ) );
473 QAction *actionDdsLegend = advMenu->addAction( tr(
"Data-defined Size Legend…" ) );
475 connect( actionDdsLegend, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
478 btnAdvanced->setMenu( advMenu );
480 mExpressionWidget->registerExpressionContextGenerator(
this );
499 QString attrName =
mRenderer->classAttribute();
500 mExpressionWidget->setField( attrName );
512 btnColorRamp->setColorRamp(
mRenderer->sourceColorRamp() );
525 if ( !selectedCats.isEmpty() )
536 Q_FOREACH (
int idx, selectedCats )
542 mRenderer->updateCategorySymbol( idx, newCatSymbol );
566 if ( !dlg.exec() || !newSymbol )
583 btnChangeCategorizedSymbol->setIcon( icon );
598 if ( idx.isValid() && idx.column() == 0 )
606 std::unique_ptr< QgsSymbol > symbol;
630 if ( !dlg.exec() || !symbol )
645 int num = values.count();
647 for (
int i = 0; i < num; i++ )
649 QVariant value = values[i];
651 if ( ! value.isNull() )
665 QString attrName = mExpressionWidget->currentField();
667 QList<QVariant> unique_vals;
678 expression->
prepare( &context );
684 QVariant value = expression->
evaluate( &context );
685 if ( unique_vals.contains( value ) )
687 unique_vals << value;
696 if ( unique_vals.size() >= 1000 )
698 int res = QMessageBox::warning(
nullptr, tr(
"Classify Categories" ),
699 tr(
"High number of classes. Classification would yield %1 entries which might not be expected. Continue?" ).arg( unique_vals.size() ),
700 QMessageBox::Ok | QMessageBox::Cancel,
701 QMessageBox::Cancel );
702 if ( res == QMessageBox::Cancel )
709 DlgAddCategories dlg(
mStyle, createDefaultSymbol(), unique_vals,
this );
716 bool deleteExisting =
false;
718 if ( !mOldClassificationAttribute.isEmpty() &&
719 attrName != mOldClassificationAttribute &&
722 int res = QMessageBox::question(
this,
723 tr(
"Delete Classification" ),
724 tr(
"The classification field was changed from '%1' to '%2'.\n" 725 "Should the existing classes be deleted before classification?" )
726 .arg( mOldClassificationAttribute, attrName ),
727 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
728 if ( res == QMessageBox::Cancel )
733 deleteExisting = ( res == QMessageBox::Yes );
737 bool keepExistingColors =
false;
738 if ( !deleteExisting )
741 keepExistingColors = !prevCats.isEmpty();
743 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
745 for (
int i = 0; i < cats.size(); ++i )
747 bool contains =
false;
748 QVariant value = cats.at( i ).value();
749 for (
int j = 0; j < prevCats.size() && !contains; ++j )
751 if ( prevCats.at( j ).value() == value )
760 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
763 cats.at( i ).symbol()->setColor( randomColors.
color( i ) );
765 prevCats.append( cats.at( i ) );
771 mOldClassificationAttribute = attrName;
788 std::unique_ptr< QgsCategorizedSymbolRenderer > r = qgis::make_unique< QgsCategorizedSymbolRenderer >( attrName, cats );
790 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
792 r->setSourceColorRamp( ramp->clone() );
796 mModel->setRenderer( r.get() );
799 if ( ! keepExistingColors && ramp )
806 if ( !btnColorRamp->isNull() )
808 mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
810 mModel->updateSymbology();
815 QModelIndex idx = viewCategories->selectionModel()->currentIndex();
816 if ( !idx.isValid() )
824 QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
826 Q_FOREACH (
const QModelIndex &r, selectedRows )
830 rows.append( r.row() );
839 mModel->deleteRows( categoryIndexes );
854 mModel->addCategory( cat );
862 QItemSelectionModel *m = viewCategories->selectionModel();
863 QModelIndexList selectedIndexes = m->selectedRows( 1 );
865 if ( m && !selectedIndexes.isEmpty() )
868 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
869 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
871 int row = ( *indexIt ).row();
875 selectedSymbols.append( s );
886 QItemSelectionModel *m = viewCategories->selectionModel();
887 QModelIndexList selectedIndexes = m->selectedRows( 1 );
889 if ( m && !selectedIndexes.isEmpty() )
891 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
892 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
894 cl.append(
mModel->category( *indexIt ) );
913 viewCategories->selectionModel()->clear();
921 QMessageBox::information(
this, tr(
"Matched Symbols" ),
922 tr(
"Matched %1 categories to symbols." ).arg( matched ) );
926 QMessageBox::warning(
this, tr(
"Matched Symbols" ),
927 tr(
"No categories could be matched to symbols in library." ) );
940 QVariantList unmatchedCategories;
941 QStringList unmatchedSymbols;
942 const int matched =
mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
944 mModel->updateSymbology();
951 QString openFileDir = settings.
value( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
953 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Match to Symbols from File" ), openFileDir,
954 tr(
"XML files (*.xml *.XML)" ) );
955 if ( fileName.isEmpty() )
960 QFileInfo openFileInfo( fileName );
961 settings.
setValue( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
964 if ( !importedStyle.
importXml( fileName ) )
966 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ),
967 tr(
"An error occurred while reading file:\n%1" ).arg( importedStyle.
errorString() ) );
974 QMessageBox::information(
this, tr(
"Match to Symbols from File" ),
975 tr(
"Matched %1 categories to symbols from file." ).arg( matched ) );
979 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ),
980 tr(
"No categories could be matched to symbols in file." ) );
984 void QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector(
QgsPanelWidget *container )
993 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget()
1004 QItemSelectionModel *m = viewCategories->selectionModel();
1005 QModelIndexList i = m->selectedRows();
1007 if ( m && !i.isEmpty() )
1011 if ( !selectedCats.isEmpty() )
1013 Q_FOREACH (
int idx, selectedCats )
1016 if ( selectedCats.count() > 1 )
1021 mRenderer->updateCategorySymbol( idx, newCatSymbol );
1041 if ( event->key() == Qt::Key_C &&
event->modifiers() == Qt::ControlModifier )
1043 mCopyBuffer.clear();
1046 else if ( event->key() == Qt::Key_V &&
event->modifiers() == Qt::ControlModifier )
1048 QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
1049 for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1051 mModel->addCategory( *rIt );
1085 void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
Abstract base class for all rendered symbols.
This class is a composition of two QSettings instances:
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QVariant evaluate()
Evaluate the feature and return the result.
QColor color(double value) const override
Returns the color corresponding to a specified value.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
A QProxyStyle subclass which correctly sets the base style to match the QGIS application style...
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
A marker symbol type, for rendering Point and MultiPoint geometries.
static QgsStyle * defaultStyle()
Returns default application-wide style.
The QgsMapSettings class contains configuration for rendering of the map.
QList< QgsRendererCategory > QgsCategoryList
SymbolType
Type of the symbol.
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
static QIcon symbolPreviewIcon(QgsSymbol *symbol, QSize size, int padding=0)
Returns an icon preview for a color ramp.
bool importXml(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Single scope for storing variables and functions for use within a QgsExpressionContext.
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
Totally random color ramp.
static void sortVariantList(QList< QVariant > &list, Qt::SortOrder order)
Sorts the passed list in requested order.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
static QgsExpressionContextScope * atlasScope(QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object...
QgsExpressionContextScope & expressionContextScope()
Returns a reference to the expression context scope for the map canvas.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
QgsSymbol * symbol() const
Returns the symbol which will be used to render this category.
QVariant value() const
Returns the value corresponding to this category.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
static QgsCategorizedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsCategorizedSymbolRenderer from an existing renderer.
QList< QgsExpressionContextScope > additionalExpressionContextScopes() const
Returns the list of additional expression context scopes to show as available within the layer...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
QString errorString()
Returns last error from load/save operation.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
Represents a vector layer which manages a vector based data sets.
bool renderState() const
Returns true if the category is currently enabled and should be rendered.
void setColor(const QColor &color)
Sets the color for the symbol.