Navigation

    • Register
    • Login
    • Search
    1. Home
    2. mikeudin
    mikeudin

    mikeudin

    @mikeudin

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

    36
    Reputation
    100
    Posts
    342
    Profile views
    1
    Followers
    0
    Following
    Joined Last Online
    Website mikeudin.net Location Spain Age 42

    • Profile
    • More
      • Following
      • Followers
      • Topics
      • Posts
      • Best
      • Groups
    mikeudin Follow

    Best posts made by mikeudin

    Batch Processor: plugin for python scripts developers

    Hello folks!

    Let me introduce you my new plugin, which is good solution for those people who developing file handling scripts for Cinema 4D R19 and higher.

    The Batch Processor plugin allows to convert between all the 3D formats that are supported by Cinema 4D and supports User Scripts, including scripts with User Interface. Plugin installation contains several scripts that can be used as an example. Also plugin help contains all the necessary information about developing scripts with Batch Processor compatibility.

    Using the Batch Processor, the developer needs to write only code part for processing the document, the plugin takes care of the rest (opening-saving file,collecting, display and saving the script results log).

    Here is an example Demo Script:

    import c4d
    from c4d import gui
    
    '''
    Batch processor User Script Basic Example
    Please follow the code comments 
    https://mikeudin.net/
    
    
    To add User Scipt dialog make sure:
    1. Your dialog is based on gui.Subdialog class
    2. Class must have a name 'UserScriptDialog'
    3. Class must have a class variable 'user_script_container' to store all data on it
    4. Script must have 'main' function that will be executed by Batch Processor
    5. 'user_script_container' from dialog will be used as input argument for 'main' function
    
    '''
    
    class UserScriptDialog(gui.SubDialog):
        """
        A SubDialog to display the passed string, its used as example for the actual content of a Tab
        """
        
        STRING_FIELD = 2000
        INT_FIELD = 2001
    
        # define user_script_container as dictionary
        user_script_container = dict()
        
        def CreateLayout(self):
    
            # Add your dialog gui elements as descibed on Cinema 4D Python API
            # Please use methods which is compatible with Cinema 4D R19+ Python API
            
            bc = c4d.BaseContainer()
            bc.SetBool(c4d.QUICKTAB_BAR, True)  
            bc.SetString(c4d.QUICKTAB_BARTITLE, 'Script Options')
            self.AddCustomGui(1000, c4d.CUSTOMGUI_QUICKTAB, '', c4d.BFH_SCALEFIT, 0, 0, bc)
    
            if self.GroupBegin(1001,flags=c4d.BFH_SCALEFIT | c4d.BFV_TOP,cols=2,rows=1,title='Script Options'):
                
                self.GroupSpace(15, 6)
                self.GroupBorderSpace(15,5,15,10)
    
                self.AddStaticText(1002, c4d.BFH_LEFT, name='My String Data')
                self.AddEditText(self.STRING_FIELD, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 0, 10)
    
                self.AddStaticText(1003, c4d.BFH_LEFT, name='My Integer Data')
                self.AddEditNumberArrows(self.INT_FIELD, c4d.BFH_LEFT | c4d.BFV_SCALEFIT, 80, 10)
    
                self.GroupEnd()
    
            return True
    
        def Command(self,id,msg):
    
            # Assign data to 'user_script_container' variable on any user gui interaction
            if id in {self.STRING_FIELD,self.INT_FIELD}:
                self.user_script_container['int'] = self.GetInt32(self.INT_FIELD)
                self.user_script_container['string'] = self.GetString(self.STRING_FIELD)
    
            return True
    
    # On execution 'user_script_container' will be used as input argument for 'main' function. 
    # if User Scipt do not contains dialog, 'main' function will be executed without arguments.
    
    def main(data):
        
        '''
        Batch Processor adds some global variables to User Script:
        
        doc - referenced to a processed document, type: c4d.BaseDocument
        doc_index - referenced to an document index in Jobs Queue, type: int
        docs_total - total number of documents in a Jobs Queue, type:int
    
        op - Active Object of processed document, type: c4d.BaseObject 
        mat - Active Material of processed document, type: c4d.Material
        tp - Particle System of processed document, type: c4d.modules.thinkingparticles.TP_MasterSystem
    
        '''
    
        # All data that needed to be printed will be passed to plugin Log tab
        print 'Processed Document Index {0} from {1}'.format(doc_index,docs_total) 
        print 'Document name: ', doc.GetDocumentName()
        print 'Document path: ', doc.GetDocumentPath()
        print 'Document Active Object is ', op
        
        print 'My String data', data['string']
        print 'My Integer data', data['int']
        
        if 3 < doc_index < 7:
            # If index of processed document have a number 4,5,6
            # in this case User Scipt will return error   
            raise Exception('Error!')
    
    

    Here is how it works:

    f873b137-b5f8-4c55-bf8f-fae9a3fc943b-image.png

    I hope the Batch Processor will be the solution that will help automate your work on processing a large number of files using the Cinema 4D Python API!

    posted in General Talk •
    RE: MCOMMAND_JOIN issue

    You have to combine objects to null, for example:

    import c4d
    from c4d import gui
    
    def smc(listy):
        null = c4d.BaseObject(c4d.Onull)
    
        for o in listy:
            o.InsertUnder(null)
    
        res = c4d.utils.SendModelingCommand(command = c4d.MCOMMAND_JOIN,
                                        list = [null],
                                        mode = c4d.MODELINGCOMMANDMODE_ALL,
                                        bc = c4d.BaseContainer(),
                                        doc = doc)
        return res[0]
    
    def main():
        obs = doc.GetActiveObjects(0)
        doc.InsertObject(smc(obs))
        c4d.EventAdd()
    
    if __name__=='__main__':
        main()
    
    posted in Cinema 4D SDK •
    RE: Plugin OnFloor Final 1.2.4RC1

    How to use this tool with objects grouped in a single null?
    Not working with Figure primitive 🤔 :

    Traceback (most recent call last):
      File "C:\Users\user\AppData\Roaming\MAXON\Cinema 4D R20_4FA5020E\plugins\OnFloor_1.0.0\OnFloor\OnFloor.pypv", line 108, in MouseInput
    
      File "C:\Users\user\AppData\Roaming\MAXON\Cinema 4D R20_4FA5020E\plugins\OnFloor_1.0.0\OnFloor\OnFloor.pypv", line 47, in CenterAxe
          File "C:\Users\user\AppData\Roaming\MAXON\Cinema 4D R20_4FA5020E\plugins\OnFloor_1.0.0\OnFloor\OnFloor.pypv", line 24, in MoveAxe
        ValueError: Invalid object length.
    

    Just in case. Using my Target4D plugin you can drop your object to any surface 🙃
    alt text

    posted in General Talk •
    RE: Dealing with Symbolcache

    May be there is unicode symbols problem. Try this

    f = open(fn.decode("utf-8"))

    posted in Cinema 4D SDK •
    RE: Autocomplete successful but symbol IDs are unrecognized (on Pycharm)

    @bentraje You have to edit your
    "C:\Program Files\MAXON\Cinema 4D R20\resource\modules\python\libs\python27\c4d_init_.py"
    file by adding all symbol ID's as module variables.

    pycharm_example3.png

    Check out an example file, make sure you have a backup!🤞

    posted in Cinema 4D SDK •
    Cinema 4D Script: Web Search Selected Text

    Hello guys! Hope you find it interesting.
    I've written little script which sends copied text to differents web pages for search.
    This script will be very helpfull to work with Cinema4D online SDK directly from Script Manager or Expression Editor.✌ 😎

    Download for free here.

    0_1552493953813_mikeudin_web_search_text.png

    posted in General Talk •
    RE: Export selected objects to separate files

    @klints2
    Try to use IsolateObjects

    A helper routine to copy the objects of document to a new document (returned). All materials associated are also copied over and the links are corrected.

    posted in Cinema 4D SDK •
    Send Python Code To C4D from any Texteditor

    Hello guys!
    I've created little solution to send python code to execute in Cinema 4D. Also it works for editing Pyton Generators, Effectors, Tags and Fields! Thanks for Remote Code Runner - Cinema 4D & Sublime Text plugin by NiklasRosenstein.

    pycharm_example.png

    Download SendPythonCodeToCinema4D

    posted in General Talk •
    RE: C++ Plugin Development Tutorials

    @kbar Thank you! Great job!

    posted in Maxon Announcements •
    RE: Developing .obj sequence exporter for selected objects

    May be Polygonize can help you.

    Make a clone of the document and turn all objects into polygon based objects.

    posted in Cinema 4D SDK •

    Latest posts made by mikeudin

    Fieldlist HasContent() GetCount() bug workaround

    Re: FieldList.GetCount Bug
    Hello!
    I have a same issue but @pyr's workaround not working for me. Because the issue of fieldlayers based on linked objects. But if FieldList have fieldlayers without linked objects like Solid this code

    def CheckFieldHealth (self,doc,field):
    
            if field == None: return False
    
            def get_field_layers(field):
                """ Returns all field layers that are referenced in a field list.
    
                https://plugincafe.maxon.net/topic/11809/iterating-trough-field-list/2
    
                """
    
                def flatten_tree(node):
                    """ Listifies a GeListNode tree.
                    """
                    res = []
                    while node:
                        res.append(node)
                        for child in node.GetChildren():
                            res += flatten_tree(child)
                        node = node.GetNext()
                    return res
    
                # get the GeListHead for the FieldList
                root = field.GetLayersRoot()
                if root is None:
                    return []
                # Get the first node under the GeListHead
                first = root.GetFirst()
                if first is None:
                    return []
    
                # traverse the graph
                return flatten_tree(first)
    
            error = 0
            for f in get_field_layers(field):
    
    
                if f.GetName() != None and f.GetLinkedObject(doc) == None and f[c4d.DESC_NAME]== None:
                    error += 1
    
            if error == field.GetCount(): return False
    
    
            return True
    

    will give same result as fieldlayer with destructed object. So how to figure out type of fieldlayer: FieldObject, Fieldlayer, Modifier Layer? Is this layer based on object or tag from OM or not? Thank you!

    posted in Cinema 4D SDK •
    RE: Tree-Generator

    Great job! 👏👏
    Needs more Cinema 4D versions support and MacOS x86/Arm

    posted in General Talk •
    TreeView for ObjectData plugin

    Hello!
    Can't figure out how to add TreeView gui to ObjectData attributes interface.
    There is some tips here, but is not clear how it can be used in python.🤷‍♂️
    Here is a sample code what i want to implement.

    testgen.zip

    /res/description/Otestgen.h

    enum {
        Otestgen = 100500,
        TG_TEST_GROUP = 1000,
        TG_TAGSGROUP_GROUP = 1001,
        TG_TAGS = 1002,
    };
    

    /res/description/Otestgen.res

    CONTAINER Otestgen {
        INCLUDE Obase;
        NAME Otestgen;
        GROUP ID_OBJECTPROPERTIES {
        }
        GROUP TG_TEST_GROUP {
            DEFAULT 1;
            GROUP TG_TAGSGROUP_GROUP {
                DEFAULT 1;
                ITEMTREE TG_TAGS { }
            }
        }
    }
    

    /res/strings_us/description/Otestgen.str

    STRINGTABLE Otestgen {
        Otestgen "TestGen";
        TG_TEST_GROUP "Test";
        TG_TAGSGROUP_GROUP "";
        TG_TAGS "Tags";
    }
    

    /testgen.pyp

    import c4d
    import os
    
    
    def load_bitmap(path):
        path = os.path.join(os.path.dirname(__file__), path)
        bmp = c4d.bitmaps.BaseBitmap()
        if bmp.InitWith(path)[0] != c4d.IMAGERESULT_OK:
            bmp = None
        return bmp
    
    
    
    class tagObject(object):
        """
        Class which represent a texture, aka an Item in our list
        """
        name = "Default"
        _checked = True  
        _selected = False
    
        def __init__(self, name):
            self.name = name
    
        @property
        def IsSelected(self):
            return self._selected
    
        def Select(self):
            self._selected = True
    
        def Deselect(self):
            self._selected = False
    
        def __repr__(self):
            return str(self)
    
        def __str__(self):
            return self.name
    
    class ListView(c4d.gui.TreeViewFunctions):
    
        def __init__(self):
            self.tagList = list() # Store all objects we need to display in this list
    
        def IsResizeColAllowed(self, root, userdata, lColID):
            return True
    
        def IsTristate(self, root, userdata):
            return False
    
        def GetColumnWidth(self, root, userdata, obj, col, area):
    
            if col == ID_NAME:
                return 200
    
            return 80  # All have the same initial width
    
        def IsMoveColAllowed(self, root, userdata, lColID):
            # The user is allowed to move all columns.
            # TREEVIEW_MOVE_COLUMN must be set in the container of AddCustomGui.
            return False
    
        def GetFirst(self, root, userdata):
            """
            Return the first element in the hierarchy, or None if there is no element.
            """
            rValue = None if not self.tagList else self.tagList[0]
            return rValue
    
    
        def GetNext(self, root, userdata, obj):
            """
            Returns the next Object to display after arg:'obj'
            """
            rValue = None
    
            pList = self.tagList
            
            currentObjIndex = pList.index(obj)
            nextIndex = currentObjIndex + 1
            if nextIndex < len(pList):
                rValue = pList[nextIndex]
    
            return rValue
    
        def GetPred(self, root, userdata, obj):
            """
            Returns the previous Object to display before arg:'obj'
            """
            pList = self.tagList
            
            currentObjIndex = pList.index(obj)
            nextIndex = currentObjIndex - 1
            if nextIndex < len(pList):
                rValue = pList[nextIndex]
    
        def GetId(self, root, userdata, obj):
            """
            Return a unique ID for the element in the TreeView.
            """
            return hash(obj)
    
        def Select(self, root, userdata, obj, mode):
            """
            Called when the user selects an element.
            """
            if mode == c4d.SELECTION_NEW:
                for preset in self.tagList:
                    preset.Deselect()
                obj.Select()
            elif mode == c4d.SELECTION_ADD:
                obj.Select()
            elif mode == c4d.SELECTION_SUB:
                obj.Deselect()
    
        def IsSelected(self, root, userdata, obj):
            """
            Returns: True if *obj* is selected, False if not.
            """
            return obj.IsSelected
    
        def SetCheck(self, root, userdata, obj, column, checked, msg):
            """
            Called when the user clicks on a checkbox for an object in a
            `c4d.LV_CHECKBOX` column.
            """
            if checked:
                obj.checked = True
            else:
                obj.checked = False
    
        def IsChecked(self, root, userdata, obj, column):
            """
            Returns: (int): Status of the checkbox in the specified *column* for *obj*.
            """
            if obj.checked:
                return c4d.LV_CHECKBOX_CHECKED | c4d.LV_CHECKBOX_ENABLED
            else:
                return c4d.LV_CHECKBOX_ENABLED
    
        def GetName(self, root, userdata, obj):
            """
            Returns the name to display for arg:'obj', only called for column of type LV_TREE
            """
            return str(obj) # Or obj.presetType
    
        def SetName(self, root, userdata, obj, name):
            """
            Returns the name to display for arg:'obj', only called for column of type LV_TREE
            """
            obj.name = name
            
    
        def ContextMenuCall(self, root, userdata, obj, lColumn, lCommand):
            
            """
            The user executes an entry of the context menu.
    
            Returns:
                (bool): True if the event was handled, False if not.
            """
            if lCommand == c4d.ID_TREEVIEW_CONTEXT_RESET:
                print ('Call Reset')
                return True
    
            if lCommand == c4d.ID_TREEVIEW_CONTEXT_REMOVE:
                print ('Call Remove All')
                return True
    
    
        def DoubleClick(self, root, userdata, obj, col, mouseinfo):
            """
            Called when the user double-clicks on an entry in the TreeView.
    
            Returns:
              (bool): True if the double-click was handled, False if the
                default action should kick in. The default action will invoke
                the rename procedure for the object, causing `SetName()` to be
                called.
            """
    
            return False
    
        def DeletePressed(self, root, userdata):
            """Called when a delete event is received."""
            
            for preset in reversed(self.tagList):
                
                if preset.IsSelected:
                    self.tagList.remove(preset)
                    continue
    
    class TestGenData(c4d.plugins.ObjectData):
    
        PLUGIN_ID = 100500 #Example PLUgiN Id 
        PLUGIN_NAME = 'TestGen'
        PLUGIN_INFO = c4d.OBJECT_GENERATOR 
        PLUGIN_DESC = 'Otestgen'
        PLUGIN_ICON = load_bitmap('res/icons/TestGen.tiff')
        PLUGIN_DISKLEVEL = 0
    
        # TreeView Column IDs.
        ID_CHECKBOX = 1
        ID_NAME = 2
    
        @classmethod
        def Register(cls):
            return c4d.plugins.RegisterObjectPlugin(
                cls.PLUGIN_ID, cls.PLUGIN_NAME, cls, cls.PLUGIN_DESC, cls.PLUGIN_INFO,
                cls.PLUGIN_ICON, cls.PLUGIN_DISKLEVEL)
    
        def __init__(self):
            self.SetOptimizeCache(True)
    
        def Init(self, node):
            # ??????
            self.InitAttr(node, c4d.TreeViewCustomData, [res.TG_TAGS]) # How to Inint this CustomDataType attribute??
            # ??????
            return True
    
        def GetDDescription(self, node, description, flags):
          
            # Before adding dynamic parameters, load the parameters from the description resource
            if not description.LoadDescription(node.GetType()):
                return False
    
            singleID = description.GetSingleDescID()
            myTreeID = c4d.DescID(c4d.TG_TAGS)
    
            # ?????? 
            # This is a way i think how to add TreeView to attributes. But how to connect TreeView class object to it?
            # ??????
            if singleID is None or myTreeID.IsPartOf(singleID)[0]:
                # Add dynamic parameter
                bc = c4d.GetCustomDataTypeDefault(c4d.CUSTOMGUI_TREEVIEW)
            
                bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_TREEVIEW)
                bc.SetString(c4d.DESC_NAME, "Animators")
                bc.SetString(c4d.DESC_SHORT_NAME, "Animators");
                bc.SetLong(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_OFF);  
                bc.SetBool(c4d.TREEVIEW_OUTSIDE_DROP, False) 
                bc.SetBool(c4d.TREEVIEW_HAS_HEADER, True)
                bc.SetBool(c4d.TREEVIEW_BORDER, c4d.BORDER_THIN_IN)
                bc.SetBool(c4d.TREEVIEW_HAS_HEADER, False) # True if the tree view may have a header line.
                bc.SetBool(c4d.TREEVIEW_HIDE_LINES, False) # True if no lines should be drawn.
                bc.SetBool(c4d.TREEVIEW_MOVE_COLUMN, True) # True if the user can move the columns.
                bc.SetBool(c4d.TREEVIEW_RESIZE_HEADER, False) # True if the column width can be changed by the user.
                bc.SetBool(c4d.TREEVIEW_FIXED_LAYOUT, True) # True if all lines have the same height.
                bc.SetBool(c4d.TREEVIEW_ALTERNATE_BG, True) # Alternate background per line.
                bc.SetBool(c4d.TREEVIEW_CURSORKEYS, True) # True if cursor keys should be processed.
                bc.SetBool(c4d.TREEVIEW_NOENTERRENAME, False) # Suppresses the rename popup when the user presses enter.
                bc.SetBool(c4d.TREEVIEW_NO_MULTISELECT, True) 
            
                # Add TREEVIEW to the parameters in specified group
                if not description.SetParameter(myTreeID, bc, c4d.DescID(c4d.TG_TAGSGROUP_GROUP)): # group ID from above post
                    return False
    
    
        def GetVirtualObjects(self, op, hh):
            return c4d.BaseObject(c4d.Ocube)
    
    if __name__ == '__main__':
        TestGenData.Register()
    
    posted in Cinema 4D SDK •
    RE: Subprocess can't detect system installed utilities

    Thank you @ferdinand !

    posted in General Talk •
    Subprocess can't detect system installed utilities

    Hello!
    I'm trying to work with external module that uses subprocess to call some cmd programms like a brew, node, npm etc. But Cinema 4D can't recognize this commands.
    Code executed in Script Manager:

    import subprocess,os
    DIRNAME = os.path.dirname(os.path.abspath(__file__))
    print(subprocess.call(["brew -v"],cwd=DIRNAME))
    

    result:

    FileNotFoundError: [Errno 2] No such file or directory: 'brew -v': 'brew -v'
    

    How to fix it? May be it is an issue of PATH system variables? Because python interpreter installed directly on system works as espected in this case.
    Thank you!

    posted in General Talk •
    RE: InExclude detect 'Remove All' context menu command

    Thank you @ferdinand ! 👏 Will chek it!

    posted in Cinema 4D SDK •
    InExclude detect 'Remove All' context menu command

    Hello!

    1. With MSG_DESCRIPTION_INEX_DELETED we can detect deleting InExclude item event. But in case choosing 'Remove All' context menu command it is not working? How to fix that?
      Thank you!

    Screenshot 2023-02-07 at 17.05.52.png

    posted in Cinema 4D SDK •
    RE: IN_EXCLUDE list with virtual objects with parameters

    Thank you for response @ferdinand! Will try to make it with hidden tags 🤔

    posted in Cinema 4D SDK •
    IN_EXCLUDE list with virtual objects with parameters

    Voronoi Sources Tab

    Hello guys!
    In my plugin i want to create functionality like on the Sources tab of Voronoy Fracture object. There is a InExclude list and a button below. With a button user can add to the list a "virtual" objects that no present on Object Manager. Below the button, after selecting an item in the list, appears some UI elements to edit "virtual" object data.
    So how it can be done? May be there is some code examples.
    Thank you!

    posted in Cinema 4D SDK •
    RE: CommandData plugin catch Switching documents event

    @ferdinand Thank you!

    posted in Cinema 4D SDK •