Gotchas
本文档试图帮助您使用Blender API时在哪些地方可能会带来麻烦,避免一些不稳定性的操作。
1 Using Operators
使用运算符
Blender的操作人员是供用户访问的工具,Python也可以访问它们,但操作有一些限制,这些限制会使它们用起来很麻烦。
这些限制主要有...
- 不能传递诸如物体、网格或材料等数据来操作(只能操作上下文数据)
- 操作符的返回是否操作成功(如果它完成了或者被取消了),在某些情况下,从API的角度来返回操作的结果会更符合逻辑。
- 运营商的投票功能失败的话,API函数会引发一个异常,详细说明原因。
1.1 Why does an operator’s poll fail?
为什么operater的poll会失败
当你调用一个操作有如下的错误提示时:
>>> bpy.ops.action.clean(threshold=0.001) RuntimeError: Operator bpy.ops.action.clean.poll() failed, context is incorrect
Which raises the question as to what the correct context might be?
这就提出了一个问题:正确的环境可能是什么?
Typically operators check for the active area type, a selection or active object they can operate on, but some operators are more picky about when they run.
一般来说,操作员会检查活动区域类型,操作的是选择或活动对象,但是一些operators 在运行时更挑剔。
In most cases you can figure out what context an operator needs simply be seeing how it’s used in Blender and thinking about what it does.
在大多数情况下,你可以弄清楚一个操作需要什么上下文,只需看看它在Blender中是如何使用的,并思考它所做的事情。
Unfortunately if you’re still stuck - the only way to really know whats going on is to read the source code for the poll function and see what its checking.
如果你仍然迷惑的话,就去看看poll函数的源码,看看他在检测什么条件
For Python operators it’s not so hard to find the source since it’s included with Blender and the source file/line is included in the operator reference docs.
查看源码很简单,因为他们就包含在Blender中,并且具体在哪一行都包含在接口文档中
Downloading and searching the C code isn’t so simple, especially if you’re not familiar with the C language but by searching the operator name or description you should be able to find the poll function with no knowledge of C.
下载并查找C源码就不容易了,特别是如果您不熟悉C语言。但是通过搜索操作符名称或描述,您应该能够找到没有C知识的轮询函数。
注意:
Blender does have the functionality for poll functions to describe why they fail, but its currently not used much, if you’re interested to help improve our API feel free to add calls to CTX_wm_operator_poll_msg_set
where its not obvious why poll fails.
Blender有一个用来描述为什么失败的功能,但是它目前没有太多的使用,如果你有兴趣帮助改进我们的API,当你认为反馈的信息不能描述poll为什么失败的话,可以给CTX_wm_operator_poll_msg_set添加调用,并添加你的建议。
>>> bpy.ops.gpencil.draw()
RuntimeError: Operator bpy.ops.gpencil.draw.poll() Failed to find Grease Pencil data to draw into
1.2 The operator still doesn’t work!
operater仍然不起作用
Certain operators in Blender are only intended for use in a specific context, some operators for example are only called from the properties window where they check the current material, modifier or constraint.
在Blender中的某些操作符只是为了在特定的环境中使用,一些操作符仅从属性窗口调用,在那里它们检查当前的内容、修饰符或约束。
像下面这些:
bpy.ops.texture.slot_move
bpy.ops.constraint.limitdistance_reset
bpy.ops.object.modifier_copy
bpy.ops.buttons.file_browse
Another possibility is that you are the first person to attempt to use this operator in a script and some modifications need to be made to the operator to run in a different context, if the operator should logically be able to run but fails when accessed from a script it should be reported to the bug tracker.
另一种可能是,你是第一个试图使用这个operater在脚本和一些要做修改操作符在不同的上下文中运行,如果operater在逻辑上应该能够运行,但不能从一个脚本访问的时候应该报告bug追踪器。
2 Stale Data
旧数据
2.1 No updates after setting values
设置后就没再更新
Sometimes you want to modify values from Python and immediately access the updated values, eg:
有时,您想从Python修改值,并立即访问更新后的值,例如:
Once changing the objects bpy.types.Object.location
you may want to access its transformation right after from bpy.types.Object.matrix_world
, but this doesn’t work as you might expect.
当你修改了bpy.types.Object.location
,你可能想获取它的变换信息bpy.types.Object.matrix_world
,但这并不如你所愿。
Consider the calculations that might go into working out the object’s final transformation, this includes:
考虑计算对象最终转换的计算,包括:
- animation function curves.
- 动画曲线函数。
- drivers and their Python expressions.
- 驱动程序及其Python表达式。
- constraints
- 约束
- parent objects and all of their f-curves, constraints etc.
- 父对象和所有的f曲线,约束等。
To avoid expensive recalculations every time a property is modified, Blender defers making the actual calculations until they are needed.
为了避免高昂的重复计算,Blender每次都在最终需要的时候才重新计算需要的值。
However, while the script runs you may want to access the updated values. In this case you need to call bpy.types.Scene.update
after modifying values, for example:
如果你想立即使属性设置生效的话,需要调用bpy.types.Scene.update
bpy.context.object.location = 1, 2, 3
bpy.context.scene.update()
Now all dependent data (child objects, modifiers, drivers... etc) has been recalculated and is available to the script.
所有相关数据(子对象、修饰符、驱动程序…(etc)已重新计算并可用于脚本。
2.2 Can I redraw during the script?
可以在脚本中重画么?
The official answer to this is no, or... “You don’t want to do that”.
官方给出的答案是 不。因为“你并不想那么做”
To give some background on the topic...
我们假设一个背景:
While a script executes Blender waits for it to finish and is effectively locked until its done, while in this state Blender won’t redraw or respond to user input. Normally this is not such a problem because scripts distributed with Blender tend not to run for an extended period of time, nevertheless scripts can take ages to execute and its nice to see whats going on in the view port.
当一个脚本执行时,Blender等待它完成并被有效锁定,直到它完成,而在这个状态下,Blender不会重新绘制或响应用户输入。通常情况下,这并不是一个问题,因为用Blender分发的脚本往往不会在很长一段时间内运行,但是脚本可以花费很长的时间来执行,而且很好地看到在视口上发生了什么。
Tools that lock Blender in a loop and redraw are highly discouraged since they conflict with Blenders ability to run multiple operators at once and update different parts of the interface as the tool runs.
在循环和重绘中锁定Blender的工具是非常不受鼓励的,因为它们与Blenders可以同时运行多个操作符 并在工具运行时更新接口的不同部分的能力 发生冲突。
So the solution here is to write a modal operator, that is - an operator which defines a modal() function, See the modal operator template in the text editor.
这里的解决方案是编写一个模态运算符,即定义一个modal()函数的操作符,在文本编辑器中看到模态操作符模板。
Modal operators execute on user input or setup their own timers to run frequently, they can handle the events or pass through to be handled by the keymap or other modal operators.
模态运算符执行用户输入或设置他们自己的定时器来频繁地运行,他们可以处理事件或通过键映射或其他模态运算符处理。
Transform, Painting, Fly-Mode and File-Select are example of a modal operators.
转换、绘图、飞行模式和文件选择是模式操作符的例子。
Writing modal operators takes more effort than a simple for
loop that happens to redraw but is more flexible and integrates better with Blenders design.
编写模态运算符需要比简单的循环更大的努力,而不是简单的重新绘制,但更灵活,更集成了Blender的设计。
Ok, Ok! I still want to draw from Python
如果你还想在Python中重画的话
If you insist - yes its possible, but scripts that use this hack wont be considered for inclusion in Blender and any issues with using it wont be considered bugs, this is also not guaranteed to work in future releases.
如果你坚持——是的,这是可能的,但是使用这种方法的脚本不会被认为包含在Blender中,而且使用它的任何问题都不会被认为是错误的,这也不能保证在未来的版本中工作。
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
3 Modes and Mesh Access
模型和网格获取
When working with mesh data you may run into the problem where a script fails to run as expected in edit-mode. This is caused by edit-mode having its own data which is only written back to the mesh when exiting edit-mode.
当使用网格数据时,您可能会遇到一个问题:在编辑模式下,脚本不能按照预期运行。这是由编辑模式造成的,它拥有自己的数据,只有在退出编辑模式时才会返回到网格中。
A common example is that exporters may access a mesh through obj.data
(a bpy.types.Mesh
) but the user is in edit-mode, where the mesh data is available but out of sync with the edit mesh.
一个常见的例子是,导出时可以通过 obj.data
(a bpy.types.Mesh
)获取网格数据,但用户处于编辑模式时,网格数据虽然可用,但与编辑网格不同步。
In this situation you can...
这种情况下,你可以...
- Exit edit-mode before running the tool.
- 在使用tool的时候推出编辑模式
- Explicitly update the mesh by calling
bmesh.types.BMesh.to_mesh
. 通过调用bmesh.types.BMesh.to_mesh
.来明确提出 更新网格- Modify the script to support working on the edit-mode data directly, see:
bmesh.from_edit_mesh
. - 修改脚本,使直接支持在编辑模式下获取数据,参见
bmesh.from_edit_mesh
. - Report the context as incorrect and only allow the script to run outside edit-mode.
- 检查环境是不是在编辑模式下,只让脚本运行在编辑模式外
4 NGons and Tessellation Faces
NGons-将多于四边的面用四边和三边面的组合表示
Tessellation -细分曲面
Since 2.63 NGons are supported, this adds some complexity since in some cases you need to access triangles/quads still (some exporters for example).
由于2.63 NGons支持,这增加了一些复杂性,因为在某些情况下您仍然需要访问三角形/ quad(例如导出的时候)。
There are now 3 ways to access faces:
现在有三种方式获取面片:
bpy.types.MeshPolygon
- this is the data structure which now stores faces in object mode (access asmesh.polygons
rather thanmesh.faces
).bpy.types.MeshPolygon
-这是现在在对象模式下存储面片的数据结构(通过mesh.polygons
获取,而不是mesh.faces
).bpy.types.MeshTessFace
- the result of triangulating (tessellated) polygons, the main method of face access in 2.62 or older (access asmesh.tessfaces
).bpy.types.MeshTessFace
- 用三角形细分曲面的结果,2.62及之前版本获取表面的主要方法(通过mesh.tessfaces获取
).bmesh.types.BMFace
- the polygons as used in editmode.bmesh.types.BMFace
-在编辑模式中使用的多边形。
For the purpose of the following documentation, these will be referred to as polygons, tessfaces and bmesh-faces respectively.
在下面的文档中,这些将分别称为polygons,tessfaces和bmesh - faces。
5+ sided faces will be referred to as ngons
.
5条以上的边将称为ngons。
4.1 Support Overview
支持概述
Usage | bpy.types.MeshPolygon | bpy.types.MeshTessFace | bmesh.types.BMFace |
---|---|---|---|
Import/Create | Poor (inflexible) | Good (supported as upgrade path) | Best |
Manipulate | Poor (inflexible) | Poor (loses ngons) | Best |
Export/Output | Good (ngon support) | Good (When ngons can’t be used) | Good (ngons, extra memory overhead) |
4.2 Creating
All 3 datatypes can be used for face creation.
三种数据类型都可以用来创建面片。
- polygons are the most efficient way to create faces but the data structure is _very_ rigid and inflexible, you must have all your vertes and faces ready and create them all at once. This is further complicated by the fact that each polygon does not store its own verts (as with tessfaces), rather they reference an index and size in
bpy.types.Mesh.loops
which are a fixed array too. - polygons是创建faces的最有效的方法,但其数据结构非常死板僵硬,你必须把所有的顶点和faces都准备好,并立即创建它们。由于每个多边形都不存储自己的顶点数据(与tessfaces一样),更复杂的是,它们引用了
bpy.types.Mesh.loops
中的索引和大小,而这是一个固定的数组。 - tessfaces ideally should not be used for creating faces since they are really only tessellation cache of polygons, however for scripts upgrading from 2.62 this is by far the most straightforward option. This works by creating tessfaces and when finished - they can be converted into polygons by calling
bpy.types.Mesh.update
. The obvious limitation is ngons can’t be created this way. - tessfaces 理论上不用来创建faces,因为它们只是多边形的细分缓存,但在2.62版本之后,这是一种最直接的操作。当细分操作完成后,通过调用
bpy.types.Mesh.update可以将其装换为多边形。一个明显的限制是ngons(多于四个边)不能以这种方法创建。
- bmesh-faces are most likely the easiest way for new scripts to create faces, since faces can be added one by one and the api has features intended for mesh manipulation. While
bmesh.types.BMesh
uses more memory it can be managed by only operating on one mesh at a time. - bmesh-faces可能是用脚本创建faces最简单的方法了,由于faces可以一个接一个地添加,而api有一个用于网格操作的特性。虽然
bmesh.types.BMesh
使用更多的内存,它只能一次只在一个网格上运行。
4.3 Editing
Editing is where the 3 data types vary most.
编辑是三种模式最不同的地方。
- Polygons are very limited for editing, changing materials and options like smooth works but for anything else they are too inflexible and are only intended for storage.
- Polygons编辑起来很有限制,改变材料和选择,比如平滑的作品,但对于其他任何东西,它们太不灵活,只用于存储。
- Tessfaces should not be used for editing geometry because doing so will cause existing ngons to be tessellated.
- Tessfaces不应该用于编辑几何,因为这样做会导致现有的ngons被镶嵌。
- BMesh-Faces are by far the best way to manipulate geometry.
- 到目前为止,BMesh-Faces 是操纵几何的最好方法。
4.4 Exporting
导出
All 3 data types can be used for exporting, the choice mostly depends on whether the target format supports ngons or not.
三种类型的数据都可以导出,选择哪一种主要取决于目标格式是否支持ngons。
- Polygons are the most direct & efficient way to export providing they convert into the output format easily enough.
- Polygons是最直接、最有效的导出方式,它们可以很容易地转换成输出格式。
- Tessfaces work well for exporting to formats which dont support ngons, in fact this is the only place where their use is encouraged.
- Tessfaces可以很好地导出不支持ngons的格式,事实上,这是唯一鼓励使用它们的地方。
- BMesh-Faces can work for exporting too but may not be necessary if polygons can be used since using bmesh gives some overhead because its not the native storage format in object mode.
- BMesh-Faces也可以用于导出,但如果使用polygons它就是没有必要的,因为使用bmesh提供了一些开销,因为它不是对象模式中的原生存储格式。
4.5 Upgrading Importers from 2.62
Importers can be upgraded to work with only minor changes.
只有很小的改动
The main change to be made is used the tessellation versions of each attribute.
最主要变化是使用每个属性的tessellation 版本。
- mesh.faces –>
bpy.types.Mesh.tessfaces
- mesh.uv_textures –>
bpy.types.Mesh.tessface_uv_textures
- mesh.vertex_colors –>
bpy.types.Mesh.tessface_vertex_colors
Once the data is created call bpy.types.Mesh.update
to convert the tessfaces into polygons.
一旦数据创建,就调用bpy.types.Mesh.update
将tessfaces转换为多边形。
4.6 Upgrading Exporters from 2.62
导出的更新
For exporters the most direct way to upgrade is to use tessfaces as with importing however its important to know that tessfaces maynot exist for a mesh, the array will be empty as if there are no faces.
对于导出来说,最直接的升级方式是使用tessfaces作为导入,但是要知道对于一个网格来说tessfaces是不存在,数组将是空的,就好像没有faces一样。
So before accessing tessface data call: bpy.types.Mesh.update
(calc_tessface=True)
.
所以要在访问tessface数据之前调用bpy.types.Mesh.update
。
5 EditBones, PoseBones, Bone... Bones 骨骼绑定
Armature Bones in Blender have three distinct data structures that contain them. If you are accessing the bones through one of them, you may not have access to the properties you really need.
注意:
In the following examples bpy.context.object
is assumed to be an armature object.
5.1 Edit Bones
bpy.context.object.data.edit_bones
contains a editbones; to access them you must set the armature mode to edit mode first (editbones do not exist in object or pose mode). Use these to create new bones, set their head/tail or roll, change their parenting relationships to other bones, etc.
Example using bpy.types.EditBone
in armature editmode:
This is only possible in edit mode.
>>> bpy.context.object.data.edit_bones["Bone"].head = Vector((1.0, 2.0, 3.0))
This will be empty outside of editmode.
>>> mybones = bpy.context.selected_editable_bones
Returns an editbone only in edit mode.
>>> bpy.context.active_bone
5.2 Bones (Object Mode)
bpy.context.object.data.bones
contains bones. These live in object mode, and have various properties you can change, note that the head and tail properties are read-only.
Example using bpy.types.Bone
in object or pose mode:
Returns a bone (not an editbone) outside of edit mode
>>> bpy.context.active_bone
This works, as with blender the setting can be edited in any mode
>>> bpy.context.object.data.bones["Bone"].use_deform = True
Accessible but read-only
>>> tail = myobj.data.bones["Bone"].tail
5.3 Pose Bones
bpy.context.object.pose.bones
contains pose bones. This is where animation data resides, i.e. animatable transformations are applied to pose bones, as are constraints and ik-settings.
Examples using bpy.types.PoseBone
in object or pose mode:
# Gets the name of the first constraint (if it exists)
bpy.context.object.pose.bones["Bone"].constraints[0].name
# Gets the last selected pose bone (pose mode only)
bpy.context.active_pose_bone
Note
Notice the pose is accessed from the object rather than the object data, this is why blender can have 2 or more objects sharing the same armature in different poses.
Note
Strictly speaking PoseBone’s are not bones, they are just the state of the armature, stored in the bpy.types.Object
rather than the bpy.types.Armature
, the real bones are however accessible from the pose bones - bpy.types.PoseBone.bone
5.4 Armature Mode Switching
While writing scripts that deal with armatures you may find you have to switch between modes, when doing so take care when switching out of edit-mode not to keep references to the edit-bones or their head/tail vectors. Further access to these will crash blender so its important the script clearly separates sections of the code which operate in different modes.
This is mainly an issue with editmode since pose data can be manipulated without having to be in pose mode, however for operator access you may still need to enter pose mode.
6 Data Names
数据名称
6.1 Naming Limitations
命名限制
A common mistake is to assume newly created data is given the requested name.
一个常见的错误是假定新创建的数据被给定请求的名称。
This can cause bugs when you add some data (normally imported) then reference it later by name.
当您添加一些数据(通常是导入的),然后再引用它的名称时,这可能会导致bug。
bpy.data.meshes.new(name=meshid) # normally some code, function calls... bpy.data.meshes[meshid]
Or with name assignment...
或名称赋值
obj.name = objname # normally some code, function calls... obj = bpy.data.meshes[objname]
Data names may not match the assigned values if they exceed the maximum length, are already used or an empty string.
如果赋的值超过了最大长度或者已经使用过了或者是一个空串,那么将不会使用。
Its better practice not to reference objects by names at all, once created you can store the data in a list, dictionary, on a class etc, there is rarely a reason to have to keep searching for the same data by name.
最好的做法是不引用对象的名称,一旦创建了可你以将数据存储在列表、字典、类等的列表中,很少有理由需要继续搜索相同的数据。
If you do need to use name references, its best to use a dictionary to maintain a mapping between the names of the imported assets and the newly created data, this way you don’t run this risk of referencing existing data from the blend file, or worse modifying it.
如果你确实需要使用名字作为索引,最好用字典维持一个名字和数据间的映射,这样,您就不会冒着从blend文件中引用现有数据的风险,或者更糟糕地修改它。
# typically declared in the main body of the function. mesh_name_mapping = {} mesh = bpy.data.meshes.new(name=meshid) mesh_name_mapping[meshid] = mesh # normally some code, or function calls... # use own dictionary rather than bpy.data mesh = mesh_name_mapping[meshid]
6.2 Library Collisions
碰撞库
Blender keeps data names unique - bpy.types.ID.name
so you can’t name two objects, meshes, scenes etc the same thing by accident.
在Blender中数据名字是唯一的,你不可能给两个对象命名相同的名字。
However when linking in library data from another blend file naming collisions can occur, so its best to avoid referencing data by name at all.
但当你从其它blend文件中连接库数据时可能出现名字相同的情况,所以最好避免使用名字索引。
This can be tricky at times and not even blender handles this correctly in some case (when selecting the modifier object for eg you can’t select between multiple objects with the same name), but its still good to try avoid problems in this area.
这有时很棘手,甚至在某些情况下,甚至连Blender都不能正确处理(当选择修改器对象时,例如不能在多个对象之间选择相同的名称),但在这一领域中,尽量避免出现问题仍然很好。
If you need to select between local and library data, there is a feature in bpy.data
members to allow for this.
如果您需要在本地和库数据之间进行选择,那么bpy.data中有一个特性成员允许这样做。
# typical name lookup, could be local or library. obj = bpy.data.objects["my_obj"] # library object name look up using a pair # where the second argument is the library path matching bpy.types.Library.filepath obj = bpy.data.objects["my_obj", "//my_lib.blend"] # local object name look up using a pair # where the second argument excludes library data from being returned. obj = bpy.data.objects["my_obj", None] # both the examples above also works for 'get' obj = bpy.data.objects.get(("my_obj", None))
7 Relative File Paths
相对路径
Blenders relative file paths are not compatible with standard Python modules such as sys
and os
.
相对文件路径的Blenders与诸如sys和os这样的标准Python模块不兼容。
Built in Python functions don’t understand blenders //
prefix which denotes the blend file path.
Python内建函数不能理解Blender//这样表示文件路径
A common case where you would run into this problem is when exporting a material with associated image paths.
当你想通过图片路径来导出材质时你可能会遇到这个问题。
>>> bpy.path.abspath(image.filepath)
When using blender data from linked libraries there is an unfortunate complication since the path will be relative to the library rather than the open blend file. When the data block may be from an external blend file pass the library argument from the bpy.types.ID
.
当使用来自链接库的blender数据时,有一个不幸的问题,因为路径将相对于库,而不是开放的blend文件。当数据块可能来自外部blend文件时,从bpy.types.ID
.中传递库参数。
>>> bpy.path.abspath(image.filepath, library=image.library)
These returns the absolute path which can be used with native Python modules.
这将返回可以被Python模块使用的绝对路径。
8 Unicode Problems
编码问题
Python supports many different encodings so there is nothing stopping you from writing a script in latin1
or iso-8859-15
.
Python支持许多不同的编码,因此没有什么可以阻止您在latin1或iso -8859- 15中编写脚本。
See pep-0263
However this complicates matters for Blender’s Python API because .blend
files don’t have an explicit encoding.
然而,这使得Blender的Python API变得更加复杂。blend文件没有明确的编码。
To avoid the problem for Python integration and script authors we have decided all strings in blend files must be UTF-8
, ASCII
compatible.
所有的脚本都应该兼容 UTF-8
, ASCII
This means assigning strings with different encodings to an object names for instance will raise an error.
这意味着给对象名称分配不同的编码字符串会引起错误。
Paths are an exception to this rule since we cannot ignore the existence of non UTF-8
paths on users file-system.
路径是这个规则的一个例外,因为我们不能忽略用户文件系统上的非utf - 8路径的存在。
This means seemingly harmless expressions can raise errors, eg.
这意味着看似无害的表达会引起错误
>>> print(bpy.data.filepath) UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-21: ordinal not in range(128) >>> bpy.context.object.name = bpy.data.filepath Traceback (most recent call last): File "<blender_console>", line 1, in <module> TypeError: bpy_struct: item.attr= val: Object.name expected a string type, not str
Here are 2 ways around filesystem encoding issues:
这里有两种方法围绕文件系统编码问题:
>>> print(repr(bpy.data.filepath)) >>> import os >>> filepath_bytes = os.fsencode(bpy.data.filepath) >>> filepath_utf8 = filepath_bytes.decode('utf-8', "replace") >>> bpy.context.object.name = filepath_utf8
Unicode encoding/decoding is a big topic with comprehensive Python documentation, to avoid getting stuck too deep in encoding problems - here are some suggestions:
下面是对编码方面的建议:
- Always use utf-8 encoding or convert to utf-8 where the input is unknown.
- 总是使用utf - 8编码或当不知道输入的编码类型时转换到utf - 8。
- Avoid manipulating filepaths as strings directly, use
os.path
functions instead. - 避免以字符串直接操作文件路径,使用os.path这个函数。
- Use
os.fsencode()
/os.fsdecode()
instead of built in string decoding functions when operating on paths. - 当操作路径串时,使用
os.fsencode()
/os.fsdecode()来编解码,而不要用字符串编解码器
- To print paths or to include them in the user interface use
repr(path)
first or"%r" % path
with string formatting. - 想要打印路径或是将他们显示在用户界面时,使用
repr(path)
first 或"%r" % path格式化字符串
注意:
Sometimes it’s preferrable to avoid string encoding issues by using bytes instead of Python strings, when reading some input its less trouble to read it as binary data though you will still need to decide how to treat any strings you want to use with Blender, some importers do this.
有时,使用字节而不是Python字符串来避免字符串编码问题是最好的,当读取一些输入时,将其作为二进制数据读取它的难度更小,尽管您仍然需要决定如何处理任何您想要使用Blender的字符串,一些模块就是这样做的。
9 Strange errors using ‘threading’ module
使用“线程”模块的奇怪错误
Python threading with Blender only works properly when the threads finish up before the script does. By using threading.join()
for example.
当线程在脚本执行之前完成时,使用Blender的Python线程才能正常工作。例如,通过使用threading . join()。
Heres an example of threading supported by Blender:
下面是一个Blender支持的线程例子:
import threading import time def prod(): print(threading.current_thread().name, "Starting") # do something vaguely useful import bpy from mathutils import Vector from random import random prod_vec = Vector((random() - 0.5, random() - 0.5, random() - 0.5)) print("Prodding", prod_vec) bpy.data.objects["Cube"].location += prod_vec time.sleep(random() + 1.0) # finish print(threading.current_thread().name, "Exiting") threads = [threading.Thread(name="Prod %d" % i, target=prod) for i in range(10)] print("Starting threads...") for t in threads: t.start() print("Waiting for threads to finish...") for t in threads: t.join()
This an example of a timer which runs many times a second and moves the default cube continuously while Blender runs (Unsupported).
这是一个计时器的例子,该计时器每秒运行多次,并在Blender运行时连续移动默认的数据集(不支持)。
def func(): print("Running...") import bpy bpy.data.objects['Cube'].location.x += 0.05 def my_timer(): from threading import Timer t = Timer(0.1, my_timer) t.start() func() my_timer()
Use cases like the one above which leave the thread running once the script finishes may seem to work for a while but end up causing random crashes or errors in Blender’s own drawing code.
像上面这样的用例,一旦脚本完成,就会让线程运行,但最终会导致在Blender的绘图代码中出现随机的崩溃或错误。
So far, no work has gone into making Blender’s Python integration thread safe, so until its properly supported, best not make use of this.
到目前为止,还没有工作使Blender的Python集成线程安全,所以在它得到适当支持之前,最好不要使用它。
注意:
Pythons threads only allow co-currency and won’t speed up your scripts on multi-processor systems, the subprocess
andmultiprocess
modules can be used with Blender and make use of multiple CPU’s too.
python线程只允许使用并发,而且不会在多处理器系统上加速您的脚本,而subprocess和multiprocess模块则可以使用Blender,并使用多个CPU。
10 Help! My script crashes Blender
脚本使Blender崩溃
Ideally it would be impossible to crash Blender from Python however there are some problems with the API where it can be made to crash.
理想情况下,用Python来使Blender崩溃是不可能的,但是API中有一些问题会导致崩溃。
Strictly speaking this is a bug in the API but fixing it would mean adding memory verification on every access since most crashes are caused by the Python objects referencing Blenders memory directly, whenever the memory is freed, further Python access to it can crash the script. But fixing this would make the scripts run very slow, or writing a very different kind of API which doesn’t reference the memory directly.
准确的说这是一个API BUG,但是修复它就意味着要增加内存访问验证,这将降低脚本速度。
Here are some general hints to avoid running into these problems.
下面是一些避坑指南:
- Be aware of memory limits, especially when working with large lists since Blender can crash simply by running out of memory.
- 要注意内存限制,特别是在使用大列表时,因为Blender可以简单地用内存耗尽而崩溃。
- Many hard to fix crashes end up being because of referencing freed data, when removing data be sure not to hold any references to it.
- 许多难以修复的崩溃最终都是因为引用了自由数据,所以当删除数据时,要确保不再有对象引用。
- Modules or classes that remain active while Blender is used, should not hold references to data the user may remove, instead, fetch data from the context each time the script is activated.
- 在使用Blender时保持活跃的模块或类,不应该保存对用户可能删除的数据的引用,而是每次激活脚本时从上下文获取数据。
- Crashes may not happen every time, they may happen more on some configurations/operating-systems.
- 崩溃可能不会每次都发生,它们可能在某些配置/操作系统上发生更多
注意:
To find the line of your script that crashes you can use the faulthandler
module. See faulthandler docs.
使用faulthandler
模块来查看你的脚本在哪一行崩溃了 See faulthandler docs.
While the crash may be in Blenders C/C++ code, this can help a lot to track down the area of the script that causes the crash.
虽然崩溃可能在Blenders C/ c++代码中,但这有助于跟踪导致崩溃的脚本的区域。
10.1 Undo/Redo
Undo invalidates all bpy.types.ID
instances (Object, Scene, Mesh, Lamp... etc).
Undo操作使所有的实例失效。
This example shows how you can tell undo changes the memory locations.
这个例子展示了如何知道撤销更改内存位置。
>>> hash(bpy.context.object) -9223372036849950810 >>> hash(bpy.context.object) -9223372036849950810 # ... move the active object, then undo >>> hash(bpy.context.object) -9223372036849951740
As suggested above, simply not holding references to data when Blender is used interactively by the user is the only way to ensure the script doesn’t become unstable.
如上所述,当用户交互式地使用Blender时,不保存对数据的引用是确保脚本不会变得不稳定的唯一方法。
10.1.1 Undo & Library Data
One of the advantages with Blenders library linking system that undo can skip checking changes in library data since it is assumed to be static.
Blenders library链接系统的优点之一,它可以跳过检查库数据的变化,因为它被认为是静态的。
Tools in Blender are not allowed to modify library data.
在Blender中不允许修改库数据。
Python however does not enforce this restriction.
Python没有强制要求。
This can be useful in some cases, using a script to adjust material values for example. But its also possible to use a script to make library data point to newly created local data, which is not supported since a call to undo will remove the local data but leave the library referencing it and likely crash.
这在某些情况下是有用的,例如使用脚本调整材料值。但是,使用脚本将库数据指向新创建的本地数据也是可能的,因为调用撤销会删除本地数据,但会让库引用它并可能崩溃 所以这一操作并不建议。
So it’s best to consider modifying library data an advanced usage of the API and only to use it when you know what you’re doing.
因此最好考虑修改库数据,这是API的高级用法,并且只有当您知道自己在做什么的时候才使用它。
10.2 Edit Mode / Memory Access
编辑模式/内存访问
Switching edit-mode bpy.ops.object.mode_set(mode='EDIT')
/ bpy.ops.object.mode_set(mode='OBJECT')
will re-allocate objects data, any references to a meshes vertices/polygons/uvs, armatures bones, curves points etc cannot be accessed after switching edit-mode.
切换到编辑模式后,所有对象的数据将被重新分配。所有指向其他资源的索引都不能使用。
Only the reference to the data its self can be re-accessed, the following example will crash.
只有对数据本身的引用可以被重新访问,下面的例子将会崩溃。
mesh = bpy.context.active_object.data polygons = mesh.polygons bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='OBJECT') # this will crash print(polygons)
So after switching edit-mode you need to re-access any object data variables, the following example shows how to avoid the crash above.
因此,在切换编辑模式后,您需要重新访问任何对象数据变量,下面的示例显示了如何避免上面的崩溃。
mesh = bpy.context.active_object.data polygons = mesh.polygons bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='OBJECT') # polygons have been re-allocated polygons = mesh.polygons print(polygons)
These kinds of problems can happen for any functions which re-allocate the object data but are most common when switching edit-mode.
这些问题可能发生于任何重新分配对象数据的时候,但在切换编辑模式时最常见的函数。
10.3 Array Re-Allocation
数组重新分配
When adding new points to a curve or vertices’s/edges/polygons to a mesh, internally the array which stores this data is re-allocated.
当向一个网格中添加新的指向曲线或顶点/边/多边形的新点时,将重新分配存储该数据的数组。
bpy.ops.curve.primitive_bezier_curve_add() point = bpy.context.object.data.splines[0].bezier_points[0] bpy.context.object.data.splines[0].bezier_points.add() # this will crash! point.co = 1.0, 2.0, 3.0
This can be avoided by re-assigning the point variables after adding the new one or by storing indices’s to the points rather than the points themselves.
这可以通过在添加新变量后重新分配点变量来避免,或者通过存储点的索引而不是点本身。
The best way is to sidestep the problem altogether add all the points to the curve at once. This means you don’t have to worry about array re-allocation and its faster too since reallocating the entire array for every point added is inefficient.
最好的解决办法是一次性添加所有的点。这就意味着你不需要再担心数组的重新分配,而且那也很快比起每次
10.4 Removing Data
删除数据
Any data that you remove shouldn’t be modified or accessed afterwards, this includes f-curves, drivers, render layers, timeline markers, modifiers, constraints along with objects, scenes, groups, bones.. etc.
您删除的任何数据都不应该再修改或访问,包括f曲线、驱动程序、渲染层、时间轴标记、修饰符、约束以及对象、场景、组、骨头。等等
The remove()
api calls will invalidate the data they free to prevent common mistakes.
调用remove()将使它释放的数据无效,这样避免了共同的错误
The following example shows how this precortion works.
下面是个例子
mesh = bpy.data.meshes.new(name="MyMesh") # normally the script would use the mesh here... bpy.data.meshes.remove(mesh) print(mesh.name) # <- give an exception rather than crashing: # ReferenceError: StructRNA of type Mesh has been removed
But take care because this is limited to scripts accessing the variable which is removed, the next example will still crash.
要避免在脚本中访问已经删除的数据,这会导致崩溃。
mesh = bpy.data.meshes.new(name="MyMesh") vertices = mesh.vertices bpy.data.meshes.remove(mesh) print(vertices) # <- this may crash
11 sys.exit
Some Python modules will call sys.exit()
themselves when an error occurs, while not common behavior this is something to watch out for because it may seem as if Blender is crashing since sys.exit()
will close Blender immediately.
当出现错误时,一些Python模块将调用sys . exit()本身,当不是常规的行为,这是值得注意的,因为sys.exit()可以立即关闭Blender就像Blender崩溃了一样。
For example, the argparse
module will print an error and exit if the arguments are invalid.
例如argparse
模块将会打印错误信息,如果参数无效的话就会推出。
An ugly way of troubleshooting this is to set sys.exit = None
and see what line of Python code is quitting, you could of course replacesys.exit
with your own function but manipulating Python in this way is bad practice.
一种不优雅的解决方法是使sys.exit = None并看看是Python哪行代码出错了,你也可以用你自己的函数替换sys.exit 但这真是很糟的。