Home Coding, Scripting, Shaders

Maya Python, inconsistent results when running script. [SOLVED]

polycounter lvl 13
Offline / Send Message
suspectlogic polycounter lvl 13
Hey guys, I'm curious if anyone has experienced this problem I'm having right now. I'm really novice to scripting, I'm hoping there's something really obvious that I'm missing. I'll try to start by explaining the new problem I'm having, and then the original thought process for my solution.

The new problem:
The script I made is behaving differently in 2 scenarios: When i use it on a file that's been opened via "file open" it behaves as intended. However, when I run it on a file that's been opened using "file > recent files" it doesn't work.

The original problem, solution & thought process:
I have many folders. Each folder contains a maya scene in a source folder and textures in an export folder. Since Maya stores texture paths as absolute strings, an asset wont display the texture until the path is fixed manually. My solution is to convert the path name to a relative value. This happens to work because most of the assets share the same hierarchy structure and naming conventions.

The main concern is there's a pretty big workflow limitation if I can't use this script on files opened via the recent menu. 

<pre class="CodeBlock"><code>import maya.cmds as cmds
fileList = []
fileList = cmds.ls(type = 'file')
localDir = '..\Export\\' # local texture path
for f in fileList:
    fPath = cmds.getAttr('%s.fileTextureName' %f) # full file path of texture 
    fileName = fPath.split('/') [-1] # file name with path stripped
    newPath = os.path.join(localDir, fileName) # injecting local path into the new path name.
    cmds.setAttr('%s.fileTextureName' %f,newPath, type = 'string') # set attributes for texture files
    print newPath

