Solved 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

MAXON SDK Specialist
developers.maxon.net

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)

MAXON SDK Specialist
developers.maxon.net

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.