smstate.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. r"""Module for generating a Python state for ParaView.
  2. This module uses paraview.smtrace to generate a trace for a selected set of
  3. proxies by mimicking the creation of various pipeline components in sequence.
  4. Typical usage of this module is as follows::
  5. from paraview import smstate
  6. state = smstate.get_state()
  7. print (state)
  8. Note, this cannot be called when Python tracing is active.
  9. """
  10. from paraview import servermanager as sm
  11. from paraview import smtrace
  12. from paraview import simple
  13. import sys
  14. if sys.version_info >= (3,):
  15. xrange = range
  16. RECORD_MODIFIED_PROPERTIES = sm.vtkSMTrace.RECORD_MODIFIED_PROPERTIES
  17. RECORD_ALL_PROPERTIES = sm.vtkSMTrace.RECORD_ALL_PROPERTIES
  18. class supported_proxies(object):
  19. """filter object used to hide proxies that are currently not supported by
  20. the state saving mechanism or those that are generally skipped in state e.g.
  21. animation proxies and time keeper."""
  22. def __call__(self, proxy):
  23. return proxy and \
  24. not proxy.GetXMLGroup() == "animation" and \
  25. not proxy.GetXMLName() == "TimeKeeper"
  26. class visible_representations(object):
  27. """filter object to skip hidden representations from being saved in state file"""
  28. def __call__(self, proxy):
  29. if not supported_proxies()(proxy): return False
  30. try:
  31. return proxy.Visibility
  32. except AttributeError:
  33. pass
  34. return True
  35. def __toposort(input_set):
  36. """implementation of Tarjan topological sort to sort proxies using consumer
  37. dependencies as graph edges."""
  38. result = []
  39. marked_set = set()
  40. while marked_set != input_set:
  41. unmarked_node = (input_set - marked_set).pop()
  42. __toposort_visit(result, unmarked_node, input_set, marked_set)
  43. result.reverse()
  44. return result
  45. def __toposort_visit(result, proxy, input_set, marked_set, t_marked_set=None):
  46. if t_marked_set is None:
  47. temporarily_marked_set = set()
  48. else:
  49. temporarily_marked_set = t_marked_set
  50. if proxy in temporarily_marked_set:
  51. raise RuntimeError ("Cycle detected in pipeline! %r" % proxy)
  52. if not proxy in marked_set:
  53. temporarily_marked_set.add(proxy)
  54. consumers = set()
  55. get_consumers(proxy, lambda x: x in input_set, consumer_set=consumers, recursive=False)
  56. for x in consumers:
  57. __toposort_visit(result, x, input_set, marked_set, temporarily_marked_set)
  58. marked_set.add(proxy)
  59. temporarily_marked_set.discard(proxy)
  60. result.append(proxy)
  61. def get_consumers(proxy, filter, consumer_set, recursive=True):
  62. """Returns the consumers for a proxy iteratively. If filter is non-None,
  63. filter is used to cull consumers."""
  64. for i in xrange(proxy.GetNumberOfConsumers()):
  65. consumer = proxy.GetConsumerProxy(i)
  66. consumer = consumer.GetTrueParentProxy() if consumer else None
  67. consumer = sm._getPyProxy(consumer)
  68. if not consumer or consumer.IsPrototype() or consumer in consumer_set:
  69. continue
  70. if filter(consumer):
  71. consumer_set.add(consumer)
  72. if recursive: get_consumers(consumer, filter, consumer_set)
  73. def get_producers(proxy, filter, producer_set):
  74. """Returns the producers for a proxy iteratively. If filter is non-None,
  75. filter is used to cull producers."""
  76. for i in xrange(proxy.GetNumberOfProducers()):
  77. producer = proxy.GetProducerProxy(i)
  78. producer = producer.GetTrueParentProxy() if producer else None
  79. producer = sm._getPyProxy(producer)
  80. if not producer or producer.IsPrototype() or producer in producer_set:
  81. continue
  82. if filter(producer):
  83. producer_set.add(producer)
  84. get_producers(producer, filter, producer_set)
  85. # FIXME: LookupTable is missed :/, darn subproxies!
  86. try:
  87. if proxy.LookupTable and filter(proxy.LookupTable):
  88. producer_set.add(proxy.LookupTable)
  89. get_producers(proxy.LookupTable, filter, producer_set)
  90. except AttributeError: pass
  91. try:
  92. if proxy.ScalarOpacityFunction and filter(proxy.ScalarOpacityFunction):
  93. producer_set.add(proxy.ScalarOpacityFunction)
  94. get_producers(proxy.ScalarOpacityFunction, filter, producer_set)
  95. except AttributeError: pass
  96. def get_state(options=None, source_set=[], filter=None, raw=False,
  97. preamble=None, postamble=None):
  98. """Returns the state string"""
  99. if options:
  100. options = sm._getPyProxy(options)
  101. propertiesToTraceOnCreate = options.PropertiesToTraceOnCreate
  102. skipHiddenRepresentations = options.SkipHiddenDisplayProperties
  103. skipRenderingComponents = options.SkipRenderingComponents
  104. else:
  105. propertiesToTraceOnCreate = RECORD_MODIFIED_PROPERTIES
  106. skipHiddenRepresentations = True
  107. skipRenderingComponents = False
  108. # essential to ensure any obsolete accessors don't linger - can cause havoc
  109. # when saving state following a Python trace session
  110. # (paraview/paraview#18994)
  111. import gc
  112. gc.collect()
  113. if sm.vtkSMTrace.GetActiveTracer():
  114. raise RuntimeError ("Cannot generate Python state when tracing is active.")
  115. if filter is None:
  116. filter = visible_representations() if skipHiddenRepresentations else supported_proxies()
  117. # build a set of proxies of interest
  118. if source_set:
  119. start_set = source_set
  120. else:
  121. # if nothing is specified, we save all views and sources.
  122. start_set = [x for x in simple.GetSources().values()] + simple.GetViews()
  123. start_set = [x for x in start_set if filter(x)]
  124. # now, locate dependencies for the start_set, pruning irrelevant branches
  125. consumers = set(start_set)
  126. for proxy in start_set:
  127. get_consumers(proxy, filter, consumers)
  128. producers = set()
  129. for proxy in consumers:
  130. get_producers(proxy, filter, producers)
  131. # proxies_of_interest is set of all proxies that we should trace.
  132. proxies_of_interest = producers.union(consumers)
  133. #print ("proxies_of_interest", proxies_of_interest)
  134. trace_config = smtrace.start_trace(preamble="")
  135. # this ensures that lookup tables/scalar bars etc. are fully traced.
  136. trace_config.SetFullyTraceSupplementalProxies(True)
  137. trace_config.SetSkipRenderingComponents(skipRenderingComponents)
  138. trace = smtrace.TraceOutput()
  139. if preamble is None:
  140. trace.append("# state file generated using %s" % simple.GetParaViewSourceVersion())
  141. trace.append("import paraview")
  142. trace.append("paraview.compatibility.major = %d" % sm.vtkSMProxyManager.GetVersionMajor())
  143. trace.append("paraview.compatibility.minor = %d" % sm.vtkSMProxyManager.GetVersionMinor())
  144. elif preamble:
  145. trace.append(preamble)
  146. trace.append_separated(smtrace.get_current_trace_output_and_reset(raw=True))
  147. #--------------------------------------------------------------------------
  148. # We trace the views and layouts, if any.
  149. if skipRenderingComponents:
  150. views = []
  151. else:
  152. views = [x for x in proxies_of_interest if smtrace.Trace.get_registered_name(x, "views")]
  153. if views:
  154. # sort views by their names, so the state has some structure to it.
  155. views = sorted(views, key=lambda x:\
  156. smtrace.Trace.get_registered_name(x, "views"))
  157. trace.append_separated([\
  158. "# ----------------------------------------------------------------",
  159. "# setup views used in the visualization",
  160. "# ----------------------------------------------------------------"])
  161. for view in views:
  162. # FIXME: save view camera positions and size.
  163. traceitem = smtrace.RegisterViewProxy(view)
  164. traceitem.finalize()
  165. del traceitem
  166. trace.append_separated(smtrace.get_current_trace_output_and_reset(raw=True))
  167. trace.append_separated(["SetActiveView(None)"])
  168. # from views, build the list of layouts of interest.
  169. layouts = set()
  170. for aview in views:
  171. l = simple.GetLayout(aview)
  172. if l:
  173. layouts.add(simple.GetLayout(aview))
  174. # trace create of layouts
  175. if layouts:
  176. layouts = sorted(layouts, key=lambda x:\
  177. smtrace.Trace.get_registered_name(x, "layouts"))
  178. trace.append_separated([\
  179. "# ----------------------------------------------------------------",
  180. "# setup view layouts",
  181. "# ----------------------------------------------------------------"])
  182. for layout in layouts:
  183. traceitem = smtrace.RegisterLayoutProxy(layout)
  184. traceitem.finalize(filter=lambda x: x in views)
  185. del traceitem
  186. trace.append_separated(smtrace.get_current_trace_output_and_reset(raw=True))
  187. if views:
  188. # restore the active view after the layouts have been created.
  189. trace.append_separated([\
  190. "# ----------------------------------------------------------------",
  191. "# restore active view",
  192. "SetActiveView(%s)" % smtrace.Trace.get_accessor(simple.GetActiveView()),
  193. "# ----------------------------------------------------------------"])
  194. #--------------------------------------------------------------------------
  195. # Next, trace data processing pipelines.
  196. sorted_proxies_of_interest = __toposort(proxies_of_interest)
  197. sorted_sources = [x for x in sorted_proxies_of_interest \
  198. if smtrace.Trace.get_registered_name(x, "sources")]
  199. if sorted_sources:
  200. trace.append_separated([\
  201. "# ----------------------------------------------------------------",
  202. "# setup the data processing pipelines",
  203. "# ----------------------------------------------------------------"])
  204. for source in sorted_sources:
  205. traceitem = smtrace.RegisterPipelineProxy(source)
  206. traceitem.finalize()
  207. del traceitem
  208. trace.append_separated(smtrace.get_current_trace_output_and_reset(raw=True))
  209. #--------------------------------------------------------------------------
  210. # Can't decide if the representations should be saved with the pipeline
  211. # objects or afterwards, opting for afterwards for now since the topological
  212. # sort doesn't guarantee that the representations will follow their sources
  213. # anyways.
  214. sorted_representations = [x for x in sorted_proxies_of_interest \
  215. if smtrace.Trace.get_registered_name(x, "representations")]
  216. scalarbar_representations = [x for x in sorted_proxies_of_interest\
  217. if smtrace.Trace.get_registered_name(x, "scalar_bars")]
  218. # print ("sorted_representations", sorted_representations)
  219. # print ("scalarbar_representations", scalarbar_representations)
  220. if not skipRenderingComponents and (sorted_representations or scalarbar_representations):
  221. for view in views:
  222. view_representations = [x for x in view.Representations if x in sorted_representations]
  223. view_scalarbars = [x for x in view.Representations if x in scalarbar_representations]
  224. if view_representations or view_scalarbars:
  225. trace.append_separated([\
  226. "# ----------------------------------------------------------------",
  227. "# setup the visualization in view '%s'" % smtrace.Trace.get_accessor(view),
  228. "# ----------------------------------------------------------------"])
  229. for rep in view_representations:
  230. try:
  231. producer = rep.Input
  232. port = rep.Input.Port
  233. traceitem = smtrace.Show(producer, port, view, rep,
  234. comment="show data from %s" % smtrace.Trace.get_accessor(producer))
  235. traceitem.finalize()
  236. del traceitem
  237. trace.append_separated(smtrace.get_current_trace_output_and_reset(raw=True))
  238. if rep.UseSeparateColorMap:
  239. trace.append_separated([\
  240. "# set separate color map",
  241. "%s.UseSeparateColorMap = True" % (\
  242. smtrace.Trace.get_accessor(rep))])
  243. except AttributeError: pass
  244. # save the scalar bar properties themselves.
  245. if view_scalarbars:
  246. trace.append_separated("# setup the color legend parameters for each legend in this view")
  247. for rep in view_scalarbars:
  248. smtrace.Trace.get_accessor(rep)
  249. trace.append_separated(smtrace.get_current_trace_output_and_reset(raw=True))
  250. trace.append_separated([\
  251. "# set color bar visibility", "%s.Visibility = %s" % (\
  252. smtrace.Trace.get_accessor(rep), rep.Visibility)])
  253. for rep in view_representations:
  254. try:
  255. producer = rep.Input
  256. port = rep.Input.Port
  257. if rep.IsScalarBarVisible(view):
  258. # FIXME: this will save this multiple times, right now,
  259. # if two representations use the same LUT.
  260. trace.append_separated([\
  261. "# show color legend",
  262. "%s.SetScalarBarVisibility(%s, True)" % (\
  263. smtrace.Trace.get_accessor(rep),
  264. smtrace.Trace.get_accessor(view))])
  265. if not rep.Visibility:
  266. traceitem = smtrace.Hide(producer, port, view)
  267. traceitem.finalize()
  268. del traceitem
  269. trace.append_separated(smtrace.get_current_trace_output_and_reset(raw=True))
  270. except AttributeError: pass
  271. #--------------------------------------------------------------------------
  272. # Now, trace the transfer functions (color maps and opacity maps) used.
  273. ctfs = set([x for x in proxies_of_interest \
  274. if smtrace.Trace.get_registered_name(x, "lookup_tables")])
  275. if not skipRenderingComponents and ctfs:
  276. trace.append_separated([\
  277. "# ----------------------------------------------------------------",
  278. "# setup color maps and opacity mapes used in the visualization",
  279. "# note: the Get..() functions create a new object, if needed",
  280. "# ----------------------------------------------------------------"])
  281. for ctf in ctfs:
  282. smtrace.Trace.get_accessor(ctf)
  283. if ctf.ScalarOpacityFunction in proxies_of_interest:
  284. smtrace.Trace.get_accessor(ctf.ScalarOpacityFunction)
  285. trace.append_separated(smtrace.get_current_trace_output_and_reset(raw=True))
  286. # Trace extractors.
  287. exgens = set([x for x in proxies_of_interest \
  288. if smtrace.Trace.get_registered_name(x, "extractors")])
  289. if exgens:
  290. trace.append_separated([\
  291. "# ----------------------------------------------------------------",
  292. "# setup extractors",
  293. "# ----------------------------------------------------------------"])
  294. for exgen in exgens:
  295. # FIXME: this currently doesn't handle multiple output ports
  296. # correctly.
  297. traceitem = smtrace.CreateExtractor(\
  298. xmlname=exgen.Writer.GetXMLName(),
  299. producer=exgen.Producer,
  300. extractor=exgen,
  301. registrationName=smtrace.Trace.get_registered_name(exgen, "extractors"))
  302. traceitem.finalize()
  303. del traceitem
  304. trace.append_separated(smtrace.get_current_trace_output_and_reset(raw=True))
  305. # restore the active source since the order in which the pipeline is created
  306. # in the state file can end up changing the active source to be different
  307. # than what it was when the state is being saved.
  308. trace.append_separated([\
  309. "# ----------------------------------------------------------------",
  310. "# restore active source",
  311. "SetActiveSource(%s)" % smtrace.Trace.get_accessor(simple.GetActiveSource()),
  312. "# ----------------------------------------------------------------"])
  313. if postamble is None:
  314. if options:
  315. # add coda about extracts generation.
  316. trace.append_separated(["",
  317. "if __name__ == '__main__':",
  318. " # generate extracts",
  319. " SaveExtracts(ExtractsOutputDirectory='%s')" % options.ExtractsOutputDirectory])
  320. elif postamble:
  321. trace.append_separated(postamble)
  322. del trace_config
  323. smtrace.stop_trace()
  324. #print (trace)
  325. return str(trace) if not raw else trace.raw_data()
  326. if __name__ == "__main__":
  327. print ( "Running test")
  328. simple.Mandelbrot()
  329. simple.Show()
  330. simple.Hide()
  331. simple.Shrink().ShrinkFactor = 0.4
  332. simple.UpdatePipeline()
  333. simple.Clip().ClipType.Normal[1] = 1
  334. rep = simple.Show()
  335. view = simple.Render()
  336. view.ViewSize=[500, 500]
  337. rep.SetScalarBarVisibility(view, True)
  338. simple.Render()
  339. # rep.SetScalarBarVisibility(view, False)
  340. print ("====================================================================")
  341. print (get_state())