123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775 |
- """
- A simple VTK widget for wxPython.
- Find wxPython info at http://wxPython.org
- Created by David Gobbi, December 2001
- Based on vtkTkRenderWindget.py
- Updated to new wx namespace and some cleaning by Andrea Gavana,
- December 2006
- """
- """
- Please see the example at the end of this file.
- ----------------------------------------
- Creation:
- wxVTKRenderWindow(parent, ID, stereo=0, [wx keywords]):
- You should create a wx.App(False) or some other wx.App subclass
- before creating the window.
- ----------------------------------------
- Methods:
- Render()
- AddRenderer(ren)
- GetRenderers()
- GetRenderWindow()
- ----------------------------------------
- Methods to override (all take a wx.Event):
- OnButtonDown(event) default: propagate event to Left, Right, Middle
- OnLeftDown(event) default: set _Mode to 'Rotate'
- OnRightDown(event) default: set _Mode to 'Zoom'
- OnMiddleDown(event) default: set _Mode to 'Pan'
- OnButtonUp(event) default: propagate event to L, R, M and unset _Mode
- OnLeftUp(event)
- OnRightUp(event)
- OnMiddleUp(event)
- OnMotion(event) default: call appropriate handler for _Mode
- OnEnterWindow(event) default: set focus to this window
- OnLeaveWindow(event) default: release focus
- OnKeyDown(event) default: [R]eset, [W]irefreme, [S]olid, [P]ick
- OnKeyUp(event)
- OnChar(event)
- OnSetFocus(event)
- OnKillFocus(event)
- OnSize(event)
- OnMove(event)
- OnPaint(event) default: Render()
- ----------------------------------------
- Protected Members:
- _Mode: Current mode: 'Rotate', 'Zoom', 'Pan'
- _LastX, _LastY: The (x,y) coordinates of the previous event
- _CurrentRenderer: The renderer that was most recently clicked in
- _CurrentCamera: The camera for the current renderer
- ----------------------------------------
- Private Members:
- __Handle: Handle to the window containing the vtkRenderWindow
- """
- # import usual libraries
- import math, os, sys
- import wx
- from vtkmodules.vtkRenderingCore import vtkCellPicker, vtkProperty, vtkRenderWindow
- # a few configuration items, see what works best on your system
- # Use GLCanvas as base class instead of wx.Window.
- # This is sometimes necessary under wxGTK or the image is blank.
- # (in wxWindows 2.3.1 and earlier, the GLCanvas had scroll bars)
- baseClass = wx.Window
- if wx.Platform == "__WXGTK__":
- import wx.glcanvas
- baseClass = wx.glcanvas.GLCanvas
- # Keep capturing mouse after mouse is dragged out of window
- # (in wxGTK 2.3.2 there is a bug that keeps this from working,
- # but it is only relevant in wxGTK if there are multiple windows)
- _useCapture = (wx.Platform == "__WXMSW__")
- # end of configuration items
- class wxVTKRenderWindow(baseClass):
- """
- A wxRenderWindow for wxPython.
- Use GetRenderWindow() to get the vtkRenderWindow.
- Create with the keyword stereo=1 in order to
- generate a stereo-capable window.
- """
- def __init__(self, parent, ID, *args, **kw):
- """Default class constructor.
- @param parent: parent window
- @param ID: window id
- @param **kw: wxPython keywords (position, size, style) plus the
- 'stereo' keyword
- """
- # miscellaneous protected variables
- self._CurrentRenderer = None
- self._CurrentCamera = None
- self._CurrentZoom = 1.0
- self._CurrentLight = None
- self._ViewportCenterX = 0
- self._ViewportCenterY = 0
- self._Picker = vtkCellPicker()
- self._PickedActor = None
- self._PickedProperty = vtkProperty()
- self._PickedProperty.SetColor(1,0,0)
- self._PrePickedProperty = None
- # these record the previous mouse position
- self._LastX = 0
- self._LastY = 0
- # the current interaction mode (Rotate, Pan, Zoom, etc)
- self._Mode = None
- self._ActiveButton = None
- # private attributes
- self.__OldFocus = None
- # used by the LOD actors
- self._DesiredUpdateRate = 15
- self._StillUpdateRate = 0.0001
- # First do special handling of some keywords:
- # stereo, position, size, width, height, style
- try:
- stereo = bool(kw['stereo'])
- del kw['stereo']
- except KeyError:
- stereo = False
- try:
- position = kw['position']
- del kw['position']
- except KeyError:
- position = wx.DefaultPosition
- try:
- size = kw['size']
- del kw['size']
- except KeyError:
- try:
- size = parent.GetSize()
- except AttributeError:
- size = wx.DefaultSize
- # wx.WANTS_CHARS says to give us e.g. TAB
- # wx.NO_FULL_REPAINT_ON_RESIZE cuts down resize flicker under GTK
- style = wx.WANTS_CHARS | wx.NO_FULL_REPAINT_ON_RESIZE
- try:
- style = style | kw['style']
- del kw['style']
- except KeyError:
- pass
- # the enclosing frame must be shown under GTK or the windows
- # don't connect together properly
- l = []
- p = parent
- while p: # make a list of all parents
- l.append(p)
- p = p.GetParent()
- l.reverse() # sort list into descending order
- for p in l:
- p.Show(1)
- # initialize the wx.Window
- if baseClass.__name__ == 'GLCanvas':
- # Set the doublebuffer attribute of the GL canvas.
- baseClass.__init__(self, parent, ID, pos=position, size=size,
- style=style,
- attribList=[wx.glcanvas.WX_GL_DOUBLEBUFFER])
- else:
- baseClass.__init__(self, parent, ID, pos=position, size=size,
- style=style)
- # create the RenderWindow and initialize it
- self._RenderWindow = vtkRenderWindow()
- self._RenderWindow.SetSize(size.width, size.height)
- if stereo:
- self._RenderWindow.StereoCapableWindowOn()
- self._RenderWindow.SetStereoTypeToCrystalEyes()
- self.__handle = None
- # refresh window by doing a Render
- self.Bind(wx.EVT_PAINT, self.OnPaint)
- # turn off background erase to reduce flicker
- self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
- # Bind the events to the event converters
- self.Bind(wx.EVT_RIGHT_DOWN, self._OnButtonDown)
- self.Bind(wx.EVT_LEFT_DOWN, self._OnButtonDown)
- self.Bind(wx.EVT_MIDDLE_DOWN, self._OnButtonDown)
- self.Bind(wx.EVT_RIGHT_UP, self._OnButtonUp)
- self.Bind(wx.EVT_LEFT_UP, self._OnButtonUp)
- self.Bind(wx.EVT_MIDDLE_UP, self._OnButtonUp)
- self.Bind(wx.EVT_MOTION, self.OnMotion)
- self.Bind(wx.EVT_ENTER_WINDOW, self._OnEnterWindow)
- self.Bind(wx.EVT_LEAVE_WINDOW, self._OnLeaveWindow)
- self.Bind(wx.EVT_CHAR, self.OnChar)
- # If we use EVT_KEY_DOWN instead of EVT_CHAR, capital versions
- # of all characters are always returned. EVT_CHAR also performs
- # other necessary keyboard-dependent translations.
- self.Bind(wx.EVT_CHAR, self.OnKeyDown)
- self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
- self.Bind(wx.EVT_SIZE, self._OnSize)
- self.Bind(wx.EVT_MOVE, self.OnMove)
- self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
- self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
- def SetDesiredUpdateRate(self, rate):
- """Mirrors the method with the same name in
- vtkRenderWindowInteractor.
- """
- self._DesiredUpdateRate = rate
- def GetDesiredUpdateRate(self):
- """Mirrors the method with the same name in
- vtkRenderWindowInteractor.
- """
- return self._DesiredUpdateRate
- def SetStillUpdateRate(self, rate):
- """Mirrors the method with the same name in
- vtkRenderWindowInteractor.
- """
- self._StillUpdateRate = rate
- def GetStillUpdateRate(self):
- """Mirrors the method with the same name in
- vtkRenderWindowInteractor.
- """
- return self._StillUpdateRate
- def OnPaint(self, event):
- """Handles the wx.EVT_PAINT event for wxVTKRenderWindow.
- """
- dc = wx.PaintDC(self)
- self.Render()
- def _OnSize(self, event):
- """Handles the wx.EVT_SIZE event for wxVTKRenderWindow.
- """
- if wx.Platform != '__WXMSW__':
- width, height = event.GetSize()
- self._RenderWindow.SetSize(width, height)
- self.OnSize(event)
- self.Render()
- def OnSize(self, event):
- """Overridable event.
- """
- pass
- def OnMove(self, event):
- """Overridable event.
- """
- pass
- def _OnEnterWindow(self, event):
- """Handles the wx.EVT_ENTER_WINDOW event for
- wxVTKRenderWindow.
- """
- self.UpdateRenderer(event)
- self.OnEnterWindow(event)
- def OnEnterWindow(self, event):
- """Overridable event.
- """
- if self.__OldFocus == None:
- self.__OldFocus = wx.Window.FindFocus()
- self.SetFocus()
- def _OnLeaveWindow(self, event):
- """Handles the wx.EVT_LEAVE_WINDOW event for
- wxVTKRenderWindow.
- """
- self.OnLeaveWindow(event)
- def OnLeaveWindow(self, event):
- """Overridable event.
- """
- if self.__OldFocus:
- self.__OldFocus.SetFocus()
- self.__OldFocus = None
- def OnSetFocus(self, event):
- """Overridable event.
- """
- pass
- def OnKillFocus(self, event):
- """Overridable event.
- """
- pass
- def _OnButtonDown(self, event):
- """Handles the wx.EVT_LEFT/RIGHT/MIDDLE_DOWN events for
- wxVTKRenderWindow.
- """
- # helper function for capturing mouse until button released
- self._RenderWindow.SetDesiredUpdateRate(self._DesiredUpdateRate)
- if event.RightDown():
- button = "Right"
- elif event.LeftDown():
- button = "Left"
- elif event.MiddleDown():
- button = "Middle"
- else:
- button = None
- # save the button and capture mouse until the button is released
- if button and not self._ActiveButton:
- self._ActiveButton = button
- if _useCapture:
- self.CaptureMouse()
- self.OnButtonDown(event)
- def OnButtonDown(self, event):
- """Overridable event.
- """
- if not self._Mode:
- # figure out what renderer the mouse is over
- self.UpdateRenderer(event)
- if event.LeftDown():
- self.OnLeftDown(event)
- elif event.RightDown():
- self.OnRightDown(event)
- elif event.MiddleDown():
- self.OnMiddleDown(event)
- def OnLeftDown(self, event):
- """Overridable event.
- """
- if not self._Mode:
- if event.ControlDown():
- self._Mode = "Zoom"
- elif event.ShiftDown():
- self._Mode = "Pan"
- else:
- self._Mode = "Rotate"
- def OnRightDown(self, event):
- """Overridable event.
- """
- if not self._Mode:
- self._Mode = "Zoom"
- def OnMiddleDown(self, event):
- """Overridable event.
- """
- if not self._Mode:
- self._Mode = "Pan"
- def _OnButtonUp(self, event):
- """Handles the wx.EVT_LEFT/RIGHT/MIDDLE_UP events for
- wxVTKRenderWindow.
- """
- # helper function for releasing mouse capture
- self._RenderWindow.SetDesiredUpdateRate(self._StillUpdateRate)
- if event.RightUp():
- button = "Right"
- elif event.LeftUp():
- button = "Left"
- elif event.MiddleUp():
- button = "Middle"
- else:
- button = None
- # if the ActiveButton is released, then release mouse capture
- if self._ActiveButton and button == self._ActiveButton:
- if _useCapture:
- self.ReleaseMouse()
- self._ActiveButton = None
- self.OnButtonUp(event)
- def OnButtonUp(self, event):
- """Overridable event.
- """
- if event.LeftUp():
- self.OnLeftUp(event)
- elif event.RightUp():
- self.OnRightUp(event)
- elif event.MiddleUp():
- self.OnMiddleUp(event)
- # if not interacting, then do nothing more
- if self._Mode:
- if self._CurrentRenderer:
- self.Render()
- self._Mode = None
- def OnLeftUp(self, event):
- """Overridable event.
- """
- pass
- def OnRightUp(self, event):
- """Overridable event.
- """
- pass
- def OnMiddleUp(self, event):
- """Overridable event.
- """
- pass
- def OnMotion(self, event):
- """Overridable event.
- """
- if self._Mode == "Pan":
- self.Pan(event)
- elif self._Mode == "Rotate":
- self.Rotate(event)
- elif self._Mode == "Zoom":
- self.Zoom(event)
- def OnChar(self, event):
- """Overridable event.
- """
- pass
- def OnKeyDown(self, event):
- """Handles the wx.EVT_KEY_DOWN events for wxVTKRenderWindow.
- """
- if event.GetKeyCode() == ord('r'):
- self.Reset(event)
- if event.GetKeyCode() == ord('w'):
- self.Wireframe()
- if event.GetKeyCode() == ord('s'):
- self.Surface()
- if event.GetKeyCode() == ord('p'):
- self.PickActor(event)
- if event.GetKeyCode() < 256:
- self.OnChar(event)
- def OnKeyUp(self, event):
- """Overridable event.
- """
- pass
- def GetZoomFactor(self):
- """Returns the current zoom factor.
- """
- return self._CurrentZoom
- def GetRenderWindow(self):
- """Returns the render window (vtkRenderWindow).
- """
- return self._RenderWindow
- def GetPicker(self):
- """Returns the current picker (vtkCellPicker).
- """
- return self._Picker
- def Render(self):
- """Actually renders the VTK scene on screen.
- """
- if self._CurrentLight:
- light = self._CurrentLight
- light.SetPosition(self._CurrentCamera.GetPosition())
- light.SetFocalPoint(self._CurrentCamera.GetFocalPoint())
- if not self.GetUpdateRegion().IsEmpty() or self.__handle:
- if self.__handle and self.__handle == self.GetHandle():
- self._RenderWindow.Render()
- elif self.GetHandle():
- # this means the user has reparented us
- # let's adapt to the new situation by doing the WindowRemap
- # dance
- self._RenderWindow.SetNextWindowInfo(str(self.GetHandle()))
- self._RenderWindow.WindowRemap()
- # store the new situation
- self.__handle = self.GetHandle()
- self._RenderWindow.Render()
- def UpdateRenderer(self, event):
- """
- UpdateRenderer will identify the renderer under the mouse and set
- up _CurrentRenderer, _CurrentCamera, and _CurrentLight.
- """
- x = event.GetX()
- y = event.GetY()
- windowX, windowY = self._RenderWindow.GetSize()
- renderers = self._RenderWindow.GetRenderers()
- numRenderers = renderers.GetNumberOfItems()
- self._CurrentRenderer = None
- renderers.InitTraversal()
- for i in range(0,numRenderers):
- renderer = renderers.GetNextItem()
- vx,vy = (0,0)
- if (windowX > 1):
- vx = float(x)/(windowX-1)
- if (windowY > 1):
- vy = (windowY-float(y)-1)/(windowY-1)
- (vpxmin,vpymin,vpxmax,vpymax) = renderer.GetViewport()
- if (vx >= vpxmin and vx <= vpxmax and
- vy >= vpymin and vy <= vpymax):
- self._CurrentRenderer = renderer
- self._ViewportCenterX = float(windowX)*(vpxmax-vpxmin)/2.0\
- +vpxmin
- self._ViewportCenterY = float(windowY)*(vpymax-vpymin)/2.0\
- +vpymin
- self._CurrentCamera = self._CurrentRenderer.GetActiveCamera()
- lights = self._CurrentRenderer.GetLights()
- lights.InitTraversal()
- self._CurrentLight = lights.GetNextItem()
- break
- self._LastX = x
- self._LastY = y
- def GetCurrentRenderer(self):
- """Returns the current renderer.
- """
- return self._CurrentRenderer
- def Rotate(self, event):
- """Rotates the scene (camera).
- """
- if self._CurrentRenderer:
- x = event.GetX()
- y = event.GetY()
- self._CurrentCamera.Azimuth(self._LastX - x)
- self._CurrentCamera.Elevation(y - self._LastY)
- self._CurrentCamera.OrthogonalizeViewUp()
- self._LastX = x
- self._LastY = y
- self._CurrentRenderer.ResetCameraClippingRange()
- self.Render()
- def Pan(self, event):
- """Pans the scene (camera).
- """
- if self._CurrentRenderer:
- x = event.GetX()
- y = event.GetY()
- renderer = self._CurrentRenderer
- camera = self._CurrentCamera
- (pPoint0,pPoint1,pPoint2) = camera.GetPosition()
- (fPoint0,fPoint1,fPoint2) = camera.GetFocalPoint()
- if camera.GetParallelProjection():
- renderer.SetWorldPoint(fPoint0,fPoint1,fPoint2,1.0)
- renderer.WorldToDisplay()
- fx,fy,fz = renderer.GetDisplayPoint()
- renderer.SetDisplayPoint(fx-x+self._LastX,
- fy+y-self._LastY,
- fz)
- renderer.DisplayToWorld()
- fx,fy,fz,fw = renderer.GetWorldPoint()
- camera.SetFocalPoint(fx,fy,fz)
- renderer.SetWorldPoint(pPoint0,pPoint1,pPoint2,1.0)
- renderer.WorldToDisplay()
- fx,fy,fz = renderer.GetDisplayPoint()
- renderer.SetDisplayPoint(fx-x+self._LastX,
- fy+y-self._LastY,
- fz)
- renderer.DisplayToWorld()
- fx,fy,fz,fw = renderer.GetWorldPoint()
- camera.SetPosition(fx,fy,fz)
- else:
- (fPoint0,fPoint1,fPoint2) = camera.GetFocalPoint()
- # Specify a point location in world coordinates
- renderer.SetWorldPoint(fPoint0,fPoint1,fPoint2,1.0)
- renderer.WorldToDisplay()
- # Convert world point coordinates to display coordinates
- dPoint = renderer.GetDisplayPoint()
- focalDepth = dPoint[2]
- aPoint0 = self._ViewportCenterX + (x - self._LastX)
- aPoint1 = self._ViewportCenterY - (y - self._LastY)
- renderer.SetDisplayPoint(aPoint0,aPoint1,focalDepth)
- renderer.DisplayToWorld()
- (rPoint0,rPoint1,rPoint2,rPoint3) = renderer.GetWorldPoint()
- if (rPoint3 != 0.0):
- rPoint0 = rPoint0/rPoint3
- rPoint1 = rPoint1/rPoint3
- rPoint2 = rPoint2/rPoint3
- camera.SetFocalPoint((fPoint0 - rPoint0) + fPoint0,
- (fPoint1 - rPoint1) + fPoint1,
- (fPoint2 - rPoint2) + fPoint2)
- camera.SetPosition((fPoint0 - rPoint0) + pPoint0,
- (fPoint1 - rPoint1) + pPoint1,
- (fPoint2 - rPoint2) + pPoint2)
- self._LastX = x
- self._LastY = y
- self.Render()
- def Zoom(self, event):
- """Zooms the scene (camera).
- """
- if self._CurrentRenderer:
- x = event.GetX()
- y = event.GetY()
- renderer = self._CurrentRenderer
- camera = self._CurrentCamera
- zoomFactor = math.pow(1.02,(0.5*(self._LastY - y)))
- self._CurrentZoom = self._CurrentZoom * zoomFactor
- if camera.GetParallelProjection():
- parallelScale = camera.GetParallelScale()/zoomFactor
- camera.SetParallelScale(parallelScale)
- else:
- camera.Dolly(zoomFactor)
- renderer.ResetCameraClippingRange()
- self._LastX = x
- self._LastY = y
- self.Render()
- def Reset(self, event=None):
- """Resets the camera.
- """
- if self._CurrentRenderer:
- self._CurrentRenderer.ResetCamera()
- self.Render()
- def Wireframe(self):
- """Sets the current actor representation as wireframe.
- """
- actors = self._CurrentRenderer.GetActors()
- numActors = actors.GetNumberOfItems()
- actors.InitTraversal()
- for i in range(0,numActors):
- actor = actors.GetNextItem()
- actor.GetProperty().SetRepresentationToWireframe()
- self.Render()
- def Surface(self):
- """Sets the current actor representation as surface.
- """
- actors = self._CurrentRenderer.GetActors()
- numActors = actors.GetNumberOfItems()
- actors.InitTraversal()
- for i in range(0,numActors):
- actor = actors.GetNextItem()
- actor.GetProperty().SetRepresentationToSurface()
- self.Render()
- def PickActor(self, event):
- """Picks an actor.
- """
- if self._CurrentRenderer:
- x = event.GetX()
- y = event.GetY()
- renderer = self._CurrentRenderer
- picker = self._Picker
- windowX, windowY = self._RenderWindow.GetSize()
- picker.Pick(x,(windowY - y - 1),0.0,renderer)
- actor = picker.GetActor()
- if (self._PickedActor != None and
- self._PrePickedProperty != None):
- self._PickedActor.SetProperty(self._PrePickedProperty)
- # release hold of the property
- self._PrePickedProperty.UnRegister(self._PrePickedProperty)
- self._PrePickedProperty = None
- if (actor != None):
- self._PickedActor = actor
- self._PrePickedProperty = self._PickedActor.GetProperty()
- # hold onto the property
- self._PrePickedProperty.Register(self._PrePickedProperty)
- self._PickedActor.SetProperty(self._PickedProperty)
- self.Render()
- #----------------------------------------------------------------------------
- def wxVTKRenderWindowConeExample():
- """Like it says, just a simple example.
- """
- from vtkmodules.vtkFiltersSources import vtkConeSource
- from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
- # load implementations for rendering and interaction factory classes
- import vtkmodules.vtkRenderingOpenGL2
- import vtkmodules.vtkInteractionStyle
- # every wx app needs an app
- app = wx.App(False)
- # create the widget
- frame = wx.Frame(None, -1, "wxVTKRenderWindow", size=(400,400))
- widget = wxVTKRenderWindow(frame, -1)
- ren = vtkRenderer()
- widget.GetRenderWindow().AddRenderer(ren)
- cone = vtkConeSource()
- cone.SetResolution(8)
- coneMapper = vtkPolyDataMapper()
- coneMapper.SetInputConnection(cone.GetOutputPort())
- coneActor = vtkActor()
- coneActor.SetMapper(coneMapper)
- ren.AddActor(coneActor)
- # show the window
- frame.Show()
- app.MainLoop()
- if __name__ == "__main__":
- wxVTKRenderWindowConeExample()
|