Blender 3.4.1 Scripting Python Console won't copy out script and errors to clipboard?

I’m trying to run some Python script in Blender 3.4.1. I navigate to the Blender 3.4.1 Scripting tab, which opens the Python Console. I am able to copy my Python code and paste into the Blender Python Console. However there are coding errors which I need to copy out and work on. Unfortunately, the clipboard won’t update from the previous copied code, even when I highlight the error code and right click the mouse to copy, as well as Ctrl+c.

I have tested with fewer lines of Python code and there’s no errors, so my code needs fixing, but there’s so much data and indentations that I need to copy out, rather than type it out and make a typo.

Here’s the code I’m pasting in:

import bpy
import bmesh
import math

def create_screw_4_40():
    # Screw parameters (approximate for a 4-40 screw)
    thread_pitch = 0.635  # 40 threads per inch ~ 0.635 mm
    major_diameter = 2.84  # Major diameter in mm
    minor_diameter = 2.40  # Minor diameter in mm
    screw_length = 20  # Length in mm
    head_diameter = 5.5  # Approximate head diameter in mm
    head_height = 2.5  # Approximate head height in mm
    
    # Create screw shaft
    bpy.ops.mesh.primitive_cylinder_add(
        vertices=32, radius=minor_diameter / 2, depth=screw_length, location=(0, 0, screw_length / 2))
    screw_shaft = bpy.context.object
    screw_shaft.name = "Screw_Shaft"
    
    # Create screw head
    bpy.ops.mesh.primitive_cylinder_add(
        vertices=32, radius=head_diameter / 2, depth=head_height, location=(0, 0, screw_length + head_height / 2))
    screw_head = bpy.context.object
    screw_head.name = "Screw_Head"
    
    # Create a helix for the threads
    bpy.ops.mesh.primitive_curve_add(
        type='CURVE', location=(0, 0, 0))
    screw_curve = bpy.context.object
    screw_curve.name = "Thread_Curve"
    
    # Convert the curve into a screw thread
    screw_curve.data.dimensions = '3D'
    screw_curve.data.bevel_depth = 0.3  # Approximate thread depth
    screw_curve.data.bevel_resolution = 4
    screw_curve.data.fill_mode = 'FULL'
    screw_curve.data.use_fill_caps = False
    
    # Add screw modifier
    modifier = screw_curve.modifiers.new(name="Screw", type='SCREW')
    modifier.axis = 'Z'
    modifier.angle = math.radians(360 * (screw_length / thread_pitch))
    modifier.steps = 100
    modifier.render_steps = 32
    modifier.screw_offset = thread_pitch
    modifier.use_merge_vertices = False
    
    # Join objects
    bpy.ops.object.select_all(action='DESELECT')
    screw_shaft.select_set(True)
    screw_head.select_set(True)
    bpy.context.view_layer.objects.active = screw_shaft
    bpy.ops.object.join()
    
    print("4-40 screw created successfully!")

create_screw_4_40()

The “normal” way to do it, is to open the text editor - paste your code there and then just run the code. Or is there any special reason why you want to run that in the python console?

If you run the code, you will get this error:

Python: Traceback (most recent call last):
File “/Text”, line 57, in
File “/Text”, line 27, in create_screw_4_40
File “/Applications/Blender.app/Contents/Resources/4.5/scripts/modules/bpy/ops.py”, line 109, in call
ret = _op_call(self.idname_py(), kw)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: Calling operator “bpy.ops.mesh.primitive_curve_add” error, could not be found

So looks like that command doesn’t exist.

Maybe you look for
bpy.ops.curves.add_bezier
and read here: https://docs.blender.org/api/current/bpy.ops.curves.html

Unsure about the text editor? I’m in the scripting page, as per screenshots.


Full errors here, sorry for screenshots, but unable to copy out data:



20250217_11h37m58s_grim

You shouldn’t use the Python Console for that. What @Blender_Fun1 suggested is the better way.

In the Text Editor (inside Blender), press the ‘New’ button to create an new text datablock.
Then paste your code in the text area, and press the ‘Run Script’ button ( :arrow_forward:).

You’ll see the errors and outputs in the System Console (so you should also have that opened). Then make the corrections to your code until it runs flawlessly.

