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

    How to select directories in a CUSTOMGUI_FILENAME?

    Cinema 4D SDK
    s26 python
    2
    3
    642
    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.
    • P
      plc
      last edited by ferdinand

      Thanks @ferdinand,
      Your post helped me a lot.
      Any idea how I could set a DIRECTORY selector instead of a FILE selector ?

      What is the corresponding pluginId to relace c4d.CUSTOMGUI_FILENAME ?

      I know how to popup a directory selector with this command

      c4d.storage.LoadDialog(c4d.FILESELECTTYPE_ANYTHING, "Select Directory", c4d.FILESELECT_DIRECTORY)
      

      But how could I integrate this into a layout ?

      Thanks 😉

      [edit @ferdinand]: Forked from https://developers.maxon.net/forum/topic/13872/

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

        Hello @plc,

        Thank you for reaching out to us. I have forked your question from the original topic, as while it was also a related question, it was effectively a new question. The reason why we are doing this, is so that forum remains searchable.

        The "trick" to let the user only select directories in the CUSTOMGUI_FILENAME, is to set its flag
        FILENAME_DIRECTORY. Custom GUIs usually have a wide range of flags/options which one can use. These flags are documented partially in the Python documentation and fully in the C++ documentation. In this case you will not find the CUSTOMGUI_FILENAME flags in the Python documentation because the matching GUI type FilenameCustomGui has not been exposed in Python. You will only find the flags for wrapped custom GUIs in the Python docs. But this often does not prevent you from using them, as these GUI controllers often must not be accessed for making use of the GUIs.

        In these cases you then must look up the custom GUI in the C++ docs, to see their flags, e.g., here for CUSTOMGUI_FILENAME. I provided an example below.

        Cheers,
        Ferdinand

        The code:

        """Simple example for a FILENAME_DIRECTORY GUI in which only directories can be selected.
        
        Can be run as a script manager script and will display all files located within a given path.
        
        The "trick" to let the user only select directories in the CUSTOMGUI_FILENAME, is to set its flag
        FILENAME_DIRECTORY. Custom GUIs usually have a wide range of flags/options which one can use. These
        flags are documented partially in the Python and fully in the C++ documentation. In this case you
        will not find the CUSTOMGUI_FILENAME in the Python documentation because the matching GUI type
        FilenameCustomGui has not been exposed in Python. You will only find the flags for wrapped custom 
        GUIs in the Python docs [1]. But this often does not prevent you from using them, as these GUI
        controllers often must not be accessed for making use of the GUIs.
        
        In these cases you then must look up the custom GUI in the C++ docs, to see their flags, e.g., here
        [2] for CUSTOMGUI_FILENAME.
        
        References:
            [1] https://developers.maxon.net/docs/py/2023_2/modules/c4d.gui/BaseCustomGui/index.html
            [2] https://developers.maxon.net/docs/cpp/2023_2/group___f_i_l_e_n_a_m_e___c_u_s_t_o_m_g_u_i_s_e_t_t_i_n_g_s.html
        
        """
        
        import c4d
        import os
        import typing
        
        class MyDialog(c4d.gui.GeDialog):
            """Defines a dialog.
            """
            ID_FILENAME = 1000
            ID_MULTILINE = 1001
            ID_BUTTON = 1002
        
            ID_GROUP = 10000
        
            def CreateLayout(self) -> bool:
                """Called by Cinema 4D to populate the dialog with gadgets.
                """
                # Set the title of the dialog.
                self.SetTitle("My Dialog")
        
                # Open a container to put other elements into.
                self.GroupBegin(id=MyDialog.ID_GROUP, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=1)
                self.GroupSpace(spacex=5, spacey=5)
                
                # Add a CUSTOMGUI_FILENAME with the FILENAME_DIRECTORY flag set, i.e., it will let the user
                # select directories instead of files. The flags of a custom GUI must be written into a
                # BaseContainer as shown below. Most of the time the flag values are booleans (on/off), but
                # in some cases they can also be other data types as integers or strings. 
        
                # The flags container.
                flags: c4d.BaseContainer = c4d.BaseContainer()
                flags[c4d.FILENAME_DIRECTORY] = True
                # Add a CUSTOMGUI_FILENAME with our flags settings.
                self.AddCustomGui(id=MyDialog.ID_FILENAME, 
                                  pluginid=c4d.CUSTOMGUI_FILENAME, 
                                  name="Path", 
                                  flags=c4d.BFH_SCALEFIT, 
                                  minw=0, minh=0, 
                                  customdata=flags)
        
                # Add text box spanning multiple lines (that is set to read only mode), it will display the
                # output of GetFilesBelowDirectory() when the "Run" button is being pressed.
                self.AddMultiLineEditText(id=MyDialog.ID_MULTILINE, flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 
                                          inith=50, style=c4d.DR_MULTILINE_READONLY)
                # Add a button.
                self.AddButton(id=MyDialog.ID_BUTTON, flags=c4d.BFH_SCALEFIT, name="Run")
        
                # Close the lyaout group.
                self.GroupEnd()
                
                return super().CreateLayout()
        
            def InitValues(self) -> bool:
                """Called by Cinema 4D to initialize a layout.
                """
                self.SetFilename(MyDialog.ID_FILENAME, "")
                self.SetString(MyDialog.ID_MULTILINE, "")
                return super().InitValues()
        
        
            def Command(self, mid: int, msg: c4d.BaseContainer) -> bool:
                """Called by Cinema 4D when the user interacts with one of the gadgets in the dialog.
                """
                # The "Run" button has been pressed.
                if mid == MyDialog.ID_BUTTON:
                    # Put the files contained within the path stored under #ID_FILENAME in the the output
                    # field #ID_MULTILINE or display an error message when there is no valid path.
                    result = MyDialog.GetFilesBelowDirectory(self.GetFilename(MyDialog.ID_FILENAME))
                    if result:
                        self.SetString(MyDialog.ID_MULTILINE, result)
                    else:
                        c4d.gui.MessageDialog("Please select a directory first.")
                    
                return super().Command(mid, msg)
        
            @staticmethod
            def GetFilesBelowDirectory(path: str) -> typing.Optional[str]:
                """Returns a string which contains all files which are below the given #path.
        
                This function is not part of the #GeDialog interface and just a cheap example how one could
                utilize a FILENAME custom in directory mode.
                """
                # Validations that #path is an existing directory path string.
                if not isinstance(path, str) or path == "":
                    return None
                if not os.path.exists(path) or not os.path.isdir(path):
                    return None
        
                # The string into which all file paths are being put.
                result: str = ""
                # Iterate over all files which are in #path or one of its ancestor paths and put them into
                # #result.
                for root, _, files in os.walk(path):
                    for f in files:
                        result += os.path.join(root, f) + "\n"
        
                return result
        
        def main():
            """Opens the dialog.
            """
            # Instantiate the dialog.
            dialog = MyDialog()
            # Open the dialog in modal mode, i.e., it will be the only thing the user can focus on.
            dialog.Open(dlgtype=c4d.DLG_TYPE_MODAL_RESIZEABLE, defaultw=400, xpos=-2, ypos=-2)
        
        if __name__=='__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • P
          plc
          last edited by plc

          Hi @ferdinand ,
          Thanks a lot, that's exactly what I was looking for !

          flags: c4d.BaseContainer = c4d.BaseContainer()
                  flags[c4d.FILENAME_DIRECTORY] = True
                  # Add a CUSTOMGUI_FILENAME with our flags settings.
                  self.AddCustomGui(id=MyDialog.ID_FILENAME, 
                                    pluginid=c4d.CUSTOMGUI_FILENAME, 
                                    name="Path", 
                                    flags=c4d.BFH_SCALEFIT, 
                                    minw=0, minh=0, 
                                    customdata=flags)
          
          1 Reply Last reply Reply Quote 0
          • ferdinandF ferdinand referenced this topic on
          • First post
            Last post