1"""
2Description:
3
4  Provides a pyGtk vtkRenderWindowInteractor widget.  This embeds a
5  vtkRenderWindow inside a GTK widget and uses the
6  vtkGenericRenderWindowInteractor for the event handling.  This is
7  based on vtkTkRenderWindow.py.
8
9  The class uses the gtkgl.GtkGLArea widget (gtkglarea).  This avoids
10  a lot of problems with flicker.
11
12  There is a working example at the bottom.
13
14Created by Prabhu Ramachandran, April 2002.
15
16Bugs:
17
18  (*) There is a focus related problem.  Tkinter has a focus object
19  that handles focus events.  I don't know of an equivalent object
20  under GTK.  So, when an 'enter_notify_event' is received on the
21  GtkVTKRenderWindow I grab the focus but I don't know what to do when
22  I get a 'leave_notify_event'.
23
24  (*) Will not work under Win32 because it uses the XID of a window in
25  OnRealize.  Suggestions to fix this will be appreciated.
26
27"""
28
29import gtk, GDK, gtkgl
30from vtkmodules.vtkRenderingCore import vtkGenericRenderWindowInteractor
31import math
32
33
34class GtkVTKRenderWindowInteractor(gtkgl.GtkGLArea):
35
36    """ Embeds a vtkRenderWindow into a pyGTK widget and uses
37    vtkGenericRenderWindowInteractor for the event handling.  This
38    class embeds the RenderWindow correctly.  A __getattr__ hook is
39    provided that makes the class behave like a
40    vtkGenericRenderWindowInteractor."""
41
42    def __init__(self, *args):
43        l = list(args)
44        attr = (gtkgl.RGBA, gtkgl.DOUBLEBUFFER)
45        l.insert(0, self)
46        l.insert(1, attr)
47        apply(gtkgl.GtkGLArea.__init__, l)
48        self._RenderWindow = vtkRenderWindow()
49
50        # private attributes
51        self.__Created = 0
52        self._ActiveButton = 0
53
54        self._Iren = vtkGenericRenderWindowInteractor()
55        self._Iren.SetRenderWindow(self._RenderWindow)
56
57        self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
58        self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)
59        self.ConnectSignals()
60
61        # need this to be able to handle key_press events.
62        self.set_flags(gtk.CAN_FOCUS)
63        # default size
64        self.set_usize(300, 300)
65
66    def set_usize(self, w, h):
67        gtkgl.GtkGLArea.set_usize(self, w, h)
68        self._RenderWindow.SetSize(w, h)
69        self._Iren.SetSize(w, h)
70        self._Iren.ConfigureEvent()
71
72    def ConnectSignals(self):
73        self.connect("realize", self.OnRealize)
74        self.connect("expose_event", self.OnExpose)
75        self.connect("configure_event", self.OnConfigure)
76        self.connect("button_press_event", self.OnButtonDown)
77        self.connect("button_release_event", self.OnButtonUp)
78        self.connect("motion_notify_event", self.OnMouseMove)
79        self.connect("enter_notify_event", self.OnEnter)
80        self.connect("leave_notify_event", self.OnLeave)
81        self.connect("key_press_event", self.OnKeyPress)
82        self.connect("delete_event", self.OnDestroy)
83        self.add_events(GDK.EXPOSURE_MASK| GDK.BUTTON_PRESS_MASK |
84                        GDK.BUTTON_RELEASE_MASK |
85                        GDK.KEY_PRESS_MASK |
86                        GDK.POINTER_MOTION_MASK |
87                        GDK.POINTER_MOTION_HINT_MASK |
88                        GDK.ENTER_NOTIFY_MASK | GDK.LEAVE_NOTIFY_MASK)
89
90    def __getattr__(self, attr):
91        """Makes the object behave like a
92        vtkGenericRenderWindowInteractor"""
93        if attr == '__vtk__':
94            return lambda t=self._Iren: t
95        elif hasattr(self._Iren, attr):
96            return getattr(self._Iren, attr)
97        else:
98            raise AttributeError(self.__class__.__name__ +
99                  " has no attribute named " + attr)
100
101    def CreateTimer(self, obj, event):
102        gtk.timeout_add(10, self._Iren.TimerEvent)
103
104    def DestroyTimer(self, obj, event):
105        """The timer is a one shot timer so will expire automatically."""
106        return 1
107
108    def GetRenderWindow(self):
109        return self._RenderWindow
110
111    def Render(self):
112        if self.__Created:
113            self._RenderWindow.Render()
114
115    def OnRealize(self, *args):
116        if self.__Created == 0:
117            # you can't get the xid without the window being realized.
118            self.realize()
119            win_id = str(self.get_window().xid)
120            self._RenderWindow.SetWindowInfo(win_id)
121            self._Iren.Initialize()
122            self.__Created = 1
123        return gtk.TRUE
124
125    def OnConfigure(self, wid, event=None):
126        sz = self._RenderWindow.GetSize()
127        if (event.width != sz[0]) or (event.height != sz[1]):
128            self._Iren.SetSize(event.width, event.height)
129            self._Iren.ConfigureEvent()
130        return gtk.TRUE
131
132    def OnExpose(self, *args):
133        self.Render()
134        return gtk.TRUE
135
136    def OnDestroy(self, event=None):
137        self.hide()
138        del self._RenderWindow
139        self.destroy()
140        return gtk.TRUE
141
142    def _GetCtrlShift(self, event):
143        ctrl, shift = 0, 0
144        if ((event.state & GDK.CONTROL_MASK) == GDK.CONTROL_MASK):
145            ctrl = 1
146        if ((event.state & GDK.SHIFT_MASK) == GDK.SHIFT_MASK):
147            shift = 1
148        return ctrl, shift
149
150    def OnButtonDown(self, wid, event):
151        """Mouse button pressed."""
152        m = self.get_pointer()
153        ctrl, shift = self._GetCtrlShift(event)
154        self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
155                                            chr(0), 0, None)
156        button = event.button
157        if button == 3:
158            self._Iren.RightButtonPressEvent()
159            return gtk.TRUE
160        elif button == 1:
161            self._Iren.LeftButtonPressEvent()
162            return gtk.TRUE
163        elif button == 2:
164            self._Iren.MiddleButtonPressEvent()
165            return gtk.TRUE
166        else:
167            return gtk.FALSE
168
169    def OnButtonUp(self, wid, event):
170        """Mouse button released."""
171        m = self.get_pointer()
172        ctrl, shift = self._GetCtrlShift(event)
173        self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
174                                            chr(0), 0, None)
175        button = event.button
176        if button == 3:
177            self._Iren.RightButtonReleaseEvent()
178            return gtk.TRUE
179        elif button == 1:
180            self._Iren.LeftButtonReleaseEvent()
181            return gtk.TRUE
182        elif button == 2:
183            self._Iren.MiddleButtonReleaseEvent()
184            return gtk.TRUE
185
186        return gtk.FALSE
187
188    def OnMouseMove(self, wid, event):
189        """Mouse has moved."""
190        m = self.get_pointer()
191        ctrl, shift = self._GetCtrlShift(event)
192        self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
193                                            chr(0), 0, None)
194        self._Iren.MouseMoveEvent()
195        return gtk.TRUE
196
197    def OnEnter(self, wid, event):
198        """Entering the vtkRenderWindow."""
199        self.grab_focus()
200        m = self.get_pointer()
201        ctrl, shift = self._GetCtrlShift(event)
202        self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
203                                            chr(0), 0, None)
204        self._Iren.EnterEvent()
205        return gtk.TRUE
206
207    def OnLeave(self, wid, event):
208        """Leaving the vtkRenderWindow."""
209        m = self.get_pointer()
210        ctrl, shift = self._GetCtrlShift(event)
211        self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
212                                            chr(0), 0, None)
213        self._Iren.LeaveEvent()
214        return gtk.TRUE
215
216    def OnKeyPress(self, wid, event):
217        """Key pressed."""
218        m = self.get_pointer()
219        ctrl, shift = self._GetCtrlShift(event)
220        keycode, keysym = event.keyval, event.string
221        key = chr(0)
222        if keycode < 256:
223            key = chr(keycode)
224        self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
225                                            key, 0, keysym)
226        self._Iren.KeyPressEvent()
227        self._Iren.CharEvent()
228        return gtk.TRUE
229
230    def OnKeyRelease(self, wid, event):
231        "Key released."
232        m = self.get_pointer()
233        ctrl, shift = self._GetCtrlShift(event)
234        keycode, keysym = event.keyval, event.string
235        key = chr(0)
236        if keycode < 256:
237            key = chr(keycode)
238        self._Iren.SetEventInformationFlipY(m[0], m[1], ctrl, shift,
239                                            key, 0, keysym)
240        self._Iren.KeyReleaseEvent()
241        return gtk.TRUE
242
243    def Initialize(self):
244        if self.__Created:
245            self._Iren.Initialize()
246
247
248def main():
249    from vtkmodules.vtkFiltersSources import vtkConeSource
250    from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
251
252    # The main window
253    window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
254    window.set_title("A GtkVTKRenderWindow Demo!")
255    window.connect("destroy", gtk.mainquit)
256    window.connect("delete_event", gtk.mainquit)
257    window.set_border_width(10)
258
259    # A VBox into which widgets are packed.
260    vbox = gtk.GtkVBox(spacing=3)
261    window.add(vbox)
262    vbox.show()
263
264    # The GtkVTKRenderWindow
265    gvtk = GtkVTKRenderWindowInteractor()
266    #gvtk.SetDesiredUpdateRate(1000)
267    gvtk.set_usize(400, 400)
268    vbox.pack_start(gvtk)
269    gvtk.show()
270    gvtk.Initialize()
271    gvtk.Start()
272    # prevents 'q' from exiting the app.
273    gvtk.AddObserver("ExitEvent", lambda o,e,x=None: x)
274
275    # The VTK stuff.
276    cone = vtkConeSource()
277    cone.SetResolution(80)
278    coneMapper = vtkPolyDataMapper()
279    coneMapper.SetInputConnection(cone.GetOutputPort())
280    #coneActor = vtkLODActor()
281    coneActor = vtkActor()
282    coneActor.SetMapper(coneMapper)
283    coneActor.GetProperty().SetColor(0.5, 0.5, 1.0)
284    ren = vtkRenderer()
285    gvtk.GetRenderWindow().AddRenderer(ren)
286    ren.AddActor(coneActor)
287
288    # A simple quit button
289    quit = gtk.GtkButton("Quit!")
290    quit.connect("clicked", gtk.mainquit)
291    vbox.pack_start(quit)
292    quit.show()
293
294    # show the main window and start event processing.
295    window.show()
296    gtk.mainloop()
297
298
299if __name__ == "__main__":
300    main()
301