Adding a Shape Key slider

I’m not sure whether this is the right place to post this, if not I apologise. I’ve created a character and have rigged it with Rigify, it’s all working great. I then went on to add a Face rig, which is all working quite nicely too. But what I want to do is add sliders for some of my shape keys, in the same way that the Rigify UI script does with things like the Head control, which gives you the slider in the 3D view for influence over the neck follow. I want to add in a slider to the lip bones to give me control over the roll of the lips. I don’t have any programming knowledge at all, and I don’t really want to spend the time learning stuff I won’t use (I’ve already spent a couple of days trying to figure it out). If I’m honest and if it’s not too difficult or time consuming, would it be possible for someone to have a look and tell me exactly what code i need to draw a slider, which I will then be able to reference in the driver for the shape key. I tried uploading the blend file as an attachment but it doesn’t seem to be letting me so here’s a link, I hope that’s okay:


Again, apologies if I’ve overstepped the mark in my asking, or posted in the wrong forum.

all you have to do is creating / re-using a property like IntProperty or FloatProperty. When added to a panel, these props will show as usual blender value sliders.

As i’m not familiar with drivers, i don’t know if there’s already some kind of slider for drivers or how they can be used. Could you tell me something about it?

I am aware you dont have experiece with Python. You can always ask me for explanation on the code

This Code will add your slider. I created a function called activeFace()

You can just simply delete all the statements inside, And write yours

I wrote them for in case if You would like to access properties to the activeObject on ur Scene

Lastly the Slider will appear on the Toolbox (left of your 3DviewPort. Simply press T and minimize everything)

Code should work on Blender 2.59 until 2.65



import bpy

ob = bpy.context.active_object

#Create Custome Objects's variables. Your Values will be stored here
#These variables are only initialized when u run scripts for 1st time 
bpy.types.Object.custName = bpy.props.StringProperty()
bpy.types.Object.custInt = bpy.props.IntProperty()
ob.custName = ob.name+"'s custname"

class myPanel(bpy.types.Panel):         #To Inherite a class in Blender,go under bpy.types
    bl_space_type="VIEW_3D"            #Blender Edit Area,
    bl_region_type="TOOLS"             
    bl_label = "My Panel"

    def draw(self, context):                 #display textbox, autocalled for Panels
        self.layout.prop(bpy.context.active_object, "custName")
        self.layout.prop(bpy.context.active_object, "custInt")
        #Prepare for the button
        layout = self.layout
        
        #print (type(layout))
        row = layout.row()
        col = row.column()
        
        col.operator("bpt.operator")

    
    class myButton(bpy.types.Operator):
        bl_idname = "bpt.operator"
        bl_label = "Sample Button Caption"
    
        def invoke(self, context,event):
            bl_idname = "bpt.sample_op"
            bl_label = "Button Caption"
            
            global ob
            activeFace(ob)
            return {"FINISHED"}               #Required operators.report enum
                 
      
bpy.utils.register_class(myPanel)
bpy.utils.register_class(myPanel.myButton) 

#****Note you can use this function to do anything you like***#
#Returns the selected Polygon in 3D Scene. Default ob.faces[0]
def activeFace(ob):
    global myPanel
   
    #Select the object in the Scene
    bpy.context.scene.objects.active = ob
    
    #Refresh scene context to receive latest information
    bpy.ops.object.mode_set()
    bpy.ops.object.editmode_toggle()
    
    print("Custm:",ob.custName,"&",ob.custInt, " for ",ob.data.polygons.active)
    
    #Get selected face index from the array 'faces' on the active Mesh object 
    return ob.data.polygons.active
 
activeFace(ob)

Shorter and Neat Version for you



import bpy

#This is your current Selected Object on your Scene
obj = bpy.context.active_object

#Create Custome Objects's variables. Your Values will be stored here
#These variables are only initialized when u run scripts for 1st time 
bpy.types.Object.custInt = bpy.props.IntProperty()
bpy.types.Object.custFloat = bpy.props.FloatProperty()

class myPanel(bpy.types.Panel):         #To Inherite a class in Blender,go under bpy.types
    bl_space_type="VIEW_3D"            #Blender Edit Area,
    bl_region_type="TOOLS"             
    bl_label = "My Panel"

    def draw(self, context):                 #display textbox, autocalled for Panels
        self.layout.prop(bpy.context.active_object, "custFloat")
        self.layout.prop(bpy.context.active_object, "custInt")
        
        #Prepare for the button
        layout = self.layout
        row = layout.row()
        col = row.column()
        col.operator("bpt.operator")

    class myButton(bpy.types.Operator):
        bl_idname = "bpt.operator"
        bl_label = "Sample Button Caption"
    
        def invoke(self, context,event):
            bl_idname = "bpt.sample_op"
            bl_label = "Button Caption"
            
            myFunction(obj)
            return {"FINISHED"}               #Required operators.report enum
                 
                      
bpy.utils.register_class(myPanel)
bpy.utils.register_class(myPanel.myButton) 

