5

QGIS offers the @symbol_color-variable in some places. But it is missing at various places of data-defined-override/field-calculator dialogues.

I tried to create a own function, but it returns wrong, varying values every time the feature is rendered again (I think, because i work with "defaultSymbol...").

How can I get the real color of every feature by a function ?

(In my case I try to use the result of the function as a data-defined color-override in a 3d-map-view with the goal, that 2d-feature color matches 3d-feature-color)

from qgis.core import *
from qgis.gui import *

@qgsfunction(args='auto', group='Custom', referenced_columns=[])
def symbol_color(layername, feature, parent):
    layer = QgsProject.instance().mapLayersByName(layername)[0]
    symbol = QgsSymbol.defaultSymbol(layer.geometryType())     
    return (symbol.color().name())

enter image description here

1 Answer 1

4

The following code gets the colour of the category that the feature is symbolised by (as it appears you are using categorised symbology in your screenshot) and constructs a dictionary of category values and colour codes. You can then loop through your features to get each one's own colour and add it to the attribute table.

lyr = iface.activeLayer()

r = lyr.renderer()

# get the categories from the renderer
cat = r.categories()

# construct a dictionary of category values and their symbol fill colours
colour_dict = {}
for c in cat:
    # get the category value to use as a key in the colour_dict
    category_value = c.value()
    
    # get the category's symbol 
    sym = c.symbol()
    
    # get (first) symbol layer - it may be a different layer, depending how complex your symbology is
    symbol_layer = sym.symbolLayers()[0]
    
    # get fill colour object from the symbol layer
    col = symbol_layer.color()

    # get the hex and rgb representations of the colour object
    hex_code = col.name()
    rgb = col.getRgb()
    
    # populate the colour_dict
    colour_dict[category_value] = {}
    colour_dict[category_value]['hex'] = hex_code
    colour_dict[category_value]['rgb'] = rgb
    
    print('category:', category_value, 'hex:', hex_code, 'rgb:', rgb)

Output:

category: category 1  | hex: #3d31e7  | rgb: (61, 49, 231, 255)
category: category 2  | hex: #e0675e  | rgb: (224, 103, 94, 255)
category: category 3  | hex: #d647cc  | rgb: (214, 71, 204, 255)
category: category 4  | hex: #83e3ec  | rgb: (131, 227, 236, 255)
category: category 5  | hex: #1de72e  | rgb: (29, 231, 46, 255)

Then you can loop through your features and get the colour for each one individually, perhaps add it as an attribute like so:

# the field that the categorised renderer is using
category_field = 'category'

# add a new field to store the colour
field_name = 'colour'

if not lyr.isEditable():
    lyr.startEditing()

# add the colour field
lyr.addAttribute(QgsField(field_name, QVariant.String, len=0))
lyr.updateFields()

# get the index of the new field
fields = lyr.fields()
idx = fields.indexFromName(field_name)

# add the colour of each feature to the new field
for f in lyr.getFeatures():
    # get the feature id
    fid = f.id()

    # get the category value of the feature
    category = f[category_field]

    # get the feature (hex) colour from the colour dictionary
    feature_colour_hex = colour_dict[category]['hex']

    # insert the colour in the field
    lyr.changeAttributeValue(fid, idx, feature_colour_hex)

# save the changes
lyr.commitChanges()

enter image description here

If you are not using a categorised renderer, let me know and I will update my answer accordingly.

4
  • 1
    Thanks. Changed print('category:', category_value, 'hex:', hex, 'rgb:', rgb) to print('category:', category_value, 'hex:', hex_code, 'rgb:', rgb)
    – user191847
    Nov 7, 2022 at 7:24
  • 1
    Good spot. I realised hex was a reserved word in Python so changed it to hex_code but missed that one. I have edited my answer, thanks!
    – Matt
    Nov 7, 2022 at 7:46
  • 2. Got an assertionError at part 2 of your code and 3. new field was not added. Tried changed code: … field_name = 'colour' lyr.startEditing() #removed "with edit(lyr):" lyr_prov=lyr.dataProvider() lyr_prov.addAttributes([QgsField(field_name,QVariant.String, len=0)]) lyr.updateFields() lyr.commitChanges() … but new field was still not added. Any idea?
    – user191847
    Nov 7, 2022 at 8:05
  • The assertion error might be because your layer is already in edit mode before with edit(lyr) is called. I will update answer to make a check.
    – Matt
    Nov 7, 2022 at 8:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.