You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blender文本输入插件开发遇阻:bpy安装及字体路径错误

Blender插件开发报错:bpy.utils.user_resource('FONTS')参数无效

问题背景

我用PyCharm开发Blender的bpy文本输入插件,计划实现以下功能:

  • 通过UI输入两行文本
  • 调用通用位置的自定义字体
  • 将文本导入并转为网格对象
  • 创建曲线并让文本对齐曲线
  • 设置Text_1和Text_2的X轴旋转90度

但插件运行时触发报错,提示bpy.utils.user_resource('FONTS')参数无效,该函数仅接受('DATAFILES', 'CONFIG', 'SCRIPTS', 'AUTOSAVE')类型的参数。

报错详情

Python: Traceback (most recent call last):
  File "C:\Foundation\Blender\4.1\scripts\addons\_init4_.py", line 75, in execute
    self.process_field1_text(context, text1)
  File "C:\Foundation\Blender\4.1\scripts\addons\_init4_.py", line 132, in process_field1_text
    font_path = os.path.join(bpy.utils.user_resource('FONTS'), "My decorative font.ttf")
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\4.1\4.1\scripts\modules\bpy\utils\__init__.py", line 749, in user_resource
    target_path = _user_resource(resource_type, path=path)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: expected a string in ('DATAFILES', 'CONFIG', 'SCRIPTS', 'AUTOSAVE'), got 'FONTS'

插件代码

classes = (
    TextImporterProperties,
    TEXT_PT_importer_panel,
    TEXT_OT_import_to_blender,
)

# Registration
def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.text_importer_props = bpy.props.PointerProperty(type=TextImporterProperties)

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)
    del bpy.types.Scene.text_importer_props

import bpy.ops.curve.primitive_bezier_circle_add
(radius=1, enter_editmode+False, align_'WORLD', location=(0, 0, 0), scale=(1, 1, 1)) as bpy
import os
from bpy.props import StringProperty

# Custom properties to store the text field values
class TextImporterProperties(bpy.types.PropertyGroup):
    text_field_1: StringProperty(
        name="Text Field 1",
        description="Enter text for field 1",
        default=""
    )
    
    text_field_2: StringProperty(
        name="Text Field 2",
        description="Enter text for field 2",
        default=""
    )

# Panel to display the text fields
class TEXT_PT_importer_panel(bpy.types.Panel):
    bl_label = "Text Importer"
    bl_idname = "TEXT_PT_importer"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'Text Importer'
    
    def draw(self, context):
        layout = self.layout
        scene = context.scene
        text_props = scene.text_importer_props
        
        # Text Field 1
        layout.label(text="Text Field 1:")
        layout.prop(text_props, "text_field_1", text="")
        
        # Text Field 2
        layout.label(text="Text Field 2:")
        layout.prop(text_props, "text_field_2", text="")
        
        # Import button
        layout.separator()
        layout.operator("text.import_to_blender", text="Import Text")

