Skip to content

Instantly share code, notes, and snippets.

@donovankeith
Created June 17, 2016 18:55
Show Gist options
  • Save donovankeith/26e086772ee94851553861bc77c26fc3 to your computer and use it in GitHub Desktop.
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.
"""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