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

    Texture renaming

    Cinema 4D SDK
    python
    4
    16
    2.2k
    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.
    • M
      moko
      last edited by

      Thanks it works like expected, now i would like to add a smal GUI but i get stuck, how do i bring the name from the GUI in as prefix.

      import c4d
      from c4d import gui
      import os
      
      #GUI
      GROUP = 1000
      TEXT = 1001
      NAME = 1010
      
      class userDialog(gui.GeDialog):
          def CreateLayout(self):
              self.SetTitle("Add Prefix")
              self.GroupBegin(GROUP, c4d.BFH_SCALEFIT, 3,5, title = "AAdds a prefix to the texturename")
              self.GroupBorder(c4d.BORDER_GROUP_IN)
              self.GroupBorderSpace(20, 5, 20 , 5)
              self.AddStaticText(TEXT, c4d.BFH_SCALEFIT, name="Prefix")
              self.AddEditText(NAME, c4d.BFH_RIGHT, 200, 0, 0)
      
      # Adds a prefix to the texturename
      def changeTexture(shader) :
      
          prefix = "Tex_"
      
          # shader ID
          texturePath = shader[c4d.BITMAPSHADER_FILENAME]
          # split aboslute path
          oldTexturename = os.path.split(texturePath)
          # add prefix
          newTexturename = prefix + oldTexturename[1]
      
          doc = shader.GetDocument()
          doc.AddUndo(c4d.UNDOTYPE_CHANGE, shader)
      
          # Assign the new value
          shader[c4d.BITMAPSHADER_FILENAME] = os.path.join(oldTexturename[0], newTexturename)
          shader.Message(c4d.MSG_UPDATE)
      
      # Iterate a hierarchy
      def recurse_hierarchy(shader) :
          while shader:
              # Check if it's a c4d shader
              if c4d.BaseShader() :
                  changeTexture(shader)
              recurse_hierarchy(shader.GetDown())
              shader = shader.GetNext()
      
      # Main function
      def main() :
          mats = doc.GetActiveMaterials()
      
          doc.StartUndo()
          # Iterate over selected material
          for mat in mats:
              recurse_hierarchy(mat.GetFirstShader())
          doc.EndUndo()
      
      def main():
          dialog = userDialog()
          dialog.Open(c4d.DLG_TYPE_MODAL, defaultw=200, defaulth=50)
          c4d.EventAdd()
      
      # Execute main()
      if __name__=='__main__':
          main()
      

      thx for help
      mok

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

        Hey, you have two

        def main():
        

        in your code 🤔

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

          Hi,

          this also probably is not what you want:

          # Check if it's a c4d shader
          if c4d.BaseShader() :
              changeTexture(shader)
          

          It will always evaluate as True since an object evaluates as True. You probably meant instead:

          # Test if the current node is a bitmap shader.
          if shader.CheckType(c4d.Xbitmap) :
              changeTexture(shader)
          

          Cheers,
          zipit

          MAXON SDK Specialist
          developers.maxon.net

          1 Reply Last reply Reply Quote 0
          • M
            moko
            last edited by

            Thank you for both inputs. Im not used to programming and copy and paste the lines. Thanks for the help!

            1 Reply Last reply Reply Quote 0
            • r_giganteR
              r_gigante
              last edited by

              Hi @moko, nice seeing your progresses.

              With regard to adding a small GUI to return a specific string I warmly recommend having a look at c4d.gui.InputDialog.
              Finally a consideration on copy and paste which looks always pretty attractive to reduce time to market: be aware that it's indeed the opposite where in the end you'll obtain a code you can manage no more. Spending time in investigating the basics and getting yourself comfortable with the code is always a rewarding time.

              Best, R

              1 Reply Last reply Reply Quote 1
              • M
                moko
                last edited by

                Thank you very much, right now i am quite happy how it works. only thing is the dialog box opens for each shader because its in the "changeshader" function wich repeats.
                I d' like to have one gui-call at the start in the main function, but if i do that theni cant transport the "prefix" argument to the changeTexture(shader) function.

                about the time investement i agree, but for this script was more an out of desperation approach in how cinema4d lacks in organizing textures, then a new grind in passion. the script helps me with a hack, to organize them on OS side, for team projects like for instance unity.

                Best
                Moko

                import c4d
                from c4d import gui
                import os
                
                #GUI
                GROUP = 1000
                TEXT = 1001
                PREFIX = 1002
                BTN_OK = 1010
                BTN_CANCEL = 1020
                
                class userDialog(gui.GeDialog):
                    def CreateLayout(self):
                        self.SetTitle("Add Prefix")
                        self.GroupBegin(GROUP, c4d.BFH_SCALEFIT, 3,5, title = "Adds a prefix to the texturename")
                        self.GroupBorder(c4d.BORDER_GROUP_IN)
                        self.GroupBorderSpace(20, 5, 20 , 5)
                        self.AddStaticText(TEXT, c4d.BFH_SCALEFIT, name="Prefix")
                        self.AddEditText(PREFIX, c4d.BFH_SCALEFIT)
                        self.AddButton(BTN_OK, c4d.BFH_SCALE, name="Ok")
                        self.AddButton(BTN_CANCEL, c4d.BFH_SCALE, name="Cancel")
                        self.GroupEnd()
                        return True
                
                    def InitValues(self):
                        #initiate the gadgets with values
                        self.SetString(PREFIX, "Tex_")
                        return True
                
                    def Command(self, id, msg):
                        #handle user input
                        if id==BTN_CANCEL:
                          self.Close()
                        elif id==BTN_OK:
                          self.TexturPrefix = self.GetString(PREFIX)
                          self.Close()
                        return True
                
                
                # Adds a prefix to the texturename
                def changeTexture(shader) :
                        
                    dialog = userDialog()
                    dialog.Open(c4d.DLG_TYPE_MODAL, defaultw=200, defaulth=50)
                    
                    prefix = dialog.TexturPrefix
                    print (prefix)
                    
                    # shader ID
                    texturePath = shader[c4d.BITMAPSHADER_FILENAME]
                    # split aboslute path
                    oldTexturename = os.path.split(texturePath)
                    # add prefix
                    newTexturename = prefix + oldTexturename[1]
                
                    doc = shader.GetDocument()
                    doc.AddUndo(c4d.UNDOTYPE_CHANGE, shader)
                
                    # Assign the new value
                    shader[c4d.BITMAPSHADER_FILENAME] = os.path.join(oldTexturename[0], newTexturename)
                    shader.Message(c4d.MSG_UPDATE)
                
                
                # Iterate a hierarchy
                def recurse_hierarchy(shader) :
                    while shader:
                        # Test if the current node is a bitmap shader.
                        if shader.CheckType(c4d.Xbitmap) :
                            changeTexture(shader)
                        recurse_hierarchy(shader.GetDown())
                        shader = shader.GetNext()
                
                # Main function
                def main() :
                    mats = doc.GetActiveMaterials()
                    
                    doc.StartUndo()
                    # Iterate over selected material
                    for mat in mats:
                        recurse_hierarchy(mat.GetFirstShader())
                    doc.EndUndo()
                
                
                # Execute main()
                if __name__=='__main__':
                    main()
                
                1 Reply Last reply Reply Quote 1
                • M
                  moko
                  last edited by

                  Now it works as intended. You can select the materials for which you want to change the prefix of the texturename. The actual name of the texture is not changed. You need to do this with a renaming-tool at the OS level.

                  thanks to everyone

                  import c4d
                  from c4d import gui
                  import os
                  
                  #GUI
                  GROUP = 1000
                  TEXT = 1001
                  PREFIX = 1002
                  BTN_OK = 1010
                  BTN_CANCEL = 1020
                  
                  prefix = "placeholder"
                  
                  class userDialog(gui.GeDialog):
                      def CreateLayout(self):
                          self.SetTitle("Add Prefix")
                          self.GroupBegin(GROUP, c4d.BFH_SCALEFIT, 3,5, title = "Adds a prefix to the texturename")
                          self.GroupBorder(c4d.BORDER_GROUP_IN)
                          self.GroupBorderSpace(20, 5, 20 , 5)
                          self.AddStaticText(TEXT, c4d.BFH_SCALEFIT, name="Prefix")
                          self.AddEditText(PREFIX, c4d.BFH_SCALEFIT)
                          self.AddButton(BTN_OK, c4d.BFH_SCALE, name="Ok")
                          self.AddButton(BTN_CANCEL, c4d.BFH_SCALE, name="Cancel")
                          self.GroupEnd()
                          return True
                  
                      def InitValues(self):
                          #initiate the gadgets with values
                          self.SetString(PREFIX, "Tex_")
                          return True
                  
                      def Command(self, id, msg):
                          #handle user input
                          if id==BTN_CANCEL:
                            self.Close()
                          elif id==BTN_OK:
                            self.TexturPrefix = self.GetString(PREFIX)
                            self.Close()
                          return True
                  
                  
                  # Adds a prefix to the texturename
                  def changeTexture(shader) :
                  
                  
                      print (prefix)
                  
                      # shader ID
                      texturePath = shader[c4d.BITMAPSHADER_FILENAME]
                      # split aboslute path
                      oldTexturename = os.path.split(texturePath)
                      # add prefix
                      newTexturename = prefix + oldTexturename[1]
                  
                      doc = shader.GetDocument()
                      doc.AddUndo(c4d.UNDOTYPE_CHANGE, shader)
                  
                      # Assign the new value
                      shader[c4d.BITMAPSHADER_FILENAME] = os.path.join(oldTexturename[0], newTexturename)
                      shader.Message(c4d.MSG_UPDATE)
                  
                  
                  # Iterate a hierarchy
                  def recurse_hierarchy(shader) :
                      while shader:
                          # Test if the current node is a bitmap shader.
                          if shader.CheckType(c4d.Xbitmap) :
                              changeTexture(shader)
                          recurse_hierarchy(shader.GetDown())
                          shader = shader.GetNext()
                  
                  # Main function
                  def main() :
                      mats = doc.GetActiveMaterials()
                  
                      dialog = userDialog()
                      dialog.Open(c4d.DLG_TYPE_MODAL, defaultw=200, defaulth=50)
                  
                      global prefix
                      prefix = dialog.TexturPrefix
                  
                      doc.StartUndo()
                      # Iterate over selected material
                      for mat in mats:
                          recurse_hierarchy(mat.GetFirstShader())
                      doc.EndUndo()
                  
                  
                  # Execute main()
                  if __name__=='__main__':
                      main()
                  
                  ferdinandF 1 Reply Last reply Reply Quote 1
                  • ferdinandF
                    ferdinand @moko
                    last edited by

                    @moko said in Texture renaming:

                    The actual name of the texture is not changed. You need to do this with a renaming-tool at the OS level.

                    You can do this with os.rename() or with open if you want to save a copy. You probably also do not need your own GeDialog since yours does exactly what c4d.gui.RenameDialog does.

                    Cheers,
                    zipit

                    MAXON SDK Specialist
                    developers.maxon.net

                    M 1 Reply Last reply Reply Quote 0
                    • M
                      moko @ferdinand
                      last edited by

                      @zipit Thank you! That was exactly i was looking for 🙂

                      here the new Version:
                      I throw out the undo function because i dont know if it can adress the os part. Is this possible to have an undo on os.rename?

                      import c4d
                      from c4d import gui
                      import os, sys
                      
                      prefix="placeholder"
                      
                      # Adds a prefix to the texturename
                      def changeTexture(shader):
                      
                          # shader ID
                          texturePath = shader[c4d.BITMAPSHADER_FILENAME]
                      
                          # split aboslute path
                          oldTexturename = os.path.split(texturePath)
                      
                          # add prefix to
                          newTexturename = prefix + oldTexturename[1]
                          print (newTexturename)
                      
                          newTexturePath = os.path.join(oldTexturename[0], newTexturename)
                          print (newTexturePath)
                      
                          # rename texture
                      
                          try : 
                              os.rename(texturePath, newTexturePath)
                              print("Source path renamed to destination path successfully.") 
                      
                          except OSError as error: 
                              print(error) 
                      
                          # Assign the new value
                          shader[c4d.BITMAPSHADER_FILENAME] = newTexturePath
                          shader.Message(c4d.MSG_UPDATE)
                      
                      
                      # Iterate a hierarchy
                      def recurse_hierarchy(shader) :
                          while shader:
                              # Test if the current node is a bitmap shader.
                              if shader.CheckType(c4d.Xbitmap) :
                                  changeTexture(shader)
                              recurse_hierarchy(shader.GetDown())
                              shader = shader.GetNext()
                      
                      # Main function
                      def main() :
                          mats = doc.GetActiveMaterials()
                      
                          global prefix
                          prefix = c4d.gui.RenameDialog("Prefix_")
                          print (prefix)
                      
                          # Iterate over selected material
                          for mat in mats:
                              recurse_hierarchy(mat.GetFirstShader())
                      
                      # Execute main()
                      if __name__=='__main__':
                          main()
                      

                      Cheers,
                      moko

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

                        Hi,

                        @moko said in Texture renaming:

                        I throw out the undo function because i dont know if it can adress the os part. Is this possible to have an undo on os.rename?

                        No, there is no Undo for os.rename. You could hook into the Undo APIs provided by Windows and MacOS, but that would be probably overly complicated and also very unsafe.

                        The easiest way to implement this, would be to create a dictionary of old_file_path and new_file_path key-value pairs and write to it every time you rename something. With that you could unwind renaming operations.

                        There are however multiple problems with this.

                        1. If I am not mistaken, your script has no safe-guard against the user renaming a texture multiple times and therefor rendering previous references invalid. This is a problem on its own, but it would also defeat the mentioned naive implementation of an Undo stack with a dictionary.
                        2. This would also require a persistently running piece of code, where you could store that dictionary. The script manager script and all its states just vanish once it has been executed. You would have at least provide a asynchronous dialog that can stay open until the user decides that he/she is done with the renaming (and undoing). Or a full blown plugin if you do not want to have a dialog dangling around. You could technically store your Undo data somewhere else, like for example in the scene or in the running Python instance, but this would fall into the domain of "hacks".
                        3. Also implementing a proper undo stack for renaming operations is not as trivial as it might seem, because you would also have to keep track of all modifications made to the tracked files, including these that are not made by you, and properly react to them, if you do not want the Undo feature causing potentially critical conflicts.

                        So all in all not really worth the hassle.

                        Cheers,
                        zipit

                        MAXON SDK Specialist
                        developers.maxon.net

                        M 1 Reply Last reply Reply Quote 0
                        • M
                          moko @ferdinand
                          last edited by

                          @zipit

                          hm yes this sounds too complicated overall, particularly point 3 there could so much happen on the os side.
                          A gui with more options like renaming the hole texturename or a replace option of certain words could be the way forward. Instead of undo it would be easy then to rename the texture with he old name etc.
                          for now im happy how this works and let this solved.

                          Much Thanks!
                          Cheers,
                          moko

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