Write to C4D console in real time



  • Hi,

    I have a script that takes a long time to complete, and I would like to keep an eye on progress.

    Is it possible for print() or WriteConsole() events to immediately be posted to the python console, rather than all posted after the script returns control to c4d? If not, is there another way in the c4d api to accomplish this?



  • I found c4d.CallCommand( 13957 ), which clears the console and posts any buffered lines.

    However, what I'd like to do is post the buffered lines without clearing. Is this possible?



  • Hi @ivodow thanks for reaching out us.

    With regard to the issue you've reported, assuming that you're not blocking Cinema 4D main thread, the Python Console is designed to spool, as soon as possible, output to the console window. Can you share an example that can reproduce the issue? It might be in the end just a design flaw rather than a C4D issue.

    Best, R



  • Here is a contrived minimal working example that demonstrates the issue. This is only intended to force the issue to occur. My actual code involves loading multiple >2 GB databases and building particle systems.

    Thank you.

    
    import c4d
    
    
    def long_process():
    
        fullpath = c4d.storage.LoadDialog( type = c4d.FILESELECTTYPE_IMAGES, title = "Select File > 10MB:" )
        if not fullpath:
            return
    
        for n in xrange( 10 ):
    
            cnt = 0
            with open( fullpath, 'rb') as f:
                while( True ):
                    ansi_byte = f.read(1)
                    if( ansi_byte == b'' ):
                        break
                    if( cnt % 100000 == 0 ):
                        print( "Scanned " + str(cnt) + " bytes..." )
                    cnt += 1
    
            print( "Pass: " + str(n) )
    
    
    if __name__ == "__main__":
    
        c4d.CallCommand( 13957 )
    
        print( "Start" )
    
        long_process()
    
        print( "End" )
    
        c4d.EventAdd()
    


  • Hi,

    I might be misunderstanding something here, but your script looks like it is intended for the script manager. A script manager script is blocking (at least when implemented like this). Which means Cinema will only interpret the sys.stdout file object after the script has ended.

    To circumvent this, you could either write some kind of plugin (CommandData would be a good choice), use a dangling asynchronous dialog (which is not recommended) or (ab)use one of the scripting nodes (which probably isn't recommended either). Or you could use c4dpy, which will give you a more traditional interpreter environment.

    Cheers,
    zipit



  • hi @ivodow, I've reported this behavior to our developers and we'll look into it in the future cause we think there's room for improving it.

    For the time being consider it as a limitation.

    As an alternative you can consider having a look here where taking advantage of existing loggers is explained.

    Cheers, R



  • @zipit

    Hi, could you elaborate a bit on these suggestions?
    I'm tackling with the same problem here from inside my CommandData plugin.

    1. open async dialog
    2. start "long processing" with occasional prints
      --> normal stdout (to console) does not show until the the processing ended
      --> stdout redirected to dialog. the dialog does not show until the processing ended (!)

    after opening a TYPE_ASYNC GeDialog, isOpen() returns True even if the window is not open, yet.
    How could one halt the script / wait until the dialog is REALLY open?
    Is there a way to deal with output buffering and get the printing flushed immediately?

    best, index



  • Hi,

    uhh, that would be a lot of guess work on my end with that description. It sounds a bit like you have something in place which blocks the main thread, but to pinpoint what exactly is going wrong in your case, you should open a new thread and post some code.

    To be clear: Unless you are constantly blocking the main thread, you should be able to print to the console without any problems.

    Cheers,
    zipit



  • @zipit said in Write to C4D console in real time:

    To be clear: Unless you are constantly blocking the main thread, you should be able to print to the console without any problems.

    yes, all is happening in the main thread, no threading involved.
    the user should not be able to change the hierarchy while processing.

    i for example traverse the whole object hierarchy and center all axes of objects and nulls.
    after that i print "xxx axes centered" and continue with other stuff
    the whole process can take 2-3 minutes and it would be nice to see the output "live" not just at the end.

    is there no possibility to trigger the console to output the buffered lines?



  • btw, also with threading the prints show only collected after the thread is finished

    t = ThreadedCode()
    t.Start()
    t.Wait(True)
    
    class ThreadedCode(c4d.threading.C4DThread):
    	def __init__(self):
    		print "thread init"
    	def Main(self):
    		print "thread start",
    		for i in range(0,100000):
    			print i
    		print "thread done"
    


  • Hi @indexofrefraction this is unfortunately not possible, at least not in python, since Python is always using the main thread, even if you have multiple threads, keep in mind this is not true parallelism more information read Python Programming/Threading. Since internally Python is executed on the main thread, this means the main thread will be in any case busy all the time of the execution. Due to some optimization done in R20 see (Disable default Right-Click Menu)[https://plugincafe.maxon.net/topic/11987/disable-default-right-click-menu] for more information. The console UI is not refreshed all the time when something new is popping but only when Draw events are processed.

    Except recreating your own console UI, there is no workaround for it.
    Cheers,
    Maxime.



  • @m_adam

    tx for those infos maxime!
    i'm actually using my own "Console" GeDialog. and i redirect stdout to it.
    but i have the same problem as with the real Console.

    It would be very interesting to know what "recreating your own console UI" means O:-)

    maybe some ConsoleDialog.flush() function would work,
    but I don't even know how to wait until that ASYNC ConsoleDialog window is really visible.
    Both GeDialog.isOpen() or isVisible() are returning false positives after Open()!

    import sys
    import time
    
    import c4d
    from c4d import gui
    
    class ConsoleDialog(gui.GeDialog):
    	ID_TEXTBOX = 1001
    	ID_BUTTON = 1002
    
    	def __init__(self, pluginid=0):
    		self.text = ""
    		self.SetTimer(350)
    		self.Open(c4d.DLG_TYPE_ASYNC, pluginid, -2, -2, 400, 300)
    
    	def CreateLayout(self):
    		self.SetTitle("ConsoleDialog")
    		self.GroupBorderNoTitle(c4d.BORDER_NONE)
    		self.GroupBorderSpace(4, 2, 2, 4)
    		self.AddMultiLineEditText(self.ID_TEXTBOX, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 0, c4d.DR_MULTILINE_WORDWRAP | c4d.DR_MULTILINE_READONLY)
    		self.AddButton(self.ID_BUTTON, c4d.BFH_RIGHT | c4d.BFV_CENTER, 100, 15, "OK")
    		return True
    
    	def write(self, text=""):
    		self.text += str(text)
    		self.SetString(self.ID_TEXTBOX, self.text)
    
    	def Timer(self, msg):
    		if self.text != self.GetString(self.ID_TEXTBOX):
    			self.SetString(self.ID_TEXTBOX, self.text)
    
    	def Command(self, id, msg):
    		if id == self.ID_BUTTON: self.Close()
    		return True
    
    if __name__ == "__main__":
    	cd = ConsoleDialog()
    	stdout_bak = sys.stdout
    	sys.stdout = cd
    	print("Hello World!")
    	for i in range(0, 5):
    		print(i)
    		time.sleep(1)
    	sys.stdout = stdout_bak
    
    # Problem 1: ConsoleDialog window does not show until after processing
    # Problem 2: Print output (probably) does not show until after processing
    

Log in to reply