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

    Can I get keyboard input mode?

    Cinema 4D SDK
    windows python c++ 2025
    2
    4
    54
    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.
    • DunhouD
      Dunhou
      last edited by

      Hey community.

      I wonder if we can get the text input event in Cinema like this. That is to say, when pressing the keyboard, it is the action of inputting text.
      11578dde-1633-49f8-a0ff-ae3d09f50e24-image.png

      Another corresponding state is the shortcut key state, such as pressing C to collapse a certain object.

      How can I get this state via Python or in C++?

      Cheers~
      DunHou

      https://boghma.com
      https://github.com/DunHouGo

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

        Hey @Dunhou,

        Thank you for reaching out to us. I think I do not have to tell you that there is c4d.gui.GetInputEvent and .GetInputState. The event variant literally grabs the last item from the input event queue and the state variant just polls for the current state again. Often they are the same, but in some scenarios Cinema 4D injects events which are then not visible as a state.

        When you have for example a tool which the user (or your plugin) set to have the shortcut "CTRL+P". Then, when the tool is invoked, you can usually still poll for CTRL or C being pressed with GetInputState because the user is likely still pressing these keys when the tool is activated. But GetInputEvent will likely not return C with the modifier CTRL being pressed, because that event likely has already been consumed.

        If an event has been consumed, is determined by returning either False (not consumed) or True (consumed) in the implementing message method such GeDialog.Message. Usually, events are consumed once they are handled by their rightful owner (but developers can make mistakes or deliberately ignore this).

        Options

        All GUI is sealed in the Cinema 4D API, i.e., we intentionally do not expose things like the object manager dialog. You therefore cannot send it messages or hook into its message stream. What you can technically do, is have a plugin or dialog which registers a timer and then regularly polls the keyboard. But that would neither tell you where one semantically connected input event starts and ends (e.g., the user is typing in a renaming field) nor who the owner is (e.g., the object manager). When you own a dialog, there is also BFM_RENAMEWINDOW but that won't help you much here.

        This is not any different in C++, as all this is an intentional design choice (to not expose GUI entities). What you would need is a core message which is emitted on renaming events but something like this does not exist at the moment.

        What you can technically also do, at least for scene elements, is hook into their message stream via BaseList2D.AddEventNotification etc., so that you get called when they receive messages, including a MSG_DESCRIPTION_POSTSETPARAMETER message when their name has been written. The issue with this is that you will only receive the messages for the exact scene element your registered for. And these methods are also for a reason internal, as you can quite easily crash Cinema 4D with them. But you will find some examples on the forum. Maybee you could just hook into the stream of the currently selected object or something like that. But you should REALLY avoid registering callbacks for countless scene elements, as this will make Cinema 4D very slow.

        Bottomline

        Cinema 4D's event system is VERY coarse, internally we are talking about this for a while and we might change this at some point. But right now you have to effectively track yourself what is happening in a scene (which is not very performant when every plugin does that and why we are inclined to change it at least to some degree).

        The easiest solution at the moment to track name changes is unfortunately to hook into EVMSG_CHANGE and build the data yourself.

        Cheers,
        Ferdinand

        Result

        9444964f-7cf6-4de7-a03c-63a8a83ef184-image.png

        Code

        """Demonstrates how to hash all objects in a scene to stores their names overt that hash, to then
        later detect name changes with that.
        
        Just create a scene, run this script, rename an object, and then run it again. This just scans for 
        objects but it would be easy to extend to other scene elements.
        """
        
        import c4d
        import mxutils
        
        doc: c4d.documents.BaseDocument  # The currently active document.
        
        # An attribute to inject data under into the c4d module. I am just doing this here so that a script
        # can have a persistent state and remembers what it did on the last execution. PLEASE DO NOT DO THIS
        # IN PRODUCTION CODE!
        NAME_STORAGE: str = "my_name_storage"
        
        def main() -> None:
            """Called by Cinema 4D when the script is being executed.
            """
            # You would either bind this code to EVMSG_CHANGE messages or better (because not as wasteful) 
            # to a specific event that makes sense in the context of your plugin. E.g., the user clicking the
            # button X in your plugin.
        
            # Get the dictionary we monkey patch into the c4d module. Please REALLY do not do this in 
            # production code, and instead use your own modules to store data! If you ignore this, it is
            # bound to lead to serious problems sooner or later!
            data: dict = getattr(c4d, NAME_STORAGE, {})
            if not data:
                print("No previous data found. Initializing new storage.")
            else:
                print("Found previous data:")
                for key, value in data.items():
                    print(f"    {key}: {value}")
        
            # Scan the scene for all objects.
            obj: c4d.BaseList2D
            noUpdates: bool = True
            for obj in mxutils.IterateTree(doc.GetFirstObject(), True, True, True):
                # Hash the object into an integer. This is reasonably collision resistant and possible since
                # Cinema 4D 2023.2 (before there was not C4DAtom.__hash__).
                hid: int = hash(obj)
                name: str = obj.GetName()
                # We found a new object.
                if hid not in data:
                    print(f"Found new object: {name} (hash: {hid})")
                    data[hid] = name
                    noUpdates = False
                # We found an already previously seen object AND its name changed.
                elif hid in data and data[hid] != name:
                    print(f"Object renamed: {data[hid]} -> {name} (hash: {hid})")
                    data[hid] = name
                    noUpdates = False
        
            if noUpdates:
                print("No changes detected.")
        
            # Write the data back into the c4d module.
            print("Storing data for next execution.")
            setattr(c4d, NAME_STORAGE, data)
        
        if __name__ == '__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        DunhouD 1 Reply Last reply Reply Quote 0
        • DunhouD
          Dunhou @ferdinand
          last edited by ferdinand

          Hey @ferdinand , thanks for your information, Iterate graph is one of the solutions to listen to name change, but what I want is the "state" of any text input.

          As I had posted on the beta forums (Typing software issue with Cinema 4D.), if someone used non-Latin languages, like Chinese for example, If you use a typing software like IME( Mircosoft Pinyin ), it should ship an English input. When we want to change something like an object name, it will set the typing to English mode. It is very annoying when you have tons of objects to rename one by one.

          So I wonder if I can get the "typing mode" so I can set the IME always in Chinese mode when I input a text, and keep in English when not typing text to use shortcuts.

          I am a very stupid beginner in C++, but you know what I mean. I try to use GetGUIThreadInfo and GUI_CARETBLINKING to get the text cursor, but it seems not work.

          Hope it is clear.

          Cheers~
          DunHou

          https://boghma.com
          https://github.com/DunHouGo

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

            Hey @Dunhou,

            I have edited your posting. It is fine to mention the beta forum, but we would rather not see its name written here.

            When you are talking about 'C++', I assume you are talking here about the Windows SDK/API, as for example GetGUIThreadInfo. I would doubt that you get very far with that here. Cinema 4D is very OS agnostic and we have our whole GUI decoupled from the OS. The little rename window in our tree view control is not an OS rename dialog. I am not even sure if this is something with an HWND handle which you could address so that you could intercept keyboard events to it, or if this is just a virtual window within our API.

            For this IMM thing to work, we would have to use the IME API in Cinema 4D, so that our text edit gadgets support it. I also do not quite understand what you are trying to do. The missing piece is here probably that our text edit gadget does not send its current content to the IME API (at a glance, via ImmSetCompositionStringW as shown here). And as a third party, you cannot really fix that. Because you (a) do not own the implementation and you (b) are not informed about an edit event, so you cannot just write a plugin which sends the object name to the IME API.

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

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