UNSOLVED Getting Render Progress

Hello,
I'd like to write a script that starts a render and prints its progress.

I found this topic on the forum:
Render to Picture Viewer and RenderDocument woes

The script below (based on the one from the aforementioned topic) does a good job with determining when a render has completed by starting a new thread, but how can I use it to determine the current render progress?

import c4d  
  
import os,time,_thread  
  
  
def isRendering(time,os) :  
  print(time.ctime())  
  while c4d.CheckIsRunning ( c4d.CHECKISRUNNING_EXTERNALRENDERING ) :  
      print("render in progress...")    
      time.sleep(3)  
    
  print(time.ctime())  
  print("render complete.")   
  
  
def main() :  
  
    
  c4d.CallCommand(12099)  #Render to Picture Viewer    
  if c4d.CheckIsRunning ( c4d.CHECKISRUNNING_EXTERNALRENDERING ) :  
      _thread.start_new_thread(isRendering,(time,os))  
  
if __name__=='__main__':  
  main()  

Thank you!

Hi,

the best way to do it is to use the progress hooks functions. We got an example on github.

Be aware that if you want to display the progress using a progress bar, this will not work from the script manager. You must write your own plugins and GeDialog to make it work.

Cheers,
Manuel

@m_magalhaes
Hi Manuel,
Thank you for the reply! I have found the GitHub example, but it does not show the progress until after the rendering has completed. When it does, it shows it during the "Before Rendering" progress type when I would expect to see it during "During Rendering." As in my original post, I believe starting a new thread is necessary, but I'm not sure how to check on the render progress with this method.

Here is the script you recommended turned into a plugin. Also, if it needs to be a plugin to work, could you please make the example on GitHub a plugin?

import c4d

PLUGIN_ID = 9102524

class ProgressDialog(c4d.gui.GeDialog):
    RENDER_TEXT = 1000
    RENDER_BUTTON = 1001

    def PythonCallBack(self, progress, progress_type):
        text = str()

        if progress_type == c4d.RENDERPROGRESSTYPE_BEFORERENDERING:
            text = "Before Rendering"

        elif progress_type == c4d.RENDERPROGRESSTYPE_DURINGRENDERING:
            text = "During Rendering"

        elif progress_type == c4d.RENDERPROGRESSTYPE_AFTERRENDERING:
            text = "After Rendering"

        elif progress_type == c4d.RENDERPROGRESSTYPE_GLOBALILLUMINATION:
            text = "GI"

        elif progress_type == c4d.RENDERPROGRESSTYPE_QUICK_PREVIEW:
            text = "Quick Preview"

        elif progress_type == c4d.RENDERPROGRESSTYPE_AMBIENTOCCLUSION:
            text = "AO"

        # Prints to the console the current progress
        prog = "ProgressHook called [{0} / p: {1}]".format(text, progress * 100.0)
        self.SetString(self.RENDER_TEXT,prog)
        print(prog)

    def PythonWriteCallBack(self, mode, bmp, fn, mainImage, frame, renderTime, streamnum, streamname):
        text = str()

        if mode == c4d.WRITEMODE_STANDARD:
            text = "Standard"

        elif mode == c4d.WRITEMODE_ASSEMBLE_MOVIE:
            text = "Assemble Movie"

        elif mode == c4d.WRITEMODE_ASSEMBLE_SINGLEIMAGE:
            text = "Assemble single image"

        wprog = "ProgressWriteHook called [{0} / p: {1}]".format(text, renderTime)
        self.SetString(self.RENDER_TEXT,wprog)
        print(wprog)

    def CreateLayout(self):
        self.SetTitle("Render Progress")

        if self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, title="", rows=1, cols=1, groupflags=c4d.BORDER_GROUP_IN):
            self.GroupBorderSpace(20, 20, 20, 20)
            self.AddStaticText(self.RENDER_TEXT, c4d.BFH_CENTER | c4d.BFH_SCALE | c4d.BFV_CENTER | c4d.BFV_SCALE, 600, 20, borderstyle=c4d.BORDER_THIN_IN)
            self.AddButton(self.RENDER_BUTTON, c4d.BFH_CENTER | c4d.BFH_SCALE | c4d.BFV_CENTER | c4d.BFV_SCALE, initw=0, inith=0, name='RENDER')

        self.GroupEnd()
        return True

    def Render(self):
        doc = c4d.documents.GetActiveDocument()

        # Retrieves the current active render settings
        rd = doc.GetActiveRenderData()

        # Creates a Multi Pass Bitmaps that will store the render result
        bmp = c4d.bitmaps.MultipassBitmap(int(rd[c4d.RDATA_XRES]), int(rd[c4d.RDATA_YRES]), c4d.COLORMODE_RGB)
        if bmp is None:
            raise RuntimeError("Failed to create the bitmap.")

        # Adds an alpha channel
        bmp.AddChannel(True, True)

        # Renders the document
        if c4d.documents.RenderDocument(doc, rd.GetData(), bmp, c4d.RENDERFLAGS_EXTERNAL, prog=self.PythonCallBack,
                                        wprog=self.PythonWriteCallBack) != c4d.RENDERRESULT_OK:
            raise RuntimeError("Failed to render the temporary document.")

        # Displays the render in the Picture Viewer
        c4d.bitmaps.ShowBitmap(bmp)

    def Command(self, messageId, bc):
        if messageId == self.RENDER_BUTTON:
            self.Render()
        return True

class RenderProgressCommandData(c4d.plugins.CommandData):
    dialog = None

    def Execute(self, doc):
        if self.dialog is None:
            self.dialog = ProgressDialog()

        return self.dialog.Open(dlgtype=c4d.DLG_TYPE_ASYNC, pluginid=PLUGIN_ID, defaulth=700, defaultw=200)

    def RestoreLayout(self, sec_ref):
        if self.dialog is None:
            self.dialog = ProgressDialog()

        return self.dialog.Restore(pluginid=PLUGIN_ID, secret=sec_ref)

if __name__ == "__main__":
    c4d.plugins.RegisterCommandPlugin(id=PLUGIN_ID,
                                      str="RenderProgress",
                                      help="Show the current render progress of Cinema 4D.",
                                      info=0,
                                      dat=RenderProgressCommandData(),
                                      icon=None)

Thank you!