Texture renaming
-
Hi, i am looking for Texture renaming scripts and found this old topic
https://developers.maxon.net/forum/topic/10897/14346_textures-renamer-script
unforgently i have no knowledge in code.. my question, has someone the time to modify it, so it works with a normal cinema4d bitmapshader instead of vray?
thanks alot,
mk -
Hi @moko , thanks for reaching out us.
With regard to your request, please consider that all the information to adapt the code are already included in the thread you've pointed to.
Please also consider that being provided with the ID of a specific parameter is done by dragging and dropping that parameter string directly into the Python Console
Feel free to come back with any further question about development issue, but for time being, I warmly recommend you to give yourself a try since with this information you can easily find your way.
Lsat but not least, for the future be sure to make use of the tagging system when posting a new topic and to select the proper category.
Best, R
-
Thanks alot it works! but the material iteration posts an error
def main() :
mats = doc.GetActiveMaterials()doc.StartUndo() # Iterate over selected material for mat in mats: recurse_hierarchy(mat.GetFirstShader()) doc.Endndo()
it didnt find doc.Endndo() is there a way to iterate over the materials?
my second question:
now the script replaces A with B. Is it possible to "add" like a prefix?
I have alot of textures and would like to add a "Tex_" as a prefix, so its easier to sort/find them.
Like "TrainStation.jpg" to "Tex_TrainStation.jpg"I found this but it need the name of a object or material:
https://developers.maxon.net/forum/topic/6085Thanks for any suggestions and maybe someone need this aswell
cheers moko -
doc.Endndo()
is nonsense.Do you mean EndUndo()?
-
Thanks @PluginStudent for stepping in.
With regard to the rest of the question, I recommend having a look at how Python treats strings or, on a broader scope, at Python for novices.
Best, R
-
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 -
Hey, you have two
def main():
in your code
-
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 asTrue
. You probably meant instead:# Test if the current node is a bitmap shader. if shader.CheckType(c4d.Xbitmap) : changeTexture(shader)
Cheers,
zipit -
Thank you for both inputs. Im not used to programming and copy and paste the lines. Thanks for the help!
-
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
-
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
Mokoimport 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()
-
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()
-
@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 withopen
if you want to save a copy. You probably also do not need your ownGeDialog
since yours does exactly whatc4d.gui.RenameDialog
does.Cheers,
zipit -
@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 -
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
andnew_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.
- 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.
- 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".
- 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 -
@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