Thanks, that progressed the debugging.
I had to change line 27 to

bpy.ops.curve.primitive_nurbs_path_add(location=(0, 0, 0))

and now output somewhat of a screw.
Somehow the threads are missing, so might play around with some plugins to make it easier.
20250217_13h47m43s_grim

Tried some other code, but also a bad output.

 import bpy
import math

# Screw parameters (in inches)
screw_diameter = 0.12
screw_length = 1.0
thread_pitch = 1/40
head_diameter = 0.21
head_height = 0.08
head_type = "hex"  # "hex" or "slotted"  <--- DEFINE HEAD TYPE HERE (Choose one)

# Create screw object
bpy.ops.mesh.primitive_cylinder_add(radius=screw_diameter / 2, depth=screw_length)
screw = bpy.context.object
screw.name = "Screw"

# Create threads (using a curve and screw modifier)
curve_data = bpy.data.curves.new(name="ScrewThreadCurve", type='CURVE')
curve_object = bpy.data.objects.new("ScrewThreadCurve", curve_data)
bpy.context.collection.objects.link(curve_object)

# Create a *single*, continuous Bézier spline with correct direction
polyline = curve_data.splines.new('BEZIER')
polyline.bezier_points.add(2) # Add two points
polyline.bezier_points[0].co = (screw_diameter / 2, 0, -0.01) # Start at Z=-0.01 (below screw)
polyline.bezier_points[1].co = (screw_diameter / 2, 0, screw_length) # Go up to screw_length

# Set handle types for smooth curve (important!)
polyline.bezier_points[0].handle_left_type = 'AUTO'
polyline.bezier_points[0].handle_right_type = 'AUTO'
polyline.bezier_points[1].handle_left_type = 'AUTO'
polyline.bezier_points[1].handle_right_type = 'AUTO'

curve_data.dimensions = '3D'

# Add screw modifier
screw_modifier = screw.modifiers.new(name="Screw", type='SCREW')
screw_modifier.object = curve_object
screw_modifier.axis = 'Z'
screw_modifier.screw_offset = thread_pitch
screw_modifier.iterations = int(screw_length / thread_pitch)

# Apply the screw modifier (Blender 3.4 way)
bpy.context.view_layer.objects.active = screw  # Make the screw the active object
bpy.ops.object.modifier_apply(modifier="Screw") # Apply by modifier name

print("Screw modifier applied.") # Debug print

# Create head (MANUAL HEX HEAD CREATION - NO BOOLEAN)
if head_type == "hex":
    # ... (Hex head creation code - same as before)
    bpy.ops.mesh.primitive_cylinder_add(radius=head_diameter / 2, depth=head_height, align='WORLD', location=(0, 0, screw_length))
    head = bpy.context.object
    head.name = "ScrewHead"

    # Enter edit mode for the head
    bpy.context.view_layer.objects.active = head
    bpy.ops.object.mode_set(mode='EDIT')

    # Select the top face (Blender 3.4 Compatible)
    for face in head.data.polygons:
        if face.center[2] > head_height / 2 - 0.001 and face.center[2] < head_height / 2 + 0.001:  # Check Z-coordinate of face center with tolerance
            face.select = True
        else:
            face.select = False

    # Extrude and Scale for Hex Shape
    bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={"value":(0, 0, 0)})
    bpy.ops.transform.resize(value=(0.866025, 0.866025, 1), constraint_axis=(False, False, False)) # Scale for accurate hex

    # Exit edit mode
    bpy.ops.object.mode_set(mode='OBJECT')

elif head_type == "slotted":
    # ... (Slotted head code - you'll need to add this, possibly manually as well)
    pass

else:
    print("Invalid head type")

# Parent head to screw
head.parent = screw
head.location[2] = screw_length

print("Head parented to screw.") # Debug print

# Smooth shading
bpy.ops.object.shade_smooth() # Smooth screw
head.select_set(True)
bpy.ops.object.shade_smooth() # Smooth head

print("Smooth shading applied.") # Debug print

print("Screw created!")

#More Debugging
print("Screw Object Location:", screw.location)
print("Curve Object Location:", curve_object.location)
print("Head Object Location:", head.location)

print("Screw Object Scale:", screw.scale)
print("Curve Object Scale:", curve_object.scale)
print("Head Object Scale:", head.scale)