Replies

  • throttlekitty
    Offline / Send Message
    throttlekitty ngon master
    "Since Maya stores texture paths as absolute strings, an asset wont display the texture until the path is fixed manually"
    I'm getting hung up on this part foremost; if the file node has an absolute path, why wouldn't it display? (well on your computer, at least.) Totally get the need for relative paths, I'm guessing that os.path isn't getting something from maya for some reason when a Recent File is opened? Could troubleshoot it by adding some print lines to make sure that your path variables are working as expected.

    Alternatively, I don't think you need os.path for this, try this instead?

    cmds.setAttr('%s.fileTextureName' %f, ('../Export/' + fileName), type = 'string') # set attributes for texture files

  • suspectlogic
    Offline / Send Message
    suspectlogic polycounter lvl 13
    "Since Maya stores texture paths as absolute strings, an asset wont display the texture until the path is fixed manually"
    I'm getting hung up on this part foremost; if the file node has an absolute path, why wouldn't it display? (well on your computer, at least.) Totally get the need for relative paths, I'm guessing that os.path isn't getting something from maya for some reason when a Recent File is opened? Could troubleshoot it by adding some print lines to make sure that your path variables are working as expected.

    Alternatively, I don't think you need os.path for this, try this instead?

    cmds.setAttr('%s.fileTextureName' %f, ('../Export/' + fileName), type = 'string') # set attributes for texture files
    Thinking about it again, what I said here was incorrect... the issue isn't really that the paths are absolute, it is more that the maya files are pointing to the wrong paths.

    Thanks for your suggestion! I just tried it and it's giving me the same results. I realize now that using os module here wasn't necessary thought.

    here's what the line looked like with what you suggested.
    newPath = '..\\export\\' + str(fileName)
    here's the print result:
    <code>Brawler_BodyTexture.png
    ..\export\<code>Brawler_FaceTexture.png</code><code>..\export\
    Here's the the previous line:
    newPath = os.path.join(localDir, fileName)
    print statement from this line:
    </code><code>..\export\Brawler_BodyTexture.png<br>..\export\Brawler_FaceTexture.png
    The results are basically the same with both versions. The print statement is even the same when it fails on recently opened files :/








  • throttlekitty
    Offline / Send Message
    throttlekitty ngon master
    COOOOOOOL.

    So what happens when it fails though, do you get an error or what?

    And just to double check, your folders look something like this, right?
    x:\blah\blah\Scene1\scene
    x:\blah\blah\Scene1\export
    x:\blah\blah\Scene2\scene
    x:\blah\blah\Scene2\export

    edit: or is not working = texture not showing? If so, what happens if you save and reload that scene?
  • suspectlogic
    Offline / Send Message
    suspectlogic polycounter lvl 13
    There's no error when it fails. maya does indicates that the texture doesn't exist only after I select the node:


    Yes the folder structure you listed is close, here's a link to some files that imitate my current setup:
    https://drive.google.com/open?id=157YozPgMuK0qsgragKijY59qjDmZNZS7

    Yes, the textures were not showing in my case. I just tried to save the "not working" scene and reloaded it, and sure enough the textures showed up fine. Would you happen to know if there a way to around saving and reloading the scene?
  • throttlekitty
    Offline / Send Message
    throttlekitty ngon master
    The first thing I'd try is to reload the textures and see if that works. I have doubts though, since maya isn't recognizing the path of the current scene to handle how the file node paths to the texture file, which is why you're in this predicament to begin with. But I can't think of a way to force maya to say "oh yeah this file IS open".

    This whole time I've been 99% sure that any paths within a maya project are stored relative, even if they're not in their designated place, such as storing all textures under a common folder. Sheepishly, I never thought to ask if you had a project set and blindly assumed. My bad!

    So I downloaded your files and created a project with default settings, where I have c:\temp\New_Project\Assets\AssetA\Source\
    Your modified script works for me when using a Recent file. But what I actually get is this result, with no complaint and the textures showed immediately.
    assets\AssetA\Source\..\Export\AssetA_FaceTexture_Default.png

    So with that said, do you have a project set for this ...project and do your files all live here?
  • Panupat
    Offline / Send Message
    Panupat polycounter lvl 17
    I would do a os.path.isfile or exists to double check. As for splitting the path I would recommend using os.path.basename instead just so it covers both / and \ unless you run a replace before the check.
  • suspectlogic
    Offline / Send Message
    suspectlogic polycounter lvl 13
    @throttlekitty

    Yeah, it would be great to just shake Maya awake! I added a line for reloading textures and it's not really doing the trick yet. (shared hacky code at the bottom)

    You're also correct about setting the project, I'm was purposefully trying to avoid that. Hopefully that's not making things even more difficult? But it's also not working for me like you've mentioned so maybe I'm not doing something correct.

    I've found 2 sequences that if followed give me the broken results I'm talking about. 

    Sequence 1: File > Open> AssetA, File > Open > AssetB, File > Open AssetC. (Don't save or run script for these actions)
    Sequence 2: File > Recent File > AssetA, File > Recent File > AssetB, File > Recent File > AssetC. (Run the script for each of these actions)

     From my tests, only AssetC will be working with the script as intended, which is the most recently opened file from the first method...

    @Panupat
     I've added the changes you suggested. They don't seem to fix the current issue, but it's definitely a cleaner solution that should avoid problems later on. Thanks!



    # To Do: Make script work consistently for recently opened files | 
    import maya.cmds as cmds
    import os
    
    localDir = '..\\export\\'
    
    def reloadTextures():
        fileList = []
        fileList = cmds.ls(type = "file")
        for f in fileList: #this loop gets the attribute and then sets it again, kind of a hacky reload.
            textureFilePath = cmds.getAttr( f + ".fileTextureName")
            cmds.setAttr( f + ".fileTextureName", textureFilePath, type = "string")
            
    def updatePaths(*args):
        fileList = []
        fileList = cmds.ls(type = "file")
        for f in fileList:
            fPath = cmds.getAttr('%s.fileTextureName' %f) # full file path of texture 
            pathChecker = os.path.isfile(fPath)
            if pathChecker == True:
                print ('Path unchaged:\n' + str(fPath))
            else:
                fileName = os.path.basename(fPath)
                newPath = str(localDir) + str(fileName) # injecting local path into the new path name.
                cmds.setAttr('%s.fileTextureName' %f,newPath, type = 'string') # set attributes for texture files
                reloadTextures()
                newPath = cmds.getAttr('%s.fileTextureName' % f)
                print ('Path changed to:\n' + newPath)
    
    updatePaths()

  • throttlekitty
    Offline / Send Message
    throttlekitty ngon master
    Any reason you're avoiding using a project? They're quite handy, easy to set up, and should avoid the problem you're facing. You can remove practically everything from the locations section when setting one up if you want.

    At this point, I don't have any further insights on the Recent Files thing, sorry.
  • suspectlogic
    Offline / Send Message
    suspectlogic polycounter lvl 13
    I just have lots of assets created by artists who kept files in various structures. I was thinking I'd correct texture paths as I work on them, but I'm starting to think that batch correcting the paths might be the way to go. But setting each scene to a standard project will probably be a good start. Thanks for your help!
  • Panupat
    Offline / Send Message
    Panupat polycounter lvl 17
    I noticed you are calling reloadTexture() inside a for loop. Each time it got called it will loop through all file nodes to set texture path. It's doing that loop over and over which I don't think is what you intended? If you want a fail safe reload you could call that separately just once at the end.

    I may be wrong but I tried rewriting your code as I  understand. Take note the information I tried printing out to help pin point what went wrong.

    import os<br>from maya import cmds<br><br><br>localDir = '../export/'<br><br><br>def  reloadTexture():<br>    # do your stuff<br>    pass<br><br>def repathTexture():<br>    fileNodes = cmds.ls(type='file')  # no need to create empty list before this<br>    for eachNode in fileNodes:  # more meaningful variable name<br>        print 'Node name ->', %s<br>        sourcePath = cmds.getAttr('%s.fileTextureName' % eachNode)<br>        if not os.path.isfile(sourcePath):  # no need to print if it exists. We only care if it doesn't<br>            fileName = os.path.basename(sourcePath)<br>            newPath = localDir + fileName  # they are both string so no need str()<br>            print '    - repath: %s -> %s' % (sourcePath, newPath)<br>            cmds.setAttr('%s.fileTextureName' % eachNode, newPath, type='string')<br>            if cmds.getAttr('%s.fileTextureName' % eachNode) != newPath:<br>                print '    - ERROR: file node not getting new value. Current value: ', cmds.getAttr('%s.fileTextureName' % eachNode)<br>        print '---------------------------------------------'<br><br>repathTexture()<br>reloadTexture()<br><br>

    EDIT - my code not displaying correctly for some reason but click Quote and you'll see.



  • Panupat
    Offline / Send Message
    Panupat polycounter lvl 17
    mistake double reply
  • suspectlogic
    Offline / Send Message
    suspectlogic polycounter lvl 13
    @Panupat

    I pulled the texture reloading out of the for loop, definitely not my intention. I also applied some of your other suggestions to the best of my ability, I'm definitely not at a level to write well optimized scripts.....but I'm happy to say that after some more stumbling around I found a solution that works!

    The new solution is to use cmds.file() to query the current files location(this gives an absolute path fortunately), split the name up a couple lines and then point it to the export folder. now the script seems to be working a lot better. and a heck of a lot better than crossing my fingers and hoping that maya converts a relative string to an absolute one.
    import maya.cmds as cmds
    import os
    
    scenePath = cmds.file(q = True, sn = True)
    scenePath = os.path.split(scenePath)[0]
    expPath = os.path.split(scenePath)[0] + '/export/'
    
    def reloadTextures():
        fileNodes = cmds.ls(type = "file")
        for node in fileNodes:
            textureFilePath = cmds.getAttr( node + ".fileTextureName")
            cmds.setAttr( node + ".fileTextureName", textureFilePath, type = "string")
    
    def updatePaths(*args):
        fileNodes = cmds.ls(type = "file")
        for node in fileNodes:
            print ('Node name: %s' % node)
            sourcePath = cmds.getAttr('%s.fileTextureName' % node)
            if not os.path.isfile(sourcePath):
                fileName = os.path.basename(sourcePath)
                newPath = expPath + fileName # joining new export path with filename
                print ('Path Updated: %s -> %s' % (sourcePath, newPath))
                cmds.setAttr('%s.fileTextureName' % node, newPath, type = 'string')
                if cmds.getAttr('%s.fileTextureName' % node) != newPath:
                    print ('ERROR: file node not getting new value. Current value: ' + cmds.getAttr('%s.fileTextureName' % node))
                    print'---------------------------------------------'
    updatePaths()
    reloadTextures()

  • Panupat
    Offline / Send Message
    Panupat polycounter lvl 17
    Ah good thinking. Yes I wasn't sure about ../ in your original code.
Sign In or Register to comment.