37 #include "qgsexpression.h" 43 #include <QMessageBox> 44 #include <QStandardItemModel> 45 #include <QStandardItem> 48 #include <QFileDialog> 52 QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent ) : QAbstractItemModel( parent )
53 , mMimeFormat( QStringLiteral(
"application/x-qgscategorizedsymbolrendererv2model" ) )
61 beginRemoveRows( QModelIndex(), 0, std::max( mRenderer->categories().size() - 1, 0 ) );
70 beginInsertRows( QModelIndex(), 0, renderer->
categories().size() - 1 );
78 if ( !mRenderer )
return;
79 int idx = mRenderer->categories().size();
80 beginInsertRows( QModelIndex(), idx, idx );
81 mRenderer->addCategory( cat );
92 int row = index.row();
93 if ( row >= catList.size() )
97 return catList.at( row );
101 Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags(
const QModelIndex &index )
const 103 if ( !index.isValid() )
105 return Qt::ItemIsDropEnabled;
108 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
109 if ( index.column() == 1 || index.column() == 2 )
111 flags |= Qt::ItemIsEditable;
116 Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions()
const 118 return Qt::MoveAction;
121 QVariant QgsCategorizedSymbolRendererModel::data(
const QModelIndex &index,
int role )
const 123 if ( !index.isValid() || !mRenderer )
128 if ( role == Qt::CheckStateRole && index.column() == 0 )
130 return category.
renderState() ? Qt::Checked : Qt::Unchecked;
132 else if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
134 switch ( index.column() )
137 return category.
value().toString();
139 return category.
label();
144 else if ( role == Qt::DecorationRole && index.column() == 0 && category.
symbol() )
148 else if ( role == Qt::TextAlignmentRole )
150 return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
152 else if ( role == Qt::EditRole )
154 switch ( index.column() )
157 return category.
value();
159 return category.
label();
168 bool QgsCategorizedSymbolRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
170 if ( !index.isValid() )
173 if ( index.column() == 0 && role == Qt::CheckStateRole )
175 mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
176 emit dataChanged( index, index );
180 if ( role != Qt::EditRole )
183 switch ( index.column() )
189 switch ( mRenderer->categories().value( index.row() ).value().type() )
194 case QVariant::Double:
195 val = value.toDouble();
198 val = value.toString();
201 mRenderer->updateCategoryValue( index.row(), val );
205 mRenderer->updateCategoryLabel( index.row(), value.toString() );
211 emit dataChanged( index, index );
215 QVariant QgsCategorizedSymbolRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const 217 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
220 lst << tr(
"Symbol" ) << tr(
"Value" ) << tr(
"Legend" );
221 return lst.value( section );
226 int QgsCategorizedSymbolRendererModel::rowCount(
const QModelIndex &parent )
const 228 if ( parent.isValid() || !mRenderer )
232 return mRenderer->categories().size();
235 int QgsCategorizedSymbolRendererModel::columnCount(
const QModelIndex &index )
const 241 QModelIndex QgsCategorizedSymbolRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const 243 if ( hasIndex( row, column, parent ) )
245 return createIndex( row, column );
247 return QModelIndex();
250 QModelIndex QgsCategorizedSymbolRendererModel::parent(
const QModelIndex &index )
const 253 return QModelIndex();
256 QStringList QgsCategorizedSymbolRendererModel::mimeTypes()
const 259 types << mMimeFormat;
263 QMimeData *QgsCategorizedSymbolRendererModel::mimeData(
const QModelIndexList &indexes )
const 265 QMimeData *mimeData =
new QMimeData();
266 QByteArray encodedData;
268 QDataStream stream( &encodedData, QIODevice::WriteOnly );
271 Q_FOREACH (
const QModelIndex &index, indexes )
273 if ( !index.isValid() || index.column() != 0 )
276 stream << index.row();
278 mimeData->setData( mMimeFormat, encodedData );
282 bool QgsCategorizedSymbolRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
286 if ( action != Qt::MoveAction )
return true;
288 if ( !data->hasFormat( mMimeFormat ) )
return false;
290 QByteArray encodedData = data->data( mMimeFormat );
291 QDataStream stream( &encodedData, QIODevice::ReadOnly );
294 while ( !stream.atEnd() )
301 int to = parent.row();
304 if ( to == -1 ) to = mRenderer->categories().size();
305 for (
int i = rows.size() - 1; i >= 0; i-- )
307 QgsDebugMsg( QString(
"move %1 to %2" ).arg( rows[i] ).arg( to ) );
310 if ( rows[i] < t ) t--;
311 mRenderer->moveCategory( rows[i], t );
313 for (
int j = 0; j < i; j++ )
315 if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
318 if ( rows[i] < to ) to--;
320 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
325 void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
327 std::sort( rows.begin(), rows.end() );
328 for (
int i = rows.size() - 1; i >= 0; i-- )
330 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
331 mRenderer->deleteCategory( rows[i] );
336 void QgsCategorizedSymbolRendererModel::removeAllRows()
338 beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
339 mRenderer->deleteAllCategories();
343 void QgsCategorizedSymbolRendererModel::sort(
int column, Qt::SortOrder order )
351 mRenderer->sortByValue( order );
353 else if ( column == 2 )
355 mRenderer->sortByLabel( order );
357 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
361 void QgsCategorizedSymbolRendererModel::updateSymbology()
363 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
367 QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QStyle *style )
368 : QProxyStyle( style )
371 void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element,
const QStyleOption *option, QPainter *painter,
const QWidget *widget )
const 373 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
375 QStyleOption opt( *option );
376 opt.rect.setLeft( 0 );
378 opt.rect.setHeight( 0 );
379 if ( widget ) opt.rect.setRight( widget->width() );
380 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
383 QProxyStyle::drawPrimitive( element, option, painter, widget );
411 mOldClassificationAttribute = attrName;
415 this->layout()->setContentsMargins( 0, 0, 0, 0 );
417 mExpressionWidget->setLayer(
mLayer );
420 btnColorRamp->setShowRandomColorRamp(
true );
423 QString defaultColorRamp =
QgsProject::instance()->
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/ColorRamp" ), QLatin1String(
"" ) );
424 if ( !defaultColorRamp.isEmpty() )
426 btnColorRamp->setColorRampFromName( defaultColorRamp );
430 btnColorRamp->setRandomColorRamp();
435 mModel =
new QgsCategorizedSymbolRendererModel(
this );
441 viewCategories->setModel( mModel );
442 viewCategories->resizeColumnToContents( 0 );
443 viewCategories->resizeColumnToContents( 1 );
444 viewCategories->resizeColumnToContents( 2 );
446 viewCategories->setStyle(
new QgsCategorizedSymbolRendererViewStyle( viewCategories->style() ) );
465 QMenu *advMenu =
new QMenu;
469 advMenu->addAction( tr(
"Symbol levels…" ),
this, SLOT(
showSymbolLevels() ) );
472 QAction *actionDdsLegend = advMenu->addAction( tr(
"Data-defined size legend…" ) );
474 connect( actionDdsLegend, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
477 btnAdvanced->setMenu( advMenu );
479 mExpressionWidget->registerExpressionContextGenerator(
this );
501 mExpressionWidget->setField( attrName );
527 if ( !selectedCats.isEmpty() )
538 Q_FOREACH (
int idx, selectedCats )
563 btnChangeCategorizedSymbol->setIcon( icon );
578 if ( idx.isValid() && idx.column() == 0 )
589 symbol = symbol->
clone();
608 int num = values.count();
610 for (
int i = 0; i < num; i++ )
612 QVariant value = values[i];
614 if ( ! value.isNull() )
628 QString attrName = mExpressionWidget->currentField();
630 QList<QVariant> unique_vals;
634 QgsExpression *expression =
new QgsExpression( attrName );
641 expression->prepare( &context );
647 QVariant value = expression->evaluate( &context );
648 if ( unique_vals.contains( value ) )
650 unique_vals << value;
659 if ( unique_vals.size() >= 1000 )
661 int res = QMessageBox::warning(
nullptr, tr(
"Classify Categories" ),
662 tr(
"High number of classes. Classification would yield %1 entries which might not be expected. Continue?" ).arg( unique_vals.size() ),
663 QMessageBox::Ok | QMessageBox::Cancel,
664 QMessageBox::Cancel );
665 if ( res == QMessageBox::Cancel )
672 DlgAddCategories dlg(
mStyle, createDefaultSymbol(), unique_vals,
this );
679 bool deleteExisting =
false;
681 if ( !mOldClassificationAttribute.isEmpty() &&
682 attrName != mOldClassificationAttribute &&
685 int res = QMessageBox::question(
this,
686 tr(
"Delete Classification" ),
687 tr(
"The classification field was changed from '%1' to '%2'.\n" 688 "Should the existing classes be deleted before classification?" )
689 .arg( mOldClassificationAttribute, attrName ),
690 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
691 if ( res == QMessageBox::Cancel )
696 deleteExisting = ( res == QMessageBox::Yes );
700 bool keepExistingColors =
false;
701 if ( !deleteExisting )
704 keepExistingColors = !prevCats.isEmpty();
705 for (
int i = 0; i < cats.size(); ++i )
707 bool contains =
false;
708 QVariant value = cats.at( i ).value();
709 for (
int j = 0; j < prevCats.size() && !contains; ++j )
711 if ( prevCats.at( j ).value() == value )
719 prevCats.append( cats.at( i ) );
724 mOldClassificationAttribute = attrName;
743 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
753 if ( ! keepExistingColors && ramp )
760 if ( !btnColorRamp->isNull() )
764 mModel->updateSymbology();
769 QModelIndex idx = viewCategories->selectionModel()->currentIndex();
770 if ( !idx.isValid() )
778 QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
780 Q_FOREACH (
const QModelIndex &r, selectedRows )
784 rows.append( r.row() );
793 mModel->deleteRows( categoryIndexes );
808 mModel->addCategory( cat );
816 QItemSelectionModel *m = viewCategories->selectionModel();
817 QModelIndexList selectedIndexes = m->selectedRows( 1 );
819 if ( m && !selectedIndexes.isEmpty() )
822 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
823 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
825 int row = ( *indexIt ).row();
829 selectedSymbols.append( s );
840 QItemSelectionModel *m = viewCategories->selectionModel();
841 QModelIndexList selectedIndexes = m->selectedRows( 1 );
843 if ( m && !selectedIndexes.isEmpty() )
845 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
846 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
848 cl.append(
mModel->category( *indexIt ) );
867 viewCategories->selectionModel()->clear();
875 QMessageBox::information(
this, tr(
"Matched Symbols" ),
876 tr(
"Matched %1 categories to symbols." ).arg( matched ) );
880 QMessageBox::warning(
this, tr(
"Matched Symbols" ),
881 tr(
"No categories could be matched to symbols in library." ) );
904 mModel->updateSymbology();
911 QString openFileDir = settings.
value( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
913 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Match to Symbols from File" ), openFileDir,
914 tr(
"XML files (*.xml *XML)" ) );
915 if ( fileName.isEmpty() )
920 QFileInfo openFileInfo( fileName );
921 settings.
setValue( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
924 if ( !importedStyle.
importXml( fileName ) )
926 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ),
927 tr(
"An error occurred while reading file:\n%1" ).arg( importedStyle.
errorString() ) );
934 QMessageBox::information(
this, tr(
"Match to Symbols from File" ),
935 tr(
"Matched %1 categories to symbols from file." ).arg( matched ) );
939 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ),
940 tr(
"No categories could be matched to symbols in file." ) );
944 void QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector(
QgsPanelWidget *container )
953 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget()
962 QItemSelectionModel *m = viewCategories->selectionModel();
963 QModelIndexList i = m->selectedRows();
965 if ( m && !i.isEmpty() )
969 if ( !selectedCats.isEmpty() )
971 Q_FOREACH (
int idx, selectedCats )
974 if ( selectedCats.count() > 1 )
997 if ( event->key() == Qt::Key_C &&
event->modifiers() == Qt::ControlModifier )
1002 else if ( event->key() == Qt::Key_V &&
event->modifiers() == Qt::ControlModifier )
1004 QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
1005 for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1007 mModel->addCategory( *rIt );
1041 void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
int lookupField(const QString &fieldName) const
Look up field's index from the field name.
Wrapper for iterator of features from vector data provider or vector layer.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
void updateColorRamp(QgsColorRamp *ramp)
Update the color ramp used and all symbols colors.
This class is a composition of two QSettings instances:
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsSymbol * symbol() const
QString classAttribute() const
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const override
Calculates a list of unique values contained within an attribute in the layer.
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...
static QgsStyle * defaultStyle()
Returns default application-wide style.
The QgsMapSettings class contains configuration for rendering of the map.
QList< QgsRendererCategory > QgsCategoryList
void setValue(const QString &key, const QVariant &value, const QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend when using data-defined size for marker symbols...
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
return new default symbol for specified geometry type
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
static QIcon symbolPreviewIcon(QgsSymbol *symbol, QSize size, int padding=0)
Returns an icon preview for a color ramp.
QgsFields fields() const override
Returns the list of fields of this layer.
bool importXml(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file...
const QgsCategoryList & categories() const
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setClassAttribute(const QString &attr)
Single scope for storing variables and functions for use within a QgsExpressionContext.
QgsSymbol * sourceSymbol()
Returns the renderer's source symbol, which is the base symbol used for the each categories' symbol b...
bool renderState() const
Returns true if the category is currently enabled and should be rendered.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
static void sortVariantList(QList< QVariant > &list, Qt::SortOrder order)
Sorts the passed list in requested order.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
void setSourceColorRamp(QgsColorRamp *ramp)
Sets the source color ramp.
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.
QgsColorRamp * sourceColorRamp()
Returns the source color ramp, from which each categories' color is derived.
virtual QgsSymbol * clone() const =0
Get a deep copy of this symbol.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QgsProject * instance()
Returns the QgsProject singleton instance.
bool updateCategorySymbol(int catIndex, QgsSymbol *symbol)
static QgsCategorizedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsCategorizedSymbolRenderer from an existing renderer.
QString errorString()
Returns last error from load/save operation.
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
void updateSymbols(QgsSymbol *sym)
Update all the symbols but leave categories and colors.
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.
void setSourceSymbol(QgsSymbol *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each categories' symbo...
QList< QgsExpressionContextScope > additionalExpressionContextScopes() const
Returns the list of additional expression context scopes to show as available within the layer...
void setColor(const QColor &color)