If you're reading this, we've just migrated servers! If anything looks broken please email dion@thinkmoult.com :)

[Blender] Create your first Blender add-on

124»

Comments

  • @Gorgious said:
    Do you want to be able to modify either of the three parameters and the other two update or Length can be readonly ?

    The length should be readonly, but when the user adjust the N or Center to Center it should see the updated length immedetialy.

    I asked chatGPT, but this code is missing something because it didn't do anything:

    def update_length(self, context):
        self.my_length = self.my_n * self.my_center_to_center_distance
        #bpy.context.area.tag_redraw()
        for area in bpy.context.screen.areas:
            if area.type == 'VIEW_3D':
                area.tag_redraw()
    class DimensionProperties(bpy.types.PropertyGroup):
    
        my_n: bpy.props.IntProperty(default=1,name="N", step=1, min=2, max=100)
        my_height: bpy.props.FloatProperty(default=0.1, min=1, max=100, name="Height")
        my_center_to_center_distance: bpy.props.FloatProperty(default=0.01, min=0.01, max=100, name="Center to Center")
    
        my_length: bpy.props.StringProperty(name="Length", update=update_length)
    
        my_profile_x: bpy.props.FloatProperty(default=0.1, min=1, max=100)
        my_profile_y: bpy.props.FloatProperty(default=0.1, min=0.1, max=100)
    
    
  • edited April 2023

    You have to initialize the callback on my_n and my_center_to_center_distance, not my_length :

    def update_length(self, context):
        self.my_length = str(self.my_n * self.my_center_to_center_distance)  # Don't forget to cast the value since you're using a string.
    
    my_n: bpy.props.IntProperty(default=1,name="N", step=1, min=2, max=100, update=update_length)
    my_center_to_center_distance: bpy.props.FloatProperty(default=0.01, min=0.01, max=100, name="Center to Center", update=update_length)
    my_length: bpy.props.StringProperty(name="Length")
    

    This should work ? BTW I don't think you have to tag the UI for a redraw here, the property changing should cause the UI to redraw.

    Coen
  • @Gorgious said:
    You have to initialize the callback on my_n and my_center_to_center_distance, not my_length :

    def update_length(self, context):
        self.my_length = str(self.my_n * self.my_center_to_center_distance)  # Don't forget to cast the value since you're using a string.
    
    my_n: bpy.props.IntProperty(default=1,name="N", step=1, min=2, max=100, update=update_length)
    my_center_to_center_distance: bpy.props.FloatProperty(default=0.01, min=0.01, max=100, name="Center to Center", update=update_length)
    my_length: bpy.props.StringProperty(name="Length")
    

    This should work ? BTW I don't think you have to tag the UI for a redraw here, the property changing should cause the UI to redraw.

    Thank you, it worked.

    in properties.py

    def update_length(self, context):
        self.my_length = str(self.my_n * round(self.my_center_to_center_distance,2))  # Don't forget to cast the value since you're using a string.
    class DimensionProperties(bpy.types.PropertyGroup):
    
        my_height: bpy.props.FloatProperty(default=0.1, min=1, max=100, name="Height")
        my_n: bpy.props.IntProperty(default=1,name="N", step=1, min=2, max=100, update=update_length)
        my_center_to_center_distance: bpy.props.FloatProperty(default=0.01, min=0.01, max=100, name="Center to Center", update=update_length)
        my_length: bpy.props.StringProperty(name="Length")
    

    in ui.py

    class PANEL_PT_demo(Panel):
        bl_label = 'Panel demo'
        bl_space_type = 'VIEW_3D'
        bl_region_type = "UI"
        bl_category = "Bim"
        #bl_region_type= 'WINDOW'
        #bl_context = 'render'
    
        def draw(self, context):
    
            dimension_properties = context.scene.dimension_properties
    
            layout = self.layout
    
            box = layout.box()
            row = box.row()
            row.prop(dimension_properties, 'my_height')
    
            box.prop(dimension_properties, "my_n")
            box.prop(dimension_properties, "my_center_to_center_distance")
            box.prop(dimension_properties,'my_length',emboss=False)
    

    Don't forget to cast the value since you're using a string.

    I have no idea what you mean by this ?

  • edited April 2023

    Hehe glad it worked :)

    Don't forget to cast the value since you're using a string.

    I have no idea what you mean by this ?

    I mean you're using a StringProperty to hold the value of my_length. It's representing a numerical value so I assume you have a reason to use a String Property instead of a FloatProperty. There are valid reasons to do so, mainly because float values are limited in precision, as you can see in your gif when your change a value the last digits of the length kind of go haywire because of that. Strings can hold an infinite precision (at least in the needed significance range for construction projects).

    Internally it is stored as a string of characters, not as a numerical value. Python is a dynamic language which lets you do a lot of things without worrying too much about the underlying data structure, but you have to be careful about some things. print("2") and print(2) will yield the same result, however print("2" + 2) will raise an error. The interpreter can't assume how you want to compute non-trivial operations. So I'd recommend you to cast the value to the intended data structure when you're dealing with dynamic data types. print("2" + str(2)) will yield "22" when print(float("2") + 2) will yield "4.0"

    Coen

  • Thanks for the help, I am trying to make some experimental add-on in which all the geometry is completed IFC based. Don't have any use case but I'm just exploring possibilities of what is possible and could be practical.

    Gorgious
  • I hope this is a very simple question, but gooling gave me unsatisfying results:
    I have the following add-on :


    The workflow should be as following:
    0) folder icon sets a path
    1) > icon adds an image in Blender, so you could make transformations to the image in Blender, e.g. scaling, rotation and position
    2) + icon saves the path images with transformation as an IfcPropertySet under IfcProject
    3) Load image(s) from IFC loads the images with transformations from the IFC.
    I have constructed the class in operator.py as following:

    class ImageCollectionActions(bpy.types.Operator):
        bl_idname = "image.collection_actions"
        bl_label = "Execute"
        action: bpy.props.EnumProperty(items=(("add",) * 3,("remove",) * 3,),)
    
        index: bpy.props.IntProperty(default=-1)
    
        def execute(self, context):
    
            image_collection = context.scene.image_collection
    
            if self.action == "add":        
                image_item =  image_collection.items.add()  
    
    
            if self.action == "remove":
                image_collection.items.remove(self.index)
    
            #for item in image_collection.items:
    
    
            return {"FINISHED"} 
    

    Now I have another class AddReferenceImage in which I want to get the path of the image stored .

    class AddReferenceImage(bpy.types.Operator):
        """Import Reference Image"""
        bl_idname = "add.referenceimage"
        bl_label = "Add Image"
    
        def execute(self, context):
    
            image_collection    =   context.scene.image_collection 
    

    the ui looks like this:

    class PANEL_PT_demo(Panel):
        bl_label = 'IFC Reference Images'
        bl_space_type = 'VIEW_3D'
        bl_region_type = "UI"
        bl_category = "IFC Reference Images"
    
        def draw(self, context):
    
            image_properties = context.scene.image_properties
    
            layout = self.layout
    
            box = layout.box()
            row = box.row()
    
            box.operator("load.referenceimage")
    
            layout = self.layout
            box = layout.box()
            box.operator("image.collection_actions", text="Add", icon="ADD").action = "add"
    
            image_collection = context.scene.image_collection
            row = layout.row(align=True)
    
            for i, item in enumerate(image_collection.items):
    
                row = box.row(align=True)
                row.prop(item, "image")
                row.operator("add.referenceimage", text="", icon="RIGHTARROW")
                row.operator("store.referenceimage", text="", icon="PLUS")
                op = row.operator("image.collection_actions", text="", icon="REMOVE")
                op.action = "remove"
                op.index = i 
    

    How can I know in AddReferenceImage class which index the user is clicking?
    Is the user clicking on the 0, 1, 2 3, etc. item of the collection?

  • I wasn't able to test right now, but would it work if you just add an index to th AddReferenceImage operator as well? And then the UI would be like:

     for i, item in enumerate(image_collection.items):
    
                row = box.row(align=True)
                row.prop(item, "image")
                op = row.operator("add.referenceimage", text="", icon="RIGHTARROW")
                op.index = i 
                row.operator("store.referenceimage", text="", icon="PLUS")
                op = row.operator("image.collection_actions", text="", icon="REMOVE")
                op.action = "remove"
                op.index = i 
    
    CoenGorgious
  • @bruno_perdigao

    Yes thank you!
    Now I can do in operator.py this:

    class AddReferenceImage(bpy.types.Operator):
        """Import Reference Image"""
        bl_idname = "add.referenceimage"
        bl_label = "Add Image"
    
        index: bpy.props.IntProperty(default=-1)
    
        def execute(self, context):
    
            image_collection    =   context.scene.image_collection
            image_item = image_collection.items[self.index]
    
    bruno_perdigao
  • How would I create environments in Python Blender without python defaulting to the system installed modules?

  • And how would I run unit and ui tests in python for a blender add-on? Also how to build a blender add-on to create a zip in github actions? Can't really find coherent documenation on these issues.

  • Hehe that's unfortunately above my skills. :)

Sign In or Register to comment.