1"""
2
3A VTK RenderWindowInteractor widget for wxPython.
4
5Find wxPython info at http://wxPython.org
6
7Created by Prabhu Ramachandran, April 2002
8Based on wxVTKRenderWindow.py
9
10Fixes and updates by Charl P. Botha 2003-2008
11
12Updated to new wx namespace and some cleaning up by Andrea Gavana,
13December 2006
14"""
15
16"""
17Please see the example at the end of this file.
18
19----------------------------------------
20Creation:
21
22 wxVTKRenderWindowInteractor(parent, ID, stereo=0, [wx keywords]):
23
24 You should create a wx.App(False) or some other wx.App subclass
25 before creating the window.
26
27Behaviour:
28
29 Uses __getattr__ to make the wxVTKRenderWindowInteractor behave just
30 like a vtkGenericRenderWindowInteractor.
31
32----------------------------------------
33
34"""
35
36# import usual libraries
37import math, os, sys
38import wx
39from vtkmodules.vtkRenderingCore import vtkRenderWindow
40from vtkmodules.vtkRenderingUI import vtkGenericRenderWindowInteractor
41
42# a few configuration items, see what works best on your system
43
44# Use GLCanvas as base class instead of wx.Window.
45# This is sometimes necessary under wxGTK or the image is blank.
46# (in wxWindows 2.3.1 and earlier, the GLCanvas had scroll bars)
47baseClass = wx.Window
48if wx.Platform == "__WXGTK__":
49    import wx.glcanvas
50    baseClass = wx.glcanvas.GLCanvas
51
52# Keep capturing mouse after mouse is dragged out of window
53# (in wxGTK 2.3.2 there is a bug that keeps this from working,
54# but it is only relevant in wxGTK if there are multiple windows)
55_useCapture = (wx.Platform == "__WXMSW__")
56
57# end of configuration items
58
59
60class EventTimer(wx.Timer):
61    """Simple wx.Timer class.
62    """
63
64    def __init__(self, iren):
65        """Default class constructor.
66        @param iren: current render window
67        """
68        wx.Timer.__init__(self)
69        self.iren = iren
70
71
72    def Notify(self):
73        """ The timer has expired.
74        """
75        self.iren.TimerEvent()
76
77
78class wxVTKRenderWindowInteractor(baseClass):
79    """
80    A wxRenderWindow for wxPython.
81    Use GetRenderWindow() to get the vtkRenderWindow.
82    Create with the keyword stereo=1 in order to
83    generate a stereo-capable window.
84    """
85
86    # class variable that can also be used to request instances that use
87    # stereo; this is overridden by the stereo=1/0 parameter.  If you set
88    # it to True, the NEXT instantiated object will attempt to allocate a
89    # stereo visual.  E.g.:
90    # wxVTKRenderWindowInteractor.USE_STEREO = True
91    # myRWI = wxVTKRenderWindowInteractor(parent, -1)
92    USE_STEREO = False
93
94    def __init__(self, parent, ID, *args, **kw):
95        """Default class constructor.
96        @param parent: parent window
97        @param ID: window id
98        @param **kw: wxPython keywords (position, size, style) plus the
99        'stereo' keyword
100        """
101        # private attributes
102        self.__RenderWhenDisabled = 0
103
104        # First do special handling of some keywords:
105        # stereo, position, size, width, height, style
106
107        try:
108            stereo = bool(kw['stereo'])
109            del kw['stereo']
110        except KeyError:
111            stereo = False
112
113        try:
114            position = kw['position']
115            del kw['position']
116        except KeyError:
117            position = wx.DefaultPosition
118
119        try:
120            size = kw['size']
121            del kw['size']
122        except KeyError:
123            try:
124                size = parent.GetSize()
125            except AttributeError:
126                size = wx.DefaultSize
127
128        # wx.WANTS_CHARS says to give us e.g. TAB
129        # wx.NO_FULL_REPAINT_ON_RESIZE cuts down resize flicker under GTK
130        style = wx.WANTS_CHARS | wx.NO_FULL_REPAINT_ON_RESIZE
131
132        try:
133            style = style | kw['style']
134            del kw['style']
135        except KeyError:
136            pass
137
138        # the enclosing frame must be shown under GTK or the windows
139        #  don't connect together properly
140        if wx.Platform != '__WXMSW__':
141            l = []
142            p = parent
143            while p: # make a list of all parents
144                l.append(p)
145                p = p.GetParent()
146            l.reverse() # sort list into descending order
147            for p in l:
148                p.Show(1)
149
150        if baseClass.__name__ == 'GLCanvas':
151            # code added by cpbotha to enable stereo and double
152            # buffering correctly where the user requests this; remember
153            # that the glXContext in this case is NOT allocated by VTK,
154            # but by WX, hence all of this.
155
156            # Initialize GLCanvas with correct attriblist
157            attribList = [wx.glcanvas.WX_GL_RGBA,
158                          wx.glcanvas.WX_GL_MIN_RED, 1,
159                          wx.glcanvas.WX_GL_MIN_GREEN, 1,
160                          wx.glcanvas.WX_GL_MIN_BLUE, 1,
161                          wx.glcanvas.WX_GL_DEPTH_SIZE, 16,
162                          wx.glcanvas.WX_GL_DOUBLEBUFFER]
163            if stereo:
164                attribList.append(wx.glcanvas.WX_GL_STEREO)
165
166            try:
167                baseClass.__init__(self, parent, ID, pos=position, size=size,
168                                   style=style,
169                                   attribList=attribList)
170            except wx.PyAssertionError:
171                # visual couldn't be allocated, so we go back to default
172                baseClass.__init__(self, parent, ID, pos=position, size=size,
173                                   style=style)
174                if stereo:
175                    # and make sure everyone knows that the stereo
176                    # visual wasn't set.
177                    stereo = 0
178
179        else:
180            baseClass.__init__(self, parent, ID, pos=position, size=size,
181                               style=style)
182
183        # create the RenderWindow and initialize it
184        self._Iren = vtkGenericRenderWindowInteractor()
185        self._Iren.SetRenderWindow( vtkRenderWindow() )
186        self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
187        self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)
188        self._Iren.GetRenderWindow().AddObserver('CursorChangedEvent',
189                                                 self.CursorChangedEvent)
190
191        try:
192            self._Iren.GetRenderWindow().SetSize(size.width, size.height)
193        except AttributeError:
194            self._Iren.GetRenderWindow().SetSize(size[0], size[1])
195
196        if stereo:
197            self._Iren.GetRenderWindow().StereoCapableWindowOn()
198            self._Iren.GetRenderWindow().SetStereoTypeToCrystalEyes()
199
200        self.__handle = None
201
202        self.BindEvents()
203
204        # with this, we can make sure that the reparenting logic in
205        # Render() isn't called before the first OnPaint() has
206        # successfully been run (and set up the VTK/WX display links)
207        self.__has_painted = False
208
209        # set when we have captured the mouse.
210        self._own_mouse = False
211        # used to store WHICH mouse button led to mouse capture
212        self._mouse_capture_button = 0
213
214        # A mapping for cursor changes.
215        self._cursor_map = {0: wx.CURSOR_ARROW, # VTK_CURSOR_DEFAULT
216                            1: wx.CURSOR_ARROW, # VTK_CURSOR_ARROW
217                            2: wx.CURSOR_SIZENESW, # VTK_CURSOR_SIZENE
218                            3: wx.CURSOR_SIZENWSE, # VTK_CURSOR_SIZENWSE
219                            4: wx.CURSOR_SIZENESW, # VTK_CURSOR_SIZESW
220                            5: wx.CURSOR_SIZENWSE, # VTK_CURSOR_SIZESE
221                            6: wx.CURSOR_SIZENS, # VTK_CURSOR_SIZENS
222                            7: wx.CURSOR_SIZEWE, # VTK_CURSOR_SIZEWE
223                            8: wx.CURSOR_SIZING, # VTK_CURSOR_SIZEALL
224                            9: wx.CURSOR_HAND, # VTK_CURSOR_HAND
225                            10: wx.CURSOR_CROSS, # VTK_CURSOR_CROSSHAIR
226                           }
227
228    def BindEvents(self):
229        """Binds all the necessary events for navigation, sizing,
230        drawing.
231        """
232        # refresh window by doing a Render
233        self.Bind(wx.EVT_PAINT, self.OnPaint)
234        # turn off background erase to reduce flicker
235        self.Bind(wx.EVT_ERASE_BACKGROUND, lambda e: None)
236
237        # Bind the events to the event converters
238        self.Bind(wx.EVT_RIGHT_DOWN, self.OnButtonDown)
239        self.Bind(wx.EVT_LEFT_DOWN, self.OnButtonDown)
240        self.Bind(wx.EVT_MIDDLE_DOWN, self.OnButtonDown)
241        self.Bind(wx.EVT_RIGHT_UP, self.OnButtonUp)
242        self.Bind(wx.EVT_LEFT_UP, self.OnButtonUp)
243        self.Bind(wx.EVT_MIDDLE_UP, self.OnButtonUp)
244        self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
245        self.Bind(wx.EVT_MOTION, self.OnMotion)
246
247        self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
248        self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
249
250        # If we use EVT_KEY_DOWN instead of EVT_CHAR, capital versions
251        # of all characters are always returned.  EVT_CHAR also performs
252        # other necessary keyboard-dependent translations.
253        self.Bind(wx.EVT_CHAR, self.OnKeyDown)
254        self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
255
256        self.Bind(wx.EVT_SIZE, self.OnSize)
257
258        # the wx 2.8.7.1 documentation states that you HAVE to handle
259        # this event if you make use of CaptureMouse, which we do.
260        if _useCapture and hasattr(wx, 'EVT_MOUSE_CAPTURE_LOST'):
261            self.Bind(wx.EVT_MOUSE_CAPTURE_LOST,
262                    self.OnMouseCaptureLost)
263
264
265    def __getattr__(self, attr):
266        """Makes the object behave like a
267        vtkGenericRenderWindowInteractor.
268        """
269        if attr == '__vtk__':
270            return lambda t=self._Iren: t
271        elif hasattr(self._Iren, attr):
272            return getattr(self._Iren, attr)
273        else:
274            raise AttributeError(self.__class__.__name__ +
275                  " has no attribute named " + attr)
276
277    def CreateTimer(self, obj, evt):
278        """ Creates a timer.
279        """
280        self._timer = EventTimer(self)
281        self._timer.Start(10, True)
282
283    def DestroyTimer(self, obj, evt):
284        """The timer is a one shot timer so will expire automatically.
285        """
286        return 1
287
288    def _CursorChangedEvent(self, obj, evt):
289        """Change the wx cursor if the renderwindow's cursor was
290        changed.
291        """
292        cur = self._cursor_map[obj.GetCurrentCursor()]
293        c = wx.StockCursor(cur)
294        self.SetCursor(c)
295
296    def CursorChangedEvent(self, obj, evt):
297        """Called when the CursorChangedEvent fires on the render
298        window."""
299        # This indirection is needed since when the event fires, the
300        # current cursor is not yet set so we defer this by which time
301        # the current cursor should have been set.
302        wx.CallAfter(self._CursorChangedEvent, obj, evt)
303
304    def HideCursor(self):
305        """Hides the cursor."""
306        c = wx.StockCursor(wx.CURSOR_BLANK)
307        self.SetCursor(c)
308
309    def ShowCursor(self):
310        """Shows the cursor."""
311        rw = self._Iren.GetRenderWindow()
312        cur = self._cursor_map[rw.GetCurrentCursor()]
313        c = wx.StockCursor(cur)
314        self.SetCursor(c)
315
316    def GetDisplayId(self):
317        """Function to get X11 Display ID from WX and return it in a format
318        that can be used by VTK Python.
319
320        We query the X11 Display with a new call that was added in wxPython
321        2.6.0.1.  The call returns a SWIG object which we can query for the
322        address and subsequently turn into an old-style SWIG-mangled string
323        representation to pass to VTK.
324        """
325        d = None
326
327        try:
328            d = wx.GetXDisplay()
329
330        except AttributeError:
331            # wx.GetXDisplay was added by Robin Dunn in wxPython 2.6.0.1
332            # if it's not available, we can't pass it.  In general,
333            # things will still work; on some setups, it'll break.
334            pass
335
336        else:
337            # wx returns None on platforms where wx.GetXDisplay is not relevant
338            if d:
339                d = hex(d)
340                # On wxPython-2.6.3.2 and above there is no leading '0x'.
341                if not d.startswith('0x'):
342                    d = '0x' + d
343
344                # VTK wants it as: _xxxxxxxx_p_void (SWIG pointer)
345                d = '_%s_%s\0' % (d[2:], 'p_void')
346
347        return d
348
349    def OnMouseCaptureLost(self, event):
350        """This is signalled when we lose mouse capture due to an
351        external event, such as when a dialog box is shown.  See the
352        wx documentation.
353        """
354
355        # the documentation seems to imply that by this time we've
356        # already lost capture.  I have to assume that we don't need
357        # to call ReleaseMouse ourselves.
358        if _useCapture and self._own_mouse:
359            self._own_mouse = False
360
361    def OnPaint(self,event):
362        """Handles the wx.EVT_PAINT event for
363        wxVTKRenderWindowInteractor.
364        """
365
366        # wx should continue event processing after this handler.
367        # We call this BEFORE Render(), so that if Render() raises
368        # an exception, wx doesn't re-call OnPaint repeatedly.
369        event.Skip()
370
371        dc = wx.PaintDC(self)
372
373        # make sure the RenderWindow is sized correctly
374        self._Iren.GetRenderWindow().SetSize(self.GetSize())
375
376        # Tell the RenderWindow to render inside the wx.Window.
377        if not self.__handle:
378
379            # on relevant platforms, set the X11 Display ID
380            d = self.GetDisplayId()
381            if d and self.__has_painted:
382                self._Iren.GetRenderWindow().SetDisplayId(d)
383
384            # store the handle
385            self.__handle = self.GetHandle()
386            # and give it to VTK
387            self._Iren.GetRenderWindow().SetWindowInfo(str(self.__handle))
388
389            # now that we've painted once, the Render() reparenting logic
390            # is safe
391            self.__has_painted = True
392
393        self.Render()
394
395    def OnSize(self,event):
396        """Handles the wx.EVT_SIZE event for
397        wxVTKRenderWindowInteractor.
398        """
399
400        # event processing should continue (we call this before the
401        # Render(), in case it raises an exception)
402        event.Skip()
403
404        try:
405            width, height = event.GetSize()
406        except:
407            width = event.GetSize().width
408            height = event.GetSize().height
409        self._Iren.SetSize(width, height)
410        self._Iren.ConfigureEvent()
411
412        # this will check for __handle
413        self.Render()
414
415    def OnMotion(self,event):
416        """Handles the wx.EVT_MOTION event for
417        wxVTKRenderWindowInteractor.
418        """
419
420        # event processing should continue
421        # we call this early in case any of the VTK code raises an
422        # exception.
423        event.Skip()
424
425        self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(),
426                                            event.ControlDown(),
427                                            event.ShiftDown(),
428                                            chr(0), 0, None)
429        self._Iren.MouseMoveEvent()
430
431    def OnEnter(self,event):
432        """Handles the wx.EVT_ENTER_WINDOW event for
433        wxVTKRenderWindowInteractor.
434        """
435
436        # event processing should continue
437        event.Skip()
438
439        self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(),
440                                            event.ControlDown(),
441              event.ShiftDown(),
442              chr(0), 0, None)
443        self._Iren.EnterEvent()
444
445
446    def OnLeave(self,event):
447        """Handles the wx.EVT_LEAVE_WINDOW event for
448        wxVTKRenderWindowInteractor.
449        """
450
451        # event processing should continue
452        event.Skip()
453
454        self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(),
455                                            event.ControlDown(),
456              event.ShiftDown(),
457              chr(0), 0, None)
458        self._Iren.LeaveEvent()
459
460
461    def OnButtonDown(self,event):
462        """Handles the wx.EVT_LEFT/RIGHT/MIDDLE_DOWN events for
463        wxVTKRenderWindowInteractor.
464        """
465
466        # allow wx event processing to continue
467        # on wxPython 2.6.0.1, omitting this will cause problems with
468        # the initial focus, resulting in the wxVTKRWI ignoring keypresses
469        # until we focus elsewhere and then refocus the wxVTKRWI frame
470        # we do it this early in case any of the following VTK code
471        # raises an exception.
472        event.Skip()
473
474        ctrl, shift = event.ControlDown(), event.ShiftDown()
475        self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(),
476                                            ctrl, shift, chr(0), 0, None)
477
478        button = 0
479        if event.RightDown():
480            self._Iren.RightButtonPressEvent()
481            button = 'Right'
482        elif event.LeftDown():
483            self._Iren.LeftButtonPressEvent()
484            button = 'Left'
485        elif event.MiddleDown():
486            self._Iren.MiddleButtonPressEvent()
487            button = 'Middle'
488
489        # save the button and capture mouse until the button is released
490        # we only capture the mouse if it hasn't already been captured
491        if _useCapture and not self._own_mouse:
492            self._own_mouse = True
493            self._mouse_capture_button = button
494            self.CaptureMouse()
495
496
497    def OnButtonUp(self,event):
498        """Handles the wx.EVT_LEFT/RIGHT/MIDDLE_UP events for
499        wxVTKRenderWindowInteractor.
500        """
501
502        # event processing should continue
503        event.Skip()
504
505        button = 0
506        if event.RightUp():
507            button = 'Right'
508        elif event.LeftUp():
509            button = 'Left'
510        elif event.MiddleUp():
511            button = 'Middle'
512
513        # if the same button is released that captured the mouse, and
514        # we have the mouse, release it.
515        # (we need to get rid of this as soon as possible; if we don't
516        #  and one of the event handlers raises an exception, mouse
517        #  is never released.)
518        if _useCapture and self._own_mouse and \
519                button==self._mouse_capture_button:
520            self.ReleaseMouse()
521            self._own_mouse = False
522
523        ctrl, shift = event.ControlDown(), event.ShiftDown()
524        self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(),
525                                            ctrl, shift, chr(0), 0, None)
526
527        if button == 'Right':
528            self._Iren.RightButtonReleaseEvent()
529        elif button == 'Left':
530            self._Iren.LeftButtonReleaseEvent()
531        elif button == 'Middle':
532            self._Iren.MiddleButtonReleaseEvent()
533
534
535    def OnMouseWheel(self,event):
536        """Handles the wx.EVT_MOUSEWHEEL event for
537        wxVTKRenderWindowInteractor.
538        """
539
540        # event processing should continue
541        event.Skip()
542
543        ctrl, shift = event.ControlDown(), event.ShiftDown()
544        self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(),
545                                            ctrl, shift, chr(0), 0, None)
546        if event.GetWheelRotation() > 0:
547            self._Iren.MouseWheelForwardEvent()
548        else:
549            self._Iren.MouseWheelBackwardEvent()
550
551
552    def OnKeyDown(self,event):
553        """Handles the wx.EVT_KEY_DOWN event for
554        wxVTKRenderWindowInteractor.
555        """
556
557        # event processing should continue
558        event.Skip()
559
560        ctrl, shift = event.ControlDown(), event.ShiftDown()
561        keycode, keysym = event.GetKeyCode(), None
562        key = chr(0)
563        if keycode < 256:
564            key = chr(keycode)
565
566        # wxPython 2.6.0.1 does not return a valid event.Get{X,Y}()
567        # for this event, so we use the cached position.
568        (x,y)= self._Iren.GetEventPosition()
569        self._Iren.SetEventInformation(x, y,
570                                       ctrl, shift, key, 0,
571                                       keysym)
572
573        self._Iren.KeyPressEvent()
574        self._Iren.CharEvent()
575
576
577    def OnKeyUp(self,event):
578        """Handles the wx.EVT_KEY_UP event for
579        wxVTKRenderWindowInteractor.
580        """
581
582        # event processing should continue
583        event.Skip()
584
585        ctrl, shift = event.ControlDown(), event.ShiftDown()
586        keycode, keysym = event.GetKeyCode(), None
587        key = chr(0)
588        if keycode < 256:
589            key = chr(keycode)
590
591        self._Iren.SetEventInformationFlipY(event.GetX(), event.GetY(),
592                                            ctrl, shift, key, 0,
593                                            keysym)
594        self._Iren.KeyReleaseEvent()
595
596
597    def GetRenderWindow(self):
598        """Returns the render window (vtkRenderWindow).
599        """
600        return self._Iren.GetRenderWindow()
601
602    def Render(self):
603        """Actually renders the VTK scene on screen.
604        """
605        RenderAllowed = 1
606
607        if not self.__RenderWhenDisabled:
608            # the user doesn't want us to render when the toplevel frame
609            # is disabled - first find the top level parent
610            topParent = wx.GetTopLevelParent(self)
611            if topParent:
612                # if it exists, check whether it's enabled
613                # if it's not enabeld, RenderAllowed will be false
614                RenderAllowed = topParent.IsEnabled()
615
616        if RenderAllowed:
617            if self.__handle and self.__handle == self.GetHandle():
618                self._Iren.GetRenderWindow().Render()
619
620            elif self.GetHandle() and self.__has_painted:
621                # this means the user has reparented us; let's adapt to the
622                # new situation by doing the WindowRemap dance
623                self._Iren.GetRenderWindow().SetNextWindowInfo(
624                    str(self.GetHandle()))
625
626                # make sure the DisplayId is also set correctly
627                d = self.GetDisplayId()
628                if d:
629                    self._Iren.GetRenderWindow().SetDisplayId(d)
630
631                # do the actual remap with the new parent information
632                self._Iren.GetRenderWindow().WindowRemap()
633
634                # store the new situation
635                self.__handle = self.GetHandle()
636                self._Iren.GetRenderWindow().Render()
637
638    def SetRenderWhenDisabled(self, newValue):
639        """Change value of __RenderWhenDisabled ivar.
640
641        If __RenderWhenDisabled is false (the default), this widget will not
642        call Render() on the RenderWindow if the top level frame (i.e. the
643        containing frame) has been disabled.
644
645        This prevents recursive rendering during wx.SafeYield() calls.
646        wx.SafeYield() can be called during the ProgressMethod() callback of
647        a VTK object to have progress bars and other GUI elements updated -
648        it does this by disabling all windows (disallowing user-input to
649        prevent re-entrancy of code) and then handling all outstanding
650        GUI events.
651
652        However, this often triggers an OnPaint() method for wxVTKRWIs,
653        resulting in a Render(), resulting in Update() being called whilst
654        still in progress.
655        """
656        self.__RenderWhenDisabled = bool(newValue)
657
658
659#--------------------------------------------------------------------
660def wxVTKRenderWindowInteractorConeExample():
661    """Like it says, just a simple example
662    """
663
664    from vtkmodules.vtkFiltersSources import vtkConeSource
665    from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
666
667    # every wx app needs an app
668    app = wx.App(False)
669
670    # create the top-level frame, sizer and wxVTKRWI
671    frame = wx.Frame(None, -1, "wxVTKRenderWindowInteractor", size=(400,400))
672    widget = wxVTKRenderWindowInteractor(frame, -1)
673    sizer = wx.BoxSizer(wx.VERTICAL)
674    sizer.Add(widget, 1, wx.EXPAND)
675    frame.SetSizer(sizer)
676    frame.Layout()
677
678    # It would be more correct (API-wise) to call widget.Initialize() and
679    # widget.Start() here, but Initialize() calls RenderWindow.Render().
680    # That Render() call will get through before we can setup the
681    # RenderWindow() to render via the wxWidgets-created context; this
682    # causes flashing on some platforms and downright breaks things on
683    # other platforms.  Instead, we call widget.Enable().  This means
684    # that the RWI::Initialized ivar is not set, but in THIS SPECIFIC CASE,
685    # that doesn't matter.
686    widget.Enable(1)
687
688    widget.AddObserver("ExitEvent", lambda o,e,f=frame: f.Close())
689
690    ren = vtkRenderer()
691    widget.GetRenderWindow().AddRenderer(ren)
692
693    cone = vtkConeSource()
694    cone.SetResolution(8)
695
696    coneMapper = vtkPolyDataMapper()
697    coneMapper.SetInputConnection(cone.GetOutputPort())
698
699    coneActor = vtkActor()
700    coneActor.SetMapper(coneMapper)
701
702    ren.AddActor(coneActor)
703
704    # show the window
705    frame.Show()
706
707    app.MainLoop()
708
709if __name__ == "__main__":
710    wxVTKRenderWindowInteractorConeExample()
711