protocols.py 154 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122
  1. r"""paraviewweb_protocols is a module that contains a set of ParaViewWeb related
  2. protocols that can be combined together to provide a flexible way to define
  3. very specific web application.
  4. """
  5. import base64
  6. import fnmatch
  7. import json
  8. import os
  9. import re
  10. import sys
  11. import time
  12. # import paraview modules.
  13. import paraview
  14. from paraview import servermanager, simple
  15. from paraview.servermanager import (
  16. InputProperty,
  17. ProxyProperty,
  18. vtkPVRenderView,
  19. vtkSMPVRepresentationProxy,
  20. vtkSMTransferFunctionManager,
  21. vtkSMTransferFunctionProxy,
  22. )
  23. from paraview.web import helper
  24. from paraview.web.decorators import (
  25. arrayListDomainDecorator,
  26. arraySelectionDomainDecorator,
  27. booleanDomainDecorator,
  28. clipScalarDecorator,
  29. enumerationDomainDecorator,
  30. genericDecorator,
  31. multiLineDecorator,
  32. numberRangeDomainDecorator,
  33. proxyEditorPropertyWidgetDecorator,
  34. proxyListDomainDecorator,
  35. stringListDomainDecorator,
  36. treeDomainDecorator,
  37. )
  38. from vtkmodules.vtkCommonCore import vtkCollection, vtkUnsignedCharArray
  39. from vtkmodules.vtkCommonDataModel import vtkDataObject, vtkImageData
  40. from vtkmodules.vtkWebCore import vtkDataEncoder, vtkWebInteractionEvent
  41. from vtkmodules.web import iteritems
  42. from vtkmodules.web import protocols as vtk_protocols
  43. from vtkmodules.web.render_window_serializer import (
  44. SynchronizationContext,
  45. getReferenceId,
  46. initializeSerializers,
  47. serializeInstance,
  48. )
  49. from wslink import schedule_callback
  50. # import RPC annotation
  51. from wslink import register as exportRpc
  52. if sys.version_info >= (3,):
  53. xrange = range
  54. # =============================================================================
  55. # Helper methods
  56. # =============================================================================
  57. def tryint(s):
  58. try:
  59. return int(s)
  60. except:
  61. return s
  62. def alphanum_key(s):
  63. """Turn a string into a list of string and number chunks.
  64. "z23a" -> ["z", 23, "a"]
  65. """
  66. return [tryint(c) for c in re.split("([0-9]+)", s)]
  67. def sanitizeKeys(mapObj):
  68. output = {}
  69. for key in mapObj:
  70. sanitizeKey = servermanager._make_name_valid(key)
  71. output[sanitizeKey] = mapObj[key]
  72. return output
  73. # =============================================================================
  74. #
  75. # Base class for any ParaView based protocol
  76. #
  77. # =============================================================================
  78. class ParaViewWebProtocol(vtk_protocols.vtkWebProtocol):
  79. def __init__(self):
  80. # self.Application = None
  81. self.coreServer = None
  82. self.multiRoot = False
  83. self.baseDirectory = ""
  84. self.baseDirectoryMap = {}
  85. def mapIdToProxy(self, id):
  86. """
  87. Maps global-id for a proxy to the proxy instance. May return None if the
  88. id is not valid.
  89. """
  90. try:
  91. id = int(id)
  92. except:
  93. return None
  94. if id <= 0:
  95. return None
  96. return simple.servermanager._getPyProxy(
  97. simple.servermanager.ActiveConnection.Session.GetRemoteObject(id)
  98. )
  99. def getView(self, vid):
  100. """
  101. Returns the view for a given view ID, if vid is None then return the
  102. current active view.
  103. :param vid: The view ID
  104. :type vid: str
  105. """
  106. view = self.mapIdToProxy(vid)
  107. if not view:
  108. # Use active view is none provided.
  109. view = simple.GetActiveView()
  110. if not view:
  111. raise Exception("no view provided: " + str(vid))
  112. return view
  113. def debug(self, msg):
  114. if self.debugMode == True:
  115. print(msg)
  116. def setBaseDirectory(self, basePath):
  117. self.overrideDataDirKey = None
  118. self.baseDirectory = ""
  119. self.baseDirectoryMap = {}
  120. self.multiRoot = False
  121. if basePath.find("|") < 0:
  122. if basePath.find("=") >= 0:
  123. basePair = basePath.split("=")
  124. if os.path.exists(basePair[1]):
  125. self.baseDirectory = basePair[1]
  126. self.overrideDataDirKey = basePair[0]
  127. else:
  128. self.baseDirectory = basePath
  129. self.baseDirectory = os.path.normpath(self.baseDirectory)
  130. else:
  131. baseDirs = basePath.split("|")
  132. for baseDir in baseDirs:
  133. basePair = baseDir.split("=")
  134. if os.path.exists(basePair[1]):
  135. self.baseDirectoryMap[basePair[0]] = os.path.normpath(basePair[1])
  136. # Check if we ended up with just a single directory
  137. bdKeys = list(self.baseDirectoryMap)
  138. if len(bdKeys) == 1:
  139. self.baseDirectory = os.path.normpath(self.baseDirectoryMap[bdKeys[0]])
  140. self.overrideDataDirKey = bdKeys[0]
  141. self.baseDirectoryMap = {}
  142. elif len(bdKeys) > 1:
  143. self.multiRoot = True
  144. def getAbsolutePath(self, relativePath):
  145. absolutePath = None
  146. if self.multiRoot == True:
  147. relPathParts = relativePath.replace("\\", "/").split("/")
  148. realBasePath = self.baseDirectoryMap[relPathParts[0]]
  149. absolutePath = os.path.join(realBasePath, *relPathParts[1:])
  150. else:
  151. absolutePath = os.path.join(self.baseDirectory, relativePath)
  152. cleanedPath = os.path.normpath(absolutePath)
  153. # Make sure the cleanedPath is part of the allowed ones
  154. if self.multiRoot:
  155. for key, value in iteritems(self.baseDirectoryMap):
  156. if cleanedPath.startswith(value):
  157. return cleanedPath
  158. elif cleanedPath.startswith(self.baseDirectory):
  159. return cleanedPath
  160. return None
  161. def updateScalarBars(self, view=None, mode=1):
  162. """
  163. Manage scalarbar state
  164. view:
  165. A view proxy or the current active view will be used.
  166. mode:
  167. HIDE_UNUSED_SCALAR_BARS = 0x01,
  168. SHOW_USED_SCALAR_BARS = 0x02
  169. """
  170. v = view or self.getView(-1)
  171. lutMgr = vtkSMTransferFunctionManager()
  172. lutMgr.UpdateScalarBars(v.SMProxy, mode)
  173. def publish(self, topic, event):
  174. if self.coreServer:
  175. self.coreServer.publish(topic, event)
  176. # =============================================================================
  177. #
  178. # Handle Mouse interaction on any type of view
  179. #
  180. # =============================================================================
  181. class ParaViewWebMouseHandler(ParaViewWebProtocol):
  182. def __init__(self, **kwargs):
  183. super(ParaViewWebMouseHandler, self).__init__()
  184. self.lastAction = "up"
  185. # RpcName: mouseInteraction => viewport.mouse.interaction
  186. @exportRpc("viewport.mouse.interaction")
  187. def mouseInteraction(self, event):
  188. """
  189. RPC Callback for mouse interactions.
  190. """
  191. view = self.getView(event["view"])
  192. realViewId = view.GetGlobalIDAsString()
  193. if hasattr(view, "UseInteractiveRenderingForScreenshots"):
  194. if event["action"] == "down":
  195. view.UseInteractiveRenderingForScreenshots = 1
  196. elif event["action"] == "up":
  197. view.UseInteractiveRenderingForScreenshots = 0
  198. buttons = 0
  199. if event["buttonLeft"]:
  200. buttons |= vtkWebInteractionEvent.LEFT_BUTTON
  201. if event["buttonMiddle"]:
  202. buttons |= vtkWebInteractionEvent.MIDDLE_BUTTON
  203. if event["buttonRight"]:
  204. buttons |= vtkWebInteractionEvent.RIGHT_BUTTON
  205. modifiers = 0
  206. if event["shiftKey"]:
  207. modifiers |= vtkWebInteractionEvent.SHIFT_KEY
  208. if event["ctrlKey"]:
  209. modifiers |= vtkWebInteractionEvent.CTRL_KEY
  210. if event["altKey"]:
  211. modifiers |= vtkWebInteractionEvent.ALT_KEY
  212. if event["metaKey"]:
  213. modifiers |= vtkWebInteractionEvent.META_KEY
  214. pvevent = vtkWebInteractionEvent()
  215. pvevent.SetButtons(buttons)
  216. pvevent.SetModifiers(modifiers)
  217. pvevent.SetX(event["x"])
  218. pvevent.SetY(event["y"])
  219. # pvevent.SetKeyCode(event["charCode"])
  220. retVal = self.getApplication().HandleInteractionEvent(view.SMProxy, pvevent)
  221. del pvevent
  222. if event["action"] == "down" and self.lastAction != event["action"]:
  223. self.getApplication().InvokeEvent("StartInteractionEvent")
  224. if event["action"] == "up" and self.lastAction != event["action"]:
  225. self.getApplication().InvokeEvent("EndInteractionEvent")
  226. if retVal:
  227. self.getApplication().InvokeEvent("UpdateEvent")
  228. self.lastAction = event["action"]
  229. return retVal
  230. # =============================================================================
  231. #
  232. # Basic 3D Viewport API (Camera + Orientation + CenterOfRotation
  233. #
  234. # =============================================================================
  235. class ParaViewWebViewPort(ParaViewWebProtocol):
  236. def __init__(self, scale=1.0, maxWidth=2560, maxHeight=1440, **kwargs):
  237. super(ParaViewWebViewPort, self).__init__()
  238. self.scale = scale
  239. self.maxWidth = maxWidth
  240. self.maxHeight = maxHeight
  241. # RpcName: resetCamera => viewport.camera.reset
  242. @exportRpc("viewport.camera.reset")
  243. def resetCamera(self, viewId):
  244. """
  245. RPC callback to reset camera.
  246. """
  247. view = self.getView(viewId)
  248. simple.Render(view)
  249. simple.ResetCamera(view)
  250. try:
  251. view.CenterOfRotation = view.CameraFocalPoint
  252. except:
  253. pass
  254. self.getApplication().InvalidateCache(view.SMProxy)
  255. self.getApplication().InvokeEvent("UpdateEvent")
  256. return view.GetGlobalIDAsString()
  257. # RpcName: updateOrientationAxesVisibility => viewport.axes.orientation.visibility.update
  258. @exportRpc("viewport.axes.orientation.visibility.update")
  259. def updateOrientationAxesVisibility(self, viewId, showAxis):
  260. """
  261. RPC callback to show/hide OrientationAxis.
  262. """
  263. view = self.getView(viewId)
  264. view.OrientationAxesVisibility = showAxis if 1 else 0
  265. self.getApplication().InvalidateCache(view.SMProxy)
  266. self.getApplication().InvokeEvent("UpdateEvent")
  267. return view.GetGlobalIDAsString()
  268. # RpcName: updateCenterAxesVisibility => viewport.axes.center.visibility.update
  269. @exportRpc("viewport.axes.center.visibility.update")
  270. def updateCenterAxesVisibility(self, viewId, showAxis):
  271. """
  272. RPC callback to show/hide CenterAxesVisibility.
  273. """
  274. view = self.getView(viewId)
  275. view.CenterAxesVisibility = showAxis if 1 else 0
  276. self.getApplication().InvalidateCache(view.SMProxy)
  277. self.getApplication().InvokeEvent("UpdateEvent")
  278. return view.GetGlobalIDAsString()
  279. # RpcName: updateCamera => viewport.camera.update
  280. @exportRpc("viewport.camera.update")
  281. def updateCamera(self, view_id, focal_point, view_up, position, forceUpdate=True):
  282. view = self.getView(view_id)
  283. view.CameraFocalPoint = focal_point
  284. view.CameraViewUp = view_up
  285. view.CameraPosition = position
  286. if forceUpdate:
  287. self.getApplication().InvalidateCache(view.SMProxy)
  288. self.getApplication().InvokeEvent("UpdateEvent")
  289. @exportRpc("viewport.camera.get")
  290. def getCamera(self, view_id):
  291. view = self.getView(view_id)
  292. bounds = [-1, 1, -1, 1, -1, 1]
  293. if view and view.GetClientSideView().GetClassName() == "vtkPVRenderView":
  294. rr = view.GetClientSideView().GetRenderer()
  295. bounds = rr.ComputeVisiblePropBounds()
  296. return {
  297. "bounds": bounds,
  298. "center": list(view.CenterOfRotation),
  299. "focal": list(view.CameraFocalPoint),
  300. "up": list(view.CameraViewUp),
  301. "position": list(view.CameraPosition),
  302. }
  303. @exportRpc("viewport.size.update")
  304. def updateSize(self, view_id, width, height):
  305. view = self.getView(view_id)
  306. w = width * self.scale
  307. h = height * self.scale
  308. if w > self.maxWidth:
  309. s = float(self.maxWidth) / float(w)
  310. w *= s
  311. h *= s
  312. elif h > self.maxHeight:
  313. s = float(self.maxHeight) / float(h)
  314. w *= s
  315. h *= s
  316. view.ViewSize = [int(w), int(h)]
  317. self.getApplication().InvokeEvent("UpdateEvent")
  318. # =============================================================================
  319. #
  320. # Provide Image delivery mechanism
  321. #
  322. # =============================================================================
  323. class ParaViewWebViewPortImageDelivery(ParaViewWebProtocol):
  324. # RpcName: stillRender => viewport.image.render
  325. @exportRpc("viewport.image.render")
  326. def stillRender(self, options):
  327. """
  328. RPC Callback to render a view and obtain the rendered image.
  329. """
  330. beginTime = int(round(time.time() * 1000))
  331. view = self.getView(options["view"])
  332. size = view.ViewSize[0:2]
  333. resize = size != options.get("size", size)
  334. if resize:
  335. size = options["size"]
  336. view.ViewSize = size
  337. t = 0
  338. if options and "mtime" in options:
  339. t = options["mtime"]
  340. quality = 100
  341. if options and "quality" in options:
  342. quality = options["quality"]
  343. localTime = 0
  344. if options and "localTime" in options:
  345. localTime = options["localTime"]
  346. reply = {}
  347. app = self.getApplication()
  348. reply["image"] = app.StillRenderToString(view.SMProxy, t, quality)
  349. # Check that we are getting image size we have set if not wait until we
  350. # do.
  351. tries = 10
  352. while (
  353. resize
  354. and list(app.GetLastStillRenderImageSize()) != size
  355. and size != [0, 0]
  356. and tries > 0
  357. ):
  358. app.InvalidateCache(view.SMProxy)
  359. reply["image"] = app.StillRenderToString(view.SMProxy, t, quality)
  360. tries -= 1
  361. if (
  362. not resize
  363. and options
  364. and ("clearCache" in options)
  365. and options["clearCache"]
  366. ):
  367. app.InvalidateCache(view.SMProxy)
  368. reply["image"] = app.StillRenderToString(view.SMProxy, t, quality)
  369. reply["stale"] = app.GetHasImagesBeingProcessed(view.SMProxy)
  370. reply["mtime"] = app.GetLastStillRenderToMTime()
  371. reply["size"] = view.ViewSize[0:2]
  372. reply["format"] = "jpeg;base64"
  373. reply["global_id"] = view.GetGlobalIDAsString()
  374. reply["localTime"] = localTime
  375. endTime = int(round(time.time() * 1000))
  376. reply["workTime"] = endTime - beginTime
  377. return reply
  378. # =============================================================================
  379. #
  380. # Provide Image publish-based delivery mechanism
  381. #
  382. # =============================================================================
  383. CAMERA_PROP_NAMES = [
  384. "CameraFocalPoint",
  385. "CameraParallelProjection",
  386. "CameraParallelScale",
  387. "CameraPosition",
  388. "CameraViewAngle",
  389. "CameraViewUp",
  390. ]
  391. def _pushCameraLink(viewSrc, viewDstList):
  392. props = {}
  393. for name in CAMERA_PROP_NAMES:
  394. props[name] = getattr(viewSrc, name)
  395. for v in viewDstList:
  396. for name in CAMERA_PROP_NAMES:
  397. v.__setattr__(name, props[name])
  398. return props
  399. class ParaViewWebPublishImageDelivery(ParaViewWebProtocol):
  400. def __init__(self, decode=True, **kwargs):
  401. ParaViewWebProtocol.__init__(self)
  402. self.trackingViews = {}
  403. self.lastStaleTime = {}
  404. self.staleHandlerCount = {}
  405. self.deltaStaleTimeBeforeRender = 0.1 # 0.1s
  406. self.staleCountLimit = 10
  407. self.decode = decode
  408. self.viewsInAnimations = []
  409. self.targetFrameRate = 30.0
  410. self.minFrameRate = 12.0
  411. self.maxFrameRate = 30.0
  412. # Camera link handling
  413. self.linkedViews = []
  414. self.linkNames = []
  415. self.onLinkChange = None
  416. # Mouse handling
  417. self.lastAction = "up"
  418. self.activeViewId = None
  419. # In case some external protocol wants to monitor when link views change
  420. def setLinkChangeCallback(self, fn):
  421. self.onLinkChange = fn
  422. def pushRender(self, vId, ignoreAnimation=False, staleCount=0):
  423. if vId not in self.trackingViews:
  424. return
  425. if not self.trackingViews[vId]["enabled"]:
  426. return
  427. if not ignoreAnimation and len(self.viewsInAnimations) > 0:
  428. return
  429. if "originalSize" not in self.trackingViews[vId]:
  430. view = self.getView(vId)
  431. self.trackingViews[vId]["originalSize"] = (
  432. int(view.ViewSize[0]),
  433. int(view.ViewSize[1]),
  434. )
  435. if "ratio" not in self.trackingViews[vId]:
  436. self.trackingViews[vId]["ratio"] = 1
  437. ratio = self.trackingViews[vId]["ratio"]
  438. mtime = self.trackingViews[vId]["mtime"]
  439. quality = self.trackingViews[vId]["quality"]
  440. size = [int(s * ratio) for s in self.trackingViews[vId]["originalSize"]]
  441. reply = self.stillRender(
  442. {"view": vId, "mtime": mtime, "quality": quality, "size": size}
  443. )
  444. # View might have been deleted
  445. if not reply:
  446. return
  447. stale = reply["stale"]
  448. if reply["image"]:
  449. # depending on whether the app has encoding enabled:
  450. if self.decode:
  451. reply["image"] = base64.standard_b64decode(reply["image"])
  452. reply["image"] = self.addAttachment(reply["image"])
  453. reply["format"] = "jpeg"
  454. # save mtime for next call.
  455. self.trackingViews[vId]["mtime"] = reply["mtime"]
  456. # echo back real ID, instead of -1 for 'active'
  457. reply["id"] = vId
  458. self.publish("viewport.image.push.subscription", reply)
  459. if stale:
  460. self.lastStaleTime[vId] = time.time()
  461. if self.staleHandlerCount[vId] == 0:
  462. self.staleHandlerCount[vId] += 1
  463. schedule_callback(
  464. self.deltaStaleTimeBeforeRender,
  465. lambda: self.renderStaleImage(vId, staleCount),
  466. )
  467. else:
  468. self.lastStaleTime[vId] = 0
  469. def renderStaleImage(self, vId, staleCount=0):
  470. if vId in self.staleHandlerCount and self.staleHandlerCount[vId] > 0:
  471. self.staleHandlerCount[vId] -= 1
  472. if self.lastStaleTime[vId] != 0:
  473. delta = time.time() - self.lastStaleTime[vId]
  474. # Break on staleCount otherwise linked view will always report to be stale
  475. # And loop forever
  476. if (
  477. delta >= (self.deltaStaleTimeBeforeRender * (staleCount + 1))
  478. and staleCount < self.staleCountLimit
  479. ):
  480. self.pushRender(vId, False, staleCount + 1)
  481. elif delta < self.deltaStaleTimeBeforeRender:
  482. self.staleHandlerCount[vId] += 1
  483. schedule_callback(
  484. self.deltaStaleTimeBeforeRender - delta + 0.001,
  485. lambda: self.renderStaleImage(vId, staleCount),
  486. )
  487. def animate(self, renderAllViews=True):
  488. if len(self.viewsInAnimations) == 0:
  489. return
  490. nextAnimateTime = time.time() + 1.0 / self.targetFrameRate
  491. # Handle the rendering of the views
  492. if self.activeViewId:
  493. self.pushRender(self.activeViewId, True)
  494. if renderAllViews:
  495. for vId in set(self.viewsInAnimations):
  496. if vId != self.activeViewId:
  497. self.pushRender(vId, True)
  498. nextAnimateTime -= time.time()
  499. if self.targetFrameRate > self.maxFrameRate:
  500. self.targetFrameRate = self.maxFrameRate
  501. if nextAnimateTime < 0:
  502. if nextAnimateTime < -1.0:
  503. self.targetFrameRate = 1
  504. if self.targetFrameRate > self.minFrameRate:
  505. self.targetFrameRate -= 1.0
  506. if self.activeViewId:
  507. # If active view, prioritize that one over the others
  508. # -> Divide by 2 the refresh rate of the other views
  509. schedule_callback(0.001, lambda: self.animate(not renderAllViews))
  510. else:
  511. # Keep animating at the best rate we can
  512. schedule_callback(0.001, lambda: self.animate())
  513. else:
  514. # We have time so let's render all
  515. if self.targetFrameRate < self.maxFrameRate and nextAnimateTime > 0.005:
  516. self.targetFrameRate += 1.0
  517. schedule_callback(nextAnimateTime, lambda: self.animate())
  518. @exportRpc("viewport.image.animation.fps.max")
  519. def setMaxFrameRate(self, fps=30):
  520. self.maxFrameRate = fps
  521. @exportRpc("viewport.image.animation.fps.get")
  522. def getCurrentFrameRate(self):
  523. return self.targetFrameRate
  524. @exportRpc("viewport.image.animation.start")
  525. def startViewAnimation(self, viewId="-1"):
  526. sView = self.getView(viewId)
  527. realViewId = sView.GetGlobalIDAsString()
  528. self.viewsInAnimations.append(realViewId)
  529. if len(self.viewsInAnimations) == 1:
  530. self.animate()
  531. @exportRpc("viewport.image.animation.stop")
  532. def stopViewAnimation(self, viewId="-1"):
  533. sView = self.getView(viewId)
  534. realViewId = sView.GetGlobalIDAsString()
  535. if realViewId in self.viewsInAnimations and realViewId in self.trackingViews:
  536. progressRendering = self.trackingViews[realViewId]["streaming"]
  537. self.viewsInAnimations.remove(realViewId)
  538. if progressRendering:
  539. self.progressiveRender(realViewId)
  540. def progressiveRender(self, viewId="-1"):
  541. sView = self.getView(viewId)
  542. realViewId = sView.GetGlobalIDAsString()
  543. if realViewId in self.viewsInAnimations:
  544. return
  545. if sView.GetSession().GetPendingProgress():
  546. schedule_callback(
  547. self.deltaStaleTimeBeforeRender, lambda: self.progressiveRender(viewId)
  548. )
  549. else:
  550. again = sView.StreamingUpdate(True)
  551. self.pushRender(realViewId, True)
  552. if again:
  553. schedule_callback(0.001, lambda: self.progressiveRender(viewId))
  554. @exportRpc("viewport.image.push")
  555. def imagePush(self, options):
  556. view = self.getView(options["view"])
  557. viewId = view.GetGlobalIDAsString()
  558. # Make sure an image is pushed
  559. self.getApplication().InvalidateCache(view.SMProxy)
  560. self.pushRender(viewId)
  561. # Internal function since the reply[image] is not
  562. # JSON(serializable) it can not be an RPC one
  563. def stillRender(self, options):
  564. """
  565. RPC Callback to render a view and obtain the rendered image.
  566. """
  567. beginTime = int(round(time.time() * 1000))
  568. viewId = str(options["view"])
  569. view = self.getView(viewId)
  570. # If no view id provided, skip rendering
  571. if not viewId:
  572. print("No view")
  573. print(options)
  574. return None
  575. # Make sure request match our selected view
  576. if viewId != "-1" and view.GetGlobalIDAsString() != viewId:
  577. # We got active view rather than our request
  578. view = None
  579. # No view to render => need some cleanup
  580. if not view:
  581. # The view has been deleted, we can not render it...
  582. # Clean up old view state
  583. if viewId in self.viewsInAnimations:
  584. self.viewsInAnimations.remove(viewId)
  585. if viewId in self.trackingViews:
  586. del self.trackingViews[viewId]
  587. if viewId in self.staleHandlerCount:
  588. del self.staleHandlerCount[viewId]
  589. # the view does not exist anymore, skip rendering
  590. return None
  591. # We are in business to render our view...
  592. # Make sure our view size match our request
  593. size = view.ViewSize[0:2]
  594. resize = size != options.get("size", size)
  595. if resize:
  596. size = options["size"]
  597. if size[0] > 10 and size[1] > 10:
  598. view.ViewSize = size
  599. # Rendering options
  600. t = 0
  601. if options and "mtime" in options:
  602. t = options["mtime"]
  603. quality = 100
  604. if options and "quality" in options:
  605. quality = options["quality"]
  606. localTime = 0
  607. if options and "localTime" in options:
  608. localTime = options["localTime"]
  609. reply = {}
  610. app = self.getApplication()
  611. if t == 0:
  612. app.InvalidateCache(view.SMProxy)
  613. if self.decode:
  614. stillRender = app.StillRenderToString
  615. else:
  616. stillRender = app.StillRenderToBuffer
  617. reply_image = stillRender(view.SMProxy, t, quality)
  618. # Check that we are getting image size we have set if not wait until we
  619. # do. The render call will set the actual window size.
  620. tries = 10
  621. while (
  622. resize
  623. and list(app.GetLastStillRenderImageSize()) != size
  624. and size != [0, 0]
  625. and tries > 0
  626. ):
  627. app.InvalidateCache(view.SMProxy)
  628. reply_image = stillRender(view.SMProxy, t, quality)
  629. tries -= 1
  630. if (
  631. not resize
  632. and options
  633. and ("clearCache" in options)
  634. and options["clearCache"]
  635. ):
  636. app.InvalidateCache(view.SMProxy)
  637. reply_image = stillRender(view.SMProxy, t, quality)
  638. # Pack the result
  639. reply["stale"] = app.GetHasImagesBeingProcessed(view.SMProxy)
  640. reply["mtime"] = app.GetLastStillRenderToMTime()
  641. reply["size"] = view.ViewSize[0:2]
  642. reply["memsize"] = reply_image.GetDataSize() if reply_image else 0
  643. reply["format"] = "jpeg;base64" if self.decode else "jpeg"
  644. reply["global_id"] = view.GetGlobalIDAsString()
  645. reply["localTime"] = localTime
  646. if self.decode:
  647. reply["image"] = reply_image
  648. else:
  649. # Convert the vtkUnsignedCharArray into a bytes object, required by Autobahn websockets
  650. reply["image"] = memoryview(reply_image).tobytes() if reply_image else None
  651. endTime = int(round(time.time() * 1000))
  652. reply["workTime"] = endTime - beginTime
  653. return reply
  654. @exportRpc("viewport.image.push.observer.add")
  655. def addRenderObserver(self, viewId):
  656. sView = self.getView(viewId)
  657. if not sView:
  658. return {"error": "Unable to get view with id %s" % viewId}
  659. realViewId = sView.GetGlobalIDAsString()
  660. if not realViewId in self.trackingViews:
  661. observerCallback = lambda *args, **kwargs: self.pushRender(realViewId)
  662. startCallback = lambda *args, **kwargs: self.startViewAnimation(realViewId)
  663. stopCallback = lambda *args, **kwargs: self.stopViewAnimation(realViewId)
  664. tag = self.getApplication().AddObserver("UpdateEvent", observerCallback)
  665. tagStart = self.getApplication().AddObserver(
  666. "StartInteractionEvent", startCallback
  667. )
  668. tagStop = self.getApplication().AddObserver(
  669. "EndInteractionEvent", stopCallback
  670. )
  671. # TODO do we need self.getApplication().AddObserver('ResetActiveView', resetActiveView())
  672. self.trackingViews[realViewId] = {
  673. "tags": [tag, tagStart, tagStop],
  674. "observerCount": 1,
  675. "mtime": 0,
  676. "enabled": True,
  677. "quality": 100,
  678. "streaming": sView.GetClientSideObject().GetEnableStreaming(),
  679. }
  680. self.staleHandlerCount[realViewId] = 0
  681. else:
  682. # There is an observer on this view already
  683. self.trackingViews[realViewId]["observerCount"] += 1
  684. self.pushRender(realViewId)
  685. return {"success": True, "viewId": realViewId}
  686. @exportRpc("viewport.image.push.observer.remove")
  687. def removeRenderObserver(self, viewId):
  688. sView = None
  689. try:
  690. sView = self.getView(viewId)
  691. except:
  692. print("no view with ID %s available in removeRenderObserver" % viewId)
  693. realViewId = sView.GetGlobalIDAsString() if sView else viewId
  694. observerInfo = None
  695. if realViewId in self.trackingViews:
  696. observerInfo = self.trackingViews[realViewId]
  697. if not observerInfo:
  698. return {"error": "Unable to find subscription for view %s" % realViewId}
  699. observerInfo["observerCount"] -= 1
  700. if observerInfo["observerCount"] <= 0:
  701. for tag in observerInfo["tags"]:
  702. self.getApplication().RemoveObserver(tag)
  703. del self.trackingViews[realViewId]
  704. del self.staleHandlerCount[realViewId]
  705. return {"result": "success"}
  706. @exportRpc("viewport.image.push.quality")
  707. def setViewQuality(self, viewId, quality, ratio=1, updateLinkedView=True):
  708. sView = self.getView(viewId)
  709. if not sView:
  710. return {"error": "Unable to get view with id %s" % viewId}
  711. realViewId = sView.GetGlobalIDAsString()
  712. observerInfo = None
  713. if realViewId in self.trackingViews:
  714. observerInfo = self.trackingViews[realViewId]
  715. if not observerInfo:
  716. return {"error": "Unable to find subscription for view %s" % realViewId}
  717. observerInfo["quality"] = quality
  718. observerInfo["ratio"] = ratio
  719. # Handle linked view quality/ratio synch
  720. if updateLinkedView and realViewId in self.linkedViews:
  721. for vid in self.linkedViews:
  722. self.setViewQuality(vid, quality, ratio, False)
  723. # Update image size right now!
  724. if "originalSize" in self.trackingViews[realViewId]:
  725. size = [
  726. int(s * ratio) for s in self.trackingViews[realViewId]["originalSize"]
  727. ]
  728. if "SetSize" in sView:
  729. sView.SetSize(size)
  730. else:
  731. sView.ViewSize = size
  732. return {"result": "success"}
  733. @exportRpc("viewport.image.push.original.size")
  734. def setViewSize(self, viewId, width, height):
  735. if width < 10 or height < 10:
  736. return {"result": "size skip"}
  737. sView = self.getView(viewId)
  738. if not sView:
  739. return {"error": "Unable to get view with id %s" % viewId}
  740. realViewId = sView.GetGlobalIDAsString()
  741. observerInfo = None
  742. if realViewId in self.trackingViews:
  743. observerInfo = self.trackingViews[realViewId]
  744. if not observerInfo:
  745. return {"error": "Unable to find subscription for view %s" % realViewId}
  746. observerInfo["originalSize"] = (int(width), int(height))
  747. return {"result": "success"}
  748. @exportRpc("viewport.image.push.enabled")
  749. def enableView(self, viewId, enabled):
  750. sView = self.getView(viewId)
  751. if not sView:
  752. return {"error": "Unable to get view with id %s" % viewId}
  753. realViewId = sView.GetGlobalIDAsString()
  754. observerInfo = None
  755. if realViewId in self.trackingViews:
  756. observerInfo = self.trackingViews[realViewId]
  757. if not observerInfo:
  758. return {"error": "Unable to find subscription for view %s" % realViewId}
  759. observerInfo["enabled"] = enabled
  760. return {"result": "success"}
  761. @exportRpc("viewport.image.push.invalidate.cache")
  762. def invalidateCache(self, viewId):
  763. sView = self.getView(viewId)
  764. if not sView:
  765. return {"error": "Unable to get view with id %s" % viewId}
  766. self.getApplication().InvalidateCache(sView.SMProxy)
  767. self.getApplication().InvokeEvent("UpdateEvent")
  768. return {"result": "success"}
  769. # -------------------------------------------------------------------------
  770. # View linked
  771. # -------------------------------------------------------------------------
  772. def validateViewLinks(self):
  773. for linkName in self.linkNames:
  774. simple.RemoveCameraLink(linkName)
  775. self.linkNames = []
  776. if len(self.linkedViews) > 1:
  777. viewList = [self.getView(vid) for vid in self.linkedViews]
  778. refView = viewList.pop(0)
  779. for view in viewList:
  780. linkName = "%s_%s" % (
  781. refView.GetGlobalIDAsString(),
  782. view.GetGlobalIDAsString(),
  783. )
  784. simple.AddCameraLink(refView, view, linkName)
  785. self.linkNames.append(linkName)
  786. # Synch camera state
  787. srcView = viewList[0]
  788. dstViews = viewList[1:]
  789. _pushCameraLink(srcView, dstViews)
  790. @exportRpc("viewport.view.link")
  791. def updateViewLink(self, viewId=None, linkState=False):
  792. if viewId:
  793. if linkState:
  794. self.linkedViews.append(viewId)
  795. else:
  796. try:
  797. self.linkedViews.remove(viewId)
  798. except:
  799. pass
  800. # self.validateViewLinks()
  801. if len(self.linkedViews) > 1:
  802. allViews = [self.getView(vid) for vid in self.linkedViews]
  803. _pushCameraLink(allViews[0], allViews[1:])
  804. if self.onLinkChange:
  805. self.onLinkChange(self.linkedViews)
  806. if linkState:
  807. self.getApplication().InvokeEvent("UpdateEvent")
  808. return self.linkedViews
  809. # -------------------------------------------------------------------------
  810. # Mouse handling
  811. # -------------------------------------------------------------------------
  812. @exportRpc("viewport.mouse.interaction")
  813. def mouseInteraction(self, event):
  814. """
  815. RPC Callback for mouse interactions.
  816. """
  817. if "x" not in event or "y" not in event:
  818. return 0
  819. view = self.getView(event["view"])
  820. if hasattr(view, "UseInteractiveRenderingForScreenshots"):
  821. if event["action"] == "down":
  822. view.UseInteractiveRenderingForScreenshots = 1
  823. elif event["action"] == "up":
  824. view.UseInteractiveRenderingForScreenshots = 0
  825. buttons = 0
  826. if event["buttonLeft"]:
  827. buttons |= vtkWebInteractionEvent.LEFT_BUTTON
  828. if event["buttonMiddle"]:
  829. buttons |= vtkWebInteractionEvent.MIDDLE_BUTTON
  830. if event["buttonRight"]:
  831. buttons |= vtkWebInteractionEvent.RIGHT_BUTTON
  832. modifiers = 0
  833. if event["shiftKey"]:
  834. modifiers |= vtkWebInteractionEvent.SHIFT_KEY
  835. if event["ctrlKey"]:
  836. modifiers |= vtkWebInteractionEvent.CTRL_KEY
  837. if event["altKey"]:
  838. modifiers |= vtkWebInteractionEvent.ALT_KEY
  839. if event["metaKey"]:
  840. modifiers |= vtkWebInteractionEvent.META_KEY
  841. pvevent = vtkWebInteractionEvent()
  842. pvevent.SetButtons(buttons)
  843. pvevent.SetModifiers(modifiers)
  844. pvevent.SetX(event["x"])
  845. pvevent.SetY(event["y"])
  846. # pvevent.SetKeyCode(event["charCode"])
  847. retVal = self.getApplication().HandleInteractionEvent(view.SMProxy, pvevent)
  848. del pvevent
  849. self.activeViewId = view.GetGlobalIDAsString()
  850. if event["action"] == "down" and self.lastAction != event["action"]:
  851. self.getApplication().InvokeEvent("StartInteractionEvent")
  852. if event["action"] == "up" and self.lastAction != event["action"]:
  853. self.getApplication().InvokeEvent("EndInteractionEvent")
  854. # if retVal :
  855. # self.getApplication().InvokeEvent('UpdateEvent')
  856. if self.activeViewId in self.linkedViews:
  857. dstViews = [self.getView(vid) for vid in self.linkedViews]
  858. _pushCameraLink(view, dstViews)
  859. self.lastAction = event["action"]
  860. return retVal
  861. @exportRpc("viewport.mouse.zoom.wheel")
  862. def updateZoomFromWheel(self, event):
  863. if "Start" in event["type"]:
  864. self.getApplication().InvokeEvent("StartInteractionEvent")
  865. viewProxy = self.getView(event["view"])
  866. if viewProxy and "spinY" in event:
  867. rootId = viewProxy.GetGlobalIDAsString()
  868. zoomFactor = 1.0 - event["spinY"] / 10.0
  869. if rootId in self.linkedViews:
  870. fp = viewProxy.CameraFocalPoint
  871. pos = viewProxy.CameraPosition
  872. delta = [fp[i] - pos[i] for i in range(3)]
  873. viewProxy.GetActiveCamera().Zoom(zoomFactor)
  874. viewProxy.UpdatePropertyInformation()
  875. pos2 = viewProxy.CameraPosition
  876. viewProxy.CameraFocalPoint = [pos2[i] + delta[i] for i in range(3)]
  877. dstViews = [self.getView(vid) for vid in self.linkedViews]
  878. _pushCameraLink(viewProxy, dstViews)
  879. else:
  880. fp = viewProxy.CameraFocalPoint
  881. pos = viewProxy.CameraPosition
  882. delta = [fp[i] - pos[i] for i in range(3)]
  883. viewProxy.GetActiveCamera().Zoom(zoomFactor)
  884. viewProxy.UpdatePropertyInformation()
  885. pos2 = viewProxy.CameraPosition
  886. viewProxy.CameraFocalPoint = [pos2[i] + delta[i] for i in range(3)]
  887. if "End" in event["type"]:
  888. self.getApplication().InvokeEvent("EndInteractionEvent")
  889. # =============================================================================
  890. #
  891. # Provide Progress support
  892. #
  893. # =============================================================================
  894. class ParaViewWebProgressUpdate(ParaViewWebProtocol):
  895. progressObserverTag = None
  896. def __init__(self, **kwargs):
  897. super(ParaViewWebProgressUpdate, self).__init__()
  898. self.listenToProgress()
  899. def listenToProgress(self):
  900. progressHandler = (
  901. simple.servermanager.ActiveConnection.Session.GetProgressHandler()
  902. )
  903. if not ParaViewWebProgressUpdate.progressObserverTag:
  904. ParaViewWebProgressUpdate.progressObserverTag = progressHandler.AddObserver(
  905. "ProgressEvent", lambda handler, event: self.updateProgress(handler)
  906. )
  907. def updateProgress(self, caller):
  908. txt = caller.GetLastProgressText()
  909. progress = caller.GetLastProgress()
  910. self.publish("paraview.progress", {"text": txt, "progress": progress})
  911. # =============================================================================
  912. #
  913. # Provide Geometry delivery mechanism (WebGL)
  914. #
  915. # =============================================================================
  916. class ParaViewWebViewPortGeometryDelivery(ParaViewWebProtocol):
  917. def __init__(self, **kwargs):
  918. super(ParaViewWebViewPortGeometryDelivery, self).__init__()
  919. self.dataCache = {}
  920. # RpcName: getSceneMetaData => viewport.webgl.metadata
  921. @exportRpc("viewport.webgl.metadata")
  922. def getSceneMetaData(self, view_id):
  923. view = self.getView(view_id)
  924. data = self.getApplication().GetWebGLSceneMetaData(view.SMProxy)
  925. return data
  926. # RpcName: getWebGLData => viewport.webgl.data
  927. @exportRpc("viewport.webgl.data")
  928. def getWebGLData(self, view_id, object_id, part):
  929. view = self.getView(view_id)
  930. data = self.getApplication().GetWebGLBinaryData(
  931. view.SMProxy, str(object_id), part - 1
  932. )
  933. return data
  934. # RpcName: getCachedWebGLData => viewport.webgl.cached.data
  935. @exportRpc("viewport.webgl.cached.data")
  936. def getCachedWebGLData(self, sha):
  937. if sha not in self.dataCache:
  938. return {"success": False, "reason": "Key %s not in data cache" % sha}
  939. return {"success": True, "data": self.dataCache[sha]}
  940. # RpcName: getSceneMetaDataAllTimesteps => viewport.webgl.metadata.alltimesteps
  941. @exportRpc("viewport.webgl.metadata.alltimesteps")
  942. def getSceneMetaDataAllTimesteps(self, view_id=-1):
  943. animationScene = simple.GetAnimationScene()
  944. timeKeeper = animationScene.TimeKeeper
  945. tsVals = timeKeeper.TimestepValues.GetData()
  946. currentTime = timeKeeper.Time
  947. oldCache = self.dataCache
  948. self.dataCache = {}
  949. returnToClient = {}
  950. view = self.getView(view_id)
  951. animationScene.GoToFirst()
  952. # Iterate over all the timesteps, building up a list of unique shas
  953. for i in xrange(len(tsVals)):
  954. simple.Render()
  955. mdString = self.getApplication().GetWebGLSceneMetaData(view.SMProxy)
  956. timestepMetaData = json.loads(mdString)
  957. objects = timestepMetaData["Objects"]
  958. # Iterate over the objects in the scene
  959. for obj in objects:
  960. sha = obj["md5"]
  961. objId = obj["id"]
  962. numParts = obj["parts"]
  963. if sha not in self.dataCache:
  964. if sha in oldCache:
  965. self.dataCache[sha] = oldCache[sha]
  966. else:
  967. transparency = obj["transparency"]
  968. layer = obj["layer"]
  969. partData = []
  970. # Ask for the binary data for each part of this object
  971. for part in xrange(numParts):
  972. partNumber = part
  973. data = self.getApplication().GetWebGLBinaryData(
  974. view.SMProxy, str(objId), partNumber
  975. )
  976. partData.append(data)
  977. # Now add object, with all its binary parts, to the cache
  978. self.dataCache[sha] = {
  979. "md5": sha,
  980. "id": objId,
  981. "numParts": numParts,
  982. "transparency": transparency,
  983. "layer": layer,
  984. "partsList": partData,
  985. }
  986. returnToClient[sha] = {"id": objId, "numParts": numParts}
  987. # Now move time forward
  988. animationScene.GoToNext()
  989. # Set the time back to where it was when all timesteps were requested
  990. timeKeeper.Time = currentTime
  991. animationScene.AnimationTime = currentTime
  992. simple.Render()
  993. return {"success": True, "metaDataList": returnToClient}
  994. # =============================================================================
  995. #
  996. # Provide an updated geometry delivery mechanism which better matches the
  997. # client-side rendering capability we have in vtk.js
  998. #
  999. # =============================================================================
  1000. class ParaViewWebLocalRendering(ParaViewWebProtocol):
  1001. def __init__(self, **kwargs):
  1002. super(ParaViewWebLocalRendering, self).__init__()
  1003. initializeSerializers()
  1004. self.context = SynchronizationContext()
  1005. self.trackingViews = {}
  1006. self.mtime = 0
  1007. # RpcName: getArray => viewport.geometry.array.get
  1008. @exportRpc("viewport.geometry.array.get")
  1009. def getArray(self, dataHash, binary=False):
  1010. if binary:
  1011. return self.addAttachment(self.context.getCachedDataArray(dataHash, binary))
  1012. return self.context.getCachedDataArray(dataHash, binary)
  1013. # RpcName: addViewObserver => viewport.geometry.view.observer.add
  1014. @exportRpc("viewport.geometry.view.observer.add")
  1015. def addViewObserver(self, viewId):
  1016. sView = self.getView(viewId)
  1017. if not sView:
  1018. return {"error": "Unable to get view with id %s" % viewId}
  1019. realViewId = sView.GetGlobalIDAsString()
  1020. def pushGeometry(newSubscription=False):
  1021. simple.Render(sView)
  1022. stateToReturn = self.getViewState(realViewId, newSubscription)
  1023. stateToReturn["mtime"] = 0 if newSubscription else self.mtime
  1024. self.mtime += 1
  1025. return stateToReturn
  1026. if not realViewId in self.trackingViews:
  1027. observerCallback = lambda *args, **kwargs: self.publish(
  1028. "viewport.geometry.view.subscription", pushGeometry()
  1029. )
  1030. tag = self.getApplication().AddObserver("UpdateEvent", observerCallback)
  1031. self.trackingViews[realViewId] = {"tags": [tag], "observerCount": 1}
  1032. else:
  1033. # There is an observer on this view already
  1034. self.trackingViews[realViewId]["observerCount"] += 1
  1035. self.publish("viewport.geometry.view.subscription", pushGeometry(True))
  1036. return {"success": True, "viewId": realViewId}
  1037. # RpcName: removeViewObserver => viewport.geometry.view.observer.remove
  1038. @exportRpc("viewport.geometry.view.observer.remove")
  1039. def removeViewObserver(self, viewId):
  1040. sView = self.getView(viewId)
  1041. if not sView:
  1042. return {"error": "Unable to get view with id %s" % viewId}
  1043. realViewId = sView.GetGlobalIDAsString()
  1044. observerInfo = None
  1045. if realViewId in self.trackingViews:
  1046. observerInfo = self.trackingViews[realViewId]
  1047. if not observerInfo:
  1048. return {"error": "Unable to find subscription for view %s" % realViewId}
  1049. observerInfo["observerCount"] -= 1
  1050. if observerInfo["observerCount"] <= 0:
  1051. for tag in observerInfo["tags"]:
  1052. self.getApplication().RemoveObserver(tag)
  1053. del self.trackingViews[realViewId]
  1054. return {"result": "success"}
  1055. # RpcName: getViewState => viewport.geometry.view.get.state
  1056. @exportRpc("viewport.geometry.view.get.state")
  1057. def getViewState(self, viewId, newSubscription=False):
  1058. sView = self.getView(viewId)
  1059. if not sView:
  1060. return {"error": "Unable to get view with id %s" % viewId}
  1061. self.context.setIgnoreLastDependencies(newSubscription)
  1062. # Get the active view and render window, use it to iterate over renderers
  1063. renderWindow = sView.GetRenderWindow()
  1064. renderWindowId = sView.GetGlobalIDAsString()
  1065. viewInstance = serializeInstance(
  1066. None, renderWindow, renderWindowId, self.context, 1
  1067. )
  1068. viewInstance["extra"] = {
  1069. "vtkRefId": getReferenceId(renderWindow),
  1070. "centerOfRotation": sView.CenterOfRotation.GetData(),
  1071. "camera": getReferenceId(sView.GetActiveCamera()),
  1072. }
  1073. self.context.setIgnoreLastDependencies(False)
  1074. self.context.checkForArraysToRelease()
  1075. if viewInstance:
  1076. return viewInstance
  1077. return None
  1078. # =============================================================================
  1079. #
  1080. # Time management
  1081. #
  1082. # =============================================================================
  1083. class ParaViewWebTimeHandler(ParaViewWebProtocol):
  1084. def __init__(self, **kwargs):
  1085. super(ParaViewWebTimeHandler, self).__init__()
  1086. # setup animation scene
  1087. self.scene = simple.GetAnimationScene()
  1088. simple.GetTimeTrack()
  1089. self.scene.PlayMode = "Snap To TimeSteps"
  1090. self.playing = False
  1091. self.playTime = 0.1 # Time in second
  1092. def nextPlay(self):
  1093. self.updateTime("next")
  1094. if self.playing:
  1095. schedule_callback(self.playTime, self.nextPlay)
  1096. # RpcName: updateTime => pv.vcr.action
  1097. @exportRpc("pv.vcr.action")
  1098. def updateTime(self, action):
  1099. view = simple.GetRenderView()
  1100. animationScene = simple.GetAnimationScene()
  1101. currentTime = view.ViewTime
  1102. if action == "next":
  1103. animationScene.GoToNext()
  1104. if currentTime == view.ViewTime:
  1105. animationScene.GoToFirst()
  1106. if action == "prev":
  1107. animationScene.GoToPrevious()
  1108. if currentTime == view.ViewTime:
  1109. animationScene.GoToLast()
  1110. if action == "first":
  1111. animationScene.GoToFirst()
  1112. if action == "last":
  1113. animationScene.GoToLast()
  1114. timestep = list(animationScene.TimeKeeper.TimestepValues).index(
  1115. animationScene.TimeKeeper.Time
  1116. )
  1117. self.publish(
  1118. "pv.time.change", {"time": float(view.ViewTime), "timeStep": timestep}
  1119. )
  1120. self.getApplication().InvokeEvent("UpdateEvent")
  1121. return view.ViewTime
  1122. @exportRpc("pv.time.index.set")
  1123. def setTimeStep(self, timeIdx):
  1124. anim = simple.GetAnimationScene()
  1125. anim.TimeKeeper.Time = anim.TimeKeeper.TimestepValues[timeIdx]
  1126. self.getApplication().InvokeEvent("UpdateEvent")
  1127. return anim.TimeKeeper.Time
  1128. @exportRpc("pv.time.index.get")
  1129. def getTimeStep(self):
  1130. anim = simple.GetAnimationScene()
  1131. try:
  1132. return list(anim.TimeKeeper.TimestepValues).index(anim.TimeKeeper.Time)
  1133. except:
  1134. return 0
  1135. @exportRpc("pv.time.value.set")
  1136. def setTimeValue(self, t):
  1137. anim = simple.GetAnimationScene()
  1138. try:
  1139. step = list(anim.TimeKeeper.TimestepValues).index(t)
  1140. anim.TimeKeeper.Time = anim.TimeKeeper.TimestepValues[step]
  1141. self.getApplication().InvokeEvent("UpdateEvent")
  1142. except:
  1143. print("Try to update time with", t, "but value not found in the list")
  1144. return anim.TimeKeeper.Time
  1145. @exportRpc("pv.time.value.get")
  1146. def getTimeValue(self):
  1147. anim = simple.GetAnimationScene()
  1148. return anim.TimeKeeper.Time
  1149. @exportRpc("pv.time.values")
  1150. def getTimeValues(self):
  1151. return list(simple.GetAnimationScene().TimeKeeper.TimestepValues)
  1152. @exportRpc("pv.time.play")
  1153. def play(self, deltaT=0.1):
  1154. if not self.playing:
  1155. self.playTime = deltaT
  1156. self.playing = True
  1157. self.getApplication().InvokeEvent("StartInteractionEvent")
  1158. self.nextPlay()
  1159. @exportRpc("pv.time.stop")
  1160. def stop(self):
  1161. self.getApplication().InvokeEvent("EndInteractionEvent")
  1162. self.playing = False
  1163. # =============================================================================
  1164. #
  1165. # Color management
  1166. #
  1167. # =============================================================================
  1168. class ParaViewWebColorManager(ParaViewWebProtocol):
  1169. def __init__(self, pathToColorMaps=None, showBuiltin=True, **kwargs):
  1170. super(ParaViewWebColorManager, self).__init__()
  1171. if pathToColorMaps:
  1172. simple.ImportPresets(filename=pathToColorMaps)
  1173. self.presets = servermanager.vtkSMTransferFunctionPresets.GetInstance()
  1174. self.colorMapNames = []
  1175. for i in range(self.presets.GetNumberOfPresets()):
  1176. if showBuiltin or not self.presets.IsPresetBuiltin(i):
  1177. self.colorMapNames.append(self.presets.GetPresetName(i))
  1178. # RpcName: getScalarBarVisibilities => pv.color.manager.scalarbar.visibility.get
  1179. @exportRpc("pv.color.manager.scalarbar.visibility.get")
  1180. def getScalarBarVisibilities(self, proxyIdList):
  1181. """
  1182. Returns whether or not each specified scalar bar is visible.
  1183. """
  1184. visibilities = {}
  1185. for proxyId in proxyIdList:
  1186. proxy = self.mapIdToProxy(proxyId)
  1187. if proxy is not None:
  1188. rep = simple.GetRepresentation(proxy)
  1189. view = self.getView(-1)
  1190. visibilities[proxyId] = vtkSMPVRepresentationProxy.IsScalarBarVisible(
  1191. rep.SMProxy, view.SMProxy
  1192. )
  1193. return visibilities
  1194. # RpcName: setScalarBarVisibilities => pv.color.manager.scalarbar.visibility.set
  1195. @exportRpc("pv.color.manager.scalarbar.visibility.set")
  1196. def setScalarBarVisibilities(self, proxyIdMap):
  1197. """
  1198. Sets the visibility of the scalar bar corresponding to each specified
  1199. proxy. The representation for each proxy is found using the
  1200. filter/source proxy id and the current view.
  1201. """
  1202. visibilities = {}
  1203. for proxyId in proxyIdMap:
  1204. proxy = self.mapIdToProxy(proxyId)
  1205. if proxy is not None:
  1206. rep = simple.GetDisplayProperties(proxy)
  1207. view = self.getView(-1)
  1208. vtkSMPVRepresentationProxy.SetScalarBarVisibility(
  1209. rep.SMProxy, view.SMProxy, proxyIdMap[proxyId]
  1210. )
  1211. visibilities[proxyId] = vtkSMPVRepresentationProxy.IsScalarBarVisible(
  1212. rep.SMProxy, view.SMProxy
  1213. )
  1214. # Render to get scalar bars in correct position when doing local rendering (webgl)
  1215. simple.Render()
  1216. self.getApplication().InvokeEvent("UpdateEvent")
  1217. return visibilities
  1218. # RpcName: rescaleTransferFunction => pv.color.manager.rescale.transfer.function
  1219. @exportRpc("pv.color.manager.rescale.transfer.function")
  1220. def rescaleTransferFunction(self, options):
  1221. """
  1222. Rescale the color transfer function to fit either the data range,
  1223. the data range over time, or to a custom range, for the array by
  1224. which the representation is currently being colored.
  1225. """
  1226. type = options["type"]
  1227. proxyId = options["proxyId"]
  1228. proxy = self.mapIdToProxy(proxyId)
  1229. rep = simple.GetRepresentation(proxy)
  1230. status = {"success": False}
  1231. if type == "time":
  1232. status[
  1233. "success"
  1234. ] = vtkSMPVRepresentationProxy.RescaleTransferFunctionToDataRangeOverTime(
  1235. rep.SMProxy
  1236. )
  1237. elif type == "data":
  1238. extend = False
  1239. if "extend" in options:
  1240. extend = options["extend"]
  1241. status[
  1242. "success"
  1243. ] = vtkSMPVRepresentationProxy.RescaleTransferFunctionToDataRange(
  1244. rep.SMProxy, extend
  1245. )
  1246. elif type == "custom":
  1247. rangemin = float(options["min"])
  1248. rangemax = float(options["max"])
  1249. extend = False
  1250. if "extend" in options:
  1251. extend = options["extend"]
  1252. lookupTable = rep.LookupTable
  1253. if lookupTable is not None:
  1254. status["success"] = vtkSMTransferFunctionProxy.RescaleTransferFunction(
  1255. lookupTable.SMProxy, rangemin, rangemax, extend
  1256. )
  1257. if status["success"]:
  1258. currentRange = self.getCurrentScalarRange(proxyId)
  1259. status["range"] = currentRange
  1260. self.getApplication().InvokeEvent("UpdateEvent")
  1261. return status
  1262. # RpcName: getCurrentScalarRange => pv.color.manager.scalar.range.get
  1263. @exportRpc("pv.color.manager.scalar.range.get")
  1264. def getCurrentScalarRange(self, proxyId):
  1265. proxy = self.mapIdToProxy(proxyId)
  1266. rep = simple.GetRepresentation(proxy)
  1267. lookupTable = rep.LookupTable
  1268. cMin = lookupTable.RGBPoints[0]
  1269. cMax = lookupTable.RGBPoints[-4]
  1270. return {"min": cMin, "max": cMax}
  1271. # RpcName: colorBy => pv.color.manager.color.by
  1272. @exportRpc("pv.color.manager.color.by")
  1273. def colorBy(
  1274. self,
  1275. representation,
  1276. colorMode,
  1277. arrayLocation="POINTS",
  1278. arrayName="",
  1279. vectorMode="Magnitude",
  1280. vectorComponent=0,
  1281. rescale=False,
  1282. ):
  1283. """
  1284. Choose the array to color by, and optionally specify magnitude or a
  1285. vector component in the case of vector array.
  1286. """
  1287. locationMap = {"POINTS": vtkDataObject.POINT, "CELLS": vtkDataObject.CELL}
  1288. repProxy = self.mapIdToProxy(representation)
  1289. lutProxy = repProxy.LookupTable
  1290. if colorMode == "SOLID":
  1291. # No array just diffuse color
  1292. repProxy.ColorArrayName = ""
  1293. else:
  1294. simple.ColorBy(repProxy, (arrayLocation, arrayName))
  1295. if repProxy.LookupTable:
  1296. lut = repProxy.LookupTable
  1297. lut.VectorMode = str(vectorMode)
  1298. lut.VectorComponent = int(vectorComponent)
  1299. if rescale:
  1300. repProxy.RescaleTransferFunctionToDataRange(rescale, False)
  1301. self.updateScalarBars()
  1302. simple.Render()
  1303. self.getApplication().InvokeEvent("UpdateEvent")
  1304. # RpcName: setOpacityFunctionPoints => pv.color.manager.opacity.points.set
  1305. @exportRpc("pv.color.manager.opacity.points.set")
  1306. def setOpacityFunctionPoints(
  1307. self, arrayName, pointArray, enableOpacityMapping=False
  1308. ):
  1309. lutProxy = simple.GetColorTransferFunction(arrayName)
  1310. pwfProxy = simple.GetOpacityTransferFunction(arrayName)
  1311. # Use whatever the current scalar range is for this array
  1312. cMin = lutProxy.RGBPoints[0]
  1313. cMax = lutProxy.RGBPoints[-4]
  1314. # Scale and bias the x values, which come in between 0.0 and 1.0, to the
  1315. # current scalar range
  1316. for i in range(len(pointArray) // 4):
  1317. idx = i * 4
  1318. x = pointArray[idx]
  1319. pointArray[idx] = (x * (cMax - cMin)) + cMin
  1320. # Set the Points property to scaled and biased points array
  1321. pwfProxy.Points = pointArray
  1322. lutProxy.EnableOpacityMapping = enableOpacityMapping
  1323. simple.Render()
  1324. self.getApplication().InvokeEvent("UpdateEvent")
  1325. # RpcName: getOpacityFunctionPoints => pv.color.manager.opacity.points.get
  1326. @exportRpc("pv.color.manager.opacity.points.get")
  1327. def getOpacityFunctionPoints(self, arrayName):
  1328. result = []
  1329. lutProxy = simple.GetColorTransferFunction(arrayName)
  1330. pwfProxy = simple.GetOpacityTransferFunction(arrayName)
  1331. # Use whatever the current scalar range is for this array
  1332. cMin = lutProxy.RGBPoints[0]
  1333. cMax = lutProxy.RGBPoints[-4]
  1334. pointArray = pwfProxy.Points
  1335. # Scale and bias the x values, which come in between 0.0 and 1.0, to the
  1336. # current scalar range
  1337. for i in range(len(pointArray) // 4):
  1338. idx = i * 4
  1339. result.append(
  1340. {
  1341. "x": (pointArray[idx] - cMin) / (cMax - cMin),
  1342. "y": pointArray[idx + 1],
  1343. "x2": pointArray[idx + 2],
  1344. "y2": pointArray[idx + 3],
  1345. }
  1346. )
  1347. return result
  1348. # RpcName: getRgbPoints => pv.color.manager.rgb.points.get
  1349. @exportRpc("pv.color.manager.rgb.points.get")
  1350. def getRgbPoints(self, arrayName):
  1351. lutProxy = simple.GetColorTransferFunction(arrayName)
  1352. # First, set the current coloring mode
  1353. colorInfo = {}
  1354. colorInfo["mode"] = (
  1355. "categorical" if lutProxy.InterpretValuesAsCategories else "continuous"
  1356. )
  1357. # Now build up the continuous coloring information
  1358. continuousInfo = {}
  1359. l = lutProxy.RGBPoints.GetData()
  1360. scalars = l[0 : len(l) : 4]
  1361. continuousInfo["scalars"] = [float(s) for s in scalars]
  1362. reds = l[1 : len(l) : 4]
  1363. greens = l[2 : len(l) : 4]
  1364. blues = l[3 : len(l) : 4]
  1365. continuousInfo["colors"] = [list(a) for a in zip(reds, greens, blues)]
  1366. colorInfo["continuous"] = continuousInfo
  1367. # Finally, build up the categorical coloring information
  1368. categoricalInfo = {}
  1369. rgbs = lutProxy.IndexedColors
  1370. reds = rgbs[0 : len(rgbs) : 3]
  1371. greens = rgbs[1 : len(rgbs) : 3]
  1372. blues = rgbs[2 : len(rgbs) : 3]
  1373. categoricalInfo["colors"] = [list(a) for a in zip(reds, greens, blues)]
  1374. l = lutProxy.Annotations
  1375. scalars = l[0 : len(l) : 2]
  1376. categoricalInfo["scalars"] = [float(s) for s in scalars]
  1377. categoricalInfo["annotations"] = l[1 : len(l) : 2]
  1378. ### If the numbers of categorical colors and scalars do not match,
  1379. ### then this is probably because you already had a categorical color
  1380. ### scheme set up when you applied a new indexed preset.
  1381. numColors = len(categoricalInfo["colors"])
  1382. numScalars = len(categoricalInfo["scalars"])
  1383. if numColors != numScalars:
  1384. # More colors than scalars means we applied a preset colormap with
  1385. # more colors than what we had set up already, so I want to add
  1386. # scalars and annotations.
  1387. if numColors > numScalars:
  1388. nextValue = 0
  1389. # Another special case here is that there were no categorical
  1390. # scalars/colors before the preset was applied, and we will need
  1391. # to insert a single space character (' ') annotation at the start
  1392. # of the list to make the scalarbar appear.
  1393. if numScalars == 0:
  1394. categoricalInfo["scalars"].append(nextValue)
  1395. categoricalInfo["annotations"].append(" ")
  1396. nextValue = categoricalInfo["scalars"][-1] + 1
  1397. for i in xrange((numColors - numScalars) - 1):
  1398. categoricalInfo["scalars"].append(nextValue)
  1399. categoricalInfo["annotations"].append("")
  1400. nextValue += 1
  1401. # More scalars than colors means we applied a preset colormap with
  1402. # *fewer* colors than what had already, so I want to add fake colors
  1403. # for any non-empty annotations I have after the end of the color list
  1404. elif numScalars > numColors:
  1405. newScalars = categoricalInfo["scalars"][0:numColors]
  1406. newAnnotations = categoricalInfo["annotations"][0:numColors]
  1407. for i in xrange(numColors, numScalars):
  1408. if categoricalInfo["annotations"][i] != "":
  1409. newScalars.append(categoricalInfo["scalars"][i])
  1410. newAnnotations.append(categoricalInfo["annotations"][i])
  1411. categoricalInfo["colors"].append([0.5, 0.5, 0.5])
  1412. categoricalInfo["scalars"] = newScalars
  1413. categoricalInfo["annotations"] = newAnnotations
  1414. # Now that we have made the number of indexed colors and associated
  1415. # scalars match, the final step in this special case is to apply the
  1416. # matched up props we just computed so that server and client ui are
  1417. # in sync.
  1418. idxColorsProperty = []
  1419. annotationsProperty = []
  1420. for aIdx in xrange(len(categoricalInfo["scalars"])):
  1421. annotationsProperty.append(str(categoricalInfo["scalars"][aIdx]))
  1422. annotationsProperty.append(str(categoricalInfo["annotations"][aIdx]))
  1423. idxColorsProperty.extend(categoricalInfo["colors"][aIdx])
  1424. lutProxy.Annotations = annotationsProperty
  1425. lutProxy.IndexedColors = idxColorsProperty
  1426. simple.Render
  1427. colorInfo["categorical"] = categoricalInfo
  1428. return colorInfo
  1429. # RpcName: setRgbPoints => pv.color.manager.rgb.points.set
  1430. @exportRpc("pv.color.manager.rgb.points.set")
  1431. def setRgbPoints(self, arrayName, rgbInfo):
  1432. lutProxy = simple.GetColorTransferFunction(arrayName)
  1433. colorMode = rgbInfo["mode"]
  1434. continuousInfo = rgbInfo["continuous"]
  1435. categoricalInfo = rgbInfo["categorical"]
  1436. # First make sure the continuous mode properties are set
  1437. continuousScalars = continuousInfo["scalars"]
  1438. continuousColors = continuousInfo["colors"]
  1439. rgbPoints = []
  1440. for idx in xrange(len(continuousScalars)):
  1441. scalar = continuousScalars[idx]
  1442. rgb = continuousColors[idx]
  1443. rgbPoints.append(float(scalar))
  1444. rgbPoints.extend(rgb)
  1445. lutProxy.RGBPoints = rgbPoints
  1446. # Now make sure the categorical mode properties are set
  1447. annotations = categoricalInfo["annotations"]
  1448. categoricalScalars = categoricalInfo["scalars"]
  1449. categoricalColors = categoricalInfo["colors"]
  1450. annotationsProperty = []
  1451. idxColorsProperty = []
  1452. for aIdx in xrange(len(annotations)):
  1453. annotationsProperty.append(str(categoricalScalars[aIdx]))
  1454. annotationsProperty.append(str(annotations[aIdx]))
  1455. idxColorsProperty.extend(categoricalColors[aIdx])
  1456. lutProxy.Annotations = annotationsProperty
  1457. lutProxy.IndexedColors = idxColorsProperty
  1458. # Finally, set the coloring mode property
  1459. if colorMode == "continuous": # continuous coloring
  1460. lutProxy.InterpretValuesAsCategories = 0
  1461. else: # categorical coloring
  1462. lutProxy.InterpretValuesAsCategories = 1
  1463. simple.Render()
  1464. self.getApplication().InvokeEvent("UpdateEvent")
  1465. # RpcName: getLutImage => pv.color.manager.lut.image.get
  1466. @exportRpc("pv.color.manager.lut.image.get")
  1467. def getLutImage(self, representation, numSamples, customRange=None):
  1468. repProxy = self.mapIdToProxy(representation)
  1469. lut = repProxy.LookupTable.GetClientSideObject()
  1470. dataRange = customRange
  1471. if not dataRange:
  1472. dataRange = lut.GetRange()
  1473. delta = (dataRange[1] - dataRange[0]) / float(numSamples)
  1474. colorArray = vtkUnsignedCharArray()
  1475. colorArray.SetNumberOfComponents(3)
  1476. colorArray.SetNumberOfTuples(numSamples)
  1477. rgb = [0, 0, 0]
  1478. for i in range(numSamples):
  1479. lut.GetColor(dataRange[0] + float(i) * delta, rgb)
  1480. r = int(round(rgb[0] * 255))
  1481. g = int(round(rgb[1] * 255))
  1482. b = int(round(rgb[2] * 255))
  1483. colorArray.SetTuple3(i, r, g, b)
  1484. # Add the color array to an image data
  1485. imgData = vtkImageData()
  1486. imgData.SetDimensions(numSamples, 1, 1)
  1487. aIdx = imgData.GetPointData().SetScalars(colorArray)
  1488. # Use the vtk data encoder to base-64 encode the image as png, using no compression
  1489. encoder = vtkDataEncoder()
  1490. # two calls in a row crash on Windows - bald timing hack to avoid the crash.
  1491. time.sleep(0.01)
  1492. b64Str = encoder.EncodeAsBase64Png(imgData, 0)
  1493. return {"range": dataRange, "image": b64Str}
  1494. @exportRpc("pv.color.manager.lut.image.all")
  1495. def getLutImages(self, numSamples):
  1496. colorArray = vtkUnsignedCharArray()
  1497. colorArray.SetNumberOfComponents(3)
  1498. colorArray.SetNumberOfTuples(numSamples)
  1499. pxm = simple.servermanager.ProxyManager()
  1500. lutProxy = pxm.NewProxy("lookup_tables", "PVLookupTable")
  1501. lut = lutProxy.GetClientSideObject()
  1502. dataRange = lut.GetRange()
  1503. delta = (dataRange[1] - dataRange[0]) / float(numSamples)
  1504. # Add the color array to an image data
  1505. imgData = vtkImageData()
  1506. imgData.SetDimensions(numSamples, 1, 1)
  1507. imgData.GetPointData().SetScalars(colorArray)
  1508. # Use the vtk data encoder to base-64 encode the image as png, using no compression
  1509. encoder = vtkDataEncoder()
  1510. # Result container
  1511. result = {}
  1512. # Loop over all presets
  1513. for name in self.colorMapNames:
  1514. lutProxy.ApplyPreset(name, True)
  1515. rgb = [0, 0, 0]
  1516. for i in range(numSamples):
  1517. lut.GetColor(dataRange[0] + float(i) * delta, rgb)
  1518. r = int(round(rgb[0] * 255))
  1519. g = int(round(rgb[1] * 255))
  1520. b = int(round(rgb[2] * 255))
  1521. colorArray.SetTuple3(i, r, g, b)
  1522. result[name] = encoder.EncodeAsBase64Png(imgData, 0)
  1523. simple.Delete(lutProxy)
  1524. return result
  1525. # RpcName: setSurfaceOpacity => pv.color.manager.surface.opacity.set
  1526. @exportRpc("pv.color.manager.surface.opacity.set")
  1527. def setSurfaceOpacity(self, representation, enabled):
  1528. repProxy = self.mapIdToProxy(representation)
  1529. lutProxy = repProxy.LookupTable
  1530. lutProxy.EnableOpacityMapping = enabled
  1531. simple.Render()
  1532. self.getApplication().InvokeEvent("UpdateEvent")
  1533. # RpcName: getSurfaceOpacity => pv.color.manager.surface.opacity.get
  1534. @exportRpc("pv.color.manager.surface.opacity.get")
  1535. def getSurfaceOpacity(self, representation):
  1536. repProxy = self.mapIdToProxy(representation)
  1537. lutProxy = repProxy.LookupTable
  1538. return lutProxy.EnableOpacityMapping
  1539. # RpcName: setSurfaceOpacityByArray => pv.color.manager.surface.opacity.by.array.set
  1540. @exportRpc("pv.color.manager.surface.opacity.by.array.set")
  1541. def setSurfaceOpacityByArray(self, arrayName, enabled):
  1542. lutProxy = simple.GetColorTransferFunction(arrayName)
  1543. lutProxy.EnableOpacityMapping = enabled
  1544. simple.Render()
  1545. self.getApplication().InvokeEvent("UpdateEvent")
  1546. # RpcName: getSurfaceOpacityByArray => pv.color.manager.surface.opacity.by.array.get
  1547. @exportRpc("pv.color.manager.surface.opacity.by.array.get")
  1548. def getSurfaceOpacityByArray(self, arrayName):
  1549. lutProxy = simple.GetColorTransferFunction(arrayName)
  1550. return lutProxy.EnableOpacityMapping
  1551. # RpcName: selectColorMap => pv.color.manager.select.preset
  1552. @exportRpc("pv.color.manager.select.preset")
  1553. def selectColorMap(self, representation, paletteName):
  1554. """
  1555. Choose the color map preset to use when coloring by an array.
  1556. """
  1557. repProxy = self.mapIdToProxy(representation)
  1558. lutProxy = repProxy.LookupTable
  1559. if lutProxy is not None:
  1560. lutProxy.ApplyPreset(paletteName, True)
  1561. simple.Render()
  1562. self.getApplication().InvokeEvent("UpdateEvent")
  1563. return {"result": "success"}
  1564. else:
  1565. return {
  1566. "result": "Representation proxy "
  1567. + representation
  1568. + " is missing lookup table"
  1569. }
  1570. return {"result": "preset " + paletteName + " not found"}
  1571. # RpcName: listColorMapNames => pv.color.manager.list.preset
  1572. @exportRpc("pv.color.manager.list.preset")
  1573. def listColorMapNames(self):
  1574. """
  1575. List the names of all color map presets available on the server. This
  1576. list will contain the names of any presets you provided in the file you
  1577. supplied to the constructor of this protocol.
  1578. """
  1579. return self.colorMapNames
  1580. # =============================================================================
  1581. #
  1582. # Proxy management protocol
  1583. #
  1584. # =============================================================================
  1585. class ParaViewWebProxyManager(ParaViewWebProtocol):
  1586. VTK_DATA_TYPES = [
  1587. "void", # 0
  1588. "bit", # 1
  1589. "char", # 2
  1590. "unsigned_char", # 3
  1591. "short", # 4
  1592. "unsigned_short", # 5
  1593. "int", # 6
  1594. "unsigned_int", # 7
  1595. "long", # 8
  1596. "unsigned_long", # 9
  1597. "float", # 10
  1598. "double", # 11
  1599. "id_type", # 12
  1600. "unspecified", # 13
  1601. "unspecified", # 14
  1602. "signed_char", # 15
  1603. "long_long", # 16
  1604. "unsigned_long_long", # 17
  1605. ]
  1606. def __init__(
  1607. self,
  1608. allowedProxiesFile=None,
  1609. baseDir=None,
  1610. fileToLoad=None,
  1611. allowUnconfiguredReaders=True,
  1612. groupProxyEditorWidgets=True,
  1613. respectPropertyGroups=True,
  1614. **kwargs
  1615. ):
  1616. """
  1617. basePath: specify the base directory (or directories) that we should start with, if this
  1618. parameter takes the form: "name1=path1|name2=path2|...", then we will treat this as the
  1619. case where multiple data directories are required. In this case, each top-level directory
  1620. will be given the name associated with the directory in the argument.
  1621. """
  1622. super(ParaViewWebProxyManager, self).__init__()
  1623. self.debugMode = False
  1624. self.groupProxyEditorPropertyWidget = groupProxyEditorWidgets
  1625. self.respectPropertyGroups = respectPropertyGroups
  1626. self.proxyDefinitionCache = {}
  1627. self.domainFunctionMap = {
  1628. "vtkSMBooleanDomain": booleanDomainDecorator,
  1629. "vtkSMProxyListDomain": proxyListDomainDecorator,
  1630. "vtkSMIntRangeDomain": numberRangeDomainDecorator,
  1631. "vtkSMDoubleRangeDomain": numberRangeDomainDecorator,
  1632. "vtkSMArrayRangeDomain": numberRangeDomainDecorator,
  1633. "vtkSMRepresentationTypeDomain": stringListDomainDecorator,
  1634. "vtkSMStringListDomain": stringListDomainDecorator,
  1635. "vtkSMArrayListDomain": arrayListDomainDecorator,
  1636. "vtkSMArraySelectionDomain": arraySelectionDomainDecorator,
  1637. "vtkSMEnumerationDomain": enumerationDomainDecorator,
  1638. "vtkSMCompositeTreeDomain": treeDomainDecorator,
  1639. }
  1640. self.alwaysIncludeProperties = ["GridAxesVisibility"]
  1641. self.alwaysExcludeProperties = [
  1642. "LookupTable",
  1643. "Texture",
  1644. "ColorArrayName",
  1645. "Representations",
  1646. "HiddenRepresentations",
  1647. "HiddenProps",
  1648. "UseTexturedBackground",
  1649. "BackgroundTexture",
  1650. "KeyLightWarmth",
  1651. "KeyLightIntensity",
  1652. "KeyLightElevation",
  1653. "KeyLightAzimuth",
  1654. "FileName",
  1655. ]
  1656. self.propertyTypeMap = {
  1657. "vtkSMIntVectorProperty": "int",
  1658. "vtkSMDoubleVectorProperty": "float",
  1659. "vtkSMStringVectorProperty": "str",
  1660. "vtkSMProxyProperty": "proxy",
  1661. "vtkSMInputProperty": "proxy",
  1662. "vtkSMDoubleMapProperty": "map",
  1663. }
  1664. self.allowedProxies = {}
  1665. self.hintsMap = {
  1666. "PropertyWidgetDecorator": {
  1667. "ClipScalarsDecorator": clipScalarDecorator,
  1668. "GenericDecorator": genericDecorator,
  1669. },
  1670. "Widget": {"multi_line": multiLineDecorator},
  1671. "ProxyEditorPropertyWidget": {
  1672. "default": proxyEditorPropertyWidgetDecorator
  1673. },
  1674. }
  1675. self.setBaseDirectory(baseDir)
  1676. self.allowUnconfiguredReaders = allowUnconfiguredReaders
  1677. # If there was a proxy list file, use it, otherwise use the default
  1678. if allowedProxiesFile:
  1679. self.readAllowedProxies(allowedProxiesFile)
  1680. else:
  1681. from ._default_proxies import getDefaultProxies
  1682. self.readAllowedProxies(getDefaultProxies())
  1683. if fileToLoad:
  1684. if "*" in fileToLoad:
  1685. fullBasePathForGroup = os.path.dirname(self.getAbsolutePath(fileToLoad))
  1686. fileNamePattern = os.path.basename(fileToLoad)
  1687. groupToLoad = []
  1688. for fileName in os.listdir(fullBasePathForGroup):
  1689. if fnmatch.fnmatch(fileName, fileNamePattern):
  1690. groupToLoad.append(os.path.join(fullBasePathForGroup, fileName))
  1691. groupToLoad.sort(key=alphanum_key)
  1692. self.open(groupToLoad)
  1693. else:
  1694. self.open(fileToLoad)
  1695. self.simpleTypes = [int, float, list, str]
  1696. self.view = simple.GetRenderView()
  1697. simple.SetActiveView(self.view)
  1698. simple.Render()
  1699. # --------------------------------------------------------------------------
  1700. # Read the configured proxies json file into a dictionary and process.
  1701. # --------------------------------------------------------------------------
  1702. def readAllowedProxies(self, filepathOrConfig):
  1703. self.availableList = {}
  1704. self.allowedProxies = {}
  1705. configurationData = {}
  1706. if type(filepathOrConfig) == dict:
  1707. configurationData = filepathOrConfig
  1708. else:
  1709. with open(filepathOrConfig, "r") as fd:
  1710. configurationData = json.load(fd)
  1711. self.configureFiltersOrSources("sources", configurationData["sources"])
  1712. self.configureFiltersOrSources("filters", configurationData["filters"])
  1713. self.configureReaders(configurationData["readers"])
  1714. # --------------------------------------------------------------------------
  1715. # Handle filters and sources sections of the proxies config file.
  1716. # --------------------------------------------------------------------------
  1717. def configureFiltersOrSources(self, key, flist):
  1718. list = []
  1719. self.availableList[key] = list
  1720. for item in flist:
  1721. if "label" in item:
  1722. self.allowedProxies[item["label"]] = item["name"]
  1723. list.append(item["label"])
  1724. else:
  1725. self.allowedProxies[item["name"]] = item["name"]
  1726. list.append(item["name"])
  1727. # --------------------------------------------------------------------------
  1728. # Handle the readers section of the proxies config file.
  1729. # --------------------------------------------------------------------------
  1730. def configureReaders(self, rlist):
  1731. self.readerFactoryMap = {}
  1732. for config in rlist:
  1733. readerName = config["name"]
  1734. readerMethod = "FileName"
  1735. useDirectory = False
  1736. if "method" in config:
  1737. readerMethod = config["method"]
  1738. if "useDirectory" in config:
  1739. useDirectory = config["useDirectory"]
  1740. for ext in config["extensions"]:
  1741. self.readerFactoryMap[ext] = [readerName, readerMethod, useDirectory]
  1742. # --------------------------------------------------------------------------
  1743. # Convenience method to get proxy defs, cached if available
  1744. # --------------------------------------------------------------------------
  1745. def getProxyDefinition(self, group, name):
  1746. cacheKey = "%s:%s" % (group, name)
  1747. if cacheKey in self.proxyDefinitionCache:
  1748. return self.proxyDefinitionCache[cacheKey]
  1749. xmlElement = servermanager.ActiveConnection.Session.GetProxyDefinitionManager().GetCollapsedProxyDefinition(
  1750. group, name, None
  1751. )
  1752. # print('\n\n\n (%s, %s): \n\n' % (group, name))
  1753. # xmlElement.PrintXML()
  1754. self.proxyDefinitionCache[cacheKey] = xmlElement
  1755. return xmlElement
  1756. # --------------------------------------------------------------------------
  1757. # Look higher up in XML hierarchy for attributes on a property (useful if
  1758. # a property is an exposed property). From the documentation of vtkSMProperty,
  1759. # the GetParent method will access the sub-proxy to which the property
  1760. # actually belongs, if that is the case.
  1761. # --------------------------------------------------------------------------
  1762. def extractParentAttributes(self, property, attributes):
  1763. proxy = None
  1764. name = ""
  1765. try:
  1766. proxy = property.SMProperty.GetParent()
  1767. name = proxy.GetPropertyName(property.SMProperty)
  1768. except:
  1769. try:
  1770. proxy = property.GetParent()
  1771. name = proxy.GetPropertyName(property)
  1772. except:
  1773. print(
  1774. "ERROR: unable to get property parent for property " + property.Name
  1775. )
  1776. return {}
  1777. xmlElement = self.getProxyDefinition(proxy.GetXMLGroup(), proxy.GetXMLName())
  1778. attrMap = {}
  1779. nbChildren = xmlElement.GetNumberOfNestedElements()
  1780. for i in range(nbChildren):
  1781. xmlChild = xmlElement.GetNestedElement(i)
  1782. elementName = xmlChild.GetName()
  1783. if elementName.endswith("Property"):
  1784. propName = xmlChild.GetAttribute("name")
  1785. if propName == name:
  1786. for attrName in attributes:
  1787. if xmlChild.GetAttribute(attrName):
  1788. attrMap[attrName] = xmlChild.GetAttribute(attrName)
  1789. return attrMap
  1790. # --------------------------------------------------------------------------
  1791. # If we have a proxy property, gather up the xml information from the
  1792. # possible proxy values it can take on.
  1793. # --------------------------------------------------------------------------
  1794. def getProxyListFromProperty(
  1795. self, proxy, proxyPropElement, inPropGroup, group, parentGroup, detailsKey
  1796. ):
  1797. propPropName = proxyPropElement.GetAttribute("name")
  1798. propVisibility = proxyPropElement.GetAttribute("panel_visibility")
  1799. propInstance = proxy.GetProperty(propPropName)
  1800. nbChildren = proxyPropElement.GetNumberOfNestedElements()
  1801. foundPLDChild = False
  1802. subProxiesToProcess = []
  1803. domain = propInstance.FindDomain("vtkSMProxyListDomain")
  1804. if domain:
  1805. foundPLDChild = True
  1806. for j in range(domain.GetNumberOfProxies()):
  1807. subProxy = domain.GetProxy(j)
  1808. pelt = self.getProxyDefinition(
  1809. subProxy.GetXMLGroup(), subProxy.GetXMLName()
  1810. )
  1811. subProxiesToProcess.append([subProxy, pelt])
  1812. if len(subProxiesToProcess) > 0:
  1813. newGroup = group
  1814. newParentGroup = parentGroup
  1815. if self.groupProxyEditorPropertyWidget:
  1816. hintChild = proxyPropElement.FindNestedElementByName("Hints")
  1817. if hintChild:
  1818. pepwChild = hintChild.FindNestedElementByName(
  1819. "ProxyEditorPropertyWidget"
  1820. )
  1821. if pepwChild:
  1822. newGroup = "%s:%s" % (proxy.GetGlobalIDAsString(), propPropName)
  1823. self.groupDetailsMap[newGroup] = {
  1824. "groupName": propPropName,
  1825. "groupWidget": "ProxyEditorPropertyWidget",
  1826. "groupVisibilityProperty": pepwChild.GetAttribute(
  1827. "property"
  1828. ),
  1829. "groupVisibility": propVisibility
  1830. if propVisibility is not None
  1831. else "default",
  1832. "groupType": "ProxyEditorPropertyWidget",
  1833. }
  1834. newParentGroup = group
  1835. self.propertyDetailsMap[detailsKey]["group"] = newGroup
  1836. self.propertyDetailsMap[detailsKey][
  1837. "parentGroup"
  1838. ] = newParentGroup
  1839. for sub in subProxiesToProcess:
  1840. self.processXmlElement(
  1841. sub[0], sub[1], inPropGroup, newGroup, newParentGroup, detailsKey
  1842. )
  1843. return foundPLDChild
  1844. # --------------------------------------------------------------------------
  1845. # Decide whether to update our internal map of property details, or just the
  1846. # ordered list of properties, or both.
  1847. #
  1848. # detailsKey => uniquely identifies a property via concatenation of proxy
  1849. # id and property name, e.g. '476:Opacity'
  1850. # name => property name
  1851. # panelVis => value of 'panel_visibilty' attribute on element
  1852. # size => number of elements to set for this property
  1853. # inPropGroup => whether or not this property is inside a 'PropertyGroup' element
  1854. # group => name of group this property belongs to (could be other than 'root',
  1855. # even if 'inPropGroup' is false, due to the grouping we impose
  1856. # when we encounter a ProxyProperty with Hints: 'ProxyEditorPropertyWidget')
  1857. # parentGroup => name of parent group (could be None for root level property)
  1858. # --------------------------------------------------------------------------
  1859. def trackProperty(
  1860. self,
  1861. detailsKey,
  1862. name,
  1863. panelVis,
  1864. panelVisQualifier,
  1865. size,
  1866. inPropGroup,
  1867. group,
  1868. parentGroup,
  1869. belongsToProxyProperty=None,
  1870. ):
  1871. if detailsKey not in self.propertyDetailsMap:
  1872. self.propertyDetailsMap[detailsKey] = {
  1873. "type": name,
  1874. "panelVis": panelVis,
  1875. "panelVisQualifier": panelVisQualifier,
  1876. "size": size,
  1877. "group": group,
  1878. "parentGroup": parentGroup,
  1879. }
  1880. if belongsToProxyProperty:
  1881. if belongsToProxyProperty not in self.proxyPropertyDependents:
  1882. self.proxyPropertyDependents[belongsToProxyProperty] = []
  1883. self.proxyPropertyDependents[belongsToProxyProperty].append(detailsKey)
  1884. # If this property belongs to a proxy property, and if that property is marked
  1885. # as advanced, then this property should be marked advanced too.
  1886. if (
  1887. self.propertyDetailsMap[belongsToProxyProperty]["panelVis"]
  1888. == "advanced"
  1889. ):
  1890. self.propertyDetailsMap[detailsKey]["panelVis"] = "advanced"
  1891. if inPropGroup:
  1892. self.propertyDetailsMap[detailsKey]["group"] = group
  1893. self.propertyDetailsMap[detailsKey]["parentGroup"] = parentGroup
  1894. if panelVis is not None:
  1895. self.propertyDetailsMap[detailsKey]["panelVis"] = panelVis
  1896. if panelVisQualifier is not None:
  1897. self.propertyDetailsMap[detailsKey][
  1898. "panelVisQualifier"
  1899. ] = panelVisQualifier
  1900. else:
  1901. if (
  1902. panelVis is not None
  1903. and self.propertyDetailsMap[detailsKey]["panelVis"] is None
  1904. ):
  1905. self.propertyDetailsMap[detailsKey]["panelVis"] = panelVis
  1906. if (
  1907. panelVisQualifier is not None
  1908. and self.propertyDetailsMap[detailsKey]["panelVisQualifier"] is None
  1909. ):
  1910. self.propertyDetailsMap[detailsKey][
  1911. "panelVisQualifier"
  1912. ] = panelVisQualifier
  1913. if detailsKey not in self.orderedNameList:
  1914. self.orderedNameList.append(detailsKey)
  1915. else:
  1916. # Do already have property in ordered list, but because we're in a
  1917. # PropertyGroup, we want to override the previous order
  1918. if inPropGroup:
  1919. idx = self.orderedNameList.index(detailsKey)
  1920. self.orderedNameList.pop(idx)
  1921. self.orderedNameList.append(detailsKey)
  1922. # Additionally, if the property we are now re-ordering is a ProxyProperty, then
  1923. # we also need to grab any properties of the sub proxies of it's proxy list domain
  1924. # and move all of those down as well
  1925. if detailsKey in self.proxyPropertyDependents:
  1926. subProxiesProps = self.proxyPropertyDependents[detailsKey]
  1927. for subProxyPropKey in subProxiesProps:
  1928. idx = self.orderedNameList.index(subProxyPropKey)
  1929. self.orderedNameList.pop(idx)
  1930. self.orderedNameList.append(subProxyPropKey)
  1931. # --------------------------------------------------------------------------
  1932. # Gather information from the xml associated with a proxy and properties.
  1933. # --------------------------------------------------------------------------
  1934. def processXmlElement(
  1935. self,
  1936. proxy,
  1937. xmlElement,
  1938. inPropGroup,
  1939. group,
  1940. parentGroup,
  1941. belongsToProxyProperty=None,
  1942. ):
  1943. proxyId = proxy.GetGlobalIDAsString()
  1944. nbChildren = xmlElement.GetNumberOfNestedElements()
  1945. for i in range(nbChildren):
  1946. xmlChild = xmlElement.GetNestedElement(i)
  1947. name = xmlChild.GetName()
  1948. nameAttr = xmlChild.GetAttribute("name")
  1949. detailsKey = proxyId + ":" + str(nameAttr)
  1950. infoOnly = xmlChild.GetAttribute("information_only")
  1951. isInternal = xmlChild.GetAttribute("is_internal")
  1952. panelVis = xmlChild.GetAttribute("panel_visibility")
  1953. numElts = xmlChild.GetAttribute("number_of_elements")
  1954. panelVisQualifier = None
  1955. if self.proxyIsRepresentation:
  1956. panelVisQualifier = xmlChild.GetAttribute(
  1957. "panel_visibility_default_for_representation"
  1958. )
  1959. size = -1
  1960. informationOnly = 0
  1961. internal = 0
  1962. # Check for attributes that might only exist on parent xml
  1963. parentQueryList = []
  1964. if numElts is None:
  1965. parentQueryList.append("number_of_elements")
  1966. else:
  1967. size = int(numElts)
  1968. if infoOnly is None:
  1969. parentQueryList.append("information_only")
  1970. else:
  1971. informationOnly = int(infoOnly)
  1972. if isInternal is None:
  1973. parentQueryList.append("is_internal")
  1974. else:
  1975. internal = int(isInternal)
  1976. if panelVis is None:
  1977. parentQueryList.append("panel_visibility")
  1978. if self.proxyIsRepresentation:
  1979. if panelVisQualifier is None:
  1980. parentQueryList.append(
  1981. "panel_visibility_default_for_representation"
  1982. )
  1983. # Try to retrieve those attributes from parent xml (implicit assumption is this is a property
  1984. # which actually belongs to a subproxy)
  1985. parentAttrs = {}
  1986. if len(parentQueryList) > 0:
  1987. propInstance = proxy.GetProperty(nameAttr)
  1988. if propInstance:
  1989. parentAttrs = self.extractParentAttributes(
  1990. propInstance, parentQueryList
  1991. )
  1992. if (
  1993. "number_of_elements" in parentAttrs
  1994. and parentAttrs["number_of_elements"]
  1995. ):
  1996. size = int(parentAttrs["number_of_elements"])
  1997. if "information_only" in parentAttrs and parentAttrs["information_only"]:
  1998. informationOnly = int(parentAttrs["information_only"])
  1999. if "is_internal" in parentAttrs and parentAttrs["is_internal"]:
  2000. internal = int(parentAttrs["is_internal"])
  2001. if "panel_visibility" in parentAttrs:
  2002. panelVis = parentAttrs["panel_visibility"]
  2003. if "panel_visibility_default_for_representation" in parentAttrs:
  2004. panelVisQualifier = parentAttrs[
  2005. "panel_visibility_default_for_representation"
  2006. ]
  2007. # Now decide whether we should filter out this property
  2008. if (
  2009. (
  2010. (name.endswith("Property") and panelVis == "never")
  2011. or informationOnly == 1
  2012. or internal == 1
  2013. )
  2014. and nameAttr not in self.alwaysIncludeProperties
  2015. ) or nameAttr in self.alwaysExcludeProperties:
  2016. self.debug(
  2017. "Filtering out property "
  2018. + str(nameAttr)
  2019. + " because panelVis is never, infoOnly is 1, isInternal is 1, or "
  2020. + str(nameAttr)
  2021. + " is an ALWAYS EXCLUDE property"
  2022. )
  2023. continue
  2024. if name == "Hints":
  2025. self.debug(" ((((((((((Got the hints))))))))) ")
  2026. for j in range(xmlChild.GetNumberOfNestedElements()):
  2027. hintChild = xmlChild.GetNestedElement(j)
  2028. if hintChild.GetName() == "Visibility":
  2029. replaceInputAttr = hintChild.GetAttribute("replace_input")
  2030. if replaceInputAttr:
  2031. self.propertyDetailsMap["specialHints"] = {
  2032. "replaceInput": int(replaceInputAttr)
  2033. }
  2034. self.debug(
  2035. " Replace input value: "
  2036. + str(replaceInputAttr)
  2037. )
  2038. elif name == "ProxyProperty" or name == "InputProperty":
  2039. self.debug(str(nameAttr) + " is a proxy property")
  2040. self.trackProperty(
  2041. detailsKey,
  2042. name,
  2043. panelVis,
  2044. panelVisQualifier,
  2045. size,
  2046. inPropGroup,
  2047. group,
  2048. parentGroup,
  2049. belongsToProxyProperty,
  2050. )
  2051. foundProxyListDomain = self.getProxyListFromProperty(
  2052. proxy, xmlChild, inPropGroup, group, parentGroup, detailsKey
  2053. )
  2054. if foundProxyListDomain == True:
  2055. self.propertyDetailsMap[detailsKey]["size"] = 1
  2056. elif name.endswith("Property"):
  2057. self.trackProperty(
  2058. detailsKey,
  2059. name,
  2060. panelVis,
  2061. panelVisQualifier,
  2062. size,
  2063. inPropGroup,
  2064. group,
  2065. parentGroup,
  2066. belongsToProxyProperty,
  2067. )
  2068. else:
  2069. # Anything else, we recursively process the element, with special handling
  2070. # in the case that the element is a PropertyGroup
  2071. if name == "PropertyGroup":
  2072. newGroup = group
  2073. newParentGroup = parentGroup
  2074. if self.respectPropertyGroups:
  2075. typeAttr = xmlChild.GetAttribute("type")
  2076. labelAttr = xmlChild.GetAttribute("label")
  2077. panelWidgetAttr = None
  2078. if not labelAttr:
  2079. panelWidgetAttr = xmlChild.GetAttribute("panel_widget")
  2080. labelAttr = panelWidgetAttr
  2081. if not labelAttr:
  2082. labelAttr = "Unlabeled Property Group (%s)" % proxyId
  2083. newGroup = "%s:%s" % (proxyId, labelAttr)
  2084. self.groupDetailsMap[newGroup] = {
  2085. "groupName": labelAttr,
  2086. "groupWidget": "PropertyGroup",
  2087. "groupVisibility": panelVis,
  2088. "groupType": typeAttr,
  2089. }
  2090. newParentGroup = group
  2091. self.processXmlElement(
  2092. proxy,
  2093. xmlChild,
  2094. True,
  2095. newGroup,
  2096. newParentGroup,
  2097. belongsToProxyProperty,
  2098. )
  2099. else:
  2100. self.processXmlElement(
  2101. proxy,
  2102. xmlChild,
  2103. inPropGroup,
  2104. group,
  2105. parentGroup,
  2106. belongsToProxyProperty,
  2107. )
  2108. # --------------------------------------------------------------------------
  2109. # Entry point for the xml processing methods.
  2110. # --------------------------------------------------------------------------
  2111. def getProxyXmlDefinitions(self, proxy):
  2112. self.orderedNameList = []
  2113. self.propertyDetailsMap = {}
  2114. self.groupDetailsMap = {}
  2115. self.proxyPropertyDependents = {}
  2116. self.proxyIsRepresentation = proxy.GetXMLGroup() == "representations"
  2117. self.groupDetailsMap["root"] = {"groupName": "root"}
  2118. xmlElement = self.getProxyDefinition(proxy.GetXMLGroup(), proxy.GetXMLName())
  2119. self.processXmlElement(proxy, xmlElement, False, "root", None)
  2120. self.debug(
  2121. "Length of final ordered property list: " + str(len(self.orderedNameList))
  2122. )
  2123. for propName in self.orderedNameList:
  2124. propRec = self.propertyDetailsMap[propName]
  2125. self.debug(
  2126. "Property "
  2127. + str(propName)
  2128. + ", type = "
  2129. + str(propRec["type"])
  2130. + ", pvis = "
  2131. + str(propRec["panelVis"])
  2132. + ", size = "
  2133. + str(propRec["size"])
  2134. )
  2135. # --------------------------------------------------------------------------
  2136. # Helper function to fill in all the properties of a proxy. Delegates
  2137. # properties with specific domain types to other helpers.
  2138. # --------------------------------------------------------------------------
  2139. def fillPropertyList(self, proxy_id, propertyList, dependency=None):
  2140. proxy = self.mapIdToProxy(proxy_id)
  2141. if proxy:
  2142. for property in proxy.ListProperties():
  2143. prop = proxy.GetProperty(property)
  2144. propertyName = prop.Name
  2145. displayName = prop.GetXMLLabel()
  2146. if propertyName in ["Refresh", "Input"] or propertyName.__contains__(
  2147. "Info"
  2148. ):
  2149. continue
  2150. pythonProp = servermanager._wrap_property(proxy, prop)
  2151. propJson = {"name": propertyName, "id": proxy_id, "label": displayName}
  2152. if dependency is not None:
  2153. propJson["dependency"] = dependency
  2154. if type(prop) == ProxyProperty or type(prop) == InputProperty:
  2155. proxyListDomain = prop.FindDomain("vtkSMProxyListDomain")
  2156. if proxyListDomain:
  2157. # Set the value of this ProxyProperty
  2158. propJson["value"] = prop.GetProxy(0).GetXMLLabel()
  2159. # Now recursively fill in properties of this proxy property
  2160. for i in range(proxyListDomain.GetNumberOfProxies()):
  2161. subProxy = proxyListDomain.GetProxy(i)
  2162. subProxyId = subProxy.GetGlobalIDAsString()
  2163. depStr = (
  2164. proxy_id
  2165. + ":"
  2166. + propertyName
  2167. + ":"
  2168. + subProxy.GetXMLLabel()
  2169. + ":1"
  2170. )
  2171. self.fillPropertyList(subProxyId, propertyList, depStr)
  2172. else:
  2173. # The value of this ProxyProperty is the list of proxy ids
  2174. value = []
  2175. for i in range(prop.GetNumberOfProxies()):
  2176. p = prop.GetProxy(i)
  2177. value.append(p.GetGlobalIDAsString())
  2178. propJson["value"] = value
  2179. else:
  2180. # For everything but ProxyProperty, we should just be able to call GetData()
  2181. try:
  2182. propJson["value"] = prop.GetData()
  2183. except AttributeError as attrErr:
  2184. self.debug(
  2185. "Property "
  2186. + propertyName
  2187. + " has no GetData() method, skipping"
  2188. )
  2189. continue
  2190. # One exception is properties which have enumeration domain, in which case we substitute
  2191. # the numeric value for the enum text value.
  2192. enumDomain = prop.FindDomain("vtkSMEnumerationDomain")
  2193. if enumDomain:
  2194. for entryNum in range(enumDomain.GetNumberOfEntries()):
  2195. if enumDomain.GetEntryText(entryNum) == propJson["value"]:
  2196. propJson["value"] = enumDomain.GetEntryValue(entryNum)
  2197. break
  2198. self.debug("Adding a property to the pre-sorted list: " + str(propJson))
  2199. propertyList.append(propJson)
  2200. # --------------------------------------------------------------------------
  2201. # Make a first pass over the properties, building up a couple of data
  2202. # structures we can use to reorder the properties to match the xml order.
  2203. # --------------------------------------------------------------------------
  2204. def reorderProperties(self, rootProxyId, proxyProperties):
  2205. self.getProxyXmlDefinitions(self.mapIdToProxy(rootProxyId))
  2206. propMap = {}
  2207. for prop in proxyProperties:
  2208. propMap[prop["id"] + ":" + prop["name"]] = prop
  2209. orderedList = []
  2210. groupsInfo = []
  2211. for name in self.orderedNameList:
  2212. if name in propMap:
  2213. orderedList.append(propMap[name])
  2214. xmlDetails = self.propertyDetailsMap[name]
  2215. groupsInfo.append(
  2216. {
  2217. "group": xmlDetails["group"],
  2218. "parentGroup": xmlDetails["parentGroup"],
  2219. }
  2220. )
  2221. return orderedList, groupsInfo
  2222. # --------------------------------------------------------------------------
  2223. # Convenience function to set a property value. Can be extended with other
  2224. # special cases as the need arises.
  2225. # --------------------------------------------------------------------------
  2226. def setProperty(self, property, propertyValue):
  2227. if propertyValue == "vtkProcessId":
  2228. property.SetElement(0, propertyValue)
  2229. else:
  2230. property.SetData(propertyValue)
  2231. # --------------------------------------------------------------------------
  2232. # Taken directly from helper.py, applies domains so we can see real values.
  2233. # --------------------------------------------------------------------------
  2234. def applyDomains(self, parentProxy, proxy_id):
  2235. """
  2236. This function is used to apply the domains so that queries for
  2237. specific values like range min and max retrieve something useful.
  2238. """
  2239. proxy = self.mapIdToProxy(proxy_id)
  2240. # Call recursively on each sub-proxy if any
  2241. for property_name in proxy.ListProperties():
  2242. prop = proxy.GetProperty(property_name)
  2243. if prop.IsA("vtkSMProxyProperty"):
  2244. try:
  2245. if len(prop.Available) and prop.GetNumberOfProxies() == 1:
  2246. listdomain = prop.FindDomain("vtkSMProxyListDomain")
  2247. if listdomain:
  2248. for i in xrange(listdomain.GetNumberOfProxies()):
  2249. internal_proxy = listdomain.GetProxy(i)
  2250. self.applyDomains(
  2251. parentProxy, internal_proxy.GetGlobalIDAsString()
  2252. )
  2253. except:
  2254. exc_type, exc_obj, exc_tb = sys.exc_info()
  2255. print("Unexpected error:", exc_type, " line: ", exc_tb.tb_lineno)
  2256. # Reset all properties to leverage domain capabilities
  2257. for prop_name in proxy.ListProperties():
  2258. if prop_name == "Input":
  2259. continue
  2260. try:
  2261. skipReset = False
  2262. prop = proxy.GetProperty(prop_name)
  2263. iter = prop.NewDomainIterator()
  2264. iter.Begin()
  2265. while not iter.IsAtEnd():
  2266. domain = iter.GetDomain()
  2267. iter.Next()
  2268. try:
  2269. if domain.IsA("vtkSMBoundsDomain"):
  2270. domain.SetDomainValues(
  2271. parentProxy.GetDataInformation().GetBounds()
  2272. )
  2273. except AttributeError as attrErr:
  2274. skipReset = True
  2275. print(
  2276. "Caught exception setting domain values in apply_domains:"
  2277. )
  2278. print(attrErr)
  2279. if not skipReset:
  2280. prop.ResetToDefault()
  2281. # Need to UnRegister to handle the ref count from the NewDomainIterator
  2282. iter.UnRegister(None)
  2283. except:
  2284. exc_type, exc_obj, exc_tb = sys.exc_info()
  2285. print("Unexpected error:", exc_type, " line: ", exc_tb.tb_lineno)
  2286. proxy.UpdateVTKObjects()
  2287. # --------------------------------------------------------------------------
  2288. # Helper function to gather information about the bounds, point, and cell
  2289. # data.
  2290. # --------------------------------------------------------------------------
  2291. def getDataInformation(self, proxy):
  2292. # The stuff in here works, but only after you have created the
  2293. # representation.
  2294. dataInfo = proxy.GetDataInformation()
  2295. # Get some basic statistics about the data
  2296. info = {
  2297. "bounds": dataInfo.DataInformation.GetBounds(),
  2298. "points": dataInfo.DataInformation.GetNumberOfPoints(),
  2299. "cells": dataInfo.DataInformation.GetNumberOfCells(),
  2300. "type": dataInfo.DataInformation.GetPrettyDataTypeString(),
  2301. "memory": dataInfo.DataInformation.GetMemorySize(),
  2302. }
  2303. arrayData = []
  2304. # Get information about point data arrays
  2305. pdInfo = proxy.GetPointDataInformation()
  2306. numberOfPointArrays = pdInfo.GetNumberOfArrays()
  2307. for idx in xrange(numberOfPointArrays):
  2308. array = pdInfo.GetArray(idx)
  2309. numComps = array.GetNumberOfComponents()
  2310. typeStr = ParaViewWebProxyManager.VTK_DATA_TYPES[array.GetDataType()]
  2311. data = {
  2312. "name": array.Name,
  2313. "size": numComps,
  2314. "location": "POINTS",
  2315. "type": typeStr,
  2316. }
  2317. magRange = array.GetRange(-1)
  2318. rangeList = []
  2319. if numComps > 1:
  2320. rangeList.append(
  2321. {"name": "Magnitude", "min": magRange[0], "max": magRange[1]}
  2322. )
  2323. for i in range(numComps):
  2324. ithrange = array.GetRange(i)
  2325. rangeList.append(
  2326. {
  2327. "name": array.GetComponentName(i),
  2328. "min": ithrange[0],
  2329. "max": ithrange[1],
  2330. }
  2331. )
  2332. else:
  2333. rangeList.append({"name": "", "min": magRange[0], "max": magRange[1]})
  2334. data["range"] = rangeList
  2335. arrayData.append(data)
  2336. # Get information about cell data arrays
  2337. cdInfo = proxy.GetCellDataInformation()
  2338. numberOfCellArrays = cdInfo.GetNumberOfArrays()
  2339. for idx in xrange(numberOfCellArrays):
  2340. array = cdInfo.GetArray(idx)
  2341. numComps = array.GetNumberOfComponents()
  2342. typeStr = ParaViewWebProxyManager.VTK_DATA_TYPES[array.GetDataType()]
  2343. data = {
  2344. "name": array.Name,
  2345. "size": numComps,
  2346. "location": "CELLS",
  2347. "type": typeStr,
  2348. }
  2349. magRange = array.GetRange(-1)
  2350. rangeList = []
  2351. if numComps > 1:
  2352. rangeList.append(
  2353. {"name": "Magnitude", "min": magRange[0], "max": magRange[1]}
  2354. )
  2355. for i in range(numComps):
  2356. ithrange = array.GetRange(i)
  2357. rangeList.append(
  2358. {
  2359. "name": array.GetComponentName(i),
  2360. "min": ithrange[0],
  2361. "max": ithrange[1],
  2362. }
  2363. )
  2364. else:
  2365. rangeList.append({"name": "", "min": magRange[0], "max": magRange[1]})
  2366. data["range"] = rangeList
  2367. arrayData.append(data)
  2368. # Get field data information
  2369. fdInfo = dataInfo.DataInformation.GetFieldDataInformation()
  2370. numFieldDataArrays = fdInfo.GetNumberOfArrays()
  2371. for idx in xrange(numFieldDataArrays):
  2372. array = fdInfo.GetArrayInformation(idx)
  2373. numComps = array.GetNumberOfComponents()
  2374. typeStr = ParaViewWebProxyManager.VTK_DATA_TYPES[array.GetDataType()]
  2375. data = {
  2376. "name": array.GetName(),
  2377. "size": numComps,
  2378. "location": "FIELDS",
  2379. "type": typeStr,
  2380. }
  2381. rangeList = []
  2382. for i in range(numComps):
  2383. ithrange = array.GetComponentRange(i)
  2384. rangeList.append(
  2385. {
  2386. "name": array.GetComponentName(i),
  2387. "min": ithrange[0],
  2388. "max": ithrange[1],
  2389. }
  2390. )
  2391. data["range"] = rangeList
  2392. arrayData.append(data)
  2393. # Time data
  2394. timeKeeper = next(
  2395. iter(servermanager.ProxyManager().GetProxiesInGroup("timekeeper").values())
  2396. )
  2397. tsVals = timeKeeper.TimestepValues
  2398. if tsVals:
  2399. if isinstance(tsVals, float):
  2400. tsVals = [tsVals]
  2401. tValStrings = []
  2402. for tsVal in tsVals:
  2403. tValStrings.append(str(tsVal))
  2404. info["time"] = tValStrings
  2405. else:
  2406. info["time"] = []
  2407. info["arrays"] = arrayData
  2408. return info
  2409. # --------------------------------------------------------------------------
  2410. # Helper function to gather information about the coloring used by this
  2411. # representation proxy.
  2412. # --------------------------------------------------------------------------
  2413. def getColorInformation(self, proxy):
  2414. """
  2415. Generates a block of color information, given a representation proxy.
  2416. colorBy: {
  2417. 'representation': '652',
  2418. 'scalarBar': 0,
  2419. 'mode': 'color' # 'array'
  2420. 'color': [ 0.5, 1.0, 1.0 ],
  2421. 'array': [ 'POINTS', 'RTData', -1 ],
  2422. 'colorMap': 'Blue to Red'
  2423. }
  2424. """
  2425. colorInfo = {"representation": proxy.GetGlobalIDAsString()}
  2426. if proxy.GetProperty("DiffuseColor"):
  2427. colorInfo["color"] = proxy.GetProperty("DiffuseColor").GetData()
  2428. if proxy.GetProperty("ColorArrayName"):
  2429. colorInfo["mode"] = "array"
  2430. view = self.getView(-1)
  2431. sbVisible = vtkSMPVRepresentationProxy.IsScalarBarVisible(
  2432. proxy.SMProxy, view.SMProxy
  2433. )
  2434. colorInfo["scalarBar"] = 1 if sbVisible else 0
  2435. lut = proxy.GetProperty("LookupTable").GetData()
  2436. component = -1
  2437. if lut and lut.GetProperty("VectorMode").GetData() == "Component":
  2438. component = lut.GetProperty("VectorComponent").GetData()
  2439. colorArrayProp = proxy.GetProperty("ColorArrayName")
  2440. colorInfo["array"] = [
  2441. colorArrayProp.GetAssociation(),
  2442. colorArrayProp.GetArrayName(),
  2443. component,
  2444. ]
  2445. else:
  2446. colorInfo["mode"] = "color"
  2447. colorInfo["scalarBar"] = 0
  2448. return colorInfo
  2449. # --------------------------------------------------------------------------
  2450. # Helper function populate the ui structures list, which corresponds with
  2451. # with the properties list, element by element, and provides information
  2452. # about the ui elements needed to represent each property.
  2453. # --------------------------------------------------------------------------
  2454. def getUiProperties(self, proxyId, proxyProperties):
  2455. """
  2456. Generates an array of objects, parallel to the properties, containing
  2457. the information needed to render ui for each property. Not all of the
  2458. following attributes will always be present.
  2459. ui : [ {
  2460. 'name': 'Clip Type',
  2461. 'advanced': 1, # 0
  2462. 'doc': 'Documentation string for the property',
  2463. 'dependency': '498:ClipFunction:Sphere:1',
  2464. 'values': { 'Plane': '456', 'Box': '457', 'Scalar': '458', 'Sphere': '459' },
  2465. 'type': 'int', # 'float', 'int', 'str', 'proxy', 'input', ...
  2466. 'widget': 'textfield', # 'checkbox', 'textarea', 'list-1', 'list-n'
  2467. 'size': -1, # -1, 0, 2, 3, 6
  2468. 'range': [ { 'min': 0, 'max': 1 }, { 'min': 4, 'max': 7 }, ... ]
  2469. }, ...
  2470. ]
  2471. """
  2472. if self.proxyIsRepresentation:
  2473. reprProxy = self.mapIdToProxy(proxyId)
  2474. reprProp = reprProxy.GetProperty("Representation")
  2475. reprValue = reprProp.GetData()
  2476. uiProps = []
  2477. for proxyProp in proxyProperties:
  2478. uiElt = {}
  2479. pid = proxyProp["id"]
  2480. propertyName = proxyProp["name"]
  2481. proxy = self.mapIdToProxy(pid)
  2482. prop = proxy.GetProperty(propertyName)
  2483. # Get the xml details we already parsed out for this property
  2484. xmlProps = self.propertyDetailsMap[pid + ":" + propertyName]
  2485. # Set a few defaults for the ui elements, which may be overridden
  2486. uiElt["size"] = xmlProps["size"]
  2487. uiElt["widget"] = "textfield"
  2488. uiElt["type"] = self.propertyTypeMap[prop.GetClassName()]
  2489. doc = prop.GetDocumentation()
  2490. if doc:
  2491. uiElt["doc"] = doc.GetDescription()
  2492. uiElt["name"] = proxyProp["label"]
  2493. proxyProp.pop("label", None)
  2494. if "dependency" in proxyProp:
  2495. uiElt["depends"] = proxyProp["dependency"]
  2496. proxyProp.pop("dependency", None)
  2497. if xmlProps["panelVis"] == "advanced":
  2498. uiElt["advanced"] = 1
  2499. if (
  2500. self.proxyIsRepresentation
  2501. and "panelVisQualifier" in xmlProps
  2502. and xmlProps["panelVisQualifier"]
  2503. and xmlProps["panelVisQualifier"].lower() == reprValue.lower()
  2504. ):
  2505. uiElt["advanced"] = 0
  2506. else:
  2507. uiElt["advanced"] = 0
  2508. # Iterate over the property domains until we find the interesting one
  2509. domainIter = prop.NewDomainIterator()
  2510. domainIter.Begin()
  2511. foundDomain = False
  2512. while domainIter.IsAtEnd() == 0 and foundDomain == False:
  2513. nextDomain = domainIter.GetDomain()
  2514. domainName = nextDomain.GetClassName()
  2515. if domainName in self.domainFunctionMap:
  2516. domainFunction = self.domainFunctionMap[domainName]
  2517. foundDomain = domainFunction(proxyProp, xmlProps, uiElt, nextDomain)
  2518. self.debug(
  2519. "Processed "
  2520. + str(propertyName)
  2521. + " as domain "
  2522. + str(domainName)
  2523. )
  2524. domainIter.Next()
  2525. if foundDomain == False:
  2526. self.debug(
  2527. " ~~~|~~~ " + str(propertyName) + " did not have recognized domain"
  2528. )
  2529. domainIter.FastDelete()
  2530. # Now get hints for the property and provide an opportunity to decorate
  2531. hints = prop.GetHints()
  2532. if hints:
  2533. numHints = hints.GetNumberOfNestedElements()
  2534. for idx in range(numHints):
  2535. hint = hints.GetNestedElement(idx)
  2536. if hint.GetName() in self.hintsMap:
  2537. hmap = self.hintsMap[hint.GetName()]
  2538. hintType = hint.GetAttribute("type")
  2539. hmapKey = None
  2540. if hintType and hintType in hmap:
  2541. hmapKey = hintType
  2542. elif "default" in hmap:
  2543. hmapKey = "default"
  2544. if hmapKey:
  2545. # We're interested in decorating based on this hint
  2546. hintFunction = hmap[hmapKey]
  2547. hintFunction(prop, uiElt, hint)
  2548. uiProps.append(uiElt)
  2549. return uiProps
  2550. # --------------------------------------------------------------------------
  2551. # Helper function to restructure the properties so they reflect the grouping
  2552. # encountered when processing the xml.
  2553. # --------------------------------------------------------------------------
  2554. def restructureProperties(self, groupList, propList, uiList):
  2555. props = []
  2556. uis = [] if uiList else None
  2557. groupMap = {"root": {"proxy": props, "ui": uis}}
  2558. for idx in range(len(groupList)):
  2559. groupInfo = groupList[idx]
  2560. group = groupInfo["group"]
  2561. parentGroup = groupInfo["parentGroup"]
  2562. if (
  2563. group != "root"
  2564. and self.groupDetailsMap[group]["groupVisibility"] == "never"
  2565. ):
  2566. self.debug(
  2567. "Culling property (%s) in group (%s) because group has visibility never"
  2568. % (propList[idx]["name"], group)
  2569. )
  2570. continue
  2571. if not group in groupMap:
  2572. groupMap[group] = {"proxy": [], "ui": [] if uiList else None}
  2573. groupMap[parentGroup]["proxy"].append(
  2574. {
  2575. "id": group,
  2576. "name": self.groupDetailsMap[group]["groupName"],
  2577. "value": False,
  2578. "children": groupMap[group]["proxy"],
  2579. }
  2580. )
  2581. if uiList:
  2582. groupMap[parentGroup]["ui"].append(
  2583. {
  2584. "widget": self.groupDetailsMap[group]["groupWidget"],
  2585. "name": self.groupDetailsMap[group]["groupName"],
  2586. "type": self.groupDetailsMap[group]["groupType"],
  2587. "advanced": 1
  2588. if self.groupDetailsMap[group]["groupVisibility"]
  2589. == "advanced"
  2590. else 0,
  2591. "children": groupMap[group]["ui"],
  2592. }
  2593. )
  2594. # Make sure we can find the group ui item we just appended
  2595. groupMap[group]["groupItem"] = groupMap[parentGroup]["ui"][-1]
  2596. pushToFront = False
  2597. if uiList:
  2598. if (
  2599. "groupVisibilityProperty" in self.groupDetailsMap[group]
  2600. and propList[idx]["name"]
  2601. == self.groupDetailsMap[group]["groupVisibilityProperty"]
  2602. ):
  2603. # If a proxy property claimed this property as its visibilty property, we should not
  2604. # allow it to remain marked as 'advanced'.
  2605. uiList[idx]["advanced"] = 0
  2606. pushToFront = True
  2607. if pushToFront:
  2608. groupMap[group]["ui"].insert(0, uiList[idx])
  2609. else:
  2610. groupMap[group]["ui"].append(uiList[idx])
  2611. if pushToFront:
  2612. groupMap[group]["proxy"].insert(0, propList[idx])
  2613. else:
  2614. groupMap[group]["proxy"].append(propList[idx])
  2615. # Before we're done we need to check for groups of properties where every
  2616. # property in the group has a panel_visibilty of 'advanced', in which
  2617. # case, the group should be marked the same.
  2618. if uiList:
  2619. for groupName in groupMap:
  2620. if groupName != "root" and "ui" in groupMap[groupName]:
  2621. groupUiList = groupMap[groupName]["ui"]
  2622. firstUiElt = groupUiList[0]
  2623. groupDependency = False
  2624. if "depends" in firstUiElt and firstUiElt["depends"]:
  2625. groupDependency = True
  2626. advancedGroup = True
  2627. for uiProp in groupUiList:
  2628. if uiProp["advanced"] == 0:
  2629. advancedGroup = False
  2630. if groupDependency and (
  2631. "depends" not in uiProp
  2632. or uiProp["depends"] != firstUiElt["depends"]
  2633. ):
  2634. groupDependency = False
  2635. if advancedGroup:
  2636. groupMap[groupName]["groupItem"]["advanced"] = 1
  2637. if groupDependency:
  2638. groupMap[groupName]["groupItem"]["depends"] = firstUiElt[
  2639. "depends"
  2640. ]
  2641. return {"proxy": props, "ui": uis}
  2642. # --------------------------------------------------------------------------
  2643. # Helper function validates the string we get from the client to make sure
  2644. # it is one of the allowed proxies that has been set up on the server side.
  2645. # --------------------------------------------------------------------------
  2646. def validate(self, functionName):
  2647. if functionName not in self.allowedProxies:
  2648. return None
  2649. else:
  2650. return functionName.strip()
  2651. @exportRpc("pv.proxy.manager.create")
  2652. def create(
  2653. self,
  2654. functionName,
  2655. parentId,
  2656. initialValues={},
  2657. skipDomain=False,
  2658. subProxyValues={},
  2659. ):
  2660. """
  2661. Creates a new filter/source proxy as a child of the specified
  2662. parent proxy. Returns the proxy state for the newly created
  2663. proxy as a JSON object.
  2664. """
  2665. name = self.validate(functionName)
  2666. if not name:
  2667. return {
  2668. "success": False,
  2669. "reason": '"'
  2670. + functionName
  2671. + '" was not valid and could not be evaluated',
  2672. }
  2673. pid = parentId
  2674. parentProxy = self.mapIdToProxy(parentId)
  2675. if parentProxy:
  2676. simple.SetActiveSource(parentProxy)
  2677. else:
  2678. pid = "0"
  2679. # Create new source/filter
  2680. sanitizedInitialValues = sanitizeKeys(initialValues)
  2681. allowed = self.allowedProxies[name]
  2682. newProxy = paraview.simple.__dict__[allowed](**sanitizedInitialValues)
  2683. # Update subproxy values
  2684. if newProxy:
  2685. for subProxyName in subProxyValues:
  2686. subProxy = newProxy.GetProperty(subProxyName).GetData()
  2687. if subProxy:
  2688. for propName in subProxyValues[subProxyName]:
  2689. prop = subProxy.SMProxy.GetProperty(propName)
  2690. value = subProxyValues[subProxyName][propName]
  2691. if isinstance(value, list):
  2692. prop.SetElements(value)
  2693. else:
  2694. prop.SetElements([value])
  2695. subProxy.UpdateVTKObjects()
  2696. # To make WebGL export work
  2697. simple.Show()
  2698. simple.Render()
  2699. self.getApplication().InvokeEvent("UpdateEvent")
  2700. try:
  2701. if not skipDomain:
  2702. self.applyDomains(parentProxy, newProxy.GetGlobalIDAsString())
  2703. except Exception as inst:
  2704. print("Caught exception applying domains:")
  2705. print(inst)
  2706. return self.get(newProxy.GetGlobalIDAsString())
  2707. @exportRpc("pv.proxy.manager.create.reader")
  2708. def open(self, relativePath):
  2709. """
  2710. Open relative file paths, attempting to use the file extension to select
  2711. from the configured readers.
  2712. """
  2713. fileToLoad = []
  2714. if type(relativePath) == list:
  2715. for file in relativePath:
  2716. validPath = self.getAbsolutePath(file)
  2717. if validPath:
  2718. fileToLoad.append(validPath)
  2719. else:
  2720. validPath = self.getAbsolutePath(relativePath)
  2721. if validPath:
  2722. fileToLoad.append(validPath)
  2723. if len(fileToLoad) == 0:
  2724. return {
  2725. "success": False,
  2726. "reason": "No valid path name " + str(relativePath),
  2727. }
  2728. # Get file extension and look for configured reader
  2729. idx = fileToLoad[0].rfind(".")
  2730. extension = fileToLoad[0][idx + 1 :]
  2731. # Check if we were asked to load a state file
  2732. if extension == "pvsm":
  2733. simple.LoadState(fileToLoad[0])
  2734. newView = simple.Render()
  2735. simple.SetActiveView(newView)
  2736. if self.getApplication():
  2737. self.getApplication().InvokeEvent("ResetActiveView")
  2738. self.getApplication().InvokeEvent("UpdateEvent")
  2739. return {"success": True, "view": newView.GetGlobalIDAsString()}
  2740. readerName = None
  2741. if extension in self.readerFactoryMap:
  2742. readerName = self.readerFactoryMap[extension][0]
  2743. customMethod = self.readerFactoryMap[extension][1]
  2744. useDirectory = self.readerFactoryMap[extension][2]
  2745. # Open the file(s)
  2746. reader = None
  2747. if readerName:
  2748. if useDirectory:
  2749. kw = {customMethod: os.path.dirname(fileToLoad[0])}
  2750. else:
  2751. kw = {customMethod: fileToLoad}
  2752. reader = paraview.simple.__dict__[readerName](**kw)
  2753. else:
  2754. if self.allowUnconfiguredReaders:
  2755. reader = simple.OpenDataFile(fileToLoad)
  2756. else:
  2757. return {
  2758. "success": False,
  2759. "reason": "No configured reader found for "
  2760. + extension
  2761. + " files, and unconfigured readers are not enabled.",
  2762. }
  2763. # Rename the reader proxy
  2764. name = fileToLoad[0].split(os.path.sep)[-1]
  2765. if len(name) > 15:
  2766. name = name[:15] + "*"
  2767. simple.RenameSource(name, reader)
  2768. # Representation, view, and camera setup
  2769. simple.Show()
  2770. simple.Render()
  2771. simple.ResetCamera()
  2772. if self.getApplication():
  2773. self.getApplication().InvokeEvent("UpdateEvent")
  2774. return {"success": True, "id": reader.GetGlobalIDAsString()}
  2775. @exportRpc("pv.proxy.manager.get")
  2776. def get(self, proxyId, ui=True):
  2777. """
  2778. Returns the proxy state for the given proxyId as a JSON object.
  2779. """
  2780. proxyProperties = []
  2781. proxyId = str(proxyId)
  2782. self.fillPropertyList(proxyId, proxyProperties)
  2783. proxyProperties, groupsInfo = self.reorderProperties(proxyId, proxyProperties)
  2784. proxyJson = {"id": proxyId}
  2785. # Perform costly request only when needed
  2786. uiProperties = None
  2787. if ui:
  2788. uiProperties = self.getUiProperties(proxyId, proxyProperties)
  2789. # Now restructure the flat, ordered lists to reflect grouping
  2790. restructuredProperties = self.restructureProperties(
  2791. groupsInfo, proxyProperties, uiProperties
  2792. )
  2793. proxyJson["properties"] = restructuredProperties["proxy"]
  2794. if ui:
  2795. proxyJson["ui"] = restructuredProperties["ui"]
  2796. if "specialHints" in self.propertyDetailsMap:
  2797. proxyJson["hints"] = self.propertyDetailsMap["specialHints"]
  2798. proxy = self.mapIdToProxy(proxyId)
  2799. if proxy.SMProxy.IsA("vtkSMRepresentationProxy") == 1:
  2800. colorInfo = self.getColorInformation(proxy)
  2801. proxyJson["colorBy"] = colorInfo
  2802. elif proxy.SMProxy.IsA("vtkSMSourceProxy") == 1:
  2803. dataInfo = self.getDataInformation(proxy)
  2804. proxyJson["data"] = dataInfo
  2805. return proxyJson
  2806. @exportRpc("pv.proxy.manager.find.id")
  2807. def findProxyId(self, groupName, proxyName):
  2808. proxyMgr = servermanager.ProxyManager()
  2809. proxy = proxyMgr.GetProxy(groupName, proxyName)
  2810. proxyId = proxy.SMProxy.GetGlobalIDAsString()
  2811. return proxyId
  2812. @exportRpc("pv.proxy.manager.update")
  2813. def update(self, propertiesList):
  2814. """
  2815. Takes a list of properties and updates the corresponding proxies.
  2816. """
  2817. failureList = []
  2818. for newPropObj in propertiesList:
  2819. try:
  2820. proxyId = str(newPropObj["id"])
  2821. proxy = self.mapIdToProxy(proxyId)
  2822. propertyName = servermanager._make_name_valid(newPropObj["name"])
  2823. propertyValue = helper.removeUnicode(newPropObj["value"])
  2824. proxyProperty = servermanager._wrap_property(
  2825. proxy, proxy.GetProperty(propertyName)
  2826. )
  2827. self.setProperty(proxyProperty, propertyValue)
  2828. except:
  2829. failureList.append(
  2830. "Unable to set property for proxy "
  2831. + proxyId
  2832. + ": "
  2833. + str(newPropObj)
  2834. )
  2835. if len(failureList) > 0:
  2836. return {"success": False, "errorList": failureList}
  2837. self.getApplication().InvokeEvent("UpdateEvent")
  2838. return {"success": True}
  2839. @exportRpc("pv.proxy.manager.delete")
  2840. def delete(self, proxyId):
  2841. """
  2842. Checks if the proxy can be deleted (it is not the input to another
  2843. proxy), and then deletes it if so. Returns True if the proxy was
  2844. deleted, False otherwise.
  2845. """
  2846. proxy = self.mapIdToProxy(proxyId)
  2847. canDelete = True
  2848. pid = "0"
  2849. for proxyJson in self.list()["sources"]:
  2850. if proxyJson["parent"] == proxyId:
  2851. canDelete = False
  2852. elif proxyJson["id"] == proxyId:
  2853. pid = proxyJson["parent"]
  2854. if proxy is not None and canDelete is True:
  2855. simple.Delete(proxy)
  2856. self.updateScalarBars()
  2857. self.getApplication().InvokeEvent("UpdateEvent")
  2858. return {"success": 1, "id": pid}
  2859. self.getApplication().InvokeEvent("UpdateEvent")
  2860. return {"success": 0, "id": "0"}
  2861. @exportRpc("pv.proxy.manager.list")
  2862. def list(self, viewId=None):
  2863. """Returns the current proxy list, specifying for each proxy it's
  2864. name, id, and parent (input) proxy id. A 'parent' of '0' means
  2865. the proxy has no input.
  2866. """
  2867. proxies = servermanager.ProxyManager().GetProxiesInGroup("sources")
  2868. viewProxy = self.getView(viewId)
  2869. proxyObj = {"view": viewProxy.GetGlobalIDAsString()}
  2870. proxyList = []
  2871. for key in proxies:
  2872. listElt = {}
  2873. listElt["name"] = key[0]
  2874. listElt["id"] = key[1]
  2875. proxy = proxies[key]
  2876. rep = simple.GetRepresentation(proxy=proxy, view=viewProxy)
  2877. listElt["rep"] = rep.GetGlobalIDAsString()
  2878. listElt["visible"] = rep.Visibility
  2879. listElt["parent"] = "0"
  2880. if hasattr(proxy, "Input") and proxy.Input:
  2881. inputProp = proxy.Input
  2882. if hasattr(inputProp, "GetNumberOfProxies"):
  2883. numProxies = inputProp.GetNumberOfProxies()
  2884. if numProxies > 1:
  2885. listElt["multiparent"] = numProxies
  2886. for inputIdx in range(numProxies):
  2887. proxyId = inputProp.GetProxy(inputIdx).GetGlobalIDAsString()
  2888. if inputIdx == 0:
  2889. listElt["parent"] = proxyId
  2890. else:
  2891. listElt["parent_%d" % inputIdx] = proxyId
  2892. elif numProxies == 1:
  2893. listElt["parent"] = inputProp.GetProxy(0).GetGlobalIDAsString()
  2894. else:
  2895. listElt["parent"] = inputProp.GetGlobalIDAsString()
  2896. proxyList.append(listElt)
  2897. proxyObj["sources"] = proxyList
  2898. return proxyObj
  2899. @exportRpc("pv.proxy.manager.available")
  2900. def available(self, typeOfProxy):
  2901. """Returns a list of the available sources or filters, depending on the
  2902. argument typeOfProxy, which can be either 'filters' or 'sources'. If
  2903. typeOfProxy is anything other than 'filter' or 'source', the empty
  2904. list will be returned.
  2905. Any attempt to create a source or filter not returned by this method
  2906. will result in an error response. The returned list has the following
  2907. form:
  2908. [ 'Wavelet', 'Cone', 'Sphere', ... ]
  2909. or
  2910. [ 'Contour', 'Clip', 'Slice', ... ]
  2911. """
  2912. return self.availableList[typeOfProxy]
  2913. # =============================================================================
  2914. #
  2915. # Key/Value Store Protocol
  2916. #
  2917. # =============================================================================
  2918. class ParaViewWebKeyValuePairStore(ParaViewWebProtocol):
  2919. def __init__(self, **kwargs):
  2920. super(ParaViewWebKeyValuePairStore, self).__init__()
  2921. self.keyValStore = {}
  2922. # RpcName: storeKeyPair => 'pv.keyvaluepair.store'
  2923. @exportRpc("pv.keyvaluepair.store")
  2924. def storeKeyPair(self, key, value):
  2925. self.keyValStore[key] = value
  2926. # RpcName: retrieveKeyPair => 'pv.keyvaluepair.retrieve'
  2927. @exportRpc("pv.keyvaluepair.retrieve")
  2928. def retrieveKeyPair(self, key):
  2929. if key in self.keyValStore:
  2930. return self.keyValStore[key]
  2931. else:
  2932. return None
  2933. # =============================================================================
  2934. #
  2935. # Save Data Protocol
  2936. #
  2937. # =============================================================================
  2938. class ParaViewWebSaveData(ParaViewWebProtocol):
  2939. def __init__(self, baseSavePath="", **kwargs):
  2940. super(ParaViewWebSaveData, self).__init__()
  2941. savePath = baseSavePath
  2942. if baseSavePath.find("=") >= 0:
  2943. parts = baseSavePath.split("=")
  2944. savePath = parts[-1]
  2945. self.baseSavePath = savePath
  2946. self.imageExtensions = ["jpg", "png"]
  2947. self.dataExtensions = [
  2948. "vtk",
  2949. "ex2",
  2950. "vtp",
  2951. "vti",
  2952. "vtu",
  2953. "vtm",
  2954. "vts",
  2955. "vtr",
  2956. "csv",
  2957. ]
  2958. self.stateExtensions = ["pvsm"]
  2959. # RpcName: saveData => 'pv.data.save'
  2960. @exportRpc("pv.data.save")
  2961. def saveData(self, filePath, options=None):
  2962. """
  2963. Save some data on the server side. Can save data from a proxy, a
  2964. screenshot, or else the current state. Options may be different
  2965. depending on the type of data to be save. Saving a screenshot
  2966. can take an optional size array indicating the desired width and
  2967. height of the saved image. Saving data can take an optional proxy
  2968. id specifying which filter or source to save output from.
  2969. options = {
  2970. 'proxyId': '426',
  2971. 'size': [ <imgWidth>, <imgHeight> ]
  2972. }
  2973. """
  2974. # make sure file path exists
  2975. fullPath = os.path.join(self.baseSavePath, filePath)
  2976. if not os.path.exists(os.path.dirname(fullPath)):
  2977. os.makedirs(os.path.dirname(fullPath))
  2978. # Get file extension and look for configured reader
  2979. idx = filePath.rfind(".")
  2980. extension = filePath[idx + 1 :]
  2981. # Now find out what kind of save it is, and do it
  2982. if extension in self.imageExtensions:
  2983. if options and "size" in options:
  2984. # Get the active view
  2985. v = self.getView(-1)
  2986. # Keep track of current size
  2987. cw = v.ViewSize[0]
  2988. ch = v.ViewSize[1]
  2989. # Resize to desired screenshot size
  2990. v.ViewSize = [
  2991. int(str(options["size"][0])),
  2992. int(str(options["size"][1])),
  2993. ]
  2994. simple.Render()
  2995. # Save actual screenshot
  2996. simple.SaveScreenshot(fullPath)
  2997. # Put the size back the way we found it
  2998. v.ViewSize = [cw, ch]
  2999. simple.Render()
  3000. else:
  3001. simple.SaveScreenshot(fullPath)
  3002. elif extension in self.dataExtensions:
  3003. proxy = None
  3004. if options and "proxyId" in options:
  3005. proxyId = str(options["proxyId"])
  3006. proxy = self.mapIdToProxy(proxyId)
  3007. simple.SaveData(fullPath, proxy)
  3008. elif extension in self.stateExtensions:
  3009. # simple.SaveState(fullPath) # FIXME: Fixed after 4.3.1
  3010. servermanager.SaveState(fullPath)
  3011. else:
  3012. msg = "ERROR: Unrecognized extension (%s) in relative path: %s" % (
  3013. extension,
  3014. filePath,
  3015. )
  3016. print(msg)
  3017. return {"success": False, "message": msg}
  3018. return {"success": True}
  3019. # =============================================================================
  3020. #
  3021. # Handle remote Connection
  3022. #
  3023. # =============================================================================
  3024. class ParaViewWebRemoteConnection(ParaViewWebProtocol):
  3025. # RpcName: connect => pv.remote.connect
  3026. @exportRpc("pv.remote.connect")
  3027. def connect(self, options):
  3028. """
  3029. Creates a connection to a remote pvserver.
  3030. Expect an option argument which should override any of
  3031. those default properties::
  3032. {
  3033. 'host': 'localhost',
  3034. 'port': 11111,
  3035. 'rs_host': None,
  3036. 'rs_port': 11111
  3037. }
  3038. """
  3039. ds_host = "localhost"
  3040. ds_port = 11111
  3041. rs_host = None
  3042. rs_port = 11111
  3043. if options:
  3044. if "host" in options:
  3045. ds_host = options["host"]
  3046. if "port" in options:
  3047. ds_port = options["port"]
  3048. if "rs_host" in options:
  3049. rs_host = options["rs_host"]
  3050. if "rs_port" in options:
  3051. rs_host = options["rs_port"]
  3052. simple.Connect(ds_host, ds_port, rs_host, rs_port)
  3053. # RpcName: reverseConnect => pv.remote.reverse.connect
  3054. @exportRpc("pv.remote.reverse.connect")
  3055. def reverseConnect(self, port=11111):
  3056. """
  3057. Create a reverse connection to a server. Listens on port and waits for
  3058. an incoming connection from the server.
  3059. """
  3060. simple.ReverseConnect(port)
  3061. # RpcName: pvDisconnect => pv.remote.disconnect
  3062. @exportRpc("pv.remote.disconnect")
  3063. def pvDisconnect(self, message):
  3064. """Free the current active session"""
  3065. simple.Disconnect()
  3066. # =============================================================================
  3067. #
  3068. # Handle remote Connection at startup
  3069. #
  3070. # =============================================================================
  3071. class ParaViewWebStartupRemoteConnection(ParaViewWebProtocol):
  3072. connected = False
  3073. def __init__(
  3074. self, dsHost=None, dsPort=11111, rsHost=None, rsPort=22221, rcPort=-1, **kwargs
  3075. ):
  3076. super(ParaViewWebStartupRemoteConnection, self).__init__()
  3077. if not ParaViewWebStartupRemoteConnection.connected and dsHost:
  3078. ParaViewWebStartupRemoteConnection.connected = True
  3079. simple.Connect(dsHost, dsPort, rsHost, rsPort)
  3080. elif not ParaViewWebStartupRemoteConnection.connected and rcPort >= 0:
  3081. ParaViewWebStartupRemoteConnection.connected = True
  3082. simple.ReverseConnect(str(rcPort))
  3083. # =============================================================================
  3084. #
  3085. # Handle plugin loading at startup
  3086. #
  3087. # =============================================================================
  3088. class ParaViewWebStartupPluginLoader(ParaViewWebProtocol):
  3089. loaded = False
  3090. def __init__(self, plugins=None, pathSeparator=":", **kwargs):
  3091. super(ParaViewWebStartupPluginLoader, self).__init__()
  3092. if not ParaViewWebStartupPluginLoader.loaded and plugins:
  3093. ParaViewWebStartupPluginLoader.loaded = True
  3094. for path in plugins.split(pathSeparator):
  3095. simple.LoadPlugin(path)
  3096. # =============================================================================
  3097. #
  3098. # Handle State Loading
  3099. #
  3100. # =============================================================================
  3101. class ParaViewWebStateLoader(ParaViewWebProtocol):
  3102. def __init__(self, state_path=None, **kwargs):
  3103. super(ParaViewWebStateLoader, self).__init__()
  3104. if state_path and state_path[-5:] == ".pvsm":
  3105. self.loadState(state_path)
  3106. # RpcName: loadState => pv.loader.state
  3107. @exportRpc("pv.loader.state")
  3108. def loadState(self, state_file):
  3109. """
  3110. Load a state file and return the list of view ids
  3111. """
  3112. simple.LoadState(state_file)
  3113. ids = []
  3114. for view in simple.GetRenderViews():
  3115. ids.append(view.GetGlobalIDAsString())
  3116. self.getApplication().InvokeEvent("UpdateEvent")
  3117. return ids
  3118. # =============================================================================
  3119. #
  3120. # Handle Server File Listing
  3121. #
  3122. # =============================================================================
  3123. class ParaViewWebFileListing(ParaViewWebProtocol):
  3124. def __init__(
  3125. self,
  3126. basePath,
  3127. name,
  3128. excludeRegex=r"^\.|~$|^\$",
  3129. groupRegex=r"[0-9]+\.",
  3130. **kwargs
  3131. ):
  3132. """
  3133. Configure the way the WebFile browser will expose the server content.
  3134. - basePath: specify the base directory (or directories) that we should start with, if this
  3135. parameter takes the form: "name1=path1|name2=path2|...", then we will treat this as the
  3136. case where multiple data directories are required. In this case, each top-level directory
  3137. will be given the name associated with the directory in the argument.
  3138. - name: Name of that base directory that will show up on the web
  3139. - excludeRegex: Regular expression of what should be excluded from the list of files/directories
  3140. """
  3141. self.setBaseDirectory(basePath)
  3142. self.rootName = self.overrideDataDirKey or name
  3143. self.pattern = re.compile(excludeRegex)
  3144. self.gPattern = re.compile(groupRegex)
  3145. pxm = simple.servermanager.ProxyManager()
  3146. self.directory_proxy = pxm.NewProxy("misc", "ListDirectory")
  3147. self.fileList = simple.servermanager.VectorProperty(
  3148. self.directory_proxy, self.directory_proxy.GetProperty("FileList")
  3149. )
  3150. self.directoryList = simple.servermanager.VectorProperty(
  3151. self.directory_proxy, self.directory_proxy.GetProperty("DirectoryList")
  3152. )
  3153. def handleSingleRoot(self, baseDirectory, relativeDir, startPath=None):
  3154. path = startPath or [self.rootName]
  3155. if len(relativeDir) > len(self.rootName):
  3156. relativeDir = relativeDir[len(self.rootName) + 1 :]
  3157. path += relativeDir.replace("\\", "/").split("/")
  3158. currentPath = os.path.normpath(os.path.join(baseDirectory, relativeDir))
  3159. normBase = os.path.normpath(baseDirectory)
  3160. if not currentPath.startswith(normBase):
  3161. print("### CAUTION ==========================================")
  3162. print(" Attempt to get to another root path ###")
  3163. print(" => Requested:", relativeDir)
  3164. print(" => BaseDir:", normBase)
  3165. print(" => Computed path:", currentPath)
  3166. print("### CAUTION ==========================================")
  3167. currentPath = normBase
  3168. self.directory_proxy.List(currentPath)
  3169. self.directory_proxy.UpdatePropertyInformation()
  3170. # build file/dir lists
  3171. files = []
  3172. if len(self.fileList) > 1:
  3173. for f in self.fileList.GetData():
  3174. if not re.search(self.pattern, f):
  3175. files.append({"label": f})
  3176. elif len(self.fileList) == 1 and not re.search(
  3177. self.pattern, self.fileList.GetData()
  3178. ):
  3179. files.append({"label": self.fileList.GetData()})
  3180. dirs = []
  3181. if len(self.directoryList) > 1:
  3182. for d in self.directoryList.GetData():
  3183. if not re.search(self.pattern, d):
  3184. dirs.append(d)
  3185. elif len(self.directoryList) == 1 and not re.search(
  3186. self.pattern, self.directoryList.GetData()
  3187. ):
  3188. dirs.append(self.directoryList.GetData())
  3189. result = {
  3190. "label": relativeDir,
  3191. "files": files,
  3192. "dirs": dirs,
  3193. "groups": [],
  3194. "path": path,
  3195. }
  3196. if relativeDir == ".":
  3197. result["label"] = self.rootName
  3198. # Filter files to create groups. Dicts are not orderable in Py3 - supply a key function.
  3199. files.sort(key=lambda x: x["label"])
  3200. groups = result["groups"]
  3201. groupIdx = {}
  3202. filesToRemove = []
  3203. for file in files:
  3204. fileSplit = re.split(self.gPattern, file["label"])
  3205. if len(fileSplit) == 2:
  3206. filesToRemove.append(file)
  3207. gName = "*.".join(fileSplit)
  3208. if gName in groupIdx:
  3209. groupIdx[gName]["files"].append(file["label"])
  3210. else:
  3211. groupIdx[gName] = {"files": [file["label"]], "label": gName}
  3212. groups.append(groupIdx[gName])
  3213. for file in filesToRemove:
  3214. gName = "*.".join(re.split(self.gPattern, file["label"]))
  3215. if len(groupIdx[gName]["files"]) > 1:
  3216. files.remove(file)
  3217. else:
  3218. groups.remove(groupIdx[gName])
  3219. return result
  3220. def handleMultiRoot(self, relativeDir):
  3221. if relativeDir == ".":
  3222. return {
  3223. "label": self.rootName,
  3224. "files": [],
  3225. "dirs": list(self.baseDirectoryMap),
  3226. "groups": [],
  3227. "path": [self.rootName],
  3228. }
  3229. pathList = relativeDir.replace("\\", "/").split("/")
  3230. currentBaseDir = self.baseDirectoryMap[pathList[1]]
  3231. if len(pathList) == 2:
  3232. return self.handleSingleRoot(currentBaseDir, ".", pathList)
  3233. else: # must be greater than 2
  3234. return self.handleSingleRoot(
  3235. currentBaseDir, "/".join([pathList[0]] + pathList[2:]), pathList[0:2]
  3236. )
  3237. # RpcName: listServerDirectory => file.server.directory.list
  3238. @exportRpc("file.server.directory.list")
  3239. def listServerDirectory(self, relativeDir="."):
  3240. """
  3241. RPC Callback to list a server directory relative to the basePath
  3242. provided at start-up.
  3243. """
  3244. if self.multiRoot == True:
  3245. return self.handleMultiRoot(relativeDir)
  3246. else:
  3247. return self.handleSingleRoot(self.baseDirectory, relativeDir)
  3248. # =============================================================================
  3249. #
  3250. # Handle Data Selection
  3251. #
  3252. # =============================================================================
  3253. class ParaViewWebSelectionHandler(ParaViewWebProtocol):
  3254. def __init__(self, **kwargs):
  3255. self.active_view = None
  3256. self.previous_interaction = -1
  3257. self.selection_type = -1
  3258. # RpcName: startSelection => pv.selection.start
  3259. @exportRpc("pv.selection.start")
  3260. def startSelection(self, viewId, selectionType):
  3261. """
  3262. Method used to initialize an interactive selection
  3263. """
  3264. self.active_view = self.getView(viewId)
  3265. if self.active_view.IsSelectionAvailable():
  3266. self.previous_interaction = self.active_view.InteractionMode
  3267. self.active_view.InteractionMode = (
  3268. vtkPVRenderView.INTERACTION_MODE_SELECTION
  3269. )
  3270. else:
  3271. self.active_view = None
  3272. # RpcName: endSelection => pv.selection.end
  3273. @exportRpc("pv.selection.end")
  3274. def endSelection(self, area, extract):
  3275. """
  3276. Method used to finalize an interactive selection by providing
  3277. the [ startPointX, startPointY, endPointX, endPointY ] area
  3278. where (0,0) match the lower left corner of the pixel screen.
  3279. """
  3280. if self.active_view:
  3281. self.active_view.InteractionMode = self.previous_interaction
  3282. representations = vtkCollection()
  3283. sources = vtkCollection()
  3284. if self.selection_type == 0:
  3285. self.active_view.SelectSurfacePoints(
  3286. area, representations, sources, False
  3287. )
  3288. elif self.selection_type == 1:
  3289. self.active_view.SelectSurfaceCells(
  3290. area, representations, sources, False
  3291. )
  3292. elif self.selection_type == 2:
  3293. self.active_view.SelectFrustumPoints(
  3294. area, representations, sources, False
  3295. )
  3296. elif self.selection_type == 3:
  3297. self.active_view.SelectFrustumCells(
  3298. area, representations, sources, False
  3299. )
  3300. else:
  3301. self.active_view.SelectSurfacePoints(
  3302. area, representations, sources, False
  3303. )
  3304. # Don't know what to do if more than one representation/source
  3305. if (
  3306. representations.GetNumberOfItems() == sources.GetNumberOfItems()
  3307. and sources.GetNumberOfItems() == 1
  3308. ):
  3309. # We are good for selection
  3310. rep = servermanager._getPyProxy(representations.GetItemAsObject(0))
  3311. selection = servermanager._getPyProxy(sources.GetItemAsObject(0))
  3312. if extract:
  3313. extract = simple.ExtractSelection(
  3314. Input=rep.Input, Selection=selection
  3315. )
  3316. simple.Show(extract)
  3317. simple.Render()
  3318. self.getApplication().InvokeEvent("UpdateEvent")
  3319. else:
  3320. rep.Input.SMProxy.SetSelectionInput(0, selection.SMProxy, 0)
  3321. # =============================================================================
  3322. #
  3323. # Handle Data Export
  3324. #
  3325. # =============================================================================
  3326. class ParaViewWebExportData(ParaViewWebProtocol):
  3327. def __init__(self, basePath, **kwargs):
  3328. self.base_export_path = basePath
  3329. # RpcName: exportData => pv.export.data
  3330. @exportRpc("pv.export.data")
  3331. def exportData(self, proxy_id, path):
  3332. proxy = self.mapIdToProxy(proxy_id)
  3333. fullpath = str(os.path.join(self.base_export_path, str(path)))
  3334. if fullpath.index(".vtk") == -1:
  3335. fullpath += ".vtk"
  3336. parentDir = os.path.dirname(fullpath)
  3337. if not os.path.exists(parentDir):
  3338. os.makedirs(parentDir)
  3339. if proxy:
  3340. writer = simple.DataSetWriter(Input=proxy, FileName=fullpath)
  3341. writer.UpdatePipeline()
  3342. del writer
  3343. # =============================================================================
  3344. #
  3345. # Protocols useful only for testing purposes
  3346. #
  3347. # =============================================================================
  3348. class ParaViewWebTestProtocols(ParaViewWebProtocol):
  3349. # RpcName: clearAll => pv.test.reset
  3350. @exportRpc("pv.test.reset")
  3351. def clearAll(self):
  3352. simple.Disconnect()
  3353. simple.Connect()
  3354. view = simple.GetRenderView()
  3355. simple.SetActiveView(view)
  3356. simple.Render()
  3357. self.getApplication().InvokeEvent("UpdateEvent")
  3358. # RpcName: getColoringInfo => pv.test.color.info.get
  3359. @exportRpc("pv.test.color.info.get")
  3360. def getColoringInfo(self, proxyId):
  3361. proxy = self.mapIdToProxy(proxyId)
  3362. rep = simple.GetRepresentation(proxy)
  3363. lut = rep.LookupTable
  3364. arrayInfo = rep.GetArrayInformationForColorArray()
  3365. arrayName = arrayInfo.GetName()
  3366. return {
  3367. "arrayName": arrayName,
  3368. "vectorMode": lut.VectorMode,
  3369. "vectorComponent": lut.VectorComponent,
  3370. }
  3371. @exportRpc("pv.test.repr.get")
  3372. def getReprFromSource(self, srcProxyId):
  3373. proxy = self.mapIdToProxy(srcProxyId)
  3374. rep = simple.GetRepresentation(proxy)
  3375. return {"reprProxyId": rep.GetGlobalIDAsString()}
  3376. # =============================================================================
  3377. #
  3378. # Handle Widget Representation
  3379. #
  3380. # =============================================================================
  3381. def _line_update_widget(self, widget):
  3382. widget.Point1WorldPosition = self.Point1
  3383. widget.Point2WorldPosition = self.Point2
  3384. def _line_widget_update(self, obj, event):
  3385. self.GetProperty("Point1").Copy(obj.GetProperty("Point1WorldPositionInfo"))
  3386. self.GetProperty("Point2").Copy(obj.GetProperty("Point2WorldPositionInfo"))
  3387. self.UpdateVTKObjects()
  3388. def _plane_update_widget(self, widget):
  3389. widget.GetProperty("OriginInfo").SetData(self.Origin)
  3390. widget.Origin = self.Origin
  3391. widget.Normal = self.Normal
  3392. widget.UpdateVTKObjects()
  3393. def _plane_widget_update(self, obj, event):
  3394. self.GetProperty("Origin").Copy(obj.GetProperty("OriginInfo"))
  3395. self.GetProperty("Normal").Copy(obj.GetProperty("NormalInfo"))
  3396. self.UpdateVTKObjects()
  3397. _hide_plane(obj)
  3398. def _draw_plane(obj, event):
  3399. obj.GetProperty("DrawPlane").SetElement(0, 1)
  3400. obj.UpdateVTKObjects()
  3401. def _hide_plane(obj):
  3402. obj.GetProperty("DrawPlane").SetElement(0, 0)
  3403. obj.UpdateVTKObjects()
  3404. class ParaViewWebWidgetManager(ParaViewWebProtocol):
  3405. # RpcName: addRuler => pv.widgets.ruler.add
  3406. @exportRpc("pv.widgets.ruler.add")
  3407. def addRuler(self, view_id=-1):
  3408. proxy = simple.Ruler(Point1=[-1.0, -1.0, -1.0], Point2=[1.0, 1.0, 1.0])
  3409. self.createWidgetRepresentation(proxy.GetGlobalID(), view_id)
  3410. return proxy.GetGlobalIDAsString()
  3411. # RpcName: createWidgetRepresentation => pv.widgets.representation.create
  3412. @exportRpc("pv.widgets.representation.create")
  3413. def createWidgetRepresentation(self, proxy_id, view_id):
  3414. proxy = self.mapIdToProxy(proxy_id)
  3415. view = self.getView(view_id)
  3416. widgetProxy = None
  3417. # Find the corresponding widget representation
  3418. if proxy.__class__.__name__ == "Plane":
  3419. widgetProxy = self.CreateWidgetRepresentation(
  3420. view, "ImplicitPlaneWidgetRepresentation"
  3421. )
  3422. setattr(proxy.__class__, "UpdateWidget", _plane_update_widget)
  3423. setattr(proxy.__class__, "WidgetUpdate", _plane_widget_update)
  3424. widgetProxy.GetProperty("DrawPlane").SetElement(0, 0)
  3425. widgetProxy.GetProperty("PlaceFactor").SetElement(0, 1.0)
  3426. proxy.UpdateWidget(widgetProxy)
  3427. widgetProxy.AddObserver("StartInteractionEvent", _draw_plane)
  3428. proxy.Observed = widgetProxy
  3429. proxy.ObserverTag = widgetProxy.AddObserver(
  3430. "EndInteractionEvent", proxy.WidgetUpdate
  3431. )
  3432. elif proxy.__class__.__name__ == "Box":
  3433. widgetProxy = self.CreateWidgetRepresentation(
  3434. view, "BoxWidgetRepresentation"
  3435. )
  3436. elif proxy.__class__.__name__ == "Handle":
  3437. widgetProxy = self.CreateWidgetRepresentation(
  3438. view, "HandleWidgetRepresentation"
  3439. )
  3440. elif proxy.__class__.__name__ == "PointSource":
  3441. widgetProxy = self.CreateWidgetRepresentation(
  3442. view, "PointSourceWidgetRepresentation"
  3443. )
  3444. elif (
  3445. proxy.__class__.__name__ == "LineSource"
  3446. or proxy.__class__.__name__ == "HighResolutionLineSource"
  3447. ):
  3448. widgetProxy = self.CreateWidgetRepresentation(
  3449. view, "LineSourceWidgetRepresentation"
  3450. )
  3451. setattr(proxy.__class__, "UpdateWidget", _line_update_widget)
  3452. setattr(proxy.__class__, "WidgetUpdate", _line_widget_update)
  3453. proxy.UpdateWidget(widgetProxy)
  3454. proxy.Observed = widgetProxy
  3455. proxy.ObserverTag = widgetProxy.AddObserver(
  3456. "EndInteractionEvent", proxy.WidgetUpdate
  3457. )
  3458. elif proxy.__class__.__name__ == "Line":
  3459. widgetProxy = self.CreateWidgetRepresentation(
  3460. view, "LineWidgetRepresentation"
  3461. )
  3462. setattr(proxy.__class__, "UpdateWidget", _line_update_widget)
  3463. setattr(proxy.__class__, "WidgetUpdate", _line_widget_update)
  3464. proxy.UpdateWidget(widgetProxy)
  3465. proxy.Observed = widgetProxy
  3466. proxy.ObserverTag = widgetProxy.AddObserver(
  3467. "EndInteractionEvent", proxy.WidgetUpdate
  3468. )
  3469. elif proxy.__class__.__name__ in ["Distance", "Ruler"]:
  3470. widgetProxy = self.CreateWidgetRepresentation(
  3471. view, "DistanceWidgetRepresentation"
  3472. )
  3473. setattr(proxy.__class__, "UpdateWidget", _line_update_widget)
  3474. setattr(proxy.__class__, "WidgetUpdate", _line_widget_update)
  3475. proxy.UpdateWidget(widgetProxy)
  3476. proxy.Observed = widgetProxy
  3477. proxy.ObserverTag = widgetProxy.AddObserver(
  3478. "EndInteractionEvent", proxy.WidgetUpdate
  3479. )
  3480. elif proxy.__class__.__name__ == "Sphere":
  3481. widgetProxy = self.CreateWidgetRepresentation(
  3482. view, "SphereWidgetRepresentation"
  3483. )
  3484. elif proxy.__class__.__name__ == "Spline":
  3485. widgetProxy = self.CreateWidgetRepresentation(
  3486. view, "SplineWidgetRepresentation"
  3487. )
  3488. else:
  3489. print("No widget representation for %s" % proxy.__class__.__name__)
  3490. return widgetProxy.GetGlobalIDAsString()
  3491. def CreateWidgetRepresentation(self, view, name):
  3492. proxy = simple.servermanager.CreateProxy("representations", name, None)
  3493. pythonWrap = simple.servermanager.rendering.__dict__[proxy.GetXMLName()]()
  3494. pythonWrap.UpdateVTKObjects()
  3495. view.Representations.append(pythonWrap)
  3496. pythonWrap.Visibility = 1
  3497. pythonWrap.Enabled = 1
  3498. return pythonWrap