OOP class struggles: treeview envoce gui refresh outside class ListView()
-
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 theredef Select
from which I would like to triger a GUI refresh inside theclass TestDialog(c4d.gui.GeDialog):
and therefore outside theclass 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 [URL-REMOVED] 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
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
userdata parameter of the TreeViewFunctions overidden methods can be anything, your dialog for example.
-
hi,
you could also add your gedialog as a parameter when you create your treeviewfunctions
something like this.
import c4d import os import weakref class Hierarchy(c4d.gui.TreeViewFunctions): def __init__(self, dlg): # Avoid a cyclic reference. self._dlg = weakref.ref(dlg) # add the code ... # when you create an instance of your TreeViewFunctions data_model = Hierarchy(self)
and in your select method you need to call the dialog function like so
self._dlg()._CalcSum()
Cheers,
Manuel -
thank you for your help, I am trying to understand ...
am I right in the assumption that this is not "clean code" in a pythonic sense ?
kind regards