Python Tools - Session 13A
Scene Archiver and Ring of Fire Final
2020.04.07 - Session 13A
Session Navigation
Overview
This session covers two topics. First, we complete the Ring of Fire tool by adding keyframe animation - the hoop now translates and rotates over the timeline, and we fix CV iteration to only use editable CVs. Second, we begin building a Scene Archiver tool that uses Python's os.path module to manipulate file paths, extract scene names, and create directory structures for archiving Maya scenes.
Key Concepts
- Editable CV iteration - Use
numCVs(editableOnly=True)to avoid extra CVs from NURBS circles - Keyframe animation - Set keyframes at start, middle, and end of timeline programmatically
- Timeline manipulation - Query and set playback options via
pm.playbackOptions - Path manipulation - Use
os.path.basename,dirname,splitext, andjoinfor file path operations - Directory creation - Use
os.makedirsto create nested directory structures
Source Code - ring_of_fire.py (Final Version)
"""
#############################################################################
filename ring_of_fire.py
author [author]
course [course]
Brief Description:
Particle FX Tool (Final)
To be used in conjunction with "ring_of_fire.ui" - place them next to each other
Session 13A - Apr 7 2020
#############################################################################
"""
import pymel.core as pm
import os
from PySide2 import QtCore
from PySide2 import QtWidgets
from PySide2 import QtUiTools
from shiboken2 import wrapInstance
import maya.OpenMayaUI as omui
def get_maya_window():
"""Return the main Maya window
"""
main_window_ptr = omui.MQtUtil.mainWindow()
return wrapInstance(long(main_window_ptr), QtWidgets.QWidget)
def get_script_dir():
"""Returns the directory where the current script lives
"""
script_file = os.path.abspath(__file__)
return os.path.dirname(script_file)
class RingOfFireUI(QtWidgets.QDialog):
def __init__(self, parent=get_maya_window()):
super(RingOfFireUI, self).__init__(parent)
self.setWindowTitle('Ring of Fire FX')
ui_file_path = os.path.join(get_script_dir(), 'ring_of_fire.ui')
qfile_object = QtCore.QFile(ui_file_path)
qfile_object.open(QtCore.QFile.ReadOnly)
loader = QtUiTools.QUiLoader()
self.ui = loader.load(qfile_object, parentWidget=self)
self.ui.btnCreateFX.clicked.connect(self.create_ring)
qfile_object.close()
self.show()
def create_ring(self):
"""Query UI values and pass them to the creation function
"""
number_of_emitters = self.ui.spnNumberOfEmitters.value()
radius = self.ui.spnRadius.value()
create(number_of_emitters, radius)
def create(emitter_count, hoop_radius):
"""Creates an animated particle FX hoop
"""
print emitter_count, hoop_radius
hoop = pm.circle(normal=[1,0,0], radius=hoop_radius, sections=emitter_count)[0]
fx = pm.nParticle()
# Iterate only over editable CVs (fixes extra CV issue from Part 1)
for cv in xrange(0, hoop.numCVs(editableOnly=True)):
pos = hoop.getCV(cv)
print pos
new_emitter = pm.emitter(pos=pos)
pm.connectDynamic(fx, emitters=new_emitter)
pm.parent(new_emitter, hoop)
# Query the current timeline end frame
end_frame = pm.playbackOptions(query=True, animationEndTime=True)
# Force the timeline range to match the whole timeline
pm.playbackOptions(min=1, max=end_frame)
# Set the first set of keyframes
pm.currentTime(1)
hoop.ty.set(0)
hoop.ry.set(0)
pm.setKeyframe(hoop)
# Set the middle set of keyframes
pm.currentTime(end_frame/2)
hoop.ty.set(15)
hoop.ry.set(1000)
pm.setKeyframe(hoop)
# Set the last set of keyframes
pm.currentTime(end_frame)
hoop.ty.set(0)
hoop.ry.set(2000)
pm.setKeyframe(hoop)
def run():
"""Checks to see if the UI exists, and closes it
"""
for ui_item in QtWidgets.qApp.allWidgets():
if type(ui_item).__name__ == 'RingOfFireUI':
ui_item.close()
RingOfFireUI()
Source Code - archiver_part1.py
"""
#############################################################################
filename archiver.py
author [author]
course [course]
Brief Description:
Scene Archive Tool (Part 1)
Project to cover the use of path manipulation and scene trawling
Session 13A - Apr 7 2020
#############################################################################
"""
import pymel.core as pm
import os
# Get the current project directory
current_wd = pm.workspace.getcwd()
# Save the scene (commented out for now)
#pm.saveFile()
# Get full file path
scene_path = pm.sceneName()
print 'Scene Path = ', scene_path
# Get the scene file name
scene_name_with_ext = os.path.basename(scene_path)
print 'Scene File = ', scene_name_with_ext
# Get the name without extension
scene_name = os.path.splitext(scene_name_with_ext)[0]
print 'Scene Name = ', scene_name
# Get scene directory
scene_dir = os.path.dirname(scene_path)
print 'Scene Dir = ', scene_dir
# Create the target archive directory
target_dir = os.path.join(scene_dir, scene_name)
print 'Target Dir = ', target_dir
# Create the target Maya scene path
new_file_name = os.path.join(target_dir, scene_name_with_ext)
print 'Target File = ', new_file_name
# Create the target texture directory
texture_dir = os.path.join(target_dir, 'textures')
print 'Texture Dir = ', texture_dir
# Ensure the directories exist
if not os.path.exists(texture_dir):
os.makedirs(texture_dir)