2
$\begingroup$

I have a blend file that contains the following scenes:

• Clothing • Compositing • Paper • Social Media • Van

The idea is that guys in my office who aren't 3d literate can save a logo design as a texture file, then open this Blend file, hit render and it spits out a bunch of images showing off the logo in different scenarios.

The only sticking point is that they may need to change the colour of parts of the files so I have put together the following python script which when run allows them to just use colour pickers to change the colours.

import bpy
from bpy.types import Panel

class ExamplePanel(Panel):
bl_label = "Background Colours"
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'


def draw(self, context):
    layout = self.layout


     #Paper Scene Inputs

    obj = bpy.data.objects["OfficePaper"]

    mat = obj.active_material
    if not mat:
        layout.label("Create a material")
        return

    nodes = mat.node_tree.nodes

    # Define the Specific Node
    paper_colour = nodes["Principled BSDF.001"]
    mix = nodes["Mix Shader"]

    layout.label("Office Paper Scene")
    layout.prop(paper_colour.inputs["Base Color"], "default_value", text="Paper Colour")


     #Clothing Scene Inputs           

    obj = bpy.data.objects["Embroidered_Cloth"]

    mat = obj.active_material
    if not mat:
        layout.label("Create a material")
        return

    nodes = mat.node_tree.nodes

    # Define the specific node
    clothing_colour = nodes["Principled BSDF"]
    mix = nodes["Mix Shader"]

    layout.label("Printed Clothing Scene")
    layout.prop(clothing_colour.inputs["Base Color"], "default_value", text="Clothing Colour")





     #Social Media Scene Inputs           

    obj = bpy.data.objects["iPhone"]

    mat = obj.active_material
    if not mat:
        layout.label("Create a material")
        return

    nodes = mat.node_tree.nodes

    # Define the specific node
    postbg_colour = nodes["MixBGColour"]


    layout.label("Social Media Scene")
    layout.prop(postbg_colour.inputs["Color1"], "default_value", text="Background Colour")




     #Vehicle Graphics Scene Inputs           

    obj = bpy.data.objects["Van"]

    mat = obj.active_material
    if not mat:
        layout.label("Create a material")
        return

    nodes = mat.node_tree.nodes

    # Define the specific node
    vanpaint_colour = nodes["VanColour"]


    layout.label("Social Media Scene")
    layout.prop(vanpaint_colour.inputs["Base Color"], "default_value", text="Van Colour")        




if __name__ == "__main__":  # only for live edit.
from bpy.utils import register_class
register_class(ExamplePanel)

This script, micraculously given my limited scripting experience, works fine.

The final hurdle though is that I want this script to be loaded ready for them so that they don't even have to worry themselves with clicking the Run Script button and hence having to leave the code open to the risk of accidental editing.

I have the the Register Check box checked and I have the name of the text datablock as cp.py I have also set the preferences to Auto Execute but so far nothing works at all.

Text Window

$\endgroup$
  • 2
    $\begingroup$ With regards to the specific question note batFinger's answer here. Be sure that after you have saved the text file you also make it internal. Currently there are several other issues with your script (aside from just formatting) the use of specific names for material nodes will cause problems if users actually need to make nodes, the test for materials will fail if an object has a material that does not use nodes, etc. $\endgroup$ – Ratt Jan 9 at 0:25
  • $\begingroup$ I found batFinger’s answer before posting but I couldn’t make sense of it. Do I need to save the script externally or can it just sit in the Text window? When he mentions just adding register() where should that sit, I tried at the foot of the script but that stopped the script working. $\endgroup$ – Tom Jelfs Jan 9 at 7:18
  • $\begingroup$ For your example above, when the registered script block is auto run its name is "cp" not "__main__" so the register code in the if statement will not run. Either remove the if, or change to if __name__ == "cp": and the register code will run. The example in my other answer has a register() method, yours doesn't. $\endgroup$ – batFINGER Jan 9 at 9:45
  • $\begingroup$ Thanks for that the first suggestion works by explicitly telling the if what to look for. $\endgroup$ – Tom Jelfs Jan 9 at 10:18
