AddUndo() for MergeDocument() ?

  • Hi,

    @m_magalhaes solution should work just fine with obj files too. If you are getting no output you probably haven't escaped special characters in your file path properly. Alternatively you could also pass a raw string (then you don't have to escape special characters).

    file_path = r"C:\myfile.c4d

    The reason why your script is not working as expected is probably because you are only adding Undos for the selected objects. However only the last object that has been merged into the document will be selected after the merge operation.


  • @m_magalhaes

    here is my code

    import c4d
    from c4d import utils, documents, plugins
    import time
    from threading import Thread
    class WatchThread(Thread):
        def __init__(self, name, doc):
            Thread.__init__(self, name=name)
            self.doc = doc
        def run(self):
            while True:
                obj_importer(self.doc, r'f:\01.obj')
                print 'Loaded.',
    def obj_importer(doc, obj_path):
        if obj_path is None:
        plug = plugins.FindPlugin(1030177, c4d.PLUGINTYPE_SCENELOADER)
        if plug is None:
        op = {}
        if plug.Message(c4d.MSG_RETRIEVEPRIVATEDATA, op):
            if "imexporter" not in op:
            obj_import = op["imexporter"]
            if obj_import is None:
            # Parameters
            obj_import[c4d.OBJIMPORTOPTIONS_POINTTRANSFORM_SWAPXY] = False
            obj_import[c4d.OBJIMPORTOPTIONS_POINTTRANSFORM_SWAPXZ] = False
            doc.AddUndo(c4d.UNDOTYPE_CHANGE, doc)
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            c4d.documents.MergeDocument(doc, obj_path,
                                        c4d.SCENEFILTER_OBJECTS |
                                        c4d.SCENEFILTER_MATERIALS |
                                        c4d.SCENEFILTER_MERGESCENE, None)
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            obj_import[c4d.OBJIMPORTOPTIONS_PRESETS] = 0
    if __name__ == '__main__':
        doc = c4d.documents.GetActiveDocument()
        wt = WatchThread('ht', doc)

  • Hi,

    first of all: If you want to use threads, you should use c4d.threading.C4DThread and not Pythons builtin thread type or weird things might happen. Also note that threading comes with certain limitations:

    For all threaded functions it is forbidden to:

    • Add an event.
    • Change the structure of objects attached to the document.
    • Create undos.

    I am also not quite sure what the purpose of your thread is, since you are loading into the active document. If you want to "load in the background" you will have to use a temporary document. But for loading just one file I do not see any performance benefits, since you have to merge that temporary document into the active document at the end.


  • hello,

    well as @zipit said it's forbidden to change the structure of a document outside the main thread.
    watch this page and the warning about threading

    I did checked the StartUndo and it's using GeIsMainThreadAndNoDrawThread and just get out.
    So it's pretty clear that you can't add undo on a thread.

    why are you using thread here ?


  • This post is deleted!

  • @m_magalhaes @zipit
    I use thread to manipulate external pipes(subprocess etc).

  • @arsen said in AddUndo() for MergeDocument() ?:

    @m_magalhaes @zipit
    I use thread to manipulate external pipes(subprocess etc).


    although I am aware of the concept of pipes, this does not really clear anything up for me 🧐. What do you mean by external and manipulate? Just for clarification: Under pipes I understand a data IO interface to communicate between different processes, provided in Python for example by os.pipe().

    And I don't really get why this forces you to use a thread to import an obj file 🤔.


  • @zipit
    I use thread to track the external file. When a file is changed, it is imported.
    When using c4d.threading, subprocess.Popen() / time.sleep() freezes C4D ( until the process is complete )

  • Hi,

    ah, ok, I do understand your problem better now ;). Well as explained in this thread your cannot load files other than from the main thread of c4d.

    You could do however the following:

    1. Use processes, threads or whatever you would like to use to monitor the status of your file(s).
    2. Use some cross-process communication interface like os.pipe() to indicate when your data has changed.
    3. Implement 1. and 2. within a MessageData plugin.
    4. Use MessageData.GetTimer and MessageData.CoreMessage to periodically peek into your pipe and then react to its status from the main thread/ the c4d process.


  • hello,

    @zipit already make another nice answer.

    I can just add that in c++ you have also a filemonitor manual where you can use the function WatchDirectory


  • @zipit @m_magalhaes
    Thanks for the answers! I figured out how to solve the problem.

  • hi,

    nice, feel free to share your solution and mark this thread as solved.


Log in to reply