Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Marquee Selection of Items in GeUserArea

    Cinema 4D SDK
    windows python 2025
    2
    5
    792
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • N
      noisyrender
      last edited by

      Hi everyone,

      I'm currently working on a custom UI using GeUserArea in Python, and I'd like to implement rectangle selection (by clicking and dragging a selection rectangle over multiple items, similar to how it works in the Object Manager ). A good example is the Xpresso Editor or Kengo Ito's XBarista

      I saw a similar function in the C++ doc and i'm wondering if it is possible in python out the box.
      97855097-ffd1-4d4d-a697-6e89c4fa0ae1-image.png
      I'm fairly new to the api.

      Here is a snippet for a larger project.

      THANK YOU

      import c4d
      
      
      NODE_COLOR = c4d.Vector(c4d.COLOR_MENU_BG_SELECTED)
      
      
      class Node:
          def __init__(self, x, y, ua):
              self.x: int = x
              self.y: int = y
              self.width: int = 80
              self.height: int = 80
              self.is_selected: bool = False
              self.col: c4d.Vector = c4d.Vector(0.6, 0.3, 0.5)
              self.is_selected_col: c4d.Vector = c4d.Vector()
              self.ua = ua
      
          def draw_node(self):
              if self.is_selected:
                  self.ua.DrawSetPen(NODE_COLOR)  # node color when selected
              else:
                  self.ua.DrawSetPen(self.col)  # else the defaault color
      
              x1, y1 = self.x, self.y
              x2, y2 = self.x + self.width, self.y + self.height
      
              self.ua.DrawRectangle(int(x1), int(y1), int(x2), int(y2))
      
          def offset(self, x, y):
              self.x -= x
              self.y -= y
      
          def select(self):
              self.is_selected = True
      
          def deselect(self):
              self.is_selected = False
      
          def is_inside(self, mx, my):  # calculate the area of the node
              return (
                  self.x <= mx <= self.x + self.width and self.y <= my <= self.y + self.height
              )
      
      
      class Gui(c4d.gui.GeDialog):
          ID_AREA = 10001
      
          def __init__(self):
              super().__init__()
      
              self.SetTitle("Marquee Selection Example")
      
              self.area = Area()
      
          def CreateLayout(self):
              self.AddUserArea(self.ID_AREA, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT)
              self.AttachUserArea(self.area, self.ID_AREA)
      
              return True
      
      
      class Area(c4d.gui.GeUserArea):
          def __init__(self):
              self.nodes = [
                  Node(250, 250, self),
                  Node(250, 150, self),
              ]  # the two nodes to marque select
      
              self.lasso = False  # start drawing the maarque when true
      
          # This create the actual custom marquee to draw
          def drawLasso(self, start_x, start_y, end_x, end_y):
              self.DrawSetPen(c4d.Vector(0.8, 0.8, 0.18))
              self.DrawFrame(start_x, start_y, end_x, end_y, 1, c4d.LINESTYLE_NORMAL)
      
          def DrawMsg(self, x1, y1, x2, y2, msg):
              self.OffScreenOn()
      
              self.DrawSetPen(c4d.Vector(0.12, 0.12, 0.14))
              self.DrawRectangle(x1, y1, x2, y2)
      
              for node in self.nodes:  # draw all nodes
                  node.draw_node()
      
              if self.lasso:
                  self.drawLasso(
                      self.lasso_start_x,
                      self.lasso_start_y,
                      self.lasso_end_x,
                      self.lasso_end_y,
                  )
      
          def InputEvent(self, msg):  # noqa: C901
              if msg[c4d.BFM_INPUT_DEVICE] == c4d.BFM_INPUT_MOUSE:
                  mx = int(msg[c4d.BFM_INPUT_X])
                  my = int(msg[c4d.BFM_INPUT_Y])
                  mx -= self.Local2Global()["x"]
                  my -= self.Local2Global()["y"]
      
                  channel = msg[c4d.BFM_INPUT_CHANNEL]
      
                  self.lasso_start_x = int(
                      mx
                  )  # Mouse positions at the moment we start drawing
                  self.lasso_start_y = int(my)
      
                  if channel == c4d.BFM_INPUT_MOUSELEFT:
                      self.MouseDragStart(
                          c4d.BFM_INPUT_MOUSELEFT,
                          mx,
                          my,
                          c4d.MOUSEDRAGFLAGS_DONTHIDEMOUSE,
                      )
      
                      while True:
                          res, dx, dy, channels = self.MouseDrag()
                          if res == c4d.MOUSEDRAGRESULT_FINISHED:
                              self.lasso = False
                              self.Redraw()
                              break  # We always need this to escape an infinite loop
                          elif res == c4d.MOUSEDRAGRESULT_ESCAPE:
                              break
                          elif res == c4d.MOUSEDRAGRESULT_CONTINUE:
                              self.lasso = True
                              c4d.gui.SetMousePointer(c4d.MOUSE_PAINTSELECTRECT)
      
                              mx -= dx
                              my -= dy
      
                              self.lasso_end_x = int(mx)  # Mouse positions when stopped
                              self.lasso_end_y = int(my)
                              self.Redraw()
      
                      self.MouseDragEnd()
      
                      for node in self.nodes:
                          if node.is_inside(mx, my):
                              node.select()
                              self.Redraw()
                          elif not node.is_inside(mx, my) and c4d.BFM_INPUT_MOUSELEFT:
                              node.deselect()
                              self.Redraw()
      
              return True
      
      
      if __name__ == "__main__":
          Gui().Open(2, 1, xpos=-2, ypos=-2, defaulth=600, defaultw=600)
      
      
      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @noisyrender
        last edited by ferdinand

        Hello @noisyrender,

        Welcome to the Maxon developers forum and its community, it is great to have you with us!

        Getting Started

        Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.

        • Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
        • Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
        • Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.

        It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: How to Ask Questions.

        About your First Question

        Your first post does not contain any questions, so I struggle with understanding the goal of your topic. For me, your code runs fine. Are you asking how to detect which node has been box selected? That would be a strange question to ask when you wrote all that other code yourself, because you have all the data right there?

        I am bit confused 😅 .

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • N
          noisyrender
          last edited by

          Sorry about the confusion 🫠.

          Being not very familiar to the API and doing this by reading example code i was wondering if there is a similar lasso selection in python to the c++
          https://developers.maxon.net/docs/cpp/2025_2_0a/classcinema_1_1_lasso_selection.html

          I realise by now that i'm asking for feedback. Mes plus plates excuses. 🙇🏽‍♂️

          ferdinandF 1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand @noisyrender
            last edited by ferdinand

            Hey @noisyrender,

            I am still not quite sure what you are asking for. When the question is if there is something like LassoSelection in Python, the answer is no.

            When you want to implement a lasso selection for your node editor, the first question would be if you want a polygon lasso, i.e., the user clicks multiple times to generate a rough shape made out of line segments. Or if your want a continuous shape lasso. I.e., the user 'draws' in a continuous mouse stroke a shape and you then probably have to clean up these points and then draw them as a spline.

            We cannot provide much support here on implementing such common patterns, we provide support for our APIs, but not for general computer science and math concepts, see our support procedures for details.

            There is for once GeUserArea.DrawBezier which allows you to draw Bezier curves, which you could use when you have constructed a smooth curve you want to draw. You could of course also just quantize your curve yourself and draw it with line segments. And then you have to carry out the hit-detection for your polygon and node. For that you would have to solve Point in Polygon problem, where the points are the vertices of your node(s), and the polygon is the quantized lasso shape.

            I usually help people when it makes sense, but I really cannot guide you all the way through a lasso implementation. When you want to do it, just get started, and when you have a concrete API problem, come back, we will help you. But the underlying abstract algorithmic problems you will have to largely solve yourself. I personally would avoid implementing this when not absolute necessary. For a polygon-lasso, you will have to record the clicks, and then likely implement 2D-raycasting for that shape to implement the even-odd-algorithm. Doable but some work. Implementing a smooth lasso that filters mouse jitter away, i.e., produces a smooth curve, and then also do the hit detection, is even more work.

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            N 1 Reply Last reply Reply Quote 0
            • N
              noisyrender @ferdinand
              last edited by

              @ferdinand Got it. Thanks you for the tips. Much appreciated

              1 Reply Last reply Reply Quote 0
              • First post
                Last post