vispy 0.15.0__cp313-cp313-win_amd64.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 (521) 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 +1003 -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 +1213 -0
  45. vispy/color/tests/__init__.py +0 -0
  46. vispy/color/tests/test_color.py +378 -0
  47. vispy/conftest.py +12 -0
  48. vispy/ext/__init__.py +0 -0
  49. vispy/ext/cocoapy.py +1522 -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 +162 -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 +700 -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 +594 -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 +568 -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 +1824 -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 +1046 -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 +105 -0
  274. vispy/scene/cameras/base_camera.py +551 -0
  275. vispy/scene/cameras/fly.py +474 -0
  276. vispy/scene/cameras/magnify.py +163 -0
  277. vispy/scene/cameras/panzoom.py +311 -0
  278. vispy/scene/cameras/perspective.py +338 -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 +183 -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 +448 -0
  306. vispy/testing/_testing.py +416 -0
  307. vispy/testing/image_tester.py +494 -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 +32 -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 +21 -0
  377. vispy/visuals/__init__.py +50 -0
  378. vispy/visuals/_scalable_textures.py +487 -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 +162 -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 +801 -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 +161 -0
  430. vispy/visuals/gridmesh.py +98 -0
  431. vispy/visuals/histogram.py +58 -0
  432. vispy/visuals/image.py +701 -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 +819 -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_gridlines.py +30 -0
  477. vispy/visuals/tests/test_histogram.py +24 -0
  478. vispy/visuals/tests/test_image.py +392 -0
  479. vispy/visuals/tests/test_image_complex.py +36 -0
  480. vispy/visuals/tests/test_infinite_line.py +53 -0
  481. vispy/visuals/tests/test_instanced_mesh.py +50 -0
  482. vispy/visuals/tests/test_isosurface.py +22 -0
  483. vispy/visuals/tests/test_linear_region.py +152 -0
  484. vispy/visuals/tests/test_markers.py +54 -0
  485. vispy/visuals/tests/test_mesh.py +261 -0
  486. vispy/visuals/tests/test_mesh_normals.py +218 -0
  487. vispy/visuals/tests/test_polygon.py +112 -0
  488. vispy/visuals/tests/test_rectangle.py +163 -0
  489. vispy/visuals/tests/test_regular_polygon.py +111 -0
  490. vispy/visuals/tests/test_scalable_textures.py +196 -0
  491. vispy/visuals/tests/test_sdf.py +73 -0
  492. vispy/visuals/tests/test_spectrogram.py +42 -0
  493. vispy/visuals/tests/test_surface_plot.py +57 -0
  494. vispy/visuals/tests/test_text.py +95 -0
  495. vispy/visuals/tests/test_volume.py +542 -0
  496. vispy/visuals/tests/test_windbarb.py +33 -0
  497. vispy/visuals/text/__init__.py +7 -0
  498. vispy/visuals/text/_sdf_cpu.cp313-win_amd64.pyd +0 -0
  499. vispy/visuals/text/_sdf_cpu.pyx +112 -0
  500. vispy/visuals/text/_sdf_gpu.py +316 -0
  501. vispy/visuals/text/text.py +675 -0
  502. vispy/visuals/transforms/__init__.py +34 -0
  503. vispy/visuals/transforms/_util.py +191 -0
  504. vispy/visuals/transforms/base_transform.py +233 -0
  505. vispy/visuals/transforms/chain.py +300 -0
  506. vispy/visuals/transforms/interactive.py +98 -0
  507. vispy/visuals/transforms/linear.py +564 -0
  508. vispy/visuals/transforms/nonlinear.py +398 -0
  509. vispy/visuals/transforms/tests/__init__.py +0 -0
  510. vispy/visuals/transforms/tests/test_transforms.py +243 -0
  511. vispy/visuals/transforms/transform_system.py +339 -0
  512. vispy/visuals/tube.py +173 -0
  513. vispy/visuals/visual.py +923 -0
  514. vispy/visuals/volume.py +1366 -0
  515. vispy/visuals/windbarb.py +291 -0
  516. vispy/visuals/xyz_axis.py +34 -0
  517. vispy-0.15.0.dist-info/METADATA +243 -0
  518. vispy-0.15.0.dist-info/RECORD +521 -0
  519. vispy-0.15.0.dist-info/WHEEL +5 -0
  520. vispy-0.15.0.dist-info/licenses/LICENSE.txt +36 -0
  521. vispy-0.15.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1003 @@
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, 'devicePixelRatioF'):
390
+ # handle high DPI displays in PyQt5
391
+ ratio = self.devicePixelRatioF()
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 `devicePixelRatioF()` 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
+ vispy_event = 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
+ # If vispy did not handle the event, clear the accept parameter of the qt event
503
+ if not vispy_event.handled:
504
+ ev.ignore()
505
+
506
+ def mouseReleaseEvent(self, ev):
507
+ if self._vispy_canvas is None:
508
+ return
509
+ vispy_event = self._vispy_mouse_release(
510
+ native=ev,
511
+ pos=_get_event_xy(ev),
512
+ button=BUTTONMAP[ev.button()],
513
+ modifiers=self._modifiers(ev),
514
+ )
515
+ # If vispy did not handle the event, clear the accept parameter of the qt event
516
+ if not vispy_event.handled:
517
+ ev.ignore()
518
+
519
+ def mouseDoubleClickEvent(self, ev):
520
+ if self._vispy_canvas is None:
521
+ return
522
+ vispy_event = self._vispy_mouse_double_click(
523
+ native=ev,
524
+ pos=_get_event_xy(ev),
525
+ button=BUTTONMAP.get(ev.button(), 0),
526
+ modifiers=self._modifiers(ev),
527
+ )
528
+ # If vispy did not handle the event, clear the accept parameter of the qt event
529
+ if not vispy_event.handled:
530
+ ev.ignore()
531
+
532
+ def mouseMoveEvent(self, ev):
533
+ if self._vispy_canvas is None:
534
+ return
535
+ # NB ignores events, returns None for events in quick succession
536
+ vispy_event = self._vispy_mouse_move(
537
+ native=ev,
538
+ pos=_get_event_xy(ev),
539
+ modifiers=self._modifiers(ev),
540
+ )
541
+ # If vispy did not handle the event, clear the accept parameter of the qt event
542
+ # Note that the handler can return None, this is equivalent to not handling the event
543
+ if vispy_event is None or not vispy_event.handled:
544
+ # Theoretically, a parent widget might want to listen to all of
545
+ # the mouse move events, including those that VisPy ignores
546
+ ev.ignore()
547
+
548
+ def wheelEvent(self, ev):
549
+ if self._vispy_canvas is None:
550
+ return
551
+ # Get scrolling
552
+ deltax, deltay = 0.0, 0.0
553
+ if hasattr(ev, 'orientation'):
554
+ if ev.orientation == QtCore.Qt.Horizontal:
555
+ deltax = ev.delta() / 120.0
556
+ else:
557
+ deltay = ev.delta() / 120.0
558
+ else:
559
+ # PyQt5 / PyQt6
560
+ delta = ev.angleDelta()
561
+ deltax, deltay = delta.x() / 120.0, delta.y() / 120.0
562
+ # Emit event
563
+ vispy_event = self._vispy_canvas.events.mouse_wheel(
564
+ native=ev,
565
+ delta=(deltax, deltay),
566
+ pos=_get_event_xy(ev),
567
+ modifiers=self._modifiers(ev),
568
+ )
569
+ # If vispy did not handle the event, clear the accept parameter of the qt event
570
+ if not vispy_event.handled:
571
+ ev.ignore()
572
+
573
+ def keyPressEvent(self, ev):
574
+ self._keyEvent(self._vispy_canvas.events.key_press, ev)
575
+
576
+ def keyReleaseEvent(self, ev):
577
+ self._keyEvent(self._vispy_canvas.events.key_release, ev)
578
+
579
+ def _handle_native_gesture_event(self, ev):
580
+ if self._vispy_canvas is None:
581
+ return
582
+ t = ev.gestureType()
583
+ # this is a workaround for what looks like a Qt bug where
584
+ # QNativeGestureEvent gives the wrong local position.
585
+ # See: https://bugreports.qt.io/browse/QTBUG-59595
586
+ try:
587
+ pos = self.mapFromGlobal(ev.globalPosition().toPoint())
588
+ except AttributeError:
589
+ # globalPos is deprecated in Qt6
590
+ pos = self.mapFromGlobal(ev.globalPos())
591
+ pos = pos.x(), pos.y()
592
+
593
+ vispy_event = None
594
+ if t == QtCore.Qt.NativeGestureType.BeginNativeGesture:
595
+ vispy_event = self._vispy_canvas.events.touch(
596
+ type='gesture_begin',
597
+ pos=_get_event_xy(ev),
598
+ modifiers=self._modifiers(ev),
599
+ )
600
+ elif t == QtCore.Qt.NativeGestureType.EndNativeGesture:
601
+ self._native_touch_total_rotation = []
602
+ self._native_touch_total_scale = []
603
+ vispy_event = self._vispy_canvas.events.touch(
604
+ type='gesture_end',
605
+ pos=_get_event_xy(ev),
606
+ modifiers=self._modifiers(ev),
607
+ )
608
+ elif t == QtCore.Qt.NativeGestureType.RotateNativeGesture:
609
+ angle = ev.value()
610
+ last_angle = (
611
+ self._native_gesture_rotation_values[-1]
612
+ if self._native_gesture_rotation_values
613
+ else None
614
+ )
615
+ self._native_gesture_rotation_values.append(angle)
616
+ total_rotation_angle = math.fsum(self._native_gesture_rotation_values)
617
+ vispy_event = self._vispy_canvas.events.touch(
618
+ type="gesture_rotate",
619
+ pos=pos,
620
+ rotation=angle,
621
+ last_rotation=last_angle,
622
+ total_rotation_angle=total_rotation_angle,
623
+ modifiers=self._modifiers(ev),
624
+ )
625
+ elif t == QtCore.Qt.NativeGestureType.ZoomNativeGesture:
626
+ scale = ev.value()
627
+ last_scale = (
628
+ self._native_gesture_scale_values[-1]
629
+ if self._native_gesture_scale_values
630
+ else None
631
+ )
632
+ self._native_gesture_scale_values.append(scale)
633
+ total_scale_factor = math.fsum(self._native_gesture_scale_values)
634
+ vispy_event = self._vispy_canvas.events.touch(
635
+ type="gesture_zoom",
636
+ pos=pos,
637
+ last_scale=last_scale,
638
+ scale=scale,
639
+ total_scale_factor=total_scale_factor,
640
+ modifiers=self._modifiers(ev),
641
+ )
642
+ # QtCore.Qt.NativeGestureType.PanNativeGesture
643
+ # Qt6 docs seem to imply this is only supported on Wayland but I have
644
+ # not been able to test it.
645
+ # Two finger pan events are anyway converted to scroll/wheel events.
646
+ # On macOS, more fingers are usually swallowed by the OS (by spaces,
647
+ # mission control, etc.).
648
+
649
+ # If vispy did not handle the event, clear the accept parameter of the qt event
650
+ # Note that some handlers return None, this is equivalent to not handling the event
651
+ if vispy_event is None or not vispy_event.handled:
652
+ ev.ignore()
653
+
654
+ def event(self, ev):
655
+ out = super(QtBaseCanvasBackend, self).event(ev)
656
+
657
+ # QNativeGestureEvent is Qt 5+
658
+ if (
659
+ (QT5_NEW_API or PYSIDE6_API or PYQT6_API)
660
+ and isinstance(ev, QtGui.QNativeGestureEvent)
661
+ ):
662
+ self._handle_native_gesture_event(ev)
663
+
664
+ return out
665
+
666
+ def _keyEvent(self, func, ev):
667
+ # evaluates the keycode of qt, and transform to vispy key.
668
+ key = int(ev.key())
669
+ if key in KEYMAP:
670
+ key = KEYMAP[key]
671
+ elif 32 <= key <= 127:
672
+ key = keys.Key(chr(key))
673
+ else:
674
+ key = None
675
+ mod = self._modifiers(ev)
676
+ vispy_event = func(native=ev, key=key, text=str(ev.text()), modifiers=mod)
677
+ # If vispy did not handle the event, clear the accept parameter of the qt event
678
+ if not vispy_event.handled:
679
+ ev.ignore()
680
+
681
+ def _modifiers(self, event):
682
+ # Convert the QT modifier state into a tuple of active modifier keys.
683
+ mod = ()
684
+ qtmod = event.modifiers()
685
+ qt_keyboard_modifiers = QtCore.Qt.KeyboardModifier if PYQT6_API else QtCore.Qt
686
+ for q, v in ([qt_keyboard_modifiers.ShiftModifier, keys.SHIFT],
687
+ [qt_keyboard_modifiers.ControlModifier, keys.CONTROL],
688
+ [qt_keyboard_modifiers.AltModifier, keys.ALT],
689
+ [qt_keyboard_modifiers.MetaModifier, keys.META]):
690
+ if qtmod & q:
691
+ mod += (v,)
692
+ return mod
693
+
694
+
695
+ _EGL_DISPLAY = None
696
+ egl = None
697
+
698
+ # todo: Make work on Windows
699
+ # todo: Make work without readpixels on Linux?
700
+ # todo: Make work on OSX?
701
+ # todo: Make work on Raspberry Pi!
702
+
703
+
704
+ class CanvasBackendEgl(QtBaseCanvasBackend, QWidget):
705
+
706
+ def _init_specific(self, p, kwargs):
707
+
708
+ # Initialize egl. Note that we only import egl if needed.
709
+ global _EGL_DISPLAY
710
+ global egl
711
+ if egl is None:
712
+ from ...ext import egl as _egl
713
+ egl = _egl
714
+ # Use MESA driver on Linux
715
+ if IS_LINUX and not IS_RPI:
716
+ os.environ['EGL_SOFTWARE'] = 'true'
717
+ # Create and init display
718
+ _EGL_DISPLAY = egl.eglGetDisplay()
719
+ CanvasBackendEgl._EGL_VERSION = egl.eglInitialize(_EGL_DISPLAY)
720
+ atexit.register(egl.eglTerminate, _EGL_DISPLAY)
721
+
722
+ # Deal with context
723
+ p.context.shared.add_ref('qt-egl', self)
724
+ if p.context.shared.ref is self:
725
+ self._native_config = c = egl.eglChooseConfig(_EGL_DISPLAY)[0]
726
+ self._native_context = egl.eglCreateContext(_EGL_DISPLAY, c, None)
727
+ else:
728
+ self._native_config = p.context.shared.ref._native_config
729
+ self._native_context = p.context.shared.ref._native_context
730
+
731
+ # Init widget
732
+ qt_window_types = QtCore.Qt.WindowType if PYQT6_API else QtCore.Qt
733
+ if p.always_on_top or not p.decorate:
734
+ hint = 0
735
+ hint |= 0 if p.decorate else qt_window_types.FramelessWindowHint
736
+ hint |= qt_window_types.WindowStaysOnTopHint if p.always_on_top else 0
737
+ else:
738
+ hint = qt_window_types.Widget # can also be a window type
739
+
740
+ QWidget.__init__(self, p.parent, hint)
741
+
742
+ qt_window_attributes = QtCore.Qt.WidgetAttribute if PYQT6_API else QtCore.Qt
743
+ if 0: # IS_LINUX or IS_RPI:
744
+ self.setAutoFillBackground(False)
745
+ self.setAttribute(qt_window_attributes.WA_NoSystemBackground, True)
746
+ self.setAttribute(qt_window_attributes.WA_OpaquePaintEvent, True)
747
+ elif IS_WIN:
748
+ self.setAttribute(qt_window_attributes.WA_PaintOnScreen, True)
749
+ self.setAutoFillBackground(False)
750
+
751
+ # Init surface
752
+ w = self.get_window_id()
753
+ self._surface = egl.eglCreateWindowSurface(_EGL_DISPLAY, c, w)
754
+ self.initializeGL()
755
+ self._initialized = True
756
+
757
+ def get_window_id(self):
758
+ """Get the window id of a PySide Widget. Might also work for PyQt4."""
759
+ # Get Qt win id
760
+ winid = self.winId()
761
+
762
+ # On Linux this is it
763
+ if IS_RPI:
764
+ nw = (ctypes.c_int * 3)(winid, self.width(), self.height())
765
+ return ctypes.pointer(nw)
766
+ elif IS_LINUX:
767
+ return int(winid) # Is int on PySide, but sip.voidptr on PyQt
768
+
769
+ # Get window id from stupid capsule thingy
770
+ # http://translate.google.com/translate?hl=en&sl=zh-CN&u=http://www.cnb
771
+ # logs.com/Shiren-Y/archive/2011/04/06/2007288.html&prev=/search%3Fq%3Dp
772
+ # yside%2Bdirectx%26client%3Dfirefox-a%26hs%3DIsJ%26rls%3Dorg.mozilla:n
773
+ # l:official%26channel%3Dfflb%26biw%3D1366%26bih%3D614
774
+ # Prepare
775
+ ctypes.pythonapi.PyCapsule_GetName.restype = ctypes.c_char_p
776
+ ctypes.pythonapi.PyCapsule_GetName.argtypes = [ctypes.py_object]
777
+ ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
778
+ ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object,
779
+ ctypes.c_char_p]
780
+ # Extract handle from capsule thingy
781
+ name = ctypes.pythonapi.PyCapsule_GetName(winid)
782
+ handle = ctypes.pythonapi.PyCapsule_GetPointer(winid, name)
783
+ return handle
784
+
785
+ def _vispy_close(self):
786
+ # Destroy EGL surface
787
+ if self._surface is not None:
788
+ egl.eglDestroySurface(_EGL_DISPLAY, self._surface)
789
+ self._surface = None
790
+ # Force the window or widget to shut down
791
+ self.close()
792
+
793
+ def _vispy_set_current(self):
794
+ egl.eglMakeCurrent(_EGL_DISPLAY, self._surface,
795
+ self._surface, self._native_context)
796
+
797
+ def _vispy_swap_buffers(self):
798
+ egl.eglSwapBuffers(_EGL_DISPLAY, self._surface)
799
+
800
+ def initializeGL(self):
801
+ self._vispy_canvas.set_current()
802
+ self._vispy_canvas.events.initialize()
803
+
804
+ def resizeEvent(self, event):
805
+ w, h = event.size().width(), event.size().height()
806
+ vispy_event = self._vispy_canvas.events.resize(size=(w, h))
807
+ # If vispy did not handle the event, clear the accept parameter of the qt event
808
+ if not vispy_event.handled:
809
+ event.ignore()
810
+
811
+ def paintEvent(self, event):
812
+ self._vispy_canvas.events.draw(region=None)
813
+
814
+ if IS_LINUX or IS_RPI:
815
+ # Arg, cannot get GL to draw to the widget, so we take a
816
+ # screenshot and draw that for now ...
817
+ # Further, QImage keeps a ref to the data that we pass, so
818
+ # we need to use a static buffer to prevent memory leakage
819
+ from ... import gloo
820
+ import numpy as np
821
+ if not hasattr(self, '_gl_buffer'):
822
+ self._gl_buffer = np.ones((3000 * 3000 * 4), np.uint8) * 255
823
+ # Take screenshot and turn into RGB QImage
824
+ im = gloo.read_pixels()
825
+ sze = im.shape[0] * im.shape[1]
826
+ self._gl_buffer[0:0+sze*4:4] = im[:, :, 2].ravel()
827
+ self._gl_buffer[1:0+sze*4:4] = im[:, :, 1].ravel()
828
+ self._gl_buffer[2:2+sze*4:4] = im[:, :, 0].ravel()
829
+ img = QtGui.QImage(self._gl_buffer, im.shape[1], im.shape[0],
830
+ QtGui.QImage.Format_RGB32)
831
+ # Paint the image
832
+ painter = QtGui.QPainter()
833
+ painter.begin(self)
834
+ rect = QtCore.QRect(0, 0, self.width(), self.height())
835
+ painter.drawImage(rect, img)
836
+ painter.end()
837
+
838
+ def paintEngine(self):
839
+ if IS_LINUX and not IS_RPI:
840
+ # For now we are drawing a screenshot
841
+ return QWidget.paintEngine(self)
842
+ else:
843
+ return None # Disable Qt's native drawing system
844
+
845
+
846
+ class CanvasBackendDesktop(QtBaseCanvasBackend, QGLWidget):
847
+
848
+ def _init_specific(self, p, kwargs):
849
+
850
+ # Deal with config
851
+ glformat = _set_config(p.context.config)
852
+ glformat.setSwapInterval(1 if p.vsync else 0)
853
+ # Deal with context
854
+ widget = kwargs.pop('shareWidget', None) or self
855
+ p.context.shared.add_ref('qt', widget)
856
+ if p.context.shared.ref is widget:
857
+ if widget is self:
858
+ widget = None # QGLWidget does not accept self ;)
859
+ else:
860
+ widget = p.context.shared.ref
861
+ if 'shareWidget' in kwargs:
862
+ raise RuntimeError('Cannot use vispy to share context and '
863
+ 'use built-in shareWidget.')
864
+
865
+ qt_window_types = QtCore.Qt.WindowType if PYQT6_API else QtCore.Qt
866
+ if p.always_on_top or not p.decorate:
867
+ hint = 0
868
+ hint |= 0 if p.decorate else qt_window_types.FramelessWindowHint
869
+ hint |= qt_window_types.WindowStaysOnTopHint if p.always_on_top else 0
870
+ else:
871
+ hint = qt_window_types.Widget # can also be a window type
872
+
873
+ if QT5_NEW_API or PYSIDE6_API or PYQT6_API:
874
+ # Qt5 >= 5.4.0 - sharing is automatic
875
+ QGLWidget.__init__(self, p.parent, hint)
876
+
877
+ # Need to create an offscreen surface so we can get GL parameters
878
+ # without opening/showing the Widget. PyQt5 >= 5.4 will create the
879
+ # valid context later when the widget is shown.
880
+ self._secondary_context = QtGui.QOpenGLContext()
881
+ self._secondary_context.setShareContext(self.context())
882
+ self._secondary_context.setFormat(glformat)
883
+ self._secondary_context.create()
884
+
885
+ self._surface = QtGui.QOffscreenSurface()
886
+ self._surface.setFormat(glformat)
887
+ self._surface.create()
888
+ self._secondary_context.makeCurrent(self._surface)
889
+ else:
890
+ # Qt4 and Qt5 < 5.4.0 - sharing is explicitly requested
891
+ QGLWidget.__init__(self, p.parent, widget, hint)
892
+ # unused with this API
893
+ self._secondary_context = None
894
+ self._surface = None
895
+
896
+ self.setFormat(glformat)
897
+ self._initialized = True
898
+ if not QT5_NEW_API and not PYSIDE6_API and not PYQT6_API and not self.isValid():
899
+ # On Qt5 >= 5.4.0, isValid is only true once the widget is shown
900
+ raise RuntimeError('context could not be created')
901
+ if not QT5_NEW_API and not PYSIDE6_API and not PYQT6_API:
902
+ # to make consistent with other backends
903
+ self.setAutoBufferSwap(False)
904
+ qt_focus_policies = QtCore.Qt.FocusPolicy if PYQT6_API else QtCore.Qt
905
+ self.setFocusPolicy(qt_focus_policies.WheelFocus)
906
+
907
+ def _vispy_close(self):
908
+ # Force the window or widget to shut down
909
+ self.close()
910
+ self.doneCurrent()
911
+ if not QT5_NEW_API and not PYSIDE6_API and not PYQT6_API:
912
+ self.context().reset()
913
+ if self._vispy_canvas is not None:
914
+ self._vispy_canvas.app.process_events()
915
+ self._vispy_canvas.app.process_events()
916
+
917
+ def _vispy_set_current(self):
918
+ if self._vispy_canvas is None:
919
+ return # todo: can we get rid of this now?
920
+ if self.isValid():
921
+ self.makeCurrent()
922
+
923
+ def _vispy_swap_buffers(self):
924
+ # Swap front and back buffer
925
+ if self._vispy_canvas is None:
926
+ return
927
+ if QT5_NEW_API or PYSIDE6_API or PYQT6_API:
928
+ ctx = self.context()
929
+ ctx.swapBuffers(ctx.surface())
930
+ else:
931
+ self.swapBuffers()
932
+
933
+ def _vispy_get_fb_bind_location(self):
934
+ if QT5_NEW_API or PYSIDE6_API or PYQT6_API:
935
+ return self.defaultFramebufferObject()
936
+ else:
937
+ return QtBaseCanvasBackend._vispy_get_fb_bind_location(self)
938
+
939
+ def initializeGL(self):
940
+ if self._vispy_canvas is None:
941
+ return
942
+ self._vispy_canvas.events.initialize()
943
+
944
+ def resizeGL(self, w, h):
945
+ if self._vispy_canvas is None:
946
+ return
947
+ if hasattr(self, 'devicePixelRatioF'):
948
+ # We take into account devicePixelRatioF, which is non-unity on
949
+ # e.g HiDPI displays.
950
+ # self.devicePixelRatioF() is a float and should have been in Qt5 according to the documentation
951
+ ratio = self.devicePixelRatioF()
952
+ w = int(w * ratio)
953
+ h = int(h * ratio)
954
+ self._vispy_set_physical_size(w, h)
955
+ self._vispy_canvas.events.resize(size=(self.width(), self.height()),
956
+ physical_size=(w, h))
957
+
958
+ def paintGL(self):
959
+ if self._vispy_canvas is None:
960
+ return
961
+ # (0, 0, self.width(), self.height()))
962
+ self._vispy_canvas.set_current()
963
+ self._vispy_canvas.events.draw(region=None)
964
+
965
+ # Clear the alpha channel with QOpenGLWidget (Qt >= 5.4), otherwise the
966
+ # window is translucent behind non-opaque objects.
967
+ # Reference: MRtrix3/mrtrix3#266
968
+ if QT5_NEW_API or PYSIDE6_API or PYQT6_API:
969
+ context = self._vispy_canvas.context
970
+ context.set_color_mask(False, False, False, True)
971
+ context.clear(color=True, depth=False, stencil=False)
972
+ context.set_color_mask(True, True, True, True)
973
+ context.flush()
974
+
975
+
976
+ # Select CanvasBackend
977
+ if USE_EGL:
978
+ CanvasBackend = CanvasBackendEgl
979
+ else:
980
+ CanvasBackend = CanvasBackendDesktop
981
+
982
+
983
+ # ------------------------------------------------------------------- timer ---
984
+
985
+ class TimerBackend(BaseTimerBackend, QtCore.QTimer):
986
+
987
+ def __init__(self, vispy_timer):
988
+ # Make sure there is an app
989
+ app = ApplicationBackend()
990
+ app._vispy_get_native_app()
991
+ # Init
992
+ BaseTimerBackend.__init__(self, vispy_timer)
993
+ QtCore.QTimer.__init__(self)
994
+ self.timeout.connect(self._vispy_timeout)
995
+
996
+ def _vispy_start(self, interval):
997
+ self.start(int(interval * 1000))
998
+
999
+ def _vispy_stop(self):
1000
+ self.stop()
1001
+
1002
+ def _vispy_timeout(self):
1003
+ self._vispy_timer._timeout()