I'm rendering an image in my plugin with documents.RenderDocument() and using StatusSetSpin().
With the code below, I'm using RenderDocument()'s prog parameter function to update the Status progress bar. Currently this code shows the spinner, but the render progress isn't given until after the rendering has completed which is unexpected. With the progress_type RENDERPROGRESSTYPE_DURINGRENDERING, I'd expect the function to execute during rendering rather than afterwards. Perhaps it is, but the UI gets frozen during rendering. With longer renders (like with the Zombie - Toon Rig scene from the Content Browser), the StatusBar doesn't get cleared with the code I have below. Here are my questions:
from c4d import documents
def updateProgressBar(p, progress_type):
if progress_type == c4d.RENDERPROGRESSTYPE_BEFORERENDERING:
elif progress_type == c4d.RENDERPROGRESSTYPE_DURINGRENDERING:
elif progress_type == c4d.RENDERPROGRESSTYPE_AFTERRENDERING:
rd = doc.GetActiveRenderData().GetData()
bmp = c4d.bitmaps.BaseBitmap()
bmp.Init(int(rd[c4d.RDATA_XRES]), int(rd[c4d.RDATA_YRES]), 24)
documents.RenderDocument(doc, rd, bmp, c4d.RENDERFLAGS_EXTERNAL, prog=updateProgressBar)
thank you for reaching out to us. What you are trying to do is unfortunately not supported by us.
Inside your callback function updateProgressBar you are inside the render thread or in other words not in the main thread. Which makes all gui operations of limits. I actually just recently asked @m_magalhaes what Maxon's stance is on the status bar functionalities being considered GUI operations or not, because I always wondered myself. Other than other GUI operations the status bar methods do not test if they are on the main thread. So you can bend the rules a bit with them. So it's kinda nice to have here an example which proves that they are subject to the same limitations although they do not enforce them.
To solve your problem you have two options:
You have here two problems at play for option two. The threading scope and its GUI limitations of the callback function and the blocking nature of RenderDocument. RenderDocument accepts a th argument with which you can pass in a BaseThread to be used as the render thread. Unfortunately it actually only accepts a BaseThread and not a C4DThread, which makes a customly designed thread type solution impossible for us here. So the only way I see which COULD work (with the emphasis it being a possibility and not a guarantee):
You need three threads:
You also will need a cross-thread communication object, I will call signal object. More on that later.
Then you do the following on the main thread:
In parallel runs the execution thread which internally runs the render thread:
Which COULD work. There is however the problem that the Cinema 4D SDK does not expose its cross-thread communication objects to Python and the one's provided by CPython like semaphore and lock are not supported. So you would have to write one yourself. Which is not really possible (at least in a safe way, due to the fact that Cinema's threading internals are not public knowledge). So there is not really a solution here for you from an official Maxon point of view (besides form using BatchRender). But since only a one-way communication is here necessary, you might be able to do it anyways ;) I hope you understand that this option clearly falls out of the scope of support.
@ferdinand Thank you, Ferdinand for the thorough answer and ideas for workarounds. I don't quite have my head around how to use threading in Cinema 4D yet but you've inspired me to look into it. Thank you!