# Import csv to Python syntax eludes me, what did I miss?

I'm trying to import a list from a csv to create a series of circles.

I've created a few scripts that move and rotate objects with help of csv data, I used that csv routine as a template.

This time I wanted to create the object and thought I understood - for i, row in enumerate( rdr ): - however, if you look at this snippet code, you might think I missed the boat completely.

My biggest problem at this moment is that I do not understand how I can get the csv values parsed correctly with add_circle().

Then I have a second issue regarding the fact that it only creates the first circle from the csv and I really thought that this would work.

Can somebody explain what I'm missing to make it work? Or does anybody know a comprehensive paper so I can figure out why this is not working as I expected.

with open( fp ) as csvfile:

# make series of circles
for i, row in enumerate( rdr ):
if i == 0: continue # Skip column titles
FRAME, PARTS, XW = row[0:3]
part_number = float(PARTS)


CSV snippet:

FRAME, PARTS, XW
1001,8,0
1002,15,1.1
1003B,32,2
etc.


In case the full script is more helpful:

# -*- coding: utf-8 -*-
"""
Blender: 2.80
Tooltip: 'Creates a shape object'
"""

import bpy, csv
import bpy.types
import time
from mathutils import *
import math

fp = "C:/Users/Maarten/Documents/blender/csv/generate-object.csv"

def object_from_data(data, name, scene, select=True):
""" Create a mesh object and link it to a scene """
mesh = bpy.data.meshes.new(name)
mesh.from_pydata(data['verts'], data['edges'], data['faces'])

obj = bpy.data.objects.new(name, mesh)
bpy.context.view_layer.objects.active = obj
obj.select = True
mesh.validate(verbose=True)
return obj

def vertex_circle(segments, z):
""" Return a ring of vertices """
verts = []
verts.append(( 0, 0, z))
for j in range(segments):
angle = (math.pi*2) * j / segments
verts.append((math.cos(angle), math.sin(angle), z))
return verts

""" Make a circle """

data = {
'verts': vertex_circle(segments, z),
'edges': [],
'faces': [],
}
data['faces'] = [(j, j+1, 0) for j in range(segments)]
data['faces'].append((segments, 1, 0))

scene = bpy.context.scene
return object_from_data(data, name, scene)

with open( fp ) as csvfile:
""" make series of circles """
for i, row in enumerate( rdr ):
if i == 0: continue # Skip column titles
FRAME, PARTS, XW = row[0:3]

#        FRAME, PARTS, XW, YW, Z, EXP1, EXP2, WALL = row[0:8]
#        parts_number = float(PARTS)
#        add_circle(FRAME, 8, 0, 2, 0, 1, 2)
#        add_circle(FRAME, parts_number, 0, 2, 0, 1, 2)

FRAME,PARTS,XW,YW,Z,EXP1,EXP2,WALL
1001,8,0,2,0,1,2,0
1002,15,1,2,1,1,2,1
1003,32,2,3,2,1,2,1
1004,64,3,4,3,1,2,1
1005,128,4,5,4,1,2,0


The first is the script, the second the csv (please remove .txt extension before use) In add_circle() I placed values PARTS=8 and XW=0. so the script creates one circle with only the name 1001 from CSV.

• I think it would help for others to be able to replicate what you're trying to do if you included a sample CSV file. At the moment it's unclear of the format of the CSV without picking through your code, which could be a barrier to someone providing help. Jul 17 '20 at 7:09
• thank you Rich, i'll do my best. there is a sample csv for download, however due to restrictions when sharing I gave it a .txt extension. and i included a small piece of the CSV here above, Jul 17 '20 at 7:20
• Please can you elaborate on the CSV contents a bit more? I think I have this working but aren't sure of what your final goal is... the columns are labelled FRAME, PARTS, XW, YW, Z, EXP1, EXP2, WALL. From what I gather, FRAME is actually NAME, Z is the Z coordinate. Are XW and YW the expected widths in X and Y directions?. What are EXP1, EXP2, WALL? Jul 17 '20 at 8:37
• Yes, for this example I made XW the Z parameter in the script, so i did not have to clutter the question with more values. This way the script is more straightforward to understand I thought. FRAME=name, PARTS=segments (triangles) and XW=z (at this moment) but it will be expanded with XW and YW for the expected width of the circle/oval, the other values will also be used in the calculations when i have got this working. Jul 17 '20 at 9:08
• although most people can't tell in the end result: data['faces'] = [(j, j+1, 0) for j in range(segments)] should be replaced by data['faces'] = [(j+1, j+2, 0) for j in range(segments-1)] for proper distribution of faces...my bad. Jul 17 '20 at 18:08

There were a couple of issues with the code.

1. The 'obj.select = True' line was referencing an invalid property of 'obj'. I've simply disabled that line.
2. I've put back in the commented out code in the last function to use the values extracted from the CSV. Note that the CSV data is returned as strings so each value needs 'casting' into the expected type - ie, to cast PARTS into an integer you can use int(PARTS)'.

Here's my amended code - additionally implementing XW and YW for the width and depth of each circle (I changed the other functions to pass them through and use them in the calculation of the vertex position :

# -*- coding: utf-8 -*-
"""
Blender: 2.80
Tooltip: 'Creates a shape object'
"""

import bpy, csv
import bpy.types
import time
from mathutils import *
import math

fp = "C:/Users/rich/Desktop/generate-object.csv"

def object_from_data(data, name, scene, select=True):
""" Create a mesh object and link it to a scene """
mesh = bpy.data.meshes.new(name)
mesh.from_pydata(data['verts'], data['edges'], data['faces'])

obj = bpy.data.objects.new(name, mesh)
bpy.context.view_layer.objects.active = obj
#obj.select = True
mesh.validate(verbose=True)
return obj

def vertex_circle(segments, xw, yw, z):
""" Return a ring of vertices """
verts = []
verts.append(( 0, 0, z))
for j in range(segments):
angle = (math.pi*2) * j / segments
verts.append((math.cos(angle)*xw, math.sin(angle)*yw, z))
return verts

def add_circle(name, segments, xw, yw, z):
""" Make a circle """

data = {
'verts': vertex_circle(segments, xw, yw, z),
'edges': [],
'faces': [],
}
data['faces'] = [(j, j+1, 0) for j in range(segments)]
data['faces'].append((segments, 1, 0))

scene = bpy.context.scene
return object_from_data(data, name, scene)

with open( fp ) as csvfile:
""" make series of circles """
for i, row in enumerate( rdr ):
if i == 0: continue # Skip column titles
#FRAME, PARTS, XW = row[0:3]

FRAME, PARTS, XW, YW, Z, EXP1, EXP2, WALL = row[0:8]
parts_number = float(PARTS)
#add_circle(FRAME, 8, 0, 2, 0, 1, 2)
#add_circle(FRAME, parts_number, 0, 2, 0, 1, 2)

• obj.select = True has been replaced with obj.select_set(True)` in 2.8. If the idea is to feed the context into another operator, often pays to have object of interest both active and selected. Jul 17 '20 at 10:41