vispy 0.14.0__cp311-cp311-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of vispy might be problematic. Click here for more details.

Files changed (519) hide show
  1. vispy/__init__.py +33 -0
  2. vispy/app/__init__.py +15 -0
  3. vispy/app/_default_app.py +76 -0
  4. vispy/app/_detect_eventloop.py +148 -0
  5. vispy/app/application.py +263 -0
  6. vispy/app/backends/__init__.py +52 -0
  7. vispy/app/backends/_egl.py +264 -0
  8. vispy/app/backends/_glfw.py +513 -0
  9. vispy/app/backends/_jupyter_rfb.py +278 -0
  10. vispy/app/backends/_offscreen_util.py +121 -0
  11. vispy/app/backends/_osmesa.py +235 -0
  12. vispy/app/backends/_pyglet.py +451 -0
  13. vispy/app/backends/_pyqt4.py +36 -0
  14. vispy/app/backends/_pyqt5.py +36 -0
  15. vispy/app/backends/_pyqt6.py +40 -0
  16. vispy/app/backends/_pyside.py +37 -0
  17. vispy/app/backends/_pyside2.py +52 -0
  18. vispy/app/backends/_pyside6.py +53 -0
  19. vispy/app/backends/_qt.py +968 -0
  20. vispy/app/backends/_sdl2.py +444 -0
  21. vispy/app/backends/_template.py +244 -0
  22. vispy/app/backends/_test.py +8 -0
  23. vispy/app/backends/_tk.py +800 -0
  24. vispy/app/backends/_wx.py +476 -0
  25. vispy/app/backends/tests/__init__.py +0 -0
  26. vispy/app/backends/tests/test_offscreen_util.py +52 -0
  27. vispy/app/backends/tests/test_rfb.py +77 -0
  28. vispy/app/base.py +294 -0
  29. vispy/app/canvas.py +828 -0
  30. vispy/app/qt.py +92 -0
  31. vispy/app/tests/__init__.py +0 -0
  32. vispy/app/tests/qt-designer.ui +58 -0
  33. vispy/app/tests/test_app.py +442 -0
  34. vispy/app/tests/test_backends.py +164 -0
  35. vispy/app/tests/test_canvas.py +122 -0
  36. vispy/app/tests/test_context.py +92 -0
  37. vispy/app/tests/test_qt.py +47 -0
  38. vispy/app/tests/test_simultaneous.py +134 -0
  39. vispy/app/timer.py +174 -0
  40. vispy/color/__init__.py +17 -0
  41. vispy/color/_color_dict.py +193 -0
  42. vispy/color/color_array.py +447 -0
  43. vispy/color/color_space.py +181 -0
  44. vispy/color/colormap.py +1134 -0
  45. vispy/color/tests/__init__.py +0 -0
  46. vispy/color/tests/test_color.py +352 -0
  47. vispy/conftest.py +12 -0
  48. vispy/ext/__init__.py +0 -0
  49. vispy/ext/cocoapy.py +1542 -0
  50. vispy/ext/cubehelix.py +138 -0
  51. vispy/ext/egl.py +375 -0
  52. vispy/ext/fontconfig.py +118 -0
  53. vispy/ext/gdi32plus.py +206 -0
  54. vispy/ext/osmesa.py +105 -0
  55. vispy/geometry/__init__.py +23 -0
  56. vispy/geometry/_triangulation_debugger.py +171 -0
  57. vispy/geometry/calculations.py +134 -0
  58. vispy/geometry/curves.py +399 -0
  59. vispy/geometry/generation.py +643 -0
  60. vispy/geometry/isocurve.py +175 -0
  61. vispy/geometry/isosurface.py +465 -0
  62. vispy/geometry/meshdata.py +698 -0
  63. vispy/geometry/normals.py +78 -0
  64. vispy/geometry/parametric.py +56 -0
  65. vispy/geometry/polygon.py +137 -0
  66. vispy/geometry/rect.py +210 -0
  67. vispy/geometry/tests/__init__.py +0 -0
  68. vispy/geometry/tests/test_calculations.py +23 -0
  69. vispy/geometry/tests/test_generation.py +56 -0
  70. vispy/geometry/tests/test_meshdata.py +106 -0
  71. vispy/geometry/tests/test_triangulation.py +506 -0
  72. vispy/geometry/torusknot.py +142 -0
  73. vispy/geometry/triangulation.py +876 -0
  74. vispy/gloo/__init__.py +56 -0
  75. vispy/gloo/buffer.py +505 -0
  76. vispy/gloo/context.py +272 -0
  77. vispy/gloo/framebuffer.py +257 -0
  78. vispy/gloo/gl/__init__.py +234 -0
  79. vispy/gloo/gl/_constants.py +332 -0
  80. vispy/gloo/gl/_es2.py +986 -0
  81. vispy/gloo/gl/_gl2.py +1365 -0
  82. vispy/gloo/gl/_proxy.py +499 -0
  83. vispy/gloo/gl/_pyopengl2.py +362 -0
  84. vispy/gloo/gl/dummy.py +24 -0
  85. vispy/gloo/gl/es2.py +62 -0
  86. vispy/gloo/gl/gl2.py +98 -0
  87. vispy/gloo/gl/glplus.py +168 -0
  88. vispy/gloo/gl/pyopengl2.py +97 -0
  89. vispy/gloo/gl/tests/__init__.py +0 -0
  90. vispy/gloo/gl/tests/test_basics.py +282 -0
  91. vispy/gloo/gl/tests/test_functionality.py +566 -0
  92. vispy/gloo/gl/tests/test_names.py +246 -0
  93. vispy/gloo/gl/tests/test_use.py +71 -0
  94. vispy/gloo/glir.py +1816 -0
  95. vispy/gloo/globject.py +101 -0
  96. vispy/gloo/preprocessor.py +67 -0
  97. vispy/gloo/program.py +543 -0
  98. vispy/gloo/tests/__init__.py +0 -0
  99. vispy/gloo/tests/test_buffer.py +558 -0
  100. vispy/gloo/tests/test_context.py +119 -0
  101. vispy/gloo/tests/test_framebuffer.py +195 -0
  102. vispy/gloo/tests/test_glir.py +307 -0
  103. vispy/gloo/tests/test_globject.py +35 -0
  104. vispy/gloo/tests/test_program.py +302 -0
  105. vispy/gloo/tests/test_texture.py +732 -0
  106. vispy/gloo/tests/test_use_gloo.py +187 -0
  107. vispy/gloo/tests/test_util.py +60 -0
  108. vispy/gloo/tests/test_wrappers.py +261 -0
  109. vispy/gloo/texture.py +1045 -0
  110. vispy/gloo/util.py +129 -0
  111. vispy/gloo/wrappers.py +762 -0
  112. vispy/glsl/__init__.py +42 -0
  113. vispy/glsl/antialias/antialias.glsl +7 -0
  114. vispy/glsl/antialias/cap-butt.glsl +31 -0
  115. vispy/glsl/antialias/cap-round.glsl +29 -0
  116. vispy/glsl/antialias/cap-square.glsl +30 -0
  117. vispy/glsl/antialias/cap-triangle-in.glsl +30 -0
  118. vispy/glsl/antialias/cap-triangle-out.glsl +30 -0
  119. vispy/glsl/antialias/cap.glsl +67 -0
  120. vispy/glsl/antialias/caps.glsl +67 -0
  121. vispy/glsl/antialias/filled.glsl +50 -0
  122. vispy/glsl/antialias/outline.glsl +40 -0
  123. vispy/glsl/antialias/stroke.glsl +43 -0
  124. vispy/glsl/arrowheads/angle.glsl +99 -0
  125. vispy/glsl/arrowheads/arrowheads.frag +60 -0
  126. vispy/glsl/arrowheads/arrowheads.glsl +12 -0
  127. vispy/glsl/arrowheads/arrowheads.vert +83 -0
  128. vispy/glsl/arrowheads/curved.glsl +48 -0
  129. vispy/glsl/arrowheads/inhibitor.glsl +26 -0
  130. vispy/glsl/arrowheads/stealth.glsl +46 -0
  131. vispy/glsl/arrowheads/triangle.glsl +97 -0
  132. vispy/glsl/arrowheads/util.glsl +13 -0
  133. vispy/glsl/arrows/angle-30.glsl +12 -0
  134. vispy/glsl/arrows/angle-60.glsl +12 -0
  135. vispy/glsl/arrows/angle-90.glsl +12 -0
  136. vispy/glsl/arrows/arrow.frag +39 -0
  137. vispy/glsl/arrows/arrow.vert +49 -0
  138. vispy/glsl/arrows/arrows.glsl +17 -0
  139. vispy/glsl/arrows/common.glsl +187 -0
  140. vispy/glsl/arrows/curved.glsl +63 -0
  141. vispy/glsl/arrows/stealth.glsl +50 -0
  142. vispy/glsl/arrows/triangle-30.glsl +12 -0
  143. vispy/glsl/arrows/triangle-60.glsl +12 -0
  144. vispy/glsl/arrows/triangle-90.glsl +12 -0
  145. vispy/glsl/arrows/util.glsl +98 -0
  146. vispy/glsl/build_spatial_filters.py +660 -0
  147. vispy/glsl/collections/agg-fast-path.frag +20 -0
  148. vispy/glsl/collections/agg-fast-path.vert +78 -0
  149. vispy/glsl/collections/agg-glyph.frag +60 -0
  150. vispy/glsl/collections/agg-glyph.vert +33 -0
  151. vispy/glsl/collections/agg-marker.frag +35 -0
  152. vispy/glsl/collections/agg-marker.vert +48 -0
  153. vispy/glsl/collections/agg-path.frag +55 -0
  154. vispy/glsl/collections/agg-path.vert +166 -0
  155. vispy/glsl/collections/agg-point.frag +21 -0
  156. vispy/glsl/collections/agg-point.vert +35 -0
  157. vispy/glsl/collections/agg-segment.frag +32 -0
  158. vispy/glsl/collections/agg-segment.vert +75 -0
  159. vispy/glsl/collections/marker.frag +38 -0
  160. vispy/glsl/collections/marker.vert +48 -0
  161. vispy/glsl/collections/raw-path.frag +15 -0
  162. vispy/glsl/collections/raw-path.vert +24 -0
  163. vispy/glsl/collections/raw-point.frag +14 -0
  164. vispy/glsl/collections/raw-point.vert +31 -0
  165. vispy/glsl/collections/raw-segment.frag +18 -0
  166. vispy/glsl/collections/raw-segment.vert +26 -0
  167. vispy/glsl/collections/raw-triangle.frag +13 -0
  168. vispy/glsl/collections/raw-triangle.vert +26 -0
  169. vispy/glsl/collections/sdf-glyph-ticks.vert +69 -0
  170. vispy/glsl/collections/sdf-glyph.frag +80 -0
  171. vispy/glsl/collections/sdf-glyph.vert +59 -0
  172. vispy/glsl/collections/tick-labels.vert +71 -0
  173. vispy/glsl/colormaps/autumn.glsl +20 -0
  174. vispy/glsl/colormaps/blues.glsl +20 -0
  175. vispy/glsl/colormaps/color-space.glsl +17 -0
  176. vispy/glsl/colormaps/colormaps.glsl +24 -0
  177. vispy/glsl/colormaps/cool.glsl +20 -0
  178. vispy/glsl/colormaps/fire.glsl +21 -0
  179. vispy/glsl/colormaps/gray.glsl +20 -0
  180. vispy/glsl/colormaps/greens.glsl +20 -0
  181. vispy/glsl/colormaps/hot.glsl +22 -0
  182. vispy/glsl/colormaps/ice.glsl +20 -0
  183. vispy/glsl/colormaps/icefire.glsl +23 -0
  184. vispy/glsl/colormaps/parse.py +40 -0
  185. vispy/glsl/colormaps/reds.glsl +20 -0
  186. vispy/glsl/colormaps/spring.glsl +20 -0
  187. vispy/glsl/colormaps/summer.glsl +20 -0
  188. vispy/glsl/colormaps/user.glsl +22 -0
  189. vispy/glsl/colormaps/util.glsl +41 -0
  190. vispy/glsl/colormaps/wheel.glsl +21 -0
  191. vispy/glsl/colormaps/winter.glsl +20 -0
  192. vispy/glsl/lines/agg.frag +320 -0
  193. vispy/glsl/lines/agg.vert +241 -0
  194. vispy/glsl/markers/arrow.glsl +12 -0
  195. vispy/glsl/markers/asterisk.glsl +16 -0
  196. vispy/glsl/markers/chevron.glsl +14 -0
  197. vispy/glsl/markers/clover.glsl +20 -0
  198. vispy/glsl/markers/club.glsl +31 -0
  199. vispy/glsl/markers/cross.glsl +17 -0
  200. vispy/glsl/markers/diamond.glsl +12 -0
  201. vispy/glsl/markers/disc.glsl +9 -0
  202. vispy/glsl/markers/ellipse.glsl +67 -0
  203. vispy/glsl/markers/hbar.glsl +9 -0
  204. vispy/glsl/markers/heart.glsl +15 -0
  205. vispy/glsl/markers/infinity.glsl +15 -0
  206. vispy/glsl/markers/marker-sdf.frag +74 -0
  207. vispy/glsl/markers/marker-sdf.vert +41 -0
  208. vispy/glsl/markers/marker.frag +36 -0
  209. vispy/glsl/markers/marker.vert +46 -0
  210. vispy/glsl/markers/markers.glsl +24 -0
  211. vispy/glsl/markers/pin.glsl +18 -0
  212. vispy/glsl/markers/ring.glsl +11 -0
  213. vispy/glsl/markers/spade.glsl +28 -0
  214. vispy/glsl/markers/square.glsl +10 -0
  215. vispy/glsl/markers/tag.glsl +11 -0
  216. vispy/glsl/markers/triangle.glsl +14 -0
  217. vispy/glsl/markers/vbar.glsl +9 -0
  218. vispy/glsl/math/circle-through-2-points.glsl +30 -0
  219. vispy/glsl/math/constants.glsl +48 -0
  220. vispy/glsl/math/double.glsl +114 -0
  221. vispy/glsl/math/functions.glsl +20 -0
  222. vispy/glsl/math/point-to-line-distance.glsl +31 -0
  223. vispy/glsl/math/point-to-line-projection.glsl +29 -0
  224. vispy/glsl/math/signed-line-distance.glsl +27 -0
  225. vispy/glsl/math/signed-segment-distance.glsl +30 -0
  226. vispy/glsl/misc/regular-grid.frag +244 -0
  227. vispy/glsl/misc/spatial-filters.frag +1407 -0
  228. vispy/glsl/misc/viewport-NDC.glsl +20 -0
  229. vispy/glsl/transforms/azimuthal-equal-area.glsl +32 -0
  230. vispy/glsl/transforms/azimuthal-equidistant.glsl +38 -0
  231. vispy/glsl/transforms/hammer.glsl +44 -0
  232. vispy/glsl/transforms/identity.glsl +6 -0
  233. vispy/glsl/transforms/identity_forward.glsl +23 -0
  234. vispy/glsl/transforms/identity_inverse.glsl +23 -0
  235. vispy/glsl/transforms/linear-scale.glsl +127 -0
  236. vispy/glsl/transforms/log-scale.glsl +126 -0
  237. vispy/glsl/transforms/mercator-transverse-forward.glsl +40 -0
  238. vispy/glsl/transforms/mercator-transverse-inverse.glsl +40 -0
  239. vispy/glsl/transforms/panzoom.glsl +10 -0
  240. vispy/glsl/transforms/polar.glsl +41 -0
  241. vispy/glsl/transforms/position.glsl +44 -0
  242. vispy/glsl/transforms/power-scale.glsl +139 -0
  243. vispy/glsl/transforms/projection.glsl +7 -0
  244. vispy/glsl/transforms/pvm.glsl +13 -0
  245. vispy/glsl/transforms/rotate.glsl +45 -0
  246. vispy/glsl/transforms/trackball.glsl +15 -0
  247. vispy/glsl/transforms/translate.glsl +35 -0
  248. vispy/glsl/transforms/transverse_mercator.glsl +38 -0
  249. vispy/glsl/transforms/viewport-clipping.glsl +14 -0
  250. vispy/glsl/transforms/viewport-transform.glsl +16 -0
  251. vispy/glsl/transforms/viewport.glsl +50 -0
  252. vispy/glsl/transforms/x.glsl +24 -0
  253. vispy/glsl/transforms/y.glsl +19 -0
  254. vispy/glsl/transforms/z.glsl +14 -0
  255. vispy/io/__init__.py +20 -0
  256. vispy/io/_data/spatial-filters.npy +0 -0
  257. vispy/io/datasets.py +94 -0
  258. vispy/io/image.py +231 -0
  259. vispy/io/mesh.py +122 -0
  260. vispy/io/stl.py +167 -0
  261. vispy/io/tests/__init__.py +0 -0
  262. vispy/io/tests/test_image.py +47 -0
  263. vispy/io/tests/test_io.py +121 -0
  264. vispy/io/wavefront.py +350 -0
  265. vispy/plot/__init__.py +36 -0
  266. vispy/plot/fig.py +58 -0
  267. vispy/plot/plotwidget.py +522 -0
  268. vispy/plot/tests/__init__.py +0 -0
  269. vispy/plot/tests/test_plot.py +46 -0
  270. vispy/scene/__init__.py +43 -0
  271. vispy/scene/cameras/__init__.py +27 -0
  272. vispy/scene/cameras/_base.py +38 -0
  273. vispy/scene/cameras/arcball.py +106 -0
  274. vispy/scene/cameras/base_camera.py +538 -0
  275. vispy/scene/cameras/fly.py +474 -0
  276. vispy/scene/cameras/magnify.py +163 -0
  277. vispy/scene/cameras/panzoom.py +308 -0
  278. vispy/scene/cameras/perspective.py +333 -0
  279. vispy/scene/cameras/tests/__init__.py +0 -0
  280. vispy/scene/cameras/tests/test_cameras.py +27 -0
  281. vispy/scene/cameras/tests/test_link.py +53 -0
  282. vispy/scene/cameras/tests/test_perspective.py +122 -0
  283. vispy/scene/cameras/turntable.py +173 -0
  284. vispy/scene/canvas.py +639 -0
  285. vispy/scene/events.py +85 -0
  286. vispy/scene/node.py +644 -0
  287. vispy/scene/subscene.py +20 -0
  288. vispy/scene/tests/__init__.py +0 -0
  289. vispy/scene/tests/test_canvas.py +119 -0
  290. vispy/scene/tests/test_node.py +142 -0
  291. vispy/scene/tests/test_visuals.py +141 -0
  292. vispy/scene/visuals.py +276 -0
  293. vispy/scene/widgets/__init__.py +18 -0
  294. vispy/scene/widgets/anchor.py +25 -0
  295. vispy/scene/widgets/axis.py +88 -0
  296. vispy/scene/widgets/colorbar.py +176 -0
  297. vispy/scene/widgets/console.py +351 -0
  298. vispy/scene/widgets/grid.py +509 -0
  299. vispy/scene/widgets/label.py +50 -0
  300. vispy/scene/widgets/tests/__init__.py +0 -0
  301. vispy/scene/widgets/tests/test_colorbar.py +47 -0
  302. vispy/scene/widgets/viewbox.py +199 -0
  303. vispy/scene/widgets/widget.py +478 -0
  304. vispy/testing/__init__.py +51 -0
  305. vispy/testing/_runners.py +446 -0
  306. vispy/testing/_testing.py +416 -0
  307. vispy/testing/image_tester.py +473 -0
  308. vispy/testing/rendered_array_tester.py +85 -0
  309. vispy/testing/tests/__init__.py +0 -0
  310. vispy/testing/tests/test_testing.py +20 -0
  311. vispy/util/__init__.py +17 -0
  312. vispy/util/bunch.py +15 -0
  313. vispy/util/check_environment.py +57 -0
  314. vispy/util/config.py +490 -0
  315. vispy/util/dpi/__init__.py +19 -0
  316. vispy/util/dpi/_linux.py +69 -0
  317. vispy/util/dpi/_quartz.py +26 -0
  318. vispy/util/dpi/_win32.py +34 -0
  319. vispy/util/dpi/tests/__init__.py +0 -0
  320. vispy/util/dpi/tests/test_dpi.py +16 -0
  321. vispy/util/eq.py +41 -0
  322. vispy/util/event.py +774 -0
  323. vispy/util/fetching.py +276 -0
  324. vispy/util/filter.py +44 -0
  325. vispy/util/fonts/__init__.py +14 -0
  326. vispy/util/fonts/_freetype.py +73 -0
  327. vispy/util/fonts/_quartz.py +192 -0
  328. vispy/util/fonts/_triage.py +36 -0
  329. vispy/util/fonts/_vispy_fonts.py +20 -0
  330. vispy/util/fonts/_win32.py +105 -0
  331. vispy/util/fonts/data/OpenSans-Bold.ttf +0 -0
  332. vispy/util/fonts/data/OpenSans-BoldItalic.ttf +0 -0
  333. vispy/util/fonts/data/OpenSans-Italic.ttf +0 -0
  334. vispy/util/fonts/data/OpenSans-Regular.ttf +0 -0
  335. vispy/util/fonts/tests/__init__.py +0 -0
  336. vispy/util/fonts/tests/test_font.py +45 -0
  337. vispy/util/fourier.py +69 -0
  338. vispy/util/frozen.py +25 -0
  339. vispy/util/gallery_scraper.py +268 -0
  340. vispy/util/keys.py +91 -0
  341. vispy/util/logs.py +358 -0
  342. vispy/util/osmesa_gl.py +17 -0
  343. vispy/util/profiler.py +135 -0
  344. vispy/util/ptime.py +16 -0
  345. vispy/util/quaternion.py +229 -0
  346. vispy/util/svg/__init__.py +18 -0
  347. vispy/util/svg/base.py +20 -0
  348. vispy/util/svg/color.py +219 -0
  349. vispy/util/svg/element.py +51 -0
  350. vispy/util/svg/geometry.py +478 -0
  351. vispy/util/svg/group.py +66 -0
  352. vispy/util/svg/length.py +81 -0
  353. vispy/util/svg/number.py +25 -0
  354. vispy/util/svg/path.py +332 -0
  355. vispy/util/svg/shapes.py +57 -0
  356. vispy/util/svg/style.py +59 -0
  357. vispy/util/svg/svg.py +40 -0
  358. vispy/util/svg/transform.py +223 -0
  359. vispy/util/svg/transformable.py +28 -0
  360. vispy/util/svg/viewport.py +73 -0
  361. vispy/util/tests/__init__.py +0 -0
  362. vispy/util/tests/test_config.py +58 -0
  363. vispy/util/tests/test_docstring_parameters.py +123 -0
  364. vispy/util/tests/test_emitter_group.py +262 -0
  365. vispy/util/tests/test_event_emitter.py +743 -0
  366. vispy/util/tests/test_fourier.py +35 -0
  367. vispy/util/tests/test_gallery_scraper.py +112 -0
  368. vispy/util/tests/test_import.py +127 -0
  369. vispy/util/tests/test_key.py +22 -0
  370. vispy/util/tests/test_logging.py +45 -0
  371. vispy/util/tests/test_run.py +14 -0
  372. vispy/util/tests/test_transforms.py +42 -0
  373. vispy/util/tests/test_vispy.py +48 -0
  374. vispy/util/transforms.py +201 -0
  375. vispy/util/wrappers.py +155 -0
  376. vispy/version.py +4 -0
  377. vispy/visuals/__init__.py +50 -0
  378. vispy/visuals/_scalable_textures.py +485 -0
  379. vispy/visuals/axis.py +678 -0
  380. vispy/visuals/border.py +208 -0
  381. vispy/visuals/box.py +79 -0
  382. vispy/visuals/collections/__init__.py +30 -0
  383. vispy/visuals/collections/agg_fast_path_collection.py +219 -0
  384. vispy/visuals/collections/agg_path_collection.py +197 -0
  385. vispy/visuals/collections/agg_point_collection.py +52 -0
  386. vispy/visuals/collections/agg_segment_collection.py +142 -0
  387. vispy/visuals/collections/array_list.py +401 -0
  388. vispy/visuals/collections/base_collection.py +482 -0
  389. vispy/visuals/collections/collection.py +253 -0
  390. vispy/visuals/collections/path_collection.py +23 -0
  391. vispy/visuals/collections/point_collection.py +19 -0
  392. vispy/visuals/collections/polygon_collection.py +25 -0
  393. vispy/visuals/collections/raw_path_collection.py +119 -0
  394. vispy/visuals/collections/raw_point_collection.py +113 -0
  395. vispy/visuals/collections/raw_polygon_collection.py +77 -0
  396. vispy/visuals/collections/raw_segment_collection.py +112 -0
  397. vispy/visuals/collections/raw_triangle_collection.py +78 -0
  398. vispy/visuals/collections/segment_collection.py +19 -0
  399. vispy/visuals/collections/triangle_collection.py +16 -0
  400. vispy/visuals/collections/util.py +168 -0
  401. vispy/visuals/colorbar.py +699 -0
  402. vispy/visuals/cube.py +41 -0
  403. vispy/visuals/ellipse.py +163 -0
  404. vispy/visuals/filters/__init__.py +10 -0
  405. vispy/visuals/filters/base_filter.py +242 -0
  406. vispy/visuals/filters/clipper.py +60 -0
  407. vispy/visuals/filters/clipping_planes.py +122 -0
  408. vispy/visuals/filters/color.py +181 -0
  409. vispy/visuals/filters/markers.py +28 -0
  410. vispy/visuals/filters/mesh.py +796 -0
  411. vispy/visuals/filters/picking.py +60 -0
  412. vispy/visuals/filters/tests/__init__.py +3 -0
  413. vispy/visuals/filters/tests/test_primitive_picking_filters.py +70 -0
  414. vispy/visuals/filters/tests/test_wireframe_filter.py +16 -0
  415. vispy/visuals/glsl/__init__.py +1 -0
  416. vispy/visuals/glsl/antialiasing.py +133 -0
  417. vispy/visuals/glsl/color.py +63 -0
  418. vispy/visuals/graphs/__init__.py +1 -0
  419. vispy/visuals/graphs/graph.py +240 -0
  420. vispy/visuals/graphs/layouts/__init__.py +55 -0
  421. vispy/visuals/graphs/layouts/circular.py +49 -0
  422. vispy/visuals/graphs/layouts/force_directed.py +211 -0
  423. vispy/visuals/graphs/layouts/networkx_layout.py +87 -0
  424. vispy/visuals/graphs/layouts/random.py +52 -0
  425. vispy/visuals/graphs/tests/__init__.py +1 -0
  426. vispy/visuals/graphs/tests/test_layouts.py +139 -0
  427. vispy/visuals/graphs/tests/test_networkx_layout.py +47 -0
  428. vispy/visuals/graphs/util.py +120 -0
  429. vispy/visuals/gridlines.py +105 -0
  430. vispy/visuals/gridmesh.py +98 -0
  431. vispy/visuals/histogram.py +58 -0
  432. vispy/visuals/image.py +688 -0
  433. vispy/visuals/image_complex.py +130 -0
  434. vispy/visuals/infinite_line.py +199 -0
  435. vispy/visuals/instanced_mesh.py +152 -0
  436. vispy/visuals/isocurve.py +213 -0
  437. vispy/visuals/isoline.py +241 -0
  438. vispy/visuals/isosurface.py +113 -0
  439. vispy/visuals/line/__init__.py +6 -0
  440. vispy/visuals/line/arrow.py +289 -0
  441. vispy/visuals/line/dash_atlas.py +90 -0
  442. vispy/visuals/line/line.py +545 -0
  443. vispy/visuals/line_plot.py +135 -0
  444. vispy/visuals/linear_region.py +199 -0
  445. vispy/visuals/markers.py +810 -0
  446. vispy/visuals/mesh.py +373 -0
  447. vispy/visuals/mesh_normals.py +159 -0
  448. vispy/visuals/plane.py +54 -0
  449. vispy/visuals/polygon.py +145 -0
  450. vispy/visuals/rectangle.py +196 -0
  451. vispy/visuals/regular_polygon.py +56 -0
  452. vispy/visuals/scrolling_lines.py +197 -0
  453. vispy/visuals/shaders/__init__.py +17 -0
  454. vispy/visuals/shaders/compiler.py +206 -0
  455. vispy/visuals/shaders/expression.py +99 -0
  456. vispy/visuals/shaders/function.py +788 -0
  457. vispy/visuals/shaders/multiprogram.py +145 -0
  458. vispy/visuals/shaders/parsing.py +140 -0
  459. vispy/visuals/shaders/program.py +161 -0
  460. vispy/visuals/shaders/shader_object.py +162 -0
  461. vispy/visuals/shaders/tests/__init__.py +0 -0
  462. vispy/visuals/shaders/tests/test_function.py +486 -0
  463. vispy/visuals/shaders/tests/test_multiprogram.py +78 -0
  464. vispy/visuals/shaders/tests/test_parsing.py +57 -0
  465. vispy/visuals/shaders/variable.py +272 -0
  466. vispy/visuals/spectrogram.py +169 -0
  467. vispy/visuals/sphere.py +80 -0
  468. vispy/visuals/surface_plot.py +192 -0
  469. vispy/visuals/tests/__init__.py +0 -0
  470. vispy/visuals/tests/test_arrows.py +109 -0
  471. vispy/visuals/tests/test_axis.py +120 -0
  472. vispy/visuals/tests/test_collections.py +15 -0
  473. vispy/visuals/tests/test_colorbar.py +179 -0
  474. vispy/visuals/tests/test_colormap.py +97 -0
  475. vispy/visuals/tests/test_ellipse.py +122 -0
  476. vispy/visuals/tests/test_histogram.py +24 -0
  477. vispy/visuals/tests/test_image.py +390 -0
  478. vispy/visuals/tests/test_image_complex.py +36 -0
  479. vispy/visuals/tests/test_infinite_line.py +53 -0
  480. vispy/visuals/tests/test_instanced_mesh.py +50 -0
  481. vispy/visuals/tests/test_isosurface.py +22 -0
  482. vispy/visuals/tests/test_linear_region.py +152 -0
  483. vispy/visuals/tests/test_markers.py +54 -0
  484. vispy/visuals/tests/test_mesh.py +261 -0
  485. vispy/visuals/tests/test_mesh_normals.py +218 -0
  486. vispy/visuals/tests/test_polygon.py +112 -0
  487. vispy/visuals/tests/test_rectangle.py +163 -0
  488. vispy/visuals/tests/test_regular_polygon.py +111 -0
  489. vispy/visuals/tests/test_scalable_textures.py +180 -0
  490. vispy/visuals/tests/test_sdf.py +73 -0
  491. vispy/visuals/tests/test_spectrogram.py +42 -0
  492. vispy/visuals/tests/test_text.py +95 -0
  493. vispy/visuals/tests/test_volume.py +542 -0
  494. vispy/visuals/tests/test_windbarb.py +33 -0
  495. vispy/visuals/text/__init__.py +7 -0
  496. vispy/visuals/text/_sdf_cpu.cpython-311-darwin.so +0 -0
  497. vispy/visuals/text/_sdf_cpu.pyx +110 -0
  498. vispy/visuals/text/_sdf_gpu.py +316 -0
  499. vispy/visuals/text/text.py +675 -0
  500. vispy/visuals/transforms/__init__.py +34 -0
  501. vispy/visuals/transforms/_util.py +191 -0
  502. vispy/visuals/transforms/base_transform.py +233 -0
  503. vispy/visuals/transforms/chain.py +300 -0
  504. vispy/visuals/transforms/interactive.py +98 -0
  505. vispy/visuals/transforms/linear.py +564 -0
  506. vispy/visuals/transforms/nonlinear.py +398 -0
  507. vispy/visuals/transforms/tests/__init__.py +0 -0
  508. vispy/visuals/transforms/tests/test_transforms.py +243 -0
  509. vispy/visuals/transforms/transform_system.py +339 -0
  510. vispy/visuals/tube.py +173 -0
  511. vispy/visuals/visual.py +923 -0
  512. vispy/visuals/volume.py +1335 -0
  513. vispy/visuals/windbarb.py +291 -0
  514. vispy/visuals/xyz_axis.py +34 -0
  515. vispy-0.14.0.dist-info/LICENSE.txt +36 -0
  516. vispy-0.14.0.dist-info/METADATA +218 -0
  517. vispy-0.14.0.dist-info/RECORD +519 -0
  518. vispy-0.14.0.dist-info/WHEEL +5 -0
  519. vispy-0.14.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,968 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) Vispy Development Team. All Rights Reserved.
