Skip to content

MarioDelgadoSr/AddBlenderCustomPropertiesFromCSV

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

84 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python Script to Add Blender Custom Properties From CSV File

QUOTE_NONNUMERIC
    ... When used with the reader, input fields that are not quoted are converted to floats.
  • Therefore, non-numeric components (including column headers) must be enclosed within double qutoes.

  • The script or Add-on can be used to 'enrich' a Blender file, that will be exported to a glTF file, with embedded data.

Usage

Option 1

  • Run the AddBlenderCustomPropertiesFromCSV.py script from within Blender and respond to the input prompt with filename (format: [folder]/filename.csv ) that contains the data to update the Custom Properies.

Note:

Option 2

Screen Shot of Installed Add-on:

Screen Shot of Demonstration

Creating a Properly Formatted CSV Files

  • All text values (including column headers) must be double quoted in the .csv file.
  • The Python Scripts will throw an exception error if they attempt to convert an un-quoted string to a float numeric.
  • Microsoft Excel does not add double quotes around text values automatically with its Save As option.
  • One option is to use the CVSFile utility macro in the vba folder.
Utility Macro's Text:
Sub CSVFile()

    ' Modified from: https://stackoverflow.com/questions/846839/excel-how-to-add-double-quotes-to-strings-only-in-csv-file
    ' Author: https://github.com/MarioDelgadoSr
    '
    ' Macro will export selection or current sheet in a format compatible with Python's QUOTE_NONNUMERIC quoting.
    ' See: https://docs.python.org/3/library/csv.html
    
    Dim SrcRg As Range
    Dim CurrRow As Range
    Dim CurrCell As Range
    Dim CurrTextStr As String
    Dim ListSep As String
    Dim FName As Variant
    
    'Prompt User for save file location
    FName = Application.GetSaveAsFilename("", "CSV File (*.csv), *.csv")

    If FName <> False Then
      
      'ListSep = Application.International(xlListSeparator)
      ListSep = ","
      
      If Selection.Cells.Count > 1 Then
      
        Set SrcRg = Selection
      
      Else
        
        Set SrcRg = ActiveSheet.UsedRange
      
      End If
      
      Open FName For Output As #1
      
        For Each CurrRow In SrcRg.Rows
        
          CurrTextStr = ""
          
          For Each CurrCell In CurrRow.Cells
          
            ' Quote only text value
            DblQuoteStr = IIf(Application.IsText(CurrCell.Value), """", "")
            CurrTextStr = CurrTextStr & DblQuoteStr & CurrCell.Value & DblQuoteStr & ListSep
          
          Next 'CurCell
          
          While Right(CurrTextStr, 1) = ListSep
            'Remove last ',' on the line
            CurrTextStr = Left(CurrTextStr, Len(CurrTextStr) - 1)
          Wend
          
          Print #1, CurrTextStr
        
        Next 'CurrRow
      
      Close #1
    
    End If
  
End Sub

Script Workflow

  • Assuming a Blender object (.type = 'MESH');
  • With a name == 'object1';
  • The following csv file contents will assign the Blender mesh Custom Properties :
    • A numeric float value of prop1 to property 'propName1';
    • A string value of "prop2" to 'propertyName2':
"objectName","propName1","propName2"
"object1",prop1,"prop2"
# Python script to set Blender Custom Properties for a mesh (.type == 'MESH')
# Author: Mario Delgado, LinkedIn: https://www.linkedin.com/in/mario-delgado-5b6195155/
# Source: http://github.com/MarioDelgadoSr/AddBlenderCustomPropertiesFromCSV
# 
# Custom Properties: https://docs.blender.org/manual/en/latest/data_system/custom_properties.html?highlight=custom%20properties

import bpy, csv

# santize = True will convert Blender's duplicate object name to a Three.js sanitized name
# This is only a concern when exporting Blender to glTF files with the intent of importing
# them into Three.js.  Any names with mesh glTF nodes with '.' in the name will have the '.' removed.
# So sanitizing the names before exporting to glTF (and eventually Three.js) will provide for consitency
# in any processes that depend on a consitent and accurate node name. 
# See Three.js sanitizing:  https://discourse.threejs.org/t/issue-with-gltfloader-and-objects-with-dots-in-their-name-attribute/6726 	

sanitize = True   #True or False

# input: https://docs.python.org/3/library/functions.html#input
# Note: input awaits a response from the system console, not the Blender Python Interactive console
# System Console: https://docs.blender.org/manual/en/dev/advanced/command_line/launch/windows.html?highlight=toggle%20system%20console
# Python Interactive Console: https://docs.blender.org/manual/en/dev/editors/python_console.html

filePath = input("Enter file name path (folder/filename.csv):")         #Example: Type "c:/data/keys.csv" when prompted in the conole

# Example of content in .csv file, line 1 contains column heading (Object Name and Properties):
#
# "objectName","propName1","propName2",...
# "object1","prop1",prop2,...
# "object2","prop1",prop2,...
#
# Script will assign bpy.data.objects[objectName].data[propNameN] = propN
#	* The quoted propNs will be treated as strings
#	* The un-quoted propNs will be converted to float.


print("********************************Add Blender Custom Properties ********************************************")
print(" ")
print("Adding Custom Properties with the following options:")
print(" ")
print("filePath: ", filePath)
print("sanitize: ", str(sanitize))
print(" ")
		
with open( filePath ) as csvfile:   # https://docs.python.org/3/library/csv.html
    rdr = csv.DictReader( csvfile, quoting = csv.QUOTE_NONNUMERIC )     
    for row in rdr:

        meshName = row[rdr.fieldnames[0]]
        
        print("******************************** meshName:", meshName ,"********************************************")
        print(" properties before assignment(s): ", bpy.data.objects[meshName].data.items()) 
        
        for x in range (1, len(rdr.fieldnames)):  
            propName =  rdr.fieldnames[x]
            propValue = row[propName]
            # List Comprehension: https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions
            mesh = [obj for obj in bpy.data.objects if obj.name == meshName and obj.type == 'MESH'][0]
			
            if sanitize:
                mesh.name = mesh.name.replace(".","")
                print (" Mesh's name sanitized from: ",meshName, " to: ", mesh.name)
                meshName = mesh.name
            
            #Custom Property Assigned to Object, this assures userData on ThreeJS is assigned to groups as well
            mesh[propName] = propValue    
            print(" Updated meshName: ", meshName, ", propName: ", propName, ", propValue:", mesh[propName])
        
        print(" properties after assignment(s): ", bpy.data.objects[meshName].items()) 
        print("")

Blender Add-on Workflow

  • Assuming a Blender object (.type = 'MESH');
  • With a name == 'object1';
  • The following csv file contents will assign the Blender mesh a Custom Properties numeric float value of prop1 to property 'propName1' and a string value of "prop2" to 'propertyName2':
"objectName","propName1","propName2"
"object1",prop1,"prop2"
  • Once installed , the Blender Add-on will be registered in the Tools panel:

Screen Shot of Demonstration

# Add-on Tutorial: https://youtu.be/OEkrQGFqM10
# Python script to set Blender Custom Properties for a mesh (.type == 'MESH')
# Author: Mario Delgado, LinkedIn: https://www.linkedin.com/in/mario-delgado-5b6195155/
# Source: http://github.com/MarioDelgadoSr/AddBlenderCustomPropertiesFromCSV
# 
# Custom Properties: https://docs.blender.org/manual/en/latest/data_system/custom_properties.html?highlight=custom%20properties
# Modified from: https://blender.stackexchange.com/questions/26898/how-to-create-a-folder-dialog/26906#26906

bl_info = {"name": "CVS to Custom Properties", "category": "Object"}

import bpy, csv

from bpy.props import (StringProperty,
                       BoolProperty,
                       IntProperty,
                       FloatProperty,
                       FloatVectorProperty,
                       EnumProperty,
                       PointerProperty,
                       )
from bpy.types import (Panel,
                       Operator,
                       AddonPreferences,
                       PropertyGroup,
                       )


class addPropsSettings(PropertyGroup):

    path = StringProperty(
        name="",
        description="Path to Directory",
        default="",
        maxlen=1024,
        subtype='FILE_PATH')
        
    sanitize_bool = BoolProperty(        #https://blender.stackexchange.com/questions/35007/how-can-i-add-a-checkbox-in-the-tools-ui
        name="Sanitize Mesh Name",
        description="Sanitize the Mesh Name",
        default = True
        )        
        
        
class processCustom(bpy.types.Operator):
  
    bl_idname = "object.process_custom"
    bl_label = ""
    
    filePath =  bpy.props.StringProperty() 
    sanitize =  bpy.props.BoolProperty() 
    
    def execute(self, context):

        filePath = self.filePath
        sanitize = self.sanitize

        # santize = True will convert Blender's duplicate object name to a Three.js sanitized name
        # This is only a concern when exporting Blender to glTF files with the intent of importing
        # them into Three.js.  Any names with mesh glTF nodes with '.' in the name will have the '.' removed.
        # So sanitizing the names before exporting to glTF (and eventually Three.js) will provide for consitency
        # in any processes that depend on a consitent and accurate node name. 
        # See Three.js sanitizing:  https://discourse.threejs.org/t/issue-with-gltfloader-and-objects-with-dots-in-their-name-attribute/6726 	
        #sanitize = True   #True or False
        

        # input: https://docs.python.org/3/library/functions.html#input
        # Note: input awaits a response from the system console, not the Blender Python Interactive console
        # System Console: https://docs.blender.org/manual/en/dev/advanced/command_line/launch/windows.html?highlight=toggle%20system%20console
        # Python Interactive Console: https://docs.blender.org/manual/en/dev/editors/python_console.html
        #filePath = input("Enter file name path (folder/filename.csv):")         #Example: Type "c:/data/keys.csv" when prompted in the conole

		# Example of content in .csv file, line 1 contains column heading (Object Name and Properties):
		#
		# "objectName","propName1","propName2",...
		# "object1","prop1",prop2,...
		# "object2","prop1",prop2,...
		#
		# Script will assign bpy.data.objects[objectName].data[propNameN] = propN
		#	* The quoted propNs will be treated as strings
		#	* The un-quoted propNs will be converted to float.

        print("********************************Add Blender Custom Properties ********************************************")
        print(" ")
        print("Adding Custom Properties with the following options:")
        print(" ")
        print("filePath: ", filePath)
        print("sanitize: ", str(sanitize))
        print(" ")

        # https://docs.python.org/3/library/csv.html
        with open( filePath ) as csvfile:
            rdr=csv.DictReader(csvfile,quoting=csv.QUOTE_NONNUMERIC)
            for row in rdr:

                meshName = row[rdr.fieldnames[0]]
                
                print("******************************** meshName:", meshName ,"********************************************")
                print(" properties before assignment(s): ", bpy.data.objects[meshName].data.items()) 
                
                for x in range (1, len(rdr.fieldnames)):  
                    propName =  rdr.fieldnames[x]
                    propValue = row[propName]
                    # List Comprehension: https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions
                    mesh = [obj for obj in bpy.data.objects if obj.name == meshName and obj.type == 'MESH'][0]
        			
                    if sanitize:
                        mesh.name = mesh.name.replace(".","")
                        print (" Mesh's name sanitized from: ",meshName, " to: ", mesh.name)
                        meshName = mesh.name
                    
					#Custom Property Assigned to Object, this assures userData on ThreeJS is assigned to groups as well
                    mesh[propName]=propValue
                    print(" Update meshName: ", meshName, ",propName: ", propName, ", proValue: ", mesh[propName])
                
                print(" properties after assignment(s): ", bpy.data.objects[meshName].items())
                print("")
		
        return {'FINISHED'}

# ---------------------------------------------------------------------------------
#  Customize Tool Panel and add file selector and check box for sanitize option
# ---------------------------------------------------------------------------------

class addCustomProperitesPanel(Panel):
    bl_idname = "addCustomProperitesPanel"
    bl_label = "CSV to Custom Props"  # scn.csv_to_custom_props
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_category = "Tools"
    bl_context = "objectmode"

    def draw(self, context):
        layout = self.layout
        scn = context.scene
        col = layout.column(align=True)
        col.prop(scn.csv_to_custom_props, "path", text="")      				#The file path selected by the user
        col.prop(scn.csv_to_custom_props, "sanitize_bool","Sanitize Mesh Name") #The sanitize option True/False 
        
        #Passing property: https://blenderartists.org/t/how-to-pass-two-arguments-to-a-button-operator/497013/8         
        processCustomButton =  col.operator("object.process_custom", text="Add Custom Props")
        processCustomButton.filePath =  scn.csv_to_custom_props.path 
        processCustomButton.sanitize =  scn.csv_to_custom_props.sanitize_bool
        
# ------------------------------------------------------------------------
#    register and unregister functions
# ------------------------------------------------------------------------

def register():
    bpy.utils.register_module(__name__)
    bpy.types.Scene.csv_to_custom_props = PointerProperty(type=addPropsSettings)

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

if __name__ == "__main__":
    register()





Running the Test(s)

Option 1

Note:

Option 2

Screen Shot of Installed Add-on Screen Shot of Demonstration

Contents of test.csv

"Mesh","value"
"Cube.000",10
"Cube.001",20

Test Output

The output is the same for either AddBlenderCustomPropertiesFromCSV.py script or AddBlenderCustomPropertiesFromCSVAddOn.py Add-on:


********************************Add Blender Custom Properties ********************************************

Adding Custom Properties with the following options:

filePath:  c:\temp\test.csv
sanitize:  True

******************************** meshName: Cube.000 ********************************************
 properties before assignment(s):  []
 Mesh's name sanitized from:  Cube.000  to:  Cube000
 Updated meshName:  Cube000 , propName:  value , propValue: 10.0
 properties after assignment(s):  [('value', 10.0)]

******************************** meshName: Cube.001 ********************************************
 properties before assignment(s):  []
 Mesh's name sanitized from:  Cube.001  to:  Cube001
 Updated meshName:  Cube001 , propName:  value , propValue: 20.0
 properties after assignment(s):  [('value', 20.0)]

The file test.gltf is a glTF file exported after the data was added to the Blender scene.

Repository

  • The repository folder contains several glTF files created with the Python script used to demonstrate the My Data Visualizer application.

  • These Blender generated exports can simply be 'dragged-and-droped' into the visualization application. The application will read both the 3D visual and data information dynamically.

Author

License

This project is licensed under the MIT License - see the LICENSE.md file for details

About

Blender Python Script to add Custom Properties in csv file

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages