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
    • Register
    • Login

    Getting only the last value of the slider (slide)

    Bugs
    2023 python
    2
    5
    613
    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.
    • P
      pim
      last edited by ferdinand

      I have a commandplugin with a dialof with a slider.
      1f6460e1-8f6f-42d8-a572-db306875e6f8-image.png

      In Command() I check the input.
      If it its the slider, I check the active object and then get the input.

          def Command(self, id, msg):  
              doc = documents.GetActiveDocument() 
      
              if (id == ID_GETSLIDER):
                  parent = doc.GetActiveObject()
                  if (not parent):
                      gui.MessageDialog('Please select an object') 
                      return True  
      
             ....
      

      However, when I change (slide) the slider and there is no active object, the message is displayed, but immediately a second, third, etc. message is displayed.
      This is - I think - because due to the slider (slide) the field is selected multiple times and thus Command() is triggered multiple times.

      How can I make sure that only the last value of the slide is returned.
      So, not all values during the slide, but only the 'last' value.

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

        Hey @pim,

        thank you for reaching out to us. The GUI messages c4d.BFM_INTERACTSTART and c4d.BFM_INTERACTEND are the formal answer to your question. You can receive them in GeDialog::Message, clamping an interaction span. There is no last value message. You would have to collect c4d.BFM_ACTION events yourself within an interaction span and then select the last value yourself once the span ended.

        Circumventing the continuous update system of dialogs and possibly consuming BFM_ACTION events before they bubble up in GeDialog.Command is however a bad idea IMHO. It seems also really unnecessary for your problem at hand. Simply add an instance bound variable _hadError: bool = False to your dialog which you set to True once you open an error dialog. Before opening dialogs (and possibly doing other things), you check if _hadError is True, if so, you simply bail. You could also make this more precise with multiple error fields, e.g., _hadObjectMissingError, _hadFooError, ...

        On more general note, I would put the use of a message dialog itself into question. Cinema 4D is deliberately very conservative with message/error dialogs. Messages dialogs should only be opened for critical information such as crashes, potential loss of data, and file IO . An object not being selected does qualify as critical. Such information should be displayed in the status bar with c4d.StatusSetText.

        Cheers,
        Ferdinand

        PS: What I do not understand about your question is that c4d.gui.MessageDialog opens a modal dialog, i.e., it should interrupt a slider drag session and also prevent new dialogs from being opened. Could you clarify the circumstances in which a 'second, third, etc message is displayed' ?

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • P
          pim
          last edited by pim

          If I understand your ps correctly;
          I do think that c4d.gui.MessageDialog indeed interrupts/stops the slider.
          However, I think the slider already 'gave' multiple messages before I handle them in Command()

          Are you also saying I should handle this in GeDialog::Message() and not in GeDialog::Command()?

          Also, could it be the same issue as in post https://developers.maxon.net/forum/topic/14470/?

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

            Hey @pim,

            So, I had a quick look.

            1. There are no BFM_ACTION events pooling up (or given as you have put it), there is simply a bug in the slider GUI that causes it not to lose focus on a modal dialog being focused. So, what happens is:
              • The user drag presses LMB on the slider.
              • The user moves the mouse.
              • Modal error dialog opens saying "Initiating self-destruct sequence".
              • BFM_ACTION stream is interrupted.
              • User goes "Eeek" and releases LMB.
              • BFM_INTERACTEND is not emitted.
              • User closes error dialog.
              • The slider is still bound to the mouse position.
              • The user moves the mouse.
              • Modal error dialog opens saying "Initiating self-destruct sequence".
              • ...
            2. When I have time, I will file a bug report for this, although the chances of this being fixed are close to zero. The devs will (rightfully) say that opening modal dialogs is not something one should do in this context.
            3. I was incorrect about there not being a last_value message. There is one, at least indirectly, there is BFM_ACTION_INDRAG indicating if a BFM_ACTION event is associated with an ongoing drag or not.
            4. With that you could suppress drag events bubbling up in your Command. See the code example at the end of my posting for details.
            ⚠ But all that does not change the fact that you should not open modal or async error dialogs for things that are not critical.

            Cheers,
            Ferdinand

            Result:
            gedlg_drag_error.gif

            Code:

            import c4d
            
            MSG_ID_STRING: dict[int, str] = {
                c4d.BFM_INTERACTSTART: "BFM_INTERACTSTART",
                c4d.BFM_INTERACTEND: "BFM_INTERACTEND",
                c4d.BFM_ACTION: "BFM_ACTION",
                c4d.BFM_ACTION_INDRAG: "BFM_ACTION_INDRAG"
            }
            
            
            class DragMessageDialog(c4d.gui.GeDialog):
                """
                """
                ID_SLD_MAIN: int = 1000
            
                def CreateLayout(self) -> bool:
                    """
                    """
                    self.SetTitle("DragMessageDialog")
                    self.GroupBorderSpace(10, 5, 10, 5)
                    self.AddSlider(DragMessageDialog.ID_SLD_MAIN, c4d.BFH_SCALEFIT)
                    return True
            
                def Command(self, cid: int, msg: c4d.BaseContainer) -> bool:
                    """
                    """
                    # Will only trigger on finished drag events, because that is the only type of #BFM_ACTION
                    # that we let propagate.
                    if cid == DragMessageDialog.ID_SLD_MAIN:
                        c4d.gui.MessageDialog("Hello World!")
                        return False
            
                    return True
            
                def Message(self, msg: c4d.BaseContainer, result: c4d.BaseContainer) -> int:
                    """
                    """
                    # Uncomment to understand the message stream that wraps a drag event.
                    # if msg.GetId() in (c4d.BFM_INTERACTSTART, c4d.BFM_INTERACTEND, c4d.BFM_ACTION):
                    #     print (MSG_ID_STRING[msg.GetId()])
                    #     for k, v in msg:
                    #         print ("\t", k, v)
                    #     return 0
            
                    # Luckily, I was wrong and there is a 'last_value' message, at least indirectly. 
                    # BFM_ACTION_INDRAG indicates an ongoing drag event for BFM_ACTION.
            
                    # We consume all BFM_ACTION for ongoing drag events, preventing them from being propagated 
                    # to DragMessageDialog.Command
                    if msg.GetId() == c4d.BFM_ACTION and msg.GetBool(c4d.BFM_ACTION_INDRAG, False):
                        return 0
            
                    return super().Message(msg, result)
            
            if __name__ == "__main__":
                dlg: DragMessageDialog = DragMessageDialog()
                dlg.Open(c4d.DLG_TYPE_ASYNC, 0, -1, -1, 500, 500)
            

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • ferdinandF ferdinand referenced this topic on
            • P
              pim
              last edited by

              Great work!!!
              Thank you.

              Regards,
              Pim

              1 Reply Last reply Reply Quote 0
              • maxonM maxon moved this topic from Cinema 4D SDK on
              • First post
                Last post