# What is the best way for me to change multiple paths or values on an object with multiple variables for each one?

I am trying to make an addon that will randomize path variables. The system I have works by adding and subtracting boxes that contain an input for a data path.

It works fine except for one issue. Because I have to create properties for each of the variables (input box, min, max, randomize on render), all of the values are linked, and every input field, max, min, etc. is all the same:

So how would I fix this issue so that I am able to have independent variables that can be easily created or destroyed? Here is my code:

import bpy
from bpy.types import Panel, Operator
from bpy.props import *
import random as r

paths = 0

bl_label = "Add a data path to change"

def execute(self, context):
global paths
paths += 1
self.report({'INFO'}, 'Added path variable')
return{'FINISHED'}

class subtract_data_path(Operator):
bl_idname = "path.subtract"
bl_label = "Get rid of a data path to change"

def execute(self, context):
global paths
if paths > 0:
paths -= 1
self.report({'INFO'}, 'Removed path variable')
return{'FINISHED'}

# Panel
class RANDOMIZER_UI(Panel):

# Create a Panel in the Tool Shelf
bl_label = "Randomizer"
bl_idname = "RANDOMIZER_Manager"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Tool"
bl_options = {"DEFAULT_CLOSED"}

# Custom Properties
text = bpy.props.StringProperty(name="Text:")

# Draw
def draw(self, context):
Mybool = True

layout = self.layout

obj = context.object
row = layout.row()
row.label(text="Path Randomizations:")
row = layout.row()
row.operator("path.subtract", text="-")

i = 0
while i < paths:
# Visible UI
box = layout.box()
row = box.row()
row.use_property_split = True
row.label(text="Path Variable:")
row.prop(context.object, "varpath", text="")
if context.object.varpath != "":
row = box.row(align=True)
row.prop(context.object, "minvar")
row.prop(context.object, "maxvar")
row = box.row(align=True)
row.prop(context.object, "randonrender", text="Randomize On Render?")
# Special Number
i += 1

row = layout.row()

RANDOMIZER_UI(self)

def register():
bpy.utils.register_class(RANDOMIZER_UI)
bpy.utils.register_class(subtract_data_path)

# Registering Variables
bpy.types.Object.varpath = StringProperty(name="Variable Path",
description="Input field for the randomized value")
bpy.types.Object.maxvar = FloatProperty(name="Max Value",
description="The maximum value for the variable path", default=1)
bpy.types.Object.minvar = FloatProperty(name="Min Value",
description="The minimum value for the variable path", default=1)
bpy.types.Object.randonrender = BoolProperty(name="Randomize On Render",
description="Determines whether or not the value is randomized upon rendering an image")

def unregister():
bpy.utils.unregister_class(RANDOMIZER_UI)
bpy.utils.unregister_class(subtract_data_path)

del(bpy.types.Object.varpath)
del(bpy.types.Object.maxvar)
del(bpy.types.Object.minvar)
del(bpy.types.Object.randonrender)

if __name__ == "__main__":
register()
$$$$

• Sorry, I just updated what I meant with a much more precise and detailed response. Try looking at it now. Dec 12 '19 at 0:14
• @AndrewCombs what project is this for? Why do you need paths? How will this be helpful to future users? The last one is arguably less important but still something that this question is lacking. Dec 12 '19 at 0:42
• @TheGreatDuck This is a personal project to generate hyper-realistic datasets for ML algorithms without having to take pictures and or mark points by hand. particularly, right now I am working on a full body tracking system and now need the dataset. I need the paths for the different integers so that I can specify different values that will be randomly changed (why there is a min, max, and randomize on render). This isnt necessarily made to be a commercial project, but I am willing to release it if necessary. Hope that clears things up. Dec 12 '19 at 1:27
• @AndrewCombs now just take that and integrate it into the body of your question. Then congradulations you wrote a semi-decent question. Dec 12 '19 at 1:38

Use a Collection Property

Set up the properties.

The number of paths is made a scene property. When it is updated it adds or removes a node to each scene objects paths collection.

Put all the relevant path node info in a property group.

Add a collection of that type to the object type.

Easiest explained by way of example, run the script below,

import bpy

from bpy.types import PropertyGroup
from bpy.props import (FloatProperty,
CollectionProperty,
IntProperty,
BoolProperty,
)

class PathNode(PropertyGroup):
minvar: FloatProperty()
maxvar: FloatProperty()
randomize: BoolProperty()

while len(ob.paths) > paths:
ob.paths.remove(len(ob.paths) - 1)
while len(ob.paths) < paths:

def update_paths(self, context):
for o in self.objects:

classes = (PathNode,)

def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.paths = IntProperty(
min=0,
soft_max=10,
update=update_paths)
bpy.types.Object.paths = CollectionProperty(type=PathNode)

if __name__ == "__main__":
register()


A draw method, to add to layout code.

    def draw(self, context):
layout = self.layout
scene = context.scene
obj = context.object
layout.prop(scene, "paths")
for node in obj.paths:
row = layout.row()
row.prop(node, "minvar")
row.prop(node, "maxvar")
row.prop(node, "randomize")


Note with scene.paths will need some type of "refresh" operator if a new object is added. A simple test is search for any object whose paths length doesn't match scene.paths

Alternatively the paths int could be associated on a per object basis so as each object can have a variable length collection.

This would involve changing update paths, since self is now an object, not the scene

def update_paths(self, context):


and instead of the scene property for path length, an object property instead.

    bpy.types.Object.num_paths = IntProperty(
min=0,
soft_max=10,
update=update_paths)


and in draw method, the object property rather than the scene.

    layout.prop(obj, "num_paths")


Because of the update method, code below will make sure all objects in scene have 10 paths

for o in scene.objects:
o.num_paths = 10


Documentation for CollectionProperty (CRUD methods)

Collections can also be laid out with bpy.types.UIList` akin to materials and vertex groups.

Create an interface which is similar to the material list box