# Operator to handle the import action
class TEXT_OT_import_to_blender(bpy.types.Operator):
    bl_idname = "text.import_to_blender"
    bl_label = "Import Text to Blender"
    bl_description = "Import the text from both fields into Blender"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        scene = context.scene
        text_props = scene.text_importer_props
        
        text1 = text_props.text_field_1
        text2 = text_props.text_field_2
        
        # Process text from Field 1
        if text1:
            self.process_field1_text(context, text1)
        
        # Process text from Field 2
        if text2:
            self.process_field2_text(context, text2)
        
        self.report({'INFO'}, "Text imported and processed successfully")
        return {'FINISHED'}
    
    def create_text_object(self, context, text_content, name, location):
        # Create a new text curve data block
        text_curve = bpy.data.curves.new(type="FONT", name=name)
        text_curve.body = text_content
        
        # Create a new object with the text curve
        text_obj = bpy.data.objects.new(name, text_curve)
        text_obj.location = location
        
        # Link the object to the current collection
        context.collection.objects.link(text_obj)
        
        return text_obj
        
    def create_cylinder(self, context, name):
        # Add a cylinder
        bpy.ops.mesh.primitive_cylinder_add(vertices=146, radius=2.5, depth=4.0)
        cylinder = context.active_object
        cylinder.name = name
        
        # Set cap fill type to "Nothing"
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_all(action='SELECT')
        bpy.ops.mesh.delete(type='ONLY_FACE')
        bpy.ops.object.mode_set(mode='OBJECT')
        
        # Set location X to 0 - Test set for offset if needed
        cylinder.location.x = 0.0

        
    def process_field1_text(self, context, text_content):
        # Create text object
        text_obj = self.create_text_object(context, text_content, "Field1Text", (0, 0, 0))
        
        # Select the text object
        bpy.ops.object.select_all(action='DESELECT')
        text_obj.select_set(True)
        context.view_layer.objects.active = text_obj
        
      -------
        
        # Convert text to curve
        bpy.ops.object.convert(target='CURVE')
        
        # Create cylinder for text deformation
        cylinder = self.create_cylinder(context, "Field1Cylinder")
        
        # Select the text again
        bpy.ops.object.select_all(action='DESELECT')
        text_obj.select_set(True)
        context.view_layer.objects.active = text_obj
        
        # Set geometry taper object to cylinder
        text_obj.data.taper_object = cylinder
        
        # Add curve modifier
        curve_modifier = text_obj.modifiers.new(name="Curve", type='CURVE')
        curve_modifier.object = cylinder
        
        # Set rotation X to -90 degrees
        text_obj.rotation_euler.x = -1.5708  # -90 degrees in radians
        
        return text_obj
        
    def process_field2_text(self, context, text_content):
        # Create text object
        text_obj = self.create_text_object(context, text_content, "Field2Text", (0, 0, 0))
        
        # Select the text object
        bpy.ops.object.select_all(action='DESELECT')
        text_obj.select_set(True)
        context.view_layer.objects.active = text_obj
        
      -------
        
        # Convert text to curve
        bpy.ops.object.convert(target='CURVE')
        
        # Create cylinder for text deformation
        cylinder = self.create_cylinder(context, "Field2Cylinder")
        
        # Select the text again
        bpy.ops.object.select_all(action='DESELECT')
        text_obj.select_set(True)
        context.view_layer.objects.active = text_obj
        
        # Set geometry taper object to cylinder
        text_obj.data.taper_object = cylinder
        
        # Add curve modifier
        curve_modifier = text_obj.modifiers.new(name="Curve", type='CURVE')
        curve_modifier.object = cylinder
        
        # Set deform axis to -X
        curve_modifier.deform_axis = 'NEG_X'
        
        return text_obj

if __name__ == "__main__":
    register()

修复方案

1. 替换字体路径获取的错误写法

Blender的bpy.utils.user_resource不支持'FONTS'参数,可通过以下两种方式获取字体路径:

方法一:用bpy.utils.system_resource获取系统字体目录

font_path = os.path.join(bpy.utils.system_resource('FONT'), "My decorative font.ttf")

方法二:手动指定插件内的字体路径

如果自定义字体放在插件目录的fonts文件夹下,可直接用相对路径:

addon_dir = os.path.dirname(os.path.abspath(__file__))
font_path = os.path.join(addon_dir, "fonts", "My decorative font.ttf")

2. 修复代码中的语法错误

原代码存在两处语法问题,需修正:

  • 删除错误的导入语句:import bpy.ops.curve.primitive_bezier_circle_add及后续括号代码,直接用import bpy即可。
  • 删除process_field1_text和process_field2_text函数中的-------注释,避免语法报错。

修正后的字体加载示例代码:

# 替换错误的导入部分
import bpy
import os
from bpy.props import StringProperty

# ... 其他代码保持不变

def process_field1_text(self, context, text_content):
    # Create text object
    text_obj = self.create_text_object(context, text_content, "Field1Text", (0, 0, 0))
    
    # Select the text object
    bpy.ops.object.select_all(action='DESELECT')
    text_obj.select_set(True)
    context.view_layer.objects.active = text_obj
    
    # 加载自定义字体
    font_path = os.path.join(bpy.utils.system_resource('FONT'), "My decorative font.ttf")
    if os.path.exists(font_path):
        font = bpy.data.fonts.load(font_path)
        text_obj.data.font = font
    
    # Convert text to curve
    bpy.ops.object.convert(target='CURVE')
    
    # ... 后续代码保持不变

内容的提问来源于stack exchange,提问作者William Rickard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 13:44:57