3
$\begingroup$

Requirements for auto running a script.

Press CTRL+ALT+U to open user prefernces.

In the File tab ensure Auto Execution is checked.

Save User settings.

In Text editor: Create new text block.

Copy script into new text block (ensure the script has an explicit register function note bottom lines).

Save text block as cp.py. Unlink text block. Open cp.py.

Click Register checkbox.

Save Blend file Close Blend file re-Open Blend file ... panel should auto run.

import bpy
from bpy.types import Panel


class ExamplePanel(Panel):
    bl_label = "Background Colours"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'

    def draw(self, context):
        layout = self.layout
        obj_types = ['MESH', ]  # add types as needed
        objs = [obj for obj in bpy.data.objects if
                obj.type in obj_types and obj.select]
        col_types = ['RGBA', ]  # add types as needed
        for obj in objs:
            box = layout.box()
            box.label(obj.name)
            # for viewport or non node colors
            if obj.active_material:
                my_mat = [mat for mat in bpy.data.materials if
                          mat.name == obj.active_material.name]
                box.label("Viewport Color")
                box.prop(my_mat[0], "diffuse_color", text="")
            else:
                box.label("Requires active material")
            # for materials using shader nodes
            if my_mat[0].use_nodes:
                my_nodes = [nd for nd in my_mat[0].node_tree.nodes]
                box = box.box()
                box.label("Render color")
                for node in my_nodes:
                    for ip in node.inputs:
                        if ip.type in col_types:
                            box.prop(ip, "default_value", text=("%s: %s" % (node.type, ip.name)))

classes = [ExamplePanel, ]


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


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


#if __name__ == "__main__":  # only for live edit.
#    register()

register()  # required for autorun

enter image description here

$\endgroup$
  • $\begingroup$ Sorry to be such a caveman but what lines from the bottom do I need to add to my script? I know you have mentioned that there are problems with my script using specific nodes but this file is designed that my colleagues can only change these specific colours, these guys don't know anything about 3D. I just want them to open the file hit render and, if the background colours don't work on certain areas, use the colour picker to change the colours to suit and re-render. My script works just fine but I need it to auto run, that's the end goal. Again I'm sorry to ask you to do the work for me. $\endgroup$ – Tom Jelfs Jan 9 at 9:20
  • $\begingroup$ @TomJelfs Notice that the lines if __name__ == '__main__': are commented out in my script and instead i use register() to always call my register function. A registered script will use its own name not __main__. Your script is never registering your panel on load because __name__ does not equal __main__ for a registered script. $\endgroup$ – Ratt Jan 9 at 9:33
  • $\begingroup$ Yes I did notice that so I changed my last lines of my script from if __name__ == "__main__": # only for live edit. from bpy.utils import register_class register_class(ExamplePanel) to #if __name__ == "__main__": # only for live edit. register() but I get the Python script fail, look in the console for now... $\endgroup$ – Tom Jelfs Jan 9 at 9:58
0
$\begingroup$

OK so to clarify for anyone stumbling across this thread in a bit of a spin like I was, the solution for me was as follows:

The bottom lines of my text read

if __name__ == "__main__":
from bpy.utils import register_class
register_class(ExamplePanel)

And, as @batFINGER pointed out in this thread and comment, the if statement was not matching.

Changing to the following worked

if __name__ == "NameOfYourPythonScript":
from bpy.utils import register_class
register_class(ExamplePanel)

Then following @Ratt's answer for saving and unlinking etc was the other missing piece as, I wasn't aware it needed to be save externally.

However my final script was edited to be as follows:

def register():
bpy.utils.register_class(ExamplePanel)

def unregister():
bpy.utils.unregister_class(ExamplePanel)

register()
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.