How 2 pass an object's custom prop 2 a function

Yes, it’s me again:yes:.

This time I have questions only very looseley (if at all) related 2 what the other thread was about, so I dare open up a new one.

Here’s the story:
I have set up an EnumProp (on scene level) which is being drawn (as popup-menu) into a panel. Now the update-function thereof is meant to cause changes to what else is displayed in said panel.
More preciseley spoken, the panel’s draw-function is meant to display one out of 16 existing custom FloatProps of an armature, depending on which of the 16 options was chosen in the popup-menu.

The puropse of all this is basically as follows:
[1.]User chooses one out of 16 options. ->
[2.]EnumProp’s update-function shows corresponding mesh (out of 16 choosable ones) & hides all other 15.->
[3.]Panel shows corresponding custom FloatProp of an armature beneath the popup-menu->
[4.]User changes value of the currently displayed custom FloatProp (see [3.])->
[5.]Custom FloatProp changes rendering opacity of shown mesh (see [2.]) via driver

And before you ask, no, I can’t just use the same custom FloatProp 4 all 16 drivers.
The whole UI described will eventually appear twice and is meant to facilitate crossfades between 2 out of said 16 meshes. So if both meshes’ drivers were using the same FloatProp, they couldn’t fade one out while fading the other in. Well, technically, they could, if one of 'em used the “scripted expression”-setting with sth. like “1 - var” put in there.
The real issue is though, it’s simply not predefined from which mesh 2 which other mesh such crossfade would occur.
Hope I explained this in an understandable way.

So, 2 come back 2 the realm of scripting:
Steps [1.] and [2.] as seen above work out fine (thx again 2 CoDEmanX 4 helping me out on that one already).
But I failed to make the EnumProp’s update-function 2 properly tell the draw-function inside the panel-class, which custom FloatProp of that Armature 2 draw.

What I tried was this:
Declare a global, empty string variable in the beginning, like so:

#blabla...
opacity_key_1 = ""

#blabla...

Then inside said EnumProp’s update-function, this var gets assigned the snippet which identifies the needed custom FloatProp of that armature, like so (the 2nd line after the if-statement):

#blabla...
def update_mouth_key_1_options(self, context):
    
    hide=True
    obs2hide=mesh_mouth_all
    hideObjects(context, obs2hide, hide)
    hide=False
    if self.mouth_key_1_options == '1':
        obs2hide=mesh_mouth_agonizing
        opacity_key_1="\'[\"Opacity Agonizing\"]\'"
        print("opacity_key_1 = %s"%(opacity_key_1))

#etc., blabla...

The print-comand tells me, it’s working as expected till there. But seems like I can’t just put in the variable as argument inside my draw-function, like so (last line before the placeholder-comment):

#blabla...
class VIEW3D_PT_face_extras_mouth(bpy.types.Panel):
    bl_idname="view3d.face_extras_mouth"
    bl_label = "Face Extras: Mouth"
    bl_space_type="VIEW_3D"
    bl_region_type="UI"

    def draw(self,context):
        layout=self.layout
        sce = context.scene
        fem = sce.face_extras_mouth
        cop = bpy.data.objects["[Head|Face|Mouth|Cheeks]"]
        arm = bpy.data.armatures["Armature"]
        ok1 = opacity_key_1
        
        layout.label("Mouthkey 1:")
        box = layout.box()
        box.prop(fem, 'mouth_key_1_options', text="")
        box.prop(arm, "%s"%(opacity_key_1), text="")

#blabla...

It won’t work if I repalce the last line with

 box.prop(arm, opacity_key_1, text="")

neither.

I guess it was a naive idea altogether to store this into a string variable and try to use that inside the draw-function (even though it seems 2 be but a string, if I typed it in manually).

So how 2 make this work? Do I need a PointerProp here? - If so, how do I set this up correctly? Wouldn’t even know which type that’d have 2 be.
Do I need something altogether different?
Or am I just missing the obvious here?

Thank you for having enough patience with me 2 even read this far. Any help would be much appreciated, I doubt I’ll get this figured out by myself (though I promise, I’ll try).

greetings & thx in advance,
Kologe

