Solved MessageDialog in NodeData plugin

Hello guys!
I’m working on a plugin which there may be exceptions and there is a need to notify the user. As I understand from this information, gui message windows cannot be shown because of threading issues. But you can use Messages. As shown here and here. So the question is: how do I make this correct when using the GetContour or GVO method with ObjectData plugin? There is a proper way to "trigger Messages method" inside the GVO? I now is it possible to use c4d.MSG_DESCRIPTION_POSTSETPARAMETER but in this case exceptions may occurs in different scenarios that not depends from plugin GUI.
Thank you!

def GetContour(self, node, doc, lod, bt):

        # Checks if there is an active object
        if node is None:
            raise RuntimeError("node is None, should never happens, that means there is no generator.")

        try:

            spline = self.GenerateSpline(node, doc)

        except Exception as e:

            # Disable generator flag 
            node[c4d.ID_BASEOBJECT_GENERATOR_FLAG] = False
            
            # Wrong way to show message ????
            c4d.gui.MessageDialog(str(e))
            
            # Return empty spline
            return c4d.BaseObject(c4d.Ospline)

        return spline

Checkout my python tutorials, plugins, scripts, xpresso presets and more
https://mikeudin.net

Hey @mikeudin,

Thank you for reaching out to us. In short, there is no good answer to your question.

  1. There is no guarantee that anything will ever run on the main thread. You must always check yourself with c4d.threading.GeIsMainThread.
  2. Scene computations, e.g., ObjectData.GetContour is run in parallel off-main-thread and is therefore subject to access restriction to avoid access violations, i.e., crashes.
  3. There is no "hack" for this. The essence is that you cannot have both the benefits of sequentialism (can use shared resources) and parallelism (is fast). There are of course data structures which allow you to handle a singular resource from multiple threads, but doing this always entails lining up multiple users in a line, each waiting for users ahead in the line to be done, i.e., hidden sequentialism.
  4. In Python this might be hard to grasp because Python itself mostly ignores the concept of parallelism and we only experience here the side effects of the parallelism of the underlying C++ Cinema 4D API.

There is also the problem that your request does not make too much sense for me on a practical level. Imagine having a plugin MySpline which opens an error dialog once its GetContour method strays from the right path.

A user now edits one hundred instances of MySpline at once and triggers the error. Cinema 4D sometimes executes passes two or three times in a row to resolve dependency problems. This would then mean that you would open three hundred error dialogs for one user interaction (if that would be technically possible in the first place).

In general, no node should open any form of dialogs on its own. Opening a dialog after a button press in the description of a node via NodeData.Message is okay.


When you are hell-bent on doing this, you could do this:

  1. Add a field to your plugin, e.g., self._error: str = "".
  2. When you are in your GetContour and an error happens, instead of raising it, simply write it to _error.
  3. Implement any NodeData method which often runs on the main thread. NodeData.Message is a good candidate.
    • On each execution of it, check if _error has content and if you are on the main thread.
    • If so, display the message and then null _error.
    • You will still need some kind of throttling so that the user is not spammed with the same error over and over again. I.e., you need an "only-new-errors" mechanism. It is up to you to implement that.

An alternative approach could go via core messages. But that is also non-ideal because setting off a c4d.SpecialEventAdd will work from a non main thread but will cause the global core message lock to engage, which is of course a bad thing to happen and exactly the "no-hack" problem I described under (4).

You could also try using c4d.StatusSetText and the other status functions. While not officially supported, they will work from non-main-thread environments. But there is no guarantee and we do not officially support this.
Cheers,
Ferdinand

MAXON SDK Specialist
developers.maxon.net

Hey @mikeudin,

Thank you for reaching out to us. In short, there is no good answer to your question.

  1. There is no guarantee that anything will ever run on the main thread. You must always check yourself with c4d.threading.GeIsMainThread.
  2. Scene computations, e.g., ObjectData.GetContour is run in parallel off-main-thread and is therefore subject to access restriction to avoid access violations, i.e., crashes.
  3. There is no "hack" for this. The essence is that you cannot have both the benefits of sequentialism (can use shared resources) and parallelism (is fast). There are of course data structures which allow you to handle a singular resource from multiple threads, but doing this always entails lining up multiple users in a line, each waiting for users ahead in the line to be done, i.e., hidden sequentialism.
  4. In Python this might be hard to grasp because Python itself mostly ignores the concept of parallelism and we only experience here the side effects of the parallelism of the underlying C++ Cinema 4D API.

There is also the problem that your request does not make too much sense for me on a practical level. Imagine having a plugin MySpline which opens an error dialog once its GetContour method strays from the right path.

A user now edits one hundred instances of MySpline at once and triggers the error. Cinema 4D sometimes executes passes two or three times in a row to resolve dependency problems. This would then mean that you would open three hundred error dialogs for one user interaction (if that would be technically possible in the first place).

In general, no node should open any form of dialogs on its own. Opening a dialog after a button press in the description of a node via NodeData.Message is okay.


When you are hell-bent on doing this, you could do this:

  1. Add a field to your plugin, e.g., self._error: str = "".
  2. When you are in your GetContour and an error happens, instead of raising it, simply write it to _error.
  3. Implement any NodeData method which often runs on the main thread. NodeData.Message is a good candidate.
    • On each execution of it, check if _error has content and if you are on the main thread.
    • If so, display the message and then null _error.
    • You will still need some kind of throttling so that the user is not spammed with the same error over and over again. I.e., you need an "only-new-errors" mechanism. It is up to you to implement that.

An alternative approach could go via core messages. But that is also non-ideal because setting off a c4d.SpecialEventAdd will work from a non main thread but will cause the global core message lock to engage, which is of course a bad thing to happen and exactly the "no-hack" problem I described under (4).

You could also try using c4d.StatusSetText and the other status functions. While not officially supported, they will work from non-main-thread environments. But there is no guarantee and we do not officially support this.
Cheers,
Ferdinand

MAXON SDK Specialist
developers.maxon.net