#****Note you can use this function to do anything you like***#
#Returns the selected Polygon in 3D Scene. Default ob.faces[0]
def myFunction(obj):
    global myPanel
    
    custInt = obj.custInt
    custFloat = obj.custFloat 
    print ("The Variable Values: 
YourInt =", custInt, "YourFloat =",custFloat)
    
 


Thanks for the replies guys, really helpful. I’ve played around with your script,??? and I’ve managed to get it quite close to the way I want it to work. I moved the panel up to the 3d views property panel. I’ve taken the button away for now but I might add it in again to use as a reset button for the sliders. I also added the drivers to my shape keys. I have two shape keys for each lip, one in and one out. So by referencing the properties that the sliders create to drive the keys I’ve been able to consolidate the two keys in to one slider. If you’ll allow me to take up your time for a little bit longer, there’s just a couple more things I’d like to fix.Firstly - the sliders are there constantly, it would be good if they only appeared when either of the lip controllers were selected.
Secondly - is it possible to limit the value on the sliders so they only go from -1 to 1 with a default value of 0.
Thirdly - The final and less important thing is that it would be nice if these sliders appeared in the ‘Rig Main Properties’ panel that Rigify has set up, is that possible.
Also, I’m not sure what some of the code means, mainly from bpy.utils.register_class(myPanel) onwards, a little explanation would help me greatly.

Thanks again for your continued help, it’s a big bonus to get this rig nice and tidy. I will be throwing it up for download once I’ve got it all sorted.
Here’s the link to the new file in case anyone wants a look:


And here’s the code as I have it at the minute, just to make it easier:


import bpy

#This is your current Selected Object on your Scene
obj = bpy.data.objects[‘Sally’]
selbone = bpy.context.active_bone

#Create Custome Objects’s variables. Your Values will be stored here
#These variables are only initialized when u run scripts for 1st time
bpy.types.Object.upperliproll = bpy.props.FloatProperty()
bpy.types.Object.lowerliproll = bpy.props.FloatProperty()

class myPanel(bpy.types.Panel):
bl_space_type=“VIEW_3D”
bl_region_type=“UI”
bl_label = “Lip Roll”

def draw(self, context):                 
    self.layout.prop(bpy.context.active_object, "upperliproll", text="Upper Lip Roll")                
    self.layout.prop(bpy.context.active_object, "lowerliproll", text="Lower Lip Roll")

bpy.utils.register_class(myPanel)

#*Note you can use this function to do anything you like#
#Returns the selected Polygon in 3D Scene. Default ob.faces[0]
def myFunction(obj):
global myPanel

upperliproll = obj.upperliproll
lowerliproll = obj.lowerliproll 
print ("The Variable Values: 

UpLipRoll =", upperliproll, “LoLipOut =”,lowerliproll)

Hi

Nice model.

I’d be tempted to set up the lip roll driver on the rotation x of the lip controllers.

If you are going to use custom properties then I’d suggest they be on the pose bone rather than the Object.

The properties can be set up with min / max / default etc


bpy.types.PoseBone.upperliproll = bpy.props.FloatProperty(default=0.0,  min=-1.0, max=1.0, description="Amount of Upper Lip Roll")
bpy.types.PoseBone.lowerliproll = bpy.props.FloatProperty(default=0.0,  min=-1.0, max=1.0, description="Amount of Lower Lip Roll")

Only one prop is really needed called lip_roll or somesuch on the appropriate bone.

When properties are set up this way there is a prop added to every object of that type in the whole file. My preferred method is to use ID properties. These are confined to only the object they are set up on. The min / max / default can be set up using the _RNA_UI dictionary.

The following will set up a custom property “liproll” on the “Upperlip” and “Bottomlip” bone (assumes the rig object is selected)


import bpy
C = bpy.context


upperlipbone = C.object.pose.bones["Upperlip"]


upperlipbone["liproll"] = 0.0


rna = dict()
rna["liproll"] = {"name": "Upper Lip Roll",
                            "min": 0.0,
                            "max": 1.0,
                            "description": "Amount of Upper Lip Roll",
                            "soft_min": 0.0,
                            "soft_max": 1.0,
                            "anythingyoubloodylike": True}


# yep you can have anything you like here as long as its an int, float, string, bool


upperlipbone["_RNA_UI"] = rna


print(upperlipbone["_RNA_UI"]["liproll"]["anythingyoubloodylike"])




# do the same for bottom lip


lipbone = C.object.pose.bones["Bottomlip"]


lipbone["liproll"] = 0.0


rna["liproll"]["name"] = "Bottom Lip Roll"
rna["liproll"]["description"] = "Amount of Bottom Lip Roll"


lipbone["_RNA_UI"] = rna



When using ID props note the data_path needs to be in the form ‘[“property”]’ , this also applies for driver targets etc.

To add to the rigify UI panel . Add this code snippet to the bottom of the RigUI draw ethod in sally_rig_ui.py This is for the props set up as ID props as above.


        lips = ["Upperlip", "Bottomlip"]
        
        if is_selected(lips):
           toplip = pose_bones["Upperlip"]
           bottomlip = pose_bones["Bottomlip"]
           layout.prop(toplip, '["liproll"]', text="Top Lip Roll", slider=True) 
           layout.prop(bottomlip, '["liproll"]', text="Bottom Lip Roll", slider=True) 

Or similarly if you wish to use the the first method


layout.prop(toplip, "upperliproll") # etc.

Sorry guys, been away for a couple of weeks. Thanks for all the help. Bat finger, you’re right, I decided that it is better putting the driver on the bone instead of a slider, mostly because if I need to create a pose library of expressions for the face then the lip roll will be included in it, whereas it wouldn’t on the slider. I do however, have another model rigged for this and he’s a robot dog. So I’ll still probably need to add in a couple of sliders for him, this will all be useful when I start that. Thanks again for all the help.