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

    Disable default Right-Click Menu

    Cinema 4D SDK
    python r19 r20 r21
    4
    9
    1.9k
    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.
    • U
      unti
      last edited by r_gigante

      Hey everyone,

      I am trying to use the right-click and middle-click function in my ASYNC-Dialog of a Command plugin.
      I can detect the clicks just fine, but I always get C4Ds basic behaviour as well. Is there a way to surpress the default context menu or the viewport toggle? I found that returning something invalid from Message() will stop the default behaviour and throw an error - but this can't be the solution, I think.
      Is there maybe a way to return a message with the c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSERIGHT, c4d.BFM_INPUT_VALUE changed to false so c4d won't try to act on the message?

      1 Reply Last reply Reply Quote 0
      • r_giganteR
        r_gigante
        last edited by r_gigante

        Hi unti, thanks for reaching out us.

        With regard to your request, the solution to avoid Cinema 4D to deliver the expected "default" behaviors on RMB-click and MMB-click is to flush all events from the window message queue by using GeDialog.KillEvents().

        Best, R

        1 Reply Last reply Reply Quote 0
        • U
          unti
          last edited by unti

          Hey r_gigante,
          thank you for your reply, I think I am doing something wrong:

          I have this code in the Message function of my dialogwhere 10002 is the ID of a Group in my Layout:

          def Message(self, msg, result):
                  # this will crash c4d with the commander window
                  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']
                      print "Found rightclick in dialog"
                      print "X: " +str(x)
                      print "Y: " +str(y)
                      print self.GetItemDim(10002)
                      
                      if self.GetPositionInGuiArea(10002, x, y):
          
                          IDM_MENU1 = c4d.FIRST_POPUP_ID
                          IDM_MENU2 = c4d.FIRST_POPUP_ID+1
                          IDM_MENU3 = c4d.FIRST_POPUP_ID+2
          
                          menu = c4d.BaseContainer()
                          menu.InsData(IDM_MENU1, 'Menu 1')
                          menu.InsData(IDM_MENU2, 'Menu 2')
                          menu.InsData(IDM_MENU3, 'Menu 3')
          
                          l2s = self.Local2Screen()
                          print str(x+l2s['x']) + " :: " + str(y+l2s['y'])
                          result = gui.ShowPopupDialog(cd=None, bc=menu, x=x+l2s['x'], y=y+l2s['y'])
          
                          self.KillEvents()
                          print result
              return gui.GeDialog.Message(self, msg, res)
          
          

          This code will still open the default popup menu - I tried with self as well as the basecall c4d.gui.GeDialog.KillEvents(). C4D will open my menu but it wil sometimes also open the default context menu after clicking something in the menu or canceling the rightclick with esc.
          Could you maybe provide an example?

          1 Reply Last reply Reply Quote 0
          • r_giganteR
            r_gigante
            last edited by

            Hi Unti, thanks for following up.

            The issue in the code you've reported is that rather than return the parent class' Message() result you need to simply return True.

            Best, R.

            1 Reply Last reply Reply Quote 0
            • U
              unti
              last edited by

              Hey,
              thanks, that worked great in R19.
              In R21, it will also work, but it will now crash Cinema4d when the area with the code above had focus when a user uses shift+c to launch the commander, insert a command and press enter.
              I learned that the GUI in R21 has been changed and is not thread-save anymore. Can I do anything to prevent the crash when using this code?

              1 Reply Last reply Reply Quote 0
              • S
                s_bach
                last edited by

                @unti said in Disable default Right-Click Menu:

                I learned that the GUI in R21 has been changed and is not thread-save anymore.

                Sorry, but where here did you "learn" that?

                MAXON SDK Specialist

                Development Blog, MAXON Registered Developer

                U 1 Reply Last reply Reply Quote 0
                • U
                  unti @s_bach
                  last edited by

                  @s_bach
                  I researched a "bug" with the new console not printing while dragging the mouse and got this answer.
                  Is it wrong? Is there maybe another reason this crashes C4D that I can work around?

                  1 Reply Last reply Reply Quote 0
                  • M
                    m_adam
                    last edited by m_adam

                    Hi @unti

                    From my side, I was also able to reproduce the Default right-click Menu Behavior in R20.
                    So the main issue regarding that is because you do GUI operation (ShowPopuDialog) into Message function which indeed not thread-safe according to the received message (this was also true in previous versions).

                    So to counter that, please check your message first and I would say listen to input only when Cinema 4D "invite you" to do it (aka during BFM_INTERACTSTART). Then make sure to call StopAllThread (Maybe another thread is also checking for input and want to display something?).

                    Then finally regarding your freeze beside the threading issue that could cause some corruption of the event queue. you call ShowPopupDialog with None, while the host GeDialog must be passed. It's needed to ensure the event queue is correctly forwarded from the dialog to the host of the modal popup dialog).

                    This way I was not anymore capable to make Cinema 4D freeze (you said crash, but personally I was never able to make it crash, only freeze, can you confirm? A crash is when C4D close, a freeze is when C4D hangs forever).

                    Here a complete example that works nicely on the previous version (R19/R20) and R21.

                    import c4d
                    
                    class TestDialog(c4d.gui.GeDialog):
                        def __init__(self):
                            self._showPopup = False, 0, 0
                    
                        def CreateLayout(self):
                            self.GroupBegin(10002, c4d.BFH_SCALEFIT| c4d.BFH_SCALEFIT,  initw=100, inith=100)
                            self.GroupEnd()
                            return True
                    
                        def IsPositionInGuiArea(self, x, y):
                            # Check click is within the GeDialog
                            if not 0 < x < self._x:
                                return False
                    
                            if not 0 < y < self._y:
                                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']
                                    
                                    if self.IsPositionInGuiArea(x, y):
                                        IDM_MENU1 = c4d.FIRST_POPUP_ID
                                        IDM_MENU2 = c4d.FIRST_POPUP_ID + 1
                                        IDM_MENU3 = c4d.FIRST_POPUP_ID + 2
                    
                                        menu = c4d.BaseContainer()
                                        menu.InsData(IDM_MENU1, 'Menu 1')
                                        menu.InsData(IDM_MENU2, 'Menu 2')
                                        menu.InsData(IDM_MENU3, 'Menu 3')
                    
                                        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)
                    
                    if __name__ == '__main__':
                        global dlg
                        dlg = TestDialog()
                        dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC)
                    

                    Finally, there was some change in the UI in R21 regarding how events are handled, to be brief previously Cinema 4D consumes OS events, while now it only listens to them but Cinema 4D UI was and is always processed in the main Thread and was never thread-safe (that's why some operations are not possible in a threaded environment) because the c4d API ensure you are in the correct thread before doing anything GUI related stuff.

                    Regarding your other issue about the console, as you may be aware R20 comes with a new Logger System.
                    And now the console only reads content from their Logger System.
                    The logger by itself is properly fed with data during a drag event (drag event = GUI interaction = main thread).
                    But as you are now aware drawing is done also in the main thread so if you do a While XXX in the dragging process the main thread doesn't have room to jump to the console drawing part.
                    Previously the logging (GePrint, Print) was also done in the main thread, so that means a redraw was done directly, but this slowdown a lot Cinema 4D UI.
                    So in R21, the data are correctly added with the correct time in the logger but it's just not displayed because now print only adds data to the logger. And the logger is displayed when its draw request has time to be processed in the main thread.

                    Not sure if it's clear, but just to recap, previously the console was refreshed in each print while now it requests redraw and redraw is done when the main thread got the time. (This is why previously printing 1000 value in the console was really slow and now pretty fast).

                    EDIT 11/03/20:
                    You could directly access the logger and passing the maxon.WRITEMETA.UI_SYNC_DRAW to force a redraw

                    import maxon
                    txt = "My Wonderfull Text"
                    pythonLogger = maxon.Loggers.Python()
                    pythonLogger.Write(maxon.TARGETAUDIENCE.ALL, txt, maxon.MAXON_SOURCE_LOCATION(1), maxon.WRITEMETA.UI_SYNC_DRAW)
                    

                    Cheers,
                    Maxime.

                    MAXON SDK Specialist

                    Development Blog, MAXON Registered Developer

                    U 1 Reply Last reply Reply Quote 2
                    • U
                      unti @m_adam
                      last edited by unti

                      @m_adam
                      Thanks!
                      Thats a perfect explanation that solves many of my issues - even in some of my other Plugins.
                      I am sorry, I did in fact refer to c4d freezing when I said crashing. Sorry for the confusion.

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