[1.]User chooses one out of 16 options. ->
[2.]EnumProp’s update-function shows corresponding mesh (out of 16 choosable ones) & hides all other 15.->
[3.]Panel shows corresponding custom FloatProp of an armature beneath the popup-menu->
[4.]User changes value of the currently displayed custom FloatProp (see [3.])->
[5.]Custom FloatProp changes rendering opacity of shown mesh (see [2.]) via driver

  1. What are these 16 options, do they correspond to a fixed set of mesh object names?

  2. Can’t you use scene layers for this?

  3. What does the Float Prop correspond to?
    Is there one bone for every mesh? Do they share names or a naming scheme?
    Is it to be stored on armature level, or on bone level (and if so, which out of the 3 bone type)?

  4. What opacity property is this? Cycles? What is the driver expression (is it a 1:1 mapping)?

Well, it’s like this:

  1. The 16 options do correspond to a fixed set of mesh object names indeed.
    The EnumProps’ keys are for convenience just numbered ‘1’ through ‘16’. So the first one’s identifier-string is ‘1’, the second one’s is ‘2’ etc.
    As far as the name-stings of the items go, they are (in this [alphabetical] order):

‘Agonizing’
‘Angry’
‘Innocent’
‘Mocking’
‘Narrow’
‘Original’
‘Pissed’
‘Sad’
‘Sheep. Wide’
‘Sheepish’
‘Shocked’
‘Smile’
‘Wicked Grin’
‘Wide’
‘Wide Smile’
‘Worried’

(as you may have guessed by now, this has sth. 2 do with facial expressions on my [2D-, thus somewhat uncommon] character-rig.)

Each of them (the EnumProp-items) corresponds to a distinct, fixed set of meshes. Though the exact number of meshes associated with each item may vary. Each (EnumProp-item) has at least 3 but no more than 4 (associated meshes, that is). So if I wrote

[2.]EnumProp’s update-function shows corresponding mesh (out of 16 choosable ones)
, this was a little simplified.
Those meshes do follow a certain naming convention. It looks like this:

