Marquee Selection of Items in GeUserArea
-
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.
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)
-
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 -
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.htmlI realise by now that i'm asking for feedback. Mes plus plates excuses.
️
-
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 -
@ferdinand Got it. Thanks you for the tips. Much appreciated