2
$\begingroup$

My __init__.py script creates a panel that lets a user select the figure (typically a humanoid figure imported from Poser) she wants to give shaders to, defines the figure type and then sets the path to the image files used in the shader script.

The bit that creates the panel:

class MatShaderPanel(bpy.types.Panel):
    """Create shaders for your Poser figure: Panel"""
    bl_label = "Figure Files Util"
    bl_idname = "MATERIALS_PT_shaders"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'

    def draw(self, context):
        # sets renderer to Cycles
        if bpy.context.scene.render.engine == 'BLENDER_RENDER':
            bpy.context.scene.render.engine = 'CYCLES'
        layout = self.layout
        obj = context.object

        row1 = layout.row()
        row1.label(text='Figure Name')

        row2 = layout.row()
        row2.label(text="Active object is: " + obj.name)
        row3 = layout.row()
        row3.prop(obj, "name")

        row4 = layout.row()
        row4.prop(context.scene, 'imag_path')
        row5 = layout.row()
        row5.operator("object.run_script", text='Apply Shaders')
        row5.enabled = context.scene.show_row5

I believe that I register that 'imag_path' property seen in row4.prop(context.scene, 'imag_path') in:

def register():
    bpy.utils.register_module(__name__)
    bpy.types.Scene.imag_path = bpy.props.StringProperty( \
      name="Images Path",
      default="",
      description="Defines the path to your images",
      subtype='DIR_PATH')
    bpy.types.Scene.figur_obj = bpy.props.StringProperty( \
      name="Figure Type",
      default="",
      description="Defines the base mesh name of your figure")

def unregister():
    del bpy.types.Scene.imag_path
    del bpy.types.Scene.figur_obj
    bpy.utils.unregister_module(__name__)

if __name__ == "__main__":
    register()

However, the imag_path property doesn't appear to be available to the rest of the script. I was thinking of making it a global variable by setting it right after the import statements, but am not quite clear on whether that's the right approach. For example, this (in a subsequent function):

def shadersSetup():
    csvFile = imag_path + "/" + "image_List.csv"

...fails, as imag_path isn't available to shaderSetup().

Think I need to read up on property scope vs variable scope and how all that relates to modules. This doesn't discuss it any.

Update

Not the full answer yet, so I can't call this solved, but I did incorporate both batFINGER's and TLousky's suggestions. Unfortunately, I am still getting an error based on what gets written to imag_path in that DIR_PATH property(?) which allows the user to go out and find the folder containing the images. I mean the last part of the resultant path looks exactly right, but the first bit seems not quite right, somehow: here is the error:

Traceback (most recent call last): File "/home/robyn/.config/blender/2.76/scripts/addons/make_Shaders/__init__.py", line 102, in execute shadersSetup() File "/home/robyn/.config/blender/2.76/scripts/addons/make_Shaders/__init__.py", line 124, in shadersSetup iList = readImgList(csvFile) File "/home/robyn/.config/blender/2.76/scripts/addons/make_Shaders/__init__.py", line 113, in readImgList with open(fname, 'r') as csvfile: FileNotFoundError: [Errno 2] No such file or directory: '//../../AllTextures/AllSkin/PS_Syri/image_list.csv'

The part of the path displayed is correct, exactly. But the script seems to expect a fully-qualified path. And I tried: csvFile = os.path.join(os.path.abspath(scn.imag_path), "image_list.csv"). Still gave me a truncated path.

I do want to say I thoroughly appreciate your help.

2nd Update

Since the problem with the path still exists, I've been running a series of test on this path issue, double-checking that the path and the file do indeed exist. I did change the csv_Path property to take the file name now:

bpy.types.Scene.csv_path = bpy.props.StringProperty(name="CSV Path",
  default="*.csv",
  maxlen=1024,
  description="CSV location",
  subtype='FILE_PATH')

Mind you, I thought by doing a default="*.csv", it would only display that csv file in the folder, but it displays all files.

Here is the path testing code:

scn = bpy.context.scene
csvFile = os.path.abspath(scn.csv_path)
csvPath = csvFile[:-14]
fullyQP = os.path.join(os.getcwd(), csvFile)
print("scn.csv_path: " + scn.csv_path)
print("csvFile: " + csvFile)
print("csvPath: " + csvPath)
print("Joined cwd and the CSV: " + fullyQP)
print(os.path.isdir(csvPath))

