# $Id: vtkMethodParser.py,v 1.17 2005/07/27 18:37:36 prabhu_r Exp $
#
# This python program/module provides functionality to parse the
# methods of a VTK object and the ability to save and reload the
# current state of a VTK object.
#
# This code is distributed under the conditions of the BSD license.
# See LICENSE.txt for details.
#
# Copyright (c) 2000-2002, Prabhu Ramachandran.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the above copyright notice for more information.
#
# Author contact information:
# Prabhu Ramachandran <prabhu_r@users.sf.net>
# http://www.aero.iitm.ernet.in/~prabhu/
"""
This python program/module provides functionality to parse the methods
of a VTK object and the ability to save and reload the current state
of a VTK object.
"""
import string, re, sys
import types
try:
# Shut up deprecation warnings for changes of function signatures
# to int from float.
import warnings
warnings.filterwarnings('ignore', category=DeprecationWarning)
except ImportError:
pass
# set this to 1 if you want to see debugging messages - very useful if
# you have problems
DEBUG=0
def debug (msg):
if DEBUG:
print msg
class VtkDirMethodParser:
""" Parses the methods from dir(vtk_obj). """
def initialize_methods (self, vtk_obj):
debug ("VtkDirMethodParser:: initialize_methods ()")
self.methods = dir (vtk_obj)[:]
# stores the <blah>On methods
self.toggle_meths = []
# stores the Set<blah>To<blah> methods
self.state_meths = []
# stores the methods that have a Get<blah> and Set<blah>
# only the <blah> is stored
self.get_set_meths = []
# pure get methods
self.get_meths = []
self.state_patn = re.compile ("To[A-Z0-9]")
# Removing the Get/SetReferenceCount method
try:
self.methods.index ('GetReferenceCount')
except ValueError:
pass
else:
self.methods.remove ('GetReferenceCount')
if 'SetReferenceCount' in self.methods:
self.methods.remove ('SetReferenceCount')
# The ReferenceCount is merely displayed
self.get_meths.append ('GetReferenceCount')
try:
self.methods.index ('GetGlobalWarningDisplay')
except ValueError:
pass
else:
for m in ('GetGlobalWarningDisplay', 'SetGlobalWarningDisplay',
'GlobalWarningDisplayOff','GlobalWarningDisplayOn'):
self.methods.remove (m)
# testing if this version of vtk has David Gobbi's cool
# stuff.
try:
junk = vtk_obj.__class__
except AttributeError:
pass
else:
# Bug in vtkRenderWindows:(
if re.match ("vtk\w*RenderWindow", vtk_obj.GetClassName ()):
for method in self.methods[:]:
if string.find (method, "StereoCapableWindow") > -1:
self.methods.remove (method)
elif string.find (method, "Position") > -1:
self.methods.remove (method)
# Infinite loop bug.in older VTK versions
if re.match ("vtk\w*Reader", vtk_obj.GetClassName ()):
for method in self.methods[:]:
if re.match ("GetNumberOf\w*InFile", method):
self.methods.remove (method)
# no need to check other bugs.
return
done = 0
# Bug in vtkRenderWindows:(
if re.match ("vtk\w*RenderWindow", vtk_obj.GetClassName ()):
for method in self.methods[:]:
if string.find (method, "FileName") > -1:
self.methods.remove (method)
done = done +1
elif string.find (method, "StereoCapableWindow") > -1:
self.methods.remove (method)
done = done +1
elif string.find (method, "Position") > -1:
self.methods.remove (method)
done = done + 1
elif string.find (method, "EventPending") > -1:
self.methods.remove (method)
done = done + 1
elif done == 7:
break
# Severe bug - causes segfault in older VTK releases
if re.match ("vtk\w*Reader", vtk_obj.GetClassName ()):
#self.methods = []
for method in self.methods[:]:
if string.find (method, "Name") > -1:
self.methods.remove (method)
elif string.find (method, "InputString") > -1:
self.methods.remove (method)
# Infinite loop bug.
elif re.match ("GetNumberOf\w*InFile", method):
self.methods.remove (method)
def parse_methods (self, vtk_obj):
self.initialize_methods (vtk_obj)
debug ("VtkDirMethodParser:: parse_methods() : initialized methods")
for method in self.methods[:]:
if string.find (method[:3], "Set") >= 0 and \
self.state_patn.search (method) is not None :
try:
eval ("vtk_obj.Get%s"%method[3:])
except AttributeError:
self.state_meths.append (method)
self.methods.remove (method)
# finding all the On/Off toggle methods
elif string.find (method[-2:], "On") >= 0:
try:
self.methods.index ("%sOff"%method[:-2])
except ValueError:
pass
else:
self.toggle_meths.append (method)
self.methods.remove (method)
self.methods.remove ("%sOff"%method[:-2])
# finding the Get/Set methods.
elif string.find (method[:3], "Get") == 0:
set_m = "Set"+method[3:]
try:
self.methods.index (set_m)
except ValueError:
pass
else:
self.get_set_meths.append (method[3:])
self.methods.remove (method)
self.methods.remove (set_m)
self.clean_up_methods (vtk_obj)
def clean_up_methods (self, vtk_obj):
self.clean_get_set (vtk_obj)
self.clean_state_methods (vtk_obj)
self.clean_get_methods (vtk_obj)
def clean_get_set (self, vtk_obj):
debug ("VtkDirMethodParser:: clean_get_set()")
# cleaning up the Get/Set methods by removing the toggle funcs.
for method in self.toggle_meths:
try:
self.get_set_meths.remove (method[:-2])
except ValueError:
pass
# cleaning them up by removing any methods that are responsible for
# other vtkObjects
for method in self.get_set_meths[:]:
try:
eval ("vtk_obj.Get%s ().GetClassName ()"%method)
except (TypeError, AttributeError):
pass
else:
self.get_set_meths.remove (method)
continue
try:
val = eval ("vtk_obj.Get%s ()"%method)
except (TypeError, AttributeError):
self.get_set_meths.remove (method)
else:
if val is None:
self.get_set_meths.remove (method)
def clean_state_methods (self, vtk_obj):
debug ("VtkDirMethodParser:: clean_state_methods()")
# Getting the remaining pure GetMethods
for method in self.methods[:]:
if string.find (method[:3], "Get") == 0:
self.get_meths.append (method)
self.methods.remove (method)
# These aren't state methods.
ignore = ['SetUpdateExtentToWholeExtent', 'SetInputArrayToProcess']
for method in self.state_meths[:]:
if method in ignore:
self.methods.append(method)
self.state_meths.remove(method)
# Grouping similar state methods
if len (self.state_meths) != 0:
tmp = self.state_meths[:]
self.state_meths = []
state_group = [tmp[0]]
end = self.state_patn.search (tmp[0]).start ()
# stores the method type common to all similar methods
m = tmp[0][3:end]
for i in range (1, len (tmp)):
if string.find (tmp[i], m) >= 0:
state_group.append (tmp[i])
else:
self.state_meths.append (state_group)
state_group = [tmp[i]]
end = self.state_patn.search (tmp[i]).start ()
m = tmp[i][3:end]
try: # remove the corresponding set method in get_set
val = self.get_set_meths.index (m)
except ValueError:
pass
else:
del self.get_set_meths[val]
#self.get_meths.append ("Get"+m)
clamp_m = "Get" + m + "MinValue"
try: # remove the GetNameMax/MinValue in get_meths
val = self.get_meths.index (clamp_m)
except ValueError:
pass
else:
del self.get_meths[val]
val = self.get_meths.index ("Get" + m + "MaxValue")
del self.get_meths[val]
if len (state_group) > 0:
self.state_meths.append (state_group)
def clean_get_methods (self, vtk_obj):
debug ("VtkDirMethodParser:: clean_get_methods()")
for method in self.get_meths[:]:
#print method
try:
res = eval ("vtk_obj.%s ()"%method)
except (TypeError, AttributeError):
self.get_meths.remove (method)
continue
else:
try:
eval ("vtk_obj.%s ().GetClassName ()"%method)
except AttributeError:
pass
else:
self.get_meths.remove (method)
continue
if string.find (method[-8:], "MaxValue") > -1:
self.get_meths.remove( method)
elif string.find (method[-8:], "MinValue") > -1:
self.get_meths.remove( method)
self.get_meths.sort ()
def toggle_methods (self):
return self.toggle_meths
def state_methods (self):
return self.state_meths
def get_set_methods (self):
return self.get_set_meths
def get_methods (self):
return self.get_meths
class VtkMethodParser:
""" This class finds the methods for a given vtkObject. It uses
the output from vtkObject->Print() (or in Python str(vtkObject))
and output from the VtkDirMethodParser to obtain the methods. """
def parse_methods (self, vtk_obj):
"Parse for the methods."
debug ("VtkMethodParser:: parse_methods()")
self.vtk_warn = -1
try:
self.vtk_warn = vtk_obj.GetGlobalWarningDisplay ()
except AttributeError:
pass
else:
vtk_obj.GlobalWarningDisplayOff ()
if self._initialize_methods (vtk_obj):
# if David Gobbi's improvements are in this version of VTK
# then I need to go no further.
self._reset_warning_status (vtk_obj)
return
for method in self.methods[:]:
# removing methods that have nothing to the right of the ':'
if (method[1] == '') or \
(string.find (method[1], "none") > -1) :
self.methods.remove (method)
elif method[0] == "ReferenceCount":
self.get_meths.append ("Get"+method[0])
self.methods.remove (method)
for method in self.methods:
# toggle methods are first identified
if (method[1] == "On") or (method[1] == "Off"):
# bug in vtkRenderWindow.cxx ver 1.104. so fixing it.
if re.match ("vtk\w*RenderWindow", vtk_obj.GetClassName ()):
if method[0] == "Swapbuffers":
method[0] = "SwapBuffers"
# more bugs
if re.match ("vtk\w*Renderer", vtk_obj.GetClassName ()):
if method[0] == "Two-sidedLighting":
method[0] = "TwoSidedLighting"
try:
val = eval ("vtk_obj.Get%s ()"%method[0])
if val == 1:
eval ("vtk_obj.%sOn ()"%method[0])
elif val == 0:
eval ("vtk_obj.%sOff ()"%method[0])
except AttributeError:
pass
else:
self.toggle_meths.append (method[0]+"On")
else: # see it it is get_set or get or a state method
found = 0
# checking if it is a state func.
# figure out the long names from the dir_state_meths
for sms in self.dir_state_meths[:]:
if string.find (sms[0], method[0]) >= 0:
self.state_meths.append (sms)
self.dir_state_meths.remove (sms)
found = 1
if found:
self.get_meths.append ("Get"+method[0])
try:
t = eval ("vtk_obj.Get%sAsString ()"%method[0])
except AttributeError:
pass
else:
self.get_meths.append ("Get"+method[0]+"AsString")
else:
# the long name is inherited or it is not a state method
try:
t = eval ("vtk_obj.Get%s ().GetClassName ()"%
method[0])
except AttributeError:
pass
else:
continue
val = 0
try:
val = eval ("vtk_obj.Get%s ()"%method[0])
except (TypeError, AttributeError):
pass
else:
try:
f = eval ("vtk_obj.Set%s"%method[0])
except AttributeError:
self.get_meths.append ("Get"+method[0])
else:
try:
f(val)
except TypeError:
try:
f(*val)
except TypeError:
self.get_meths.append ("Get"+method[0])
else:
self.get_set_meths.append (method[0])
else:
self.get_set_meths.append (method[0])
self._clean_up_methods (vtk_obj)
def _get_str_obj (self, vtk_obj):
debug ("VtkMethodParser:: _get_str_obj()")
self.methods = str (vtk_obj)
self.methods = string.split (self.methods, "\n")
del self.methods[0]
def _initialize_methods (self, vtk_obj):
"Do the basic parsing and setting up"
debug ("VtkMethodParser:: _initialize_methods()")
dir_p = VtkDirMethodParser ()
dir_p.parse_methods (vtk_obj)
# testing if this version of vtk has David Gobbi's cool
# stuff. If it does then no need to do other things.
try:
junk = vtk_obj.__class__
except AttributeError:
pass
else:
self.toggle_meths = dir_p.toggle_methods ()
self.state_meths = dir_p.state_methods ()
self.get_set_meths = dir_p.get_set_methods ()
self.get_meths = dir_p.get_methods ()
return 1
self.dir_toggle_meths = dir_p.toggle_methods ()
self.dir_state_meths = dir_p.state_methods ()
self.dir_get_set_meths = dir_p.get_set_methods ()
self.dir_get_meths = dir_p.get_methods ()
self._get_str_obj (vtk_obj)
patn = re.compile (" \S")
for method in self.methods[:]:
if not patn.match (method):
self.methods.remove (method)
for method in self.methods[:]:
if string.find (method, ":") == -1:
self.methods.remove (method)
for i in range (0, len (self.methods)):
strng = self.methods[i]
strng = string.replace (strng, " ", "")
self.methods[i] = string.split (strng, ":")
done = 0
if re.match ("vtk\w*RenderWindow", vtk_obj.GetClassName ()):
for method in self.methods[:]:
if string.find (method[0], "Position") > -1:
self.methods.remove (method)
done = done +1
elif done == 2:
break
self.toggle_meths = []
self.state_meths = []
self.get_set_meths = []
self.get_meths = []
return 0
def _clean_up_methods (self, vtk_obj):
"Merge dir and str methods. Finish up."
debug ("VtkMethodParser:: _clean_up_methods()")
for meth_list in ((self.dir_toggle_meths, self.toggle_meths),\
(self.dir_get_set_meths, self.get_set_meths),\
(self.dir_get_meths, self.get_meths)):
for method in meth_list[0]:
try:
meth_list[1].index (method)
except ValueError:
meth_list[1].append (method)
# Remove all get_set methods that are already in toggle_meths
# This case can happen if the str produces no "On/Off" but
# dir does and str produces a get_set instead.
for method in self.toggle_meths:
try:
self.get_set_meths.remove (method[:-2])
except ValueError:
pass
self.toggle_meths.sort ()
self.state_meths.sort ()
self.get_set_meths.sort ()
self.get_meths.sort ()
self._reset_warning_status (vtk_obj)
def _reset_warning_status (self, vtk_obj):
debug ("VtkMethodParser:: _reset_warning_status ()")
if self.vtk_warn > -1:
vtk_obj.SetGlobalWarningDisplay (self.vtk_warn)
def toggle_methods (self):
return self.toggle_meths
def state_methods (self):
return self.state_meths
def get_set_methods (self):
return self.get_set_meths
def get_methods (self):
return self.get_meths
class VtkPicklerException (Exception):
pass
class VtkPickler:
""" Enables saving/loading the configuration of VTK objects by
printing out the methods and the corresponding arguments to a
file. While dumping it uses the VtkMethodParser to get the
methods."""
def _parse_methods (self, vtk_obj):
debug ("VtkPickler:: _parse_methods()")
parser = VtkMethodParser ()
parser.parse_methods (vtk_obj)
self.toggle_meths = parser.toggle_methods ()
self.state_meths = parser.state_methods ()
self.get_set_meths = parser.get_set_methods ()
def _shut_off_warnings(self, obj):
"""Turn off VTK deprecation warnings and save the old status
of the warning flag."""
self.vtk_warn = -1
try:
self.vtk_warn = obj.GetGlobalWarningDisplay ()
except AttributeError:
pass
else:
obj.GlobalWarningDisplayOff ()
def _reset_warnings(self, obj):
"""Reset warnings to saved state."""
if self.vtk_warn > -1:
obj.SetGlobalWarningDisplay (self.vtk_warn)
def _write_config(self, out):
debug ("VtkPickler:: write_config()")
state_patn = re.compile ("To[A-Z0-9]")
methods = []
args = []
for method in self.toggle_meths:
func = "Set"+method[:-2]
val = eval ("self.obj.Get%s ()"%func[3:])
methods.append ((func, val))
for method in self.state_meths:
end = state_patn.search (method[0]).start ()
func = method[0][:end]
val = eval ("self.obj.Get%s ()"%func[3:])
methods.append ((func, val))
for method in self.get_set_meths:
func = "Set"+method
val = eval ("self.obj.Get%s ()"%method)
methods.append ((func, val))
if hasattr(out, 'write'):
out.write (self.obj.GetClassName ()+"\n")
out.write (str (methods))
out.write ("\n")
else:
d = {}
d[self.obj.GetClassName()] = dict(methods)
if hasattr(out, 'update'):
out.update(d)
else:
out = d
def _write_old_config (self, out):
debug ("VtkPickler:: write_old_config()")
state_patn = re.compile ("To[A-Z0-9]")
methods = []
args = []
for method in self.toggle_meths:
methods.append ("Set"+method[:-2])
val = eval ("self.obj.Get%s ()"%method[:-2])
args.append (val)
for method in self.state_meths:
end = state_patn.search (method[0]).start ()
m = method[0][:end]
methods.append (m)
val = eval ("self.obj.Get%s ()"%m[3:])
args.append (val)
for method in self.get_set_meths:
methods.append ("Set"+method)
val = eval ("self.obj.Get%s ()"%method)
args.append (val)
if hasattr(out, 'write'):
out.write (self.obj.GetClassName ()+"\n")
out.write (str (methods))
out.write ("\n")
out.write (str (args))
out.write ("\n")
else:
d = {}
d[self.obj.GetClassName()] = dict(map(None, methods, args))
if hasattr(out, 'update'):
out.update(d)
else:
out = d
def _load_config (self, input, equiv):
"""Loads the configuration.
Input args:
input -- input data either as a file or a dictionary.
equiv -- is an equivalent name for the object's class.
Useful to get around names being renamed by VTK folks.
"""
debug ("VtkPickler:: load_config ()")
vtk_obj = self.obj
if hasattr(input, 'readline'):
c_name = input.readline ()[:-1]
else:
c_name = input.keys()[0]
obj_name = vtk_obj.GetClassName ()
if (obj_name != c_name) and (c_name != equiv):
msg = "Error: Object given doesn't match object saved."
msg = msg + "\nObject given is %s."%vtk_obj.GetClassName ()
msg = msg + "\nObject saved is %s."%c_name
msg = msg + "\nNot loading saved configuration!"
raise VtkPicklerException, msg
if hasattr(input, 'readline'):
methods = eval (input.readline ())
t = type (methods[0])
if t is types.TupleType or t is types.ListType:
self._load_new_config (methods)
else:
self._load_old_config (input, methods)
else:
methods = input[c_name].items()
self._load_new_config (methods)
def _load_new_config(self, methods):
debug ("VtkPickler:: load_new_config ()")
for method in methods:
func = method[0]
arg = method[1]
if func != 'SetGlobalWarningDisplay':
self._call_function (func, arg)
def _load_old_config (self, input, methods):
debug ("VtkPickler:: load_old_config ()")
args = eval (input.readline ())
for i in range (0, len (methods)):
func = methods[i]
arg = args[i]
if func != 'SetGlobalWarningDisplay':
self._call_function (func, arg)
def _call_function(self, func, arg):
debug ("VtkPickler:: _call_function ()")
try:
f = eval ("self.obj.%s"%func)
except AttributeError:
msg = "Warning: The installed version of VTK does "\
"not have the member " + func + \
"for the class " + self.obj.GetClassName () + \
".\nIgnoring it.\n"
debug (msg)
else:
try:
f(arg)
except TypeError:
f(*arg)
def dump (self, vtk_obj, output):
"""Dump the configuration for the vtk object into the given
object (file or dictionary).
Input args:
vtk_obj - the VTK object to dump.
output - The object to dump into (either a file or a dict).
"""
debug ("VtkPickler:: dump()")
self.obj = vtk_obj
self._parse_methods (vtk_obj)
self._shut_off_warnings(vtk_obj)
try:
self._write_config (output)
finally:
self._reset_warnings(vtk_obj)
def load (self, vtk_obj, input, equiv=""):
"""Load the configuration for the vtk object from the file
passed.
Input args:
vtk_obj - the VTK object
input - the file from which to load the configuration
equiv - a hack to allow name changes (arghh) made in VTK.
"""
debug ("VtkPickler:: load()")
self.obj = vtk_obj
self._shut_off_warnings(vtk_obj)
try:
self._load_config (input, equiv)
finally:
self._reset_warnings(vtk_obj)
|