1"""
2Description:
3
4  Provides a simple VTK widget for pyGtk.  This embeds a
5  vtkRenderWindow inside a GTK widget.  This is based on
6  vtkTkRenderWidget.py.  The GtkVTKRenderWindowBase class provides the
7  abstraction necessary for someone to use their own interaction
8  behaviour.  The method names are similar to those in
9  vtkInteractorStyle.h.
10
11  The class uses the gtkgl.GtkGLArea widget (gtkglarea).  This avoids
12  a lot of problems with flicker.
13
14  There is a working example at the bottom.
15
16Credits:
17
18  Thanks to Dave Reed for testing the code under various platforms and
19  for his suggestion to use the GtkGLArea widget to avoid flicker
20  related issues.
21
22Created by Prabhu Ramachandran, March 2001.
23
24Using GtkGLArea, March, 2002.
25
26Bugs:
27
28  (*) There is a focus related problem.  Tkinter has a focus object
29  that handles focus events.  I don't know of an equivalent object
30  under GTK.  So, when an 'enter_notify_event' is received on the
31  GtkVTKRenderWindow I grab the focus but I don't know what to do when
32  I get a 'leave_notify_event'.
33
34  (*) Will not work under Win32 because it uses the XID of a window in
35  OnRealize.  Suggestions to fix this will be appreciated.
36
37"""
38
39import gtk, GDK, gtkgl
40from vtkmodules.vtkRenderingCore import vtkCellPicker, vtkProperty, vtkRenderWindow
41import math
42
43
44class GtkVTKRenderWindowBase(gtkgl.GtkGLArea):
45
46    """ A base class that enables one to embed a vtkRenderWindow into
47    a pyGTK widget.  This class embeds the RenderWindow correctly.
48    Provided are some empty methods that can be overloaded to provide
49    a user defined interaction behaviour.  The event handling
50    functions have names that are somewhat similar to the ones in the
51    vtkInteractorStyle class included with VTK. """
52
53    def __init__(self, *args):
54        l = list(args)
55        attr = (gtkgl.RGBA, gtkgl.DOUBLEBUFFER)
56        l.insert(0, self)
57        l.insert(1, attr)
58        apply(gtkgl.GtkGLArea.__init__, l)
59        self._RenderWindow = vtkRenderWindow()
60
61        # private attributes
62        self.__Created = 0
63
64        # used by the LOD actors
65        self._DesiredUpdateRate = 15
66        self._StillUpdateRate = 0.0001
67
68        self.ConnectSignals()
69
70        # need this to be able to handle key_press events.
71        self.set_flags(gtk.CAN_FOCUS)
72        # default size
73        self.set_usize(300, 300)
74
75    def ConnectSignals(self):
76        self.connect("realize", self.OnRealize)
77        self.connect("expose_event", self.OnExpose)
78        self.connect("configure_event", self.OnConfigure)
79        self.connect("button_press_event", self.OnButtonDown)
80        self.connect("button_release_event", self.OnButtonUp)
81        self.connect("motion_notify_event", self.OnMouseMove)
82        self.connect("enter_notify_event", self.OnEnter)
83        self.connect("leave_notify_event", self.OnLeave)
84        self.connect("key_press_event", self.OnKeyPress)
85        self.connect("delete_event", self.OnDestroy)
86        self.add_events(GDK.EXPOSURE_MASK| GDK.BUTTON_PRESS_MASK |
87                        GDK.BUTTON_RELEASE_MASK |
88                        GDK.KEY_PRESS_MASK |
89                        GDK.POINTER_MOTION_MASK |
90                        GDK.POINTER_MOTION_HINT_MASK |
91                        GDK.ENTER_NOTIFY_MASK | GDK.LEAVE_NOTIFY_MASK)
92
93    def GetRenderWindow(self):
94        return self._RenderWindow
95
96    def GetRenderer(self):
97        self._RenderWindow.GetRenderers().InitTraversal()
98        return self._RenderWindow.GetRenderers().GetNextItem()
99
100    def SetDesiredUpdateRate(self, rate):
101        """Mirrors the method with the same name in
102        vtkRenderWindowInteractor."""
103        self._DesiredUpdateRate = rate
104
105    def GetDesiredUpdateRate(self):
106        """Mirrors the method with the same name in
107        vtkRenderWindowInteractor."""
108        return self._DesiredUpdateRate
109
110    def SetStillUpdateRate(self, rate):
111        """Mirrors the method with the same name in
112        vtkRenderWindowInteractor."""
113        self._StillUpdateRate = rate
114
115    def GetStillUpdateRate(self):
116        """Mirrors the method with the same name in
117        vtkRenderWindowInteractor."""
118        return self._StillUpdateRate
119
120    def Render(self):
121        if self.__Created:
122            self._RenderWindow.Render()
123
124    def OnRealize(self, *args):
125        if self.__Created == 0:
126            # you can't get the xid without the window being realized.
127            self.realize()
128            win_id = str(self.get_window().xid)
129            self._RenderWindow.SetWindowInfo(win_id)
130            self.__Created = 1
131        return gtk.TRUE
132
133    def OnConfigure(self, wid, event=None):
134        sz = self._RenderWindow.GetSize()
135        if (event.width != sz[0]) or (event.height != sz[1]):
136            self._RenderWindow.SetSize(event.width, event.height)
137        return gtk.TRUE
138
139    def OnExpose(self, *args):
140        self.Render()
141        return gtk.TRUE
142
143    def OnDestroy(self, event=None):
144        self.hide()
145        del self._RenderWindow
146        self.destroy()
147        return gtk.TRUE
148
149    def OnButtonDown(self, wid, event):
150        """Mouse button pressed."""
151        self._RenderWindow.SetDesiredUpdateRate(self._DesiredUpdateRate)
152        return gtk.TRUE
153
154    def OnButtonUp(self, wid, event):
155        """Mouse button released."""
156        self._RenderWindow.SetDesiredUpdateRate(self._StillUpdateRate)
157        return gtk.TRUE
158
159    def OnMouseMove(self, wid, event):
160        """Mouse has moved."""
161        return gtk.TRUE
162
163    def OnEnter(self, wid, event):
164        """Entering the vtkRenderWindow."""
165        return gtk.TRUE
166
167    def OnLeave(self, wid, event):
168        """Leaving the vtkRenderWindow."""
169        return gtk.TRUE
170
171    def OnKeyPress(self, wid, event):
172        """Key pressed."""
173        return gtk.TRUE
174
175    def OnKeyRelease(self, wid, event):
176        "Key released."
177        return gtk.TRUE
178
179
180class GtkVTKRenderWindow(GtkVTKRenderWindowBase):
181
182    """ An example of a fully functional GtkVTKRenderWindow that is
183    based on the vtkRenderWidget.py provided with the VTK sources."""
184
185    def __init__(self, *args):
186        l = list(args)
187        l.insert(0, self)
188        apply(GtkVTKRenderWindowBase.__init__, l)
189
190        self._CurrentRenderer = None
191        self._CurrentCamera = None
192        self._CurrentZoom = 1.0
193        self._CurrentLight = None
194
195        self._ViewportCenterX = 0
196        self._ViewportCenterY = 0
197
198        self._Picker = vtkCellPicker()
199        self._PickedAssembly = None
200        self._PickedProperty = vtkProperty()
201        self._PickedProperty.SetColor(1, 0, 0)
202        self._PrePickedProperty = None
203
204        self._OldFocus = None
205
206        # these record the previous mouse position
207        self._LastX = 0
208        self._LastY = 0
209
210    def OnButtonDown(self, wid, event):
211        self._RenderWindow.SetDesiredUpdateRate(self._DesiredUpdateRate)
212        return self.StartMotion(wid, event)
213
214    def OnButtonUp(self, wid, event):
215        self._RenderWindow.SetDesiredUpdateRate(self._StillUpdateRate)
216        return self.EndMotion(wid, event)
217
218    def OnMouseMove(self, wid, event=None):
219        if ((event.state & GDK.BUTTON1_MASK) == GDK.BUTTON1_MASK):
220            if ((event.state & GDK.SHIFT_MASK) == GDK.SHIFT_MASK):
221                m = self.get_pointer()
222                self.Pan(m[0], m[1])
223                return gtk.TRUE
224            else:
225                m = self.get_pointer()
226                self.Rotate(m[0], m[1])
227                return gtk.TRUE
228        elif ((event.state & GDK.BUTTON2_MASK) == GDK.BUTTON2_MASK):
229            m = self.get_pointer()
230            self.Pan(m[0], m[1])
231            return gtk.TRUE
232        elif ((event.state & GDK.BUTTON3_MASK) == GDK.BUTTON3_MASK):
233            m = self.get_pointer()
234            self.Zoom(m[0], m[1])
235            return gtk.TRUE
236        else:
237            return gtk.FALSE
238
239    def OnEnter(self, wid, event=None):
240        self.grab_focus()
241        w = self.get_pointer()
242        self.UpdateRenderer(w[0], w[1])
243        return gtk.TRUE
244
245    def OnLeave(self, wid, event):
246        return gtk.TRUE
247
248    def OnKeyPress(self, wid, event=None):
249        if (event.keyval == GDK.r) or (event.keyval == GDK.R):
250            self.Reset()
251            return gtk.TRUE
252        elif (event.keyval == GDK.w) or (event.keyval == GDK.W):
253            self.Wireframe()
254            return gtk.TRUE
255        elif (event.keyval == GDK.s) or (event.keyval == GDK.S):
256            self.Surface()
257            return gtk.TRUE
258        elif (event.keyval == GDK.p) or (event.keyval == GDK.P):
259            m = self.get_pointer()
260            self.PickActor(m[0], m[1])
261            return gtk.TRUE
262        else:
263            return gtk.FALSE
264
265    def GetZoomFactor(self):
266        return self._CurrentZoom
267
268    def SetZoomFactor(self, zf):
269        self._CurrentZoom = zf
270
271    def GetPicker(self):
272        return self._Picker
273
274    def Render(self):
275        if (self._CurrentLight):
276            light = self._CurrentLight
277            light.SetPosition(self._CurrentCamera.GetPosition())
278            light.SetFocalPoint(self._CurrentCamera.GetFocalPoint())
279
280        GtkVTKRenderWindowBase.Render(self)
281
282    def UpdateRenderer(self,x,y):
283        """
284        UpdateRenderer will identify the renderer under the mouse and set
285        up _CurrentRenderer, _CurrentCamera, and _CurrentLight.
286        """
287        windowX = self.get_window().width
288        windowY = self.get_window().height
289
290        renderers = self._RenderWindow.GetRenderers()
291        numRenderers = renderers.GetNumberOfItems()
292
293        self._CurrentRenderer = None
294        renderers.InitTraversal()
295        for i in range(0,numRenderers):
296            renderer = renderers.GetNextItem()
297            vx,vy = (0,0)
298            if (windowX > 1):
299                vx = float(x)/(windowX-1)
300            if (windowY > 1):
301                vy = (windowY-float(y)-1)/(windowY-1)
302            (vpxmin,vpymin,vpxmax,vpymax) = renderer.GetViewport()
303
304            if (vx >= vpxmin and vx <= vpxmax and
305                vy >= vpymin and vy <= vpymax):
306                self._CurrentRenderer = renderer
307                self._ViewportCenterX = float(windowX)*(vpxmax-vpxmin)/2.0\
308                                        +vpxmin
309                self._ViewportCenterY = float(windowY)*(vpymax-vpymin)/2.0\
310                                        +vpymin
311                self._CurrentCamera = self._CurrentRenderer.GetActiveCamera()
312                lights = self._CurrentRenderer.GetLights()
313                lights.InitTraversal()
314                self._CurrentLight = lights.GetNextItem()
315                break
316
317        self._LastX = x
318        self._LastY = y
319
320    def GetCurrentRenderer(self):
321        return self._CurrentRenderer
322
323    def StartMotion(self, wid, event=None):
324        x = event.x
325        y = event.y
326        self.UpdateRenderer(x,y)
327        return gtk.TRUE
328
329    def EndMotion(self, wid, event=None):
330        if self._CurrentRenderer:
331            self.Render()
332        return gtk.TRUE
333
334    def Rotate(self,x,y):
335        if self._CurrentRenderer:
336
337            self._CurrentCamera.Azimuth(self._LastX - x)
338            self._CurrentCamera.Elevation(y - self._LastY)
339            self._CurrentCamera.OrthogonalizeViewUp()
340
341            self._LastX = x
342            self._LastY = y
343
344            self._CurrentRenderer.ResetCameraClippingRange()
345            self.Render()
346
347    def Pan(self,x,y):
348        if self._CurrentRenderer:
349
350            renderer = self._CurrentRenderer
351            camera = self._CurrentCamera
352            (pPoint0,pPoint1,pPoint2) = camera.GetPosition()
353            (fPoint0,fPoint1,fPoint2) = camera.GetFocalPoint()
354
355            if (camera.GetParallelProjection()):
356                renderer.SetWorldPoint(fPoint0,fPoint1,fPoint2,1.0)
357                renderer.WorldToDisplay()
358                fx,fy,fz = renderer.GetDisplayPoint()
359                renderer.SetDisplayPoint(fx-x+self._LastX,
360                                         fy+y-self._LastY,
361                                         fz)
362                renderer.DisplayToWorld()
363                fx,fy,fz,fw = renderer.GetWorldPoint()
364                camera.SetFocalPoint(fx,fy,fz)
365
366                renderer.SetWorldPoint(pPoint0,pPoint1,pPoint2,1.0)
367                renderer.WorldToDisplay()
368                fx,fy,fz = renderer.GetDisplayPoint()
369                renderer.SetDisplayPoint(fx-x+self._LastX,
370                                         fy+y-self._LastY,
371                                         fz)
372                renderer.DisplayToWorld()
373                fx,fy,fz,fw = renderer.GetWorldPoint()
374                camera.SetPosition(fx,fy,fz)
375
376            else:
377                (fPoint0,fPoint1,fPoint2) = camera.GetFocalPoint()
378                # Specify a point location in world coordinates
379                renderer.SetWorldPoint(fPoint0,fPoint1,fPoint2,1.0)
380                renderer.WorldToDisplay()
381                # Convert world point coordinates to display coordinates
382                dPoint = renderer.GetDisplayPoint()
383                focalDepth = dPoint[2]
384
385                aPoint0 = self._ViewportCenterX + (x - self._LastX)
386                aPoint1 = self._ViewportCenterY - (y - self._LastY)
387
388                renderer.SetDisplayPoint(aPoint0,aPoint1,focalDepth)
389                renderer.DisplayToWorld()
390
391                (rPoint0,rPoint1,rPoint2,rPoint3) = renderer.GetWorldPoint()
392                if (rPoint3 != 0.0):
393                    rPoint0 = rPoint0/rPoint3
394                    rPoint1 = rPoint1/rPoint3
395                    rPoint2 = rPoint2/rPoint3
396
397                camera.SetFocalPoint((fPoint0 - rPoint0) + fPoint0,
398                                     (fPoint1 - rPoint1) + fPoint1,
399                                     (fPoint2 - rPoint2) + fPoint2)
400
401                camera.SetPosition((fPoint0 - rPoint0) + pPoint0,
402                                   (fPoint1 - rPoint1) + pPoint1,
403                                   (fPoint2 - rPoint2) + pPoint2)
404
405            self._LastX = x
406            self._LastY = y
407
408            self.Render()
409
410    def Zoom(self,x,y):
411        if self._CurrentRenderer:
412
413            renderer = self._CurrentRenderer
414            camera = self._CurrentCamera
415
416            zoomFactor = math.pow(1.02,(0.5*(self._LastY - y)))
417            self._CurrentZoom = self._CurrentZoom * zoomFactor
418
419            if camera.GetParallelProjection():
420                parallelScale = camera.GetParallelScale()/zoomFactor
421                camera.SetParallelScale(parallelScale)
422            else:
423                camera.Dolly(zoomFactor)
424                renderer.ResetCameraClippingRange()
425
426            self._LastX = x
427            self._LastY = y
428
429            self.Render()
430
431    def Reset(self):
432        if self._CurrentRenderer:
433            self._CurrentRenderer.ResetCamera()
434
435        self.Render()
436
437    def Wireframe(self):
438        actors = self._CurrentRenderer.GetActors()
439        numActors = actors.GetNumberOfItems()
440        actors.InitTraversal()
441        for i in range(0,numActors):
442            actor = actors.GetNextItem()
443            actor.GetProperty().SetRepresentationToWireframe()
444
445        self.Render()
446
447    def Surface(self):
448        actors = self._CurrentRenderer.GetActors()
449        numActors = actors.GetNumberOfItems()
450        actors.InitTraversal()
451        for i in range(0,numActors):
452            actor = actors.GetNextItem()
453            actor.GetProperty().SetRepresentationToSurface()
454
455        self.Render()
456
457    def PickActor(self,x,y):
458        if self._CurrentRenderer:
459
460            renderer = self._CurrentRenderer
461            picker = self._Picker
462
463            windowY = self.get_window().height
464            picker.Pick(x,(windowY - y - 1),0.0,renderer)
465            assembly = picker.GetAssembly()
466
467            if (self._PickedAssembly != None and
468                self._PrePickedProperty != None):
469                self._PickedAssembly.SetProperty(self._PrePickedProperty)
470                # release hold of the property
471                self._PrePickedProperty.UnRegister(self._PrePickedProperty)
472                self._PrePickedProperty = None
473
474            if (assembly != None):
475                self._PickedAssembly = assembly
476                self._PrePickedProperty = self._PickedAssembly.GetProperty()
477                # hold onto the property
478                self._PrePickedProperty.Register(self._PrePickedProperty)
479                self._PickedAssembly.SetProperty(self._PickedProperty)
480
481            self.Render()
482
483
484def main():
485    from vtkmodules.vtkFiltersSources import vtkConeSource
486    from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
487
488    # The main window
489    window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
490    window.set_title("A GtkVTKRenderWindow Demo!")
491    window.connect("destroy", gtk.mainquit)
492    window.connect("delete_event", gtk.mainquit)
493    window.set_border_width(10)
494
495    # A VBox into which widgets are packed.
496    vbox = gtk.GtkVBox(spacing=3)
497    window.add(vbox)
498    vbox.show()
499
500    # The GtkVTKRenderWindow
501    gvtk = GtkVTKRenderWindow()
502    #gvtk.SetDesiredUpdateRate(1000)
503    gvtk.set_usize(400, 400)
504    vbox.pack_start(gvtk)
505    gvtk.show()
506
507    # The VTK stuff.
508    cone = vtkConeSource()
509    cone.SetResolution(80)
510    coneMapper = vtkPolyDataMapper()
511    coneMapper.SetInputConnection(cone.GetOutputPort())
512    #coneActor = vtkLODActor()
513    coneActor = vtkActor()
514    coneActor.SetMapper(coneMapper)
515    coneActor.GetProperty().SetColor(0.5, 0.5, 1.0)
516    ren = vtkRenderer()
517    gvtk.GetRenderWindow().AddRenderer(ren)
518    ren.AddActor(coneActor)
519
520    # A simple quit button
521    quit = gtk.GtkButton("Quit!")
522    quit.connect("clicked", gtk.mainquit)
523    vbox.pack_start(quit)
524    quit.show()
525
526    # show the main window and start event processing.
527    window.show()
528    gtk.mainloop()
529
530
531if __name__ == "__main__":
532    main()
533