Forum Moderators: Lobo3433, Staff Forum Coordinators: Anim8dtoon
Poser Python Scripting F.A.Q (Last Updated: 2026 Apr 22 10:09 am)
I tried it with a NosePointed nose morph, located in the Head....
body_actor = SCENE.CurrentFigure().Actor("Head")
except poser.error:
raise Aborted("Make sure a figure with an actor named 'Head' exists.")
morph = body_actor.Parameter("NosePointed")
if not morph:
raise Aborted("Make sure a morph named 'NosePointed' exist in actor 'Head'.")
But it had no effect at all on the nose, on moving and then releasing the Mover cube. The messages from the script appeared to be the standard guidance, not errors. Does it only work on certain types of morphs?
Learn the Secrets of Poser 11 and Line-art Filters.
Pretty neat. Also found out that if you delete the mover, it hides the polys the morph was affecting, while set at anything other than 0. This could be a cool way to add transparency to a mesh without using a map.
--------------------------------------------------------------------------------
Consider me insane if you wish, but is your reality any better?
If the Mover is deleted, nothing should actually happen. The last known morph state remains as it is and is not changed anymore. "update_morph" is no longer called.
But: Actually, there should be an error message, because "move_actor.WorldDisplacement()" cannot be executed by Poser anymore (the "move_actor" is gone).
The script here is not intended for production. In order to use the method in a meaningful way, a little bit more work is needed around it.
And a new one. This time able to translate and rotate morphs!

