nrosenstein-c4d / c4d-prototype-converter

A Cinema 4D plugin to convert plugin prototypes to actual Python plugins.
MIT License
26 stars 4 forks source link

Accessing resource symbols in the Python Plugin Stub #16

Closed NiklasRosenstein closed 6 years ago

NiklasRosenstein commented 6 years ago

@donovankeith

Using the c4d. module for resource symbols defined in description resources of your plugin is dangerous because of how easily Cinema 4D's symbol cache goes out of date. If you create the resource files for your plugin once, then add a parameter and update the resource files again, the newly added parameter is likely not in the symbol cache.

Personally, I prefer to use the c4ddev symbols command to generate Python code that contains all resource symbols. The automatic code generation makes it very easy to update the symbol information in the Plugin, just run the command again and copy&paste the output into the plugin source code.

I am not sure how easily this can be combined with the automatic plugin source code generator in the UserData to Description Resource Convert. One possibility would be to use res. instead of c4d. everywhere in the generated plugin stub, and add a note at the top of the stub

import c4d

# Note: It is not recommended to access your plugin's resource symbols via the c4d
# module due to how quickly the Cinema 4D symbol cache goes out of date. We
# recommend hard-coding your resource symbols in your source file. This code can
# be automatically generated with C4DDev (see https://github.com/NiklasRosenstein/c4ddev).
res = c4d

class MyPluginData(c4d.plugins.ObjectData):
  # ...
  def InitAttr(self, node):
    node[res.MY_PLUGIN_FOOBAR] = 42
    return True

This way, the user could easily drop in a replacement object with the name res and he/she will not need to modify the code further.

donovankeith commented 6 years ago

Let's do the 'res.' solution. Seems like the simplest method. Also, thanks for the reminder, I need to update some of my plugins to not use the 'c4d.' style.

NiklasRosenstein commented 6 years ago

I was just thinking about actually rendering the plugin stub as

# Note: The name `res` is used to access your plugin's resource symbols. It is not
# recommended to use the `c4d` module due to the high chance that the symbol
# cache is not in sync with your plugin's description once you made changes to it.
# If you want, you can still exchange this list of hardcoded symbols with `res = c4d`.
# Alternatively, C4DDev can generated this kind of code for you.
#   (https://github.com/NiklasRosenstein/c4ddev)
class res(object):
  MY_PLUGIN_FOOBAR = 1000
res = res()
NiklasRosenstein commented 6 years ago

Example output:

# Copyright (C) <year> <author>

import c4d
import os

# Note: The name `res` is used to access your plugin's resource symbols. It is not
# recommended to use the `c4d` module due to the high chance that the symbol
# cache is not in sync with your plugin's description once you made changes to it.
# If you want, you can still exchange this list of hardcoded symbols with `res = c4d`.
# Alternatively, C4DDev can generated this kind of code for you.
#   (https://github.com/NiklasRosenstein/c4ddev)
class res(object):
  TEST_OFFSET = 1000
  TEST_TARGET = 1001
  TEST_VERBOSE = 1002
  TEST_DYNAMIC_OBJECTS = 1003

res = res()

class TestData(c4d.plugins.ObjectData):

  PLUGIN_ID = 32423432
  PLUGIN_NAME = 'Test'
  PLUGIN_INFO = 0 
  PLUGIN_DESC = 'Otest'
  PLUGIN_ICON = None
  PLUGIN_DISKLEVEL = 0

  @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, node):
    self.InitAttr(node, c4d.Vector, [res.TEST_OFFSET])
    self.InitAttr(node, c4d.BaseList2D, [res.TEST_TARGET])
    self.InitAttr(node, bool, [res.TEST_VERBOSE])
    self.InitAttr(node, int, [res.TEST_DYNAMIC_OBJECTS])

    node[res.TEST_OFFSET] = c4d.Vector(100, 0, 0)
    node[res.TEST_VERBOSE] = True
    return True

  def Message(self, node, msg_type, data):
    return True

if __name__ == '__main__':
  TestData.Register()
NiklasRosenstein commented 6 years ago

I've added a parameter that allows you to switch between res = c4d and the class res: versions.