Dear Developpers,
the last days have been enlightening for me because I am getting more comfortable with this class thing ;-) Nevertheless beginner ??? are bound to happen ...
I have running plugin with a c4d.gui.TreeViewFunctions
in there def Select
from which I would like to triger a GUI refresh inside the class TestDialog(c4d.gui.GeDialog):
and therefore outside the class ListView(c4d.gui.TreeViewFunctions):
The manual calculation of the sum with self._CalcSum()
works (by pressing a button) but I would like to have it interactive when the user selects something in the treeview.
I do not expext you to go through all my code a nice easy example would do I guess.
All the code should work besides some not usefull bookmarks ;-)
import c4d, os
from c4d import gui
# Be sure to use a unique ID obtained from http://www.plugincafe.com/.
PLUGIN_ID = 1000011 # TEST ID ONLY
PLUGIN_NAME = "Cad File Importer"
#TreeView Column IDs.
ID_CHECKBOX = 1
ID_NAME = 2
ID_OTHER = 3
ID_BOOKMARKLIST = ("C:\\Users\\admin\\Pictures", "E:\Bilder\Bilder Temp\Screenshots", "", "C:\\wrong path", "C:\\")
ID_FOLDERSELECTION_SYSTEM = 1111
ID_SCANCONTENT = 1112
ID_PATHSTRING = 1113
ID_INFOBOX = 201
ID_CLOSE = 1114
ID_SELECTALL = 1115
ID_SUMBOX = 1116
ID_COLOREDBORDER = 1117
ID_IMPORT = 1118
class TextureObject(object):
"""
Class which represent a texture, aka an Item in our list
"""
texturePath = "TexPath"
otherData = "OtherData "
_selected = False
#filesize = 0
def __init__(self, texturePath, otherData):
self.texturePath = texturePath
self.otherData = otherData
#self.otherData = round(float(otherData) / 1024.0 / 1024.0, 1)
#self.filesize = filesize
@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.texturePath
def NiceFileSize(self):
return round(float(self.otherData) / 1024.0 / 1024.0, 1)
class ListView(c4d.gui.TreeViewFunctions):
def __init__(self):
self.listOfTexture = list() # Store all objects we need to display in this list
"""
def _CalcSum(self):
sumselected = 0
for fileitem in self.listOfTexture:
if fileitem.IsSelected == True:
sumselected += fileitem.otherData
print (round(float(sumselected) / 1024.0 / 1024.0, 1))
return (round(float(sumselected) / 1024.0 / 1024.0, 1))
"""
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 # All have the same initial width
def IsMoveColAllowed(self, root, userdata, lColID):
# The user is allowed to move all columns.
# TREEVIEW_MOVE_COLUMN must be set in the container of AddCustomGui.
return True
def GetFirst(self, root, userdata):
"""
Return the first element in the hierarchy, or None if there is no element.
"""
rValue = None if not self.listOfTexture else self.listOfTexture[0]
return rValue
def GetDown(self, root, userdata, obj):
"""
Return a child of a node, since we only want a list, we return None everytime
"""
return None
def GetNext(self, root, userdata, obj):
"""
Returns the next Object to display after arg:'obj'
"""
rValue = None
currentObjIndex = self.listOfTexture.index(obj)
nextIndex = currentObjIndex + 1
if nextIndex < len(self.listOfTexture):
rValue = self.listOfTexture[nextIndex]
return rValue
def GetPred(self, root, userdata, obj):
"""
Returns the previous Object to display before arg:'obj'
"""
rValue = None
currentObjIndex = self.listOfTexture.index(obj)
predIndex = currentObjIndex - 1
if 0 <= predIndex < len(self.listOfTexture):
rValue = self.listOfTexture[predIndex]
return rValue
def GetId(self, root, userdata, obj):
"""
Return a unique ID for the element in the TreeView.
"""
return hash(obj)
def Select(self, root, userdata, obj, mode):
"""
Called when the user selects an element.
"""
if mode == c4d.SELECTION_NEW:
# for tex in self.listOfTexture:
# tex.Deselect()
obj.Select()
elif mode == c4d.SELECTION_ADD:
obj.Select()
elif mode == c4d.SELECTION_SUB:
obj.Deselect()
# i want to call _CalcSum here how ??? ------------------------------------------------------------------------------------------------------------------------------
"""
MenuCommand ok
dialog
RestoreLayout
TestDialog ok
"""
#sum = MenuCommand.dialog.RestoreLayout.TestDialog._CalcSum
print (TestDialog._CalcSum)
def IsSelected(self, root, userdata, obj):
"""
Returns: True if *obj* is selected, False if not.
"""
return obj.IsSelected
def SetCheck(self, root, userdata, obj, column, checked, msg):
"""
Called when the user clicks on a checkbox for an object in a
`c4d.LV_CHECKBOX` column.
"""
if checked:
obj.Select()
else:
obj.Deselect()
def IsChecked(self, root, userdata, obj, column):
"""
Returns: (int): Status of the checkbox in the specified *column* for *obj*.
"""
if obj.IsSelected:
return c4d.LV_CHECKBOX_CHECKED | c4d.LV_CHECKBOX_ENABLED
else:
return c4d.LV_CHECKBOX_ENABLED
def GetName(self, root, userdata, obj):
"""
Returns the name to display for arg:'obj', only called for column of type LV_TREE
"""
return str(obj) # Or obj.texturePath
def DrawCell(self, root, userdata, obj, col, drawinfo, bgColor):
"""
Draw into a Cell, only called for column of type LV_USER
"""
if col == ID_OTHER:
#name = obj.otherData
name = obj.NiceFileSize()
geUserArea = drawinfo["frame"]
w = geUserArea.DrawGetTextWidth(name)
h = geUserArea.DrawGetFontHeight()
xpos = drawinfo["xpos"]
ypos = drawinfo["ypos"] + drawinfo["height"]
drawinfo["frame"].DrawText(name, xpos, ypos - h * 1.1)
def DoubleClick(self, root, userdata, obj, col, mouseinfo):
"""
Called when the user double-clicks on an entry in the TreeView.
Returns:
(bool): True if the double-click was handled, False if the
default action should kick in. The default action will invoke
the rename procedure for the object, causing `SetName()` to be
called.
"""
#c4d.gui.MessageDialog("You clicked on " + str(obj))
return True
def DeletePressed(self, root, userdata):
"Called when a delete event is received."
for tex in reversed(self.listOfTexture):
if tex.IsSelected:
self.listOfTexture.remove(tex)
class TestDialog(c4d.gui.GeDialog):
_treegui = None # Our CustomGui TreeView
_listView = ListView() # Our Instance of c4d.gui.TreeViewFunctions
def CreateLayout(self):
#create the menu
self.MenuFlushAll()
#Bookmarks menu
self.MenuSubBegin("Bookmarks")
#self.MenuAddString(MNU_BOOKMARK_1["id"], MNU_BOOKMARK_1["name"])
#self.MenuAddString(MNU_BOOKMARK_2["id"], MNU_BOOKMARK_2["name"])
for addid, folderlink in enumerate(ID_BOOKMARKLIST):
self.MenuAddString(addid+500, folderlink) # we add 500 to be out of range of common IDs we subtract 500 below in the comand section
self.MenuSubEnd()
if self.GroupBegin(id=1, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, rows=5, cols=1, groupflags=c4d.BORDER_GROUP_IN):
self.GroupBorderSpace(5, 5, 5, 5)
self.GroupBorderNoTitle(borderstyle=c4d.BORDER_BLACK)
# Folder Selection group with title
#----------------------------------------------------------
if self.GroupBegin(id=11, flags=c4d.BFH_SCALEFIT, rows=1, cols=1, title="Folderpath", groupflags=c4d.BORDER_GROUP_IN):
self.GroupBorderSpace(5, 5, 5, 5)
self.GroupBorder(c4d.BORDER_ROUND)
# Colored Border group
#----------------------------------------------------------
if self.GroupBegin(id=ID_COLOREDBORDER, flags=c4d.BFH_SCALEFIT, rows=1, cols=3, groupflags=c4d.BORDER_GROUP_OUT):
self.GroupBorderNoTitle(c4d.BORDER_ACTIVE_2)
self.AddEditText(id=ID_PATHSTRING, flags=c4d.BFH_LEFT | c4d.BFH_SCALEFIT, initw=432, inith=0, editflags=c4d.EDITTEXT_HELPTEXT)
self.SetString(id=ID_PATHSTRING, value="Choose folder with dialog or paste the path here.", flags=c4d.EDITTEXT_HELPTEXT )
self.AddButton(id=ID_FOLDERSELECTION_SYSTEM, flags=c4d.BFV_FIT | c4d.BFH_FIT | c4d.BFH_RIGHT, name="Select Folder")
self.AddButton(id=ID_SCANCONTENT, flags=c4d.BFV_FIT | c4d.BFH_FIT | c4d.BFH_RIGHT, name="Load folder content")
self.GroupEnd()
self.GroupEnd()
if self.GroupBegin(ID_INFOBOX, cols=1, flags=c4d.BFH_SCALEFIT, initw=0, inith=0 ,title=""):
self.AddStaticText(212, flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_CENTER | c4d.BFH_SCALE, initw=0, inith=0, name="Info box")
self.GroupEnd()
if self.GroupBegin(999, cols=1, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, initw=0, inith=0 ,title="Files in Folder:"):
self.GroupBorderSpace(5, 5, 5, 5)
self.GroupBorder(c4d.BORDER_ROUND)
self.AddCheckbox( ID_SELECTALL, flags=c4d.BFH_LEFT, initw=0, inith=0 , name="Select All")
# Create the TreeView GUI.
#----------------------------------------------------------
customgui = c4d.BaseContainer()
#customgui.SetBool(c4d.TREEVIEW_BORDER, c4d.BORDER_THIN_IN )
customgui.SetBool(c4d.TREEVIEW_BORDER, c4d.BORDER_NONE | c4d.BORDER_GROUP_IN)
customgui.SetBool(c4d.TREEVIEW_HAS_HEADER, True) # True if the tree view may have a header line.
customgui.SetBool(c4d.TREEVIEW_HIDE_LINES, True) # True if no lines should be drawn.
customgui.SetBool(c4d.TREEVIEW_MOVE_COLUMN, False) # True if the user can move the columns.
customgui.SetBool(c4d.TREEVIEW_RESIZE_HEADER, True) # True if the column width can be changed by the user.
customgui.SetBool(c4d.TREEVIEW_FIXED_LAYOUT, True) # True if all lines have the same height.
customgui.SetBool(c4d.TREEVIEW_ALTERNATE_BG, True) # Alternate background per line.
customgui.SetBool(c4d.TREEVIEW_CURSORKEYS, True) # True if cursor keys should be processed.
customgui.SetBool(c4d.TREEVIEW_NOENTERRENAME, True) # Suppresses the rename popup when the user presses enter.
customgui.SetBool(c4d.TREEVIEW_NO_DELETE, True) # Disable Delete Message Callback completely for backspace and delete.
#customgui.SetBool(c4d.TREEVIEW_ADDROW, True) # Show an add new column row at the bottom of the list.
self._treegui = self.AddCustomGui( 1000, c4d.CUSTOMGUI_TREEVIEW, "", c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 400, 100, customgui) # minw, minh
if not self._treegui:
print ("[ERROR]: Could not create TreeView")
return False
self.GroupEnd()
if self.GroupBegin(ID_SUMBOX, cols=2, flags=c4d.BFH_SCALEFIT, initw=0, inith=0, title="Filesizesum:"):
self.GroupBorderSpace(5, 5, 5, 5)
self.GroupBorderNoTitle(borderstyle=c4d.BORDER_BLACK)
self.AddStaticText(332, flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_CENTER | c4d.BFH_SCALE, initw=0, inith=0, name="Selected: ")
self.AddStaticText(333, flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_RIGHT | c4d.BFH_SCALE, initw=0, inith=0, name="Filesize Sum: ")
self.GroupEnd()
# OK Cancel
#----------------------------------------------------------
if self.GroupBegin(id=3, flags=c4d.BFV_BOTTOM | c4d.BFH_SCALEFIT, inith=25, rows=1, cols=2, title="", groupflags=c4d.BORDER_GROUP_IN):
#self.GroupBorderSpace(5, 5, 5, 5)
self.GroupBorderNoTitle(borderstyle=c4d.BORDER_NONE) # BORDER_NONE BORDER_BLACK
self.AddButton(ID_CLOSE, flags=c4d.BFH_LEFT | c4d.BFV_SCALEFIT | c4d.BFH_SCALE | c4d.BFV_BOTTOM, name="Close")
self.AddButton(ID_IMPORT, flags=c4d.BFH_RIGHT | c4d.BFV_SCALEFIT | c4d.BFH_SCALE | c4d.BFV_BOTTOM, name="Import")
self.GroupEnd()
self.GroupEnd() # main group end
# self.AddButton(1001, c4d.BFH_CENTER, name="Add")
return True
def InitValues(self):
self.folderpath = None
# Initialize the column layout for the TreeView.
layout = c4d.BaseContainer()
layout.SetLong(ID_CHECKBOX, c4d.LV_CHECKBOX)
layout.SetLong(ID_NAME, c4d.LV_TREE)
layout.SetLong(ID_OTHER, c4d.LV_USER)
self._treegui.SetLayout(3, layout)
# Set the header titles.
self._treegui.SetHeaderText(ID_CHECKBOX, "Check")
self._treegui.SetHeaderText(ID_NAME, "File Name")
self._treegui.SetHeaderText(ID_OTHER, "Filesize (MB)")
self._treegui.Refresh()
# Set TreeViewFunctions instance used by our CUSTOMGUI_TREEVIEW
self._treegui.SetRoot(self._treegui, self._listView, None)
return True
# Change GUI
# --------------------------------------------------------------------------------------------------------------------------
def _DrawGroup(self):
print ("_DrawGroup: ", self.folderpath)
# Flush the content of the group that holds all ours SubDialogs
self.LayoutFlushGroup(ID_COLOREDBORDER)
# new content
if self.folderpath == None or self.folderpath == "":
self.GroupBorderNoTitle(c4d.BORDER_ACTIVE_4) # orange
self.AddEditText(id=ID_PATHSTRING, flags=c4d.BFH_LEFT | c4d.BFH_SCALEFIT, initw=432, inith=0, editflags=c4d.EDITTEXT_HELPTEXT)
self.SetString(id=ID_PATHSTRING, value="Choose folder with dialog or paste the path here.", flags=c4d.EDITTEXT_HELPTEXT )
self.AddButton(id=ID_FOLDERSELECTION_SYSTEM, flags=c4d.BFV_FIT | c4d.BFH_FIT | c4d.BFH_RIGHT, name="Select Folder")
self.AddButton(id=ID_SCANCONTENT, flags=c4d.BFV_FIT | c4d.BFH_FIT | c4d.BFH_RIGHT, name="Load folder content")
self.LayoutFlushGroup(ID_INFOBOX)
self.AddStaticText(212, flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_CENTER | c4d.BFH_SCALE, initw=0, inith=0, name = "Select a path.")
elif not os.path.exists(self.folderpath):
self.GroupBorderNoTitle(c4d.BORDER_ACTIVE_3) # red
self.AddEditText(id=ID_PATHSTRING, flags=c4d.BFH_LEFT | c4d.BFH_SCALEFIT, initw=432, inith=0, editflags=c4d.EDITTEXT_HELPTEXT)
self.SetString(id=ID_PATHSTRING, value=self.folderpath)
self.AddButton(id=ID_FOLDERSELECTION_SYSTEM, flags=c4d.BFV_FIT | c4d.BFH_FIT | c4d.BFH_RIGHT, name="Select Folder")
self.AddButton(id=ID_SCANCONTENT, flags=c4d.BFV_FIT | c4d.BFH_FIT | c4d.BFH_RIGHT, name="Load folder content")
self.LayoutFlushGroup(ID_INFOBOX)
self.AddStaticText(212, flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_CENTER | c4d.BFH_SCALE, initw=0, inith=0, name = "Invalid path please check.")
else:
self.GroupBorderNoTitle(c4d.BORDER_ACTIVE_2)
self.AddEditText(id=ID_PATHSTRING, flags=c4d.BFH_LEFT | c4d.BFH_SCALEFIT, initw=432, inith=0, editflags=c4d.EDITTEXT_HELPTEXT)
self.AddButton(id=ID_FOLDERSELECTION_SYSTEM, flags=c4d.BFV_FIT | c4d.BFH_FIT | c4d.BFH_RIGHT, name="Select Folder")
self.AddButton(id=ID_SCANCONTENT, flags=c4d.BFV_FIT | c4d.BFH_FIT | c4d.BFH_RIGHT, name="Load folder content")
self.SetString(id=ID_PATHSTRING, value=self.folderpath) # flags=c4d.EDITTEXT_HELPTEXT
self.LayoutFlushGroup(ID_INFOBOX)
self.AddStaticText(212, flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_CENTER | c4d.BFH_SCALE, initw=0, inith=0, name = "Good to go.")
# Notifies the content of the Group has changed
self.LayoutChanged(ID_INFOBOX)
self.LayoutChanged(ID_COLOREDBORDER)
# --------------------------------------------------------------------------------------------------------------------------
def _ShowSum(self, valuetodisplay=0, selected=0):
print ("_ShowSum: ")
howmanyselected = "Selected: " + str(selected)
valuetodisplay = "Filesize Sum: " + str(valuetodisplay) + " MB"
self.LayoutFlushGroup(ID_SUMBOX)
self.GroupBorderSpace(5, 5, 5, 5)
self.GroupBorderNoTitle(borderstyle=c4d.BORDER_BLACK)
self.AddStaticText(332, flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_CENTER | c4d.BFH_SCALE, initw=0, inith=0, name=howmanyselected)
self.AddStaticText(333, flags=c4d.BFV_CENTER | c4d.BFV_SCALE | c4d.BFH_RIGHT | c4d.BFH_SCALE, initw=0, inith=0, name=valuetodisplay)
self.LayoutChanged(ID_SUMBOX)
def _CalcSum(self):
sumselected = 0
selected = 0
for fileitem in self._listView.listOfTexture:
if fileitem.IsSelected == True:
sumselected += fileitem.otherData
selected += 1
self._ShowSum((round(float(sumselected) / 1024.0 / 1024.0, 1)), selected)
#return (round(float(sumselected) / 1024.0 / 1024.0, 1))
# --------------------------------------------------------------------------------------------------------------------------
def loadfolder(self):
self._listView.listOfTexture = list() # flush the list hence we want to load the content only once
# Add data to our DataStructure (ListView)
newID = len(self._listView.listOfTexture)
if self.folderpath != None and self.folderpath != "" and os.path.exists(self.folderpath):
filelist = os.listdir(self.folderpath.decode("utf-8", "strict"))
filelist.sort()
for fileid, img in enumerate ( filelist ):
#print (img)
img = img.encode("utf-8", "strict")
#print (img)
fullpath = self.folderpath + "\\" + img
#print (fullpath)
#texsize = round(float(os.path.getsize(fullpath)) / 1024.0 / 1024.0, 1)
texsize = os.path.getsize(fullpath)
#tex = TextureObject("T{}".format(newID + fileid))
tex = TextureObject(img.format(newID + fileid), texsize) # the object
self._listView.listOfTexture.append(tex) # the big list
#print (img)
else:
print (self.folderpath)
# Refresh the TreeView
self._treegui.Refresh()
def Command(self, id, msg):
if id == ID_SELECTALL:
print ("Checkbox Staus: ", self.GetBool(ID_SELECTALL))
if self.GetBool(ID_SELECTALL) == True:
for fileitem in self._listView.listOfTexture:
fileitem.Select()
if self.GetBool(ID_SELECTALL) == False:
for fileitem in self._listView.listOfTexture:
fileitem.Deselect()
self._treegui.Refresh()
return True
if 500 <= id <= 550:
bigid = int(id-500)
#print ("Dropdown ID: ", id, ID_BOOKMARKLIST[bigid])
self.SetString(ID_PATHSTRING, ID_BOOKMARKLIST[bigid])
self.folderpath = self.GetString(ID_PATHSTRING)
self._DrawGroup()
return True
if id == ID_FOLDERSELECTION_SYSTEM:
self.folderpath = c4d.storage.LoadDialog(type=c4d.FILESELECTTYPE_ANYTHING, title='Choose a Folder with the 3D Data to import.', flags=c4d.FILESELECT_DIRECTORY, force_suffix='', def_path='', def_file='')
self.SetString(ID_PATHSTRING, str(self.folderpath))
self._DrawGroup()
return True
if id == ID_SCANCONTENT:
self.folderpath = self.GetString(ID_PATHSTRING)
self._DrawGroup()
self.loadfolder()
if id == ID_CLOSE:
self.Close()
return True
if id == ID_IMPORT:
self._CalcSum()
return True
return True
def CoreMessage(self, id, msg):
if id == c4d.EVMSG_CHANGE:
print "Scene was Changed"
return gui.GeDialog.CoreMessage(self, id, msg)
def Message(self, id, msg):
if c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, c4d.KEY_ESC, msg):
if msg[c4d.BFM_INPUT_VALUE]:
#print ("ESC Pressed - closing window.")
self.Close()
return True
return gui.GeDialog.Message(self, id, msg)
class MenuCommand(c4d.plugins.CommandData):
dialog = None
def Execute(self, doc):
if self.dialog is None:
self.dialog = TestDialog()
return self.dialog.Open(c4d.DLG_TYPE_ASYNC, PLUGIN_ID, defaulth=200, defaultw=600)
def RestoreLayout(self, sec_ref):
if self.dialog is None:
self.dialog = TestDialog()
return self.dialog.Restore(PLUGIN_ID, secret=sec_ref)
def main():
# Retrieves the icon path
directory, _ = os.path.split(__file__)
fn = os.path.join(directory, "res", "cadfile_importer_icon.tif")
if os.path.isfile(fn) :
# Creates a BaseBitmap
bmp = c4d.bitmaps.BaseBitmap()
if bmp is None:
raise MemoryError("Failed to create a BaseBitmap.")
# Init the BaseBitmap with the icon
if bmp.InitWith(fn)[0] != c4d.IMAGERESULT_OK:
#raise MemoryError("Failed to initialize the BaseBitmap.")
print ("Failed to initialize the BaseBitmap.")
else:
print ("Bitmap not Found.", PLUGIN_NAME)
bmp = None
try:
c4d.plugins.RegisterCommandPlugin( id=PLUGIN_ID,
str=PLUGIN_NAME,
info=0,
icon=bmp,
help="Lists files of a folder.",
dat=MenuCommand()
)
except ValueError:
print ("Following Plugin could not be registered: ", PLUGIN_NAME)
if __name__ == "__main__":
c4d.CallCommand(13957) # clear concole
main()
PS: I never know in which subforum to put these broad questions ...
kind regards mogh