Created
June 17, 2016 18:55
-
-
Save donovankeith/26e086772ee94851553861bc77c26fc3 to your computer and use it in GitHub Desktop.
A Cinema 4D Python TreeView example that supports Shift Clicking and Up/Down Arrows.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""ObjectBrowser | |
Based on: https://developers.maxon.net/?p=439""" | |
# ====== IMPORTS ====== # | |
import c4d | |
# ====== GLOBALS ====== # | |
debug = True | |
PLUGIN_ID = 1037588 | |
PLUGIN_NAME = "Object Browser" | |
PLUGIN_HELP = "A simple treeview example" | |
# ====== UTILS ====== # | |
def GetNextElement(op): | |
"""Returns the next material or shader after `op`.""" | |
if not op: | |
return | |
if op.GetDown(): | |
return op.GetDown() | |
while (not op.GetNext()) and op.GetUp(): | |
print "Up... ", op.GetUp().GetName() | |
op = op.GetUp() | |
return op.GetNext() | |
class ListItem(): | |
def __init__(self, obj): | |
self.obj = obj | |
def GetName(self): | |
return self.obj.GetName() | |
def IsSelected(self): | |
return self.obj.GetBit(c4d.BIT_ACTIVE) | |
def Select(self): | |
self.obj.SetBit(c4d.BIT_ACTIVE) | |
def Deselect(self): | |
self.obj.DelBit(c4d.BIT_ACTIVE) | |
def IsOpened(self): | |
return self.obj.GetBit(c4d.BIT_OFOLD) | |
# ====== TREEVIEW ====== # | |
class ObjectTree(c4d.gui.TreeViewFunctions): | |
"""Data structure for a TreeView of Materials & Shaders.""" | |
def __init__(self, dlg): | |
if debug: | |
print "ObjectTree()" | |
self.items = [] | |
self.LoadObjects() | |
def LoadObjects(self): | |
if debug: | |
print "LoadObjects()" | |
doc = c4d.documents.GetActiveDocument() | |
if not doc: | |
return | |
obj = doc.GetFirstObject() | |
while(obj is not None): | |
wrapped_obj = ListItem(obj) | |
self.items.append(wrapped_obj) | |
obj = GetNextElement(obj) | |
def GetFirst(self, root, userdata): | |
"""Returns the first Material in the document.""" | |
if self.items: | |
return self.items[0] | |
def GetDown(self, root, userdata, obj): | |
"""Get the next shader in the list.""" | |
return None | |
def GetNext(self, root, userdata, obj): | |
"""Get the next material/shader in the list.""" | |
if obj in self.items: | |
obj_index = self.items.index(obj) | |
next_index = obj_index + 1 | |
if next_index < len(self.items): | |
return self.items[next_index] | |
def GetPred(self, root, userdata, obj): | |
if obj in self.items: | |
obj_index = self.items.index(obj) | |
prev_index = obj_index - 1 | |
if prev_index > 0: | |
return self.items[prev_index] | |
def GetName(self, root, userdata, obj): | |
"""Returns the name of obj.""" | |
if not obj: | |
return | |
if not obj: | |
return | |
return obj.GetName() | |
def IsOpened(self, root, userdata, obj): | |
"""Returns True if obj is unfolded.""" | |
return obj.IsOpened() | |
def IsSelected(self, root, userdata, obj): | |
"""Returns True if obj is selected.""" | |
return obj.IsSelected() | |
def Select(self, root, userdata, obj, mode): | |
"""Selects `obj` based on `mode`.""" | |
if debug: | |
print "Select()" | |
if mode == c4d.SELECTION_NEW: | |
for item in self.items: | |
item.Deselect() | |
if item == obj: | |
item.Select() | |
elif mode == c4d.SELECTION_ADD: | |
obj.Select() | |
elif mode == c4d.SELECTION_SUB: | |
obj.Deselect() | |
def Open(self, root, userdata, obj, onoff): | |
"""Folds or unfolds obj based on onoff.""" | |
print "Open()" | |
if not obj: | |
return | |
if onoff: | |
obj.SetBit(c4d.BIT_OFOLD) | |
else: | |
obj.DelBit(c4d.BIT_OFOLD) | |
c4d.EventAdd() | |
# ====== DIALOG ====== # | |
class ObjectBrowser(c4d.gui.GeDialog): | |
"""Dialog that contains a list of Materials & Shaders in the active document.""" | |
_tree_gui = None | |
def CreateLayout(self): | |
"""Build the overall dialog layout.""" | |
self.SetTitle(PLUGIN_NAME) | |
# Build the ShaderTree GUI Element | |
tree_gui_settings = c4d.BaseContainer() | |
tree_gui_settings.SetLong(c4d.TREEVIEW_BORDER, c4d.BORDER_THIN_IN) | |
tree_gui_settings.SetBool(c4d.TREEVIEW_HAS_HEADER, True) | |
tree_gui_settings.SetBool(c4d.TREEVIEW_HIDE_LINES, False) | |
tree_gui_settings.SetBool(c4d.TREEVIEW_MOVE_COLUMN, True) | |
tree_gui_settings.SetBool(c4d.TREEVIEW_RESIZE_HEADER, True) | |
tree_gui_settings.SetBool(c4d.TREEVIEW_FIXED_LAYOUT, True) # Don't allow Columns to be re-ordered | |
tree_gui_settings.SetBool(c4d.TREEVIEW_ALTERNATE_BG, True) # Alternate Light/Dark Gray BG | |
tree_gui_settings.SetBool(c4d.TREEVIEW_CURSORKEYS, True) # Process Up/Down Arrow Keys | |
self._tree_gui = self.AddCustomGui( | |
0, | |
c4d.CUSTOMGUI_TREEVIEW, | |
"", | |
c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, | |
300, | |
300, | |
tree_gui_settings | |
) | |
return True | |
def InitValues(self): | |
"""Set initial values when dialog is first opened.""" | |
tree_data = ObjectTree(self) | |
self._tree_gui.SetRoot(None, tree_data, None) | |
return True | |
# ====== COMMAND ====== # | |
class ObjectBrowserCommand(c4d.plugins.CommandData): | |
"""Command that opens a ObjectTree dialog.""" | |
dlg = None | |
def Execute(self, doc): | |
if self.dlg is None: | |
self.dlg = ObjectBrowser() | |
return self.dlg.Open( | |
dlgtype=c4d.DLG_TYPE_ASYNC, | |
pluginid=PLUGIN_ID, | |
xpos=-1, | |
ypos=-1, | |
defaultw=300, | |
defaulth=500 | |
) | |
def GetState(self, doc): | |
return c4d.CMD_ENABLED | |
def RestoreLayout(self, sec_ref): | |
if self.dlg is None: | |
self.dlg = ObjectBrowser() | |
return self.dlg.Restore(PLUGIN_ID, secret=sec_ref) | |
def main(): | |
"""Register the plugin with Cinema 4D.""" | |
c4d.plugins.RegisterCommandPlugin( | |
PLUGIN_ID, | |
PLUGIN_NAME, | |
0, | |
None, | |
PLUGIN_HELP, | |
ObjectBrowserCommand() | |
) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment