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