您好，欢迎使用艺术家脚本。我叫Sybren。
在这一集中，我们将介绍“自描绘属性”。
在上一个视频中，我们创建了一个面板，并向其中添加了现有属性。
这已经非常强大。
如果这足以满足您的情况，请不要使其变得更加复杂。
越简单越好。
但是，在某些情况下，您想将自己的数据附加到Blender中的事物上。
首先，让我们看一下其中的属性。
在整个系列中，您已经看到了诸如
object.location”或“ context.scene”之类的现有属性。
然后，您可以在“自描绘属性”面板。
也许您还看到了这些属性，这些地方的“编辑”按钮消失了，
并且显示了“API Define”，这意味着它是从Python描绘的。
这也是我们将在本视频中看到的内容。
为了给我们一个具体的目标，我们将创建一个Mass OBJ Importer。
每个场景都会获得一个包含以下内容的属性
包含其OBJ文档的文档夹的路径。接着，
我们将创建一个运算符，从该文档夹加载所有OBJ文档。
最后，我们将研究创建一个从磁盘重新加载这些OBJ文档的运算符。
所以这样
您可以一键重载所有OBJ文档。
这是很多任务作。因此，让我们开始吧。
对于本集，我们将创建一个新的Add-on，我已经开始了。
我添加了一个新的Python文档。
我已将其安装为插件，并将其加载到我的编辑器中，
这是Visual Studio代码。
我将快速解决这个问题，因为您在之前的视频中已经看过所有这些内容。
如果您还没有看到它，请观看有关创建自己的插件和创建用户界面的视频。
因此，我们从BL信息字典开始。
我已将名称设置为“脚本脚本添加进程的艺术家”“大量导入进程”。
其余信息几乎与您期望的一样，
接下来是“导入bpy”。
然后，我想在那里的面板中提供我们要做的所有事情。
我们可以显示按钮，我们可以显示属性，我们可以做我们想做的所有事情。
我通过复制代码创建了一个新面板
我们对猴子网格的代码，然后对其进行调整。
因此，除了具有标签“mass import”(质量导入)外，
它几乎相同，并且也反映在类名中。
现在称为“查看3D PT批量导入”
我们将要画些东西，但还不是。然后，我们当然具有注册和注销功能。
这些对于任何插件都是必需的。我们将向该文档添加更多的类。
因此，我们将有一个运算符进行批量导入。
然后我们还会有一个运算符
一次重新导入一个对象，然后再运行一个运算符
大量重新导入所有导入的对象。
因此，我们将拥有更多。而且我不想复制粘贴
“bpy.utils.unregister类”，然后确保
在未注册的目录中，还复制到unregister类，并管理所有这些。
因此，我创建了一个列表，而不是
称为Blender Classes，其中只有我们现在拥有的面板。
然后，在注册和注销之前，
我们使用前循环对其进行循环。
因此，对于Blender类和Blender类，将超过此列表。 对于该列表中的每个项目，
它将调用“bpy.utils.register类”或“取消注册类”
这使得以后添加新内容变得容易一些，因为可以将新类添加到此列表中。
现在，我们的代码非常简单。
让我们看一下它实际上在Blender中有效，这样我们就可以确定
当我们开始添加内容时，我们是在实际有效的基础上添加的。
因此，在用户首选项中，激活插件。
然后在这里的“猴子”选项卡中，我们可以看到“批量导入”面板。
当然，还没有画出来，但这就是我们所期望的。
因此，根据我们的代码“在面板本身存在的情况下进行注册和注销”，
这都是正确的。
好的。因此，让我们开始创建我们想要的属性。
实际上，这很简单。
您知道“bpy.types”中有很多东西，并且我们希望在场景中包含
我们想要的OBJ文档的导入路径。
因此，我们从“bpy.types.Scene”开始， 使用大写字母“Scenene”，
“.”然后是我们财产的名称。
我们称之为“大量导入路径”。
现在，这与将自己的属性添加到自己的运算符非常相似，
除了，与运算符一起分类别，我们可以使用
声明属性，现在该类已经存在。
因此，我们无法对其添加任何内容。所以有点不同。
因此，我们使用“=”符号代替使用“：”，然后再次分配属性。
它们甚至是道具的路径，文档路径或目录路径只是一个字符串。
这样就给我们一个String属性。
现在为用户界面，我们可能要给它起一个好名字。
因此，我们将其称为“obj文档夹”，
并且它们总是以“，”结尾，因为那样，我可以在下一行添加内容
而无需返回并再次添加“，”。
这是有效的Python，它使您的更改更加本地化。您现在可以添加
其他内容，而无需更改上面的行。
这在Register函数中，因此我们在其中添加了一个属性。
当然，我们必须在“取消注册”中再次将其删除以清理所做的事情。
然后，基本上，您接受该内容，然后说“删除”该属性。
这就告诉Python，只需删除它即可。这就足够了。
让我们看看它在Blender中的外观。
在这里，我们必须重新加载我们的代码。在Blender 2.83及更高版本中，
可以在这里使用重新加载脚本来做到这一点。
如果您使用的是Blender的旧版本，请按F3，
然后输入“重新加载脚本”。他们俩都做同样的事情。
除了通过菜单，您可以在按Q时将其添加到快捷菜单中。这是我将使用的方法
从现在开始。因此，我们重新加载了代码。
让我们来看看场景。
你去 我们拥有自己的属性“大量导入路径”。
默认值是空的，因为我们没有设置任何默认值，它就在这里，每个场景都会有它。
我们还可以从Python更改属性。
请注意此处的“场景”选项卡中的“自描绘属性”面板。
看一看，当我实际分配一个值时会发生什麽。
您必须使用鼠标一点点才能使其重绘。
但是您可以看到，现在，我们已经为其分配了一个值。
它实际上存储在“自描绘属性”中。
它是API描绘的，这意味着您不能只删除它。
这是象这样使用“属性”的主要优点之一，而不是仅单击此处的“添加”按钮
并创建一个新的。
通过Python创建它时，可以确保该属性在那里。
即使未设置，也将具有默认值。因此，不必担心。
另一个优点是，我们可以设置子类型。
这将向Blender指示我们正在谈论的字符串类型。
所以这将是一条路径，
如果它有个小按钮，您可以使用文档浏览器为您选择路径，那就太好了，
无需在Python控制台中键入它。
一旦正确设置了子类型，所有这些操作都将由Blender完成。
来做吧。
子类型已设置
按Subtype参数，然后传递一个表示Subtype的字符串。
为了找到特定属性的子类型，
您可以返回Blender，"帮助"，
Python，API参考，并蒐索“bpy.props.string属性”
然后它会告诉您有哪个子类型。
在我们的情况下，它是目录路径或“目录路径”。
现在，让我们重新加载代码，并在接口的这一角看到这里发生的情况。
你去 它具有浏览按钮，您可以选择目录。
因为我将其设置为傻值，所以它仅从傻目录开始，因为它不是有效路径。
但是如果我将其更改为“//”，则表示
Blend文档的当前目录，
它将在这里打开它，我可以导航到OBJ文档目录，
单击“接受”，然后在此处设置路径。
这正是我们想要的，当然，除了我们希望在自己的面板中运行此行为。
来做吧。在我们的面板代码中
我们可以删除通行证，因为我们实际上将要做一些事情，让我们从获取布局开始。
我们以前没有这样做，但是确实知道每个“自我”(某物)
我们将做一个小小的舞蹈，看看是否在该类中描绘了， 如果没有，请看一下父级，是否在该类中描绘了，
如果没有的话，看看祖父母等等。所以我们说
“layout=self.layout”，我们只跳舞一次，然后只使用layout对象。
让我们添加一列，
然后添加属性。该属性是在场景中描绘的，
即“context.scene”。 属性名称是
“大量导入路径”。这就对了。
重新加载代码，然后有“属性”浏览按钮。
现在，让我们创建一个运算符，该运算符从该目录导入所有OBJ文档。
我想命名相似名称导入OBJ文档已经存在的运算符。
因此，让我们来看一下。那就是“bpy.ops.import_scene.obj”
所以我们打电话给我们的接线员
“导入scene.obj质量”。所以在这里，我们有一个空运算符。
您之前已经看过。我只是以不同的方式命名这些，
称为“imports scene.obj mass”，它具有Execute函数。
我把这个小注释作为参考，然后它称为
“self.report”。
这采用的报告类型可以是信息，警告或错误，然后是未报告的字符串。
它返回“已取消”，因为尚未运行任何操作。
同样，我正在采取一些小步骤。
我首先确保您的打印机在那里，并且已正确注册。然后，我们可以将其添加到面板中。
我们可以单击该按钮，可以看到它可以工作，然后再添加更多的复杂性。
所以运算符在这里。
现在，我复制了此名称，然后转到Blender classes列表，
我将名称添加到列表中，并负责注册和注销。
现在剩下的就是将其添加到面板中。
因此，我们需要的只是“BL ID名称”。
粘贴它，保存在Blender中，重新加载脚本。我们有一个按钮，我们单击它。
它说“没有代码可从/ /加载”，这是我们期望的错误。
现在，下一步是找到obj文档，然后将其导入。之后，我们希望能够
重新导入它们。所以我们必须跟踪
从哪个文档导入了哪个对象。
最好的方法是向对象类型添加另一个属性。所以现在就开始做
在我们开始导入它们之前。
在这里，在Register函数中，我们将运行与场景相同的操作，
但然后到一个对象。
然后，它也变成了String属性，
但随后使用OBJ文档而不是OBJ文档夹。
因此，文档夹和文档名的组合将为我们提供OBJ文档的最终路径。
因为没有仅文档名的子类型，所以仅用于完整的文档路径或完整的目录路径。
仅将其保留为字符串，并且不描绘任何子类型。
因此，在查看导入之前，请将此属性添加到面板中。
我们在这里有面板代码，让我们为此创建一个新列。
那里，我们拥有属性。但是请记住“context.object”
可以是无，所以我们必须对此加一点警剔。
在Python中，没有一个被认为是错误的，而在实际对象中则被认为是正确的。
因此，如果“context.object”的求值结果为“true”， 则它是真实对象，然后绘制该属性。
它将评估为“假”
如果没有活动对象，则它将绘制标签。
试一试吧。
在这里，我们有了我们的属性，可以将其设置为“每个对象”，
如你看到的。
现在让我们构建导入本身。
因此，在这里，我们有大量进口商运营商。让我们写一些布局
它应该做什麽。它必须找到目标文档，
对于每个文档，
导入它
并在“对象属性”中记录其文档名。让我们看看第一步。
实际上，这将采取一些步骤。
因为要查找文档，我们必须使用Python功能。
而且Python无法理解相对于当前Blend文档的Blender特定的“//”含义。
因此，我们必须将其转换为Python可以理解的内容，
然后使用它来查找OBJ文档，然后将其转换回
Blender可以理解的东西，然后导入文档。
首先，我们必须将“//”转换为绝对值，这是通过...
“bpy.path.abspath”中的functin完成的
“bpy.path.abspath(context.scene.mass_import_path”
将为我们提供我们要导入的文档的绝对路径。
我们可以通过在错误消息中报告此内容来进行检查。
只是暂停视频，尝试一下，然后再回到这里。
现在，我们必须将其转换为Python的Path，Lib库中的path对象。
然后，我们可以使用此导入路径变量来查找所有OBJ文档， 并象这样一遍遍地遍历它们。
这将找到与“* .obj”匹配的所有内容，因此文档或目录以该“obj”结尾。
从现在开始，我将假设您不会创建任何子目录
“(something).obj”，
并且所有内容都是我们导入的实际OBJ文档。
现在，我们要做的就是导入它。
在Blender中
转到OBJ导入进程，然后按Ctrl C，然后按Ctrl V，代码。
这会将我们拥有的该路径对象转换回字符串，以便Blender能够理解它。
然后，这整行将导入OBJ文档。
现在，我们已经介绍了导入的每个文档，剩下的就是记录其文档名。
此Import运算符的部分工作是
它取消选择所有内容，然后选择要导入的对象。
这意味着我们可以遍历所有选定的对象，然后将其自描绘属性分配给它们。
这就是为什麽我如此喜欢Path Lib对象的原因。
如果您只想拥有路径的文档名部分，则使用起来非常简单，
只需运行“path.name”即可。
如果要使用驱动器对象，请运行“.drive”
这是一种非常面向对象的Paths方法，我非常喜欢。
现在我们正在导入。我们记录文档名的来源。
所以应该在.....尝试一下。
我将扔掉现有的物体，
浏览到我的OBJ文档目录，
然后批量导入所有内容，然后就可以了。文档已导入。
它们都是源文档，每个文档的OBJ文档名都设置为它们的来源。
现在，剩下要做的就是让运算符重新加载OBJ文档。
为了简单起见，我们不要大量地这样做，只需重新加载活动对象即可。
这意味着从场景中删除对象
然后再次从OBJ文档加载它。
当然，在我们从场景中删除之前，我们必须确保
我们存储了对象的变换，以便在重新加载它时可以将其还原。
这样就可以以相同的方式定位
就象我们重新加载之前一样
让我们从一个新的运算符开始。
我们称其为“obj reload”。在这里，我选择了一些文本，按Ctrl D，
将选择该文本的下一个出现位置。
然后，我可以点击“重新加载”。
以及类名和ID名，我们将更改为“obj reload”。
假设“重新加载大量导入的obj”，然后我们可以编写Execute函数。
对于此代码，我将假定每个OBJ
里面有一个物体。您可以扩展代码，以便它可以处理OBJ的多个对象。
但是现在让我们保持简单。
我们将多次引用活动对象，因此让我们简化生活。
只需将其称为“ob”即可，让我们再次为自己做一些概述。
首先，我们必须存储
我们想要记住的。
从场景中删除对象，
加载OBJ文档，
并恢复我们所记得的。
加载OBJ文档不会自动设置我们的自描绘属性。
最后，这就是我们要还原的内容。而且我们也希望能够还原转换。
让我们记住文档名。
让我们复制矩阵。如果我不打算复制矩阵，那麽变量矩阵世界
将是对对象矩阵的引用。如果我们删除该对象，则该引用将不再有效，
您可以获得各种奇怪的结果。
现在，从场景中删除对象实际上意味着从其所在的所有集合中删除对象。
幸运的是，Blender为我们提供了所有这些收藏的清单。
这将取消所有集合中对象的链接。
问题是，当然，一旦我们从第一个集合中取消对象的链接，我们就会循环
会改变。在循环中进行更改永远不是一个好主意。
我们可以做到“list(ob.user_collections)”
这将为我们创建一个新列表。
该新列表是一个副本，因此不会通过从那些集合中删除对象来对其进行更改，
从而使此for循环安全。
如果需要，您现在也可以从“bpy.data”中删除该对象，
但这只有在没有其他引用的情况下才是安全的。
再次，Blender营救。
“ob.users”将为我们提供该用户数。
在整个Blend文档中的不同位置使用“材质”和“网格”时， 也会看到一个很小的小方块。
如果该数字为0，则表示该对象不再使用， 们实际上可以将其从Blender的内存中删除。
从这一点开始，我们不再引用
“ob”，因为它已从Blender中删除。
因此，为了防止再次使用它，
我只是说“del ob”。这将从Python的内存中删除“ob”名称。
这意味着，如果我在这里说“ob.location”，
Python会对我们大喊大叫，因为在此行之后，它不再知道“
ob”的含义。 对我们自己来说只是一点点安全
这样我们就不会使用不再有效的东西。
现在。我们需要再次加载OBJ文档
我们之前做过的 因此，让我们看一下该代码。
我们必须再次运行此技巧，然后仅对一个对象运行此行。
因此，我们将其放入一个函数中
我们可以从两个地方调用。
在这里，我创建了一个带场景的新函数，
这意味着它将返回“pathLib.path”。
我非常喜欢这种表示法，因为这样，您可以看到
返回什麽，而不必深入研究代码，看看会发生什麽。
Python不会对此进行检查，
因此，您有责任切实履行自己的诺言。
现在，此代码与此几乎相同，并返回了新的路径对象。
这意味着我们可以删除这一行
我们可以在这里运行“mass_import_path(context.scene)”。
我专门通过了这个场景，而不是上下文。必须在这里解决一个小错误。
我明确地通过了场景而不是整个上下文。
如果我们要传递上下文，效果会很好，但是您将看不到上下文的哪一部分
实际上是由该功能使用的。
这就是为什麽我要传递场景本身而不是上下文。
当然，我们也可以传递“
context.scene(大量导入路径)”，但这会使调用时间更长一些，
我认为这是一个很好的中间立场。
但是，当然，您可以使用自己认为最好的方法来处理代码。
现在，我们有了要获取的代码导入路径，我们可以复制此行
进入我们的Reload运算符。
然后我们可以得到这条线，
放到这里
现在，我们要做的就是从目录的“导入路径”到“导入文档路径”，
这是一个文档名。导入文档路径是
“import_ path / mass_import_fname”是我们在那里复制的。
Python中的路径对象支持此“/”表示法。
它会自动在任何平台上正常工作。
在Windows上，它使用反斜杠； 在Linux和Mac OS上，它使用“/”。
现在我们已经构建了文档名，可以将其传递给加载OBJ文档的运算符。
现在，剩下的就是还原我们想要还原的内容。
同样，我们必须遍历所选对象，并设置“批量导入”文档名。
现在，我们必须还原矩阵。
会告诉Blender运算符做了工作。
然后我们修复两个错字。
这里的“：”和这里必须使用“collection”，而不是“collections”。
最后，我们要做的是注册和注销运算符，并在面板上添加一个按钮。
我们复制了类名，并将其添加到此列表中。
我们复制BL ID名称并将其添加到面板中，
保存文档，
重新加载脚本，然后点击按钮。
如您所见，我们单击它。
在导入OBJ文档中使用时，不再有活动对象，
仅选择导入的对象，在这种情况下为Suzanne。
现在，让我们看看它是否真正重新加载。
我在其中放置了一个新文档“cube.obj”，
现在应该用该多维数据集替换Suzanne。
而且确实如此，所以我们有了它。
我们的代码正在运行，我们可以批量加载OBJ文档，
并且可以选择要重新加载的单个对象。
我将其保留为练习为您提供一个批量重载运算符，
该运算符可对所有对象或仅对所有选定对象进行迭代。
我会留给你。这就是“艺术家脚本”这一集的内容。这不是最后一次
我们将讨论自描绘属性，但我认为该视频已经播放了足够长的时间。
如果你有任何问题或评论，将它们留在下面，我们很快会再见。