Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    description.SetParameter() Question

    Cinema 4D SDK
    r23 2023 python windows
    2
    11
    1.6k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • ferdinandF
      ferdinand @ThomasB
      last edited by ferdinand

      Hello @ThomasB,

      Thank you for reaching out to us.

      1. No, the Description.SetParameter call in your code is not necessary because the Description.GetParameterI call you use returns the parameter data container instance. Description.GetParameter on the other hand returns a copy, and such container must be written back with Description.SetParameter when changes to it should be reflected in the node.
      2. Side stepping the localization system of Cinma 4D should be avoided. But you have the variable german in your code, indicating in which language your plugin is running. These strings also are static, so I do not understand why you circumvent the str files of the plugin. In case you want these parameter names to be semi-programmatically defined, I would recommend still using the localization system of Cinema 4D. Define the c4d_strings.str file of res\{LANG_CODE} folder and then load them in with c4d.plugins.GeLoadString. Invoking for example c4d.plugins.GeLoadString(IDS_MY_STRING) will for example then always load in the localized variant of IDS_MY_STRING.

      Cheers,
      Ferdinand

      MAXON SDK Specialist
      developers.maxon.net

      ThomasBT 1 Reply Last reply Reply Quote 0
      • ThomasBT
        ThomasB @ferdinand
        last edited by ThomasB

        @ferdinand said in description.SetParameter() Question:

        c4d.plugins.GeLoadString(IDS_MY_STRING)

        Thank you Ferdinand,

        Well, this ID/parameter originally controls the number of horizontal bars. But if the user selects the other bars method from the drop-down menu, I would have to create a new ID in res, header and str-files. I didn't want that because I can also use this int parameter to subdivide a circle. so I used the same ID and just renamed it to make it easier for the user and not get confused.
        I did it like this with several parameters...
        This ID controls 2 different parameters, so to speak, and has to change its name dynamically depending on the method.

        So here you can see paramter height, vertical and horizontal.
        If Bars is set to 0,1,2 it shows this naming
        Screenshot 2023-05-02 120456.png

        if bars set to 3 it has to change the naming but uses the same ID´s
        Screenshot 2023-05-02 120529.png

        I actually only asked on time in the code which language the user has set and write this into a variable, to be able to use it over the whole description and depending on this and the type of method the user chooses, I change DESC_NAME. There are over 6 different types and I had to do that a lot. So in the left window the user chooses method 1 in the right window he chooses method 4 all simultanously. Hmmm

        So you mean that I should create an additional str. file with the changed names and then read the parameters from this str. file? So I end up with 2 string files for german and 2 string-files for english? I haven't been using this c4d.plugins.GeLoadString(IDS_MY_STRING).

        Cheers

        Thanks,
        T.S.B

        ferdinandF 1 Reply Last reply Reply Quote 0
        • ferdinandF
          ferdinand @ThomasB
          last edited by ferdinand

          Hello @ThomasB,

          It depends on what you are exactly doing, but it looks a bit like you are picking the wrong approach.

          1. In general, it is very uncommon to modify the name of a parameter at runtime unless the parameter has been generated dynamically or the parameter name must be truly dynamic as for example Length Cube.1, i.e., dependent on some scene element.
            • When you just want to have condition A under which parameter x is shown, and condition B under which parameter y is shown, you should simply hide and unhide parameters using c4d.DESC_HIDE, or alternatively grey them out using Nodedata.GetDEnabling.
            • The same can also be done for cycles by modifying c4d.DESC_CYCLE.
          2. In consequence this means that different things should not share a parameter ID, as this can lead to many problems. So, when an object can have a Radius or a Diameter based on the condition x, these parameters should not share the ID ID_MYPLUG_RADIUS_OR_DIAMETER, and instead there should be a ID_MYPLUG_RADIUS and a ID_MYPLUG_DIAMETER. The same goes for cycle values which should not be "repurposed".
          3. Any form of localization should be done with this localization system of Cinema 4D. For description resources that would be simply the description string files (the ones you have simply nulled).
          4. When parameter names must be dynamic, you can use c4d.plugins.GeLoadString as shown below. But that does not seem to be what you should do here, because you do not seem to want to have a parameter name which for example reflects an object name, but rather change the purpose of a parameter at runtime by renaming it. Which is not recommended. See the example at the end for details.

          See also:

          • How to dynamically hide parameters: Shows how to show and hide parameters at runtime.
          • CUSTOMGUI_CYCLE: SetContainer: Shows how to populate a cycle container at the example of user data, but for description data provided in NodeData.GetDDescription it is the same.

          Cheers,
          Ferdinand

          Code for loading localized strings with c4d.plugins.GeLoadString.

          # This pseudo code operates on the assumption of the following folder structure:
          # 
          #   + {root}                        # The root directory of the plugin.
          #       + res                           # The resources of the plugin.
          #           + description                   # The description definition resources of the plugin.
          #           + dialogs                       # The dialog definition resources of the plugin.
          #           + string_en-US                  # The English string definitions for the plugin.
          #               + description                   # The description string definitions for the plugin.
          #               + dialogs                       # The dialog string definitions for the plugin.
          #               - c4d_string.str                # The generic string definitions for the plugin.
          #           + string_de-De                  # The German string definitions for the plugin.
          #               * ...
          #           + string_fr-FR                  # The French string definitions for the plugin.
          #               * ...
          #           - c4d_symbols.h                 # The dialog and generic symbols for the resources.
          #       - somenode.pyp                  # The plugin entry point file.
          
          # The symbols file `res/c4d_symbols.h`:
          #
          #    #ifndef C4D_SYMBOLS_H__
          #    #define C4D_SYMBOLS_H__
          #
          #    enum
          #    {
          #        IDS_SEGMENTS = 1000,
          #        IDS_HORIZONTAL,
          #
          #        // Dummy
          #        ___DEFDUMMY_
          #    };
          #endif // C4D_SYMBOLS_H__
          
          # The string definitions for generic English strings `res/string_en-US/c4d_string.str`:
          #
          # STRINGTABLE
          # {
          #     IDS_SEGMENTS      "Segments";
          #     IDS_HORIZONTAL    "Horizontal";
          # }
          
          # The string definitions for generic German strings `res/string_de-De/c4d_string.str`:
          #
          # STRINGTABLE
          # {
          #     IDS_SEGMENTS      "Segmente";
          #     IDS_HORIZONTAL    "Horizontal";
          # }
          
          # The string definitions for generic French strings `res/string_fr-FR /c4d_string.str`:
          #
          # STRINGTABLE
          # {
          #     IDS_SEGMENTS      "segments";
          #     IDS_HORIZONTAL    "horizontale";
          # }
          
          # --------------------------------------------------------------------------------------------------
          
          # Exposing symbols for dialog and general resources does not work out of the box in Python as it 
          # does for descriptions. You must either redefine the integer values for the symbols, e.g., do
          # this (assuming these were the values defined in your 'c4d_symbols.h'):
          # ...
          IDS_SEGMENTS: int = 1000
          IDS_HORIZONTAL: int = 1001
          # ...
          
          import os
          # Alternatively, you can use the symbol parser. Note that there is a bug with exporting symbols to
          # the local scope in the current version. Exporting symbols to the global scope works fine and the
          # bug has been fixed for the next major release of Cinema 4D.
          # 
          # For more information, see:
          #   https://developers.maxon.net/docs/py/2023_2/manuals/foundation/symbols.html
          #
          # There are however also some errors in the current documentation of the symbol parser (will also 
          # be fixed in the next release)
          
          # Parse the symbols of this plugin into the global scope, would expose for example IDS_SEGMENTS and
          # IDS_HORIZONTAL as done above manually.
          import symbol_parser
          path: str = os.path.join(os.path.dirname(__file__), "res", "c4d_symbols.h")
          symbol_parser.parse_and_export_in_caller(path)
          
          import c4d
          
          class SomeNode(c4d.plugins.NodeData):
              """
              """
              def GetDDescription(*args) -> tuple[bool, int]:
                  """
                  """
                  if op[c4d.PY_1BARS_LEFT] == 3:
                      # Load in the string for "Segments" based on the locale Cinema 4D is running in and the 
                      # translations your plugin does provide. When the user is in a local we did not provide,
                      # e.g., Spanish (es-ES) or Japanese (ja-JP), Cinema 4D will fall back to the default
                      # language en-Us.
                      db.SetString(c4d.DESC_NAME, c4d.plugins.GeLoadString(IDS_SEGMENTS))
                  else:
                      # Load in the string for "Horizontal"
                      db.SetString(c4d.DESC_NAME, c4d.plugins.GeLoadString(IDS_HORIZONTAL))
          

          MAXON SDK Specialist
          developers.maxon.net

          ThomasBT 1 Reply Last reply Reply Quote 0
          • ThomasBT
            ThomasB @ferdinand
            last edited by ThomasB

            @ferdinand

            thank you very much I understand,

            but I do not understand complaining about sharing the same ID and just renaming it.

            I mean it just changes the name not the value. I need the value in Method 1-3 and also in Method 4? So basically it doesn´t matter how it is called. It should only be a visual confirmation so the user can tell it apart.
            I gave it a blank "" string in the string-files, otherwise the SetString() Method doesn´t work.

            Of course, each object type that I create with the plugin has its own bar method and ID's. And all use their own ID's. Object 1 has this bars method with height, vertical and horizontal,
            and Object_2 has its own so the name is then only changed for the corresponding Object.

            Object_1 c4d_PY_1BARS_LEFT

            Object_2 c4d.PY_2BARS_LEFT

            Etc.

            I did it in this manner because if I use seperate ID´s, just for the height, vertical and horizontal parameter to rename it in radius, streaks and subd, I end up in 15 or more ID´s to setup just for another name. Because I have more Object-Types and each type has this bars-method, also multiple times...

            Too bad actually

            Cheers

            Thanks,
            T.S.B

            ferdinandF 1 Reply Last reply Reply Quote 0
            • ferdinandF
              ferdinand @ThomasB
              last edited by ferdinand

              Hey @ThomasB,

              1. Relabeling parameters will cause the plugin to have one value where it should have two or more.
                1. The user sets the parameter Radius (ID_RADIUS) in a node to the value 5.
                2. The user changes the parameter Mode to Diameter.
                3. The parameter formerly labeled as Radius is now labeled Parameter.
                4. But since it is the same paramater, its ID is still ID_RADIUS, the value is still 5.
                5. The user then changes the value to 10.
                6. The user changes the parameter Mode back to Radius.
                7. The parameter formerly labeled as Parameter is now labeled Radius.
                8. The parameter value is still 10 and the old Radius value has been lost.
              2. Hiding or disabling parameters will not have that issue, you can even tie here setting the radius to setting the diameter if you wanted to, so that both values are always correctly in sync.
              3. Based on 1., this can also introduce problems when users load presets for a node. Because your node is incapable of expressing a valid state for all parameter combinations (since some are repurposed on the fly), the user could load in a preset which looks and works fine. But as soon as he or she changes the value of the parameter Mode to Diameter, the output of the plugin does not make sense anymore. Only when a node expresses all its states as parameters, can these states also be saved as a preset asset.

              Cheers,
              Ferdinand

              MAXON SDK Specialist
              developers.maxon.net

              ThomasBT 1 Reply Last reply Reply Quote 0
              • ThomasBT
                ThomasB @ferdinand
                last edited by ThomasB

                @ferdinand

                Yes, I know that, but that doesn't matter in this case.
                I've already taken that into account, but since you can set a keyframe in the case of an animation, it doesn't really matter with my plugin.

                But this raises another question for me...

                Is it then possible if I use the same ID that when the user toggles the method then set the value to another e.g. to a default value in the message method?

                For instance with c4d.MSG_DESCRIPTION_POSTSETPARAMETER

                So when the user toggles the drop-down menue to another value and the parameter switches it's label to "radius" that it gets a default value.
                Or can I also set the DEFAULT in the res-file and when the user switches the drop-down-menu it sets it to the DEFAULT value which is set in the res-File. How is that working?

                Cheers

                Thanks,
                T.S.B

                ferdinandF 1 Reply Last reply Reply Quote 0
                • ferdinandF
                  ferdinand @ThomasB
                  last edited by ferdinand

                  Hey @ThomasB,

                  Yes, I know that, but that doesn't matter in this case.

                  Maxon is quite off-hands when it comes to UX and GUI guidelines for third parties. We enforce only a few things and most of the time take a 'if it works for you approach'.

                  From our point of view, it does matter because you are breaking a central interface paradigm of Cinema 4D. Every parameter is unique and therefore has its own editing history. Users are accustomed to this. We will not try to enforce any guidelines here, but I must point out that you are leaving the grounds of scope of support. When you actively decide to ignore our advice, you are welcome to do that (truly, without any saltiness from our side), but you then also own the consequences.

                  With that being said, you can do all sorts of things, as there are multiple parameter related node messages, and MSG_DESCRIPTION_POSTSETPARAMETER is being sent after a parameter has changed. You could also overwrite NodeData.GetDParameter and .SetDParameter to more directly mess with the parameter handling, and for example restore a default parameter or even properly emulate the Cinema 4D behavior and keep an editing history of both Radius and Diameter around. But doing all this is not recommended when it is not required.

                  Also, the DEFAULT flag can be used to set the initial toggle state of group in a description. It cannot be used to set the default value of the atomic parameter types listed in the description resource manual. The default values of a node must be set in NodeData.Init or via DESC_DEFAULT in case of dynamically added parameters of type LONG, REAL, or COLOR/VECTOR.

                  Cheers,
                  Ferdinand

                  MAXON SDK Specialist
                  developers.maxon.net

                  ThomasBT 1 Reply Last reply Reply Quote 0
                  • ThomasBT
                    ThomasB @ferdinand
                    last edited by ThomasB

                    @ferdinand
                    I understand thank you for this detailed explanation.
                    hm then I have to create 36 new ID's and change the 4600 * 3 lines of code (multi version support), instead to give it a different name. 😫

                    Well it's no use, it has to be.

                    Thank you!

                    Thanks,
                    T.S.B

                    ferdinandF 1 Reply Last reply Reply Quote 0
                    • ferdinandF
                      ferdinand @ThomasB
                      last edited by

                      Hey @ThomasB,

                      then I have to create 36 new ID's and change the 4600 * 3 lines of code

                      when I said

                      when you actively decide to ignore our advice, you are welcome to do that (truly, without any saltiness from our side)

                      I truly meant that. I personally would sometimes also choose my own path because it is less work for me. I personally would not take your route in this case, but different people, different development goals. I simply must point out things like scope of support because cases like this can become a bottomless pit in development. And it is IMHO better to let developers know early when we will not touch certain topics.

                      I would say the risk that things go south is only small to medium in this case, but I cannot guarantee that. In the end it is your decision.

                      Cheers,
                      Ferdinand

                      MAXON SDK Specialist
                      developers.maxon.net

                      ThomasBT 1 Reply Last reply Reply Quote 0
                      • ThomasBT
                        ThomasB @ferdinand
                        last edited by

                        @ferdinand

                        At first I wanted to do it exactly like you recommended, but then I thought why, if you save 36 IDs, it's certainly more memory-efficient. And when you just rename it, you've already learned something again.

                        Actually, I wanted to do it with separate IDs. But then I recognized that I need the exact same 3 parameters, just with other Labels. but since renaming was faster, I decided to do it. It's an update of the previous version of the plugin and it was easiest to just change the labels.
                        I'll think about it, I've got time.

                        Thank you because my English is not always the best, I sometimes find it difficult to read your texts. They are already very extensive in some cases. 🙂

                        Good evening!

                        Thanks,
                        T.S.B

                        1 Reply Last reply Reply Quote 0
                        • First post
                          Last post