vtkTkImageViewerWidget.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. """
  2. A vtkTkImageViewerWidget for python, which is based on the
  3. vtkTkImageWindowWidget.
  4. Specify double=1 to get a double-buffered window.
  5. Created by David Gobbi, Nov 1999
  6. """
  7. from __future__ import absolute_import
  8. import math, os, sys
  9. from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline
  10. from vtkmodules.vtkInteractionImage import vtkImageViewer
  11. from vtkmodules.vtkRenderingCore import vtkActor2D, vtkTextMapper
  12. if sys.hexversion < 0x03000000:
  13. # for Python2
  14. import Tkinter as tkinter
  15. else:
  16. # for Python3
  17. import tkinter
  18. from .vtkLoadPythonTkWidgets import vtkLoadPythonTkWidgets
  19. class vtkTkImageViewerWidget(tkinter.Widget):
  20. """
  21. A vtkTkImageViewerWidget for Python.
  22. Use GetImageViewer() to get the vtkImageViewer.
  23. Create with the keyword double=1 in order to generate a
  24. double-buffered viewer.
  25. Create with the keyword focus_on_enter=1 to enable
  26. focus-follows-mouse. The default is for a click-to-focus mode.
  27. """
  28. def __init__(self, master, cnf={}, **kw):
  29. """
  30. Constructor.
  31. Keyword arguments:
  32. iv -- Use passed image viewer instead of creating a new one.
  33. double -- If True, generate a double-buffered viewer.
  34. Defaults to False.
  35. focus_on_enter -- If True, use a focus-follows-mouse mode.
  36. Defaults to False where the widget will use a click-to-focus
  37. mode.
  38. """
  39. # load the necessary extensions into tk
  40. vtkLoadPythonTkWidgets(master.tk)
  41. try: # use specified vtkImageViewer
  42. imageViewer = kw['iv']
  43. except KeyError: # or create one if none specified
  44. imageViewer = vtkImageViewer()
  45. doubleBuffer = 0
  46. try:
  47. if kw['double']:
  48. doubleBuffer = 1
  49. del kw['double']
  50. except:
  51. pass
  52. # check if focus should follow mouse
  53. if kw.get('focus_on_enter'):
  54. self._FocusOnEnter = 1
  55. del kw['focus_on_enter']
  56. else:
  57. self._FocusOnEnter = 0
  58. kw['iv'] = imageViewer.GetAddressAsString("vtkImageViewer")
  59. tkinter.Widget.__init__(self, master, 'vtkTkImageViewerWidget',
  60. cnf, kw)
  61. if doubleBuffer:
  62. imageViewer.GetRenderWindow().DoubleBufferOn()
  63. self.BindTkImageViewer()
  64. def __getattr__(self,attr):
  65. # because the tk part of vtkTkImageViewerWidget must have
  66. # the only remaining reference to the ImageViewer when
  67. # it is destroyed, we can't actually store the ImageViewer
  68. # as an attribute but instead have to get it from the tk-side
  69. if attr == '_ImageViewer':
  70. addr = self.tk.call(self._w, 'GetImageViewer')[5:]
  71. return vtkImageViewer('_%s_vtkImageViewer_p' % addr)
  72. raise AttributeError(self.__class__.__name__ +
  73. " has no attribute named " + attr)
  74. def GetImageViewer(self):
  75. return self._ImageViewer
  76. def Render(self):
  77. self._ImageViewer.Render()
  78. def BindTkImageViewer(self):
  79. imager = self._ImageViewer.GetRenderer()
  80. # stuff for window level text.
  81. mapper = vtkTextMapper()
  82. mapper.SetInput("none")
  83. t_prop = mapper.GetTextProperty()
  84. t_prop.SetFontFamilyToTimes()
  85. t_prop.SetFontSize(18)
  86. t_prop.BoldOn()
  87. t_prop.ShadowOn()
  88. self._LevelMapper = mapper
  89. actor = vtkActor2D()
  90. actor.SetMapper(mapper)
  91. actor.SetLayerNumber(1)
  92. actor.GetPositionCoordinate().SetValue(4,22)
  93. actor.GetProperty().SetColor(1,1,0.5)
  94. actor.SetVisibility(0)
  95. imager.AddActor2D(actor)
  96. self._LevelActor = actor
  97. mapper = vtkTextMapper()
  98. mapper.SetInput("none")
  99. t_prop = mapper.GetTextProperty()
  100. t_prop.SetFontFamilyToTimes()
  101. t_prop.SetFontSize(18)
  102. t_prop.BoldOn()
  103. t_prop.ShadowOn()
  104. self._WindowMapper = mapper
  105. actor = vtkActor2D()
  106. actor.SetMapper(mapper)
  107. actor.SetLayerNumber(1)
  108. actor.GetPositionCoordinate().SetValue(4,4)
  109. actor.GetProperty().SetColor(1,1,0.5)
  110. actor.SetVisibility(0)
  111. imager.AddActor2D(actor)
  112. self._WindowActor = actor
  113. self._LastX = 0
  114. self._LastY = 0
  115. self._OldFocus = 0
  116. self._InExpose = 0
  117. # bindings
  118. # window level
  119. self.bind("<ButtonPress-1>",
  120. lambda e,s=self: s.StartWindowLevelInteraction(e.x,e.y))
  121. self.bind("<B1-Motion>",
  122. lambda e,s=self: s.UpdateWindowLevelInteraction(e.x,e.y))
  123. self.bind("<ButtonRelease-1>",
  124. lambda e,s=self: s.EndWindowLevelInteraction())
  125. # Get the value
  126. self.bind("<ButtonPress-3>",
  127. lambda e,s=self: s.StartQueryInteraction(e.x,e.y))
  128. self.bind("<B3-Motion>",
  129. lambda e,s=self: s.UpdateQueryInteraction(e.x,e.y))
  130. self.bind("<ButtonRelease-3>",
  131. lambda e,s=self: s.EndQueryInteraction())
  132. self.bind("<Expose>",
  133. lambda e,s=self: s.ExposeTkImageViewer())
  134. self.bind("<Enter>",
  135. lambda e,s=self: s.EnterTkViewer())
  136. self.bind("<Leave>",
  137. lambda e,s=self: s.LeaveTkViewer())
  138. self.bind("<KeyPress-e>",
  139. lambda e,s=self: s.quit())
  140. self.bind("<KeyPress-r>",
  141. lambda e,s=self: s.ResetTkImageViewer())
  142. def GetImageViewer(self):
  143. return self._ImageViewer
  144. def Render(self):
  145. self._ImageViewer.Render()
  146. def _GrabFocus(self):
  147. self._OldFocus=self.focus_get()
  148. self.focus()
  149. def EnterTkViewer(self):
  150. if self._FocusOnEnter:
  151. self._GrabFocus()
  152. def LeaveTkViewer(self):
  153. if self._FocusOnEnter and (self._OldFocus != None):
  154. self._OldFocus.focus()
  155. def ExposeTkImageViewer(self):
  156. if (self._InExpose == 0):
  157. self._InExpose = 1
  158. if (not self._ImageViewer.GetRenderWindow().
  159. IsA('vtkCocoaRenderWindow')):
  160. self.update()
  161. self._ImageViewer.Render()
  162. self._InExpose = 0
  163. def StartWindowLevelInteraction(self,x,y):
  164. if not self._FocusOnEnter:
  165. self._GrabFocus()
  166. viewer = self._ImageViewer
  167. self._LastX = x
  168. self._LastY = y
  169. self._Window = float(viewer.GetColorWindow())
  170. self._Level = float(viewer.GetColorLevel())
  171. # make the window level text visible
  172. self._LevelActor.SetVisibility(1)
  173. self._WindowActor.SetVisibility(1)
  174. self.UpdateWindowLevelInteraction(x,y)
  175. def EndWindowLevelInteraction(self):
  176. # make the window level text invisible
  177. self._LevelActor.SetVisibility(0)
  178. self._WindowActor.SetVisibility(0)
  179. self.Render()
  180. def UpdateWindowLevelInteraction(self,x,y):
  181. # compute normalized delta
  182. dx = 4.0*(x - self._LastX)/self.winfo_width()*self._Window
  183. dy = 4.0*(self._LastY - y)/self.winfo_height()*self._Level
  184. # abs so that direction does not flip
  185. if (self._Window < 0.0):
  186. dx = -dx
  187. if (self._Level < 0.0):
  188. dy = -dy
  189. # compute new window level
  190. window = self._Window + dx
  191. if (window < 0.0):
  192. level = self._Level + dy
  193. else:
  194. level = self._Level - dy
  195. viewer = self._ImageViewer
  196. viewer.SetColorWindow(window)
  197. viewer.SetColorLevel(level)
  198. self._WindowMapper.SetInput("Window: %g" % window)
  199. self._LevelMapper.SetInput("Level: %g" % level)
  200. self.Render()
  201. def ResetTkImageViewer(self):
  202. # Reset: Set window level to show all values
  203. viewer = self._ImageViewer
  204. input = viewer.GetInput()
  205. if (input == None):
  206. return
  207. # Get the extent in viewer
  208. z = viewer.GetZSlice()
  209. input.UpdateInformation()
  210. info = input.GetOutputInformation(0)
  211. ext = info.Get(vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT())
  212. ext[4] = z
  213. ext[5] = z
  214. input.Update(0, 1, 0, ext)
  215. (low,high) = input.GetScalarRange()
  216. viewer.SetColorWindow(high - low)
  217. viewer.SetColorLevel((high + low) * 0.5)
  218. self.Render()
  219. def StartQueryInteraction(self,x,y):
  220. if not self._FocusOnEnter:
  221. self._GrabFocus()
  222. # Query PixleValue stuff
  223. self._WindowActor.SetVisibility(1)
  224. self.UpdateQueryInteraction(x,y)
  225. def EndQueryInteraction(self):
  226. self._WindowActor.SetVisibility(0)
  227. self.Render()
  228. def UpdateQueryInteraction(self,x,y):
  229. viewer = self._ImageViewer
  230. input = viewer.GetInput()
  231. z = viewer.GetZSlice()
  232. # y is flipped upside down
  233. y = self.winfo_height() - y
  234. # make sure point is in the extent of the image.
  235. (xMin,xMax,yMin,yMax,zMin,zMax) = input.GetExtent()
  236. if (x < xMin or x > xMax or y < yMin or \
  237. y > yMax or z < zMin or z > zMax):
  238. return
  239. numComps = input.GetNumberOfScalarComponents()
  240. text = ""
  241. for i in xrange(numComps):
  242. val = input.GetScalarComponentAsDouble(x,y,z,i)
  243. text = "%s %.1f" % (text,val)
  244. self._WindowMapper.SetInput("(%d, %d): %s" % (x,y,text))
  245. self.Render()
  246. #-----------------------------------------------------------------------------
  247. # an example of how to use this widget
  248. if __name__ == "__main__":
  249. from vtkmodules.vtkImagingSources import vtkImageCanvasSource2D
  250. # load implementations for rendering and interaction factory classes
  251. import vtkmodules.vtkRenderingOpenGL2
  252. import vtkmodules.vtkInteractionStyle
  253. canvas = vtkImageCanvasSource2D()
  254. canvas.SetNumberOfScalarComponents(3)
  255. canvas.SetScalarType(3)
  256. canvas.SetExtent(0,511,0,511,0,0)
  257. canvas.SetDrawColor(100,100,0)
  258. canvas.FillBox(0,511,0,511)
  259. canvas.SetDrawColor(200,0,200)
  260. canvas.FillBox(32,511,100,500)
  261. canvas.SetDrawColor(100,0,0)
  262. canvas.FillTube(550,20,30,400,5)
  263. canvas.SetDrawColor(255,255,255)
  264. canvas.DrawSegment3D(10,20,0,90,510,0)
  265. canvas.SetDrawColor(200,50,50)
  266. canvas.DrawSegment3D(510,90,0,10,20,0)
  267. # Check segment clipping
  268. canvas.SetDrawColor(0,200,0)
  269. canvas.DrawSegment(-10,30,30,-10)
  270. canvas.DrawSegment(-10,481,30,521)
  271. canvas.DrawSegment(481,-10,521,30)
  272. canvas.DrawSegment(481,521,521,481)
  273. # Check Filling a triangle
  274. canvas.SetDrawColor(20,200,200)
  275. canvas.FillTriangle(-100,100,190,150,40,300)
  276. # Check drawing a circle
  277. canvas.SetDrawColor(250,250,10)
  278. canvas.DrawCircle(350,350,200.0)
  279. # Check drawing a point
  280. canvas.SetDrawColor(250,250,250)
  281. canvas.DrawPoint(350,350)
  282. canvas.DrawPoint(350,550)
  283. # Test filling functionality
  284. canvas.SetDrawColor(55,0,0)
  285. canvas.DrawCircle(450,350,80.0)
  286. canvas.SetDrawColor(100,255,100)
  287. canvas.FillPixel(450,350)
  288. # Create the GUI: two renderer widgets and a quit button
  289. frame = tkinter.Frame()
  290. widget = vtkTkImageViewerWidget(frame,width=512,height=512,double=1)
  291. viewer = widget.GetImageViewer()
  292. viewer.SetInputConnection(canvas.GetOutputPort())
  293. viewer.SetColorWindow(256)
  294. viewer.SetColorLevel(127.5)
  295. button = tkinter.Button(frame,text="Quit",command=frame.quit)
  296. widget.pack(side='top',padx=3,pady=3,fill='both',expand='t')
  297. frame.pack(fill='both',expand='t')
  298. button.pack(fill='x')
  299. frame.mainloop()