Getting only the last value of the slider (slide)
-
I have a commandplugin with a dialof with a slider.
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. -
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 collectc4d.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 inGeDialog.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 toTrue
once you open an error dialog. Before opening dialogs (and possibly doing other things), you check if_hadError
isTrue
, 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,
FerdinandPS: 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' ? -
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/?
-
Hey @pim,
So, I had a quick look.
- 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".
- ...
- 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.
- I was incorrect about there not being a
last_value
message. There is one, at least indirectly, there isBFM_ACTION_INDRAG
indicating if aBFM_ACTION
event is associated with an ongoing drag or not. - 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,
FerdinandResult:
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)
- There are no
-
-
Great work!!!
Thank you.Regards,
Pim -