smtrace.py 80 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905
  1. """smtrace module is used along with vtkSMTrace to generate Python trace for
  2. ParaView. While this module is primarily designed to be used from the ParaView
  3. GUI, Python scripts can use this module too to generate trace from the script
  4. executed.
  5. Typical usage is as follows::
  6. from paraview import smtrace
  7. config = smtracer.start_trace()
  8. # config is an instance of vtkSMTrace. One can setup properties on this
  9. # object to control the generated trace. e.g.
  10. config.SetFullyTraceSupplementalProxies(True)
  11. # do the actions to trace.
  12. ...
  13. # stop trace. The generated trace is returned.
  14. txt = smtracer.stop_trace()
  15. =========================
  16. Developer Documentation
  17. =========================
  18. This section describes the module design for developers wanted to extend this
  19. module or use this module for advance tracing/state generation.
  20. The design can be described as follows:
  21. C++ code (either in ServerManager or the GUI layer) should trace actions.
  22. This is done using SM_SCOPED_TRACE() macro provided by vtkSMTrace. When tracing
  23. is enabled, each SM_SCOPED_TRACE() call creates a :class:`.TraceItem`. The TraceItem
  24. instance is scoped, i.e. the object is finalized and destroyed when the scope
  25. exits.
  26. There are various types of TraceItem, ranging from those that trace specific
  27. action such as :class:`.Show`, or those that trace any modified properties
  28. (:class:`.PropertiesModified`). Generic TraceItem types, such as
  29. :class:`.CallMethod` and :class:`.CallFunction` can be used to trace methods
  30. called on vtkObject instances or functions in the global namespace.
  31. TraceItems create or use :class:`.Accessor` instances. Accessors are objects
  32. created for Proxies and Properties in ParaView. Accessor knows how to access
  33. that proxy or property in the Python trace. TraceItems that create new proxies
  34. such as :class:`.RegisterPipelineProxy` and :class:`.RegisterViewProxy`, create
  35. new :class:`.ProxyAccessor` instances. Other such as
  36. :class:`.PropertiesModified` trace item rely on accessors already created.
  37. :class:`.Trace` can provide access to already created accessor as well as create
  38. new accessor for proxies create before the tracing began
  39. (:method:`.Trace.get_accessor`).
  40. Additionally, there are filters such as :class:`.ProxyFilter`,
  41. :class:`.PipelineProxyFilter`, etc. which are used to filter properties that get
  42. traced and where they get traced i.e. in constructor call or right after it.
  43. ===============================
  44. Notes about references
  45. ===============================
  46. RealProxyAccessor keeps a hard reference to the servermanager.Proxy instance.
  47. This is required. If we don't, then the Python object for vtkSMProxy also gets
  48. garbage collected since there's no reference to it.
  49. """
  50. from __future__ import absolute_import, division, print_function
  51. import weakref
  52. import paraview.servermanager as sm
  53. import paraview.simple as simple
  54. import sys
  55. from paraview.vtk import vtkTimeStamp
  56. if sys.version_info >= (3,):
  57. xrange = range
  58. def _get_skip_rendering():
  59. return sm.vtkSMTrace.GetActiveTracer().GetSkipRenderingComponents()
  60. def _pretty_print(object, indent=4, width=80, prefix=""):
  61. from pprint import pformat
  62. offset = len(prefix)
  63. txt = pformat(object, indent=indent, width=(width - offset))
  64. # now lets indent all of the lines based on prefix length
  65. spaces = "\n" + " " * offset
  66. return prefix + spaces.join(txt.split("\n"))
  67. class TraceOutput:
  68. """Internal class used to collect the trace output. Everytime anything is pushed into
  69. this using the append API, we ensure that the trace is updated. Trace
  70. doesn't put commands to the trace-output as soon as modifications are noticed
  71. to try to consolidate the state changes."""
  72. def __init__(self, data=None):
  73. self.__data = []
  74. self.append(data) if data else None
  75. def append(self, data):
  76. if isinstance(data, list):
  77. self.__data += data
  78. #print ("\n".join(data),"\n")
  79. elif isinstance(data, str):
  80. self.__data.append(data)
  81. #print (data,"\n")
  82. def append_separator(self):
  83. try:
  84. self.__data.append("") if self.__data[-1] != "" else None
  85. except IndexError:
  86. pass
  87. def append_separated(self, data):
  88. self.append_separator()
  89. self.append(data)
  90. def __str__(self):
  91. return '\n'.join(self.__data)
  92. def raw_data(self): return self.__data
  93. def reset(self): self.__data = []
  94. class Trace(object):
  95. __REGISTERED_ACCESSORS = {}
  96. Output = None
  97. @classmethod
  98. def reset(cls):
  99. """Resets the Output and clears all register accessors."""
  100. cls.__REGISTERED_ACCESSORS.clear()
  101. cls.Output = TraceOutput()
  102. @classmethod
  103. def get_registered_name(cls, proxy, reggroup):
  104. """Returns the registered name for `proxy` in the given `reggroup`."""
  105. return proxy.SMProxy.GetSessionProxyManager().GetProxyName(reggroup, proxy.SMProxy)
  106. @classmethod
  107. def get_varname(cls, name):
  108. """returns an unique variable name given a suggested variable name. If
  109. the suggested variable name is already taken, this method will try to
  110. find a good suffix that's available."""
  111. name = sm._make_name_valid(name)
  112. name = name[0].lower() + name[1:]
  113. original_name = name
  114. suffix = 1
  115. # build a set of existing variable names
  116. varnameset = set([accessor.Varname for accessor in cls.__REGISTERED_ACCESSORS.values()])
  117. while name in varnameset:
  118. name = "%s_%d" % (original_name, suffix)
  119. suffix += 1
  120. return name
  121. @classmethod
  122. def register_accessor(cls, accessor):
  123. """Register an instance of an Accessor or subclass"""
  124. cls.__REGISTERED_ACCESSORS[accessor.get_object()] = accessor
  125. @classmethod
  126. def unregister_accessor(cls, accessor):
  127. if accessor.get_object() in cls.__REGISTERED_ACCESSORS:
  128. del cls.__REGISTERED_ACCESSORS[accessor.get_object()]
  129. @classmethod
  130. def get_accessor(cls, obj):
  131. """Returns an accessor for obj. If none exists, a new one may be
  132. created, if possible. Currently obj is expected to be a
  133. :class:`servermanager.Proxy` instance. In future, we may change this to
  134. be a vtkSMProxy instance instead."""
  135. if obj is None:
  136. return None
  137. assert isinstance(obj, sm.Proxy)
  138. try:
  139. return cls.__REGISTERED_ACCESSORS[obj]
  140. except KeyError:
  141. # Create accessor if possible else raise
  142. # "untraceable" exception.
  143. if cls.create_accessor(obj):
  144. return cls.__REGISTERED_ACCESSORS[obj]
  145. #return "<unknown>"
  146. raise Untraceable(
  147. "%s is not 'known' at this point. Hence, we cannot trace "\
  148. "it. Skipping this action." % repr(obj))
  149. @classmethod
  150. def has_accessor(cls, obj):
  151. return obj in cls.__REGISTERED_ACCESSORS
  152. @classmethod
  153. def create_accessor(cls, obj):
  154. """Create a new accessor for a proxy. This returns True when a
  155. ProxyAccessor has been created, other returns False. This is needed to
  156. bring into trace proxies that were either already created when the trace
  157. was started or were created indirectly and hence not explicitly traced."""
  158. skip_rendering = _get_skip_rendering()
  159. if isinstance(obj, sm.SourceProxy):
  160. # handle pipeline source/filter proxy.
  161. pname = obj.SMProxy.GetSessionProxyManager().GetProxyName("sources", obj.SMProxy)
  162. if pname:
  163. if obj == simple.GetActiveSource():
  164. accessor = ProxyAccessor(cls.get_varname(pname), obj)
  165. cls.Output.append_separated([\
  166. "# get active source.",
  167. "%s = GetActiveSource()" % accessor])
  168. else:
  169. accessor = ProxyAccessor(cls.get_varname(pname), obj)
  170. cls.Output.append_separated([\
  171. "# find source",
  172. "%s = FindSource('%s')" % (accessor, pname)])
  173. return True
  174. if not skip_rendering and obj.SMProxy.IsA("vtkSMViewProxy"):
  175. # handle view proxy.
  176. pname = obj.SMProxy.GetSessionProxyManager().GetProxyName("views", obj.SMProxy)
  177. if pname:
  178. trace = TraceOutput()
  179. accessor = ProxyAccessor(cls.get_varname(pname), obj)
  180. if obj == simple.GetActiveView():
  181. trace.append("# get active view")
  182. trace.append("%s = GetActiveViewOrCreate('%s')" % (accessor, obj.GetXMLName()))
  183. else:
  184. ctor_args = "'%s', viewtype='%s'" % (pname, obj.GetXMLName())
  185. trace.append("# find view")
  186. trace.append("%s = FindViewOrCreate(%s)" % (accessor, ctor_args))
  187. cls.Output.append_separated(trace.raw_data())
  188. return True
  189. if not skip_rendering and obj.SMProxy.IsA("vtkSMRepresentationProxy"):
  190. # handle representations.
  191. if hasattr(obj, "Input"):
  192. inputAccsr = cls.get_accessor(obj.Input)
  193. view = simple.LocateView(obj)
  194. viewAccessor = cls.get_accessor(view)
  195. pname = obj.SMProxy.GetSessionProxyManager().GetProxyName("representations", obj.SMProxy)
  196. if pname:
  197. varname = "%sDisplay" % inputAccsr
  198. accessor = ProxyAccessor(cls.get_varname(varname), obj)
  199. cls.Output.append_separated([\
  200. "# get display properties",
  201. "%s = GetDisplayProperties(%s, view=%s)" %\
  202. (accessor, inputAccsr, viewAccessor)])
  203. return True
  204. if not skip_rendering and cls.get_registered_name(obj, "lookup_tables"):
  205. pname = cls.get_registered_name(obj, "lookup_tables")
  206. if cls._create_accessor_for_tf(obj, pname):
  207. return True
  208. if not skip_rendering and cls.get_registered_name(obj, "piecewise_functions"):
  209. pname = cls.get_registered_name(obj, "piecewise_functions")
  210. if cls._create_accessor_for_tf(obj, pname):
  211. return True
  212. if not skip_rendering and cls.get_registered_name(obj, "transfer_2d_functions"):
  213. pname = cls.get_registered_name(obj, "transfer_2d_functions")
  214. if cls._create_accessor_for_tf(obj, pname):
  215. return True
  216. if not skip_rendering and cls.get_registered_name(obj, "scalar_bars"):
  217. # trace scalar bar.
  218. lutAccessor = cls.get_accessor(obj.LookupTable)
  219. view = simple.LocateView(obj)
  220. viewAccessor = cls.get_accessor(view)
  221. varname = cls.get_varname("%sColorBar" % lutAccessor)
  222. accessor = ProxyAccessor(varname, obj)
  223. trace = TraceOutput()
  224. trace.append(\
  225. "# get color legend/bar for %s in view %s" % (lutAccessor, viewAccessor))
  226. trace.append(accessor.trace_ctor(\
  227. "GetScalarBar",
  228. SupplementalProxy(ScalarBarProxyFilter()),
  229. ctor_args="%s, %s" % (lutAccessor, viewAccessor)))
  230. cls.Output.append_separated(trace.raw_data())
  231. return True
  232. if cls.get_registered_name(obj, "animation"):
  233. return cls._create_accessor_for_animation_proxies(obj)
  234. if obj.SMProxy.GetXMLName() == "RepresentationAnimationHelper":
  235. sourceAccessor = cls.get_accessor(obj.Source)
  236. varname = cls.get_varname("%sRepresentationAnimationHelper" % (sourceAccessor))
  237. accessor = ProxyAccessor(varname, obj)
  238. cls.Output.append_separated([\
  239. " # get animation representation helper for '%s'" % (sourceAccessor),
  240. "%s = GetRepresentationAnimationHelper(%s)" % (accessor, sourceAccessor)])
  241. return True
  242. if not skip_rendering and cls.get_registered_name(obj, "layouts"):
  243. view = simple.GetActiveView()
  244. if view and obj.GetViewLocation(view.SMProxy) != -1:
  245. viewAccessor = cls.get_accessor(view)
  246. varname = cls.get_varname(cls.get_registered_name(obj, "layouts"))
  247. accessor = ProxyAccessor(varname, obj)
  248. cls.Output.append_separated([\
  249. "# get layout",
  250. "%s = GetLayout()" % accessor])
  251. return True
  252. else:
  253. varname = cls.get_varname(cls.get_registered_name(obj, "layouts"))
  254. accessor = ProxyAccessor(varname, obj)
  255. cls.Output.append_separated([\
  256. "# get layout",
  257. "%s = GetLayoutByName(\"%s\")" % (accessor, cls.get_registered_name(obj, "layouts"))])
  258. return True
  259. if obj.SMProxy.IsA("vtkSMTimeKeeperProxy"):
  260. tkAccessor = ProxyAccessor(cls.get_varname(cls.get_registered_name(obj, "timekeeper")), obj)
  261. cls.Output.append_separated([\
  262. "# get the time-keeper",
  263. "%s = GetTimeKeeper()" % tkAccessor])
  264. return True
  265. if obj.SMProxy.IsA("vtkSMSettingsProxy"):
  266. pname = obj.SMProxy.GetSessionProxyManager().GetProxyName("settings", obj.SMProxy)
  267. accessor = ProxyAccessor(cls.get_varname(pname), obj)
  268. cls.Output.append_separated([\
  269. "# find settings proxy",
  270. "%s = GetSettingsProxy('%s')" % (accessor, pname)])
  271. return True
  272. if not skip_rendering and obj.GetVTKClassName() == "vtkPVLight":
  273. view = simple.GetViewForLight(obj)
  274. if view:
  275. index = view.AdditionalLights.index(obj)
  276. viewAccessor = cls.get_accessor(view)
  277. accessor = ProxyAccessor(cls.get_varname(cls.get_registered_name(obj, "additional_lights")), obj)
  278. cls.Output.append_separated([\
  279. "# get light",
  280. "%s = GetLight(%s, %s)" % (accessor, index, viewAccessor)])
  281. else:
  282. # create a new light, should be handled by RegisterLightProxy
  283. accessor = ProxyAccessor(cls.get_varname(cls.get_registered_name(obj, "additional_lights")), obj)
  284. cls.Output.append_separated([\
  285. "# create a new light",
  286. "%s = CreateLight()" % (accessor)])
  287. return True
  288. if not skip_rendering and obj.SMProxy.IsA("vtkSMMaterialLibraryProxy"):
  289. tkAccessor = ProxyAccessor(cls.get_varname(cls.get_registered_name(obj, "materiallibrary")), obj)
  290. cls.Output.append_separated([\
  291. "# get the material library",
  292. "%s = GetMaterialLibrary()" % tkAccessor])
  293. return True
  294. if not skip_rendering and obj.GetVTKClassName() == "vtkTexture":
  295. pname = obj.SMProxy.GetSessionProxyManager().GetProxyName("textures", obj.SMProxy)
  296. if pname:
  297. accessor = ProxyAccessor(cls.get_varname(pname), obj)
  298. filename = obj.FileName
  299. # repr() will escape the path correctly, especially backslash on Windows.
  300. cls.Output.append_separated([\
  301. "# a texture",
  302. "%s = CreateTexture(%s)" % (accessor, repr(filename))])
  303. return True
  304. if cls.get_registered_name(obj, "extractors"):
  305. regName = cls.get_registered_name(obj, "extractors")
  306. accessor = ProxyAccessor(cls.get_varname(regName), obj)
  307. cls.Output.append_separated([\
  308. "# get extractor",
  309. "%s = FindExtractor('%s')" % (accessor, regName)])
  310. return False
  311. @classmethod
  312. def rename_separate_tf_and_get_representation(cls, arrayName):
  313. import re
  314. representation = None
  315. varname = arrayName
  316. regex = re.compile(r"(^Separate_)([0-9]*)_(.*$)")
  317. if re.match(regex, arrayName):
  318. gid = re.sub(regex, "\g<2>", arrayName)
  319. representation = next((value for key, value in simple.GetRepresentations().items() if key[1] == gid), None)
  320. if representation:
  321. repAccessor = Trace.get_accessor(representation)
  322. arrayName = re.sub(regex, "\g<3>", arrayName)
  323. varname = ("Separate_%s_%s" % (repAccessor, arrayName))
  324. return arrayName, varname, representation
  325. @classmethod
  326. def _create_accessor_for_tf(cls, proxy, regname):
  327. import re
  328. m = re.match("^[0-9.]*(.+)\\.%s$" % proxy.GetXMLName(), regname)
  329. if m:
  330. arrayName = m.group(1)
  331. if proxy.GetXMLGroup() == "lookup_tables":
  332. arrayName, varname, rep = cls.rename_separate_tf_and_get_representation(arrayName)
  333. if rep:
  334. repAccessor = Trace.get_accessor(rep)
  335. args = ("'%s', %s, separate=True" % (arrayName, repAccessor))
  336. comment = "separate color transfer function/color map"
  337. else :
  338. args = ("'%s'" % arrayName)
  339. comment = "color transfer function/color map"
  340. method = "GetColorTransferFunction"
  341. varsuffix = "LUT"
  342. elif proxy.GetXMLGroup() == "piecewise_functions":
  343. arrayName, varname, rep = cls.rename_separate_tf_and_get_representation(arrayName)
  344. if rep:
  345. repAccessor = Trace.get_accessor(rep)
  346. args = ("'%s', %s, separate=True" % (arrayName, repAccessor))
  347. comment = "separate opacity transfer function/opacity map"
  348. else :
  349. args = ("'%s'" % arrayName)
  350. comment = "opacity transfer function/opacity map"
  351. method = "GetOpacityTransferFunction"
  352. varsuffix = "PWF"
  353. else:
  354. arrayName, varname, rep = cls.rename_separate_tf_and_get_representation(arrayName)
  355. if rep:
  356. repAccessor = Trace.get_accessor(rep)
  357. args = ("'%s', %s, separate=True" % (arrayName, repAccessor))
  358. comment = "separate 2D transfer function"
  359. else :
  360. args = ("'%s'" % arrayName)
  361. comment = "2D transfer function"
  362. method = "GetTransferFunction2D"
  363. varsuffix = "TF2D"
  364. varname = cls.get_varname("%s%s" % (varname, varsuffix))
  365. accessor = ProxyAccessor(varname, proxy)
  366. #cls.Output.append_separated([\
  367. # "# get %s for '%s'" % (comment, arrayName),
  368. # "%s = %s('%s')" % (accessor, method, arrayName)])
  369. trace = TraceOutput()
  370. trace.append("# get %s for '%s'" % (comment, arrayName))
  371. trace.append(accessor.trace_ctor(\
  372. method, SupplementalProxy(TransferFunctionProxyFilter()), ctor_args = args))
  373. cls.Output.append_separated(trace.raw_data())
  374. return True
  375. return False
  376. @classmethod
  377. def _create_accessor_for_animation_proxies(cls, obj):
  378. pname = cls.get_registered_name(obj, "animation")
  379. if obj == simple.GetAnimationScene():
  380. sceneAccessor = ProxyAccessor(cls.get_varname(pname), obj)
  381. cls.Output.append_separated([\
  382. "# get animation scene",
  383. "%s = GetAnimationScene()" % sceneAccessor])
  384. return True
  385. if obj == simple.GetTimeTrack():
  386. accessor = ProxyAccessor(cls.get_varname(pname), obj)
  387. cls.Output.append_separated([\
  388. "# get time animation track",
  389. "%s = GetTimeTrack()" % accessor])
  390. return True
  391. if obj.GetXMLName() == "CameraAnimationCue":
  392. # handle camera animation cue.
  393. view = obj.AnimatedProxy
  394. viewAccessor = cls.get_accessor(view)
  395. accessor = ProxyAccessor(cls.get_varname(pname), obj)
  396. cls.Output.append_separated([\
  397. "# get camera animation track for the view",
  398. "%s = GetCameraTrack(view=%s)" % (accessor, viewAccessor)])
  399. return True
  400. if obj.GetXMLName() == "KeyFrameAnimationCue":
  401. animatedProxyAccessor = cls.get_accessor(obj.AnimatedProxy)
  402. animatedElement = int(obj.AnimatedElement)
  403. animatedPropertyName = obj.AnimatedPropertyName
  404. varname = cls.get_varname("%s%sTrack" % (animatedProxyAccessor, animatedPropertyName))
  405. accessor = ProxyAccessor(varname, obj)
  406. cls.Output.append_separated([\
  407. "# get animation track",
  408. "%s = GetAnimationTrack('%s', index=%d, proxy=%s)" %\
  409. (accessor, animatedPropertyName, animatedElement, animatedProxyAccessor)])
  410. return True
  411. if obj.GetXMLName() == "PythonAnimationCue":
  412. raise Untraceable("PythonAnimationCue's are currently not supported in trace")
  413. if obj.GetXMLGroup() == "animation_keyframes":
  414. accessor = ProxyAccessor(cls.get_varname(pname), obj)
  415. ctor = sm._make_name_valid(obj.GetXMLLabel())
  416. cls.Output.append_separated("# create a new key frame")
  417. cls.Output.append(accessor.trace_ctor(ctor, ProxyFilter()))
  418. return True
  419. return False
  420. class Untraceable(Exception):
  421. def __init__(self, logmessage="<unspecified>"):
  422. self.LogMessage = logmessage
  423. def __str__(self):
  424. return repr(self.LogMessage)
  425. class Accessor(object):
  426. def __init__(self, varname, obj):
  427. self.Varname = varname
  428. self.__Object = obj
  429. Trace.register_accessor(self)
  430. def finalize(self):
  431. Trace.unregister_accessor(self)
  432. self.__Object = None
  433. def __str__(self):
  434. return self.Varname
  435. def get_object(self):
  436. return self.__Object
  437. class RealProxyAccessor(Accessor):
  438. __CreateCallbacks = []
  439. @classmethod
  440. def register_create_callback(cls, function):
  441. cls.__CreateCallbacks.insert(0, function)
  442. @classmethod
  443. def unregister_create_callback(cls, function):
  444. cls.__CreateCallbacks.remove(function)
  445. @classmethod
  446. def create(cls, *args, **kwargs):
  447. for x in cls.__CreateCallbacks:
  448. try:
  449. return x(*args, **kwargs)
  450. except NotImplementedError: pass
  451. return RealProxyAccessor(*args, **kwargs)
  452. def __init__(self, varname, proxy):
  453. Accessor.__init__(self, varname, proxy)
  454. self.OrderedProperties = []
  455. # Create accessors for properties on this proxy.
  456. oiter = sm.vtkSMOrderedPropertyIterator()
  457. oiter.SetProxy(proxy.SMProxy)
  458. while not oiter.IsAtEnd():
  459. prop_name = oiter.GetKey()
  460. prop_label = oiter.GetPropertyLabel()
  461. sanitized_label = sm._make_name_valid(prop_label)
  462. prop = proxy.GetProperty(prop_name)
  463. if not type(prop) == sm.Property:
  464. # Note: when PropertyTraceHelper for a property with ProxyListDomain is
  465. # created, it creates accessors for all proxies in the domain as well.
  466. prop_accessor = PropertyTraceHelper(sanitized_label, self)
  467. self.OrderedProperties.append(prop_accessor)
  468. oiter.Next()
  469. del oiter
  470. def finalize(self):
  471. for x in self.OrderedProperties:
  472. x.finalize()
  473. Accessor.finalize(self)
  474. def get_property(self, name):
  475. for x in self.OrderedProperties:
  476. if x.get_property_name() == name:
  477. return x
  478. return None
  479. def get_properties(self):
  480. return self.OrderedProperties[:]
  481. def get_ctor_properties(self):
  482. """Returns a list of property accessors that should be specified
  483. in the constructor."""
  484. return [x for x in self.OrderedProperties if self.is_ctor_property(x)]
  485. def is_ctor_property(self, prop):
  486. return prop.get_object().IsA("vtkSMInputProperty") or \
  487. prop.get_object().FindDomain("vtkSMFileListDomain") != None
  488. def trace_properties(self, props, in_ctor):
  489. joiner = ",\n " if in_ctor else "\n"
  490. return joiner.join([x.get_property_trace(in_ctor) for x in props])
  491. def trace_ctor(self, ctor, filter, ctor_args=None, skip_assignment=False,
  492. ctor_var=None, ctor_extra_args=None):
  493. args_in_ctor = str(ctor_args) if ctor_args is not None else ""
  494. # trace any properties that the 'filter' tells us should be traced
  495. # in ctor.
  496. ctor_props = [x for x in self.OrderedProperties if filter.should_trace_in_ctor(x)]
  497. ctor_props_trace = self.trace_properties(ctor_props, in_ctor=True)
  498. if args_in_ctor and ctor_props_trace:
  499. args_in_ctor = "%s, %s" % (args_in_ctor, ctor_props_trace)
  500. else:
  501. args_in_ctor += ctor_props_trace
  502. if args_in_ctor and ctor_extra_args:
  503. args_in_ctor = "%s, %s" % (args_in_ctor, ctor_extra_args)
  504. elif ctor_extra_args:
  505. args_in_ctor = ctor_extra_args
  506. # locate all the other properties that should be traced in create.
  507. other_props = [x for x in self.OrderedProperties \
  508. if filter.should_trace_in_create(x) and not filter.should_trace_in_ctor(x)]
  509. trace = TraceOutput()
  510. if not ctor is None:
  511. if not skip_assignment:
  512. trace.append("%s = %s(%s)" % (self, ctor, args_in_ctor))
  513. else:
  514. assert len(other_props) == 0
  515. trace.append("%s(%s)" % (ctor, args_in_ctor))
  516. return trace.raw_data()
  517. # FIXME: would like trace_properties() to return a list instead of
  518. # a string.
  519. txt = self.trace_properties(other_props, in_ctor=False)
  520. if txt: trace.append(txt)
  521. # Now, if any of the props has ProxyListDomain, we should trace their
  522. # "ctors" as well. Tracing ctors for ProxyListDomain proxies simply
  523. # means tracing their property values.
  524. pld_props = [x for x in self.OrderedProperties if x.has_proxy_list_domain()]
  525. for prop in pld_props:
  526. paccessor = Trace.get_accessor(prop.get_property_value())
  527. if not prop.DisableSubTrace:
  528. sub_trace = paccessor.trace_ctor(None, filter)
  529. if sub_trace:
  530. trace.append_separated(\
  531. "# init the %s selected for '%s'" % (prop.get_value(), prop.get_property_name()))
  532. trace.append(sub_trace)
  533. return trace.raw_data()
  534. def ProxyAccessor(*args, **kwargs):
  535. return RealProxyAccessor.create(*args, **kwargs)
  536. class PropertyTraceHelper(object):
  537. """PropertyTraceHelper is used by RealProxyAccessor to help with tracing
  538. properites. In its constructor, RealProxyAccessor creates a
  539. PropertyTraceHelper for each of its properties that could potentially need
  540. to be traced."""
  541. def __init__(self, propertyname, proxyAccessor):
  542. """Constructor.
  543. :param propertyname: name used to access the property. This is the
  544. sanitized property label.
  545. :param proxyAccessor: RealProxyAccessor instance for the proxy.
  546. """
  547. assert isinstance(proxyAccessor, RealProxyAccessor)
  548. assert type(propertyname) == str
  549. self.__PyProperty = None
  550. self.PropertyName = propertyname
  551. self.ProxyAccessor = proxyAccessor
  552. self.FullScopedName = "%s.%s" % (proxyAccessor, propertyname)
  553. pyprop = self.get_object()
  554. assert not pyprop is None
  555. pld_domain = pyprop.FindDomain("vtkSMProxyListDomain")
  556. self.HasProxyListDomain = isinstance(pyprop, sm.ProxyProperty) and pld_domain != None
  557. self.ProxyListDomainProxyAccessors = []
  558. self.DisableSubTrace = pyprop.GetDisableSubTrace()
  559. if self.HasProxyListDomain:
  560. # register accessors for proxies in the proxy list domain.
  561. # This is cheating. Since there's no accessor for a proxy in the domain
  562. # unless the proxy is "active" in the property. However, since ParaView
  563. # UI never modifies the other properties, we cheat
  564. for i in xrange(pld_domain.GetNumberOfProxies()):
  565. domain_proxy = pld_domain.GetProxy(i)
  566. plda = ProxyAccessor(self.get_varname(), sm._getPyProxy(domain_proxy))
  567. self.ProxyListDomainProxyAccessors.append(plda)
  568. def __del__(self):
  569. self.finalize()
  570. def finalize(self):
  571. for x in self.ProxyListDomainProxyAccessors:
  572. x.finalize()
  573. self.ProxyListDomainProxyAccessors = []
  574. def get_object(self):
  575. """Returns the servermanager.Property (or subclass) for the
  576. vtkSMProperty this trace helper is helping with."""
  577. if self.__PyProperty is None or self.__PyProperty() is None:
  578. # This will raise Untraceable exception is the ProxyAccessor cannot
  579. # locate the servermanager.Proxy for the SMProxy it refers to.
  580. pyproperty = self.ProxyAccessor.get_object().GetProperty(self.get_property_name())
  581. self.__PyProperty = weakref.ref(pyproperty)
  582. return pyproperty
  583. return self.__PyProperty()
  584. def get_property_trace(self, in_ctor):
  585. """return trace-text for the property.
  586. :param in_ctor: If False, the trace is generated trace will use
  587. fully-scoped name when referring to the property e.g.
  588. sphere0.Radius=2, else it will use just the property name, *e.g.*,
  589. Radius=2.
  590. """
  591. varname = self.get_varname(in_ctor)
  592. if in_ctor: return "%s=%s" % (varname, self.get_value())
  593. else: return "%s = %s" % (varname, self.get_value())
  594. def get_varname(self, not_fully_scoped=False):
  595. """Returns the variable name to use when referring to this property.
  596. :param not_fully_scoped: If False, this will return
  597. fully-scoped name when referring to the property e.g. sphere0.Radius,
  598. else it will use just the property name, *e.g.*, Radius.
  599. """
  600. return self.PropertyName if not_fully_scoped else self.FullScopedName
  601. def get_value(self):
  602. """Returns the property value as a string. For proxy properties, this
  603. will either be a string used to refer to another proxy or a string used
  604. to refer to the proxy in a proxy list domain."""
  605. myobject = self.get_object()
  606. fileListDomain = myobject.SMProperty.FindDomain("vtkSMFileListDomain")
  607. if isinstance(myobject, sm.ProxyProperty):
  608. data = myobject[:]
  609. if self.has_proxy_list_domain():
  610. data = ["'%s'" % x.GetXMLLabel() for x in data]
  611. else:
  612. data = [str(Trace.get_accessor(x)) for x in data]
  613. if isinstance(myobject, sm.InputProperty):
  614. # this is an input property, we may have to hook on to a
  615. # non-zero output port. If so, we trace `OutputPort(source,
  616. # port)`, else we just trace `source`.
  617. # Fixes #17035
  618. ports = [myobject.GetOutputPortForConnection(x) for x in range(len(data))]
  619. data = [src if port==0 else "OutputPort(%s,%d)" % (src,port) \
  620. for src,port in zip(data, ports)]
  621. try:
  622. if len(data) > 1:
  623. return "[%s]" % (", ".join(data))
  624. else:
  625. return data[0]
  626. except IndexError:
  627. return "None"
  628. elif myobject.SMProperty.IsA("vtkSMStringVectorProperty") and not (fileListDomain and fileListDomain.GetIsOptional() == 0):
  629. # handle multiline properties (see #18480)
  630. # but, not if the property is a list of files (see #21100)
  631. return self.create_multiline_string(repr(myobject))
  632. else:
  633. return repr(myobject)
  634. def has_proxy_list_domain(self):
  635. """Returns True if this property has a ProxyListDomain, else False."""
  636. return self.HasProxyListDomain
  637. def get_property_name(self):
  638. return self.PropertyName
  639. def get_property_value(self):
  640. """Return the Property value as would be returned by
  641. servermanager.Proxy.GetPropertyValue()."""
  642. return self.ProxyAccessor.get_object().GetPropertyValue(self.get_property_name())
  643. def get_proxy(self):
  644. return self.ProxyAccessor.get_object()
  645. def create_multiline_string(self, astr):
  646. """helper to convert a string representation into a multiline string"""
  647. if '\\n' in astr:
  648. # this happens for multiline string-vector properties.
  649. # for those, we ensure that the `astr` has raw \n's rather than
  650. # the escaped version. we also fix the string indicators.
  651. # replace '\\n' with real '\n'
  652. astr = astr.replace('\\n','\n')
  653. # escape any `"""` in the script
  654. astr = astr.replace('"""', '\\"\\"\\"')
  655. # replace first and last characters with `"""`
  656. astr = '"""' + astr[1:-1] + '"""'
  657. return astr
  658. # ===================================================================================================
  659. # === Filters used to filter properties traced ===
  660. # ===================================================================================================
  661. class ProxyFilter(object):
  662. def __init__(self, trace_all_in_ctor=False):
  663. self.trace_all_in_ctor = trace_all_in_ctor
  664. def should_never_trace(self, prop, hide_gui_hidden=True):
  665. if prop.get_object().GetIsInternal() or prop.get_object().GetInformationOnly():
  666. return True
  667. # should we hide properties hidden from panels? yes, generally, except
  668. # Views.
  669. if hide_gui_hidden == True and prop.get_object().GetPanelVisibility() == "never":
  670. return True
  671. # if a property is "linked" to settings, then skip it here too. We
  672. # should eventually add an option for user to save, yes, save these too.
  673. # Note, however, any property that is linked with `unlink_if_modified`
  674. # set to 1 should be traced if the link no longer persists -- indicating
  675. # the user manually changed it (and hence broke the link).
  676. if prop.get_object().GetHints():
  677. plink = prop.get_object().GetHints().FindNestedElementByName("PropertyLink")
  678. if plink and plink.GetAttribute("group") == "settings":
  679. if plink.GetAttribute("unlink_if_modified") != "1":
  680. return True
  681. proxy = prop.get_proxy()
  682. pxm = proxy.GetSessionProxyManager()
  683. settings = pxm.GetProxy("settings", plink.GetAttribute("proxy"))
  684. if settings and settings.GetSourcePropertyName(proxy.SMProxy, prop.get_property_name()):
  685. # the settings link still exists, skip tracing
  686. return True
  687. return False
  688. def should_trace_in_create(self, prop, user_can_modify_in_create=True):
  689. if self.should_never_trace(prop): return False
  690. setting = sm.vtkSMTrace.GetActiveTracer().GetPropertiesToTraceOnCreate()
  691. if setting == sm.vtkSMTrace.RECORD_USER_MODIFIED_PROPERTIES and not user_can_modify_in_create:
  692. # In ParaView, user never changes properties in Create. It's only
  693. # afterwords, so skip all properties.
  694. return False
  695. trace_props_with_default_values = True \
  696. if setting == sm.vtkSMTrace.RECORD_ALL_PROPERTIES else False
  697. return (trace_props_with_default_values or not prop.get_object().IsValueDefault())
  698. def should_trace_in_ctor(self, prop):
  699. return False if not self.trace_all_in_ctor else \
  700. (not self.should_never_trace(prop) and self.should_trace_in_create(prop))
  701. class PipelineProxyFilter(ProxyFilter):
  702. def should_trace_in_create(self, prop):
  703. return ProxyFilter.should_trace_in_create(self, prop, user_can_modify_in_create=False)
  704. def should_never_trace(self, prop):
  705. """overridden to avoid hiding "non-gui" properties such as FileName."""
  706. # should we hide properties hidden from panels?
  707. if not prop.get_object().FindDomain("vtkSMFileListDomain") is None:
  708. return False
  709. else:
  710. return ProxyFilter.should_never_trace(self, prop)
  711. def should_trace_in_ctor(self, prop):
  712. if self.should_never_trace(prop): return False
  713. return prop.get_object().IsA("vtkSMInputProperty") or \
  714. prop.get_object().FindDomain("vtkSMFileListDomain") != None
  715. class ExodusIIReaderFilter(PipelineProxyFilter):
  716. def should_never_trace(self, prop):
  717. if PipelineProxyFilter.should_never_trace(self, prop): return True
  718. # Exodus reader has way too many wacky properties tracing them causes
  719. # the reader to segfault. We need to either remove those properties
  720. # entirely or fix them. Until I get a chance to get to the bottom of it,
  721. # I am opting to ignore those properties when tracing.
  722. return prop.get_property_name() in [\
  723. "FilePrefix", "XMLFileName", "FilePattern", "FileRange"]
  724. class ExtractSelectionFilter(PipelineProxyFilter):
  725. def should_never_trace(self, prop):
  726. if PipelineProxyFilter.should_never_trace(self, prop): return True
  727. # Selections are not registered with the proxy manager, so we will not try to trace them.
  728. return prop.get_property_name() in ["Selection"]
  729. class RepresentationProxyFilter(PipelineProxyFilter):
  730. def should_trace_in_ctor(self, prop): return False
  731. def should_never_trace(self, prop):
  732. if PipelineProxyFilter.should_never_trace(self, prop): return True
  733. if prop.get_property_name() in ["Input",\
  734. "SelectionCellFieldDataArrayName",\
  735. "SelectionPointFieldDataArrayName"] : return True
  736. return False
  737. def should_trace_in_create(self, prop):
  738. """for representations, we always trace the 'Representation' property,
  739. even when it's same as the default value (see issue #17196)."""
  740. if prop.get_object().FindDomain("vtkSMRepresentationTypeDomain"):
  741. return True
  742. return PipelineProxyFilter.should_trace_in_create(self, prop)
  743. class ViewProxyFilter(ProxyFilter):
  744. def should_never_trace(self, prop):
  745. # skip "Representations" property and others.
  746. # The fact that we need to skip so many properties means that we are
  747. # missing something in the design of vtkSMProperties here. We need to
  748. # reclassify properties to cleanly address all its "roles".
  749. if prop.get_property_name() in [\
  750. "ViewTime", "CacheKey", "Representations"]: return True
  751. return ProxyFilter.should_never_trace(self, prop, hide_gui_hidden=False)
  752. class AnimationProxyFilter(ProxyFilter):
  753. def should_never_trace(self, prop):
  754. if ProxyFilter.should_never_trace(self, prop): return True
  755. if prop.get_property_name() in ["AnimatedProxy", "AnimatedPropertyName",
  756. "AnimatedElement", "AnimatedDomainName"]:
  757. return True
  758. return False
  759. class ExporterProxyFilter(ProxyFilter):
  760. def should_trace_in_ctor(self, prop):
  761. return not self.should_never_trace(prop) and self.should_trace_in_create(prop)
  762. def should_never_trace(self, prop):
  763. if ProxyFilter.should_never_trace(self, prop): return True
  764. if prop.get_property_name() == "FileName" : return True
  765. return False
  766. class WriterProxyFilter(ProxyFilter):
  767. def should_trace_in_ctor(self, prop):
  768. return not self.should_never_trace(prop) and self.should_trace_in_create(prop)
  769. def should_never_trace(self, prop):
  770. if ProxyFilter.should_never_trace(self, prop): return True
  771. if prop.get_property_name() in ["FileName", "Input"] : return True
  772. return False
  773. class ScreenShotHelperProxyFilter(ProxyFilter):
  774. def should_never_trace(self, prop):
  775. if prop.get_property_name() == "Format": return True
  776. return ProxyFilter.should_never_trace(self, prop)
  777. def should_trace_in_ctor(self, prop):
  778. return not self.should_never_trace(prop) and self.should_trace_in_create(prop)
  779. class TransferFunctionProxyFilter(ProxyFilter):
  780. def should_trace_in_ctor(self, prop): return False
  781. def should_never_trace(self, prop):
  782. if ProxyFilter.should_never_trace(self, prop, hide_gui_hidden=False): return True
  783. if prop.get_property_name() in ["ScalarOpacityFunction"]: return True
  784. return False
  785. class ScalarBarProxyFilter(ProxyFilter):
  786. def should_trace_in_ctor(self, prop): return False
  787. def should_never_trace(self, prop):
  788. # despite being hidden from the panel, these properties should not be
  789. # skipped in trace.
  790. if prop.get_property_name() in ["Position", "Position2", "Orientation"]:
  791. return False
  792. return ProxyFilter.should_never_trace(self, prop)
  793. class ExtractorFilter(ProxyFilter):
  794. def should_trace_in_ctor(self, prop): return False
  795. def should_never_trace(self, prop):
  796. if prop.get_property_name() in ["Writer"]:
  797. return True
  798. return super(ExtractorFilter, self).should_never_trace(prop)
  799. class SaveExtractsFilter(ProxyFilter):
  800. def should_trace_in_ctor(self, prop): return True
  801. def SupplementalProxy(cls):
  802. """This function decorates a ProxyFilter. Designed to be
  803. used for supplemental proxies, so that we can centralize the logic
  804. to decide whether to trace any of the properties on the supplemental
  805. proxies the first time that proxy is accessed."""
  806. setting = sm.vtkSMTrace.GetActiveTracer().GetFullyTraceSupplementalProxies()
  807. if setting: return cls
  808. def should_trace_in_ctor(self, *args, **kwargs):
  809. return False
  810. def should_trace_in_create(self, *args, **kwargs):
  811. return False
  812. cls.should_trace_in_create = should_trace_in_create
  813. cls.should_trace_in_ctor = should_trace_in_ctor
  814. return cls
  815. # ===================================================================================================
  816. # === TraceItem types ==
  817. # TraceItems are units of traceable actions triggered by the application using vtkSMTrace
  818. # ===================================================================================================
  819. class RenderingMixin(object):
  820. @property
  821. def skip_from_trace(self):
  822. if _get_skip_rendering():
  823. return True
  824. return False
  825. class TraceItem(object):
  826. def __init__(self):
  827. try:
  828. if self.skip_from_trace:
  829. raise Untraceable("skipped")
  830. except AttributeError:
  831. pass
  832. def finalize(self):
  833. pass
  834. class NestableTraceItem(TraceItem):
  835. """Base class for trace item that can be nested i.e.
  836. can trace when some other trace item is active."""
  837. pass
  838. class BookkeepingItem(NestableTraceItem):
  839. """Base class for trace items that are only used for
  840. book keeping and don't affect the trace itself."""
  841. pass
  842. class RegisterPipelineProxy(TraceItem):
  843. """This traces the creation of a Pipeline Proxy such as
  844. sources/filters/readers etc."""
  845. def __init__(self, proxy):
  846. TraceItem.__init__(self)
  847. self.Proxy = sm._getPyProxy(proxy)
  848. def finalize(self):
  849. pname = Trace.get_registered_name(self.Proxy, "sources")
  850. varname = Trace.get_varname(pname)
  851. accessor = ProxyAccessor(varname, self.Proxy)
  852. ctor = sm._make_name_valid(self.Proxy.GetXMLLabel())
  853. trace = TraceOutput()
  854. trace.append("# create a new '%s'" % self.Proxy.GetXMLLabel())
  855. if isinstance(self.Proxy, sm.ExodusIIReaderProxy):
  856. filter_type = ExodusIIReaderFilter()
  857. elif self.Proxy.GetXMLLabel() == "Extract Selection":
  858. filter_type = ExtractSelectionFilter()
  859. else:
  860. filter_type = PipelineProxyFilter()
  861. ctor_args = "registrationName='%s'" % pname
  862. trace.append(accessor.trace_ctor(ctor, filter_type, ctor_args=ctor_args))
  863. Trace.Output.append_separated(trace.raw_data())
  864. TraceItem.finalize(self)
  865. class Delete(TraceItem):
  866. """This traces the deletion of a Pipeline proxy"""
  867. def __init__(self, proxy):
  868. TraceItem.__init__(self)
  869. proxy = sm._getPyProxy(proxy)
  870. accessor = Trace.get_accessor(proxy)
  871. Trace.Output.append_separated([\
  872. "# destroy %s" % (accessor),
  873. "Delete(%s)" % (accessor),
  874. "del %s" % accessor])
  875. accessor.finalize()
  876. del accessor
  877. import gc
  878. gc.collect()
  879. class CleanupAccessor(BookkeepingItem):
  880. def __init__(self, proxy):
  881. self.Proxy = sm._getPyProxy(proxy)
  882. def finalize(self):
  883. if Trace.has_accessor(self.Proxy):
  884. accessor = Trace.get_accessor(self.Proxy)
  885. accessor.finalize()
  886. del accessor
  887. import gc
  888. gc.collect()
  889. class BlockTraceItems(NestableTraceItem):
  890. """Item to block further creation of trace items, even
  891. those that are `NestableTraceItem`. Simply create this and
  892. no trace items will be created by `_create_trace_item_internal`
  893. until this instance is cleaned up.
  894. """
  895. pass
  896. class PropertiesModified(NestableTraceItem):
  897. """Traces properties modified on a specific proxy."""
  898. def __init__(self, proxy, comment=None):
  899. TraceItem.__init__(self)
  900. proxy = sm._getPyProxy(proxy)
  901. self.ProxyAccessor = Trace.get_accessor(proxy)
  902. self.MTime = vtkTimeStamp()
  903. self.MTime.Modified()
  904. self.Comment = "#%s" % comment if not comment is None else \
  905. "# Properties modified on %s" % str(self.ProxyAccessor)
  906. def finalize(self):
  907. props = self.ProxyAccessor.get_properties()
  908. props_to_trace = [k for k in props if self.MTime.GetMTime() < k.get_object().GetMTime()]
  909. if props_to_trace:
  910. Trace.Output.append_separated([
  911. self.Comment,
  912. self.ProxyAccessor.trace_properties(props_to_trace, in_ctor=False)])
  913. # Remember, we are monitoring a proxy to trace any properties on it that
  914. # are modified. When that's the case, properties on a proxy-property on
  915. # that proxy may have been modified too and it would make sense to trace
  916. # those as well (e.g. ScalarOpacityFunction on a PVLookupTable proxy).
  917. # This loop handles that. We explicitly skip "InputProperty"s, however
  918. # since tracing properties modified on the input should not be a
  919. # responsibility of this method.
  920. for prop in props:
  921. if not isinstance(prop.get_object(), sm.ProxyProperty) or \
  922. isinstance(prop.get_object(), sm.InputProperty) or \
  923. prop.DisableSubTrace:
  924. continue
  925. val = prop.get_property_value()
  926. try:
  927. # val can be None or list of proxies. We are not tracing list of
  928. # proxies since we don't want to trace properties like
  929. # `view.Representations`.
  930. if not val or not isinstance(val, sm.Proxy): continue
  931. valaccessor = Trace.get_accessor(val)
  932. except Untraceable:
  933. continue
  934. else:
  935. props = valaccessor.get_properties()
  936. props_to_trace = [k for k in props if self.MTime.GetMTime() < k.get_object().GetMTime()]
  937. if props_to_trace:
  938. Trace.Output.append_separated([
  939. "# Properties modified on %s" % valaccessor,
  940. valaccessor.trace_properties(props_to_trace, in_ctor=False)])
  941. TraceItem.finalize(self)
  942. class ScalarBarInteraction(RenderingMixin, NestableTraceItem):
  943. """Traces scalar bar interactions"""
  944. def __init__(self, proxy, comment=None):
  945. TraceItem.__init__(self)
  946. proxy = sm._getPyProxy(proxy)
  947. self.ProxyAccessor = Trace.get_accessor(proxy)
  948. self.MTime = vtkTimeStamp()
  949. self.MTime.Modified()
  950. self.Comment = "#%s" % comment if not comment is None else \
  951. "# Properties modified on %s" % str(self.ProxyAccessor)
  952. def finalize(self):
  953. props = self.ProxyAccessor.get_properties()
  954. props_to_trace = [k for k in props if self.MTime.GetMTime() < k.get_object().GetMTime()]
  955. afilter = ScalarBarProxyFilter()
  956. props_to_trace = [k for k in props_to_trace if not afilter.should_never_trace(k)]
  957. if props_to_trace:
  958. Trace.Output.append_separated([
  959. self.Comment,
  960. self.ProxyAccessor.trace_properties(props_to_trace, in_ctor=False)])
  961. class Show(RenderingMixin, TraceItem):
  962. """Traces Show"""
  963. def __init__(self, producer, port, view, display, comment=None):
  964. TraceItem.__init__(self)
  965. producer = sm._getPyProxy(producer)
  966. view = sm._getPyProxy(view)
  967. display = sm._getPyProxy(display)
  968. self.ProducerAccessor = Trace.get_accessor(producer)
  969. self.ViewAccessor = Trace.get_accessor(view)
  970. self.OutputPort = port
  971. self.Display = display
  972. self.Comment = comment
  973. def finalize(self):
  974. display = self.Display
  975. output = TraceOutput()
  976. if not Trace.has_accessor(display):
  977. pname = "%sDisplay" % self.ProducerAccessor
  978. accessor = ProxyAccessor(Trace.get_varname(pname), display)
  979. trace_ctor = True
  980. else:
  981. accessor = Trace.get_accessor(display)
  982. trace_ctor = False
  983. port = self.OutputPort
  984. if not self.Comment is None:
  985. output.append("# %s" % self.Comment)
  986. else:
  987. output.append("# show data in view")
  988. if port > 0:
  989. output.append("%s = Show(OutputPort(%s, %d), %s, '%s')" % \
  990. (str(accessor), str(self.ProducerAccessor), port, str(self.ViewAccessor), \
  991. str(display.GetXMLName())))
  992. else:
  993. output.append("%s = Show(%s, %s, '%s')" % \
  994. (str(accessor), str(self.ProducerAccessor), str(self.ViewAccessor), \
  995. str(display.GetXMLName())))
  996. Trace.Output.append_separated(output.raw_data())
  997. output = TraceOutput()
  998. if trace_ctor:
  999. # Now trace default values.
  1000. ctor_trace = accessor.trace_ctor(None, RepresentationProxyFilter())
  1001. if ctor_trace:
  1002. output.append("# trace defaults for the display properties.")
  1003. output.append(ctor_trace)
  1004. Trace.Output.append_separated(output.raw_data())
  1005. TraceItem.finalize(self)
  1006. class Hide(RenderingMixin, TraceItem):
  1007. """Traces Hide"""
  1008. def __init__(self, producer, port, view):
  1009. TraceItem.__init__(self)
  1010. producer = sm._getPyProxy(producer)
  1011. view = sm._getPyProxy(view)
  1012. producerAccessor = Trace.get_accessor(producer)
  1013. viewAccessor = Trace.get_accessor(view)
  1014. Trace.Output.append_separated([\
  1015. "# hide data in view",
  1016. "Hide(%s, %s)" % (str(producerAccessor), str(viewAccessor)) if port == 0 else \
  1017. "Hide(OutputPort(%s, %d), %s)" % (str(producerAccessor), port, str(viewAccessor))])
  1018. class SetScalarColoring(RenderingMixin, TraceItem):
  1019. """Trace vtkSMPVRepresentationProxy.SetScalarColoring"""
  1020. def __init__(self, display, arrayname, attribute_type, component=None, separate=False, lut=None):
  1021. TraceItem.__init__(self)
  1022. self.Display = sm._getPyProxy(display)
  1023. self.ArrayName = arrayname
  1024. self.AttributeType = attribute_type
  1025. self.Component = component
  1026. self.Lut = sm._getPyProxy(lut)
  1027. self.Separate = separate
  1028. def finalize(self):
  1029. TraceItem.finalize(self)
  1030. if self.ArrayName:
  1031. if self.Component is None:
  1032. if self.Separate:
  1033. Trace.Output.append_separated([\
  1034. "# set scalar coloring using an separate color/opacity maps",
  1035. "ColorBy(%s, ('%s', '%s'), %s)" % (\
  1036. str(Trace.get_accessor(self.Display)),
  1037. sm.GetAssociationAsString(self.AttributeType),
  1038. self.ArrayName, self.Separate)])
  1039. else:
  1040. Trace.Output.append_separated([\
  1041. "# set scalar coloring",
  1042. "ColorBy(%s, ('%s', '%s'))" % (\
  1043. str(Trace.get_accessor(self.Display)),
  1044. sm.GetAssociationAsString(self.AttributeType),
  1045. self.ArrayName)])
  1046. else:
  1047. if self.Separate:
  1048. Trace.Output.append_separated([\
  1049. "# set scalar coloring using an separate color/opacity maps",
  1050. "ColorBy(%s, ('%s', '%s', '%s'), %s)" % (\
  1051. str(Trace.get_accessor(self.Display)),
  1052. sm.GetAssociationAsString(self.AttributeType),
  1053. self.ArrayName, self.Component, self.Separate)])
  1054. else:
  1055. Trace.Output.append_separated([\
  1056. "# set scalar coloring",
  1057. "ColorBy(%s, ('%s', '%s', '%s'))" % (\
  1058. str(Trace.get_accessor(self.Display)),
  1059. sm.GetAssociationAsString(self.AttributeType),
  1060. self.ArrayName, self.Component)])
  1061. else:
  1062. Trace.Output.append_separated([\
  1063. "# turn off scalar coloring",
  1064. "ColorBy(%s, None)" % str(Trace.get_accessor(self.Display))])
  1065. # only for "Fully Trace Supplemental Proxies" support
  1066. if self.Lut:
  1067. Trace.get_accessor(self.Lut)
  1068. class RegisterViewProxy(RenderingMixin, TraceItem):
  1069. """Traces creation of a new view (vtkSMParaViewPipelineController::RegisterViewProxy)."""
  1070. def __init__(self, proxy):
  1071. TraceItem.__init__(self)
  1072. self.Proxy = sm._getPyProxy(proxy)
  1073. assert not self.Proxy is None
  1074. def finalize(self):
  1075. pname = Trace.get_registered_name(self.Proxy, "views")
  1076. varname = Trace.get_varname(pname)
  1077. accessor = ProxyAccessor(varname, self.Proxy)
  1078. trace = TraceOutput()
  1079. # create dynamic lights as needed.
  1080. if hasattr(self.Proxy, "AdditionalLights"):
  1081. for light in self.Proxy.AdditionalLights:
  1082. trace.append('# create light')
  1083. lightTrace = RegisterLightProxy(light)
  1084. lightTrace.finalize()
  1085. # unlike for filters/sources, for views the CreateView function still takes the
  1086. # xml name for the view, not its label.
  1087. ctor_args = "'%s'" % self.Proxy.GetXMLName()
  1088. trace.append("# Create a new '%s'" % self.Proxy.GetXMLLabel())
  1089. filter = ViewProxyFilter()
  1090. trace.append(accessor.trace_ctor("CreateView", filter, ctor_args))
  1091. # append dynamic lights as needed.
  1092. # if hasattr(self.Proxy, "AdditionalLights"):
  1093. # lightsList = []
  1094. # for light in self.Proxy.AdditionalLights:
  1095. # lightAccessor = Trace.get_accessor(light)
  1096. # lightsList.append(lightAccessor)
  1097. # trace.append("%s.AdditionalLights = [%s]" % (Trace.get_accessor(self.Proxy), ", ".join(lightsList)))
  1098. Trace.Output.append_separated(trace.raw_data())
  1099. # we assume views don't have proxy list domains for now, and ignore tracing them.
  1100. TraceItem.finalize(self)
  1101. class RegisterLightProxy(RenderingMixin, TraceItem):
  1102. """Traces creation of a new light (vtkSMParaViewPipelineController::RegisterLightProxy)."""
  1103. def __init__(self, proxy, view=None):
  1104. TraceItem.__init__(self)
  1105. self.Proxy = sm._getPyProxy(proxy)
  1106. self.View = sm._getPyProxy(view)
  1107. assert not self.Proxy is None
  1108. def finalize(self):
  1109. pname = Trace.get_registered_name(self.Proxy, "additional_lights")
  1110. varname = Trace.get_varname(pname)
  1111. accessor = ProxyAccessor(varname, self.Proxy)
  1112. trace = TraceOutput()
  1113. trace.append("# Create a new '%s'" % self.Proxy.GetXMLLabel())
  1114. filter = ProxyFilter()
  1115. if self.View:
  1116. viewAccessor = Trace.get_accessor(self.View)
  1117. trace.append(accessor.trace_ctor("AddLight", filter, ctor_args="view=%s" % viewAccessor))
  1118. else:
  1119. trace.append(accessor.trace_ctor("CreateLight", filter))
  1120. Trace.Output.append_separated(trace.raw_data())
  1121. TraceItem.finalize(self)
  1122. class ExportView(RenderingMixin, TraceItem):
  1123. def __init__(self, view, exporter, filename):
  1124. TraceItem.__init__(self)
  1125. view = sm._getPyProxy(view)
  1126. exporter = sm._getPyProxy(exporter)
  1127. viewAccessor = Trace.get_accessor(view)
  1128. exporterAccessor = ProxyAccessor("temporaryExporter", exporter)
  1129. trace = TraceOutput()
  1130. trace.append("# export view")
  1131. trace.append(\
  1132. exporterAccessor.trace_ctor("ExportView", ExporterProxyFilter(),
  1133. ctor_args="%s, view=%s" % (repr(filename), viewAccessor),
  1134. skip_assignment=True))
  1135. exporterAccessor.finalize() # so that it will get deleted
  1136. del exporterAccessor
  1137. Trace.Output.append_separated(trace.raw_data())
  1138. class SaveData(TraceItem):
  1139. def __init__(self, writer, filename, source, port):
  1140. TraceItem.__init__(self)
  1141. source = sm._getPyProxy(source, port)
  1142. sourceAccessor = Trace.get_accessor(source)
  1143. writer = sm._getPyProxy(writer)
  1144. writerAccessor = ProxyAccessor("temporaryWriter", writer)
  1145. if port > 0:
  1146. ctor_args_1 = "OutputPort(%s, %d)" % (sourceAccessor, port)
  1147. else:
  1148. ctor_args_1 = "%s" % sourceAccessor
  1149. trace = TraceOutput()
  1150. trace.append("# save data")
  1151. trace.append(\
  1152. writerAccessor.trace_ctor("SaveData", WriterProxyFilter(),
  1153. ctor_args="%s, proxy=%s" % (repr(filename), ctor_args_1),
  1154. skip_assignment=True))
  1155. writerAccessor.finalize() # so that it will get deleted.
  1156. del writerAccessor
  1157. del writer
  1158. Trace.Output.append_separated(trace.raw_data())
  1159. class SaveScreenshotOrAnimation(RenderingMixin, TraceItem):
  1160. def __init__(self, helper, filename, view, layout, mode_screenshot=False):
  1161. TraceItem.__init__(self)
  1162. assert(view != None or layout != None)
  1163. helper = sm._getPyProxy(helper)
  1164. helperAccessor = ProxyAccessor("temporaryHelper", helper)
  1165. if view:
  1166. view = sm._getPyProxy(view)
  1167. ctor_args_1 = "%s" % Trace.get_accessor(view)
  1168. elif layout:
  1169. layout = sm._getPyProxy(layout)
  1170. ctor_args_1 = "%s" % Trace.get_accessor(layout)
  1171. trace = TraceOutput()
  1172. if mode_screenshot:
  1173. trace.append("# save screenshot")
  1174. else:
  1175. trace.append("# save animation")
  1176. _filter = ScreenShotHelperProxyFilter()
  1177. # tracing "Format" is handled specially. PLD properties are not traced
  1178. # in ctor, but we trick it as follows:
  1179. formatAccessor = ProxyAccessor("temporaryHelperFormat", helper.Format)
  1180. formatProps = [x for x in formatAccessor.get_properties() if _filter.should_trace_in_ctor(x)]
  1181. format_txt = formatAccessor.trace_properties(formatProps, in_ctor=True)
  1182. if format_txt:
  1183. format_txt = "\n # %s options\n %s" % (helper.Format.GetXMLLabel(), format_txt)
  1184. trace.append(\
  1185. helperAccessor.trace_ctor(\
  1186. "SaveScreenshot" if mode_screenshot else "SaveAnimation",
  1187. ScreenShotHelperProxyFilter(),
  1188. ctor_args="'%s', %s" % (filename, ctor_args_1),
  1189. ctor_extra_args=format_txt,
  1190. skip_assignment=True))
  1191. helperAccessor.finalize()
  1192. del helperAccessor
  1193. del helper
  1194. Trace.Output.append_separated(trace.raw_data())
  1195. class SaveAnimationExtracts(TraceItem):
  1196. """Used by vtkSMSaveAnimationExtractsProxy to trace saving of extracts
  1197. generation."""
  1198. def __init__(self, proxy):
  1199. TraceItem.__init__(self)
  1200. assert(proxy is not None)
  1201. proxy = sm._getPyProxy(proxy)
  1202. accessor = ProxyAccessor("temporaryWriter", proxy)
  1203. trace = TraceOutput()
  1204. trace.append("# save extracts")
  1205. trace.append(\
  1206. accessor.trace_ctor("SaveExtracts", SaveExtractsFilter(),
  1207. skip_assignment=True))
  1208. del accessor
  1209. del proxy
  1210. Trace.Output.append_separated(trace.raw_data())
  1211. class LoadState(TraceItem):
  1212. def __init__(self, filename, options):
  1213. TraceItem.__init__(self)
  1214. self._filename = filename
  1215. self._options = options
  1216. def finalize(self):
  1217. import re
  1218. filename = self._filename
  1219. options = self._options
  1220. options = sm._getPyProxy(options)
  1221. trace = TraceOutput()
  1222. mode = options.SMProxy.GetProperty("LoadStateDataFileOptions").GetElement(0)
  1223. if mode == options.SMProxy.USE_FILES_FROM_STATE:
  1224. trace.append("# load state")
  1225. trace.append("LoadState('%s')" % filename)
  1226. elif mode == options.SMProxy.USE_DATA_DIRECTORY:
  1227. trace.append("# load state using data from chosen directory")
  1228. trace.append(\
  1229. ["LoadState('%s'," % filename,
  1230. " data_directory='%s'," % options.DataDirectory,
  1231. " restrict_to_data_directory=%s)",
  1232. True if options.OnlyUseFilesInDataDirectory else False])
  1233. elif mode == options.SMProxy.CHOOSE_FILES_EXPLICITLY:
  1234. iter = sm.PropertyIterator(options)
  1235. params = {}
  1236. for smprop in iter:
  1237. pname = iter.GetKey()
  1238. m = re.match(r"(\d+)\.(.+)", pname)
  1239. if m and options.IsPropertyModified(int(m.group(1)), m.group(2)):
  1240. sid = m.group(1)
  1241. readername = options.GetReaderName(int(sid))
  1242. d = params.get(sid, {'name': readername, 'id' : sid})
  1243. if smprop.GetNumberOfElements() == 1:
  1244. d[m.group(2)] = smprop.GetElement(0)
  1245. else:
  1246. d[m.group(2)] = [smprop.GetElement(x) for x in range(smprop.GetNumberOfElements())]
  1247. params[sid] = d
  1248. if params:
  1249. trace.append("# load state using specified data files")
  1250. trace.append("LoadState('%s'," % filename)
  1251. trace.append(_pretty_print(list(params.values()), prefix=" filenames=") + ")")
  1252. else:
  1253. # this happens when user didn't modify any paths.
  1254. trace.append("# load state")
  1255. trace.append("LoadState('%s')" % filename)
  1256. del options
  1257. Trace.Output.append_separated(trace.raw_data())
  1258. class RegisterLayoutProxy(RenderingMixin, TraceItem):
  1259. def __init__(self, layout):
  1260. TraceItem.__init__(self)
  1261. self.Layout = sm._getPyProxy(layout)
  1262. def finalize(self, filter=None):
  1263. if filter is None:
  1264. filter = lambda x: True
  1265. pname = Trace.get_registered_name(self.Layout, "layouts")
  1266. accessor = ProxyAccessor(Trace.get_varname(pname), self.Layout)
  1267. Trace.Output.append_separated([\
  1268. "# create new layout object '%s'" % pname,
  1269. "%s = CreateLayout(name='%s')" % (accessor, pname)])
  1270. # Let's trace out the state for the layout.
  1271. def _trace_layout(layout, laccessor, location):
  1272. sdir = layout.GetSplitDirection(location)
  1273. sfraction = layout.GetSplitFraction(location)
  1274. if sdir == layout.SMProxy.VERTICAL:
  1275. Trace.Output.append([\
  1276. "%s.SplitVertical(%d, %f)" % (laccessor, location, sfraction)])
  1277. _trace_layout(layout, laccessor, layout.GetFirstChild(location))
  1278. _trace_layout(layout, laccessor, layout.GetSecondChild(location))
  1279. elif sdir == layout.SMProxy.HORIZONTAL:
  1280. Trace.Output.append([\
  1281. "%s.SplitHorizontal(%d, %f)" % (laccessor, location, sfraction)])
  1282. _trace_layout(layout, laccessor, layout.GetFirstChild(location))
  1283. _trace_layout(layout, laccessor, layout.GetSecondChild(location))
  1284. elif sdir == layout.SMProxy.NONE:
  1285. view = layout.GetView(location)
  1286. if view and filter(view):
  1287. vaccessor = Trace.get_accessor(view)
  1288. Trace.Output.append([\
  1289. "%s.AssignView(%d, %s)" % (laccessor, location, vaccessor)])
  1290. _trace_layout(self.Layout, accessor, 0)
  1291. # save size, if non-empty.
  1292. lsize = self.Layout.GetSize()
  1293. if lsize[0] > 0 and lsize[1] > 0:
  1294. Trace.Output.append("%s.SetSize(%d, %d)" % (accessor, lsize[0], lsize[1]))
  1295. TraceItem.finalize(self)
  1296. class LoadPlugin(TraceItem):
  1297. def __init__(self, filename, remote):
  1298. Trace.Output.append_separated([\
  1299. "# load plugin",
  1300. "LoadPlugin('%s', remote=%s, ns=globals())" % (filename, remote)])
  1301. class CreateAnimationTrack(TraceItem):
  1302. # FIXME: animation tracing support in general needs to be revamped after moving
  1303. # animation control logic to the server manager from Qt layer.
  1304. def __init__(self, cue):
  1305. TraceItem.__init__(self)
  1306. self.Cue = sm._getPyProxy(cue)
  1307. def finalize(self):
  1308. TraceItem.finalize(self)
  1309. # We let Trace create an accessor for the cue. We will then simply log the
  1310. # default property values.
  1311. accessor = Trace.get_accessor(self.Cue) # type: RealProxyAccessor
  1312. # Now trace properties on the cue.
  1313. trace = TraceOutput()
  1314. trace.append_separated("# initialize the animation track")
  1315. trace.append(accessor.trace_ctor(None, AnimationProxyFilter()))
  1316. Trace.Output.append_separated(trace.raw_data())
  1317. class RenameProxy(TraceItem):
  1318. "Trace renaming of a source proxy."
  1319. def __init__(self, proxy):
  1320. TraceItem.__init__(self)
  1321. proxy = sm._getPyProxy(proxy)
  1322. if Trace.get_registered_name(proxy, "sources"):
  1323. self.Accessor = Trace.get_accessor(proxy)
  1324. self.Proxy = proxy
  1325. else:
  1326. raise Untraceable("Only source proxy renames are traced.")
  1327. def finalize(self):
  1328. if self.Accessor:
  1329. newname = Trace.get_registered_name(self.Proxy, "sources")
  1330. Trace.Output.append_separated([\
  1331. "# rename source object",
  1332. "RenameSource('%s', %s)" % (newname, self.Accessor)])
  1333. TraceItem.finalize(self)
  1334. class SetCurrentProxy(TraceItem):
  1335. """Traces change in active view/source etc."""
  1336. def __init__(self, selmodel, proxy, command):
  1337. TraceItem.__init__(self)
  1338. if proxy and proxy.IsA("vtkSMOutputPort"):
  1339. # FIXME: need to handle port number.
  1340. proxy = sm._getPyProxy(proxy.GetSourceProxy())
  1341. else:
  1342. proxy = sm._getPyProxy(proxy)
  1343. accessor = Trace.get_accessor(proxy)
  1344. pxm = selmodel.GetSessionProxyManager()
  1345. if selmodel is pxm.GetSelectionModel("ActiveView"):
  1346. if RenderingMixin().skip_from_trace:
  1347. raise Untraceable("skipped")
  1348. Trace.Output.append_separated([\
  1349. "# set active view",
  1350. "SetActiveView(%s)" % accessor])
  1351. elif selmodel is pxm.GetSelectionModel("ActiveSources"):
  1352. Trace.Output.append_separated([\
  1353. "# set active source",
  1354. "SetActiveSource(%s)" % accessor])
  1355. else:
  1356. raise Untraceable("Unknown selection model")
  1357. class CallMethod(TraceItem):
  1358. def __init__(self, proxy, methodname, *args, **kwargs):
  1359. TraceItem.__init__(self)
  1360. trace = self.get_trace(proxy, methodname, args, kwargs)
  1361. if trace:
  1362. Trace.Output.append_separated(trace)
  1363. def get_trace(self, proxy, methodname, args, kwargs):
  1364. to_trace = []
  1365. try:
  1366. to_trace.append("# " + kwargs["comment"])
  1367. del kwargs["comment"]
  1368. except KeyError:
  1369. pass
  1370. accessor = Trace.get_accessor(sm._getPyProxy(proxy))
  1371. args = [str(CallMethod.marshall(x)) for x in args]
  1372. args += ["%s=%s" % (key, CallMethod.marshall(val)) for key, val in kwargs.items()]
  1373. to_trace.append("%s.%s(%s)" % (accessor, methodname, ", ".join(args)))
  1374. return to_trace
  1375. @classmethod
  1376. def marshall(cls, x):
  1377. try:
  1378. if x.IsA("vtkSMProxy"):
  1379. return Trace.get_accessor(sm._getPyProxy(x))
  1380. except AttributeError:
  1381. return "'%s'" % x if type(x) == str else x
  1382. def _bind_on_event(ref):
  1383. def _callback(obj, string):
  1384. ref().on_event(obj, string)
  1385. return _callback
  1386. class CallMethodIfPropertiesModified(CallMethod):
  1387. """Similar to CallMethod, except that the trace will get logged only
  1388. if the proxy fires PropertiesModified event before the trace-item is
  1389. finalized."""
  1390. def __init__(self, proxy, methodname, *args, **kwargs):
  1391. self.proxy = proxy
  1392. self.methodname = methodname
  1393. self.args = args
  1394. self.kwargs = kwargs
  1395. self.tag = proxy.AddObserver("PropertyModifiedEvent", _bind_on_event(weakref.ref(self)))
  1396. self.modified = False
  1397. def on_event(self, obj, string):
  1398. self.modified = True
  1399. def finalize(self):
  1400. self.proxy.RemoveObserver(self.tag)
  1401. self.tag = None
  1402. if self.modified:
  1403. trace = self.get_trace(self.proxy, self.methodname, self.args, self.kwargs)
  1404. Trace.Output.append_separated(trace)
  1405. CallMethod.finalize(self)
  1406. def __del__(self):
  1407. if self.proxy and self.tag:
  1408. self.proxy.RemoveObserver(self.tag)
  1409. class CallFunction(TraceItem):
  1410. def __init__(self, functionname, *args, **kwargs):
  1411. TraceItem.__init__(self)
  1412. to_trace = []
  1413. try:
  1414. to_trace.append("# " + kwargs["comment"])
  1415. del kwargs["comment"]
  1416. except KeyError:
  1417. pass
  1418. args = [str(CallMethod.marshall(x)) for x in args]
  1419. args += ["%s=%s" % (key, CallMethod.marshall(val)) for key, val in kwargs.items()]
  1420. to_trace.append("%s(%s)" % (functionname, ", ".join(args)))
  1421. Trace.Output.append_separated(to_trace)
  1422. class TraceText(TraceItem):
  1423. """Add text directly to the trace. For paraview client applications to use with non-proxy objects."""
  1424. def __init__(self, text, *args, **kwargs):
  1425. TraceItem.__init__(self)
  1426. to_trace = []
  1427. try:
  1428. to_trace.append("# " + kwargs["comment"])
  1429. del kwargs["comment"]
  1430. except KeyError:
  1431. pass
  1432. to_trace.append(text)
  1433. Trace.Output.append_separated(to_trace)
  1434. class ChooseTexture(RenderingMixin, TraceItem):
  1435. """Traces changes of texture object selection. For example renderview background."""
  1436. def __init__(self, owner, texture, prop):
  1437. TraceItem.__init__(self)
  1438. owner = sm._getPyProxy(owner)
  1439. texture = sm._getPyProxy(texture)
  1440. ownerAccessor = Trace.get_accessor(owner)
  1441. textureAccessor = Trace.get_accessor(texture)
  1442. Trace.Output.append_separated([\
  1443. "# change texture",
  1444. "%s.%s = %s" % (str(ownerAccessor), prop.GetXMLName(), str(textureAccessor))])
  1445. class SaveCameras(RenderingMixin, BookkeepingItem):
  1446. """This is used to request recording of cameras in trace"""
  1447. # This is a little hackish at this point. We'll figure something cleaner out
  1448. # in time.
  1449. def __init__(self, proxy=None, **kwargs):
  1450. trace = self.get_trace(proxy)
  1451. try:
  1452. Trace.Output.append(["# " + kwargs["comment"]])
  1453. del kwargs["comment"]
  1454. except KeyError:
  1455. pass
  1456. if trace:
  1457. Trace.Output.append_separated(trace)
  1458. @classmethod
  1459. def get_trace(cls, proxy=None):
  1460. trace = TraceOutput()
  1461. proxy = sm._getPyProxy(proxy)
  1462. if proxy is None:
  1463. views = [x for x in simple.GetViews() if Trace.has_accessor(x)]
  1464. for v in views:
  1465. trace.append_separated(cls.get_trace(proxy=v))
  1466. elif proxy.IsA("vtkSMViewLayoutProxy"):
  1467. views = simple.GetViewsInLayout(proxy)
  1468. for v in views:
  1469. trace.append_separated(cls.get_trace(proxy=v))
  1470. elif proxy.IsA("vtkSMViewProxy"):
  1471. if proxy.GetProperty("CameraPosition"):
  1472. accessor = Trace.get_accessor(proxy)
  1473. prop_names = ["CameraPosition", "CameraFocalPoint",
  1474. "CameraViewUp", "CameraViewAngle",
  1475. "CameraParallelScale", "CameraParallelProjection",
  1476. "EyeAngle", "InteractionMode"]
  1477. props = [x for x in accessor.get_properties() \
  1478. if x.get_property_name() in prop_names and \
  1479. not x.get_object().IsValueDefault()]
  1480. if props:
  1481. trace.append("# current camera placement for %s" % accessor)
  1482. trace.append(accessor.trace_properties(props, in_ctor=False))
  1483. else: pass # non-camera views
  1484. elif proxy.IsA("vtkSMAnimationSceneProxy"):
  1485. for view in proxy.GetProperty("ViewModules"):
  1486. trace.append_separated(cls.get_trace(proxy=view))
  1487. else:
  1488. raise Untraceable("Invalid argument type %r"% proxy)
  1489. return trace.raw_data()
  1490. class SaveLayoutSizes(RenderingMixin, BookkeepingItem):
  1491. """
  1492. A bookkeeping item to trace sizes for all layouts used by the trace (or
  1493. the one explicitly passed to the constructor).
  1494. This ensures that all the layouts (and consequently views in those layout)
  1495. are setup with sizes similar to those in the UI (BUG #20102).
  1496. """
  1497. def __init__(self, proxy=None):
  1498. trace = self.get_trace(proxy)
  1499. if trace:
  1500. Trace.Output.append_separated(trace)
  1501. @classmethod
  1502. def get_trace(cls, proxy=None):
  1503. trace = TraceOutput()
  1504. proxy = sm._getPyProxy(proxy)
  1505. if proxy is None:
  1506. # scan all views we have created trace-accessors for and get layouts
  1507. # for those views.
  1508. views = [x for x in simple.GetViews() if Trace.has_accessor(x)]
  1509. layouts = set([simple.GetLayout(v) for v in views \
  1510. if simple.GetLayout(v) is not None])
  1511. for l in layouts:
  1512. trace.append_separated(cls.get_trace(l))
  1513. elif proxy.IsA("vtkSMViewLayoutProxy"):
  1514. layoutAccessor = Trace.get_accessor(proxy)
  1515. lsize = proxy.GetSize()
  1516. trace.append_separated([\
  1517. "# layout/tab size in pixels",
  1518. "%s.SetSize(%d, %d)" % (layoutAccessor, lsize[0], lsize[1])])
  1519. elif proxy.IsA("vtkSMViewProxy"):
  1520. l = simple.GetLayout(proxy)
  1521. if l:
  1522. trace.append_separated(cls.get_trace(l))
  1523. else:
  1524. raise Untraceable("Invalid argument type %r" % proxy)
  1525. return trace.raw_data()
  1526. class CreateExtractor(TraceItem):
  1527. """Traces creation of extractors"""
  1528. def __init__(self, xmlname, producer, extractor, registrationName, comment=None):
  1529. super(CreateExtractor, self).__init__()
  1530. producer = sm._getPyProxy(producer)
  1531. extractor = sm._getPyProxy(extractor)
  1532. self.ProducerAccessor = Trace.get_accessor(producer)
  1533. self.OutputPort = producer.Port
  1534. self.Extractor = extractor
  1535. self.XMLName = xmlname
  1536. self.Comment = comment
  1537. self.Name = registrationName
  1538. def finalize(self):
  1539. output = TraceOutput()
  1540. port = self.OutputPort
  1541. regName = self.Name
  1542. accessor = ProxyAccessor(Trace.get_varname(regName), self.Extractor)
  1543. if self.Comment is not None:
  1544. output.append("# %s", self.Comment)
  1545. else:
  1546. output.append("# create extractor")
  1547. if port > 0:
  1548. output.append(\
  1549. "%s = CreateExtractor('%s', OutputPort(%s, %d), registrationName='%s')" % \
  1550. (str(accessor), self.XMLName, str(self.ProducerAccessor), port, regName))
  1551. else:
  1552. output.append(\
  1553. "%s = CreateExtractor('%s', %s, registrationName='%s')" % \
  1554. (str(accessor), self.XMLName, str(self.ProducerAccessor), regName))
  1555. ctor_trace = accessor.trace_ctor(None, ExtractorFilter())
  1556. if ctor_trace:
  1557. output.append("# trace defaults for the extractor.")
  1558. output.append(ctor_trace)
  1559. Trace.Output.append_separated(output.raw_data())
  1560. super(CreateExtractor, self).finalize()
  1561. # __ActiveTraceItems is simply used to keep track of items that are currently
  1562. # active to avoid non-nestable trace items from being created when previous
  1563. # items are active.
  1564. __ActiveTraceItems = []
  1565. def _create_trace_item_internal(key, args=None, kwargs=None):
  1566. global __ActiveTraceItems
  1567. # trim __ActiveTraceItems to remove None references.
  1568. __ActiveTraceItems = [x for x in __ActiveTraceItems if not x() is None]
  1569. g = globals()
  1570. if key in g and callable(g[key]):
  1571. args = args if args else []
  1572. kwargs = kwargs if kwargs else {}
  1573. traceitemtype = g[key]
  1574. if len(__ActiveTraceItems) == 0 or \
  1575. issubclass(traceitemtype, NestableTraceItem):
  1576. if len(__ActiveTraceItems) > 0 and \
  1577. isinstance(__ActiveTraceItems[-1](), BlockTraceItems):
  1578. raise Untraceable("Not tracing since `BlockTraceItems` is active.")
  1579. instance = traceitemtype(*args, **kwargs)
  1580. if not issubclass(traceitemtype, BookkeepingItem):
  1581. __ActiveTraceItems.append(weakref.ref(instance))
  1582. return instance
  1583. raise Untraceable("Non-nestable trace item. Ignoring in current context.")
  1584. raise Untraceable("Unknown trace item type %s" % key)
  1585. #print ("Hello again", key, args)
  1586. #return A(key)
  1587. def _start_trace_internal(preamble=None):
  1588. """**internal** starts tracing. Called by vtkSMTrace::StartTrace()."""
  1589. Trace.reset()
  1590. if preamble:
  1591. Trace.Output.append(preamble)
  1592. Trace.Output.append([\
  1593. "#### import the simple module from the paraview",
  1594. "from paraview.simple import *"])
  1595. if not _get_skip_rendering():
  1596. Trace.Output.append([\
  1597. "#### disable automatic camera reset on 'Show'",
  1598. "paraview.simple._DisableFirstRenderCameraReset()"])
  1599. return True
  1600. def _stop_trace_internal():
  1601. """**internal** stops trace. Called by vtkSMTrace::StopTrace()."""
  1602. if not _get_skip_rendering():
  1603. # ensure we trace the active view, so camera changes will be recorded.
  1604. Trace.get_accessor(simple.GetActiveView())
  1605. Trace.Output.append_separated([\
  1606. "#================================================================",
  1607. "# addendum: following script captures some of the application",
  1608. "# state to faithfully reproduce the visualization during playback",
  1609. "#================================================================"])
  1610. # save layout sizes
  1611. layout_trace = SaveLayoutSizes.get_trace(None)
  1612. if layout_trace:
  1613. Trace.Output.append_separated([\
  1614. "#--------------------------------",
  1615. "# saving layout sizes for layouts"])
  1616. Trace.Output.append_separated(layout_trace)
  1617. camera_trace = SaveCameras.get_trace(None)
  1618. if camera_trace:
  1619. Trace.Output.append_separated([\
  1620. "#-----------------------------------",
  1621. "# saving camera placements for views"])
  1622. Trace.Output.append_separated(camera_trace)
  1623. Trace.Output.append_separated([\
  1624. "#--------------------------------------------",
  1625. "# uncomment the following to render all views",
  1626. "# RenderAllViews()",
  1627. "# alternatively, if you want to write images, you can use SaveScreenshot(...)."
  1628. ])
  1629. trace = str(Trace.Output)
  1630. Trace.reset()
  1631. # essential to ensure any obsolete accessor don't linger can cause havoc
  1632. # when saving state following a Python trace session
  1633. # (paraview/paraview#18994)
  1634. import gc
  1635. gc.collect()
  1636. gc.collect()
  1637. return trace
  1638. #------------------------------------------------------------------------------
  1639. # Public methods
  1640. #------------------------------------------------------------------------------
  1641. def start_trace(preamble=None):
  1642. """Starting tracing. On successful start, will return a vtkSMTrace object.
  1643. One can set tracing options on it to control how the tracing. If tracing was
  1644. already started, calling this contine with the same trace."""
  1645. return sm.vtkSMTrace.StartTrace(preamble)
  1646. def stop_trace():
  1647. """Stops the trace and returns the generated trace output string."""
  1648. return sm.vtkSMTrace.StopTrace()
  1649. def get_current_trace_output(raw=False):
  1650. """Returns the trace generated so far in the tracing process."""
  1651. return str(Trace.Output) if not raw else Trace.Output.raw_data()
  1652. def get_current_trace_output_and_reset(raw=False):
  1653. """Equivalent to calling::
  1654. get_current_trace_output(raw)
  1655. reset_trace_output()
  1656. """
  1657. output = get_current_trace_output(raw)
  1658. reset_trace_output()
  1659. return output
  1660. def reset_trace_output():
  1661. """Resets the trace output without resetting the tracing datastructures
  1662. themselves."""
  1663. Trace.Output.reset()
  1664. #------------------------------------------------------------------------------
  1665. if __name__ == "__main__":
  1666. print ("Running test")
  1667. start_trace()
  1668. s = simple.Sphere()
  1669. c = simple.PlotOverLine()
  1670. simple.Show()
  1671. print ("***** TRACE RESULT *****")
  1672. print (stop_trace())