How to Override DoubleClick() from the TreeViewFunctions?
-
Hi,
By default, when you double click in a
TreeView
item, an editable text box appears and allows you to rename theTreeView
item. Using the code from @Donovan Keith, it results to the following:
https://www.dropbox.com/s/oxbr1o68vwjfwd3/c4d268_override_double_click_treeview.mp4?dl=0It works if you are using the c4d objects but not if you are using custom nodes.
I don't know how to override the DoubleClick. The following code prints the statement properly but it doesn't call the "renaming" feature or fetch the new string.def DoubleClick(self, root, userdata, obj, col, mouseinfo): print "Please Rename Me" return True
The result can be seen here.
Is there a way around this?
Regards,
Ben -
hi,
Your
DoubleClick
function is returning True, so the renaming function is not kicking in.You could open a "rename" dialogbox and do the rename in the DoubleClick function or set a "global" variable where you can store the
col
and let theGetName
andSetName
Cheers,
Manuel -
@m_magalhaes
Thanks for the response.
RE: You could open a "rename" dialogbox
I understand I can do that, but I prefer the default behavior of the double click: An edit text appears and when I hit enter the treeview item is immediately renamed.Is this still possible with non native C4D objects?
I guess in more direct term what is the UI code for
when I double click an edit text appears and when I hit enter the treeview item is immediately renamed.
See video above in the first post for reference.
-
Hm,
probably not what you are looking for, but when you run out of other options, you could try to use
mouseinfo
and the screen position of your hosting dialog to open a borderless dialog just in the right place, i.e. over the element you want to rename. Will probably require some fiddling, but should be possible.Cheers,
zipit -
hi,
Sorry my answer wasn't clear. I'm not using c4d object.
The rename default behaviour will not happen if your double click function return True. (meaning the double click have been handled)
It's not related with using default c4d objects or not.
So don't return true in your
DoubleClick
function.The "problem" is that it will use the
GetName/SetName
functions. Those functions doesn't know what columns have been clicked.
When you double click, theGetName
function is called to display the text that you want to rename, and when you press enter, theSetName
is called.Imagine you have FirstName and LastName. You double click the FirstName. In the
DoubleClick
function you will set a variable "FirstName"
In the SetName function you will be able to use this kind of "code"# just to get the idea if self.firstName: obj._data.firstName = str else: obj._data.lastName = str
Cheers
Manuel. -
Thanks for the response
@zipit
Yea I think that would be the last resort as it might end up with more code than just having ah popup rename dialog. I was just thinking of leveraging the default behavior and fetching theold name
and thenew name
@m_magalhaes
RE: If I omit the
Return True
It will give me an error ofTypeError: DoubleClick expected bool, not None
RE: GetName function is called to display the text
RE: when you press enter, the SetName is called.
Correct me if I'm wrong the functionsGetName
andSetName
are only available only on default C4D objects. I can't use it.RE: Imagine you have FirstName and LastName. You double click the FirstName
Sorry I don't follow this. In my video example above, I don't have any other column, except 1.Apologies if this is longer than expected, but it is there I can fetch the new name without using the
GetName
as it does not work on myobj
since its not a native C4D.
For instance,- There is a
name
variable - Current value is "Folder A". Corresponds with the name of the Tree Item.
- Double Click. In place add edit text appears. Then Rename it to "Folder B".
- Store the new string to
name
variable - Print
name
variable. Should be "Folder B"
- There is a
-
@bentraje said in How to Override DoubleClick() from the TreeViewFunctions?:
RE: If I omit the Return True
the documentation say :
Returns:
True if the event was handled, otherwise False.
So you double click, it call the double click function. If you return True, cinema4D will think you have handle that double click and will not call the default "rename" function.
If you return False, Cinema4D will think that you couldn't handle it and will launch it's default function to rename the object.You have to differentiate the data and the UI. It's two thing different with different purpose and functions.
The TreeViewFunction have
GetName
andSetName
functions. This tree purpose is to display hierarchies. That's why it also have those function, InsertUnder, GetNext etc etc.
Those function are here to make the UI understand how to retrieve and handle your data.When you are using the c4d's object, they come with all those function by default. But that doesn't mean you can't use your own data with there own function.
It's easier to use c4d's object but not mandatory.I've picked maxime's example and adapt it so you can understand.
It has two columns. Depending on where you click, it will either set a data do "default" or return false and c4d will call the
SetName
of the TreeViewFunction were you can change your data.I didn't implemented
SetName
on my object were i store my data.I also passed a link of the GUI to the TreeViewFunction so i can refresh it inside the
DoubleClickFunction
#More information https://developers.maxon.net/forum/topic/10654#56287 import c4d # Be sure to use a unique ID obtained from [URL-REMOVED] PLUGIN_ID = 1000010 # TEST ID ONLY # TreeView Column IDs. ID_CHECKBOX = 1 ID_NAME = 2 ID_OTHER = 3 class TextureObject(object): """ Class which represent a texture, aka an Item in our list """ texturePath = "TexPath" otherData = "OtherData" _selected = False def __init__(self, texturePath): self.texturePath = texturePath self.otherData += texturePath @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 class ListView(c4d.gui.TreeViewFunctions): def __init__(self, dlgPointer): self.listOfTexture = list() # Store all objects we need to display in this list self.dlgPointer = dlgPointer # Add some defaults values t1 = TextureObject("T1") t2 = TextureObject("T2") t3 = TextureObject("T3") t4 = TextureObject("T4") self.listOfTexture.extend([t1, t2, t3, t4]) 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() 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 SetName(self, root, userdata, obj, str): obj.otherData = str 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 """ rgbSelectedColor = c4d.gui.GeUserArea().GetColorRGB(c4d.COLOR_TEXT_SELECTED) selectedColor = c4d.Vector(rgbSelectedColor["r"], rgbSelectedColor["g"], rgbSelectedColor["b"]) / 255.0 txtColor = selectedColor if obj.IsSelected else c4d.Vector(0.2, 0.4, 0.8) drawinfo["frame"].DrawSetTextCol(txtColor, drawinfo["bgCol"]) if col == ID_NAME: name = str(obj) 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) xpos = drawinfo["xpos"] ypos = drawinfo["ypos"] + drawinfo["height"] if col == ID_OTHER: name = obj.otherData 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)) # if the col 2 is double clicked if (col == 2): obj.otherData = "default" self.dlgPointer._treegui.Refresh() return True return False 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 def __init__(self): self._listView = ListView(self) # Our Instance of c4d.gui.TreeViewFunctions def CreateLayout(self): # Create the TreeView GUI. customgui = c4d.BaseContainer() customgui.SetBool(c4d.TREEVIEW_BORDER, c4d.BORDER_THIN_IN) customgui.SetBool(c4d.TREEVIEW_HAS_HEADER, True) # True if the tree view may have a header line. customgui.SetBool(c4d.TREEVIEW_HIDE_LINES, False) # True if no lines should be drawn. customgui.SetBool(c4d.TREEVIEW_MOVE_COLUMN, True) # 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, False) # Suppresses the rename popup when the user presses enter. self._treegui = self.AddCustomGui( 1000, c4d.CUSTOMGUI_TREEVIEW, "", c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 300, 300, customgui) if not self._treegui: print "[ERROR]: Could not create TreeView" return False self.AddButton(1001, c4d.BFH_CENTER, name="Add") return True def InitValues(self): # Initialize the column layout for the TreeView. layout = c4d.BaseContainer() layout.SetLong(ID_CHECKBOX, c4d.LV_CHECKBOX) layout.SetLong(ID_NAME, c4d.LV_USER) 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, "Name") self._treegui.SetHeaderText(ID_OTHER, "Other") self._treegui.Refresh() # Set TreeViewFunctions instance used by our CUSTOMGUI_TREEVIEW self._treegui.SetRoot(self._treegui, self._listView, None) return True def Command(self, id, msg): # Click on button if id == 1001: # Add data to our DataStructure (ListView) newID = len(self._listView.listOfTexture) + 1 tex = TextureObject("T{}".format(newID)) self._listView.listOfTexture.append(tex) # Refresh the TreeView self._treegui.Refresh() return True def main(): global dlg dlg = TestDialog() dlg.Open(c4d.DLG_TYPE_ASYNC, PLUGIN_ID, defaulth=600, defaultw=600) if __name__ == "__main__": main()
let me know if something is not clear. I'm trying to be more generic than just answer your question
in your problem just do something like
obj.name = "my new name"
Cheers,
Manuel
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.
-
This post is deleted! -
@m_magalhaes
Thanks for the response.
The confusion is entirely mine.I was confused because I initially thought the
GetName
andSetName
is only for typical C4D objects.
but there is actually a separateGetName
andSetName
functions for the TreeView objects.I was able to retrieve the
new string
by just this code:def SetName(self,root, userdata, obj, name): print name # new name when you hit enter
I can now use the name variable to use in my separate renaming function (i.e. rename a folder for which the TreeView was based on).
Thanks!