2
$\begingroup$

My goal

I'm currently working on an addon. I'm quite new to Python, but I'm fluent in programmation in general. My addon's goal is to speed up the process of naming output files, keep track of version number of each shot/scene, and other features in the future.

Most properties of my addon are per-scene (and working perfectly), but I also need to have a per-file property, like a global variable. As shown below, some properties are attached to the current scene (orange), while other properties should be attached to the current file (red).

Example of my properties, file-level and scene-level

The file-level property will be a main folder, containing other subfolders (one per Scene). I'm unable to implement that.

The scene-level properties will keep track of multiple informations on the current Scene. These properties are already working as I want it.

Here is a list of the conditions I want the property to satisfy :

  • The property should be unique inside the Blender file
  • All scenes should have access to this property
  • The property should not be shared between multiple Blender files.
  • (Redundant/trivial to say it but : any change of the property in one scene should update the property for all scenes.)

What I've found so far

I found some informations about that here. The only solution that corresponds to my condition is to have a dummy ID block and I have no idea what it is exactly (nor Google).

My question

How am I supposed to attach a property to the blend file, satisfying the conditions above ? Are dummy ID blocks the only way of achieving that ? If yes, how is it supposed to be implemented ?

Thanks for your time

$\endgroup$
6
  • 1
    $\begingroup$ i think he meant, that you just create a text (bpy.data.texts) which you maybe call "my global variables" and then you can add your custom properties to that text. e.g. bpy.data.texts["Text"]["a"] = 7. And i assume you know how you can create a new text and add custom properties to it ;) and since text is not scene dependant, i think that is a nice workaround/solution! $\endgroup$
    – Chris
    Commented Mar 21 at 7:16
  • 2
    $\begingroup$ if you begin the data block name with a dot . eg .my_text it won't appear in the regular UI search so you can kind of hide it. $\endgroup$
    – Gorgious
    Commented Mar 21 at 15:16
  • $\begingroup$ Thank you both ! I'll take a look at your answers, it should work for my case. $\endgroup$
    – Lutzi
    Commented Mar 21 at 17:01
  • $\begingroup$ @Chris Did you mean bpy.types.text ? I haven't encountered text objects for now, and I couldn't find bpy.data.text in the documentation. docs.blender.org/api/current/bpy.types.Text.html $\endgroup$
    – Lutzi
    Commented Mar 21 at 17:55
  • $\begingroup$ @Lutzi: yes, i mean exactly that. I don't understand your question!? your link leads to the right documentation... ;) [1]: i.sstatic.net/gz3GN.png $\endgroup$
    – Chris
    Commented Mar 22 at 7:33

1 Answer 1

1
$\begingroup$

ok, here a very "dumb" and small example:

bl_info = {
    "name": "New Object",
    "author": "Your Name Here",
    "version": (1, 0),
    "blender": (2, 80, 0),
    "location": "View3D > Add > Mesh > New Object",
    "description": "Adds a new Mesh Object",
    "warning": "",
    "doc_url": "",
    "category": "Add Mesh",
}


import bpy
from bpy.types import Operator
from bpy.props import FloatVectorProperty
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from mathutils import Vector


def add_object(self, context):
    scale_x = self.scale.x
    scale_y = self.scale.y

    verts = [
        Vector((-1 * scale_x, 1 * scale_y, 0)),
        Vector((1 * scale_x, 1 * scale_y, 0)),
        Vector((1 * scale_x, -1 * scale_y, 0)),
        Vector((-1 * scale_x, -1 * scale_y, 0)),
    ]

    edges = []
    faces = [[0, 1, 2, 3]]

    mesh = bpy.data.meshes.new(name="New Object Mesh")
    mesh.from_pydata(verts, edges, faces)
    # useful for development when the mesh may be invalid.
    # mesh.validate(verbose=True)
    object_data_add(context, mesh, operator=self)


class OBJECT_OT_add_object(Operator, AddObjectHelper):
    """Create a new Mesh Object"""
    bl_idname = "mesh.add_object"
    bl_label = "Add Mesh Object"
    bl_options = {'REGISTER', 'UNDO'}

    scale: FloatVectorProperty(
        name="scale",
        default=(1.0, 1.0, 1.0),
        subtype='TRANSLATION',
        description="scaling",
    )

    def execute(self, context):

        add_object(self, context)
        

        #Added by me start
        context.object["read text property"] = bpy.data.texts["top secret"]["my custom property1"]
        context.object["read text property2"] = bpy.data.texts["top secret"]["my custom property2"]
        #Added by me end
        
        return {'FINISHED'}