3
+ # Distributed under the (new) BSD License. See LICENSE.txt for more info.
4
+
5
+ """
6
+ Base code for the Qt backends. Note that this is *not* (anymore) a
7
+ backend by itself! One has to explicitly use either PySide, PyQt4 or
8
+ PySide2, PyQt5 or PyQt6. Note that the automatic backend selection prefers
9
+ a GUI toolkit that is already imported.
10
+
11
+ The _pyside, _pyqt4, _pyside2, _pyqt5 and _pyside6 modules will
12
+ import * from this module, and also keep a ref to the module object.
13
+ Note that if two of the backends are used, this module is actually
14
+ reloaded. This is a sorts of poor mans "subclassing" to get a working
15
+ version for both backends using the same code.
16
+
17
+ Note that it is strongly discouraged to use the
18
+ PySide/PyQt4/PySide2/PyQt5/PySide6 backends simultaneously. It is
19
+ known to cause unpredictable behavior and segfaults.
20
+ """
21
+
22
+ from __future__ import division
23
+
24
+ from time import sleep, time
25
+ import math
26
+ import os
27
+ import sys
28
+ import atexit
29
+ import ctypes
30
+ from packaging.version import Version
31
+
32
+ from ...util import logger
33
+ from ..base import (BaseApplicationBackend, BaseCanvasBackend,
34
+ BaseTimerBackend)
35
+ from ...util import keys
36
+ from ... import config
37
+ from . import qt_lib
38
+
39
+ USE_EGL = config['gl_backend'].lower().startswith('es')
40
+
41
+ # Get platform
42
+ IS_LINUX = IS_OSX = IS_WIN = IS_RPI = False
43
+ if sys.platform.startswith('linux'):
44
+ if os.uname()[4].startswith('arm'):
45
+ IS_RPI = True
46
+ else:
47
+ IS_LINUX = True
48
+ elif sys.platform.startswith('darwin'):
49
+ IS_OSX = True
50
+ elif sys.platform.startswith('win'):
51
+ IS_WIN = True
52
+
53
+ # -------------------------------------------------------------------- init ---
54
+
55
+
56
+ def _check_imports(lib):
57
+ # Make sure no conflicting libraries have been imported.
58
+ libs = ['PyQt4', 'PyQt5', 'PyQt6', 'PySide', 'PySide2', 'PySide6']
59
+ libs.remove(lib)
60
+ for lib2 in libs:
61
+ lib2 += '.QtCore'
62
+ if lib2 in sys.modules:
63
+ raise RuntimeError("Refusing to import %s because %s is already "
64
+ "imported." % (lib, lib2))
65
+
66
+
67
+ def _get_event_xy(ev):
68
+ # QT6 (and the Python bindings like PyQt6, PySide6) report position differently from previous versions
69
+ if hasattr(ev, 'pos'):
70
+ posx, posy = ev.pos().x(), ev.pos().y()
71
+ else:
72
+ # Compatibility for PySide6 / PyQt6
73
+ posx, posy = ev.position().x(), ev.position().y()
74
+
75
+ return posx, posy
76
+
77
+
78
+ # Get what qt lib to try. This tells us wheter this module is imported
79
+ # via _pyside or _pyqt4 or _pyqt5
80
+ QGLWidget = object
81
+ QT5_NEW_API = False
82
+ PYSIDE6_API = False
83
+ PYQT6_API = False
84
+ if qt_lib == 'pyqt4':
85
+ _check_imports('PyQt4')
86
+ if not USE_EGL:
87
+ from PyQt4.QtOpenGL import QGLWidget, QGLFormat
88
+ from PyQt4 import QtGui, QtCore, QtTest
89
+ QWidget, QApplication = QtGui.QWidget, QtGui.QApplication # Compat
90
+ elif qt_lib == 'pyqt5':
91
+ _check_imports('PyQt5')
92
+ if not USE_EGL:
93
+ from PyQt5.QtCore import QT_VERSION_STR
94
+ if Version(QT_VERSION_STR) >= Version('5.4.0'):
95
+ from PyQt5.QtWidgets import QOpenGLWidget as QGLWidget
96
+ from PyQt5.QtGui import QSurfaceFormat as QGLFormat
97
+ QT5_NEW_API = True
98
+ else:
99
+ from PyQt5.QtOpenGL import QGLWidget, QGLFormat
100
+ from PyQt5 import QtGui, QtCore, QtWidgets, QtTest
101
+ QWidget, QApplication = QtWidgets.QWidget, QtWidgets.QApplication # Compat
102
+ elif qt_lib == 'pyqt6':
103
+ _check_imports('PyQt6')
104
+ if not USE_EGL:
105
+ from PyQt6.QtCore import QT_VERSION_STR
106
+ if Version(QT_VERSION_STR) >= Version('6.0.0'):
107
+ from PyQt6.QtOpenGLWidgets import QOpenGLWidget as QGLWidget
108
+ from PyQt6.QtGui import QSurfaceFormat as QGLFormat
109
+ PYQT6_API = True
110
+ else:
111
+ from PyQt6.QtOpenGL import QGLWidget, QGLFormat
112
+ from PyQt6 import QtGui, QtCore, QtWidgets, QtTest
113
+ QWidget, QApplication = QtWidgets.QWidget, QtWidgets.QApplication # Compat
114
+ elif qt_lib == 'pyside6':
115
+ _check_imports('PySide6')
116
+ if not USE_EGL:
117
+ from PySide6.QtCore import __version__ as QT_VERSION_STR
118
+ if Version(QT_VERSION_STR) >= Version('6.0.0'):
119
+ from PySide6.QtOpenGLWidgets import QOpenGLWidget as QGLWidget
120
+ from PySide6.QtGui import QSurfaceFormat as QGLFormat
121
+ PYSIDE6_API = True
122
+ else:
123
+ from PySide6.QtOpenGL import QGLWidget, QGLFormat
124
+ from PySide6 import QtGui, QtCore, QtWidgets, QtTest
125
+ QWidget, QApplication = QtWidgets.QWidget, QtWidgets.QApplication # Compat
126
+ elif qt_lib == 'pyside2':
127
+ _check_imports('PySide2')
128
+ if not USE_EGL:
129
+ from PySide2.QtCore import __version__ as QT_VERSION_STR
130
+ if Version(QT_VERSION_STR) >= Version('5.4.0'):
131
+ from PySide2.QtWidgets import QOpenGLWidget as QGLWidget
132
+ from PySide2.QtGui import QSurfaceFormat as QGLFormat
133
+ QT5_NEW_API = True
134
+ else:
135
+ from PySide2.QtOpenGL import QGLWidget, QGLFormat
136
+ from PySide2 import QtGui, QtCore, QtWidgets, QtTest
137
+ QWidget, QApplication = QtWidgets.QWidget, QtWidgets.QApplication # Compat
138
+ elif qt_lib == 'pyside':
139
+ _check_imports('PySide')
140
+ if not USE_EGL:
141
+ from PySide.QtOpenGL import QGLWidget, QGLFormat
142
+ from PySide import QtGui, QtCore, QtTest
143
+ QWidget, QApplication = QtGui.QWidget, QtGui.QApplication # Compat
144
+ elif qt_lib:
145
+ raise RuntimeError("Invalid value for qt_lib %r." % qt_lib)
146
+ else:
147
+ raise RuntimeError("Module backends._qt should not be imported directly.")
148
+
149
+ # todo: add support for distinguishing left and right shift/ctrl/alt keys.
150
+ # Linux scan codes: (left, right)
151
+ # Shift 50, 62
152
+ # Ctrl 37, 105
153
+ # Alt 64, 108
154
+ qt_keys = QtCore.Qt.Key if qt_lib == 'pyqt6' else QtCore.Qt
155
+ KEYMAP = {
156
+ qt_keys.Key_Shift: keys.SHIFT,
157
+ qt_keys.Key_Control: keys.CONTROL,
158
+ qt_keys.Key_Alt: keys.ALT,
159
+ qt_keys.Key_AltGr: keys.ALT,
160
+ qt_keys.Key_Meta: keys.META,
161
+
162
+ qt_keys.Key_Left: keys.LEFT,
163
+ qt_keys.Key_Up: keys.UP,
164
+ qt_keys.Key_Right: keys.RIGHT,
165
+ qt_keys.Key_Down: keys.DOWN,
166
+ qt_keys.Key_PageUp: keys.PAGEUP,
167
+ qt_keys.Key_PageDown: keys.PAGEDOWN,
168
+
169
+ qt_keys.Key_Insert: keys.INSERT,
170
+ qt_keys.Key_Delete: keys.DELETE,
171
+ qt_keys.Key_Home: keys.HOME,
172
+ qt_keys.Key_End: keys.END,
173
+
174
+ qt_keys.Key_Escape: keys.ESCAPE,
175
+ qt_keys.Key_Backspace: keys.BACKSPACE,
176
+
177
+ qt_keys.Key_F1: keys.F1,
178
+ qt_keys.Key_F2: keys.F2,
179
+ qt_keys.Key_F3: keys.F3,
180
+ qt_keys.Key_F4: keys.F4,
181
+ qt_keys.Key_F5: keys.F5,
182
+ qt_keys.Key_F6: keys.F6,
183
+ qt_keys.Key_F7: keys.F7,
184
+ qt_keys.Key_F8: keys.F8,
185
+ qt_keys.Key_F9: keys.F9,
186
+ qt_keys.Key_F10: keys.F10,
187
+ qt_keys.Key_F11: keys.F11,
188
+ qt_keys.Key_F12: keys.F12,
189
+
190
+ qt_keys.Key_Space: keys.SPACE,
191
+ qt_keys.Key_Enter: keys.ENTER,
192
+ qt_keys.Key_Return: keys.ENTER,
193
+ qt_keys.Key_Tab: keys.TAB,
194
+ }
195
+ if PYQT6_API or PYSIDE6_API:
196
+ BUTTONMAP = {
197
+ QtCore.Qt.MouseButton.NoButton: 0,
198
+ QtCore.Qt.MouseButton.LeftButton: 1,
199
+ QtCore.Qt.MouseButton.RightButton: 2,
200
+ QtCore.Qt.MouseButton.MiddleButton: 3,
201
+ QtCore.Qt.MouseButton.BackButton: 4,
202
+ QtCore.Qt.MouseButton.ForwardButton: 5
203
+ }
204
+ else:
205
+ BUTTONMAP = {0: 0, 1: 1, 2: 2, 4: 3, 8: 4, 16: 5}
206
+
207
+
208
+ # Properly log Qt messages
209
+ def message_handler(*args):
210
+
211
+ if qt_lib in ("pyqt4", "pyside"):
212
+ msg_type, msg = args
213
+ elif qt_lib in ("pyqt5", "pyqt6", "pyside2", "pyside6"): # Is this correct for pyside2?
214
+ msg_type, context, msg = args
215
+ elif qt_lib:
216
+ raise RuntimeError("Invalid value for qt_lib %r." % qt_lib)
217
+ else:
218
+ raise RuntimeError("Module backends._qt ",
219
+ "should not be imported directly.")
220
+
221
+ BLACKLIST = [
222
+ # Ignore spam about tablet input
223
+ 'QCocoaView handleTabletEvent: This tablet device is unknown',
224
+ # Not too sure why this warning is emitted when using
225
+ # Spyder + PyQt5 + Vispy
226
+ # https://github.com/vispy/vispy/issues/1787
227
+ # In either case, it is really annoying. We should filter it away
228
+ 'QSocketNotifier: Multiple socket notifiers for same',
229
+ ]
230
+ for item in BLACKLIST:
231
+ if msg.startswith(item):
232
+ return
233
+
234
+ msg = msg.decode() if not isinstance(msg, str) else msg
235
+ logger.warning(msg)
236
+
237
+
238
+ def use_shared_contexts():
239
+ """Enable context sharing for PyQt5 5.4+ API applications.
240
+
241
+ This is disabled by default for PyQt5 5.4+ due to occasional segmentation
242
+ faults and other issues when contexts are shared.
243
+
244
+ """
245
+ forced_env_var = os.getenv('VISPY_PYQT5_SHARE_CONTEXT', 'false').lower() == 'true'
246
+ return not (QT5_NEW_API or PYSIDE6_API or PYQT6_API) or forced_env_var
247
+
248
+
249
+ try:
250
+ QtCore.qInstallMsgHandler(message_handler)
251
+ except AttributeError:
252
+ QtCore.qInstallMessageHandler(message_handler) # PyQt5, PyQt6
253
+
254
+
255
+ # -------------------------------------------------------------- capability ---
256
+
257
+ capability = dict( # things that can be set by the backend
258
+ title=True,
259
+ size=True,
260
+ position=True,
261
+ show=True,
262
+ vsync=True,
263
+ resizable=True,
264
+ decorate=True,
265
+ fullscreen=True,
266
+ context=use_shared_contexts(),
267
+ multi_window=True,
268
+ scroll=True,
269
+ parent=True,
270
+ always_on_top=True,
271
+ )
272
+
273
+
274
+ # ------------------------------------------------------- set_configuration ---
275
+ def _set_config(c):
276
+ """Set the OpenGL configuration"""
277
+ glformat = QGLFormat()
278
+ glformat.setRedBufferSize(c['red_size'])
279
+ glformat.setGreenBufferSize(c['green_size'])
280
+ glformat.setBlueBufferSize(c['blue_size'])
281
+ glformat.setAlphaBufferSize(c['alpha_size'])
282
+ if QT5_NEW_API:
283
+ # Qt5 >= 5.4.0 - below options automatically enabled if nonzero.
284
+ glformat.setSwapBehavior(glformat.DoubleBuffer if c['double_buffer']
285
+ else glformat.SingleBuffer)
286
+ elif PYQT6_API or PYSIDE6_API:
287
+ glformat.setSwapBehavior(glformat.SwapBehavior.DoubleBuffer if c['double_buffer']
288
+ else glformat.SwapBehavior.SingleBuffer)
289
+ else:
290
+ # Qt4 and Qt5 < 5.4.0 - buffers must be explicitly requested.
291
+ glformat.setAccum(False)
292
+ glformat.setRgba(True)
293
+ glformat.setDoubleBuffer(True if c['double_buffer'] else False)
294
+ glformat.setDepth(True if c['depth_size'] else False)
295
+ glformat.setStencil(True if c['stencil_size'] else False)
296
+ glformat.setSampleBuffers(True if c['samples'] else False)
297
+ glformat.setDepthBufferSize(c['depth_size'] if c['depth_size'] else 0)
298
+ glformat.setStencilBufferSize(c['stencil_size'] if c['stencil_size']
299
+ else 0)
300
+ glformat.setSamples(c['samples'] if c['samples'] else 0)
301
+ glformat.setStereo(c['stereo'])
302
+ return glformat
303
+
304
+
305
+ # ------------------------------------------------------------- application ---
306
+
307
+ class ApplicationBackend(BaseApplicationBackend):
308
+
309
+ def __init__(self):
310
+ BaseApplicationBackend.__init__(self)
311
+ # sharing is currently buggy and causes segmentation faults for tests with PyQt 5.6
312
+ if (QT5_NEW_API or PYSIDE6_API) and use_shared_contexts():
313
+ # For Qt5 >= 5.4.0 - Enable sharing of context between windows.
314
+ QApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts, True)
315
+ elif PYQT6_API and use_shared_contexts():
316
+ QApplication.setAttribute(QtCore.Qt.ApplicationAttribute.AA_ShareOpenGLContexts, True)
317
+
318
+ def _vispy_get_backend_name(self):
319
+ name = QtCore.__name__.split('.')[0]
320
+ return name
321
+
322
+ def _vispy_process_events(self):
323
+ app = self._vispy_get_native_app()
324
+ # sendPostedEvents replaces flush which has been removed from Qt6.0+
325
+ # This should be compatible with Qt4.x and Qt5.x
326
+ app.sendPostedEvents()
327
+ app.processEvents()
328
+
329
+ def _vispy_run(self):
330
+ app = self._vispy_get_native_app()
331
+ if hasattr(app, '_in_event_loop') and app._in_event_loop:
332
+ pass # Already in event loop
333
+ else:
334
+ # app.exec_() for PyQt <=5 and app.exec() for PyQt >=5
335
+ exec_func = app.exec if hasattr(app, "exec") else app.exec_
336
+ return exec_func()
337
+
338
+ def _vispy_quit(self):
339
+ return self._vispy_get_native_app().quit()
340
+
341
+ def _vispy_get_native_app(self):
342
+ # Get native app in save way. Taken from guisupport.py
343
+ app = QApplication.instance()
344
+ if app is None:
345
+ app = QApplication([''])
346
+ # Store so it won't be deleted, but not on a vispy object,
347
+ # or an application may produce error when closed.
348
+ QtGui._qApp = app
349
+ # Return
350
+ return app
351
+
352
+ def _vispy_sleep(self, duration_sec):
353
+ QtTest.QTest.qWait(duration_sec * 1000) # in ms
354
+
355
+
356
+ # ------------------------------------------------------------------ canvas ---
357
+
358
+ def _get_qpoint_pos(pos):
359
+ """Return the coordinates of a QPointF object."""
360
+ return pos.x(), pos.y()
361
+
362
+
363
+ class QtBaseCanvasBackend(BaseCanvasBackend):
364
+ """Base functionality of Qt backend. No OpenGL Stuff."""
365
+
366
+ def __init__(self, vispy_canvas, **kwargs):
367
+ BaseCanvasBackend.__init__(self, vispy_canvas)
368
+ # Maybe to ensure that exactly all arguments are passed?
369
+ p = self._process_backend_kwargs(kwargs)
370
+ self._initialized = False
371
+
372
+ # Init in desktop GL or EGL way
373
+ self._init_specific(p, kwargs)
374
+ assert self._initialized
375
+
376
+ self.setMouseTracking(True)
377
+ self._vispy_set_title(p.title)
378
+ self._vispy_set_size(*p.size)
379
+ if p.fullscreen is not False:
380
+ if p.fullscreen is not True:
381
+ logger.warning('Cannot specify monitor number for Qt '
382
+ 'fullscreen, using default')
383
+ self._fullscreen = True
384
+ else:
385
+ self._fullscreen = False
386
+
387
+ # must set physical size before setting visible or fullscreen
388
+ # operations may make the size invalid
389
+ if hasattr(self, 'devicePixelRatio'):
390
+ # handle high DPI displays in PyQt5
391
+ ratio = self.devicePixelRatio()
392
+ else:
393
+ ratio = 1
394
+ self._physical_size = (p.size[0] * ratio, p.size[1] * ratio)
395
+
396
+ if not p.resizable:
397
+ self.setFixedSize(self.size())
398
+ if p.position is not None:
399
+ self._vispy_set_position(*p.position)
400
+ if p.show:
401
+ self._vispy_set_visible(True)
402
+
403
+ # Qt supports OS double-click events, so we set this here to
404
+ # avoid double events
405
+ self._double_click_supported = True
406
+
407
+ try:
408
+ # see screen_changed docstring for more details
409
+ self.window().windowHandle().screenChanged.connect(self.screen_changed)
410
+ except AttributeError:
411
+ # either not PyQt5 backend or no parent window available
412
+ pass
413
+
414
+ # QNativeGestureEvent does not keep track of last or total
415
+ # values like QGestureEvent does
416
+ self._native_gesture_scale_values = []
417
+ self._native_gesture_rotation_values = []
418
+
419
+ def screen_changed(self, new_screen):
420
+ """Window moved from one display to another, resize canvas.
421
+
422
+ If display resolutions are the same this is essentially a no-op except for the redraw.
423
+ If the display resolutions differ (HiDPI versus regular displays) the canvas needs to
424
+ be redrawn to reset the physical size based on the current `devicePixelRatio()` and
425
+ redrawn with that new size.
426
+
427
+ """
428
+ self.resizeGL(*self._vispy_get_size())
429
+
430
+ def _vispy_warmup(self):
431
+ etime = time() + 0.25
432
+ while time() < etime:
433
+ sleep(0.01)
434
+ self._vispy_canvas.set_current()
435
+ self._vispy_canvas.app.process_events()
436
+
437
+ def _vispy_set_title(self, title):
438
+ # Set the window title. Has no effect for widgets
439
+ if self._vispy_canvas is None:
440
+ return
441
+ self.setWindowTitle(title)
442
+
443
+ def _vispy_set_size(self, w, h):
444
+ # Set size of the widget or window
445
+ self.resize(w, h)
446
+
447
+ def _vispy_set_physical_size(self, w, h):
448
+ self._physical_size = (w, h)
449
+
450
+ def _vispy_get_physical_size(self):
451
+ if self._vispy_canvas is None:
452
+ return
453
+ return self._physical_size
454
+
455
+ def _vispy_set_position(self, x, y):
456
+ # Set location of the widget or window. May have no effect for widgets
457
+ self.move(x, y)
458
+
459
+ def _vispy_set_visible(self, visible):
460
+ # Show or hide the window or widget
461
+ if visible:
462
+ if self._fullscreen:
463
+ self.showFullScreen()
464
+ else:
465
+ self.showNormal()
466
+ else:
467
+ self.hide()
468
+
469
+ def _vispy_set_fullscreen(self, fullscreen):
470
+ self._fullscreen = bool(fullscreen)
471
+ self._vispy_set_visible(True)
472
+
473
+ def _vispy_get_fullscreen(self):
474
+ return self._fullscreen
475
+
476
+ def _vispy_update(self):
477
+ if self._vispy_canvas is None:
478
+ return
479
+ # Invoke a redraw
480
+ self.update()
481
+
482
+ def _vispy_get_position(self):
483
+ g = self.geometry()
484
+ return g.x(), g.y()
485
+
486
+ def _vispy_get_size(self):
487
+ g = self.geometry()
488
+ return g.width(), g.height()
489
+
490
+ def sizeHint(self):
491
+ return self.size()
492
+
493
+ def mousePressEvent(self, ev):
494
+ if self._vispy_canvas is None:
495
+ return
496
+ self._vispy_mouse_press(
497
+ native=ev,
498
+ pos=_get_event_xy(ev),
499
+ button=BUTTONMAP.get(ev.button(), 0),
500
+ modifiers=self._modifiers(ev),
501
+ )
502
+
503
+ def mouseReleaseEvent(self, ev):
504
+ if self._vispy_canvas is None:
505
+ return
506
+ self._vispy_mouse_release(
507
+ native=ev,
508
+ pos=_get_event_xy(ev),
509
+ button=BUTTONMAP[ev.button()],
510
+ modifiers=self._modifiers(ev),
511
+ )
512
+
513
+ def mouseDoubleClickEvent(self, ev):
514
+ if self._vispy_canvas is None:
515
+ return
516
+ self._vispy_mouse_double_click(
517
+ native=ev,
518
+ pos=_get_event_xy(ev),
519
+ button=BUTTONMAP.get(ev.button(), 0),
520
+ modifiers=self._modifiers(ev),
521
+ )
522
+
523
+ def mouseMoveEvent(self, ev):
524
+ if self._vispy_canvas is None:
525
+ return
526
+ self._vispy_mouse_move(
527
+ native=ev,
528
+ pos=_get_event_xy(ev),
529
+ modifiers=self._modifiers(ev),
530
+ )
531
+
532
+ def wheelEvent(self, ev):
533
+ if self._vispy_canvas is None:
534
+ return
535
+ # Get scrolling
536
+ deltax, deltay = 0.0, 0.0
537
+ if hasattr(ev, 'orientation'):
538
+ if ev.orientation == QtCore.Qt.Horizontal:
539
+ deltax = ev.delta() / 120.0
540
+ else:
541
+ deltay = ev.delta() / 120.0
542
+ else:
543
+ # PyQt5 / PyQt6
544
+ delta = ev.angleDelta()
545
+ deltax, deltay = delta.x() / 120.0, delta.y() / 120.0
546
+ # Emit event
547
+ self._vispy_canvas.events.mouse_wheel(
548
+ native=ev,
549
+ delta=(deltax, deltay),
550
+ pos=_get_event_xy(ev),
551
+ modifiers=self._modifiers(ev),
552
+ )
553
+
554
+ def keyPressEvent(self, ev):
555
+ self._keyEvent(self._vispy_canvas.events.key_press, ev)
556
+
557
+ def keyReleaseEvent(self, ev):
558
+ self._keyEvent(self._vispy_canvas.events.key_release, ev)
559
+
560
+ def _handle_native_gesture_event(self, ev):
561
+ if self._vispy_canvas is None:
562
+ return
563
+ t = ev.gestureType()
564
+ # this is a workaround for what looks like a Qt bug where
565
+ # QNativeGestureEvent gives the wrong local position.
566
+ # See: https://bugreports.qt.io/browse/QTBUG-59595
567
+ try:
568
+ pos = self.mapFromGlobal(ev.globalPosition().toPoint())
569
+ except AttributeError:
570
+ # globalPos is deprecated in Qt6
571
+ pos = self.mapFromGlobal(ev.globalPos())
572
+ pos = pos.x(), pos.y()
573
+
574
+ if t == QtCore.Qt.NativeGestureType.BeginNativeGesture:
575
+ self._vispy_canvas.events.touch(
576
+ type='gesture_begin',
577
+ pos=_get_event_xy(ev),
578
+ )
579
+ elif t == QtCore.Qt.NativeGestureType.EndNativeGesture:
580
+ self._native_touch_total_rotation = []
581
+ self._native_touch_total_scale = []
582
+ self._vispy_canvas.events.touch(
583
+ type='gesture_end',
584
+ pos=_get_event_xy(ev),
585
+ )
586
+ elif t == QtCore.Qt.NativeGestureType.RotateNativeGesture:
587
+ angle = ev.value()
588
+ last_angle = (
589
+ self._native_gesture_rotation_values[-1]
590
+ if self._native_gesture_rotation_values
591
+ else None
592
+ )
593
+ self._native_gesture_rotation_values.append(angle)
594
+ total_rotation_angle = math.fsum(self._native_gesture_rotation_values)
595
+ self._vispy_canvas.events.touch(
596
+ type="gesture_rotate",
597
+ pos=pos,
598
+ rotation=angle,
599
+ last_rotation=last_angle,
600
+ total_rotation_angle=total_rotation_angle,
601
+ )
602
+ elif t == QtCore.Qt.NativeGestureType.ZoomNativeGesture:
603
+ scale = ev.value()
604
+ last_scale = (
605
+ self._native_gesture_scale_values[-1]
606
+ if self._native_gesture_scale_values
607
+ else None
608
+ )
609
+ self._native_gesture_scale_values.append(scale)
610
+ total_scale_factor = math.fsum(self._native_gesture_scale_values)
611
+ self._vispy_canvas.events.touch(
612
+ type="gesture_zoom",
613
+ pos=pos,
614
+ last_scale=last_scale,
615
+ scale=scale,
616
+ total_scale_factor=total_scale_factor,
617
+ )
618
+ # QtCore.Qt.NativeGestureType.PanNativeGesture
619
+ # Qt6 docs seem to imply this is only supported on Wayland but I have
620
+ # not been able to test it.
621
+ # Two finger pan events are anyway converted to scroll/wheel events.
622
+ # On macOS, more fingers are usually swallowed by the OS (by spaces,
623
+ # mission control, etc.).
624
+
625
+ def event(self, ev):
626
+ out = super(QtBaseCanvasBackend, self).event(ev)
627
+
628
+ # QNativeGestureEvent is Qt 5+
629
+ if (
630
+ (QT5_NEW_API or PYSIDE6_API or PYQT6_API)
631
+ and isinstance(ev, QtGui.QNativeGestureEvent)
632
+ ):
633
+ self._handle_native_gesture_event(ev)
634
+
635
+ return out
636
+
637
+ def _keyEvent(self, func, ev):
638
+ # evaluates the keycode of qt, and transform to vispy key.
639
+ key = int(ev.key())
640
+ if key in KEYMAP:
641
+ key = KEYMAP[key]
642
+ elif 32 <= key <= 127:
643
+ key = keys.Key(chr(key))
644
+ else:
645
+ key = None
646
+ mod = self._modifiers(ev)
647
+ func(native=ev, key=key, text=str(ev.text()), modifiers=mod)
648
+
649
+ def _modifiers(self, event):
650
+ # Convert the QT modifier state into a tuple of active modifier keys.
651
+ mod = ()
652
+ qtmod = event.modifiers()
653
+ qt_keyboard_modifiers = QtCore.Qt.KeyboardModifier if PYQT6_API else QtCore.Qt
654
+ for q, v in ([qt_keyboard_modifiers.ShiftModifier, keys.SHIFT],
655
+ [qt_keyboard_modifiers.ControlModifier, keys.CONTROL],
656
+ [qt_keyboard_modifiers.AltModifier, keys.ALT],
657
+ [qt_keyboard_modifiers.MetaModifier, keys.META]):
658
+ if qtmod & q:
659
+ mod += (v,)
660
+ return mod
661
+
662
+
663
+ _EGL_DISPLAY = None
664
+ egl = None
665
+
666
+ # todo: Make work on Windows
667
+ # todo: Make work without readpixels on Linux?
668
+ # todo: Make work on OSX?
669
+ # todo: Make work on Raspberry Pi!
670
+
671
+
672
+ class CanvasBackendEgl(QtBaseCanvasBackend, QWidget):
673
+
674
+ def _init_specific(self, p, kwargs):
675
+
676
+ # Initialize egl. Note that we only import egl if needed.
677
+ global _EGL_DISPLAY
678
+ global egl
679
+ if egl is None:
680
+ from ...ext import egl as _egl
681
+ egl = _egl
682
+ # Use MESA driver on Linux
683
+ if IS_LINUX and not IS_RPI:
684
+ os.environ['EGL_SOFTWARE'] = 'true'
685
+ # Create and init display
686
+ _EGL_DISPLAY = egl.eglGetDisplay()
687
+ CanvasBackendEgl._EGL_VERSION = egl.eglInitialize(_EGL_DISPLAY)
688
+ atexit.register(egl.eglTerminate, _EGL_DISPLAY)
689
+
690
+ # Deal with context
691
+ p.context.shared.add_ref('qt-egl', self)
692
+ if p.context.shared.ref is self:
693
+ self._native_config = c = egl.eglChooseConfig(_EGL_DISPLAY)[0]
694
+ self._native_context = egl.eglCreateContext(_EGL_DISPLAY, c, None)
695
+ else:
696
+ self._native_config = p.context.shared.ref._native_config
697
+ self._native_context = p.context.shared.ref._native_context
698
+
699
+ # Init widget
700
+ qt_window_types = QtCore.Qt.WindowType if PYQT6_API else QtCore.Qt
701
+ if p.always_on_top or not p.decorate:
702
+ hint = 0
703
+ hint |= 0 if p.decorate else qt_window_types.FramelessWindowHint
704
+ hint |= qt_window_types.WindowStaysOnTopHint if p.always_on_top else 0
705
+ else:
706
+ hint = qt_window_types.Widget # can also be a window type
707
+
708
+ QWidget.__init__(self, p.parent, hint)
709
+
710
+ qt_window_attributes = QtCore.Qt.WidgetAttribute if PYQT6_API else QtCore.Qt
711
+ if 0: # IS_LINUX or IS_RPI:
712
+ self.setAutoFillBackground(False)
713
+ self.setAttribute(qt_window_attributes.WA_NoSystemBackground, True)
714
+ self.setAttribute(qt_window_attributes.WA_OpaquePaintEvent, True)
715
+ elif IS_WIN:
716
+ self.setAttribute(qt_window_attributes.WA_PaintOnScreen, True)
717
+ self.setAutoFillBackground(False)
718
+
719
+ # Init surface
720
+ w = self.get_window_id()
721
+ self._surface = egl.eglCreateWindowSurface(_EGL_DISPLAY, c, w)
722
+ self.initializeGL()
723
+ self._initialized = True
724
+
725
+ def get_window_id(self):
726
+ """Get the window id of a PySide Widget. Might also work for PyQt4."""
727
+ # Get Qt win id
728
+ winid = self.winId()
729
+
730
+ # On Linux this is it
731
+ if IS_RPI:
732
+ nw = (ctypes.c_int * 3)(winid, self.width(), self.height())
733
+ return ctypes.pointer(nw)
734
+ elif IS_LINUX:
735
+ return int(winid) # Is int on PySide, but sip.voidptr on PyQt
736
+
737
+ # Get window id from stupid capsule thingy
738
+ # http://translate.google.com/translate?hl=en&sl=zh-CN&u=http://www.cnb
739
+ # logs.com/Shiren-Y/archive/2011/04/06/2007288.html&prev=/search%3Fq%3Dp
740
+ # yside%2Bdirectx%26client%3Dfirefox-a%26hs%3DIsJ%26rls%3Dorg.mozilla:n
741
+ # l:official%26channel%3Dfflb%26biw%3D1366%26bih%3D614
742
+ # Prepare
743
+ ctypes.pythonapi.PyCapsule_GetName.restype = ctypes.c_char_p
744
+ ctypes.pythonapi.PyCapsule_GetName.argtypes = [ctypes.py_object]
745
+ ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
746
+ ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object,
747
+ ctypes.c_char_p]
748
+ # Extract handle from capsule thingy
749
+ name = ctypes.pythonapi.PyCapsule_GetName(winid)
750
+ handle = ctypes.pythonapi.PyCapsule_GetPointer(winid, name)
751
+ return handle
752
+
753
+ def _vispy_close(self):
754
+ # Destroy EGL surface
755
+ if self._surface is not None:
756
+ egl.eglDestroySurface(_EGL_DISPLAY, self._surface)
757
+ self._surface = None
758
+ # Force the window or widget to shut down
759
+ self.close()
760
+
761
+ def _vispy_set_current(self):
762
+ egl.eglMakeCurrent(_EGL_DISPLAY, self._surface,
763
+ self._surface, self._native_context)
764
+
765
+ def _vispy_swap_buffers(self):
766
+ egl.eglSwapBuffers(_EGL_DISPLAY, self._surface)
767
+
768
+ def initializeGL(self):
769
+ self._vispy_canvas.set_current()
770
+ self._vispy_canvas.events.initialize()
771
+
772
+ def resizeEvent(self, event):
773
+ w, h = event.size().width(), event.size().height()
774
+ self._vispy_canvas.events.resize(size=(w, h))
775
+
776
+ def paintEvent(self, event):
777
+ self._vispy_canvas.events.draw(region=None)
778
+
779
+ if IS_LINUX or IS_RPI:
780
+ # Arg, cannot get GL to draw to the widget, so we take a
781
+ # screenshot and draw that for now ...
782
+ # Further, QImage keeps a ref to the data that we pass, so
783
+ # we need to use a static buffer to prevent memory leakage
784
+ from ... import gloo
785
+ import numpy as np
786
+ if not hasattr(self, '_gl_buffer'):
787
+ self._gl_buffer = np.ones((3000 * 3000 * 4), np.uint8) * 255
788
+ # Take screenshot and turn into RGB QImage
789
+ im = gloo.read_pixels()
790
+ sze = im.shape[0] * im.shape[1]
791
+ self._gl_buffer[0:0+sze*4:4] = im[:, :, 2].ravel()
792
+ self._gl_buffer[1:0+sze*4:4] = im[:, :, 1].ravel()
793
+ self._gl_buffer[2:2+sze*4:4] = im[:, :, 0].ravel()
794
+ img = QtGui.QImage(self._gl_buffer, im.shape[1], im.shape[0],
795
+ QtGui.QImage.Format_RGB32)
796
+ # Paint the image
797
+ painter = QtGui.QPainter()
798
+ painter.begin(self)
799
+ rect = QtCore.QRect(0, 0, self.width(), self.height())
800
+ painter.drawImage(rect, img)
801
+ painter.end()
802
+
803
+ def paintEngine(self):
804
+ if IS_LINUX and not IS_RPI:
805
+ # For now we are drawing a screenshot
806
+ return QWidget.paintEngine(self)
807
+ else:
808
+ return None # Disable Qt's native drawing system
809
+
810
+
811
+ class CanvasBackendDesktop(QtBaseCanvasBackend, QGLWidget):
812
+
813
+ def _init_specific(self, p, kwargs):
814
+
815
+ # Deal with config
816
+ glformat = _set_config(p.context.config)
817
+ glformat.setSwapInterval(1 if p.vsync else 0)
818
+ # Deal with context
819
+ widget = kwargs.pop('shareWidget', None) or self
820
+ p.context.shared.add_ref('qt', widget)
821
+ if p.context.shared.ref is widget:
822
+ if widget is self:
823
+ widget = None # QGLWidget does not accept self ;)
824
+ else:
825
+ widget = p.context.shared.ref
826
+ if 'shareWidget' in kwargs:
827
+ raise RuntimeError('Cannot use vispy to share context and '
828
+ 'use built-in shareWidget.')
829
+
830
+ qt_window_types = QtCore.Qt.WindowType if PYQT6_API else QtCore.Qt
831
+ if p.always_on_top or not p.decorate:
832
+ hint = 0
833
+ hint |= 0 if p.decorate else qt_window_types.FramelessWindowHint
834
+ hint |= qt_window_types.WindowStaysOnTopHint if p.always_on_top else 0
835
+ else:
836
+ hint = qt_window_types.Widget # can also be a window type
837
+
838
+ if QT5_NEW_API or PYSIDE6_API or PYQT6_API:
839
+ # Qt5 >= 5.4.0 - sharing is automatic
840
+ QGLWidget.__init__(self, p.parent, hint)
841
+
842
+ # Need to create an offscreen surface so we can get GL parameters
843
+ # without opening/showing the Widget. PyQt5 >= 5.4 will create the
844
+ # valid context later when the widget is shown.
845
+ self._secondary_context = QtGui.QOpenGLContext()
846
+ self._secondary_context.setShareContext(self.context())
847
+ self._secondary_context.setFormat(glformat)
848
+ self._secondary_context.create()
849
+
850
+ self._surface = QtGui.QOffscreenSurface()
851
+ self._surface.setFormat(glformat)
852
+ self._surface.create()
853
+ self._secondary_context.makeCurrent(self._surface)
854
+ else:
855
+ # Qt4 and Qt5 < 5.4.0 - sharing is explicitly requested
856
+ QGLWidget.__init__(self, p.parent, widget, hint)
857
+ # unused with this API
858
+ self._secondary_context = None
859
+ self._surface = None
860
+
861
+ self.setFormat(glformat)
862
+ self._initialized = True
863
+ if not QT5_NEW_API and not PYSIDE6_API and not PYQT6_API and not self.isValid():
864
+ # On Qt5 >= 5.4.0, isValid is only true once the widget is shown
865
+ raise RuntimeError('context could not be created')
866
+ if not QT5_NEW_API and not PYSIDE6_API and not PYQT6_API:
867
+ # to make consistent with other backends
868
+ self.setAutoBufferSwap(False)
869
+ qt_focus_policies = QtCore.Qt.FocusPolicy if PYQT6_API else QtCore.Qt
870
+ self.setFocusPolicy(qt_focus_policies.WheelFocus)
871
+
872
+ def _vispy_close(self):
873
+ # Force the window or widget to shut down
874
+ self.close()
875
+ self.doneCurrent()
876
+ if not QT5_NEW_API and not PYSIDE6_API and not PYQT6_API:
877
+ self.context().reset()
878
+ if self._vispy_canvas is not None:
879
+ self._vispy_canvas.app.process_events()
880
+ self._vispy_canvas.app.process_events()
881
+
882
+ def _vispy_set_current(self):
883
+ if self._vispy_canvas is None:
884
+ return # todo: can we get rid of this now?
885
+ if self.isValid():
886
+ self.makeCurrent()
887
+
888
+ def _vispy_swap_buffers(self):
889
+ # Swap front and back buffer
890
+ if self._vispy_canvas is None:
891
+ return
892
+ if QT5_NEW_API or PYSIDE6_API or PYQT6_API:
893
+ ctx = self.context()
894
+ ctx.swapBuffers(ctx.surface())
895
+ else:
896
+ self.swapBuffers()
897
+
898
+ def _vispy_get_fb_bind_location(self):
899
+ if QT5_NEW_API or PYSIDE6_API or PYQT6_API:
900
+ return self.defaultFramebufferObject()
901
+ else:
902
+ return QtBaseCanvasBackend._vispy_get_fb_bind_location(self)
903
+
904
+ def initializeGL(self):
905
+ if self._vispy_canvas is None:
906
+ return
907
+ self._vispy_canvas.events.initialize()
908
+
909
+ def resizeGL(self, w, h):
910
+ if self._vispy_canvas is None:
911
+ return
912
+ if hasattr(self, 'devicePixelRatio'):
913
+ # We take into account devicePixelRatio, which is non-unity on
914
+ # e.g HiDPI displays.
915
+ # self.devicePixelRatio() is a float and should have been in Qt5 according to the documentation
916
+ ratio = self.devicePixelRatio()
917
+ w = int(w * ratio)
918
+ h = int(h * ratio)
919
+ self._vispy_set_physical_size(w, h)
920
+ self._vispy_canvas.events.resize(size=(self.width(), self.height()),
921
+ physical_size=(w, h))
922
+
923
+ def paintGL(self):
924
+ if self._vispy_canvas is None:
925
+ return
926
+ # (0, 0, self.width(), self.height()))
927
+ self._vispy_canvas.set_current()
928
+ self._vispy_canvas.events.draw(region=None)
929
+
930
+ # Clear the alpha channel with QOpenGLWidget (Qt >= 5.4), otherwise the
931
+ # window is translucent behind non-opaque objects.
932
+ # Reference: MRtrix3/mrtrix3#266
933
+ if QT5_NEW_API or PYSIDE6_API or PYQT6_API:
934
+ context = self._vispy_canvas.context
935
+ context.set_color_mask(False, False, False, True)
936
+ context.clear(color=True, depth=False, stencil=False)
937
+ context.set_color_mask(True, True, True, True)
938
+ context.flush()
939
+
940
+
941
+ # Select CanvasBackend
942
+ if USE_EGL:
943
+ CanvasBackend = CanvasBackendEgl
944
+ else:
945
+ CanvasBackend = CanvasBackendDesktop
946
+
947
+
948
+ # ------------------------------------------------------------------- timer ---
949
+
950
+ class TimerBackend(BaseTimerBackend, QtCore.QTimer):
951
+
952
+ def __init__(self, vispy_timer):
953
+ # Make sure there is an app
954
+ app = ApplicationBackend()
955
+ app._vispy_get_native_app()
956
+ # Init
957
+ BaseTimerBackend.__init__(self, vispy_timer)
958
+ QtCore.QTimer.__init__(self)
959
+ self.timeout.connect(self._vispy_timeout)
960
+
961
+ def _vispy_start(self, interval):
962
+ self.start(int(interval * 1000))
963
+
964
+ def _vispy_stop(self):
965
+ self.stop()
966
+
967
+ def _vispy_timeout(self):
968
+ self._vispy_timer._timeout()