Proud to announce the c4dtools library!



  • On 06/01/2013 at 09:57, xxxxxxxx wrote:

    Hello dear Community!

    I'm very proud to tell you about the initial release of the c4dtools library. It is a Python package
    containing classes and functions that make it easier writing plugins for Cinema 4D.

    Significant features:
    â–º Easily access symbols from your c4d_symbols.h file.
    â–º Easily load strings from your c4d_strings.str file.
    â–º Fast: Symbol-loading will be cached by default.
    â–º Easily load libraries from your plugin's lib directory using a self-contained importer.
    â–º Convenient wrapper for the c4d.plugins.CommandData class.
    â–º Thoroughly documented code!
    â–º and more..

    Here's some example code:

    import c4d  
    import c4dtools  
      
    res, importer = c4dtools.prepare(__file__)  
      
    # Import libraries from the `lib` folder relative to the plugins  
    # directory, 100% self-contained and independent from `sys.path`.  
    mylib = importer.import_('mylib')  
    mylib.do_stuff()  
      
    class MyDialog(c4d.gui.GeDialog) :  
      
      def CreateLayout(self) :  
            # Access symbols from the `res/c4d_symbols.h` file via  
          # the global `res` variable returned by `c4dtools.prepare()`.  
          return self.LoadDialogResource(res.DLG_MYDIALOG)  
      
      def InitValues(self) :  
            # Load strings from the `res/strings_xx/c4d_strings.str` file  
          # via `res.string`.  
          string_1 = res.string.IDC_MYSTRING_1()  
          string_2 = res.string.IDC_MYSTRING_2("Peter")  
      
          self.SetString(res.EDT_STRING1, string_1)  
          self.SetString(res.EDT_STRING2, string_2)  
      
          return True  
      
    # As of the current release, the only wrapped plugin class is  
    # `c4d.plugins.CommandData`. The plugin is registered automatically  
    # in `c4dtools.plugins.main()`, the information for registration  
    # is defined on class-level.  
    class MyCommand(c4dtools.plugins.Command) :  
      
      PLUGIN_ID = 100008 # !! Must be obtained from the plugincafe !!  
      PLUGIN_NAME = res.string.IDC_MYCOMMAND()  
      PLUGIN_HELP = res.string.IDC_MYCOMMAND_HELP()  
      
      def Execute(self, doc) :  
          dlg = MyDialog()  
          return dlg.Open(c4d.DLG_TYPE_MODAL)  
      
    # Invoke the registration of `MyCommand` via `c4dtools.plugins.main()`  
    # on the main-run of the python-plugin.  
    if __name__ == '__main__':  
      c4dtools.plugins.main()
    

    You can download the c4dtools library from github here.

    I will continue developing this library and wouldn't mind any forkers. 😉
    Some feedback would be nice as well, thanks!

    Best,
    Niklas

    UPDATE: 1.1.0

    The c4dtools library has been improved and is now licensed under the Simplified BSD License,
    allowing it to be used in commercial projects! The documentation has been updated as well and
    is included in the repository.

    Grab it from github!



  • On 06/01/2013 at 11:07, xxxxxxxx wrote:

    hey nikklas,

    i think that it is a great idea to create some sort of c4d related python libs. any form symbol
    management is really welcome  i think, it kind of sucks, that you have always do the work twice
    currently. however, i am not sure if i really want to wrap the plugin registration. loosing pretty 
    much control for just 2-3 lines here.



  • On 07/01/2013 at 07:08, xxxxxxxx wrote:

    Hello Niklas.
    Thank you for lib!



  • On 07/01/2013 at 08:24, xxxxxxxx wrote:

    Hello Ferdinand,

    Thanks for your feedback. I've been experimenting very much until I found this way of loading
    and using the symbols. It's quite usable IMHO now.

    If you don't want to loose this control, you do not have to use the plugin class. I was tired
    of writing the registration code over and over again, (including loading the bitmap, etc.). You could
    also overwrite the c4dtools.plugins.Command.register() function.

    You wouldn't have needed to remove the code you have posted. I've already put the
    console-flushing and container-printing function into the module, thanks for the ideas. They'll
    be included in the next commit, but in the development branch. The master-branch will be updated
    as major changes come up.

    Best,
    Niklas



  • On 07/01/2013 at 09:25, xxxxxxxx wrote:

    hey,

    i am to lazzy to write a wall of text, so just a list:

    Register()

    1. return the results of the RegisterXyzData() methods in some form.

    2. add a member which let you print some text to the console when RegisterXyzData() is True.

    3. provide a reference to the actual instance of the registered plugin class.

    Symbols :

    1. it would be nice if the text length required to call a member of res would be shorter.
    i am currently using a module which is just called con, where all my constants are sitting.
    so it would be cool if it would be possible just to type res.IDC_MYELEMENTID.

    2. i really like the feature that you can add manually constants. at least i am reading
    string_2 = res.string.IDC_MYSTRING_2("Peter") in that way (or is it a dict search value) ?

    3. another cool feature would be an automatic attribute initialization (InitAttr()) for ressource
    based GUIs by parsing the res file and choosing the correct attribute type.



  • On 07/01/2013 at 10:18, xxxxxxxx wrote:

    Hi Ferdinand,

    Register

    @1: Don't get it.
    @2: You can overwrite the register() method for this.

    class MyCommand(c4dtools.plugins.Command) :
          # ...
         def register(self) :
              if super(MyCommand, self).register() :
                  print "Plugin Successfully registered."
                    return True
              return False

    @3: You can prevent the class from being registered by c4dtools.plugins.main() and
    register it yourself. This information is contained in the source-code inline documentation,
    see https://github.com/NiklasRosenstein/c4dtools/blob/master/c4dtools/plugins.py#L12.

    class MyCommand(c4dtools.plugins.Command) :
          # ...
          autoregister = False

    instance = MyCommand()
      instance.register()

    Symbols

    @1: This is the way to access the symbols. You can of course call the variable whatever you
    like, con as well of course.

    con, importer = c4dtools.prepare(__file__)
        print con.IDC_MYSYMBOL

    @2: The res.string slot points to a c4dtools.resource.StringLoader instance. Requesting an
    attribute will return a callable object wrapping c4d.plugins.GeLoadString with the symbol-id
    already set. Therefore, the returned callable object accepts 4 parameters (like the GeLoadString
    function does, minus 1 because the id is already set).
    See https://github.com/NiklasRosenstein/c4dtools/blob/master/c4dtools/resource.py#L163

    @3: Don't get it. Do you mean calling a function for each symbol and instead of taking the symbols
    id, taking the functions return value as value for the symbol?

    -Nik



  • On 07/01/2013 at 15:40, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    @3: Don't get it. Do you mean calling a function for each symbol and instead of taking the symbols
    id, taking the functions return value as value for the symbol?

    It would be a new functionality completely unrelated to the dialog ressource loading features.

    res file :

    CONTAINER mynode
    {
    	[...]
    	DATETIME STAGE_BUDGET { ANIM OFF; TIME_CONTROL; OPEN;}
    	[...]
    }
    

    now:

    class mynode(plugins.ObjectData) :
        def Init(self, node) :
    	[...]
            self.InitAttr(node, c4d.DateTimeData, c4d.STAGE_BUDGET)
    	[...]
    

    what would be cool :

    class mynode(plugins.ObjectData) :
        def Init(self, node) :
    	# does the same as the example above
    	res.InitAttributes(self, node, relatedRessourcFile)
    


  • On 08/01/2013 at 03:56, xxxxxxxx wrote:

    Hello Ferdinand,

    I did not know you were not referring to dialogs, but to descriptions. The current implementation
    does not parse the description symbols as they are automatically loaded in to the c4d module.
    Although it would not be a problem parsing the descriptions' symbol-files (*.h), it would be very
    much work parsing the *.res files, which is required to get the information how to initialize the
    attributes in the node.

    PS: I know about the symbolcache issue.. Still not a reason for me to include the description
    symbols in the res parser.

    -Nik



  • On 08/01/2013 at 08:25, xxxxxxxx wrote:

    I admit that parsing the description symbols as well might be useful sometimes, but I don't
    want it to be the default behavior. One can now optionally parse the description symbols. The
    code has been committed to the development branch. See
    https://github.com/NiklasRosenstein/c4dtools/commit/7e09b5f8f321af76717af2596d1bd4de0206a2fb.
    The argument parse_descriptions must be set to True on c4dtools.prepare() for this.

    Thanks for your feedback Ferdinand 😉

    -Niklas



  • On 14/01/2013 at 06:56, xxxxxxxx wrote:

    Hey Niklas, fantastic stuff as always. I don't have the time to look into it right now, but if it does what you say it should be a very handy timesaver in the future.
    Thank you for sharing!



  • On 30/01/2013 at 04:08, xxxxxxxx wrote:

    I like the initiative!
    Do you have an overview / documentation of all functions in this library?



  • On 01/02/2013 at 09:48, xxxxxxxx wrote:

    Hi pgroof,

    I've added a Sphinx documentation to the repository. You can find it under docs/build/html.

    I have also merged the development branch into the master branch, c4dtools is now on Version 1.0.1.

    Best,
    Niklas



  • On 14/02/2013 at 06:48, xxxxxxxx wrote:

    UPDATE: 1.1.0

    The c4dtools library has been improved and is now licensed under the Simplified BSD License,
    allowing it to be  used in commercial projects! The documentation has been updated as well and
    is included in the repository.

    Grab it from github!

    -Niklas



  • On 22/03/2013 at 11:12, xxxxxxxx wrote:

    Hi!

    The c4dtools library has been updated to 1.2.8. The new version includes some extremely cool new
    features. The documentation has also been updated.

    Some of the new features include:

    • Menu resource parser: Don't fuzz with GeDialog.MenuAddString(), ~MenuAddSeparator() etc. anymore! Easily create menu resource files, automatically enabling multilanguage support! (requires scan module) [c4dtools.resource.menuparser]
    • Dialog parameter manager: Store and restore values of parameters in dialogs! Retrieve and set dialog parameters in a comfortable and elegant way. (c4dtools.misc.dialog)
    • c4dtools.utils.AtomDict: Dictionary-like object. Enables to use c4d.C4DAtom objects to be used as dictionary keys!
    • Updated the c4dtools.utils.Importer class to behave correctly regarding to importing external dependencies. (see this post).
    • Polygon-normal alignment: Utility functions for finding normals facing into the wrong direction! (c4dtools.misc.normalalign)

    c4dtools.misc.dialog

    Here's a small code-snippet that demonstrates using the c4dtools.misc.dialog module:

    class MainDialog(c4d.gui.GeDialog) :
      
        # Must be a unique plugincafe identifier!
        ID_DATA = 1001204
      
        def __init__(self) :
            super(MainDialog, self).__init__()
            self.params = c4dtools.misc.dialog.DefaultParameterManager()
      
            # Initialize the Parameter Manager.
            p = self.params
      
            p.add('reset_axes', res.CHK_RESETAXES, 'b', True)
            p.add('optimize', res.CHK_OPTIMIZE, 'b', True)
            p.add('optimize_tolerance', res.EDT_OPTIMIZE_TOLERANCE, 'm', 0.01)
            p.add('set_phong_angle', res.CHK_SETPHONGANGLES, 'b', True)
            p.add('phong_angle', res.EDT_PHONGANGLE, 'd', math.radians(21))
            p.add('untri', res.CMB_UNTRIANGULATE, 'i', res.CMB_UNTRIANGULATE_NGONS)
      
        def InitValues(self) :
            # Load saved values.
            bc = c4d.plugins.GetWorldPluginData(self.ID_DATA)
            self.params.load_container(self, bc)
            return True
      
        def AskClose(self) :
            # Store the dialog values.
            params = getattr(self, 'params', None)
            if params:
                bc = params.to_container(self)
                c4d.plugins.SetWorldPluginData(self.ID_DATA, bc, False)
      
            # Close the dialog.
            return False
    

    c4dtools.resource.menuparser

    Since this module requires the scan module, it is not imported implcitly and the c4dtools library can safely be used without this dependency installed!

    Here's a small code-snippet on how to use MENU resources:

    res, imp = c4dtools.prepare(__file__, __res__)
      
    class MyDialog(c4d.gui.GeDialog) :
      
        MENU_FILE = res.file('menu', 'my_menu.menu')
        RECENTS_START = 1000000
      
        def CreateLayout(self) :
            menu = c4dtools.resource.menuparser.parse_file(self.MENU_FILE)
            recents = menu.find_node(res.MENU_FILE_RECENTS)
      
            item_id = self.RECENTS_START
            for fn in get_recent_files() : # arbitrary function
                node = c4dtools.resource.menuparser.MenuItem(item_id, str(fn))
                recents.add(node)
      
            # Render the menu on the dialog, passing the dialog itself
            # and the c4dtools resource.
            self.MenuFlushAll()
            menu.render(self, res)
            self.MenuFinished()
      
            # ...
            return True
    

    The corresponding MENU resource looks like this:

    # Write comments like in Python.
    MENU MENU_FILE {
        MENU_FILE_OPEN;
        MENU_FILE_SAVE;
        --------------;         # Adds a separator.
        COMMAND COMMAND_ID;     # Uses GeDialog.MenuAddCommand().
        COMMAND 5159;           # Same here.
      
        # Create a sub-menu.
        MENU_FILE_RECENTS {
            # Will be filled programatically.
        }
    }
    # More menus may follow ...
    

    The symbols used in the menu resource must be defined in the res variable passed to the render() method.

    Online Documentation

    The c4dtools library now has an index in the Python Package Index and the 1.2.8 documentation is hosted online at http://pythonhosted.org/c4dtools/.

    Edit: corrected example code


Log in to reply