# $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 # 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 On methods self.toggle_meths = [] # stores the SetTo methods self.state_meths = [] # stores the methods that have a Get and Set # only the 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)