The "Mover" is a cube generated on the fly. (green if inactive, red if active). Grab that cube and move it around (after you selected an actor and a morph and clicked "Start"). Or use the dials to position more exactly. The morph will follow the movement.
This is not new. But now you can rotate the "mover" and your morph will rotate! Rotation center is the position of the mover. This is why "No Translation" exist: If you check it, you can move the mover to a new position to set a rotation center.
Note: It may last a moment after pressing "Start" until the "mover" appears. Poser has to create that new grouping object first.
I'll post the source here, but to be sure better download it from here, because the editor here may destray parts of the script. And the source code is more readable on my website.
from __future__ import print_function
import math
import sys
import numpy as NP
import wx
try:
import poser
except ImportError:
# Not required while inside Poser Python, but very helpfull for external editors.
# See https://adp.spdns.org
from PoserLibs import POSER_FAKE as poser
if sys.version_info.major > 2:
# Python 3 (Poser 12 and above)
map = lambda a, b: [a(_b) for _b in b]
basestring = str
else:
range = xrange
SCENE = poser.Scene()
X, Y, Z = range(3)
def actor_has_geom(ac):
return isinstance(ac, poser.ActorType)
and hasattr(ac, "Geometry")
and ac.Geometry() is not None
and ac.Geometry().NumVertices() > 0
def is_morphtarget(parm):
return isinstance(parm, poser.ParmType)
and parm.IsMorphTarget()
and parm.NumMorphTargetDeltas() != 0
def _rotate(ar, theta, axis=(0, 0, 0), order="XYZ"):
AX, AY, AZ = axis
def _X(c, s, ar=ar):
return NP.dot(ar, NP.array([
[1.0, AY, AZ],
[AX, c, -s],
[AX, s, c]
]))
def _Y(c, s, ar=ar):
return NP.dot(ar, NP.array([
[c, AY, -s],
[AX, 1.0, AZ],
[s, AY, c]
]))
def _Z(c, s, ar=ar):
return NP.dot(ar, NP.array([
[c, -s, AZ],
[s, c, AZ],
[AX, AY, 1.0],
]))
for entry in order.upper():
idx = "XYZ".index(entry)
ar = locals().get("_" + entry)(math.cos(theta[idx]), math.sin(theta[idx]), ar)
return ar
def rotated_poserVerts(verts, theta, order):
return _rotate(NP.array([[v.X(), v.Y(), v.Z()] for v in verts]), theta, order)
def rotate_actor(actor, theta, order):
if isinstance(actor, basestring):
try:
actor = SCENE.Actor(actor)
except poser.error:
actor = SCENE.ActorByInternalName(actor)
geom = actor.Geometry()
for verts, vv in zip(rotated_poserVerts(geom.Vertices(), theta, order), geom.Vertices()):
vv.SetX(verts[0])
vv.SetY(verts[1])
vv.SetZ(verts[2])
class Mover(object):
"""
This class uses Posers callbacks. Because of this no Poser object
can be directly hold in a variable (actor, morph, etc.).
Internal names are used instead to avoid crashes.
"""
__slots__ = "_saved_active",
"_figure_iname", "_moveactor_iname", "_morphactor_iname", "_morphname",
"morphindices", "morphdata", "morph_origdata", "morphcenter",
"_morph_min", "_morph_max",
"mover_startpos", "last_coords", "use_trans", "use_rot",
"current_rotation"
def __init__(self, morphparameter):
assert is_morphtarget(morphparameter), "Parameter has no Morphdata."
assert actor_has_geom(morphparameter.Actor()), "Actor to morph must have Geometry."
self.last_coords = NP.array([0, 0, 0], NP.float)
self.current_rotation = NP.array([0, 0, 0], NP.float)
self._saved_active = [False, ]
self._figure_iname = None
self._morphname = self._morphactor_iname = None
self._moveactor_iname = None
self.morphindices = self.morphdata = self.morph_origdata = None
self.use_trans = self.use_rot = False
self.createMover()
self.setmorph(morphparameter)
self.morphcenter = self.get_morph_center(False)
self._morph_min, self._morph_max = self.get_morph_minmax(True)
self.mover_startpos = self.mover_defaultpos()
self.move_mover(self.mover_startpos)
self.set_movercolor(0, 255, 0)
SCENE.DrawAll()
@property
def active(self):
return self._saved_active[-1]
@property
def morphactor(self):
try:
return SCENE.ActorByInternalName(self._morphactor_iname)
except poser.error:
print("Actor '%s' does not exist anymore." % self._morphactor_iname)
self.stop()
@morphactor.setter
def morphactor(self, actor):
if actor is None:
self._morphactor_iname = None
else:
assert isinstance(actor, poser.ActorType)
self._morphactor_iname = actor.InternalName()
@property
def moveactor(self):
try:
return SCENE.ActorByInternalName(self._moveactor_iname)
except poser.error:
print("Move actor does not exist anymore.")
self.stop()
@moveactor.setter
def moveactor(self, actor):
if actor is None:
self._moveactor_iname = None
else:
assert isinstance(actor, poser.ActorType)
self._moveactor_iname = actor.InternalName()
@property
def figure(self):
try:
return SCENE.FigureByInternalName(self._figure_iname)
except poser.error:
return None
@figure.setter
def figure(self, figure):
if figure is None:
self._figure_iname = None
else:
assert isinstance(figure, poser.FigureType)
self._figure_iname = figure.InternalName()
@property
def morph(self):
try:
return self.morphactor.Parameter(self._morphname)
except Exception:
print("Morph '%s' does not exist anymore." % self._morphname)
@morph.setter
def morph(self, parameter):
assert isinstance(parameter, poser.ParmType)
self._morphname = parameter.InternalName()
def mover_defaultpos(self):
c = NP.array(self.get_morph_center(True))
c[2] = self._morph_max[2] + 0.02
return c
def push_active(self, v):
self._saved_active.append(bool(v))
return v
def pop_active(self):
return None if not self._saved_active
else self._saved_active.pop()
def start(self):
SCENE.SelectFigure(self.figure)
self._saved_active = [False, ]
self.reset_mover()
self._start_mover()
self.set_movercolor(255, 0, 0)
self._saved_active = [True, ]
SCENE.SelectActor(self.moveactor)
def stop(self):
SCENE.SelectFigure(self.figure)
self._saved_active = [False, ]
self._stop_mover()
self.set_movercolor(0, 255, 0)
def set_movercolor(self, r, g, b):
s = self.moveactor.Material("Preview").ShaderTree()
s.Node(0).Input(0).SetColor(r, g, b)
s.UpdatePreview()
def setmorph(self, morphparm):
self.push_active(False)
if isinstance(morphparm, basestring):
morphparm = SCENE.CurrentActor().Parameter(morphparm)
if is_morphtarget(morphparm):
self.morph = morphparm
self.morphactor = ac = morphparm.Actor()
fig = ac.ItsFigure() if ac.IsBodyPart() else SCENE.Figures()[0]
self.figure = fig if fig else None
numv = ac.Geometry().NumVertices()
self.morph_origdata = NP.array([morphparm.MorphTargetDelta(idx)
for idx in range(numv)])
self.morphindices = NP.array([idx for idx, v in enumerate(self.morph_origdata)
if not all(v == (0, 0, 0))], NP.int32)
self.morphdata = self.morph_origdata[self.morphindices]
else:
print("Choosen Parameter is not a morphtarget with geometry.")
print("Actor:", self._morphactor_iname, "- Parameter:", self._morphname)
self.pop_active()
def rotate_morphdata(self, theta, order):
if not self.use_rot:
self.morphdata = _rotate(self.morphdata, theta, self.morphcenter, order=order)
def update_morph(self):
"""
Called from callbacks if mover was moved or
rotated and morph needs to be updated.
"""
self.last_coords = NP.array(self.moveactor.WorldDisplacement())
diff = [0, 0, 0] if self.use_trans
else (self.last_coords - self.mover_startpos) * .25
ar = self.morphdata + diff
m = self.morph
for idx, (x, y, z) in zip(self.morphindices, ar):
m.SetMorphTargetDelta(idx, x, y, z)
SCENE.DrawAll()
def undo_morph(self):
m = self.morph
if m:
for idx in self.morphindices:
x, y, z = self.morph_origdata[idx]
m.SetMorphTargetDelta(idx, x, y, z)
self.morphdata = self.morph_origdata.copy()
self.mover_startpos = self.moveactor.WorldDisplacement()
SCENE.DrawAll()
def current_morph_permanent(self):
self.push_active(False)
self.update_morph() # make sure last changes are done.
self.setmorph(self.morph) # setting it will take the data currently in morph
center = self.get_morph_center(True)
self.morphcenter = center
self.mover_startpos = self.mover_defaultpos()
self.move_mover(self.mover_startpos)
SCENE.DrawAll()
self.pop_active()
def get_morph_minmax(self, world=False):
geom = self.morph.Actor().Geometry()
verts = NP.array([[v.X(), v.Y(), v.Z()]
for v in (geom.WorldVertices() if world else geom.Vertices())])
verts = verts[self.morphindices] + NP.array([self.morph.MorphTargetDelta(i)
for i in self.morphindices])
return NP.min(verts, axis=0), NP.max(verts, axis=0)
def get_morph_center(self, world=False):
""":rtype: NP.ndarray"""
_min, _max = self.get_morph_minmax(world)
return _min + (_max - _min) / 2.0
def __trans__(self, parm, value):
"""Callback on translation changes."""
if self.active:
idx = [poser.kParmCodeXTRAN, poser.kParmCodeYTRAN, poser.kParmCodeZTRAN].index(parm.TypeCode())
if self.last_coords[idx] != parm.Actor().WorldDisplacement()[idx]:
self.update_morph()
return value
def __rot__(self, parm, value):
"""Callback on rotation changes."""
if self.active:
idx = [poser.kParmCodeXROT, poser.kParmCodeYROT, poser.kParmCodeZROT].index(parm.TypeCode())
r = math.radians(value)
if self.current_rotation[idx] != r:
self.current_rotation[idx] = r
self.rotate_morphdata(self.current_rotation, "XYZ"[idx])
self.update_morph()
return value
def createMover(self):
try:
ac = SCENE.Actor("MorphMover")
except poser.error:
ac = SCENE.CreateGrouping()
ac.SetName("MorphMover")
ac.ParameterByCode(poser.kParmCodeASCALE).SetValue(.15)
ac.SetDisplayStyle(poser.kDisplayCodeFLATLINED)
ac.SetVisible(True)
self._moveactor_iname = ac.InternalName()
return ac
def move_mover(self, coords):
ma = self.moveactor
if ma:
self.push_active(False)
ma.ParameterByCode(poser.kParmCodeXTRAN).SetValue(coords[X])
ma.ParameterByCode(poser.kParmCodeYTRAN).SetValue(coords[Y])
ma.ParameterByCode(poser.kParmCodeZTRAN).SetValue(coords[Z])
self.last_coords = NP.array(coords, NP.float)
SCENE.DrawAll()
self.pop_active()
def reset_mover(self):
ma = self.moveactor
if ma:
self.mover_startpos = NP.array(self.moveactor.WorldDisplacement())
self.move_mover(self.mover_startpos)
def _start_mover(self):
ma = self.moveactor
if ma:
for code in (poser.kParmCodeXROT, poser.kParmCodeYROT, poser.kParmCodeZROT):
p = ma.ParameterByCode(code)
p.SetValue(0)
p.SetUpdateCallback(self.__rot__)
for code in (poser.kParmCodeXSCALE, poser.kParmCodeYSCALE, poser.kParmCodeZSCALE):
v = ma.ParameterByCode(code).Value()
ma.ParameterByCode(code).SetUpdateCallback(lambda a, b: v)
for code in (poser.kParmCodeXTRAN, poser.kParmCodeYTRAN, poser.kParmCodeZTRAN):
ma.ParameterByCode(code).SetUpdateCallback(self.__trans__)
def _stop_mover(self):
ma = self.moveactor
if ma:
for code in (poser.kParmCodeXTRAN, poser.kParmCodeYTRAN, poser.kParmCodeZTRAN,
poser.kParmCodeXROT, poser.kParmCodeYROT, poser.kParmCodeZROT,
poser.kParmCodeASCALE,
poser.kParmCodeXSCALE, poser.kParmCodeYSCALE, poser.kParmCodeZSCALE):
ma.ParameterByCode(code).ClearUpdateCallback()
for code in (poser.kParmCodeXROT, poser.kParmCodeYROT, poser.kParmCodeZROT):
ma.ParameterByCode(code).SetValue(0)
for code in (poser.kParmCodeXSCALE, poser.kParmCodeYSCALE, poser.kParmCodeZSCALE):
ma.ParameterByCode(code).SetValue(1)
# ----------------------------------------------------------------
# wxPython UI related
# ----------------------------------------------------------------
def collectActornames(figure=None):
if figure is None:
figure = SCENE.CurrentFigure()
return [ac.Name() for ac in (SCENE.Actors() if not figure else figure.Actors())]
def collectMorphnames(actor=None, filter=None):
if filter is None:
filter = lambda n: True
if actor is None:
actor = SCENE.CurrentActor()
return [p.Name() for p in actor.Parameters() if filter(p.Name()) and is_morphtarget(p)]
class MyFrame(wx.Frame):
def __init__(self, parent, *args, **kwargs):
super(MyFrame, self).__init__(parent, wx.ID_ANY, *args, **kwargs)
self.morph_mover = None # type: None or Mover
self._selected_actor = None
self._selected_parameter = dict()
self.init_ui()
self.Bind(wx.EVT_CLOSE, self.ev_close)
self.Bind(wx.EVT_SIZE, self.ev_size)
self.Bind(wx.EVT_CHECKBOX, self.on_check)
self.Bind(wx.EVT_BUTTON, self.on_button)
self.Bind(wx.EVT_CHOICE, self.on_choice)
@property
def selected_actor(self):
if self._selected_actor:
try:
return SCENE.ActorByInternalName(self._selected_actor)
except poser.error:
pass
return None
@property
def selected_parameter(self):
if self.selected_actor:
return self._selected_parameter.get(self._selected_actor, None)
return None
def startMover(self):
if self.morph_mover is not None:
self.morph_mover.start()
elif self.selected_parameter is not None:
ac = self.selected_actor
if ac:
try:
parm = ac.Parameter(self.selected_parameter)
self.morph_mover = Mover(parm)
self.morph_mover.start()
except poser.error:
print("Can't use parameter '%s' in actor '%s'." % (
self.selected_parameter, self.selected_actor.Name()))
if self.morph_mover:
self.morph_mover.use_trans = self.FindWindowByName("cb_notranslation").IsChecked()
self.morph_mover.use_rot = self.FindWindowByName("cb_norotation").IsChecked()
def ev_close(self, event):
if self.morph_mover is not None:
self.morph_mover.stop()
self.morph_mover.moveactor.Delete()
event.Skip()
def ev_size(self, event):
self.SetSize(event.Size)
self.Refresh()
event.Skip()
def init_ui(self):
panel = wx.Panel(self)
main_sz = wx.BoxSizer(wx.VERTICAL)
sz_standard = (0, wx.EXPAND | wx.LEFT | wx.RIGHT, 8)
sz_choice = (0, wx.EXPAND | wx.LEFT | wx.RIGHT, 8)
sz_check = (1, wx.ALIGN_CENTER | wx.ALL, 8)
btn_style = wx.NO_BORDER
choice_style = wx.CB_SORT | wx.NO_BORDER
spacersize = 12
main_sz.Add(wx.StaticText(panel, wx.ID_ANY, "Select Actor"), *sz_choice)
main_sz.Add(wx.Choice(panel, wx.ID_ANY, style=choice_style, name="choice_actors",
choices=collectActornames()), *sz_choice)
main_sz.AddSpacer(spacersize)
main_sz.Add(wx.StaticText(panel, wx.ID_ANY, "Select Morph"), *sz_choice)
main_sz.Add(
wx.Choice(panel, wx.ID_ANY, style=choice_style, name="choice_parameters",
choices=collectMorphnames()), *sz_choice)
main_sz.AddSpacer(spacersize)
main_sz.AddStretchSpacer(1)
btn_sz = wx.GridSizer(4, 2, vgap=2, hgap=2)
btn_sz.Add(wx.CheckBox(panel, wx.ID_ANY, label="No Translation", name="cb_notranslation"), *sz_check)
btn_sz.Add(wx.CheckBox(panel, wx.ID_ANY, label="No Rotation", name="cb_norotation"), *sz_check)
btn_sz.Add(wx.Button(panel, wx.ID_ANY, label="Start", style=btn_style, name="btn_start"), *sz_standard)
btn_sz.Add(wx.Button(panel, wx.ID_ANY, label="Stop", style=btn_style, name="btn_stop"), *sz_standard)
btn_sz.Add(wx.Button(panel, wx.ID_ANY, label="Reload", style=btn_style, name="btn_reload"), *sz_standard)
btn_sz.Add(wx.Button(panel, wx.ID_ANY, label="Reset Mover", style=btn_style, name="btn_reset"), *sz_standard)
btn_sz.Add(wx.Button(panel, wx.ID_ANY, label="Reset Morph", style=btn_style, name="btn_undo"), *sz_standard)
btn_sz.Add(wx.Button(panel, wx.ID_ANY, label="Morph Permanent", style=btn_style, name="btn_permanent"),
*sz_standard)
main_sz.Add(btn_sz, 0, wx.ALIGN_CENTER)
main_sz.AddSpacer(spacersize)
main_sz.AddStretchSpacer(1)
main_sz.Layout()
panel.SetSizer(main_sz)
self.reload_actors()
def reload_actors(self):
ac = SCENE.CurrentActor()
self._selected_actor = ac.InternalName()
ac_choice = self.FindWindowByName("choice_actors")
ac_choice.Clear()
map(ac_choice.Append, collectActornames(None if not hasattr(ac, "ItsFigure")
else ac.ItsFigure()))
ac_choice.SetStringSelection(ac.Name())
self.reload_parms()
def reload_parms(self):
ac = self.selected_actor
if ac:
choice = self.FindWindowByName("choice_parameters") # type: wx.Choice
choice.Clear()
for name in collectMorphnames(ac, filter=lambda n: n[0] not in ".-"):
choice.Append(name)
sel = self.selected_parameter
if not sel:
sel = choice.GetString(0)
self._selected_parameter[self._selected_actor] = sel
choice.SetStringSelection(sel)
def on_check(self, event):
obj = event.GetEventObject() # type: wx.CheckBox
name = obj.Name.lower()
if self.morph_mover:
if "notrans" in name:
self.morph_mover.use_trans = obj.IsChecked()
if "norot" in name:
self.morph_mover.use_rot = obj.IsChecked()
self.morph_mover.update_morph()
def on_button(self, event):
obj = event.GetEventObject() # type: wx.Button
name = obj.Name.lower()
if "start" in name:
self.startMover()
elif "stop" in name:
self.morph_mover.stop()
elif "reload" in name:
self.reload_actors()
elif "reset" in name:
self.morph_mover.reset_mover()
elif "undo" in name:
self.morph_mover.undo_morph()
elif "permanent" in name:
self.morph_mover.current_morph_permanent()
def on_choice(self, event):
obj = event.GetEventObject() # type: wx.Choice
name = obj.Name.lower()
sel_str = obj.GetStringSelection()
if "actor" in name:
self._selected_actor = SCENE.Actor(sel_str).InternalName()
self.reload_parms()
elif "parameter" in name:
self._selected_parameter[self._selected_actor] = sel_str
if self.morph_mover is not None:
self.morph_mover.stop()
self.morph_mover = None
self.startMover()
if __name__ == "__main__":
app = poser.WxApp()
aui = poser.WxAuiManager() # type: wx.aui.AuiManager
root = aui.GetManagedWindow() # type: wx.Window
main = MyFrame(root)
main.Show()
By the way: This is not meant for production but to show and test what is possible with Poser Python.
This site uses cookies to deliver the best experience. Our own cookies make user accounts and other features possible. Third-party cookies are used to display relevant ads and to analyze how Renderosity is used. By using our site, you acknowledge that you have read and understood our Terms of Service, including our Cookie Policy and our Privacy Policy.
I made a morph. Nothing special. But then I thought: The whole morph should move a bit to the right.
Tried it with Posers morph brush: Didn*t work.
So I made a little script to do the job. Fine. For this one.
Based on this idea I made a script that can move a morph by letting it follow to the movement of another object (a prop?).
It's really simple:
For a demonstration: Load a figure. Create a morph named "ABC" in the "Chest" actor. Load any prop, name it "Mover". Start the following (complete) script: