Get the Button GadgetID Directly Under the Mouse?



  • Hi,

    Is there a command to get the Button Gadget ID directly under the mouse?
    I understand that you can get the GadgetID by clicking the button but I'm using the Message() rather than Command().

    In overview, I have several buttons and when you RMB them, they have there own dedicated pop-up commands.
    It works when I have them typed separately but I'm having a problem with making them dynamic through a class.

    You can see the illustration here:
    https://www.dropbox.com/s/k5d38cctk4hgtz7/c4d206_get_gadgetid_under_mouse.jpg?dl=0

    You can check the WIP code here:

    import c4d
    
    class ColorButton(object):
    
        def __init__(self):
            self.width = None
            self.height = None
            self.color = None
            self.color = None
            self.btn_id = None
            self.menu_list = None
    
    
        def create(self, dlg, w, h, color, btn_id):
    
            self.width = w
            self.height = h
            self.color = color
            self.btn_id = btn_id
    
            bmp_color = c4d.bitmaps.BaseBitmap()
            bmp_color.Init(w, h)
    
            for y in xrange(w):
                for x in xrange(h):
                    bmp_color.SetPixel(x, y, color[0], color[1], color[2])
    
            bcBitmapButton = c4d.BaseContainer()
            bcBitmapButton[c4d.BITMAPBUTTON_BUTTON] = True
    
            bmp_btn = dlg.AddCustomGui(self.btn_id, c4d.CUSTOMGUI_BITMAPBUTTON, "", c4d.BFH_CENTER | c4d.BFV_CENTER, w, h, bcBitmapButton)
    
            bmp_btn.SetImage(bmp_color, True)
    
        def menu(self):
    
            self.menu = c4d.BaseContainer()
    
            for menu_item in self.menu_list: 
                counter = 0
                IDM_MENU = c4d.FIRST_POPUP_ID + counter
                self.menu.InsData(IDM_MENU, menu_item)
    
                counter += 1
    
    class MyDialog(c4d.gui.GeDialog):
    
        def __init__(self):
            self.btn_id_list = []
            self.class_btn_id_dict = {}
    
        def CreateLayout(self):
    
            red_button = ColorButton()
            red_button.create(self, w=50,h=50,color=(255,0,0), btn_id=6000)
            red_button.menu_list = ['Menu1', 'Menu2', 'Menu3']
            self.btn_id_list.append(red_button.btn_id)
            self.class_btn_id_dict[6000] = red_button
    
            blue_button = ColorButton()
            blue_button.create(self, w=50,h=50,color=(0,0,255), btn_id=7000)
            blue_button.menu_list = ['Menu4', 'Menu5', 'Menu6', 'Menu7']
            self.btn_id_list.append(blue_button.btn_id)
            self.class_btn_id_dict[7000] = blue_button
    
    
            green_button = ColorButton()
            green_button.create(self, w=50,h=50,color=(0,0,255), btn_id=7000)
            green_button.menu_list = ['Menu8', 'Menu9']
            self.btn_id_list.append(blue_button.btn_id)
            self.class_btn_id_dict[8000] = green_button
    
            return True
    
        def IsPositionOnGadget(self, gadgetId, x, y):
            # Be sure that the windows is opened,
            # in our case since we call it in BFM_INTERACTSTART it's ok
            buttonData = self.GetItemDim(gadgetId)
            
            if not buttonData["x"] < x < buttonData["x"] + buttonData["w"]:
                return False
    
            if not buttonData["y"] < y < buttonData["y"] + buttonData["h"]:
                return False
    
            return True
        
        def Message(self, msg, result):
            if msg.GetId() == c4d.BFM_ADJUSTSIZE:
              self._x = msg[3] # Retrieve Y size of the GeDialog
              self._y = msg[4] # Retrieve Y size of the GeDialog
    
            # We are on the main thread here
            elif msg.GetId() == c4d.BFM_INTERACTSTART:
                c4d.StopAllThreads()
    
                state = c4d.BaseContainer()
                self.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSERIGHT, state)
    
                if state.GetInt32(c4d.BFM_INPUT_VALUE) == True:
    
                    x = state.GetInt32(c4d.BFM_INPUT_X)
                    y = state.GetInt32(c4d.BFM_INPUT_Y)
                    g2l  = self.Global2Local() 
                    x += g2l['x']  
                    y += g2l['y']
    
                    gadgetId = function_to_determine_gadgetId_under_mouse_cursor()
    
                    if gadgetId in self.btn_id_list:
    
                         if self.IsPositionOnGadget(gadgetId=gadgetId, x=x, y=y):
    
                              button_class = self.class_btn_id_dict[gadgetId] 
                              button_class.menu()
    
    
                              l2s = self.Local2Screen()
                              print str(x+l2s['x']) + " :: " + str(y+l2s['y'])
                              self.KillEvents()
                              res = c4d.gui.ShowPopupDialog(cd=self, bc=menu, x=x+l2s['x'], y=y+l2s['y'])
                              return True
    
            return c4d.gui.GeDialog.Message(self, msg, result)
    
            return True
    if __name__ == "__main__":
        dlg = MyDialog()
        dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=20304050)
    


  • Hi,

    I think you you mean the gadget under the mouse, do you?



  • Oh sorry for the confusion. Yes, I meant under the mouse. I was thinking of hover.



  • Hi @bentraje there is no builtin way for doing it, you will have to tests each Gadget to determine which one match with the function posted in Right Click Contextual Menu on a Button?.

    Final note you can retrieve the current mouse position in the message function with

            if msg.GetId() == c4d.BFM_GETCURSORINFO:
                x = msg.GetInt32(c4d.BFM_DRAG_SCREENX)
                y = msg.GetInt32(c4d.BFM_DRAG_SCREENY)
    

    Cheers,
    Maxime.



  • @m_adam

    Thanks for the response. Apologies for the late response. I was trying to figure out this one: you will have to tests each Gadget

    I tried a for loop but the Message function does not seem to work with it.

    Is there a way around this?

    You can check the illustration here:
    https://www.dropbox.com/s/inizedq3rt3660f/c4d212_dynamic_rmb_context_menu.mp4?dl=0

    You can check the sample file here:
    https://www.dropbox.com/s/hvh759wyjfiw7o7/c4d212_dynamic_rmb_context_menu.rar?dl=0



  • Its seems that GetItemDim is not properly supported by baseCustomGui ( I've to check if its a Python issue or not)
    And BaseCustomGui.GetWidth seems to also not work correctly (same things I have to check if it's a Python issue).

    So, unfortunately, I guess in your case, the best way is to either replace the CustomGui by a GeUserArea or store the gadgetId with its coordinate + size information during the creation.

    Cheers,
    Maxime.



  • @m_adam

    Thanks for the response. I'm trying the later solution since I'm not that familiar with the GeUserArea

    RE: store the gadgetId with its coordinate + size information during the creation.
    By this, I assume you mean moving the GeDialog.GetItemDim(id) to the CreateLayout function.

    If so, I did with this file but it has the same result as above. The dialog does not pop up and also no error.
    https://www.dropbox.com/s/slyc9irbyfjj6u7/c4d212_dynamic_rmb_context_menu02.rar?dl=0



  • @bentraje said in Get the Button GadgetID Directly Under the Mouse?:

    RE: store the gadgetId with its coordinate + size information during the creation.
    By this, I assume you mean moving the GeDialog.GetItemDim(id) to the CreateLayout function.

    @m_adam said in Get the Button GadgetID Directly Under the Mouse?:

    Its seems that GetItemDim is not properly supported by baseCustomGui ( I've to check if its a Python issue or not)

    No, but since you input a size/height to draw the bitmapbutton and you store theses information in self.width and self.height you can already do the checking yourself.



  • @m_adam

    I'm sorry but I'm having a hard time following.
    Isn't the self.width and self.height a bit irrelevant since it doesn't contain the coordinates of the button relative to the whole dialog? Or are you referring to a different thing.

    RE:you can already do the checking yourself.
    I believe this satisfies the checking
    if self.IsPositionOnGadget(gadgetId=button_id, buttonData = self.button_data_list[idx], buttonx=x, y=y):

    It works on a standalone but it doesn't with a for loop



  • First of all sorry for the confusion I forget GetItemDim should be called within BFM_INTERACTSTART so that's why I didn't success previously to get it working.

    Here is the method function_to_determine_gadgetId_under_mouse_cursor.

        def function_to_determine_gadgetId_under_mouse_cursor(self, x, y):
            for gadgetId in self.btn_id_list:
                if self.IsPositionOnGadget(gadgetId, x, y):
                    return gadgetId
    

    Now there are several issues in your code, and while I understand you may be a beginner remember we are not here to debug your code or even develop for you so please next time try to double-check your code.

    @bentraje said in Get the Button GadgetID Directly Under the Mouse?:

      def menu(self):
    
          self.menu = c4d.BaseContainer()
    
          for menu_item in self.menu_list:
              counter = 0
              IDM_MENU = c4d.FIRST_POPUP_ID + counter
              self.menu.InsData(IDM_MENU, menu_item)
    
              counter += 1
    

    Call this method another name than a member variable name, otherwise, Python has no way to know which one is the good one (or I would say, as soon as you do self.menu = Something you override the value).
    Keep in mind in Python everything is an object (variable, function everything) and everything can be overridden (almost). So please rename this function or your variable.

    @bentraje said in Get the Button GadgetID Directly Under the Mouse?:

        def CreateLayout(self):
            green_button = ColorButton()
            green_button.create(self, w=50,h=50,color=(0,0,255), btn_id=7000)
            green_button.menu_list = ['Menu8', 'Menu9']
            self.btn_id_list.append(blue_button.btn_id)
            self.class_btn_id_dict[8000] = green_button
    

    You passed wrong instance and an already used Id.

    green_button = ColorButton()
    green_button.create(self, w=50, h=50, color=(0, 0, 255), btn_id=7000)
    green_button.menu_list = ['Menu8', 'Menu9']
    self.btn_id_list.append(green_button.btn_id)
    self.class_btn_id_dict[8000] = green_button
    

    @bentraje said in Get the Button GadgetID Directly Under the Mouse?:

            res = c4d.gui.ShowPopupDialog(cd=self, bc=menu, x=x+l2s['x'], y=y+l2s['y'])
    

    This should be bc=button_class.menu.

    Cheers,
    Maxime.



  • Hi @m_adam

    Thanks for the patience. Your suggestions and reminders works are greatly appreciated.

    I guess the confusion stems mainly on my part because I posted slightly two different codes. You were responding to my initial post but I was thinking with the code from the succeeding post(the one in the rar file). Totally my bad.

    Anyhow, here is the working code (using the initial post) which works as I expected:

    import c4d
    
    class ColorButton(object):
    
        def __init__(self):
            self.width = None
            self.height = None
            self.color = None
            self.color = None
            self.btn_id = None
            self.menu_list = None
    
    
        def create(self, dlg, w, h, color, btn_id):
    
            self.width = w
            self.height = h
            self.color = color
            self.btn_id = btn_id
    
            bmp_color = c4d.bitmaps.BaseBitmap()
            bmp_color.Init(w, h)
    
            for y in xrange(w):
                for x in xrange(h):
                    bmp_color.SetPixel(x, y, color[0], color[1], color[2])
    
            bcBitmapButton = c4d.BaseContainer()
            bcBitmapButton[c4d.BITMAPBUTTON_BUTTON] = True
    
            bmp_btn = dlg.AddCustomGui(self.btn_id, c4d.CUSTOMGUI_BITMAPBUTTON, "", c4d.BFH_CENTER | c4d.BFV_CENTER, w, h, bcBitmapButton)
    
            bmp_btn.SetImage(bmp_color, True)
    
        def create_menu(self):
    
            self.menu = c4d.BaseContainer()
    
            for menu_item in self.menu_list: 
                counter = 0
                IDM_MENU = c4d.FIRST_POPUP_ID + counter
                self.menu.InsData(IDM_MENU, menu_item)
    
                counter += 1
    
    class MyDialog(c4d.gui.GeDialog):
    
        def __init__(self):
            self.btn_id_list = []
            self.class_btn_id_dict = {}
    
        def CreateLayout(self):
    
            red_button = ColorButton()
            red_button.create(self, w=50,h=50,color=(255,0,0), btn_id=6000)
            red_button.menu_list = ['Menu1', 'Menu2', 'Menu3']
            self.btn_id_list.append(red_button.btn_id)
            self.class_btn_id_dict[6000] = red_button
    
            blue_button = ColorButton()
            blue_button.create(self, w=50,h=50,color=(0,0,255), btn_id=7000)
            blue_button.menu_list = ['Menu4', 'Menu5', 'Menu6', 'Menu7']
            self.btn_id_list.append(blue_button.btn_id)
            self.class_btn_id_dict[7000] = blue_button
    
    
            green_button = ColorButton()
            green_button.create(self, w=50,h=50,color=(0,255,0), btn_id=8000)
            green_button.menu_list = ['Menu8', 'Menu9']
            self.btn_id_list.append(green_button.btn_id)
            self.class_btn_id_dict[8000] = green_button
    
            return True
    
        def IsPositionOnGadget(self, gadgetId, x, y):
            # Be sure that the windows is opened,
            # in our case since we call it in BFM_INTERACTSTART it's ok
            buttonData = self.GetItemDim(gadgetId)
            
            if not buttonData["x"] < x < buttonData["x"] + buttonData["w"]:
                return False
    
            if not buttonData["y"] < y < buttonData["y"] + buttonData["h"]:
                return False
    
            return True
        
        def function_to_determine_gadgetId_under_mouse_cursor(self, x, y):
            for gadgetId in self.btn_id_list:
                if self.IsPositionOnGadget(gadgetId, x, y):
                    return gadgetId
                
        def Message(self, msg, result):
            if msg.GetId() == c4d.BFM_ADJUSTSIZE:
              self._x = msg[3] # Retrieve Y size of the GeDialog
              self._y = msg[4] # Retrieve Y size of the GeDialog
    
            # We are on the main thread here
            elif msg.GetId() == c4d.BFM_INTERACTSTART:
                c4d.StopAllThreads()
    
                state = c4d.BaseContainer()
                self.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSERIGHT, state)
    
                if state.GetInt32(c4d.BFM_INPUT_VALUE) == True:
    
                    x = state.GetInt32(c4d.BFM_INPUT_X)
                    y = state.GetInt32(c4d.BFM_INPUT_Y)
                    g2l  = self.Global2Local() 
                    x += g2l['x']  
                    y += g2l['y']
    
                    gadgetId = self.function_to_determine_gadgetId_under_mouse_cursor(x=x,y=y)
    
                    if gadgetId in self.btn_id_list:
    
                         if self.IsPositionOnGadget(gadgetId=gadgetId, x=x, y=y):
    
                              button_class = self.class_btn_id_dict[gadgetId] 
                              button_class.create_menu()
    
    
                              l2s = self.Local2Screen()
                              print str(x+l2s['x']) + " :: " + str(y+l2s['y'])
                              self.KillEvents()
                              res = c4d.gui.ShowPopupDialog(cd=self, bc=button_class.menu, x=x+l2s['x'], y=y+l2s['y'])
                              return True
    
            return c4d.gui.GeDialog.Message(self, msg, result)
    
    if __name__ == "__main__":
        dlg = MyDialog()
        dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=20304050)
    

Log in to reply