Hello everyone, I have encountered a problem. When I select a folder through the button, it will update the. c4d type files in the folder to TREELIST, including the path in an EDITNEXT that I set. However, after I restart the plugin or C4D, these contents are missing. Do you have any methods or documentation to refer to? be deeply grateful
Latest posts made by Neekoe
-
Storage issues with plugin settings
-
RE: Retrieve. c4d from folders and subfolders and update tree list
@Neekoe
I seem to have solved my problem again. Next, I will share my code, which is complete. If you bind it to a button, it will be a good list refresh (although it is very poor quality)folder_path = c4d.storage.LoadDialog(c4d.FILESELECT_DIRECTORY, "Select Folder", c4d.FILESELECT_DIRECTORY) if folder_path: # 如果选择了文件夹,更新列表 # self.UpdateTreeView(folder_path) # 更新显示的文件夹路径 self.SetString(self.ID_FILEPATH, folder_path) self._listView.listOfProjectPreset = list() # 刷新列表,因此我们只想加载一次内容 self.folderpath = folder_path # 检查文件夹路径有效性 if self.folderpath is not None and self.folderpath != "" and os.path.exists(self.folderpath): # 获取指定文件夹内的所有文件 filelist = os.listdir(self.folderpath) # 用来存储最终文件名列表 final_names = [] # 使用 os.walk 遍历文件夹及子文件夹 for root, dirs, files in os.walk(self.folderpath): for file in files: if file.endswith(".c4d"): # 判断文件扩展名 # 获取文件的完整路径 file_path = os.path.join(root, file) # 获取文件的修改时间 mtime = os.path.getmtime(file_path) final_names.append(file) # 将文件名添加到 final_names 列表 # 按文件修改时间排序,使用 mtime 作为排序的依据 final_names.sort(key=lambda x: x[1]) # 按修改时间升序排序 # 如果想要按降序排序,可以使用: # final_names.sort(key=lambda x: x[1], reverse=True) # 打印最终的文件名列表 for name in final_names: print(name) # 逐个打印文件名 # 将 ProjectPreset 对象添加到 listOfProjectPreset 列表 # 假设 ProjectPreset 是一个类,用来包装文件名 for name in final_names: projecname = ProjectPreset(name) # 为每个文件名创建一个 ProjectPreset 对象 self._listView.listOfProjectPreset.append(projecname) # 将对象添加到 listOfProjectPreset 列表中 # 刷新 TreeView self._treegui.Refresh() return True
-
Retrieve. c4d from folders and subfolders and update tree list
Hello everyone, I have a question about treeview. I have set a button (ID_TREEBUTTON2) to select the path, and the tree list will retrieve the. c4d file from the folder (including subfolders) of the set path, and update the file name to the list
In addition, I found that every time I open the plugin, the files in my list are missing (I wrote some content, and when I use the (IDUTREEBUTTON1) button to save the project, the current project name will be automatically updated and updated to the list, so I will find this problem)
I have written a loadfile and printed it out, but I am unable to add it to the tree list, which confuses meimport c4d import os from c4d import gui, plugins, storage PLUGINPATH = os.path.dirname(__file__) PLUGINID = 1064765 PLUGINNAME = "Work" PLUGINHELP = "" ID_OTHER = 1124 ID_ICON = 1125 ID_NAME = 1126 # ///////////////// 插件界面图片加载 class LogoUserArea(c4d.gui.GeUserArea): def __init__(self): super().__init__() self.bmp = c4d.bitmaps.BaseBitmap() self.image_path = os.path.join(PLUGINPATH, "res", "logo.png") # 确保路径正确 result, ismovie = self.bmp.InitWith(self.image_path) if result != c4d.IMAGERESULT_OK: self.bmp = None # 如果加载失败,设置为空 else: self.img_width = self.bmp.GetBw() self.img_height = self.bmp.GetBh() self.aspect_ratio = self.img_width / self.img_height # 计算图片宽高比 def GetMinSize(self): """返回最小尺寸,确保图片初始化时的大小""" return 300, int(300 / self.aspect_ratio) # 按比例设置初始高度 def DrawMsg(self, x1, y1, x2, y2, msg): """按比例缩放图片,确保不变形""" if not self.bmp: return area_width = x2 - x1 area_height = y2 - y1 area_ratio = area_width / area_height if area_ratio > self.aspect_ratio: new_height = area_height new_width = int(new_height * self.aspect_ratio) else: new_width = area_width new_height = int(new_width / self.aspect_ratio) x_offset = (area_width - new_width) // 2 y_offset = (area_height - new_height) // 2 new_width = min(new_width, area_width) new_height = min(new_height, area_height) self.DrawBitmap(self.bmp, x_offset, y_offset, new_width, new_height, 0, 0, self.img_width, self.img_height, c4d.BMP_NORMALSCALED | c4d.BMP_ALLOWALPHA) # ///////////////// TreeView界面 class ProjectPreset(object): # 类,它表示一个项目,也就是我们列表中的 Item projectptPath = "projectptPath" otherData = "OtherData" _selected = False def __init__(self, projectptPath) : self.projectptPath = projectptPath self.otherData += projectptPath listName = os.path.basename(projectptPath) self.ProName = projectptPath # 确保存在 name 属性 @property def IsSelected(self) : return self._selected def Select(self) : self._selected = True def Deselect(self) : self._selected = False def __repr__(self) : return str(self) def __str__(self) : return self.projectptPath class ListView(c4d.gui.TreeViewFunctions) : def __init__(self) : self.listOfProjectPreset = list()# 存储我们需要在此列表中显示的所有对象 self.c4dPath = r"C:\Users\86159\Desktop\test" # 设置路径 """ def __init__(self): self.listOfProjectPreset = list() # Store all objects we need to display in this list #TreeView 的整个概念是覆盖一些函数。因此,请务必阅读文档以了解根据您的需要覆盖哪些函数。 # 因此,我们将为 TreeView 定义一些更通用的选项 def IsResizeColAllowed(self, root, userdata, lColID) : return True def IsTristate(self, root, userdata) : return False def GetColumnWidth(self, root, userdata, obj, col, area) : return 80 # 所有的初始宽度都相同 def IsMoveColAllowed(self, root, userdata, lColID) : # 允许用户移动所有列。 # 必须在 AddCustomGui 的容器中设置 TREEVIEW_MOVE_COLUMN。 return True def GetFirst(self, root, userdata) : # 返回层次结构中的第一个元素,如果没有元素,则返回 None rValue = None if not self.listOfProjectPreset else self.listOfProjectPreset[0] return rValue # 然后我们必须处理 GetDown(对于子对象,因为在我们的例子中它是一个简单的列表,我们返回 None) # GetNext 是当前 Object 之后的 Object,GetPred 是当前 Object 之前的 Object。 # 我们还必须覆盖 GetID 才能唯一标识列表中的对象 def GetDown(self, root, userdata, obj) : # 返回节点的子节点,因为我们只需要一个列表,所以每次都返回 None return None def GetNext(self, root, userdata, obj) : # 返回 arg:'obj' 之后要显示的下一个对象 rValue = None currentObjIndex = self.listOfProjectPreset.index(obj) nextIndex = currentObjIndex + 1 if nextIndex < len(self.listOfProjectPreset) : rValue = self.listOfProjectPreset[nextIndex] return rValue def GetPred(self, root, userdata, obj) : # 返回要在 arg 之前显示的上一个 Object:'obj' rValue = None currentObjIndex = self.listOfProjectPreset.index(obj) predIndex = currentObjIndex - 1 if 0 <= predIndex < len(self.listOfProjectPreset) : rValue = self.listOfProjectPreset[predIndex] return rValue def GetId(self, root, userdata, obj) : # 返回 TreeView 中元素的唯一 ID return obj.GetUniqueID() def Select(self, root, userdata, obj, mode) : # 在用户选择元素时调用 if mode == c4d.SELECTION_NEW: for tex in self.listOfProjectPreset: tex.Deselect() obj.Select() elif mode == c4d.SELECTION_ADD: obj.Select() elif mode == c4d.SELECTION_SUB: obj.Deselect() def IsSelected(self, root, userdata, obj) : # 返回: 如果选择了 *obj*,则为 True,否则为 False return obj.IsSelected def InsertObject(self, root, userdata, obj, dragtype, dragobject, insertmode, bCopy): self.listOfProjectPreset.append(dragobject) return True def SetCheck(self, root, userdata, obj, column, checked, msg) : # 当用户单击'c4d.LV_CHECKBOX' 列中对象的复选框时调用 if checked: obj.Select() else: obj.Deselect() def IsChecked(self, root, userdata, obj, column) : # 返回: (int) : *obj* 的指定 *column* 中复选框的状态 if obj.IsSelected: return c4d.LV_CHECKBOX_CHECKED | c4d.LV_CHECKBOX_ENABLED else: return c4d.LV_CHECKBOX_ENABLED # 然后LV_TREE元素将通过调用 GetName 来检查 Object name def GetName(self, root, userdata, obj) : # 返回要为 arg:'obj' 显示的名称, 仅对type LV_TREE类型的列调用 return str(obj) # Or obj.texturePath # 最后,LV_USER允许我们执行一些绘图功能,就像您在 GeUserArea 中所做的那样 # 使用 DrawCell 绘制您想要的任何内容(在我们的例子中是文本)。看看 DrawInfo-Dict def DrawCell(self, root, userdata, obj, col, drawinfo, bgColor): # 在 TreeView 的单元格中绘制内容 LV_USER if col == ID_NAME: # print("Drawcell obj: ", str(obj)) geUserArea = drawinfo["frame"] ICON_SIZE = drawinfo["height"] TEXT_SPACER = 6 bgColor = c4d.COLOR_SB_TEXTHG1 if drawinfo["line"] % 2 else c4d.COLOR_SB_TEXTHG2 # 使用自定义图标 icon = c4d.bitmaps.BaseBitmap() path = os.path.join(PLUGINPATH, "res", "icon.tif") if not icon.InitWith(path): print("图标加载失败,请检查路径!") return False # 图标加载失败,退出 geUserArea.DrawSetPen(bgColor) geUserArea.DrawBitmap(icon, drawinfo["xpos"], drawinfo["ypos"], ICON_SIZE, ICON_SIZE, 0, 0, icon.GetBw(), icon.GetBh(), c4d.BMP_ALLOWALPHA) # 绘制文本 name = str(obj) fontHeight = geUserArea.DrawGetFontHeight() fontWidth = geUserArea.DrawGetTextWidth(name) x = drawinfo["xpos"] + ICON_SIZE + TEXT_SPACER y = drawinfo["ypos"] + (ICON_SIZE - fontHeight) / 2 txtColor = c4d.COLOR_SB_TEXT_ACTIVE1 if obj.IsSelected else c4d.COLOR_SB_TEXT geUserArea.DrawSetTextCol(txtColor, bgColor) # 处理保存工程操作 def save_project(self, doc, file_path): doc = c4d.documents.GetActiveDocument() # 弹出保存文件对话框,获取文件路径 save_path = c4d.gui.SaveFileDialog(c4d.FILESELECTTYPE_SCENE, "Save Scene As", "", "") if not save_path: return # 如果用户没有选择路径,退出 # 保存文档(包含资源) c4d.documents.SaveDocument(doc, save_path, c4d.SAVEDOCUMENTFLAGS_SAVE_TEXTURES, c4d.FORMAT_C4DEXPORT) # 获取保存后的工程名称(文件名部分) project_name = save_path.split("\\")[-1] # 提取文件名部分 # 创建一个新的 ProjectPreset 对象并将其添加到列表中 t1 = ProjectPreset(project_name) self.listOfProjectPreset.append(t1) # 可选:输出保存的工程名称,以便调试 print(f"Project '{project_name}' has been saved and added to the list.") def on_button_click(self, msg, doc): # 响应按钮点击事件,弹出保存对话框 file_path = c4d.storage.SaveDialog(c4d.FILESELECTTYPE_SCENE, "选择保存位置") if file_path: self.save_project(doc, file_path) # 执行保存工程操作 def get_list(self): return self.listOfProjectPreset # Performing a rename on any item in the Treeview is crashing Cinema reliably. def SetName(self, root, userdata, obj, name): # 当用户重命名元素时调用`DoubleClick()`必须返回False才能正常工作 doc = c4d.documents.GetActiveDocument() doc.StartUndo() doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, obj) obj.SetName(name) doc.EndUndo() c4d.EventAdd() def EmptyText(self, root, userdata): # 判断是否为空,如果为空返回"???",否则返回默认的文本 if not root: # 如果根节点为空 return "工程名字" # 返回显示的文本 return "预设显示列表" # 返回空文本,不显示内容 # 然后很少有有用的东西,比如当你双击时通过覆盖 DoubleClick # 触发一些脚本,以及通过覆盖 DeletePressed 来管理器 Delete 键 def DoubleClick(self, root, userdata, obj, col, mouseinfo) : # 当用户双击 TreeView 中的条目时调用。返回:返回:(bool) :如果双击 # 已处理,则为 True,如果default作应启动,则为 False。默认作将调用对 # 象的重命名过程,启用 'SetName()' if not isinstance(obj, c4d.BaseList2D): return True obj.SetName(c4d.gui.RenameDialog(obj.GetName())) return True #c4d.gui.MessageDialog("你确定要合并 " + str(obj)+"到当前项目吗?(增加确定和取消按钮)") #return True def IsResizeColAllowed(self, root, userdata, lColID): if lColID > 2: return True return False def IsTristate(self, root, userdata): return False def GetDragType(self, root, userdata, node): # 返回:(int):拖动数据类型。 return c4d.NOTOK def DeletePressed(self, root, userdata) : # 收到删除事件时调用 for tex in reversed(self.listOfProjectPreset) : if tex.IsSelected: self.listOfProjectPreset.remove(tex) def HeaderClick(self, root, userdata, column, channel, is_double_click, mouseX, mouseY, ua): # 在单击 TreeView 标头时调用。返回:结果(bool):如果事件已处理,则为 True,否则为 False # c4d.gui.MessageDialog("You clicked on the '%i' header." % (column)) return True def AcceptDragObject(self, root, userdata, node, dragtype, dragobject): # 当拖放作悬停在 TreeView 上以检查是否可以接受拖动时调用。返回:(整数,布尔值) # if dragtype != c4d.DRAGTYPE_ATOMARRAY: # return 0 # return c4d.INSERT_BEFORE | c4d.INSERT_AFTER | c4d.INSERT_UNDER, True return 0 def GenerateDragArray(self, root, userdata, node): # 返回:(c4d 列表。BaseList2D):生成一个对象列表,这些对象可以从 'c4d.DRAGTYPE_ATOMARRAY' 类型的 TreeView 中拖动 if node.GetBit(c4d.BIT_ACTIVE): return [node, ] def InsertObject(self, root, userdata, node, dragtype, dragobject, insertmode, bCopy): # 在 TreeView 上放置拖动时调用 if dragtype != c4d.DRAGTYPE_ATOMARRAY: return # 不应该发生,我们在 AcceptDragObject 中捕获了它 for op in dragobject: op.Remove() if insertmode == c4d.INSERT_BEFORE: op.InsertBefore(node) elif insertmode == c4d.INSERT_AFTER: op.InsertAfter(node) elif insertmode == c4d.INSERT_UNDER: op.InsertUnder(node) return def IsMoveColAllowed(self, root, userdata, lColID): # 允许用户移动所有列。 # 必须在 AddCustomGui 的容器中设置 TREEVIEW_MOVE_COLUMN。 return False def update_treeview_with_c4d_files(self, path): # 先清空现有的列表 self.listOfProjectPreset.clear() # 检查路径是否存在 if not os.path.exists(path): c4d.gui.MessageDialog("路径不存在!") return # 遍历该路径及其子文件夹中的所有 .c4d 文件 for root, dirs, files in os.walk(path): for file in files: if file.endswith(".c4d"): # 获取文件的完整路径并添加到列表 full_path = os.path.join(root, file) project = ProjectPreset(full_path) self.listOfProjectPreset.append(project) # 刷新 TreeView self.refresh_treeview() def add_projects_from_path(self, path): # 获取所有 .c4d 文件 c4d_files = self.get_all_c4d_files(path) # 清空旧的列表内容 self.clear_treeview() # 清空TreeView内容 # 打印路径内容,检查文件夹是否正确 print(f"读取路径: {path}") # 将每个找到的 .c4d 文件添加到 TreeView 中 for c4d_file in c4d_files: project_name = os.path.basename(c4d_file) # 获取文件名 self.add_to_treeview(project_name) # 添加文件到TreeView print(f"已加载项目:{self.listOfProjectPreset}") def get_all_c4d_files(self, path): c4d_files = [] # 使用 os.walk 遍历目录及子目录 for root, dirs, files in os.walk(path): for file in files: if file.lower().endswith(".c4d"): # 检查文件扩展名 full_path = os.path.join(root, file) # 获取文件的完整路径 c4d_files.append(full_path) # 添加到列表 return c4d_files def clear_treeview(self): # 清空TreeView的所有项 item = self.GetFirst(None, None) # 获取TreeView中的第一个项目,root和userdata设置为None while item: next_item = self.GetNextItem(item) # 获取下一个项目 self.RemoveItem(item) # 移除当前项目 item = next_item # 继续遍历下一个项目 def add_to_treeview(self, project_name): # 向TreeView添加项目 item_id = self.AddItem(None, project_name) # 添加项目到TreeView self.Refresh() # 刷新TreeView,显示新的内容 self.listOfProjectPreset.append(project_name) # 将项目添加到 list def refresh_treeview(self): # 这里是刷新 TreeView 的逻辑 print("刷新 TreeView") # ///////////////// 插件界面 class DL_MainDialog(c4d.gui.GeDialog): ID_MENU = 10 ID_MENU1 = ID_MENU+1 ID_MENU2 = ID_MENU+2 ID_GROUP_START = 100 ID_GROUP_NONE1 = ID_GROUP_START+1 ID_TABGROUP = 1 ID_TREEBUTTON1 = 1501 ID_TREEBUTTON2 = 1502 ID_TREEBUTTON3 = 1503 ID_STATICETEXT1 = 2000 ID_STATICETEXT2 = 2001 #未引用 ID_FILEPATH = 2501 ID_LINE = 4000 ID_LINE1 = ID_LINE+1 ID_LINE2 = ID_LINE+2 ID_USERAREA = 5000 ID_USERAREA1 = ID_USERAREA+1 ID_TREEVIEW = 6001 ID_NAME = 6501 ID_RENDER = 6502 ID_TREEICON1 = 7001 _treegui = None # 我们的 CustomGui TreeView _listView = ListView() # 我们的 c4d.gui.TreeViewFunctions 实例 _listView2 = [] def __init__(self): super().__init__() self.logo_area = LogoUserArea() def CreateLayout(self): self.SetTitle(PLUGINNAME) # 加载并显示封面图片 cover_image_path = os.path.join(PLUGINPATH, "res", "logo.png") # 图片路径 cover_image = c4d.bitmaps.BaseBitmap() # 创建图片对象 if self.GroupBegin(self.ID_GROUP_NONE1, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=1, rows=0, title = PLUGINNAME, groupflags=0, initw=0, inith=0): self.GroupBegin(self.ID_GROUP_NONE1, c4d.BFH_CENTER | c4d.BFV_CENTER, 0, 0, "Logo") self.GroupBorderNoTitle(c4d.BORDER_NONE) self.AddUserArea(self.ID_USERAREA1, c4d.BFH_CENTER | c4d.BFV_CENTER) self.AttachUserArea(self.logo_area, self.ID_USERAREA1) self.GroupEnd() if self.GroupBegin(self.ID_GROUP_NONE1, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 1, "项目预设", groupflags=0, initw=0, inith=0): self.GroupBorder(c4d.BORDER_ACTIVE_1) self.GroupBorderSpace(6,6,6,6) customdata = c4d.BaseContainer() customdata.SetBool(c4d.TREEVIEW_BORDER,c4d.BORDER_ROUND) customdata.SetBool(c4d.TREEVIEW_HAS_HEADER, True) customdata.SetBool(c4d.TREEVIEW_HIDE_LINES, True) customdata.SetBool(c4d.TREEVIEW_MOVE_COLUMN, True) customdata.SetBool(c4d.TREEVIEW_RESIZE_HEADER, True) customdata.SetBool(c4d.TREEVIEW_FIXED_LAYOUT, True) customdata.SetBool(c4d.TREEVIEW_ALTERNATE_BG, True) customdata.SetBool(c4d.TREEVIEW_OUTSIDE_DROP, True) customdata.SetBool(c4d.TREEVIEW_NO_MULTISELECT, True) customdata.SetBool(c4d.TREEVIEW_NO_OPEN_CTRLCLK, False) self._treegui = self.AddCustomGui(self.ID_TREEVIEW,c4d.CUSTOMGUI_TREEVIEW,"",c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT,0,0,customdata) self.AddButton(self.ID_TREEBUTTON1, c4d.BFH_CENTER | c4d.BFV_SCALE, 50, 20, name="添加预设工程") self.AddButton(self.ID_TREEBUTTON2, c4d.BFH_CENTER | c4d.BFV_SCALE, 100, 20, name="选择保存工程路径") # self.AddButton(self.ID_TREEBUTTON3, c4d.BFH_CENTER | c4d.BFV_SCALE, 50, 20, name="保存工程") self.AddEditText(self.ID_FILEPATH, c4d.BFH_SCALEFIT, 0, 20) # self.AddCustomGui(self.ID_FILEPATH, c4d.CUSTOMGUI_STATICTEXT, "", c4d.BFH_LEFT, 0, 0) self.SetString(self.ID_FILEPATH, "C:/path/to/your/project.c4d") # 设置默认路径 # self.AddMultiLineEditText(1541, c4d.BFH_SCALEFIT | c4d.BFV_SCALE, 0, 40) # self.Hierarchy(self,tv,headlist = ["Name","test"]) # 添加列标题(这里添加了三列) if not self._treegui: print ("[ERROR]: 不能生成TreeView") return False self.GroupEnd() self.GroupEnd() return True def InitValues(self) : icon = c4d.bitmaps.BaseBitmap() path = os.path.join(PLUGINPATH, "res", "icon.tif") if not icon.InitWith(path): print("图标加载失败,请检查路径!") return False # 图标加载失败,退出 icon.InitWith(path) # Initialize the column layout for the TreeView. layout = c4d.BaseContainer() # layout.SetLong(self.ID_TREEICON1, c4d.LV_CHECKBOX) layout.SetInt32(ID_NAME, c4d.LV_USERTREE) layout.SetLong(self.ID_TREEICON1, c4d.LV_TREE ) layout.SetLong(self.ID_RENDER, c4d.LV_DROPDOWN) self._treegui.SetLayout(3, layout) # 设置头部标题 self._treegui.SetHeaderText(self.ID_NAME, "Icon") self._treegui.SetHeaderText(self.ID_TREEICON1, "项目名") self._treegui.SetHeaderText(self.ID_RENDER, "使用渲染器") self._treegui.Refresh() # # 设置根节点 self._treegui.SetRoot(self._treegui, self._listView, None) return True def Command(self, id, msg): if id == self.ID_TREEBUTTON1: # 执行 Cinema 4D 内置的保存功能(保存工程并包含资源) c4d.CallCommand(12255) # 触发保存工程(包含资源)功能 # 获取当前文档 doc = c4d.documents.GetActiveDocument() # 获取保存后的文件路径 doc_path = doc.GetDocumentPath() # 检查文件是否保存成功 if doc_path: # 如果路径有效,说明文件已保存 project_name = doc.GetDocumentName() # 获取当前文档的名称 # 检查是否已经存在相同名称的工程文件 # 遍历 ProjectPreset 列表,确保我们只对 ProjectPreset 对象操作 if not any(isinstance(prjname, ProjectPreset) and prjname.ProName == project_name for prjname in self._listView.listOfProjectPreset): # 创建一个新的 ProjectPreset 对象,并将其添加到列表中 prjname = ProjectPreset(project_name) self._listView.listOfProjectPreset.append(prjname) # 刷新 TreeView 以显示更新后的列表 self._treegui.Refresh() else: print(f"工程 '{project_name}' 已经存在,不会添加。") else: print("文件未保存或保存失败!") return True if id == self.ID_TREEBUTTON2: # 如果点击了按钮 folder_path = c4d.storage.LoadDialog(c4d.FILESELECT_DIRECTORY, "Select Folder", c4d.FILESELECT_DIRECTORY) if folder_path: # 如果选择了文件夹,更新列表 self.UpdateTreeView(folder_path) # 更新显示的文件夹路径 self.SetString(self.ID_FILEPATH, folder_path) return True def UpdateTreeView(self, folder_path): # 遍历文件夹,查找所有 .c4d 文件(包括子文件夹),并更新列表 # 清空现有的列表 self._listView = [] # 递归遍历文件夹,查找 .c4d 文件 self.ScanFolderForC4DFiles(folder_path) # 在这里你可以更新 TreeView 或者 ListView 来显示文件 for item in self._listView: print(f"Found .c4d file: {item.ProName}") # 你可以根据需要刷新 GUI 元素,例如 TreeView def ScanFolderForC4DFiles(self, folder_path): # 递归遍历文件夹,查找所有 .c4d 文件,并将其添加到列表 for root, dirs, files in os.walk(folder_path): for file in files: if file.lower().endswith(".c4d"): # 只处理 .c4d 文件 full_path = os.path.join(root, file) # 创建 ProjectPreset 对象并加入列表 project = ProjectPreset(full_path) self._listView.append(project) def set_save_directory(self): # 打开文件夹选择对话框,让用户选择路径 selected_dir = c4d.gui.FileSelectionDialog(c4d.FILESELECTTYPE_ANYTHING, "选择保存目录", self.saved_projects_dir) if selected_dir: self.saved_projects_dir = selected_dir # 保存用户选择的路径 print("已选择的路径:", self.saved_projects_dir) def expand_all_nodes(self, node): # 递归展开所有节点 self.treeview.expanded_nodes.add(node) if isinstance(node, dict) and node.get("children"): for child in node["children"]: self.expand_all_nodes(child) # ///////////////// 插件界面 class DL_MAIN(c4d.plugins.CommandData): def __init__(self): self.dialog = None if __name__ == "__main__": icon = c4d.bitmaps.BaseBitmap() icon_path = os.path.join(PLUGINPATH,"res\icon.tif") icon.InitWith(icon_path) c4d.plugins.RegisterCommandPlugin(PLUGINID, PLUGINNAME, 0, icon, PLUGINHELP, DL_MAIN(), )
-
RE: Add icons to treeview
@Dunhou said in Add icons to treeview:
boghma
I did refer to the teaching of Jack, a member of Boghma, which involved some methods and pre written libraries for calling. However, I would prefer to start from scratch because it is not as simple as the add button. The code snippets use parts of the function from Git and Boghma respectively. I can copy the parts that I have already learned. Thank you to the members of Boghma
-
RE: Add icons to treeview
Thank you again to MAXON's friends for providing forum materials
-
RE: Add icons to treeview
The problem has been resolved, and the reason why the icon does not appear is that I used layout in 'def InitValues (self):' SetLong(self.ID_NAME, c4d.LV_TREE ), The correct one should be layout SetInt32(ID_NAME, c4d.LV_USERTREE)
-
Add icons to treeview
I'm sorry, I saw a lot of information about treeview on the forum, but I still can't understand it. I'm too stupid, and I feel like I've done a lot, but still can't get the answer
I hope to get a custom icon, similar to the green custom icon in image 2, and the treeview result will let me know what difficulty isimport c4d PLUGINID = 1064765 ID_OTHER = 1124 ID_ICON = 1125 ID_NAME = 1126 # ///////////////// TreeView界面 class ProjectPreset(object): # 类,它表示一个项目,也就是我们列表中的 Item projectptPath = "projectptPath" otherData = "OtherData" _selected = False def __init__(self, projectptPath) : self.projectptPath = projectptPath self.otherData += projectptPath @property def IsSelected(self) : return self._selected def Select(self) : self._selected = True def Deselect(self) : self._selected = False def __repr__(self) : return str(self) def __str__(self) : return self.projectptPath class ListView(c4d.gui.TreeViewFunctions) : def __init__(self) : self.listOfProjectPreset = list() # 存储我们需要在此列表中显示的所有对象 # Add some defaults values t1 = ProjectPreset("T1") t2 = ProjectPreset("T2") t3 = ProjectPreset("T3") t4 = ProjectPreset("T4") t5 = ProjectPreset("T5") self.listOfProjectPreset.extend([t1, t2, t3, t4]) #TreeView 的整个概念是覆盖一些函数。因此,请务必阅读文档以了解根据您的需要覆盖哪些函数。 # 因此,我们将为 TreeView 定义一些更通用的选项 def IsResizeColAllowed(self, root, userdata, lColID) : return True def IsTristate(self, root, userdata) : return False def GetColumnWidth(self, root, userdata, obj, col, area) : return 80 # 所有的初始宽度都相同 def IsMoveColAllowed(self, root, userdata, lColID) : # 允许用户移动所有列。 # 必须在 AddCustomGui 的容器中设置 TREEVIEW_MOVE_COLUMN。 return True def GetFirst(self, root, userdata) : # 返回层次结构中的第一个元素,如果没有元素,则返回 None rValue = None if not self.listOfProjectPreset else self.listOfProjectPreset[0] return rValue # 然后我们必须处理 GetDown(对于子对象,因为在我们的例子中它是一个简单的列表,我们返回 None) # GetNext 是当前 Object 之后的 Object,GetPred 是当前 Object 之前的 Object。 # 我们还必须覆盖 GetID 才能唯一标识列表中的对象 def GetDown(self, root, userdata, obj) : # 返回节点的子节点,因为我们只需要一个列表,所以每次都返回 None return None def GetNext(self, root, userdata, obj) : # 返回 arg:'obj' 之后要显示的下一个对象 rValue = None currentObjIndex = self.listOfProjectPreset.index(obj) nextIndex = currentObjIndex + 1 if nextIndex < len(self.listOfProjectPreset) : rValue = self.listOfProjectPreset[nextIndex] return rValue def GetPred(self, root, userdata, obj) : # 返回要在 arg 之前显示的上一个 Object:'obj' rValue = None currentObjIndex = self.listOfProjectPreset.index(obj) predIndex = currentObjIndex - 1 if 0 <= predIndex < len(self.listOfProjectPreset) : rValue = self.listOfProjectPreset[predIndex] return rValue def GetId(self, root, userdata, obj) : # 返回 TreeView 中元素的唯一 ID return hash(obj) def Select(self, root, userdata, obj, mode) : # 在用户选择元素时调用 if mode == c4d.SELECTION_NEW: for tex in self.listOfProjectPreset: tex.Deselect() obj.Select() elif mode == c4d.SELECTION_ADD: obj.Select() elif mode == c4d.SELECTION_SUB: obj.Deselect() def IsSelected(self, root, userdata, obj) : # 返回: 如果选择了 *obj*,则为 True,否则为 False return obj.IsSelected def InsertObject(self, root, userdata, obj, dragtype, dragobject, insertmode, bCopy): self.listOfProjectPreset.append(dragobject) return True def SetCheck(self, root, userdata, obj, column, checked, msg) : # 当用户单击'c4d.LV_CHECKBOX' 列中对象的复选框时调用 if checked: obj.Select() else: obj.Deselect() def IsChecked(self, root, userdata, obj, column) : # 返回: (int) : *obj* 的指定 *column* 中复选框的状态 if obj.IsSelected: return c4d.LV_CHECKBOX_CHECKED | c4d.LV_CHECKBOX_ENABLED else: return c4d.LV_CHECKBOX_ENABLED # 然后LV_TREE元素将通过调用 GetName 来检查 Object name def GetName(self, root, userdata, obj) : # 返回要为 arg:'obj' 显示的名称, 仅对type LV_TREE类型的列调用 return str(obj) # Or obj.texturePath # 最后,LV_USER允许我们执行一些绘图功能,就像您在 GeUserArea 中所做的那样 # 使用 DrawCell 绘制您想要的任何内容(在我们的例子中是文本)。看看 DrawInfo-Dict def DrawCell(self, root, userdata, obj, col, drawinfo, bgColor): # 在 TreeView 的单元格中绘制内容 LV_USER if col == ID_NAME: print("Drawcell obj: ", str(obj)) geUserArea = drawinfo["frame"] ICON_SIZE = drawinfo["height"] TEXT_SPACER = 6 bgColor = c4d.COLOR_SB_TEXTHG1 if drawinfo["line"] % 2 else c4d.COLOR_SB_TEXTHG2 # 使用自定义图标 icon = c4d.bitmaps.BaseBitmap() path = os.path.join(PLUGINPATH, "res", "icon.tif") if not icon.InitWith(path): print("图标加载失败,请检查路径!") return False # 图标加载失败,退出 geUserArea.DrawSetPen(bgColor) geUserArea.DrawBitmap(icon, drawinfo["xpos"], drawinfo["ypos"], ICON_SIZE, ICON_SIZE, 0, 0, icon.GetBw(), icon.GetBh(), c4d.BMP_ALLOWALPHA) # 绘制文本 name = str(obj) fontHeight = geUserArea.DrawGetFontHeight() fontWidth = geUserArea.DrawGetTextWidth(name) x = drawinfo["xpos"] + ICON_SIZE + TEXT_SPACER y = drawinfo["ypos"] + (ICON_SIZE - fontHeight) / 2 txtColor = c4d.COLOR_SB_TEXT_ACTIVE1 if obj.IsSelected else c4d.COLOR_SB_TEXT geUserArea.DrawSetTextCol(txtColor, bgColor) geUserArea.DrawText(name, x, y) # Performing a rename on any item in the Treeview is crashing Cinema reliably. def SetName(self, root, userdata, obj, name): # 当用户重命名元素时调用`DoubleClick()`必须返回False才能正常工作 doc = c4d.documents.GetActiveDocument() doc.StartUndo() doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, obj) obj.SetName(name) doc.EndUndo() c4d.EventAdd() # 然后很少有有用的东西,比如当你双击时通过覆盖 DoubleClick # 触发一些脚本,以及通过覆盖 DeletePressed 来管理器 Delete 键 def DoubleClick(self, root, userdata, obj, col, mouseinfo) : # 当用户双击 TreeView 中的条目时调用。返回:返回:(bool) :如果双击 # 已处理,则为 True,如果default作应启动,则为 False。默认作将调用对 # 象的重命名过程,启用 'SetName()' if not isinstance(obj, c4d.BaseList2D): return True obj.SetName(c4d.gui.RenameDialog(obj.GetName())) return True #c4d.gui.MessageDialog("你确定要合并 " + str(obj)+"到当前项目吗?(增加确定和取消按钮)") #return True def DeletePressed(self, root, userdata) : # 收到删除事件时调用 for tex in reversed(self.listOfProjectPreset) : if tex.IsSelected: self.listOfProjectPreset.remove(tex) class DL_MainDialog(c4d.gui.GeDialog): ID_TREEVIEW = 6001 ID_NAME = 6501 ID_RENDER = 6502 ID_TREEICON1 = 7001 _treegui = None # 我们的 CustomGui TreeView _listView = ListView() # 我们的 c4d.gui.TreeViewFunctions 实例 def __init__(self): super().__init__() # treeview 容器预设 # self.tv_manager = TV_Manager() def CreateLayout(self): self.SetTitle(PLUGINNAME) # 添加菜单 self.MenuSubBegin("File") self.MenuAddString(self.ID_MENU1,"What") self.MenuSubEnd() self.MenuSubBegin("Edit") self.MenuAddString(self.ID_MENU2,"What") self.MenuSubEnd() if self.GroupBegin(self.ID_GROUP_NONE1, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 1, "项目预设", groupflags=0, initw=0, inith=0): self.GroupBorder(c4d.BORDER_ACTIVE_1) self.GroupBorderSpace(6,6,6,6) customdata = c4d.BaseContainer() customdata.SetBool(c4d.TREEVIEW_BORDER,c4d.BORDER_ROUND) customdata.SetBool(c4d.TREEVIEW_HAS_HEADER, True) customdata.SetBool(c4d.TREEVIEW_HIDE_LINES, True) customdata.SetBool(c4d.TREEVIEW_MOVE_COLUMN, True) customdata.SetBool(c4d.TREEVIEW_RESIZE_HEADER, True) customdata.SetBool(c4d.TREEVIEW_FIXED_LAYOUT, True) customdata.SetBool(c4d.TREEVIEW_ALTERNATE_BG, True) customdata.SetBool(c4d.TREEVIEW_OUTSIDE_DROP, True) customdata.SetBool(c4d.TREEVIEW_NO_MULTISELECT, True) customdata.SetBool(c4d.TREEVIEW_NO_OPEN_CTRLCLK, False) self._treegui = self.AddCustomGui(self.ID_TREEVIEW,c4d.CUSTOMGUI_TREEVIEW,"",c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT,0,0,customdata) self.AddButton(self.ID_TREEBUTTON1, c4d.BFH_CENTER | c4d.BFH_SCALEFIT | c4d.BFV_SCALE, 100, 20, name="展开所有") # self.AddMultiLineEditText(1541, c4d.BFH_SCALEFIT | c4d.BFV_SCALE, 0, 40) # self.AddEditText(1842, c4d.BFH_SCALEFIT, 0, 25) # self.Hierarchy(self,tv,headlist = ["Name","test"]) # 添加列标题(这里添加了三列) if not self._treegui: print ("[ERROR]: 不能生成TreeView") return False self.GroupEnd() return True def InitValues(self) : icon = c4d.bitmaps.BaseBitmap() path = os.path.join(PLUGINPATH, "res", "icon.tif") if not icon.InitWith(path): print("图标加载失败,请检查路径!") return False # 图标加载失败,退出 icon.InitWith(path) # Initialize the column layout for the TreeView. layout = c4d.BaseContainer() layout.SetLong(self.ID_TREEICON1, c4d.LV_DROPDOWN) layout.SetLong(self.ID_NAME, c4d.LV_TREE ) layout.SetLong(self.ID_RENDER, c4d.LV_CHECKBOX) self._treegui.SetLayout(3, layout) # 设置头部标题 self._treegui.SetHeaderText(self.ID_TREEICON1, "Icon") self._treegui.SetHeaderText(self.ID_NAME, "Name") self._treegui.SetHeaderText(self.ID_RENDER, "使用渲染器") self._treegui.Refresh() # # 设置根节点 self._treegui.SetRoot(self._treegui, self._listView, None) return True
-
RE: C4D uses Python to search for referenced objects
@ferdinand Thank you so much. He perfectly solved all my problems
-
C4D uses Python to search for referenced objects
This is my Python code. I want to write a button to delete invalid null objects, even if they are at any level. However, this code is invalid in check_deference because executing the code will delete the linked null objects together. How can I find the referenced objects?
In addition, I have attached an image where I want to remove the red null object and keep the green null object (green object 1111 is linked to the target label, hoping to handle different levels), as it points to the target label. Other default colors are used for various working conditions and have already met the code settings..
Thank you.
def check_references(obj): references = [] # 检查标签引用 for tag in obj.GetTags(): tag_type = tag.GetType() if tag_type in [c4d.Tphong, c4d.Ttexture, c4d.Texpresso]: # 更新为 Texpresso(表达式标签) references.append(f"Tag link: {tag_type}") if references: print(f"Object {obj.GetName()} has references: {', '.join(references)}") return references # 判断 Null 对象是否为空 def is_empty_null(obj): if not obj or obj.GetType() != c4d.Onull: return False if obj.GetTags(): return False child = obj.GetDown() while child: if child.GetType() != c4d.Onull: return False if not is_empty_null(child): return False child = child.GetNext() return True # 获取场景中所有对象 def get_all_objects(obj): all_objects = [] while obj: all_objects.append(obj) all_objects.extend(get_all_objects(obj.GetDown())) obj = obj.GetNext() return all_objects # 删除无效 Null 对象 def delete_null_objects(): doc = c4d.documents.GetActiveDocument() if not doc: return doc.StartUndo() # 获取场景中的所有对象 objects = doc.GetObjects() for obj in objects: if obj.GetType() == c4d.Onull: if check_references(obj): continue # 被引用的对象跳过 # 只有 `is_empty_null` 返回 True 才会删除 if not is_empty_null(obj): continue # 记录删除操作的撤销 doc.AddUndo(c4d.UNDOTYPE_DELETE, obj) # 获取 Null 对象的父对象 parent = obj.GetUp() # 获取子对象并移到父对象下 child = obj.GetDown() while child: next_child = child.GetNext() child.Remove() # 记录撤销(对于每个子对象的改变) if parent: doc.AddUndo(c4d.UNDOTYPE_CHANGE, child) parent.InsertUnder(child) child = next_child # 删除 Null 对象 obj.Remove() # 更新对象列表,因为在删除过程中可能会修改它 objects = doc.GetObjects() # 额外逻辑:删除非 `Null` 对象下的空 `Null` 子对象 for obj in objects: child = obj.GetDown() while child: next_child = child.GetNext() # 如果子对象是 `Null` 且为空,并且没有标签,则递归删除该 `Null` 对象及其子对象 if child.GetType() == c4d.Onull: if is_empty_null(child): doc.AddUndo(c4d.UNDOTYPE_DELETE, child) child.Remove() else: # 如果子对象不是空 `Null`,递归检查其子级 delete_empty_null_children(child) child = next_child # 更新界面 c4d.EventAdd() doc.EndUndo() # 递归删除子级空 Null 对象 def delete_empty_null_children(parent): doc = c4d.documents.GetActiveDocument() child = parent.GetDown() while child: next_child = child.GetNext() if child.GetType() == c4d.Onull: if is_empty_null(child): # 记录撤销 doc.AddUndo(c4d.UNDOTYPE_DELETE, child) child.Remove() else: delete_empty_null_children(child) child = next_child
-
RE: How to change polygon axis
@ferdinand Thank you very much for your continuous help in the past two days. I just searched axis related content in cafe, and it is confirmed that my idea is wrong. You are a patient and good teacher. Thank you again, Ferdinand.