Collapsing/Foldable Groups in Python?

On 17/11/2013 at 18:53, xxxxxxxx wrote:

I seem to remember being told this wasn't possible in Python, but now I'm wanting to double check this.

When doing User Data, if you put something in a new group, that group can be foldable/collapsable.

Is there any way to do this in a GeDialog in Python?

To see what I mean create a cube, look in the Coords tab. The Freeze Transformation can be collapses. I'm looking to be able to do that in my GeDialog plugin.

On 18/11/2013 at 04:05, xxxxxxxx wrote:

Hi Bret,

unfortunately the groupflag which makes the group foldable is broken. It is however possible by using a
button/userarea widget and the GeDialog.HideElement() function.

Here's a quick example:

 

>
> import c4d
> import weakref
>
> class FoldUa(c4d.gui.GeUserArea) :
>
> def __init__(self, dlg, wid, parent_id, state_1=None, state_2=None) :
> super(FoldUa, self).__init__()
> self.dlg = weakref.ref(dlg)
> self.wid = wid
> self.parent_id = parent_id
> self.hidden = False
>
> if not state_1: state_1 = c4d.RESOURCEIMAGE_PLUS
> if not state_2: state_2 = c4d.RESOURCEIMAGE_SUBGROUP
> self.state_1 = state_1
> self.state_2 = state_2
> print self.state_1, self.state_2
>
> def Toggle(self, notify=True) :
> self.hidden = not self.hidden
> self.SetState(self.hidden, notify)
>
> def SetState(self, hidden, notify=True) :
> self.hidden = bool(hidden)
> self.dlg().HideElement(self.wid, self.hidden)
> if notify:
> self.LayoutChanged()
> self.dlg().LayoutChanged(self.parent_id)
>
> def GetStateIcon(self) :
> if self.hidden:
> icon = self.state_1
> else:
> icon = self.state_2
>
> if isinstance(icon, int) :
> icon = c4d.gui.GetIcon(icon)
> elif isinstance(icon, dict) :
> pass
> elif isinstance(icon, c4d.bitmaps.BaseBitmap) :
> w, h = icon.GetSize()
> icon = {'x': 0, 'y': 0, 'w': w, 'h': h, 'bmp': icon}
> elif not icon:
> pass
> else:
> raise TypeError('Expected int, dict or BaseBitmap for state field.')
>
> return icon
>
> def DrawMsg(self, x1, y1, x2, y2, msg) :
> self.DrawSetPen(c4d.COLOR_BG)
> self.DrawRectangle(x1, y1, x2 - 1, y2 - 1)
>
> icon = self.GetStateIcon()
> if not icon: return
> self.DrawBitmap(icon['bmp'], 0, 0, icon['w'], icon['h'],
> icon['x'], icon['y'], icon['w'], icon['h'],
> c4d.BMP_ALLOWALPHA)
>
> def InputEvent(self, msg) :
> device = msg.GetLong(c4d.BFM_INPUT_DEVICE)
> channel = msg.GetLong(c4d.BFM_INPUT_CHANNEL)
> if device == c4d.BFM_INPUT_MOUSE and channel == c4d.BFM_INPUT_MOUSELEFT:
> self.Toggle()
> return True
>
> def GetMinSize(self) :
> icon = self.GetStateIcon()
> if icon:
> return (icon['w'], icon['h'])
> else:
> return (0, 0)
>
> class Dialog(c4d.gui.GeDialog) :
>
> GRP_FOLDABLE = 1000
> EDT_SOMEFIELD = 1001
>
> def __init__(self) :
> super(Dialog, self).__init__()
> self.__foldindex = 10000
> self.__folduas = {}
>
> def GroupBeginFoldable(self, id, flags, *args, **kwargs) :
> self.GroupBegin(self.__foldindex, flags, 0, 1)
> ua = FoldUa(self, id, self.__foldindex)
> self.__foldindex += 1
>
> self.AddUserArea(self.__foldindex, 0)
> self.AttachUserArea(ua, self.__foldindex)
> self.__folduas[self.__foldindex] = ua
> self.__foldindex += 1
>
> return self.GroupBegin(id, flags, *args, **kwargs)
>
> def GroupEndFoldable(self) :
> self.GroupEnd()
> self.GroupEnd()
>
> # c4d.gui.GeDialog
>
> def CreateLayout(self) :
> if self.GroupBeginFoldable(self.GRP_FOLDABLE, c4d.BFH_SCALEFIT, 0, 1) :
> self.AddStaticText(0, 0, name="Field")
> self.AddEditSlider(self.EDT_SOMEFIELD, c4d.BFH_SCALEFIT)
> self.GroupEndFoldable()
>
> self.AddDlgGroup(c4d.DLG_CANCEL)
> return True
>
> def Command(self, wid, msg) :
> if wid == c4d.DLG_CANCEL:
> self.Close()
> return True
>
> dlg = Dialog()
> dlg.Open(c4d.DLG_TYPE_MODAL_RESIZEABLE)

Best,
-Niklas

On 18/11/2013 at 07:30, xxxxxxxx wrote:

Is there a way though to make it so it will display the group name still?

On 18/11/2013 at 11:17, xxxxxxxx wrote:

Requires a bit more hacky approach using a 2-level group call.

import c4d
import weakref
  