print("Screw Object Dimensions:", screw.dimensions)
print("Curve Object Dimensions:", curve_object.dimensions)
print("Head Object Dimensions:", head.dimensions)

print("Curve Data Dimensions:", curve_data.dimensions)

print("Curve Bezier Point 1 Coords:", polyline.bezier_points[0].co)
print("Curve Bezier Point 2 Coords:", polyline.bezier_points[1].co)

20250217_15h45m47s_grim

Finally, a 3rd code attempt doesn’t create thread, but a nice screw, albeit too small.

import bpy
import math

# Clear existing objects
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)

# Parameters for #4-40 screw
major_diameter = 0.112  # Major diameter in inches
minor_diameter = 0.089  # Minor diameter in inches
thread_pitch = 1 / 40  # Thread pitch in inches (40 threads per inch)
length = 0.5            # Length of the screw in inches
head_diameter = 0.228   # Head diameter in inches
head_height = 0.075     # Head height in inches

# Convert inches to Blender units (1 inch = 0.0254 meters)
scale = 0.0254
major_diameter *= scale
minor_diameter *= scale
thread_pitch *= scale
length *= scale
head_diameter *= scale
head_height *= scale

# Create the screw shank
bpy.ops.mesh.primitive_cylinder_add(vertices=32, radius=minor_diameter/2, depth=length, location=(0, 0, length/2))
shank = bpy.context.object
shank.name = "Screw_Shank"

# Create the screw head
bpy.ops.mesh.primitive_cylinder_add(vertices=32, radius=head_diameter/2, depth=head_height, location=(0, 0, -head_height/2))
head = bpy.context.object
head.name = "Screw_Head"

# Create the threads
thread_length = length
thread_resolution = 32
thread_height = (major_diameter - minor_diameter) / 2
thread_start = 0

# Create a screw thread profile
bpy.ops.curve.primitive_bezier_curve_add()
thread_profile = bpy.context.object
thread_profile.name = "Thread_Profile"
thread_profile.data.dimensions = '2D'
thread_profile.data.resolution_u = 2

# Adjust the thread profile
thread_profile.data.splines[0].bezier_points[0].co = (0, 0, 0)
thread_profile.data.splines[0].bezier_points[1].co = (thread_height, thread_pitch/2, 0)
thread_profile.data.splines[0].bezier_points[0].handle_right = (thread_height/2, 0, 0)
thread_profile.data.splines[0].bezier_points[1].handle_left = (thread_height/2, thread_pitch/2, 0)

# Manually create a helix for the thread path
def create_helix(name, radius, height, turns, resolution):
    mesh = bpy.data.meshes.new(name)
    obj = bpy.data.objects.new(name, mesh)
    bpy.context.collection.objects.link(obj)
    
    verts = []
    edges = []
    
    for i in range(resolution):
        angle = 2 * math.pi * turns * (i / (resolution - 1))
        x = radius * math.cos(angle)
        y = radius * math.sin(angle)
        z = height * (i / (resolution - 1))
        verts.append((x, y, z))
        
        if i > 0:
            edges.append((i - 1, i))
    
    mesh.from_pydata(verts, edges, [])
    return obj

# Create the helix path
helix = create_helix(
    name="Thread_Path",
    radius=minor_diameter/2 + thread_height/2,
    height=thread_length,
    turns=thread_length / thread_pitch,
    resolution=thread_resolution
)

# Apply the thread profile to the helix path
bpy.ops.object.select_all(action='DESELECT')
thread_profile.select_set(True)
helix.select_set(True)
bpy.context.view_layer.objects.active = helix
bpy.ops.object.parent_set(type='FOLLOW')

# Convert the thread to a mesh
bpy.ops.object.convert(target='MESH')

# Combine all parts
bpy.ops.object.select_all(action='DESELECT')
shank.select_set(True)
head.select_set(True)
helix.select_set(True)
bpy.context.view_layer.objects.active = shank
bpy.ops.object.join()

# Set the origin to the center of the screw
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')

# Smooth the screw
bpy.ops.object.shade_smooth()

# Set the screw to the world origin
bpy.context.object.location = (0, 0, 0)

print("Screw created successfully!")