“[Head|Face|Mouth|Lip_Lower_[]"
"[Head|Face|Mouth|Lip_Upper_[
]”
“[Head|Face|Mouth|Mouth_[******]”

(without the quotation marks), while the ****** stand for “Agonizing”, “Angry”, “Innocent” ( just as those EnumProp items are named, you get the idea [without the quotation marks here either]).

So the 3 meshes associated with EnumProp-index ‘1’ are named:

“[Head|Face|Mouth|Lip_Lower_[Agonizing]”
“[Head|Face|Mouth|Lip_Upper_[Agonizing]”
“[Head|Face|Mouth|Mouth_[Agonizing]”

Those three are the 3 meshes found associated with each of the EnumProp-inices.
In some cases, there’s also another mesh named like so:

“[Head|Face|Mouth|Tongue_[******]”

This is why all the sets of meshes associated with a certain EnumProp-index consist of at least 3 mesh-objects, while some also have a tongue-mesh.
So it’s like this:

“[Head|Face|Mouth|Lip_Lower_[]"
"[Head|Face|Mouth|Lip_Upper_[
]”
“[Head|Face|Mouth|Mouth_[]"
"[Head|Face|Mouth|Tongue_[
]”

while the last name listed (“[Head|Face|Mouth|Tongue_[******]”) is only found in some cases.

  1. Guess I could, if you mean weather I could push meshes onto a non-visible layer instead of hiding 'em. Actually I originally only did it this way as I figured out how to hide stuff via python before finding out how to move stuff between layers via python (truth be told, I’d bareley know how to do this right now, as I write this).
    I realize hiding stuff may complicate things in some edge-cases of my complex overall setup, as it’s obviously a boolean value while stuff could be otherwise organized onto 16 distinct hidden layers (each set of meshes it’s own ‘home’-layer, so to speak).
    Though I believe it doesn’t really make a difference? The exact way how certain (non-choosen) meshes are kept from being displayed (and rendered) isn’t really of much relevance for this UI-thing (I believe).

  2. The only purpose of the FloatProp is to drive the opacity property of the associated set of meshes.
    It has nothing to do with bones whatsoever. Not even with the armature I attached the FloatProps to.
    Attaching 'em to the armature was an arbitrary desicion, at bottom. Could have used an empty instead, a camera, whatever can have custom props attached to it.
    First I tried putting 'em on scene-level, but seems the drivers don’t work properly then. They won’t update upon a change of a scene-level FloatProp (no matter weather it’s set up in the UI or via python). You’d have to click the “Update Dependencies”-button after every adjustment of the scene-lvl FloatProp.
    I assume this is some sort of deeply rooted dependency-graph-related limitation, but that’s just my guess.

So I repeat: I’ve attached those FloatProps to the armature for no particular reason. It’s on the actual armature-data-level, not on bone-level nor object-level.

The keys of the 16 FloatProps are, consequently, named:

‘Agonizing’
‘Angry’
‘Innocent’
‘Mocking’
‘Narrow’
‘Original’
‘Pissed’
‘Sad’
‘Sheep Wide’ <-Note this FloatProp-key lacks the ‘.’ found in the corresponding EnumProp-item-name-string*
‘Sheepish’
‘Shocked’
‘Smile’
‘Wicked Grin’
‘Wide’
‘Wide Smile’
‘Worried’

*As you sure know, putting in a ‘.’ there would conflict the blender python-API’s dot-notation for RNA-props.

  1. (not quite 5.;))Yes, you guessed it. The drivers drive the “Value”-Prop of a cycles mixnode. If it’s 1.0, the material is completeley opaque, if it’s 0.0, the output of said mixnode is a transparent shader, thus making the material completely transparent.
    The driver’s mapping is 1:1. It’s set to “Average Value”, takes only the one single default variable “var”, which is set to be the value of the corresponding FloatProp of that armature.
    Each distinct mesh has it’s own unique material, no multible users here. But they only differ in terms of which texture they use. What I wrote about those drivers holds true for each of them.

I’ll attach a little screenshot, which may help give you an idea.
One last thought for now: Effectiveley, I could obviously achive what I described via brute-force condition testing. Like e.g. having the EnumProps’ update-function set a global int variable to a value between 1 & 16, depending on the option chosen & checking for said variable’s value inside the draw-function of the panel-class, to decide which exact FloatProp to draw.
I just felt this seemed very dirty and inelegant and would bloat the code of the draw-function & I thought there had to be a way of directly passing the FloatProp to be drawn at any given time to the draw-function?

Guess that’s it. Thx for trying to understand my problem. You must be about the most helpfull person on all of BA, given how you show up in about every thread in the whole “Python Support”-Forum :slight_smile: (I get the impression).

greetings, Kologe

Attachments


OK, so I tried to make this work the unelegant way, via brute-force condition-testing inside the panel’s draw-function.

After (for reasons completeley above my head, but probably somehow syntax-related) failing to make it work using a global int variable as originally intended, I thought I’d resort to using a scene-level IntProp instead.

The code may be a bit less elegant than what I had in mind originally, this way, but it works just fine.:slight_smile:

So now, (*), I’m almost done writing this little script.
There’s just one more thing left to figure out, which is somewhat similar to my original question/problem in this thread:

For the sake of simplicity and easier understanding let’s pretend I had several custom props attached to the standard-cube.
And let’s assume I had named 'em e.g. “MyCustomProp” & “MyCustomProp2”.

If I then, wrote inside a draw-function of a panel-class, this:

#blabla...
box = layout.box()

for key in bpy.data.objects["Cube"].keys():
    box.prop(bpy.data.objects["Cube"], key, text="")

Why doesn’t this work? Why does this trow errors in the console like so:

rna_uiItemR: property not found: Object.MyCustomProp
rna_uiItemR: property not found: Object.MyCustomProp2

While both custom properties do exist and are called this?

What’s the matter? What am I missing here? (Of course it doesn’t draw the properties to the panel, btw.)

If I would just get this last thing figured out, I’ll be all merry and happy & stuff & will to 99.9% not even bother anyone with stupid python-questions any more, how does that sound?

So if somebody could enlighten me one last time, I’d definiteley be thankfull and could finally wrap this all up & start animating.

greetings & thx in advance,

Kologe

*apart from some minor cleanup work and functionality-polish like adding polls, as well as adding a few trivial lines here & there 2 catch some edge-cases, when different input on different popup-menus in my panels may have conflicting effects

bpy.data.objects[“Cube”].keys() returns the ID properties for object Cube, and no bpy.props properties for bpy.types.Object

the key you supply to .prop() has the bpy.props notation. For ID props, you need to use

[“key”], so ‘["%s"]’ % key

Besten Dank.

It works now. I probably should have realized this myself, already had that premunition it’d be some stupid syntax-related fault I made.
The only strange thing is, though it works fine in effect, it seems it still throws errors behind the scene.
Basically the same as before, though it adapted the synthax accordingly:

rna_uiItemR: property not found: Object.[“_RNA_UI”]
rna_uiItemR: property not found: Object.[“_RNA_UI”]

Well maybe I’ll dare asking two more python-questions, even if I promised not to :slight_smile: :
What’s the reason the following code added to the end of a panel-class-definition doesn’t seem to work out?

@classmethod
        def poll(self, context):
            cxt = bpy.context
            obj = cxt.active_object
            bool = obj.data.layers[3]
            
            return(bool and obj.type == 'ARMATURE' and obj.name == "Armature")

2nd question would be this (mostly out of curiosity, isn’t important after all):
If I have the following function (takes the name of an existing object-group, plus an int >= -1 as parameters, sorted returns list containing the names of the objects in the group specified):

def getGroupObName(grp, int):
    grp_ob_lst=[]
    
    for group in bpy.data.groups:
        if group.name == grp:
            for ob in bpy.data.groups['%s'%group.name].objects:
                grp_ob_lst.append('%s'%ob.name)     
        grp_ob_lst.sort()

    if int == -1:
        return(grp_ob_lst)
    else:
        return(grp_ob_lst[int])

Why does the following function not seem 2 return the specified object when passed a valid group-name and index >= 0?

def getGroupOb(context, grp, int):
    
    obj_names=getGroupObName(grp, int)
    for ob_name in obj_names:
        ob = context.scene.objects.get(ob_name)
        if ob is not None:
            return (ob)

I don’t really need this. Mostly just wondering.

And lastly a question not exactly on the matter of python, but blender’s text-editor rather (probably a realy dumb question):
At some point my cursor suddenly changed from the usual little vertical line to something like an underscore.
From this:
|
To this:
_
Also the behaviour of text-editing changed & made it rather cumbersome & annoying.
Anyway, I must have accidenally hit some shortcut. Works normally in any other .blend. I’ve already searched through all the options in the header’s menus & properties-panel, but couldn’t identify the cause.

So if anyone could tell me how 2 change this back to default behaviour, I’d be pleased to know.

In any case, thx again,
greetings,
Kologe

rna_uiItemR: property not found: Object.[“_RNA_UI”]

I guess you are using dir() or .keys(), so it will return all kinds of properties and even functions is the former case.
_RNA_UI is a property used by the Custom Properties panel/system, you should skip it if you loop over all props of an object.

In most cases, everything that starts with an underscore should be ignored,

[prop for prop in ob.keys() if not prop.startswith(“_”)]

Such variables and functions are hidden in auto-complete, and two underscores means it being a private member - it’s a convention however, pyton doesn’t really protect it from outside access.

What’s the reason the following code added to the end of a panel-class-definition doesn’t seem to work out?

Is it indented like that? Doesn’t look right, should more like:

        @classmethod
        def poll(cls, context):
            ob = context.object

            return (ob is not None and
                    ob.type == 'ARMATURE' and
                    ob.data.name == "Armature" and
                    ob.data.layers[3])

You don’t need such a “get ob names” function:

group = bpy.data.groups.get(grp_name)
# get() will return None if there's no group with the given name
# instead of raising a KeyError. So test here for "group is None: ..."

# returns objects
return sorted(group.objects, key=lambda ob: ob.name)

# returns object names
return sorted(ob.name for ob in group.objects)
return 

At some point my cursor suddenly changed from the usual little vertical line to something like an underscore.

Hit INSERT key again to return to usual cursor.