"""

A fully functional VTK widget for Tkinter that uses
vtkGenericRenderWindowInteractor.  The widget is called
vtkTkRenderWindowInteractor.  The initialization part of this code is
similar to that of the vtkTkRenderWidget.

Created by Prabhu Ramachandran, April 2002

"""

import Tkinter
import math, os, sys
import vtk

from vtkLoadPythonTkWidgets import vtkLoadPythonTkWidgets

class vtkTkRenderWindowInteractor(Tkinter.Widget):

    """ A vtkTkRenderWidndowInteractor for Python.  Use
    GetRenderWindow() to get the vtkRenderWindow.  Create with the
    keyword stereo=1 in order to generate a stereo-capable window.
    __getattr__ is used to make the widget also behave like a
    vtkGenericRenderWindowInteractor.  """
    
    def __init__(self, master, cnf={}, **kw):
        # load the necessary extensions into tk
        vtkLoadPythonTkWidgets(master.tk)

        try: # check to see if a render window was specified
            renderWindow = kw['rw']
        except KeyError:
            renderWindow = vtk.vtkRenderWindow()

        try:  # was a stereo rendering context requested?
            if kw['stereo']:
	       renderWindow.StereoCapableWindowOn()
               del kw['stereo']
	except KeyError:
            pass
 
        kw['rw'] = renderWindow.GetAddressAsString("vtkRenderWindow")
        Tkinter.Widget.__init__(self, master, 'vtkTkRenderWidget', cnf, kw)

        self._Iren = vtk.vtkGenericRenderWindowInteractor()
        self._Iren.SetRenderWindow(self._RenderWindow)

        self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
        self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)

        # private attributes
        self._OldFocus = None
        self.__InExpose = 0

        # create the Tk bindings
        self.BindEvents()
        #self.tk_focusFollowsMouse()

    def __getattr__(self, attr):
        # because the tk part of vtkTkRenderWidget must have
        # the only remaining reference to the RenderWindow when
        # it is destroyed, we can't actually store the RenderWindow
        # as an attribute but instead have to get it from the tk-side
        if attr == '__vtk__':
            return lambda t=self._Iren: t
        elif attr == '_RenderWindow':
            return self.GetRenderWindow()
        elif hasattr(self._Iren, attr):
            return getattr(self._Iren, attr)
        else:
            raise AttributeError, self.__class__.__name__ + \
                  " has no attribute named " + attr

    def BindEvents(self):        
        """ Bind all the events.  """
        self.bind("<Motion>",
                  lambda e, s=self: s.MouseMoveEvent(e, 0, 0))
        self.bind("<Control-Motion>",
                  lambda e, s=self: s.MouseMoveEvent(e, 1, 0))
        self.bind("<Shift-Motion>",
                  lambda e, s=self: s.MouseMoveEvent(e, 1, 1))
        self.bind("<Control-Shift-Motion>",
                  lambda e, s=self: s.MouseMoveEvent(e, 0, 1))

        # Left Button
        self.bind("<ButtonPress-1>",
                  lambda e, s=self: s.LeftButtonPressEvent(e, 0, 0))
        self.bind("<Control-ButtonPress-1>",
                  lambda e, s=self: s.LeftButtonPressEvent(e, 1, 0))
        self.bind("<Shift-ButtonPress-1>",
                  lambda e, s=self: s.LeftButtonPressEvent(e, 0, 1))
        self.bind("<Control-Shift-ButtonPress-1>",
                  lambda e, s=self: s.LeftButtonReleaseEvent(e, 1, 1))
        self.bind("<ButtonRelease-1>",
                  lambda e, s=self: s.LeftButtonReleaseEvent(e, 0, 0))
        self.bind("<Control-ButtonRelease-1>",
                  lambda e, s=self: s.LeftButtonReleaseEvent(e, 1, 0))
        self.bind("<Shift-ButtonRelease-1>",
                  lambda e, s=self: s.LeftButtonReleaseEvent(e, 0, 1))
        self.bind("<Control-Shift-ButtonRelease-1>",
                  lambda e, s=self: s.LeftButtonReleaseEvent(e, 1, 1))

        # Middle Button
        self.bind("<ButtonPress-2>",
                  lambda e, s=self: s.MiddleButtonPressEvent(e, 0, 0))
        self.bind("<Control-ButtonPress-2>",
                  lambda e, s=self: s.MiddleButtonPressEvent(e, 1, 0))
        self.bind("<Shift-ButtonPress-2>",
                  lambda e, s=self: s.MiddleButtonPressEvent(e, 0, 1))
        self.bind("<Control-Shift-ButtonPress-2>",
                  lambda e, s=self: s.MiddleButtonReleaseEvent(e, 1, 1))
        self.bind("<ButtonRelease-2>",
                  lambda e, s=self: s.MiddleButtonReleaseEvent(e, 0, 0))
        self.bind("<Control-ButtonRelease-2>",
                  lambda e, s=self: s.MiddleButtonReleaseEvent(e, 1, 0))
        self.bind("<Shift-ButtonRelease-2>",
                  lambda e, s=self: s.MiddleButtonReleaseEvent(e, 0, 1))
        self.bind("<Control-Shift-ButtonRelease-2>",
                  lambda e, s=self: s.MiddleButtonReleaseEvent(e, 1, 1))
        
        # Right Button
        self.bind("<ButtonPress-3>",
                  lambda e, s=self: s.RightButtonPressEvent(e, 0, 0))
        self.bind("<Control-ButtonPress-3>",
                  lambda e, s=self: s.RightButtonPressEvent(e, 1, 0))
        self.bind("<Shift-ButtonPress-3>",
                  lambda e, s=self: s.RightButtonPressEvent(e, 0, 1))
        self.bind("<Control-Shift-ButtonPress-3>",
                  lambda e, s=self: s.RightButtonReleaseEvent(e, 1, 1))
        self.bind("<ButtonRelease-3>",
                  lambda e, s=self: s.RightButtonReleaseEvent(e, 0, 0))
        self.bind("<Control-ButtonRelease-3>",
                  lambda e, s=self: s.RightButtonReleaseEvent(e, 1, 0))
        self.bind("<Shift-ButtonRelease-3>",
                  lambda e, s=self: s.RightButtonReleaseEvent(e, 0, 1))
        self.bind("<Control-Shift-ButtonRelease-3>",
                  lambda e, s=self: s.RightButtonReleaseEvent(e, 1, 1))

        # Key related events
        self.bind("<KeyPress>", 
                  lambda e, s=self: s.KeyPressEvent(e, 0, 0))
        self.bind("<Control-KeyPress>", 
                  lambda e, s=self: s.KeyPressEvent(e, 1, 0))
        self.bind("<Shift-KeyPress>", 
                  lambda e, s=self: s.KeyPressEvent(e, 0, 1))
        self.bind("<Control-Shift-KeyPress>", 
                  lambda e, s=self: s.KeyPressEvent(e, 1, 1))
        
        self.bind("<KeyRelease>", 
                  lambda e, s=self: s.KeyReleaseEvent(e, 0, 0))
        self.bind("<Control-KeyRelease>", 
                  lambda e, s=self: s.KeyReleaseEvent(e, 1, 0))
        self.bind("<Shift-KeyRelease>", 
                  lambda e, s=self: s.KeyReleaseEvent(e, 0, 1))
        self.bind("<Control-Shift-KeyRelease>", 
                  lambda e, s=self: s.KeyReleaseEvent(e, 1, 1))

        self.bind("<Enter>",
                  lambda e, s=self: s.EnterEvent(e, 0, 0))
        self.bind("<Control-Enter>",
                  lambda e, s=self: s.EnterEvent(e, 1, 0))
        self.bind("<Shift-Enter>",
                  lambda e, s=self: s.EnterEvent(e, 0, 1))
        self.bind("<Control-Shift-Enter>",
                  lambda e, s=self: s.EnterEvent(e, 1, 1))
        self.bind("<Leave>",
                  lambda e, s=self: s.LeaveEvent(e, 0, 0))
        self.bind("<Control-Leave>",
                  lambda e, s=self: s.LeaveEvent(e, 1, 0))
        self.bind("<Shift-Leave>",
                  lambda e, s=self: s.LeaveEvent(e, 0, 1))
        self.bind("<Control-Shift-Leave>",
                  lambda e, s=self: s.LeaveEvent(e, 1, 1))


        self.bind("<Configure>", self.ConfigureEvent)
        self.bind("<Expose>",lambda e,s=self: s.ExposeEvent())

    def CreateTimer(self, obj, evt):
        self.after(10, self._Iren.TimerEvent)

    def DestroyTimer(self, obj, event):
        """The timer is a one shot timer so will expire automatically."""
        return 1

    def MouseMoveEvent(self, event, ctrl, shift):
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
                                            shift, chr(0), 0, None)
        self._Iren.MouseMoveEvent()

    def LeftButtonPressEvent(self, event, ctrl, shift):
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
                                            shift, chr(0), 0, None)
        self._Iren.LeftButtonPressEvent()

    def LeftButtonReleaseEvent(self, event, ctrl, shift):
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
                                            shift, chr(0), 0, None)
        self._Iren.LeftButtonReleaseEvent()

    def MiddleButtonPressEvent(self, event, ctrl, shift):
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
                                            shift, chr(0), 0, None)
        self._Iren.MiddleButtonPressEvent()

    def MiddleButtonReleaseEvent(self, event, ctrl, shift):
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
                                            shift, chr(0), 0, None)
        self._Iren.MiddleButtonReleaseEvent()
        
    def RightButtonPressEvent(self, event, ctrl, shift):
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
                                            shift, chr(0), 0, None)
        self._Iren.RightButtonPressEvent()

    def RightButtonReleaseEvent(self, event, ctrl, shift):
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
                                            shift, chr(0), 0, None)
        self._Iren.RightButtonReleaseEvent()

    def KeyPressEvent(self, event, ctrl, shift):
        key = chr(0)
        if event.keysym_num < 256:
            key = chr(event.keysym_num)
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
                                            shift, key, 0, event.keysym)
        self._Iren.KeyPressEvent()
        self._Iren.CharEvent()

    def KeyReleaseEvent(self, event, ctrl, shift):
        key = chr(0)
        if event.keysym_num < 256:
            key = chr(event.keysym_num)
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl,
                                            shift, key, 0, event.keysym)
        self._Iren.KeyReleaseEvent()

    def ConfigureEvent(self, event):
        self._Iren.SetSize(event.width, event.height)
        self._Iren.ConfigureEvent()

    def EnterEvent(self, event, ctrl, shift):
        self._OldFocus=self.focus_get()
        self.focus()
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl, shift,
                                            chr(0), 0, None)
        self._Iren.EnterEvent()

    def LeaveEvent(self, event, ctrl, shift):
        if (self._OldFocus != None):
            self._OldFocus.focus()
        self._Iren.SetEventInformationFlipY(event.x, event.y, ctrl, shift,
                                            chr(0), 0, None)
        self._Iren.LeaveEvent()

    def ExposeEvent(self):
        if (not self.__InExpose):
            self.__InExpose = 1
            self.update()
            self._RenderWindow.Render()
            self.__InExpose = 0

    def GetRenderWindow(self):
        addr = self.tk.call(self._w, 'GetRenderWindow')[5:]
        return vtk.vtkRenderWindow('_%s_vtkRenderWindow_p' % addr)

    def Render(self):
        self._RenderWindow.Render()


#----------------------------------------------------------------------------  
def vtkRenderWindowInteractorConeExample():
    """Like it says, just a simple example
    """
    # create root window
    root = Tkinter.Tk()

    # create vtkTkRenderWidget
    pane = vtkTkRenderWindowInteractor(root, width=300, height=300)
    pane.Initialize()

    def quit(obj=root):
        obj.quit()

    pane.AddObserver("ExitEvent", lambda o,e,q=quit: q())

    ren = vtk.vtkRenderer()
    pane.GetRenderWindow().AddRenderer(ren)

    cone = vtk.vtkConeSource()
    cone.SetResolution(8)
    
    coneMapper = vtk.vtkPolyDataMapper()
    coneMapper.SetInput(cone.GetOutput())
    
    coneActor = vtk.vtkActor()
    coneActor.SetMapper(coneMapper)

    ren.AddActor(coneActor)

    # pack the pane into the tk root
    pane.pack(fill='both', expand=1)
    pane.Start()

    # start the tk mainloop
    root.mainloop()
    
if __name__ == "__main__":
    vtkRenderWindowInteractorConeExample()

