Kari Noriy
Kari Noriy

Reputation: 61

How to create a progress window via Python in Maya?

I was wondering if there is a way to create a progress window that shows how long until the moment your program finishes. This is what I have so far, but it's not doing what I expect.

I want it to run until all the curves have been created and then stop.

import maya.cmds as cmds

cmds.setAttr( "nParticle1Shape.lifespanMode", 2 ) 
cmds.setAttr( "nParticle1Shape.maxCount", 5 ) 
cmds.setAttr( "nParticle1Shape.lifespanRandom", 3 )

allParticleDictionary = {}
minFrames = cmds.playbackOptions( q=True, min=True )
maxFrames = cmds.playbackOptions( q=True, max=True )

running = 0
amount = 0
cmds.progressWindow( title='Doing Nothing',progress=amount,status='Sleeping: 0%',isInterruptable=True )

for currentFrame in range(0, int(maxFrames)):
    cmds.currentTime( currentFrame, update=True, edit=True )
    cmds.select( 'nParticle1' )

    Particles = cmds.ls( sl=True, type='transform' )
    for part in Particles:
        for particleCount in range( 0,cmds.particle(part, q=True,ct=True) ):
            particleName = cmds.particle( part, q=True, order=particleCount, at='id' )
            particlesPosition = cmds.particle( part, q=True, order=particleCount, at='position' )
            particleDictionary = {}

            if str( particleName[0] ) in allParticleDictionary.keys():
                particleDictionary = allParticleDictionary[str(particleName[0])]

                particleDictionary[currentFrame] = particlesPosition
                allParticleDictionary[str(particleName[0])] = particleDictionary


    emptyFolder = cmds.group( em=True, n="Curves" )
    for curveParticleId in allParticleDictionary.keys():
        pointList = []
        sortedKeyFrameList = sorted( allParticleDictionary[curveParticleId].keys() )

        if len( sortedKeyFrameList ) > 1:
            for keyFrame in sortedKeyFrameList:
                pointList.append( allParticleDictionary[curveParticleId][keyFrame] )

                curveObj = cmds.curve( name = "pCurve" + str(curveParticleId ), p = pointList)
                Locators = cmds.spaceLocator( name = "locator" + str(curveParticleId) )
                cmds.pathAnimation( Locators, stu=sortedKeyFrameList[0], etu=sortedKeyFrameList[-1], c=curveObj )       

                running = 1
                # place all objects in Group called Curves 
                cmds.parent( curveObj, emptyFolder )
                cmds.select( Locators )
                cmds.delete()

            while running == 0:

                # Checks if the dialog has been cancelled
                if cmds.progressWindow( query=True, isCancelled=True ) :
                    break

                # Checks if end condition has been reached
                if cmds.progressWindow( query=True, progress=True ) >= 100 :
                    break

                amount += 20
                cmds.progressWindow( edit=True, progress=amount, status=('Sleeping: ' + `amount` + '%' ) )
                cmds.pause( seconds=1 )
                cmds.progressWindow( endProgress=1 )

Upvotes: 2

Views: 3362

Answers (1)

mapofemergence
mapofemergence

Reputation: 458

The preface is that I'm not entirely sure I got your logic right: as @theodox said, your indents seem wrong even after your latest correction.

Anyways, here's a version which should do you what you aim to, if I interpreted it right:

from maya import cmds

def checkProgressEscape():
    # check if dialog has been cancelled
    cancelled = cmds.progressWindow(query=True, isCancelled=True)
    if cancelled:
        cmds.progressWindow(endProgress=1)
    return cancelled