Here is the output to the print()s:

scn.csv_path: //../../AllTextures/AllSkin/PS_Syri/image_List.csv
csvFile: //AllTextures/AllSkin/PS_Syri/image_List.csv
csvPath: //AllTextures/AllSkin/PS_Syri/
Joined cwd and the CSV: //AllTextures/AllSkin/PS_Syri/image_List.csv
False

The last is most significant, as it is not seeing that relative path as a workable path. In my preferences, under File, I have Relative Paths ticked. I'm completely baffled as to how to proceed.

ETA: I guess if I can't get this working, I might need to have the users put those csv files (and image files) where Blender and the script can easily find them. Thing is: what I was hoping was that they could use existing folders somewhere in their /runtime/textures/character directories instead of having to make a copy to their Blender projects folders.

$\endgroup$
11
  • 2
    $\begingroup$ Don't think you'll get away with changing properties in the draw method of a panel, in your case the render engine. Also using os.path.join(scene.imag_path, "image_list.csv") is good practice. $\endgroup$
    – batFINGER
    Nov 10, 2015 at 6:12
  • $\begingroup$ You were right, batFINGER... changed to Blender Internal and the If statement failed. Working in the function further down (shaderSetup()) which is where it should have been. Also, added os.path.join() - thank you for the suggestion. $\endgroup$
    – RobinsSea
    Nov 10, 2015 at 10:36
  • $\begingroup$ related: blender.stackexchange.com/questions/26898/… $\endgroup$
    – p2or
    Nov 10, 2015 at 11:02
  • $\begingroup$ Is the filename image_list.csv or image_List.csv if you're on linux it's case sensitive. Also do you need the csv file, you can select multiple files from the filebrowser blenderartists.org/forum/… or if you do why not make the string_prop a FILE_PATH and pick the csv file itself. $\endgroup$
    – batFINGER
    Nov 10, 2015 at 11:42
  • $\begingroup$ Your suggestion makes good sense, batFINGER, for a number of reasons, paramount of which being: if a .csv hasn't been created, selecting the folder only will still cause the script to fail. I should be able to get the location of the files from that. $\endgroup$
    – RobinsSea
    Nov 10, 2015 at 20:10

2 Answers 2

1
$\begingroup$

When you add a property to the scene type, such as with your bpy.types.Scene.imag_path, you need to access it through a scene object.

For instance:

print( bpy.context.scene.imag_path )

Or in your case:

def shadersSetup():
    csvFile = bpy.context.scene.imag_path + "/" + "image_List.csv"

You actually have used it this way in another part of your code, which gave away the hint:

row4.prop(context.scene, 'imag_path')
$\endgroup$
3
  • 1
    $\begingroup$ IMO using a property group makes most sense and is easier to manage: blender.stackexchange.com/questions/26898/… $\endgroup$
    – p2or
    Nov 10, 2015 at 11:07
  • $\begingroup$ I agree. Also with batFINGER's comment from above advising to use os.path.join as a safer, platform independent way to concatenate paths. $\endgroup$
    – TLousky
    Nov 10, 2015 at 11:26
  • $\begingroup$ @TLousky - As this script is to be platform-independent, I will definitely include this approach. @ poor - I've had a look at the script you linked to: thank you for the heads up and the suggestion. Whilst running the script from text editor was simple and didn't require using properties, I think your approach might be needed to manage material_slots for a figure. $\endgroup$
    – RobinsSea
    Nov 10, 2015 at 20:15
0
$\begingroup$

I'm going to close this question (which totally became more of a path issue than anything else) by saying that os.path.join() works in this context: by placing the files you wish to access in the same folder as the .blend, and then using bpy.data.filepath:

blendPath = bpy.data.filepath
blendDir = os.path.dirname(blendPath)
csv_file_path = os.path.join(blendDir, "path_List.csv")

blendDir is the fully-qualified path to the currently opened .blend, in which directory the path_List.csv will also reside.

$\endgroup$

You must log in to answer this question.

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