QVTKRenderWindowInteractor.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. # coding=utf-8
  2. """
  3. A simple VTK widget for PyQt or PySide.
  4. See http://www.trolltech.com for Qt documentation,
  5. http://www.riverbankcomputing.co.uk for PyQt, and
  6. http://pyside.github.io for PySide.
  7. This class is based on the vtkGenericRenderWindowInteractor and is
  8. therefore fairly powerful. It should also play nicely with the
  9. vtk3DWidget code.
  10. Created by Prabhu Ramachandran, May 2002
  11. Based on David Gobbi's QVTKRenderWidget.py
  12. Changes by Gerard Vermeulen Feb. 2003
  13. Win32 support.
  14. Changes by Gerard Vermeulen, May 2003
  15. Bug fixes and better integration with the Qt framework.
  16. Changes by Phil Thompson, Nov. 2006
  17. Ported to PyQt v4.
  18. Added support for wheel events.
  19. Changes by Phil Thompson, Oct. 2007
  20. Bug fixes.
  21. Changes by Phil Thompson, Mar. 2008
  22. Added cursor support.
  23. Changes by Rodrigo Mologni, Sep. 2013 (Credit to Daniele Esposti)
  24. Bug fix to PySide: Converts PyCObject to void pointer.
  25. Changes by Greg Schussman, Aug. 2014
  26. The keyPressEvent function now passes keysym instead of None.
  27. Changes by Alex Tsui, Apr. 2015
  28. Port from PyQt4 to PyQt5.
  29. Changes by Fabian Wenzel, Jan. 2016
  30. Support for Python3
  31. Changes by Tobias Hänel, Sep. 2018
  32. Support for PySide2
  33. Changes by Ruben de Bruin, Aug. 2019
  34. Fixes to the keyPressEvent function
  35. Changes by Chen Jintao, Aug. 2021
  36. Support for PySide6
  37. Changes by Eric Larson and Guillaume Favelier, Apr. 2022
  38. Support for PyQt6
  39. """
  40. # Check whether a specific PyQt implementation was chosen
  41. try:
  42. import vtkmodules.qt
  43. PyQtImpl = vtkmodules.qt.PyQtImpl
  44. except ImportError:
  45. pass
  46. # Check whether a specific QVTKRenderWindowInteractor base
  47. # class was chosen, can be set to "QGLWidget" in
  48. # PyQt implementation version lower than Qt6,
  49. # or "QOpenGLWidget" in Pyside6 and PyQt6
  50. QVTKRWIBase = "QWidget"
  51. try:
  52. import vtkmodules.qt
  53. QVTKRWIBase = vtkmodules.qt.QVTKRWIBase
  54. except ImportError:
  55. pass
  56. from vtkmodules.vtkRenderingCore import vtkRenderWindow
  57. from vtkmodules.vtkRenderingUI import vtkGenericRenderWindowInteractor
  58. if PyQtImpl is None:
  59. # Autodetect the PyQt implementation to use
  60. try:
  61. import PySide6.QtCore
  62. PyQtImpl = "PySide6"
  63. except ImportError:
  64. try:
  65. import PyQt6.QtCore
  66. PyQtImpl = "PyQt6"
  67. except ImportError:
  68. try:
  69. import PyQt5.QtCore
  70. PyQtImpl = "PyQt5"
  71. except ImportError:
  72. try:
  73. import PySide2.QtCore
  74. PyQtImpl = "PySide2"
  75. except ImportError:
  76. try:
  77. import PyQt4.QtCore
  78. PyQtImpl = "PyQt4"
  79. except ImportError:
  80. try:
  81. import PySide.QtCore
  82. PyQtImpl = "PySide"
  83. except ImportError:
  84. raise ImportError("Cannot load either PyQt or PySide")
  85. # Check the compatibility of PyQtImpl and QVTKRWIBase
  86. if QVTKRWIBase != "QWidget":
  87. if PyQtImpl in ["PySide6", "PyQt6"] and QVTKRWIBase == "QOpenGLWidget":
  88. pass # compatible
  89. elif PyQtImpl in ["PyQt5", "PySide2","PyQt4", "PySide"] and QVTKRWIBase == "QGLWidget":
  90. pass # compatible
  91. else:
  92. raise ImportError("Cannot load " + QVTKRWIBase + " from " + PyQtImpl)
  93. if PyQtImpl == "PySide6":
  94. if QVTKRWIBase == "QOpenGLWidget":
  95. from PySide6.QtOpenGLWidgets import QOpenGLWidget
  96. from PySide6.QtWidgets import QWidget
  97. from PySide6.QtWidgets import QSizePolicy
  98. from PySide6.QtWidgets import QApplication
  99. from PySide6.QtWidgets import QMainWindow
  100. from PySide6.QtGui import QCursor
  101. from PySide6.QtCore import Qt
  102. from PySide6.QtCore import QTimer
  103. from PySide6.QtCore import QObject
  104. from PySide6.QtCore import QSize
  105. from PySide6.QtCore import QEvent
  106. elif PyQtImpl == "PyQt6":
  107. if QVTKRWIBase == "QOpenGLWidget":
  108. from PyQt6.QtOpenGLWidgets import QOpenGLWidget
  109. from PyQt6.QtWidgets import QWidget
  110. from PyQt6.QtWidgets import QSizePolicy
  111. from PyQt6.QtWidgets import QApplication
  112. from PyQt6.QtWidgets import QMainWindow
  113. from PyQt6.QtGui import QCursor
  114. from PyQt6.QtCore import Qt
  115. from PyQt6.QtCore import QTimer
  116. from PyQt6.QtCore import QObject
  117. from PyQt6.QtCore import QSize
  118. from PyQt6.QtCore import QEvent
  119. elif PyQtImpl == "PyQt5":
  120. if QVTKRWIBase == "QGLWidget":
  121. from PyQt5.QtOpenGL import QGLWidget
  122. from PyQt5.QtWidgets import QWidget
  123. from PyQt5.QtWidgets import QSizePolicy
  124. from PyQt5.QtWidgets import QApplication
  125. from PyQt5.QtWidgets import QMainWindow
  126. from PyQt5.QtGui import QCursor
  127. from PyQt5.QtCore import Qt
  128. from PyQt5.QtCore import QTimer
  129. from PyQt5.QtCore import QObject
  130. from PyQt5.QtCore import QSize
  131. from PyQt5.QtCore import QEvent
  132. elif PyQtImpl == "PySide2":
  133. if QVTKRWIBase == "QGLWidget":
  134. from PySide2.QtOpenGL import QGLWidget
  135. from PySide2.QtWidgets import QWidget
  136. from PySide2.QtWidgets import QSizePolicy
  137. from PySide2.QtWidgets import QApplication
  138. from PySide2.QtWidgets import QMainWindow
  139. from PySide2.QtGui import QCursor
  140. from PySide2.QtCore import Qt
  141. from PySide2.QtCore import QTimer
  142. from PySide2.QtCore import QObject
  143. from PySide2.QtCore import QSize
  144. from PySide2.QtCore import QEvent
  145. elif PyQtImpl == "PyQt4":
  146. if QVTKRWIBase == "QGLWidget":
  147. from PyQt4.QtOpenGL import QGLWidget
  148. from PyQt4.QtGui import QWidget
  149. from PyQt4.QtGui import QSizePolicy
  150. from PyQt4.QtGui import QApplication
  151. from PyQt4.QtGui import QMainWindow
  152. from PyQt4.QtCore import Qt
  153. from PyQt4.QtCore import QTimer
  154. from PyQt4.QtCore import QObject
  155. from PyQt4.QtCore import QSize
  156. from PyQt4.QtCore import QEvent
  157. elif PyQtImpl == "PySide":
  158. if QVTKRWIBase == "QGLWidget":
  159. from PySide.QtOpenGL import QGLWidget
  160. from PySide.QtGui import QWidget
  161. from PySide.QtGui import QSizePolicy
  162. from PySide.QtGui import QApplication
  163. from PySide.QtGui import QMainWindow
  164. from PySide.QtCore import Qt
  165. from PySide.QtCore import QTimer
  166. from PySide.QtCore import QObject
  167. from PySide.QtCore import QSize
  168. from PySide.QtCore import QEvent
  169. else:
  170. raise ImportError("Unknown PyQt implementation " + repr(PyQtImpl))
  171. # Define types for base class, based on string
  172. if QVTKRWIBase == "QWidget":
  173. QVTKRWIBaseClass = QWidget
  174. elif QVTKRWIBase == "QGLWidget":
  175. QVTKRWIBaseClass = QGLWidget
  176. elif QVTKRWIBase == "QOpenGLWidget":
  177. QVTKRWIBaseClass = QOpenGLWidget
  178. else:
  179. raise ImportError("Unknown base class for QVTKRenderWindowInteractor " + QVTKRWIBase)
  180. if PyQtImpl == 'PyQt6':
  181. CursorShape = Qt.CursorShape
  182. MouseButton = Qt.MouseButton
  183. WindowType = Qt.WindowType
  184. WidgetAttribute = Qt.WidgetAttribute
  185. KeyboardModifier = Qt.KeyboardModifier
  186. FocusPolicy = Qt.FocusPolicy
  187. ConnectionType = Qt.ConnectionType
  188. Key = Qt.Key
  189. SizePolicy = QSizePolicy.Policy
  190. EventType = QEvent.Type
  191. else:
  192. CursorShape = MouseButton = WindowType = WidgetAttribute = \
  193. KeyboardModifier = FocusPolicy = ConnectionType = Key = Qt
  194. SizePolicy = QSizePolicy
  195. EventType = QEvent
  196. if PyQtImpl in ('PyQt4', 'PySide'):
  197. MiddleButton = MouseButton.MidButton
  198. else:
  199. MiddleButton = MouseButton.MiddleButton
  200. def _get_event_pos(ev):
  201. try: # Qt6+
  202. return ev.position().x(), ev.position().y()
  203. except AttributeError: # Qt5
  204. return ev.x(), ev.y()
  205. class QVTKRenderWindowInteractor(QVTKRWIBaseClass):
  206. """ A QVTKRenderWindowInteractor for Python and Qt. Uses a
  207. vtkGenericRenderWindowInteractor to handle the interactions. Use
  208. GetRenderWindow() to get the vtkRenderWindow. Create with the
  209. keyword stereo=1 in order to generate a stereo-capable window.
  210. The user interface is summarized in vtkInteractorStyle.h:
  211. - Keypress j / Keypress t: toggle between joystick (position
  212. sensitive) and trackball (motion sensitive) styles. In joystick
  213. style, motion occurs continuously as long as a mouse button is
  214. pressed. In trackball style, motion occurs when the mouse button
  215. is pressed and the mouse pointer moves.
  216. - Keypress c / Keypress o: toggle between camera and object
  217. (actor) modes. In camera mode, mouse events affect the camera
  218. position and focal point. In object mode, mouse events affect
  219. the actor that is under the mouse pointer.
  220. - Button 1: rotate the camera around its focal point (if camera
  221. mode) or rotate the actor around its origin (if actor mode). The
  222. rotation is in the direction defined from the center of the
  223. renderer's viewport towards the mouse position. In joystick mode,
  224. the magnitude of the rotation is determined by the distance the
  225. mouse is from the center of the render window.
  226. - Button 2: pan the camera (if camera mode) or translate the actor
  227. (if object mode). In joystick mode, the direction of pan or
  228. translation is from the center of the viewport towards the mouse
  229. position. In trackball mode, the direction of motion is the
  230. direction the mouse moves. (Note: with 2-button mice, pan is
  231. defined as <Shift>-Button 1.)
  232. - Button 3: zoom the camera (if camera mode) or scale the actor
  233. (if object mode). Zoom in/increase scale if the mouse position is
  234. in the top half of the viewport; zoom out/decrease scale if the
  235. mouse position is in the bottom half. In joystick mode, the amount
  236. of zoom is controlled by the distance of the mouse pointer from
  237. the horizontal centerline of the window.
  238. - Keypress 3: toggle the render window into and out of stereo
  239. mode. By default, red-blue stereo pairs are created. Some systems
  240. support Crystal Eyes LCD stereo glasses; you have to invoke
  241. SetStereoTypeToCrystalEyes() on the rendering window. Note: to
  242. use stereo you also need to pass a stereo=1 keyword argument to
  243. the constructor.
  244. - Keypress e: exit the application.
  245. - Keypress f: fly to the picked point
  246. - Keypress p: perform a pick operation. The render window interactor
  247. has an internal instance of vtkCellPicker that it uses to pick.
  248. - Keypress r: reset the camera view along the current view
  249. direction. Centers the actors and moves the camera so that all actors
  250. are visible.
  251. - Keypress s: modify the representation of all actors so that they
  252. are surfaces.
  253. - Keypress u: invoke the user-defined function. Typically, this
  254. keypress will bring up an interactor that you can type commands in.
  255. - Keypress w: modify the representation of all actors so that they
  256. are wireframe.
  257. """
  258. # Map between VTK and Qt cursors.
  259. _CURSOR_MAP = {
  260. 0: CursorShape.ArrowCursor, # VTK_CURSOR_DEFAULT
  261. 1: CursorShape.ArrowCursor, # VTK_CURSOR_ARROW
  262. 2: CursorShape.SizeBDiagCursor, # VTK_CURSOR_SIZENE
  263. 3: CursorShape.SizeFDiagCursor, # VTK_CURSOR_SIZENWSE
  264. 4: CursorShape.SizeBDiagCursor, # VTK_CURSOR_SIZESW
  265. 5: CursorShape.SizeFDiagCursor, # VTK_CURSOR_SIZESE
  266. 6: CursorShape.SizeVerCursor, # VTK_CURSOR_SIZENS
  267. 7: CursorShape.SizeHorCursor, # VTK_CURSOR_SIZEWE
  268. 8: CursorShape.SizeAllCursor, # VTK_CURSOR_SIZEALL
  269. 9: CursorShape.PointingHandCursor, # VTK_CURSOR_HAND
  270. 10: CursorShape.CrossCursor, # VTK_CURSOR_CROSSHAIR
  271. }
  272. def __init__(self, parent=None, **kw):
  273. # the current button
  274. self._ActiveButton = MouseButton.NoButton
  275. # private attributes
  276. self.__saveX = 0
  277. self.__saveY = 0
  278. self.__saveModifiers = KeyboardModifier.NoModifier
  279. self.__saveButtons = MouseButton.NoButton
  280. self.__wheelDelta = 0
  281. # do special handling of some keywords:
  282. # stereo, rw
  283. try:
  284. stereo = bool(kw['stereo'])
  285. except KeyError:
  286. stereo = False
  287. try:
  288. rw = kw['rw']
  289. except KeyError:
  290. rw = None
  291. # create base qt-level widget
  292. if QVTKRWIBase == "QWidget":
  293. if "wflags" in kw:
  294. wflags = kw['wflags']
  295. else:
  296. wflags = WindowType.Widget # what Qt.WindowFlags() returns (0)
  297. QWidget.__init__(self, parent, wflags | WindowType.MSWindowsOwnDC)
  298. elif QVTKRWIBase == "QGLWidget":
  299. QGLWidget.__init__(self, parent)
  300. elif QVTKRWIBase == "QOpenGLWidget":
  301. QOpenGLWidget.__init__(self, parent)
  302. if rw: # user-supplied render window
  303. self._RenderWindow = rw
  304. else:
  305. self._RenderWindow = vtkRenderWindow()
  306. WId = self.winId()
  307. # Python2
  308. if type(WId).__name__ == 'PyCObject':
  309. from ctypes import pythonapi, c_void_p, py_object
  310. pythonapi.PyCObject_AsVoidPtr.restype = c_void_p
  311. pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object]
  312. WId = pythonapi.PyCObject_AsVoidPtr(WId)
  313. # Python3
  314. elif type(WId).__name__ == 'PyCapsule':
  315. from ctypes import pythonapi, c_void_p, py_object, c_char_p
  316. pythonapi.PyCapsule_GetName.restype = c_char_p
  317. pythonapi.PyCapsule_GetName.argtypes = [py_object]
  318. name = pythonapi.PyCapsule_GetName(WId)
  319. pythonapi.PyCapsule_GetPointer.restype = c_void_p
  320. pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p]
  321. WId = pythonapi.PyCapsule_GetPointer(WId, name)
  322. self._RenderWindow.SetWindowInfo(str(int(WId)))
  323. if stereo: # stereo mode
  324. self._RenderWindow.StereoCapableWindowOn()
  325. self._RenderWindow.SetStereoTypeToCrystalEyes()
  326. try:
  327. self._Iren = kw['iren']
  328. except KeyError:
  329. self._Iren = vtkGenericRenderWindowInteractor()
  330. self._Iren.SetRenderWindow(self._RenderWindow)
  331. # do all the necessary qt setup
  332. self.setAttribute(WidgetAttribute.WA_OpaquePaintEvent)
  333. self.setAttribute(WidgetAttribute.WA_PaintOnScreen)
  334. self.setMouseTracking(True) # get all mouse events
  335. self.setFocusPolicy(FocusPolicy.WheelFocus)
  336. self.setSizePolicy(QSizePolicy(SizePolicy.Expanding, SizePolicy.Expanding))
  337. self._Timer = QTimer(self)
  338. self._Timer.timeout.connect(self.TimerEvent)
  339. self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
  340. self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)
  341. self._Iren.GetRenderWindow().AddObserver('CursorChangedEvent',
  342. self.CursorChangedEvent)
  343. # If we've a parent, it does not close the child when closed.
  344. # Connect the parent's destroyed signal to this widget's close
  345. # slot for proper cleanup of VTK objects.
  346. if self.parent():
  347. self.parent().destroyed.connect(self.close, ConnectionType.DirectConnection)
  348. def __getattr__(self, attr):
  349. """Makes the object behave like a vtkGenericRenderWindowInteractor"""
  350. if attr == '__vtk__':
  351. return lambda t=self._Iren: t
  352. elif hasattr(self._Iren, attr):
  353. return getattr(self._Iren, attr)
  354. else:
  355. raise AttributeError(self.__class__.__name__ +
  356. " has no attribute named " + attr)
  357. def Finalize(self):
  358. '''
  359. Call internal cleanup method on VTK objects
  360. '''
  361. self._RenderWindow.Finalize()
  362. def CreateTimer(self, obj, evt):
  363. self._Timer.start(10)
  364. def DestroyTimer(self, obj, evt):
  365. self._Timer.stop()
  366. return 1
  367. def TimerEvent(self):
  368. self._Iren.TimerEvent()
  369. def CursorChangedEvent(self, obj, evt):
  370. """Called when the CursorChangedEvent fires on the render window."""
  371. # This indirection is needed since when the event fires, the current
  372. # cursor is not yet set so we defer this by which time the current
  373. # cursor should have been set.
  374. QTimer.singleShot(0, self.ShowCursor)
  375. def HideCursor(self):
  376. """Hides the cursor."""
  377. self.setCursor(CursorShape.BlankCursor)
  378. def ShowCursor(self):
  379. """Shows the cursor."""
  380. vtk_cursor = self._Iren.GetRenderWindow().GetCurrentCursor()
  381. qt_cursor = self._CURSOR_MAP.get(vtk_cursor, CursorShape.ArrowCursor)
  382. self.setCursor(qt_cursor)
  383. def closeEvent(self, evt):
  384. self.Finalize()
  385. def sizeHint(self):
  386. return QSize(400, 400)
  387. def paintEngine(self):
  388. return None
  389. def paintEvent(self, ev):
  390. self._Iren.Render()
  391. def resizeEvent(self, ev):
  392. scale = self._getPixelRatio()
  393. w = int(round(scale*self.width()))
  394. h = int(round(scale*self.height()))
  395. self._RenderWindow.SetDPI(int(round(72*scale)))
  396. vtkRenderWindow.SetSize(self._RenderWindow, w, h)
  397. self._Iren.SetSize(w, h)
  398. self._Iren.ConfigureEvent()
  399. self.update()
  400. def _GetKeyCharAndKeySym(self, ev):
  401. """ Convert a Qt key into a char and a vtk keysym.
  402. This is essentially copied from the c++ implementation in
  403. GUISupport/Qt/QVTKInteractorAdapter.cxx.
  404. """
  405. # if there is a char, convert its ASCII code to a VTK keysym
  406. try:
  407. keyChar = ev.text()[0]
  408. keySym = _keysyms_for_ascii[ord(keyChar)]
  409. except IndexError:
  410. keyChar = '\0'
  411. keySym = None
  412. # next, try converting Qt key code to a VTK keysym
  413. if keySym is None:
  414. try:
  415. keySym = _keysyms[ev.key()]
  416. except KeyError:
  417. keySym = None
  418. # use "None" as a fallback
  419. if keySym is None:
  420. keySym = "None"
  421. return keyChar, keySym
  422. def _GetCtrlShift(self, ev):
  423. ctrl = shift = False
  424. if hasattr(ev, 'modifiers'):
  425. if ev.modifiers() & KeyboardModifier.ShiftModifier:
  426. shift = True
  427. if ev.modifiers() & KeyboardModifier.ControlModifier:
  428. ctrl = True
  429. else:
  430. if self.__saveModifiers & KeyboardModifier.ShiftModifier:
  431. shift = True
  432. if self.__saveModifiers & KeyboardModifier.ControlModifier:
  433. ctrl = True
  434. return ctrl, shift
  435. @staticmethod
  436. def _getPixelRatio():
  437. if PyQtImpl in ["PyQt5", "PySide2", "PySide6", "PyQt6"]:
  438. # Source: https://stackoverflow.com/a/40053864/3388962
  439. pos = QCursor.pos()
  440. for screen in QApplication.screens():
  441. rect = screen.geometry()
  442. if rect.contains(pos):
  443. return screen.devicePixelRatio()
  444. # Should never happen, but try to find a good fallback.
  445. return QApplication.instance().devicePixelRatio()
  446. else:
  447. # Qt4 seems not to provide any cross-platform means to get the
  448. # pixel ratio.
  449. return 1.
  450. def _setEventInformation(self, x, y, ctrl, shift,
  451. key, repeat=0, keysum=None):
  452. scale = self._getPixelRatio()
  453. self._Iren.SetEventInformation(int(round(x*scale)),
  454. int(round((self.height()-y-1)*scale)),
  455. ctrl, shift, key, repeat, keysum)
  456. def enterEvent(self, ev):
  457. ctrl, shift = self._GetCtrlShift(ev)
  458. self._setEventInformation(self.__saveX, self.__saveY,
  459. ctrl, shift, chr(0), 0, None)
  460. self._Iren.EnterEvent()
  461. def leaveEvent(self, ev):
  462. ctrl, shift = self._GetCtrlShift(ev)
  463. self._setEventInformation(self.__saveX, self.__saveY,
  464. ctrl, shift, chr(0), 0, None)
  465. self._Iren.LeaveEvent()
  466. def mousePressEvent(self, ev):
  467. ctrl, shift = self._GetCtrlShift(ev)
  468. repeat = 0
  469. if ev.type() == EventType.MouseButtonDblClick:
  470. repeat = 1
  471. x, y = _get_event_pos(ev)
  472. self._setEventInformation(x, y,
  473. ctrl, shift, chr(0), repeat, None)
  474. self._ActiveButton = ev.button()
  475. if self._ActiveButton == MouseButton.LeftButton:
  476. self._Iren.LeftButtonPressEvent()
  477. elif self._ActiveButton == MouseButton.RightButton:
  478. self._Iren.RightButtonPressEvent()
  479. elif self._ActiveButton == MiddleButton:
  480. self._Iren.MiddleButtonPressEvent()
  481. def mouseReleaseEvent(self, ev):
  482. ctrl, shift = self._GetCtrlShift(ev)
  483. x, y = _get_event_pos(ev)
  484. self._setEventInformation(x, y,
  485. ctrl, shift, chr(0), 0, None)
  486. if self._ActiveButton == MouseButton.LeftButton:
  487. self._Iren.LeftButtonReleaseEvent()
  488. elif self._ActiveButton == MouseButton.RightButton:
  489. self._Iren.RightButtonReleaseEvent()
  490. elif self._ActiveButton == MiddleButton:
  491. self._Iren.MiddleButtonReleaseEvent()
  492. def mouseMoveEvent(self, ev):
  493. self.__saveModifiers = ev.modifiers()
  494. self.__saveButtons = ev.buttons()
  495. x, y = _get_event_pos(ev)
  496. self.__saveX = x
  497. self.__saveY = y
  498. ctrl, shift = self._GetCtrlShift(ev)
  499. self._setEventInformation(x, y,
  500. ctrl, shift, chr(0), 0, None)
  501. self._Iren.MouseMoveEvent()
  502. def keyPressEvent(self, ev):
  503. key, keySym = self._GetKeyCharAndKeySym(ev)
  504. ctrl, shift = self._GetCtrlShift(ev)
  505. self._setEventInformation(self.__saveX, self.__saveY,
  506. ctrl, shift, key, 0, keySym)
  507. self._Iren.KeyPressEvent()
  508. self._Iren.CharEvent()
  509. def keyReleaseEvent(self, ev):
  510. key, keySym = self._GetKeyCharAndKeySym(ev)
  511. ctrl, shift = self._GetCtrlShift(ev)
  512. self._setEventInformation(self.__saveX, self.__saveY,
  513. ctrl, shift, key, 0, keySym)
  514. self._Iren.KeyReleaseEvent()
  515. def wheelEvent(self, ev):
  516. if hasattr(ev, 'delta'):
  517. self.__wheelDelta += ev.delta()
  518. else:
  519. self.__wheelDelta += ev.angleDelta().y()
  520. if self.__wheelDelta >= 120:
  521. self._Iren.MouseWheelForwardEvent()
  522. self.__wheelDelta = 0
  523. elif self.__wheelDelta <= -120:
  524. self._Iren.MouseWheelBackwardEvent()
  525. self.__wheelDelta = 0
  526. def GetRenderWindow(self):
  527. return self._RenderWindow
  528. def Render(self):
  529. self.update()
  530. def QVTKRenderWidgetConeExample():
  531. """A simple example that uses the QVTKRenderWindowInteractor class."""
  532. from vtkmodules.vtkFiltersSources import vtkConeSource
  533. from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
  534. # load implementations for rendering and interaction factory classes
  535. import vtkmodules.vtkRenderingOpenGL2
  536. import vtkmodules.vtkInteractionStyle
  537. # every QT app needs an app
  538. app = QApplication(['QVTKRenderWindowInteractor'])
  539. window = QMainWindow()
  540. # create the widget
  541. widget = QVTKRenderWindowInteractor(window)
  542. window.setCentralWidget(widget)
  543. # if you don't want the 'q' key to exit comment this.
  544. widget.AddObserver("ExitEvent", lambda o, e, a=app: a.quit())
  545. ren = vtkRenderer()
  546. widget.GetRenderWindow().AddRenderer(ren)
  547. cone = vtkConeSource()
  548. cone.SetResolution(8)
  549. coneMapper = vtkPolyDataMapper()
  550. coneMapper.SetInputConnection(cone.GetOutputPort())
  551. coneActor = vtkActor()
  552. coneActor.SetMapper(coneMapper)
  553. ren.AddActor(coneActor)
  554. # show the widget
  555. window.show()
  556. widget.Initialize()
  557. widget.Start()
  558. # start event processing
  559. # Source: https://doc.qt.io/qtforpython/porting_from2.html
  560. # 'exec_' is deprecated and will be removed in the future.
  561. # Use 'exec' instead.
  562. try:
  563. app.exec()
  564. except AttributeError:
  565. app.exec_()
  566. _keysyms_for_ascii = (
  567. None, None, None, None, None, None, None, None,
  568. None, "Tab", None, None, None, None, None, None,
  569. None, None, None, None, None, None, None, None,
  570. None, None, None, None, None, None, None, None,
  571. "space", "exclam", "quotedbl", "numbersign",
  572. "dollar", "percent", "ampersand", "quoteright",
  573. "parenleft", "parenright", "asterisk", "plus",
  574. "comma", "minus", "period", "slash",
  575. "0", "1", "2", "3", "4", "5", "6", "7",
  576. "8", "9", "colon", "semicolon", "less", "equal", "greater", "question",
  577. "at", "A", "B", "C", "D", "E", "F", "G",
  578. "H", "I", "J", "K", "L", "M", "N", "O",
  579. "P", "Q", "R", "S", "T", "U", "V", "W",
  580. "X", "Y", "Z", "bracketleft",
  581. "backslash", "bracketright", "asciicircum", "underscore",
  582. "quoteleft", "a", "b", "c", "d", "e", "f", "g",
  583. "h", "i", "j", "k", "l", "m", "n", "o",
  584. "p", "q", "r", "s", "t", "u", "v", "w",
  585. "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "Delete",
  586. )
  587. _keysyms = {
  588. Key.Key_Backspace: 'BackSpace',
  589. Key.Key_Tab: 'Tab',
  590. Key.Key_Backtab: 'Tab',
  591. # Key.Key_Clear : 'Clear',
  592. Key.Key_Return: 'Return',
  593. Key.Key_Enter: 'Return',
  594. Key.Key_Shift: 'Shift_L',
  595. Key.Key_Control: 'Control_L',
  596. Key.Key_Alt: 'Alt_L',
  597. Key.Key_Pause: 'Pause',
  598. Key.Key_CapsLock: 'Caps_Lock',
  599. Key.Key_Escape: 'Escape',
  600. Key.Key_Space: 'space',
  601. # Key.Key_Prior : 'Prior',
  602. # Key.Key_Next : 'Next',
  603. Key.Key_End: 'End',
  604. Key.Key_Home: 'Home',
  605. Key.Key_Left: 'Left',
  606. Key.Key_Up: 'Up',
  607. Key.Key_Right: 'Right',
  608. Key.Key_Down: 'Down',
  609. Key.Key_SysReq: 'Snapshot',
  610. Key.Key_Insert: 'Insert',
  611. Key.Key_Delete: 'Delete',
  612. Key.Key_Help: 'Help',
  613. Key.Key_0: '0',
  614. Key.Key_1: '1',
  615. Key.Key_2: '2',
  616. Key.Key_3: '3',
  617. Key.Key_4: '4',
  618. Key.Key_5: '5',
  619. Key.Key_6: '6',
  620. Key.Key_7: '7',
  621. Key.Key_8: '8',
  622. Key.Key_9: '9',
  623. Key.Key_A: 'a',
  624. Key.Key_B: 'b',
  625. Key.Key_C: 'c',
  626. Key.Key_D: 'd',
  627. Key.Key_E: 'e',
  628. Key.Key_F: 'f',
  629. Key.Key_G: 'g',
  630. Key.Key_H: 'h',
  631. Key.Key_I: 'i',
  632. Key.Key_J: 'j',
  633. Key.Key_K: 'k',
  634. Key.Key_L: 'l',
  635. Key.Key_M: 'm',
  636. Key.Key_N: 'n',
  637. Key.Key_O: 'o',
  638. Key.Key_P: 'p',
  639. Key.Key_Q: 'q',
  640. Key.Key_R: 'r',
  641. Key.Key_S: 's',
  642. Key.Key_T: 't',
  643. Key.Key_U: 'u',
  644. Key.Key_V: 'v',
  645. Key.Key_W: 'w',
  646. Key.Key_X: 'x',
  647. Key.Key_Y: 'y',
  648. Key.Key_Z: 'z',
  649. Key.Key_Asterisk: 'asterisk',
  650. Key.Key_Plus: 'plus',
  651. Key.Key_Minus: 'minus',
  652. Key.Key_Period: 'period',
  653. Key.Key_Slash: 'slash',
  654. Key.Key_F1: 'F1',
  655. Key.Key_F2: 'F2',
  656. Key.Key_F3: 'F3',
  657. Key.Key_F4: 'F4',
  658. Key.Key_F5: 'F5',
  659. Key.Key_F6: 'F6',
  660. Key.Key_F7: 'F7',
  661. Key.Key_F8: 'F8',
  662. Key.Key_F9: 'F9',
  663. Key.Key_F10: 'F10',
  664. Key.Key_F11: 'F11',
  665. Key.Key_F12: 'F12',
  666. Key.Key_F13: 'F13',
  667. Key.Key_F14: 'F14',
  668. Key.Key_F15: 'F15',
  669. Key.Key_F16: 'F16',
  670. Key.Key_F17: 'F17',
  671. Key.Key_F18: 'F18',
  672. Key.Key_F19: 'F19',
  673. Key.Key_F20: 'F20',
  674. Key.Key_F21: 'F21',
  675. Key.Key_F22: 'F22',
  676. Key.Key_F23: 'F23',
  677. Key.Key_F24: 'F24',
  678. Key.Key_NumLock: 'Num_Lock',
  679. Key.Key_ScrollLock: 'Scroll_Lock',
  680. }
  681. if __name__ == "__main__":
  682. print(PyQtImpl)
  683. QVTKRenderWidgetConeExample()