Turns out that you don't really need msgbus for this. I don't know exactly how this would integrate into your code, so I'm just going to post the necessary code fragments and let you figure out where to put them.
First, you need two custom color properties, attached to anything, but I've attached them to
bpy.types.Scene because that means you can use them anywhere in your scene and each scene will have its own copy.
bpy.types.Scene.mainColor = bpy.props.FloatVectorProperty(
description = 'The primary color',
bpy.types.Scene.secondaryColor = bpy.props.FloatVectorProperty(
name = 'secondary color',
description = 'The secondary color',
You can, of course call them whatever you want. In my example,
mainColor is the color that you would use for both the body and hair, and
secondaryColor is the color for just the hair.
The "sneaky" bit is the last line of the definition of
mainColor: the addition of
update=update_colors to an otherwise typical Color custom property.
This tells bpy that you want a function called
update_colors called each time
mainColor is updated. That function handles resetting
def update_colors(self, context):
context.scene.secondaryColor = context.scene.mainColor
I added a panel class so I could test this, but you could just integrate the two new properties into your existing panel:
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Monkeys'
bl_label = 'Color Pair'
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
For my test, this creates a panel that looks like:
(The panel shows up in the Monkeys tab of the sidebar because I've been following the Scripting for Artists tutorial and that's the tab used in the tutorial.)
Here's the tab showing the two colors being different after I change first the main and then the secondary:
Here's the tab immediately after changing the main color without modifying the secondary.
Having done this, you can use the new custom properties as drivers for RGB inputs in shaders, or anyplace else you want to use them.