2
$\begingroup$

I'm very new to scripting in blender, and I'm making an addon to simplify the depth of field process in Blender. I would like to put a slider in my ui panel to control the camera's aperture. I think I need to use a .prop function but I'm not sure. Can I have some help with this? Here is the code for my ui panel:

def draw(self, context):
    layout = self.layout
    camera = bpy.data.objects['Camera']
    
    
    row = layout.row()
    row.label(text= "Focus", icon = "CAMERA_DATA")
    row = layout.row()
    row.operator("object.add_focus")
    row = layout.row()
    row.operator("object.remove_focus")
    row = layout.row()
    row.prop(camera, "dof.aperture_fstop", slider=True)

The last line is the one I don't know how to do. Here is what my UI panel looks like so far: enter image description here

Thanks!

$\endgroup$
1
  • 1
    $\begingroup$ FYI: Photographer add-on does this already: blendermarket.com/products/photographer, Nice exercise, though! Just pass the object data and the property layout.prop(context.object.data.dof, "aperture_fstop") or layout.prop(context.object.data, "lens") Also I'd suggest add a poll method to only display the panel only if the selected object's type is 'CAMERA' Recommend against using using the index operator [] -> return context.object.type == 'CAMERA' $\endgroup$
    – brockmann
    Dec 30, 2020 at 22:17

2 Answers 2

3
$\begingroup$

A single property

Think of a property layout akin to python's getattr If we want the value of foo.bar.foo it's getattr(foo.bar, "foo") never getattr(foo, "bar.foo")

Avoid using foo.bar["Foo"]

IMO get out of the habit of using, in this example

camera = bpy.data.objects['Camera']

which will fail (throw a KeyError) if there is no object named camera, which could simply mean its been renamed, or is camera in a different language etc. It could be the camera linked to another scene, or no scene at all... in which case sliding it would appear to do nought.

Use Scene Camera

Suggest in the case of camera, the active camera is assigned to the scene, scene.camera. As long as this is set can use instead. This is the goto camera when pressing Numpad 0 for camera view.

Object Data Properties

With a camera active or pinned, the object of interest in the object data properties tab (camera icon) in the properties panel is its data. In the usual case of it being the context object.

>>> C.object
bpy.data.objects['Camera']

>>> C.object.data
bpy.data.cameras['Camera']

>>> C.object.data.dof.aperture_fstop
44.41624069213867

>>> C.scene.camera
bpy.data.objects['Camera']

Putting it all together

Have added some changes to code below. Without having the operators mentioned in question have replaced with splash screen proxies. Am assuming that the operators use the context object to set the focus object.

Once again use of the scene camera in those operators would make more sense than relying on the camera named "Camera" being the active camera.

A poll method is added to ensure the scene has an active camera. A poll method on your other custom operators could test for active object. If there is no active object and hence they don't poll, the buttons will appear disabled.

A simple way to have layout in rows is to use one column layout.

Test code.

import bpy

class VIEW3D_PT_dof(bpy.types.Panel):
    bl_idname = "VIEW3D_PT_dof"
    bl_label = "Quick Focus"
    bl_category = "DOF"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
 
    @classmethod
    def poll(cls, context):
        return context.scene.camera is not None
    
    def draw_header(self, context):
        layout = self.layout
        layout.label(icon='CAMERA_DATA')
        
    def draw(self, context):
        layout = self.layout
                
        cam = context.scene.camera
        col = layout.column()     
        #col.label(text= "Focus", icon='CAMERA_DATA')
        col.operator("wm.splash")
        col.operator("wm.splash")
        col.prop(cam.data.dof, "aperture_fstop", slider=True)
        

classes = (
        VIEW3D_PT_dof,
        )          
        
def register():
    for cls in classes:
        bpy.utils.register_class(cls)

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)


if __name__ == "__main__":
    register()
$\endgroup$
2
$\begingroup$

you must use properties, please check the current API

please import all libraries you should need.

the class with the panel type and draw method allow create the UI. in the draw method you should call the property.

Properties can be created in multiple places depending on the purpose. In this case I wrote it in the registry attached to the WindowsManager

properties that are numeric are displayed as sliders (floats, integers...)

please see that all properties has many parameters, as "update" in order to execute some code when the property's value change.

STRUCTURE

here code:

import bpy
from bpy.types import Operator
from bpy.props import FloatVectorProperty, IntProperty
from bpy.types import WindowManager



class TEST_PT_test(bpy.types.Panel):
    bl_idname = "TEST_PT_test"
    bl_label = "test"
    bl_category = "TEST"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

    def draw(self, context):
        layout = self.layout
        
        
        wm = context.window_manager
        row = layout.box()     
        
        if  bpy.context.active_object.type == 'CAMERA':
            row.prop(wm, "aperture_fstop")
            
            
        
        
def changeaperturecamera(self, context):
    wm = context.window_manager
    camera = bpy.data.objects['Camera']
    camera.data.dof.aperture_fstop =  wm.aperture_fstop
    
    
        
def register():
    
    WindowManager.aperture_fstop = IntProperty(
        name="aperture_fstop",
        min = 0,
        update = changeaperturecamera
    )
    
    bpy.utils.register_class(TEST_PT_test)


def unregister():

    bpy.utils.unregister_class(TEST_PT_test)
    del WindowManager.aperture_fstop


if __name__ == "__main__":
    register()

also you can send the int property witout any property

layout.prop(context.object.data.dof, "aperture_fstop")

but you must create some filters in order to hidde/show the slider only if a camera is selected

$\endgroup$
3
  • 1
    $\begingroup$ Why this extra WM property? layout.prop(context.object.data.dof, "aperture_fstop"), done. Would recommend a poll method for the panel as well, eg.: return context.object.type == 'CAMERA' $\endgroup$
    – brockmann
    Dec 30, 2020 at 22:57
  • $\begingroup$ @brockmann you right. but he is blender python's newcomer. give some alternatives works fine. right? $\endgroup$
    – yhoyo
    Dec 31, 2020 at 0:00
  • $\begingroup$ agree somewhat, suggest using Scene over WindowManager. In which case self is the scene, self.camera is the camera, and context.object is the target object of interest. Could then be used to replace the operators $\endgroup$
    – batFINGER
    Dec 31, 2020 at 4:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .