Interactive/PPTFromShotsCustom.py

##
## (C) Copyright 2012 Autodesk, Inc.  All rights reserved.
##
## Use of this software is subject to the terms of the Autodesk license
## agreement provided at the time of installation or download, or which
## otherwise accompanies this software in either electronic or hard copy
## form.
##

# Create a PowerPoint presentation from a template and all the shots.

# These need to be modified:
TheResultDirectory = "C:\\ShowcasePowerPointResults"
TheResultFile = "ShowcaseResult.pptx"
TheTemplateFile = "C:\\showcasetemplate.pptx"

# Movie attributes
TheMovieFramesPerSecond = 4
TheMovieCompression = ('Microsoft Video 1',7500)
TheMovieExtension = "avi"

# Raytracing (True) or not (False).  It will use the
# scene parameters.
TheUseRaytracing = False

# What anti-aliasing quality (0 - 9)
TheAALevel = 4

__all__ = ['PPTFromShotsCustom', 'instantiate']

from MessageInterpreter  import MessageInterpreter
from ScriptRunner        import ScriptRunner
from UserCustomization   import UserCustomBase, CustomInfo
from Utilities           import CreateDirectory, RemoveFile
from RunInUiThread       import RunInUiThread, RunInUiThreadBlock

import PowerPoint
import os.path
import ShotIO
import math

# ------------------------------------------------------------------------

class BaseClass(ScriptRunner):
    def __init__( self, scriptName, args ):
        ScriptRunner.__init__( self, scriptName, args )
        self.mySizes = None
        self.myContactSize = None
        self.myContactSizes = None
        self.myItems = None


    def Main( self ):
        if not os.path.isdir(TheResultDirectory):
            CreateDirectory(TheResultDirectory)
        self.sendMessageSync( 'STATUSBAR_SET_TEXT',
                              (("Using template %s" % (TheTemplateFile)),))
        self.process(TheResultDirectory,TheResultFile,"images",False)
        RunInUiThreadBlock(self._collectSizes)
        self.process(TheResultDirectory,TheResultFile,"images",True)
        if self.myItems:
            RunInUiThread(self.presentation)


    def movie( self, width, height, file, name ):
        self.sendMessageSync( 'SAVE_SHOT_AS_MOVIE_AT_RESOLUTION',
                              (width+width%2,
                               height+height%2,
                               TheMovieFramesPerSecond, 0,
                               TheAALevel, False,
                               file, (name,),
                               TheMovieCompression,
                               (None,None,("","",False))),
                              userConditions="SHOT_PLAYING_DONE",
                              userTimeout=3600)


    def position(self,shot):
        (navState,dispState) = shot.do_step(1.0)
        fov = math.degrees(dispState.getFOV())
        self.sendMessageSync('NAVIGATION_SET_VIEW',(navState,))
        self.sendMessageSync('NAVIGATION_SET_COI',(navState.getCOI(),))
        self.sendMessageSync('DISPLAY_SET_VERTICAL_FOV',(fov,False))
        return (navState,dispState)


    def image( self, width, height, file ):
        self.sendMessageSync('SAVE_SCREENSHOT_AT_RESOLUTION',
                             (file,width,height,
                              3, TheAALevel, False,))

    def _collectSizes(self):
        if self.myItems:
            self.announce("Template %s" % (TheTemplateFile))
            p = PowerPoint.Presentation(TheTemplateFile)
            p.setItems(self.myItems)
            css = p.contactShapeSize()
            if css:
                if css[0]:
                    self.myContactSize = (css[1],css[2])
                else:
                    self.myContactSizes = css[1]
            self.mySizes = p.assemble(False)
            self.announce("Sizes %s and %s and %s" % (str(self.myContactSize),
                                                      str(self.myContactSizes),
                                                      str(self.mySizes)))
            p.closeTemplate()
            p.reset()

    def presentation(self):
        if self.myItems:
            p = PowerPoint.Presentation(TheTemplateFile)
            p.setItems(self.myItems)
            p.setDestination(os.path.join(TheResultDirectory,TheResultFile))
            p.contact(0.25)
            p.assemble(True)
            p.closeTemplate()
            p.saveDestination()
            #p.closeDestination()
            #p.reset()

            for toremove in p.myConsumed:
                RemoveFile(toremove)


    def announce( self, m ):
        print m
        self.sendMessage( 'STATUSBAR_SET_TEXT', (m,) )



class SynchronizedScriptShots( BaseClass ):
    def __init__( self, scriptName, args ):
        BaseClass.__init__( self, scriptName, args )


    def process(self, imageDir, imageFile, imageRoot, create):
        # Using TheImageHeight, TheImageWidth, TheImageExtension, TheAALevel

        self.myItems = []

        if self.document().has(ShotIO.id):
            defaultWidth = 512
            defaultHeight = 512

            dashPresentDir = os.path.join(imageDir, "%s-presentation" % (os.path.splitext(imageFile)[0]))
            if not os.path.isdir(dashPresentDir):
                CreateDirectory(dashPresentDir)

            self.announce( "Shots on %s in %s" % (imageRoot, imageDir) )
            cntr = 0

            for s in self.document().get(ShotIO.id).shots:
                shotId = s.getName()
                shotLabel = s.getLabel()
                if self.mySizes:
                    try:
                        ss = self.mySizes[shotId]
                        shotWidth = int(ss[0])
                        shotHeight = int(ss[1])
                    except:
                        shotWidth = int(defaultWidth)
                        shotHeight = int(defaultHeight)
                else:
                    shotWidth = int(defaultWidth)
                    shotHeight = int(defaultHeight)

                if self.myContactSize:
                    thumbWidth = int(self.myContactSize[0])
                    thumbHeight = int(self.myContactSize[1])
                elif self.myContactSizes and cntr < len(self.myContactSizes):
                    cs = self.myContactSizes[cntr]
                    thumbWidth = int(cs[0])
                    thumbHeight = int(cs[1])
                else:
                    (thumbWidth,thumbHeight) = (defaultWidth,defaultHeight)

                cntr += 1
                fThumb = os.path.join( dashPresentDir, "%s_thumbnail_%s.%s" % (imageRoot,
                                                                               s.getName(),
                                                                               "jpg") )
                if s.getShotType() == "ShotStill":
                    self.announce("Processing image from %s (%s)" % (s.getLabel(),s.getName()))
                    fImage = os.path.join( dashPresentDir, "%s_%s.%s" % (imageRoot,
                                                                         s.getName(),
                                                                         "jpg") )
                    shotMedia = fImage
                    shotMediaType = 'image'

                    if create:
                        self.announce( "Positioning %s" % (s.getLabel()) )
                        (navState,dispState) = self.position(s)
                        self.announce( "Saving image of shot %s (%s) to %s at (%d,%d) and (%d,%d)" % \
                                       (s.getLabel(),s.getName(),shotMedia,
                                        shotWidth,shotHeight,
                                        thumbWidth,thumbHeight) )
                        self.image(shotWidth, shotHeight, fImage)
                        self.image(thumbWidth, thumbHeight, fThumb,)

                    else:
                        pk = s.getParameters()['Keyframe']
                        navState = pk[0]
                        dispState = pk[1]

                    pos = navState.getPosition()
                    la = navState.getLookAt()

                    shotTitle = shotLabel
                    shotThumbnail = fThumb

                    shotNotes = _("  Resolution %dx%d\n") % (shotWidth, shotHeight)

                    showcaseItem = PowerPoint.ShowcaseItem(shotId,shotLabel,shotNotes,
                                                           shotTitle,shotMedia,shotMediaType,
                                                           shotWidth,shotHeight,shotThumbnail)
                    self.myItems.append(showcaseItem)
                else:
                    # Do the movie...
                    self.announce("Processing movie from %s (%s)" % (s.getLabel(),s.getName()))

                    # Movie message is different - it appends .avi on its own
                    fMovie = os.path.join( dashPresentDir, "%s_%s" % (imageRoot,s.getName()) )
                    shotMedia = "%s.%s" % (fMovie,TheMovieExtension)
                    shotMediaType = 'movie'

                    if create:
                        self.announce( "Positioning %s" % (s.getLabel()) )
                        self.announce( "Saving movie of shot %s (%s) to %s at (%d,%d) and (%d,%d)" % \
                                       (s.getLabel(),s.getName(),shotMedia,
                                        shotWidth,shotHeight,
                                        thumbWidth,thumbHeight) )
                        self.movie(shotWidth, shotHeight, fMovie, s.getName())
                        self.image(thumbWidth, thumbHeight, fThumb)

                    shotTitle = shotLabel
                    shotThumbnail = fThumb

                    pk = s.getParameters()['Keyframe']
                    navState = pk[0]
                    dispState = pk[1]
                    pos = navState.getPosition()
                    la = navState.getLookAt()

                    shotNotes = _("  Resolution %dx%d\n") % (shotWidth, shotHeight)
                    showcaseItem = PowerPoint.ShowcaseItem(shotId,shotLabel,shotNotes,
                                                           shotTitle,shotMedia,shotMediaType,
                                                           shotWidth,shotHeight,shotThumbnail)
                    self.myItems.append(showcaseItem)