def getAllParticlesDictionary(shape):
    cmds.progressWindow(title='Fetch Particle Data', progress=0, status='Fetched: 0%', isInterruptable=True)

    ptcsDict = {}
    minFrames = int(cmds.playbackOptions( q=True, min=True))
    maxFrames = int(cmds.playbackOptions( q=True, max=True))
    nFrames = float(maxFrames - minFrames)
    # better use the actual range, since you queried the playback options
    for currentFrame in range(minFrames, maxFrames):
        if checkProgressEscape():
            return
        amount = 100.0 / nFrames * (currentFrame - minFrames + 1)
        cmds.progressWindow(edit=True, progress=amount, status='Fetched: %d%%' % amount)
        # cmds.pause(seconds=.001)

        cmds.currentTime(currentFrame, update=True, edit=True)
        for particleCount in xrange(cmds.particle(shape, q=True, ct=True)):
            particleId = int(cmds.particle(shape, q=True, order=particleCount, at='id')[0])
            # better check the key exists first, then add your logic
            if particleId not in ptcsDict.keys():
                ptcsDict[particleId] = {}                    
            ptcsDict[particleId][currentFrame] = cmds.particle(shape, q=True, order=particleCount, at='position')

    cmds.progressWindow(endProgress=1)
    return ptcsDict

def curvesFromParticle(ptcsDict):
    cmds.progressWindow(title='Build Curves', progress=0, status='Building Curves: 0%', isInterruptable=True)
    # this section had to be de-indented
    emptyFolder = cmds.group(em=True, n="Curves")
    ptcCount = len(ptcsDict)
    for i, (curveParticleId, data) in enumerate(ptcsDict.iteritems()):
        if checkProgressEscape():
            return

        sortedKeyFrameList = sorted(data.keys())
        pointList = []
        for keyFrame in sortedKeyFrameList:
            pointList.append(ptcsDict[curveParticleId][keyFrame])

        curveObj = cmds.curve(name="pCurve%d" % curveParticleId, p=pointList)
        locators = cmds.spaceLocator( name = "locator%d" % curveParticleId)
        cmds.pathAnimation( locators, stu=sortedKeyFrameList[0], etu=sortedKeyFrameList[-1], c=curveObj)       
        # place all objects in Group called Curves 
        cmds.parent(curveObj, emptyFolder)
        cmds.delete(locators)

        amount = 100 / ptcCount * float(i)
        cmds.progressWindow(edit=True, progress=amount, status='Building Curves: %d%%' % amount)
    cmds.progressWindow(endProgress=1)


ptcs = cmds.ls(typ='nParticle')
for shape in ptcs:
    cmds.setAttr("%s.lifespanMode" % (shape, ), 2)
    cmds.setAttr("%s.maxCount" % (shape, ), 100)
    cmds.setAttr("%s.lifespanRandom" % (shape, ), 3)
    ptcDict = getAllParticlesDictionary(shape)
    if not ptcDict:
        print 'data was not be fetched for %s; skipped' % (shape, )
        continue
    curvesFromParticle(ptcDict)

Above is a (slightly) modified version of your script; as you can see:

  • I moved the main blocks of logic to functions (just for readability)
  • I removed your while statements as they're not required by the progress bar: you're already looping in a for statement
  • I also removed your progress check, if equal or greater than 100, because once you're out of your loop you can assume you don't need to show the progress, anymore, and just call the sidebar command with the endProgress argument; you still need checking if the user escaped the process, though
  • I added two progress bar "sessions", one for the loop fetching the particles' data and populating your dictionary, one for building the curves

On a side note, you might want to know (if you don't already) that maya is mostly single threaded. When using the API, you might work around some limitations but, generally, when using maya cmds you can be pretty sure you're working on a single thread.

This means the progress bar will be running on the same thread where your logic runs, hence limiting the performance of your script by several orders of magnitude: I've been doing some extensive work with particles + progress bars and I found that drawing the UI can be much slower than just running your logic. As the number of particles gets bigger, your script can become horribly slow, just because of your progress bar refreshing, not because of the logic itself.

A good practice is to update the progress only at known intervals (read: every n particles). Let's say you have 1000000 particles and you want to update the sidebar only by 5% steps, you would write something like this:

import random

cmds.progressWindow(title='Stepped Progress Bar', progress=0, status='Progress (stepped by 1): 0%%', isInterruptable=True)
limit = 1000000
step = 5
for i in xrange(limit):
    progress = 100.0 / limit * i
    if progress % step:
        continue
    cmds.progressWindow(e=1, progress=progress, status='Progress (stepped by 1): %d%%' % progress)
    # the following is to simulate some processing time
    cmds.pause(seconds=random.random() * 2)
cmds.progressWindow(endProgress=1)

Upvotes: 1

Related Questions