@mfersaoui said in TreeView Menu:
GroupBegin
For my second question I found this function to interact with the htmlViewer area.
c4d.SpecialEventAdd(ACTION_ID)
@mfersaoui said in TreeView Menu:
GroupBegin
For my second question I found this function to interact with the htmlViewer area.
c4d.SpecialEventAdd(ACTION_ID)
Hi @m_adam ,
Thank you so much, I have two questions:
The first is What the right way to rewriting the following code and obtain same result:
class ListView(c4d.gui.TreeViewFunctions):
def __init__(self):
self.listOfTexture = list() # Store all objects we need to display in this list
def CreateLayout(self):
self.GroupBegin(0, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 2, 0)
self.GroupBorderSpace(5, 5, 5, 5)
self.GroupBegin(0, flags=c4d.BFH_LEFT | c4d.BFV_SCALEFIT, cols=1, rows=1, initw=400, inith=0)
# Create the TreeView GUI.
customgui = c4d.BaseContainer()
customgui.SetBool(c4d.TREEVIEW_BORDER, c4d.BORDER_THIN_IN)
customgui.SetBool(c4d.TREEVIEW_HAS_HEADER, False) # 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, False) # 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.
customgui.SetBool(c4d.TREEVIEW_NO_MULTISELECT, True)
self._treegui = self.AddCustomGui( 1000, c4d.CUSTOMGUI_TREEVIEW, "", c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 0, customgui)
if not self._treegui:
print "[ERROR]: Could not create TreeView"
return False
self.GroupEnd()
self.GroupBegin(0, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=1, rows=1)
html_file = "file.html"
htmlViewer = self.AddCustomGui(1000001, c4d.CUSTOMGUI_HTMLVIEWER, "Viewer", c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 0)
htmlViewer.SetUrl(html_file, c4d.URL_ENCODING_UTF16)
self.GroupEnd()
return True
def InitValues(self):
# Initialize the column layout for the TreeView.
layout = c4d.BaseContainer()
layout.SetLong(ID_NAME, c4d.LV_TREE)
self._treegui.SetLayout(1, layout)
mdata = {
"Menu 01": {
"Item 01": ["sub-item 01", "sub-item 02", "sub-item 03"],
"Item 02": ["sub-item 01", "sub-item 02"],
"Item 03": [],
},
"Menu 02": [],
"Menu 03": ["sub-item 01", "sub-item 02"],
"Menu 04": ["sub-item 01"]
}
res = []
for m in m_data:
m1 = TextureObject(m)
res.append(m1)
m1_items = m_data[m]
if len(m1_items):
for m1_name in m1_items:
m1_item = TextureObject(m1_name)
m1.AddChild(m1_item)
if isinstance(m1_items, dict):
m2_items = m1_items[m1_name]
if len(m2_items):
for m2_name in m2_items:
m2_item = TextureObject(m2_name)
m1_item.AddChild(m2_item)
self._listView.listOfTexture.extend(res)
# Set TreeViewFunctions instance used by our CUSTOMGUI_TREEVIEW
self._treegui.SetRoot(self._treegui, self._listView, None)
return True
Result:
The second question is How to interact with the htmlViewer area at the right of dialog from the my TreeView menu (Changing the htmlViewer file when clicking on a menu items).
def Select(self, root, userdata, obj, mode):
"""
Called when the user selects an element.
"""
if mode == c4d.SELECTION_NEW:
html_file = "file.html"
#TestDialog()
for tex in TextureObjectIterator(self.listOfTexture):
tex.Deselect()
obj.Select()
Thanks
Hello,
Is there a TreeView example to create a similar tree view menu like as the Cinema 4D help dialog?
Thanks.
Hi @m_magalhaes,
I used your suggestion and I replaced the +=
. Yes is better, but it's always the same problem. I don't know how to explain the problem, and for that reason I have created this plugin object example. So the best way to understand the problem is by testing the example.
I will try to explain roughly the problem:
The handle point is pinned on child object of the main object plugin and this child object has a dynamic (position and rotation) that depend on the Pitch & Rotation values. So I must to dynamically adapt the info.direction
depending on the position and rotation of the child object. And concerning this thread, this helped me to control the Pitch and Rotation via Handle but not the (Width, Height and Depth) values (of child object).
At the moment, I guess I'll just use the SwapPoint()
it doesn't have a perfect result but it's acceptable.
Thanks.
Hi, @zipit
Yes I'm searching a solution. I tried your solution but this does not work. the problem is with the handle point that not moving correctly (check the screen video above)
I actually using the following method, but is too long and its works at 90%
def GetAxis(self, pitch, rot):
pitch = int(math.degrees(pitch))
rot = int(math.degrees(rot))
if -90 <= rot < -45 or 225 <= rot < 270:
if -45 <= pitch < 45:
axis = 0 # +x
elif -225 <= pitch < -135 or 135 <= pitch < 225:
axis = 1 # -z
elif -135 <= pitch < -45:
axis = 3 # -y
elif 45 <= pitch < 135 or -270 <= pitch < -225:
axis = 2 # +y
else:
axis = 0 # +x
elif -45 <= rot < 45:
if -45 <= pitch < 45:
axis = 4 # +z
elif -225 <= pitch < -135 or 135 <= pitch < 225:
axis = 5 # -z
elif -135 <= pitch < -45:
axis = 13 # -y
elif 45 <= pitch < 135 or -270 <= pitch < -225:
axis = 12 # +y
else:
axis = 4 # +z
elif 45 <= rot < 135:
if -45 <= pitch < 45 or -225 <= pitch < -46:
axis = 11 # +x
elif -225 <= pitch < -135 or 135 <= pitch < 225:
axis = 10 # -z
elif 135 <= pitch < -45:
axis = 23 # -y
elif 45 <= pitch < 135 or -270 <= pitch < -225:
axis = 22 # +y
else:
axis = 1 # +x
elif 135 <= rot < 225:
if -45 <= pitch < 45:
axis = 15 # +x
elif -225 <= pitch < -135 or 135 <= pitch < 225:
axis = 14 # -z
elif -135 <= pitch < -45:
axis = 33 # -y
elif 45 <= pitch < 135 or -270 <= pitch < -225:
axis = 32 # +y
else:
axis = 5 # +x
else:
axis = 0
return axis
def SwapPoint(self, p, axis):
if axis == 0 :
return c4d.Vector(-p.z, p.y, p.x)
elif axis == 1 :
return c4d.Vector(p.z, -p.y, p.x)
elif axis == 2 :
return c4d.Vector(-p.y, -p.z, p.x)
elif axis == 3 :
return c4d.Vector(p.y, p.z, p.x)
elif axis == 4 :
return c4d.Vector(-p.x, p.y, -p.z)
elif axis == 5 :
return c4d.Vector(-p.x, -p.y, p.z)
elif axis == 12 :
return c4d.Vector(-p.x, -p.z, -p.y)
elif axis == 13 :
return c4d.Vector(-p.x, p.z, p.y)
elif axis == 10 :
return c4d.Vector(-p.z, -p.y, -p.x)
elif axis == 11 :
return c4d.Vector(p.z, p.y, -p.x)
elif axis == 22 :
return c4d.Vector(p.y, -p.z, -p.x)
elif axis == 23:
return c4d.Vector(-p.y, p.z, -p.x)
elif axis == 14:
return c4d.Vector(p.x, -p.y, -p.z)
elif axis == 15:
return c4d.Vector(p.x, p.y, p.z)
elif axis == 32 :
return c4d.Vector(p.x, -p.z, p.y)
elif axis == 33 :
return c4d.Vector(p.x, p.z, -p.y)
return p
axis = self.GetAxis(pitch, rotation)
if handle_index == 0: # Width
info.position = rot_mx * Vector(-size_x, 0, depth)
info.direction = self.SwapPoint(info.direction, axis)
Thanks.
Hello,
I'm searching if there a method to dynamically change the HandleInfo.direction depend on the rotation and pitch value of my object. I created the following object plugin example to illustrate exactly what I want to do.
Screen video:
pyp file:
import c4d, os
from c4d import utils, DescID, BaseObject, bitmaps, plugins, Vector, HandleInfo
from math import radians
PLUGIN_ID = 1055615
class Osobject(plugins.ObjectData):
def Init(self, op):
self.fs_mg = c4d.Matrix()
self.head_obj_h = 36
data = op.GetDataInstance()
data.SetFloat(c4d.OSOBJECT_SIZEX, 60.0)
data.SetFloat(c4d.OSOBJECT_SIZEY, 50.0)
data.SetFloat(c4d.OSOBJECT_DEPTH, 50.0)
data.SetFloat(c4d.OSOBJECT_PITCH, radians(-30))
data.SetFloat(c4d.OSOBJECT_ROTATION, radians(90))
return True
def GetVirtualObjects(self, op, hh):
dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTY_MATRIX | c4d.DIRTY_DATA)
if not dirty:
return op.GetCache(hh)
data = op.GetDataInstance()
size_x = data.GetFloat(c4d.OSOBJECT_SIZEX)
size_y = data.GetFloat(c4d.OSOBJECT_SIZEY)
depth = data.GetFloat(c4d.OSOBJECT_DEPTH)
pitch = data.GetFloat(c4d.OSOBJECT_PITCH)
rotation = data.GetFloat(c4d.OSOBJECT_ROTATION)
container = BaseObject(c4d.Onull)
container.SetRelPos(Vector(0, 40, 0))
bar = BaseObject(c4d.Ocylinder)
bar.SetName("bar")
bar.InsertUnder(container)
bar.SetParameter(DescID(c4d.PRIM_CYLINDER_RADIUS), 1.5, c4d.DESCFLAGS_GET_0)
bar.SetParameter(DescID(c4d.PRIM_CYLINDER_HEIGHT), 40, c4d.DESCFLAGS_GET_0)
bar.SetParameter(DescID(c4d.PRIM_AXIS), 2, c4d.DESCFLAGS_GET_0)
bar.SetRelPos(Vector(0, -20, 0))
# Rotation Object
rot_obj = BaseObject(c4d.Onull)
rot_obj.SetRelRot(Vector(rotation, 0, 0))
rot_obj.SetName("rotation")
rot_obj.InsertUnder(container)
# Pith Object
pitch_obj = BaseObject(c4d.Onull)
pitch_obj.SetRelRot(Vector(0, pitch, 0))
pitch_obj.SetName("pitch")
pitch_obj.InsertUnder(rot_obj)
# Head Container
head_container = BaseObject(c4d.Onull)
head_container.SetName("head_container")
head_container.InsertUnder(pitch_obj)
# Head Object
head_obj = BaseObject(c4d.Ocylinder)
head_obj.SetName("head")
head_obj.InsertUnder(head_container)
head_obj.SetParameter(DescID(c4d.PRIM_CYLINDER_RADIUS), 10, c4d.DESCFLAGS_GET_0)
head_obj.SetParameter(DescID(c4d.PRIM_CYLINDER_HEIGHT), self.head_obj_h, c4d.DESCFLAGS_GET_0)
head_obj.SetParameter(DescID(c4d.PRIM_AXIS), 5, c4d.DESCFLAGS_GET_0)
# depth Object
depth_obj = BaseObject(c4d.Ocylinder)
depth_obj.SetName("cyl")
depth_obj.InsertUnder(head_container)
depth_obj.SetParameter(DescID(c4d.PRIM_CYLINDER_RADIUS), 3, c4d.DESCFLAGS_GET_0)
depth_obj.SetParameter(DescID(c4d.PRIM_CYLINDER_HEIGHT), depth, c4d.DESCFLAGS_GET_0)
depth_obj.SetParameter(DescID(c4d.PRIM_AXIS), 5, c4d.DESCFLAGS_GET_0)
depth_obj_pos = self.head_obj_h/2 + depth/2
depth_obj.SetRelPos(Vector(0, 0, depth_obj_pos))
# Front Surface Object
fs_obj = BaseObject(c4d.Oplane)
fs_obj.SetName("front_surface")
fs_obj.InsertUnder(head_container)
self.fs_mg = fs_obj.GetMg()
fs_obj.SetParameter(DescID(c4d.PRIM_PLANE_WIDTH), size_x, c4d.DESCFLAGS_GET_0)
fs_obj.SetParameter(DescID(c4d.PRIM_PLANE_HEIGHT), size_y, c4d.DESCFLAGS_GET_0)
fs_obj.SetParameter(DescID(c4d.PRIM_PLANE_SUBW), 1, c4d.DESCFLAGS_GET_0)
fs_obj.SetParameter(DescID(c4d.PRIM_PLANE_SUBH), 1, c4d.DESCFLAGS_GET_0)
fs_obj.SetParameter(DescID(c4d.PRIM_AXIS), 5, c4d.DESCFLAGS_GET_0)
fs_obj_pos = self.head_obj_h/2 + depth
fs_obj.SetRelPos(Vector(0, 0, fs_obj_pos))
return container
def GetHandleCount(self, op):
return 4
def GetHandle(self, op, handle_index, info):
data = op.GetDataInstance()
size_x = data.GetFloat(c4d.OSOBJECT_SIZEX)
size_y = data.GetFloat(c4d.OSOBJECT_SIZEY)
depth = data.GetFloat(c4d.OSOBJECT_DEPTH)
pitch = data.GetFloat(c4d.OSOBJECT_PITCH)
rotation = data.GetFloat(c4d.OSOBJECT_ROTATION)
size_x = size_x/2
size_y = size_y/2
depth = depth+self.head_obj_h/2
mg = self.fs_mg
pos = mg.off
rot = utils.MatrixToHPB(mg, c4d.ROTATIONORDER_DEFAULT)
rot_mx = utils.HPBToMatrix(rot)
rot_mx.off += pos
if handle_index == 0: # Width
info.position = rot_mx * Vector(-size_x, 0, depth)
info.direction = Vector(1, 0, 0)
elif handle_index == 1: # Height
info.position = rot_mx * Vector(0, size_y, depth)
info.direction = Vector(0, 1, 0)
elif handle_index == 2: # Depth
info.position = rot_mx * Vector(0, 0, depth)
info.direction = Vector(0, 0, 1)
info.type = c4d.HANDLECONSTRAINTTYPE_LINEAR
def SetHandle(self, op, handle_index, handle_position, info):
handle_origin = HandleInfo()
self.GetHandle(op, handle_index, handle_origin)
data = op.GetDataInstance()
size_x = data.GetFloat(c4d.OSOBJECT_SIZEX)
size_y = data.GetFloat(c4d.OSOBJECT_SIZEY)
depth = data.GetFloat(c4d.OSOBJECT_DEPTH)
pitch = data.GetFloat(c4d.OSOBJECT_PITCH)
rotation = data.GetFloat(c4d.OSOBJECT_ROTATION)
size_x = size_x/2
size_y = size_y/2
value = (handle_position - handle_origin.position) * info.direction
if handle_index == 0:
op[c4d.OSOBJECT_SIZEX] -= value
elif handle_index == 1:
op[c4d.OSOBJECT_SIZEY] += value
elif handle_index == 2:
op[c4d.OSOBJECT_DEPTH] -= value
def Draw(self, op, drawpass, bd, bh):
if drawpass != c4d.DRAWPASS_HANDLES:
return c4d.DRAWRESULT_SKIP
data = op.GetDataInstance()
size_x = data.GetFloat(c4d.OSOBJECT_SIZEX)
size_y = data.GetFloat(c4d.OSOBJECT_SIZEY)
depth = data.GetFloat(c4d.OSOBJECT_DEPTH)
pitch = data.GetFloat(c4d.OSOBJECT_PITCH)
rotation = data.GetFloat(c4d.OSOBJECT_ROTATION)
size_x = size_x/2
size_y = size_y/2
bd.SetMatrix_Matrix(op, bh.GetMg())
hitid = op.GetHighlightHandle(bd)
for i in xrange(self.GetHandleCount(op)):
bd.SetPen(c4d.GetViewColor(c4d.VIEWCOLOR_ACTIVEPOINT))
info = HandleInfo()
self.GetHandle(op, i, info)
bd.DrawHandle(info.position, c4d.DRAWHANDLE_BIG, 0)
bd.SetMatrix_Matrix(op, bh.GetMg())
return c4d.DRAWRESULT_OK
if __name__ == "__main__":
icon = bitmaps.BaseBitmap()
icon.InitWith(os.path.join(os.path.dirname(__file__), 'res', 'icon.tif'))
plugins.RegisterObjectPlugin(PLUGIN_ID, 'Super Object', Osobject, 'osobject', c4d.OBJECT_GENERATOR, icon)
And here the full example files: osobject.zip
Thanks
hi @m_magalhaes,
Thank you much, that works.
I have an another question concerning Description Resource
, I don't know if I post it here or I must create a new post.
Here is the code:
GROUP
{
REAL MYOBJECT_PITCH { UNIT DEGREE; CUSTOMGUI REALSLIDER; MIN -360; MAX 360; STEP 1; }
BITMAPBUTTON MYOBJECT_STRAIGHTEST_PITCH
{
SIZE 16;
BUTTON;
ALIGN_LEFT;
ICONID1 5160;
}
REAL MYOBJECT_ROTATION { UNIT DEGREE; CUSTOMGUI REALSLIDER; MINSLIDER -90; MAXSLIDER 270; STEP 1; }
STATICTEXT { JOINENDSCALE; }
}
And here screenshot of what I want to do:
Thanks
Hello,
Is it possible to create a toggle bitmap button using only the .res
file?
I tried with the GetDDescription
function its work well, but when I use .res
file this doesn't work.
Also, when I use the FORCE_SIZE
on .res
file I get an error.
BITMAPBUTTON BUTTON_ID
{
TOGGLE;
BUTTON;
FORCE_SIZE 16;
ICONID1 5160;
ICONID2 5159;
}
Thanks
Hello,
I'm searching how to rename the default tab ID_OBJECTPROPERTIES
"Object", I have created a new GROUP instead of it, this is works.
But the problem now is: when I open or my plugin object the "Basic" tab is selected by default but I want to set the new GROUP as the default selected tab.
Thanks.