# -----------------------------------------------------------------------

class PPTFromShotsBatch:
    def instantiate( self, scriptName, args ):
        return SynchronizedScriptShots( scriptName, args )

class PPTFromSlidesBatch:
    def instantiate( self, scriptName, args ):
        return SynchronizedScriptSlides( scriptName, args )

# -----------------------------------------------------------------------

class PPTFromShotsCustom (UserCustomBase):
    def __init__(self):
        self.myCallMenu = None
        self.myCallId = None
        self.myInterpreter = LocalInterpreter()

    def getInterpreter(self,isInteractive):
        if isInteractive:
            return self.myInterpreter
        return None

    def appendMenuItems(self,id,menu):
        if "File" == id:
            self.myCallMenu = menu
            self.myCallId = menu.appendItem(_( 'Shots as PowerPoint' ),
                                              self.__onPPTFromShots )
            self.myCallId = menu.appendItem(_( 'Slides as PowerPoint' ),
                                              self.__onPPTFromSlides )


    def enableMenuStates(self,id,enableStates):
        if "File" == id:
            enableStates[self.myCallId] = True

    # ------------------------------------------------
    def __onPPTFromShots( self, event ):
        self.myInterpreter.PPTFromShots( event )

    def __onPPTFromSlides( self, event ):
        self.myInterpreter.PPTFromSlides( event )


# -----------------------------------------------------------------------

class LocalInterpreter( MessageInterpreter ):

    def __init__( self ):
        MessageInterpreter.__init__( self )

    def PPTFromShots( self, event ):
        module = PPTFromShotsBatch()
        self.sendMessage( "EXECUTE_MODULE", ("PPTExportShots", module) )

    def PPTFromSlides( self, event ):
        module = PPTFromSlidesBatch()
        self.sendMessage( "EXECUTE_MODULE", ("PPTExportSlides", module) )



# --------------------------------------------------------------------

def instantiate():
    return PPTFromShotsCustom()


def info():
    customInfo = CustomInfo()
    customInfo.vendor = 'Autodesk'
    customInfo.version = '1.0'
    customInfo.api = '2013'
    customInfo.shortInfo = "PowerPoint presentation from Showcase shots."
    customInfo.longInfo = \
"""This only works if the PowerPoint template for Showcase exists in %s.  The PowerPoint will be saved in %s, as will all the original images.  In general, each still shot will produce an image.
""" % (TheTemplateFile,os.path.join(TheResultDirectory,TheResultFile))
    return customInfo