class FoldUa(c4d.gui.GeUserArea) :
  
    minsize = (10, 10)
  
    def __init__(self, dlg, wid, parent_id, state_1=None, state_2=None) :
        super(FoldUa, self).__init__()
        self.dlg = weakref.ref(dlg)
        self.wid = wid
        self.parent_id = parent_id
        self.hidden = False
  
        if not state_1: state_1 = c4d.RESOURCEIMAGE_PLUS
        if not state_2: state_2 = c4d.RESOURCEIMAGE_SUBGROUP
        self.state_1 = state_1
        self.state_2 = state_2
  
    def Toggle(self, notify=True) :
        self.hidden = not self.hidden
        self.SetState(self.hidden, notify)
  
    def SetState(self, hidden, notify=True) :
        self.hidden = bool(hidden)
        self.dlg().HideElement(self.wid, self.hidden)
        if notify:
            self.LayoutChanged()
            self.dlg().LayoutChanged(self.parent_id)
  
    def GetStateIcon(self) :
        if self.hidden:
            icon = self.state_1
        else:
            icon = self.state_2
  
        if isinstance(icon, int) :
            icon = c4d.gui.GetIcon(icon)
        elif isinstance(icon, dict) :
            pass
        elif isinstance(icon, c4d.bitmaps.BaseBitmap) :
            w, h = icon.GetSize()
            icon = {'x': 0, 'y': 0, 'w': w, 'h': h, 'bmp': icon}
        elif not icon:
            pass
        else:
            raise TypeError('Expected int, dict or BaseBitmap for state field.')
  
        return icon
  
    def DrawMsg(self, x1, y1, x2, y2, msg) :
        self.DrawSetPen(c4d.COLOR_BGFOCUS)
        self.DrawRectangle(x1, y1, x2 - 1, y2 - 1)
  
        icon = self.GetStateIcon()
        if not icon: return
  
        x = (self.GetWidth() - icon['w']) / 2
        y = (self.GetHeight() - icon['h']) / 2
        self.DrawBitmap(icon['bmp'], x, x, icon['w'], icon['h'],
                        icon['x'], icon['y'], icon['w'], icon['h'],
                        c4d.BMP_ALLOWALPHA)
  
    def InputEvent(self, msg) :
        device = msg.GetLong(c4d.BFM_INPUT_DEVICE)
        channel = msg.GetLong(c4d.BFM_INPUT_CHANNEL)
        if device == c4d.BFM_INPUT_MOUSE and channel == c4d.BFM_INPUT_MOUSELEFT:
            self.Toggle()
        return True
  
    def GetMinSize(self) :
        icon = self.GetStateIcon()
        if icon:
            return tuple(map(max, zip(self.minsize, (icon['w'], icon['h']))))
        else:
            return tuple(self.minsize)
  
class Dialog(c4d.gui.GeDialog) :
  
    GRP_FOLDABLE = 1000
    EDT_SOMEFIELD = 1001
  
    def __init__(self) :
        super(Dialog, self).__init__()
        self.__foldindex = 10000
        self.__folduas = {}
  
    def GroupBeginFoldable(self, id, flags, rows=0, cols=0, title="",
                groupflags=0, initw=0, inith=0) :
        id_ua = self.__foldindex
        id_group = self.__foldindex + 1
        self.__foldindex += 2
  
        self.GroupBegin(id, flags, 2, 0, title, groupflags, initw, inith)
        self.__continue_data = (id, flags, rows, cols, title, groupflags,
                initw, inith, id_ua, id_group)
  
        return True
  
    def GroupContinueFoldable(self) :
        if not self.__continue_data:
            raise RuntimeError('No foldable group opened.')
  
        (id, flags, rows, cols, title, groupflags,
                initw, inith, id_ua, id_group) = self.__continue_data
        self.__continue_data = None
  
        # Column 1: User Area
        ua = FoldUa(self, wid=id_group, parent_id=id)
        self.__folduas[id_ua] = ua
        self.AddUserArea(id_ua, 0)
        self.AttachUserArea(ua, id_ua)
  
        # Column 2: The inner group.
        self.GroupBegin(id_group, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT,
                rows, cols, title, groupflags, initw, inith)
  
        return True
  
    def GroupEndFoldable(self) :
        self.GroupEnd()
        self.GroupEnd()
  
    # c4d.gui.GeDialog
  
    def CreateLayout(self) :
        if self.GroupBeginFoldable(self.GRP_FOLDABLE, c4d.BFH_SCALEFIT, 0, 1,
                                   title="Foldable Group") :
            self.GroupBorder(c4d.BORDER_GROUP_TOP | c4d.BORDER_WITH_TITLE)
            self.GroupContinueFoldable()
  
            self.AddStaticText(0, 0, name="Field")
            self.AddEditSlider(self.EDT_SOMEFIELD, c4d.BFH_SCALEFIT)
            self.GroupEndFoldable()
  
        self.AddDlgGroup(c4d.DLG_CANCEL)
        return True
  
    def Command(self, wid, msg) :
        if wid == c4d.DLG_CANCEL:
            self.Close()
        return True
  
dlg = Dialog()
dlg.Open(c4d.DLG_TYPE_MODAL_RESIZEABLE)

On 19/11/2013 at 02:20, xxxxxxxx wrote:

I think one thing worth mentioning in that context is also, that GeDialog.LoadDialogRessource 
does not overwrite the dialogs ressources, but appends the IDs content to the dialog.