# Registration

def add_object_button(self, context):
    self.layout.operator(
        OBJECT_OT_add_object.bl_idname,
        text="Add Object",
        icon='PLUGIN')


# This allows you to right click on a button and link to documentation
def add_object_manual_map():
    url_manual_prefix = "https://docs.blender.org/manual/en/latest/"
    url_manual_mapping = (
        ("bpy.ops.mesh.add_object", "scene_layout/object/types.html"),
    )
    return url_manual_prefix, url_manual_mapping


def register():
    bpy.utils.register_class(OBJECT_OT_add_object)
    bpy.utils.register_manual_map(add_object_manual_map)
    bpy.types.VIEW3D_MT_mesh_add.append(add_object_button)
    
    #Added by me start
    if bpy.data.texts.get("top secret") == None:
        bpy.data.texts.new("top secret")
        bpy.data.texts["top secret"]["my custom property1"] = 7
        bpy.data.texts["top secret"]["my custom property2"] = "that's the way"
    #Added by me end
        


def unregister():
    bpy.utils.unregister_class(OBJECT_OT_add_object)
    bpy.utils.unregister_manual_map(add_object_manual_map)
    bpy.types.VIEW3D_MT_mesh_add.remove(add_object_button)


if __name__ == "__main__":
    register()

The example is from Blender's python example -> add-on

I added these line:

if bpy.data.texts.get("top secret") == None:
        bpy.data.texts.new("top secret")
        bpy.data.texts["top secret"]["my custom property1"] = 7
        bpy.data.texts["top secret"]["my custom property2"] = "that's the way"

this creates your new text object with 2 custom properties (which are scene independent, because they are stored in text objects)

i also added these 2 lines:

context.object["read text property"] = bpy.data.texts["top secret"]["my custom property1"]
context.object["read text property2"] = bpy.data.texts["top secret"]["my custom property2"]
            

which read out the text custom properties and assigns them to the newly created object as their "own" custom properties.

Hope this helps a bit.

enter image description here


Update for Gorgious (because he answered my comment's question so quick and nice):

import bpy

if bpy.data.texts.get("top secret") == None:
        bpy.data.texts.new("top secret")
        bpy.data.texts["top secret"]["my custom property1"] = 7
        bpy.data.texts["top secret"]["my custom property2"] = "that's the way"

bpy.context.object["read text property"] = bpy.data.texts["top secret"]["my custom property1"]
bpy.context.object["read text property2"] = bpy.data.texts["top secret"]["my custom property2"]
           

you can copy paste this code and run it, but you should select an object before (e.g. your default cube). If you watch your custom properties before you run the code, you will see, that there were no custom properties. Afterwards you will have a new text object and 2 new custom properties on the text object and on your selected object.

$\endgroup$
5
  • $\begingroup$ by the way: i have no idea how you can "see" the custom properties of a text object - so if somebody knows, just let me know, i would add it to the answer. thx. $\endgroup$
    – Chris
    Commented Mar 22 at 7:46
  • 1
    $\begingroup$ Interesting question. It is indeed possible. See blender.stackexchange.com/a/315216/86891 BTW I think your answer is good but maybe you can strip some code that doesn't really relate to the question, seeing as the OP is not really fluent with python. Cheers $\endgroup$
    – Gorgious
    Commented Mar 22 at 8:21
  • $\begingroup$ I got it kinda working for a scene, but when creating a new scene, my property doesn't use the text. Is there somewhere in your code, where you instanciate the property's value based on the text data (if text exists) ? My understanding is that your solution doesn't work for multiple scenes. $\endgroup$
    – Lutzi
    Commented Mar 24 at 18:18
  • $\begingroup$ Also, I don't understand what the left part from context.object["read text property"] = bpy.data.texts["top secret"]["my custom property1"] is supposed to achieve (in the OBJECT_OT_add_object class). $\endgroup$
    – Lutzi
    Commented Mar 24 at 18:20
  • $\begingroup$ AFAIK the text objects are not dependent on scenes. So if yours are working only in one scene, maybe provide your code, how you are using it? I don't say that my "example" is super sophisticated, it just should show, how it works. The line just creates a custom property (read text property) for the active object and gives it the value of the custom property (my custom propterty1) of the text (top secret) $\endgroup$
    – Chris
    Commented Mar 25 at 6:59

You must log in to answer this question.

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