More Examples for GeUserArea?



  • Hi,

    Are there other examples of the GeUserArea?
    In threads on the forum, the MemoryViewer example is often referred. But it's far off from my usage.
    The API documentation doesn't also present a quickstart example.

    Like the previous threads, I am aiming for

    1. Transparent background. I was thinking of adding background to the GeUserArea but a .PNG transparent one.
    2. Just a column of modified buttons with corresponding commands

    As confirmed here, transparency is not possible with GeDialog.
    As confirmed here, its not possible to format/highlight buttons with GeDialog

    Ultimately, I wanted something like in Blender functionality of pop-up as you can see here:
    https://www.dropbox.com/s/8dhs505yz34lap3/c4d140_modified_popup_menu.mp4?dl=0

    Its my understanding that I could achieve this with GeUserArea.

    Is this correct? If so, how?

    Thank you for looking at my problem.



  • Hi,

    a GeUserArea is just a custom GeDialog gadget. And no, you cannot achieve alpha blending with a GeUserArea, as the underlying dialog will always be opaque. At least not alpha blending in the way you want it. You can totally draw an item with a non-rectangular shape, but you cannot have other elements shine through your gadget like shown in your video. You also cannot and stack dialog gadgets within a dialog (draw one on top of another).

    If you want some sort of semi-transparent menu in an editor view, which is letting the scene geometry shine through, your only option is to draw directly into a BaseDraw. But I am not really sure if you could achieve there something like in your video. You would definitely have to prerender some stuff into bitmaps and then blend these bitmaps.

    What are the parts of GeUserArea you don`t understand \ need an example for? I could provide some code, but mine is probably not better than what Maxon offers.

    Cheers
    zipit



  • Hi @bentraje there is no way at the moment to make a transparency background. But I've forward the idea to our development team.

    Regarding List of icon and command, you can do it with a GeDialog, a group with multiple columns of BitmapButtonCustomGui.

    Regarding more examples of GeUserArea: Take a look in the GitHub repository, especially geuserarea_basic_r13.py.

    But if you have an idea about example, feel free to create an issue with the IDEA tag or even to contribute.

    Cheers,
    Maxime.



  • @zipit
    Thanks for the answer

    RE: a GeUserArea is just a custom GeDialog gadget.
    Thanks for the clarification. I thought they were separated based on the documentation.

    RE: What are the parts of GeUserArea you dont understand \ need an example for?
    It's just a list of text with their commands (i.e. they are clickable).
    In the GeDialog, there is AddButton but there seems to be no equivalent with GeUserArea.

    @m_adam
    Thanks for the answer.

    RE: cinema4d_py_sdk_extended
    Is this new? Huh. Indeed there is some GeUserArea examples. I was looking at the cinema4d_py_sdk because I got it bookmarked but it seems like this is already old (deprecated).

    RE: GuiDialog
    Yes, I was able to create one with the help of the forum. But I was having a problem with the highlighting when you hover. And the "button" looking like buttons. I just want them to be a text that I can click.
    You can see an illustration here:
    https://www.dropbox.com/s/dvk3mghn2s2ox8t/c4d141_highlight_hovered_button.mp4?dl=0

    RE: BitmapButtonCustomGui.
    Thanks for pointing it out. But may I ask how do I implement it?
    I couldn't see an example in the scripts directory.

    The closest reference I have in the forum is this thread
    But its only a snippet. I'm not sure how to add it to the actual code.



  • Hi,

    you have to invoke AddUserArea and then attach an instance of your implemented type to it. Something like this:

    my_user_area = MyUserAreaType()
    self.AddUserArea(1000,*other_arguments)
    self.AttachUserArea(my_user_area, 1000)
    

    I have attached an example which does some things you are trying to do (rows of things, highlighting stuff, etc.). The gadget is meant to display a list of boolean values and the code is over five years old. I had a rather funny idea of what good Python should look like then and my attempts of documentation were also rather questionable. I just wrapped the gadget into a quick example dialog you could run as a script. I did not maintain the code, so there might be newer and better ways to do things now.

    Also a warning: GUI stuff is usually a lot of work and very little reward IMHO.

    Cheers
    zipit

    import c4d
    import math
    import random
    
    from c4d import gui
    
    # Pattern Gadget
    IDC_SELECTLOOP_CELLSIZE = [32, 32]
    IDC_SELECTLOOP_GADGET_MINW = 400
    IDC_SELECTLOOP_GADGET_MINH = 32
    
    class ExampleDialog(gui.GeDialog):
        """
        """
        def CreateLayout(self):
            """
            """
    
            self.Pattern = c4d.BaseContainer()
            for i in range(10):
                self.Pattern[i] = random.choice([True, False])
            self.PatternSize = len(self.Pattern)
    
            self.gadget = Patterngadget(host=self)
            self.AddUserArea(1000, c4d.BFH_FIT, 400, 32)
            self.AttachUserArea(self.gadget, 1000)
            return True
    
    class Patterngadget(gui.GeUserArea):
        """
        A gui gadget to modify and display boolean patterns.
        """
    
        def __init__(self, host):
            """
            :param host: The hosting BaseToolData instance
            """
            self.Host = host
            self.BorderWidth = None
            self.CellPerColumn = None
            self.CellWidht = IDC_SELECTLOOP_CELLSIZE[0]
            self.CellHeight = IDC_SELECTLOOP_CELLSIZE[1]
            self.Columns = None
            self.Height = None
            self.Width = None
            self.MinHeight = IDC_SELECTLOOP_GADGET_MINH
            self.MinWidht = IDC_SELECTLOOP_GADGET_MINW
            self.MouseX = None
            self.MouseY = None
    
        """------------------------------------------------------------------------
            Overridden methods
            --------------------------------------------------------------------"""
    
        def Init(self):
            """
            Init the gadget.
            :return : Bool
            """
            self._get_colors()
            return True
    
        def GetMinSize(self):
            """
            Resize the gadget
            :return : int, int
            """
            return int(self.MinWidht), int(self.MinHeight)
    
        def Sized(self, w, h):
            """
            Get the gadgets height and width
            """
            self.Height, self.Width = int(h), int(w)
            self._fit_gadget()
    
        def Message(self, msg, result):
            """
            Fetch and store mouse over events
            :return : bool
            """
            if msg.GetId() == c4d.BFM_GETCURSORINFO:
                base = self.Local2Screen()
                if base:
                    self.MouseX = msg.GetLong(c4d.BFM_DRAG_SCREENX) - base['x']
                    self.MouseY = msg.GetLong(c4d.BFM_DRAG_SCREENY) - base['y']
                    self.Redraw()
                    self.SetTimer(1000)
            return gui.GeUserArea.Message(self, msg, result)
    
        def InputEvent(self, msg):
            """
            Fetch and store mouse clicks
            :return : bool
            """
            if not isinstance(msg, c4d.BaseContainer):
                return True
            if msg.GetLong(c4d.BFM_INPUT_DEVICE) == c4d.BFM_INPUT_MOUSE:
                if msg.GetLong(c4d.BFM_INPUT_CHANNEL) == c4d.BFM_INPUT_MOUSELEFT:
                    base = self.Local2Global()
                    if base:
                        x = msg.GetLong(c4d.BFM_INPUT_X) - base['x']
                        y = msg.GetLong(c4d.BFM_INPUT_Y) - base['y']
                        pid = self._get_id(x, y)
                        if pid <= self.Host.PatternSize:
                            self.Host.Pattern[pid] = not self.Host.Pattern[pid]
                            self.Redraw()
            return True
    
        def Timer(self, msg):
            """
            Timer loop to catch OnMouseExit
            """
            base = self.Local2Global()
            bc = c4d.BaseContainer()
            res = gui.GetInputState(c4d.BFM_INPUT_MOUSE,
                                    c4d.BFM_INPUT_MOUSELEFT, bc)
            mx = bc.GetLong(c4d.BFM_INPUT_X) - base['x']
            my = bc.GetLong(c4d.BFM_INPUT_Y) - base['y']
            if res:
                if not (mx >= 0 and mx <= self.Width and
                        my >= 0 and my <= self.Height):
                    self.SetTimer(0)
                    self.Redraw()
    
        def DrawMsg(self, x1, y1, x2, y2, msg):
            """
            Draws the gadget
            """
            # double buffering
            self.OffScreenOn(x1, y1, x2, y2)
            # background & border
            self.DrawSetPen(self.ColBackground)
            self.DrawRectangle(x1, y1, x2, y2)
            if self.BorderWidth:
                self.DrawBorder(c4d.BORDER_THIN_IN, x1, y1,
                                self.BorderWidth + 2, y2 - 1)
            # draw pattern
            for pid, state in self.Host.Pattern:
                x, y = self._get_rect(pid)
                self._draw_cell(x, y, state, self._is_focus(x, y))
    
        """------------------------------------------------------------------------
            Public methods
            --------------------------------------------------------------------"""
    
        def Update(self, cid=None):
            """
            Update the gadget.
            :param cid: A pattern id to toggle.
            """
            if cid and cid < self.Host.PatternSize:
                self.Host.Pattern[cid] = not self.Host.Pattern[cid]
            self._fit_gadget()
            self.Redraw()
    
        """------------------------------------------------------------------------
            Private methods
            --------------------------------------------------------------------"""
    
        def _get_colors(self, force=False):
            """
            Set the drawing colors.
            :return : Bool
            """
            self.ColScale = 1.0 / 255.0
            if self.IsEnabled() or force:
                self.ColBackground = self._get_color_vector(c4d.COLOR_BG)
                self.ColCellActive = c4d.GetViewColor(
                    c4d.VIEWCOLOR_ACTIVEPOINT) * 0.9
                self.ColCellFocus = self._get_color_vector(c4d.COLOR_BGFOCUS)
                self.ColCellInactive = self._get_color_vector(c4d.COLOR_BGEDIT)
                self.ColEdgeDark = self._get_color_vector(c4d.COLOR_EDGEDK)
                self.ColEdgeLight = self._get_color_vector(c4d.COLOR_EDGELT)
            else:
                self.ColBackground = self._get_color_vector(c4d.COLOR_BG)
                self.ColCellActive = self._get_color_vector(c4d.COLOR_BG)
                self.ColCellFocus = self._get_color_vector(c4d.COLOR_BG)
                self.ColCellInactive = self._get_color_vector(c4d.COLOR_BG)
                self.ColEdgeDark = self._get_color_vector(c4d.COLOR_EDGEDK)
                self.ColEdgeLight = self._get_color_vector(c4d.COLOR_EDGELT)
            return True
    
        def _get_cell_pen(self, state, _is_focus):
            """
            Get the color for cell depending on its state.
            :param state   : The state
            :param _is_focus : If the cell is hoovered.
            :return        : c4d.Vector()
            """
            if state:
                pen = self.ColCellActive
            else:
                pen = self.ColCellInactive
            if self.IsEnabled() and _is_focus:
                return (pen + c4d.Vector(2)) * 1/3
            else:
                return pen
    
        def _draw_cell(self, x, y, state, _is_focus):
            """
            Draws a gadget cell.
            :param x:       local x
            :param y:       local y
            :param state:   On/Off
            :param _is_focus: MouseOver state
            """
            # left and top bright border
            self.DrawSetPen(self.ColEdgeLight)
            self.DrawLine(x, y, x + self.CellWidht, y)
            self.DrawLine(x, y, x, y + self.CellHeight)
            # bottom and right dark border
            self.DrawSetPen(self.ColEdgeDark)
            self.DrawLine(x, y + self.CellHeight - 1, x +
                          self.CellWidht - 1, y + self.CellHeight - 1)
            self.DrawLine(x + self.CellWidht - 1, y, x +
                          self.CellWidht - 1, y + self.CellHeight - 1)
            # cell content
            self.DrawSetPen(self._get_cell_pen(state, _is_focus))
            self.DrawRectangle(x + 1, y + 1, x + self.CellWidht -
                               2, y + self.CellHeight - 2)
    
        def _get_rect(self, pid, offset=1):
            """
            Get the drawing rect for an array id.
            :param pid    : the pattern id
            :param offset : the pixel border offset
            :return       : int, int
            """
            pid = int(pid)
            col = pid / self.CellPerColumn
            head = pid % self.CellPerColumn
            return self.CellWidht * head + offset, self.CellHeight * col + offset
    
        def _get_id(self, x, y):
            """
            Get the array id for a coord within the gadget.
            :param x : local x
            :param y : local y
            :return  : int
            """
            col = (y - 1) / self.CellHeight
            head = (x - 1) / self.CellWidht
            return col * self.CellPerColumn + head
    
        def _is_focus(self, x, y):
            """
            Test if the cell coords are under the cursor.
            :param x : local x
            :param y : local y
            :return  : bool
            """
            if (self.MouseX >= x and self.MouseX <= x + self.CellWidht and
                    self.MouseY >= y and self.MouseY <= y + self.CellHeight):
                self.MouseX = c4d.NOTOK
                self.MouseY = c4d.NOTOK
                return True
            else:
                return False
    
        def _fit_gadget(self):
            """
            Fit the gadget size to the the array
            """
            oldHeight = self.MinHeight
            self.CellPerColumn = int((self.Width - 2) / self.CellWidht)
            self.Columns = math.ceil(
                self.Host.PatternSize / self.CellPerColumn) + 1
            self.MinHeight = int(IDC_SELECTLOOP_GADGET_MINH * self.Columns) + 3
            self.MinWidht = int(IDC_SELECTLOOP_GADGET_MINW)
            self.BorderWidth = self.CellWidht * self.CellPerColumn
            if oldHeight != self.MinHeight:
                self.LayoutChanged()
    
        def _get_color_vector(self, cid):
            """
            Get a color vector from a color ID.
            :param cid : The color ID
            :return    : c4d.Vector()
            """
            dic = self.GetColorRGB(cid)
            if dic:
                return c4d.Vector(float(dic['r']) * self.ColScale,
                                  float(dic['g']) * self.ColScale,
                                  float(dic['b']) * self.ColScale)
            else:
                return c4d.Vector()
    
    if __name__ == "__main__":
        dlg = ExampleDialog()
        dlg.Open(c4d.DLG_TYPE_ASYNC, defaultw=400, defaulth=400)
    


  • Hi @zipit

    Apologies for the late response.
    Your code sample looks interesting. And yep, close to what I was looking for. Stacking columns of clickable images/text
    I keep looking back at it every now and then but I have to admit, it's beyond me at the moment really beyond me at the moment.

    Maybe I was just a bit spoiled with other GUI interface such as tkinter or PyQT, where it is relatively easy to implement.
    Just copy some code and modify some parts. Or maybe there are just more available examples in tkinter and PyQT that fits my need.

    Will get back to this topic when I have the necessary skillset.
    Sorry for the trouble and thanks again.


Log in to reply