Kivy-examples 2.3.1.dev0__py3-none-any.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.
Files changed (624) hide show
  1. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/3Drendering/main.py +77 -0
  2. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/3Drendering/monkey.obj +7966 -0
  3. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/3Drendering/objloader.py +147 -0
  4. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/3Drendering/simple.glsl +47 -0
  5. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/RST_Editor/editor.kv +72 -0
  6. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/RST_Editor/main.py +64 -0
  7. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/android/compass/android.txt +3 -0
  8. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/android/compass/compass.kv +25 -0
  9. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/android/compass/main.py +77 -0
  10. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/android/compass/needle.png +0 -0
  11. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/android/compass/rose.png +0 -0
  12. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/android/takepicture/android.txt +3 -0
  13. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/android/takepicture/main.py +80 -0
  14. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/android/takepicture/shadow32.png +0 -0
  15. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/android/takepicture/takepicture.kv +40 -0
  16. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/animation/animate.py +42 -0
  17. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/application/app_suite.py +152 -0
  18. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/application/app_suite_data/testkvdir.kv +4 -0
  19. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/application/app_with_build.py +24 -0
  20. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/application/app_with_config.py +49 -0
  21. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/application/app_with_kv.py +25 -0
  22. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/application/app_with_kv_in_template1.py +27 -0
  23. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/application/template1/test.kv +4 -0
  24. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/application/test.kv +4 -0
  25. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/application/testkvfile.kv +4 -0
  26. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/async/asyncio_advanced.py +90 -0
  27. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/async/asyncio_basic.py +59 -0
  28. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/async/trio_advanced.py +93 -0
  29. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/async/trio_basic.py +58 -0
  30. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12908_sweet_trip_mm_clap_hi.wav +0 -0
  31. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12909_sweet_trip_mm_clap_lo.wav +0 -0
  32. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12910_sweet_trip_mm_clap_mid.wav +0 -0
  33. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12911_sweet_trip_mm_hat_cl.wav +0 -0
  34. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12913_sweet_trip_mm_kick_hi.wav +0 -0
  35. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12914_sweet_trip_mm_kick_lo.wav +0 -0
  36. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12915_sweet_trip_mm_kick_mid.wav +0 -0
  37. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12916_sweet_trip_mm_kwik_mod_01.wav +0 -0
  38. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12917_sweet_trip_mm_kwik_mod_02.wav +0 -0
  39. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12918_sweet_trip_mm_kwik_mod_03.wav +0 -0
  40. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12919_sweet_trip_mm_kwik_mod_04.wav +0 -0
  41. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12920_sweet_trip_mm_kwik_mod_05.wav +0 -0
  42. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12921_sweet_trip_mm_kwik_mod_06.wav +0 -0
  43. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12922_sweet_trip_mm_kwik_mod_07.wav +0 -0
  44. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12923_sweet_trip_mm_metal_clave.wav +0 -0
  45. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12925_sweet_trip_mm_sweep_x.wav +0 -0
  46. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12926_sweet_trip_mm_sweep_y.wav +0 -0
  47. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/12927_sweet_trip_mm_sweep_z.wav +0 -0
  48. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/audio.kv +59 -0
  49. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/main.py +81 -0
  50. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/audio/pitch.py +42 -0
  51. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/camera/main.py +59 -0
  52. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/bezier.py +105 -0
  53. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/canvas_stress.py +72 -0
  54. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/circle.py +84 -0
  55. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/fbo_canvas.py +91 -0
  56. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/kiwi.jpg +0 -0
  57. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/lines.py +296 -0
  58. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/lines_extended.py +183 -0
  59. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/mesh.py +57 -0
  60. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/mesh_manipulation.py +101 -0
  61. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/mtexture1.png +0 -0
  62. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/mtexture2.png +0 -0
  63. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/multitexture.py +110 -0
  64. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/repeat_texture.py +65 -0
  65. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/rotation.py +35 -0
  66. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/rounded_rectangle.py +153 -0
  67. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/scale.py +37 -0
  68. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/stencil_canvas.py +85 -0
  69. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/tesselate.py +146 -0
  70. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/texture.py +143 -0
  71. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/canvas/texture_example_image.png +0 -0
  72. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/container/kv/1.kv +28 -0
  73. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/container/kv/2.kv +28 -0
  74. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/container/kv/3.kv +28 -0
  75. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/container/kv/root.kv +32 -0
  76. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/container/main.py +63 -0
  77. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/cover/cover_image.py +25 -0
  78. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/cover/cover_video.py +31 -0
  79. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/camera_puzzle.py +98 -0
  80. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/AnchorLayoutContainer.kv +15 -0
  81. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/BoxLayoutContainer.kv +14 -0
  82. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/ButtonContainer.kv +27 -0
  83. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/CheckBoxContainer.kv +28 -0
  84. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/FileChooserContainer.kv +19 -0
  85. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/FloatLayoutContainer.kv +16 -0
  86. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/GridLayoutContainer.kv +20 -0
  87. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/LabelContainer.kv +62 -0
  88. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/MediaContainer.kv +9 -0
  89. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/PlaygroundContainer.kv +4 -0
  90. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/PopupContainer.kv +39 -0
  91. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/ProgressBarContainer.kv +17 -0
  92. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/RestContainer.kv +5 -0
  93. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/ScatterContainer.kv +16 -0
  94. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/SelectorsContainer.kv +10 -0
  95. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/StackLayoutContainer.kv +25 -0
  96. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/container_kvs/TextContainer.kv +20 -0
  97. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/kivycatalog.kv +144 -0
  98. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/kivycatalog/main.py +192 -0
  99. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/multistroke/gesturedatabase.kv +117 -0
  100. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/multistroke/gesturedatabase.py +180 -0
  101. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/multistroke/helpers.py +30 -0
  102. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/multistroke/historymanager.kv +164 -0
  103. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/multistroke/historymanager.py +276 -0
  104. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/multistroke/main.py +144 -0
  105. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/multistroke/multistroke.kv +101 -0
  106. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/multistroke/settings.kv +110 -0
  107. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/multistroke/settings.py +97 -0
  108. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/pictures/android.txt +3 -0
  109. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/pictures/images/Bubbles.jpg +0 -0
  110. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/pictures/images/Ill1.jpg +0 -0
  111. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/pictures/images/Wall.jpg +0 -0
  112. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/pictures/images/faust_github.jpg +0 -0
  113. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/pictures/main.py +73 -0
  114. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/pictures/pictures.kv +57 -0
  115. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/pictures/shadow32.png +0 -0
  116. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/shadereditor/main.py +147 -0
  117. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/shadereditor/shadereditor.kv +39 -0
  118. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/README.txt +11 -0
  119. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/android.txt +3 -0
  120. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/background.png +0 -0
  121. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/faust_github.jpg +0 -0
  122. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/icons/README +4 -0
  123. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/icons/bug.png +0 -0
  124. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/icons/chevron-left.png +0 -0
  125. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/icons/chevron-right.png +0 -0
  126. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/accordions.kv +39 -0
  127. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/bubbles.kv +33 -0
  128. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/buttons.kv +26 -0
  129. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/carousel.kv +51 -0
  130. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/checkboxes.kv +33 -0
  131. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/codeinput.kv +9 -0
  132. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/dropdown.kv +40 -0
  133. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/filechoosers.kv +24 -0
  134. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/popups.kv +17 -0
  135. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/progressbar.kv +15 -0
  136. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/rstdocument.kv +24 -0
  137. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/scatter.kv +14 -0
  138. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/screenmanager.kv +46 -0
  139. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/sliders.kv +55 -0
  140. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/spinner.kv +11 -0
  141. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/splitter.kv +13 -0
  142. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/switches.kv +39 -0
  143. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/tabbedpanel + layouts.kv +69 -0
  144. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/textinputs.kv +48 -0
  145. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/data/screens/togglebutton.kv +39 -0
  146. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/main.py +243 -0
  147. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/showcase/showcase.kv +100 -0
  148. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/touchtracer/README.txt +12 -0
  149. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/touchtracer/android.txt +3 -0
  150. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/touchtracer/icon.png +0 -0
  151. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/touchtracer/main.py +170 -0
  152. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/touchtracer/particle.png +0 -0
  153. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/demo/touchtracer/touchtracer.kv +29 -0
  154. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/frameworks/twisted/echo_client_app.py +82 -0
  155. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/frameworks/twisted/echo_server_app.py +49 -0
  156. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/frameworks/twisted/twistd_app.py +66 -0
  157. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/gestures/gesture_board.py +95 -0
  158. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/gestures/my_gestures.py +90 -0
  159. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/guide/designwithkv/controller.kv +16 -0
  160. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/guide/designwithkv/main.py +29 -0
  161. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/guide/firstwidget/1_skeleton.py +15 -0
  162. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/guide/firstwidget/2_print_touch.py +16 -0
  163. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/guide/firstwidget/3_draw_ellipse.py +22 -0
  164. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/guide/firstwidget/4_draw_line.py +26 -0
  165. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/guide/firstwidget/5_random_colors.py +28 -0
  166. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/guide/firstwidget/6_button.py +38 -0
  167. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/guide/quickstart/main.py +15 -0
  168. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/includes/button.kv +9 -0
  169. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/includes/layout.kv +6 -0
  170. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/includes/main.py +19 -0
  171. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/includes/test.kv +5 -0
  172. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/keyboard/android.txt +3 -0
  173. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/keyboard/main.py +226 -0
  174. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/keyboard/numeric.json +38 -0
  175. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kinect/README.txt +13 -0
  176. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kinect/kinectviewer.py +283 -0
  177. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/app_button.kv +9 -0
  178. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/app_camera.kv +25 -0
  179. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/app_fbo.kv +11 -0
  180. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/app_layout.kv +23 -0
  181. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/app_logo.kv +36 -0
  182. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/app_scatter.kv +6 -0
  183. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/app_stencil.kv +27 -0
  184. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/app_video.kv +42 -0
  185. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/builder_template.py +28 -0
  186. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/ids/id_in_kv/id_in_kv.py +20 -0
  187. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/ids/id_in_kv/test.kv +11 -0
  188. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/ids/kv_and_py/kv_and_py.py +34 -0
  189. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/ids/kv_and_py/test.kv +11 -0
  190. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/kivy.jpg +0 -0
  191. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/kv/kvrun.py +38 -0
  192. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/clipboard.py +63 -0
  193. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/custom_titlebar.py +56 -0
  194. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/imagesave.py +14 -0
  195. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/joystick.py +83 -0
  196. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/multiple_dropfile.py +51 -0
  197. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/on_textedit_event.py +104 -0
  198. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/opacitywindow.py +28 -0
  199. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/shapecollisions.py +397 -0
  200. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/shapedwindow.py +98 -0
  201. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/two_panes.py +47 -0
  202. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/miscellaneous/urlrequest.py +128 -0
  203. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/settings/android.txt +3 -0
  204. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/settings/main.py +122 -0
  205. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/shader/plasma.kv +9 -0
  206. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/shader/plasma.py +86 -0
  207. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/shader/rotated.kv +91 -0
  208. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/shader/rotated.py +102 -0
  209. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/shader/shadertree.kv +29 -0
  210. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/shader/shadertree.py +193 -0
  211. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/shader/tex3.jpg +0 -0
  212. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/svg/benchmark.py +19 -0
  213. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/svg/cloud.svg +25 -0
  214. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/svg/main-smaa.py +155 -0
  215. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/svg/main.py +50 -0
  216. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/svg/music.svg +68 -0
  217. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/svg/rotated.svg +23 -0
  218. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/svg/ship.svg +181 -0
  219. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/svg/sun.svg +35 -0
  220. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/svg/tiger.svg +813 -0
  221. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/text/pango_demo.py +296 -0
  222. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/notes/final/data/icon.png +0 -0
  223. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/notes/final/main.py +163 -0
  224. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/notes/final/note.kv +154 -0
  225. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/main.py +82 -0
  226. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/pong.kv +53 -0
  227. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/steps/step1/main.py +15 -0
  228. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/steps/step1/pong.kv +2 -0
  229. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/steps/step2/main.py +15 -0
  230. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/steps/step2/pong.kv +21 -0
  231. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/steps/step3/main.py +26 -0
  232. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/steps/step3/pong.kv +31 -0
  233. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/steps/step4/main.py +48 -0
  234. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/steps/step4/pong.kv +34 -0
  235. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/steps/step5/main.py +75 -0
  236. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/tutorials/pong/steps/step5/pong.kv +53 -0
  237. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/accordion_1.py +17 -0
  238. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/actionbar.py +32 -0
  239. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/asyncimage.py +35 -0
  240. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/boxlayout_poshint.py +54 -0
  241. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/bubble_test.py +63 -0
  242. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/camera.py +33 -0
  243. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/carousel_buttons.py +50 -0
  244. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/cityCC0.mpg +0 -0
  245. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/cityCC0.png +0 -0
  246. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/codeinput.py +225 -0
  247. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/codeinputtest.kv +53 -0
  248. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/colorpicker.py +234 -0
  249. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/colorusage.py +62 -0
  250. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/compound_selection.py +83 -0
  251. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/customcollide.py +82 -0
  252. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/effectwidget.py +193 -0
  253. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/effectwidget2.py +47 -0
  254. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/effectwidget3_advanced.py +62 -0
  255. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/fbowidget.py +111 -0
  256. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/focus_behavior.py +98 -0
  257. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/image_mipmap.py +31 -0
  258. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/keyboardlistener.py +42 -0
  259. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/label_mipmap.py +28 -0
  260. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/label_sizing.py +193 -0
  261. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/label_text_size.py +49 -0
  262. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/label_with_markup.py +21 -0
  263. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/lang_dynamic_classes.py +18 -0
  264. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/pagelayout.py +64 -0
  265. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/popup_with_kv.py +35 -0
  266. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/recycleview/basic_data.py +124 -0
  267. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/recycleview/infinite_scrolling.py +143 -0
  268. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/recycleview/key_viewclass.py +149 -0
  269. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/recycleview/messenger.py +196 -0
  270. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/recycleview/pull_to_refresh.py +95 -0
  271. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/recycleview/rv_animate_items.py +109 -0
  272. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/rstexample.py +128 -0
  273. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/scatter.kv +53 -0
  274. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/scatter.py +16 -0
  275. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/screenmanager.py +106 -0
  276. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/scrollview.kv +16 -0
  277. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/scrollview.py +40 -0
  278. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/android.txt +3 -0
  279. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/data/images/bird.zip +0 -0
  280. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/data/images/button_white.png +0 -0
  281. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/data/images/button_white_animated.zip +0 -0
  282. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/data/images/cube.zip +0 -0
  283. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/data/images/info.png +0 -0
  284. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/data/images/info.zip +0 -0
  285. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/data/images/simple_cv_joint_animated.gif +0 -0
  286. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/main.kv +63 -0
  287. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/main.py +150 -0
  288. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/uix/__init__.py +7 -0
  289. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/sequenced_images/uix/custom_button.py +98 -0
  290. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/settings.py +93 -0
  291. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/shorten_text.py +80 -0
  292. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/spinner.py +17 -0
  293. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/splitter.py +74 -0
  294. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/tabbed_panel_showcase.py +316 -0
  295. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/tabbedpanel.py +50 -0
  296. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/textalign.kv +49 -0
  297. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/textalign.py +48 -0
  298. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/textinput.py +59 -0
  299. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/unicode_textinput.py +244 -0
  300. Kivy_examples-2.3.1.dev0.data/data/share/kivy-examples/widgets/videoplayer.py +26 -0
  301. Kivy_examples-2.3.1.dev0.dist-info/AUTHORS +66 -0
  302. Kivy_examples-2.3.1.dev0.dist-info/LICENSE +21 -0
  303. Kivy_examples-2.3.1.dev0.dist-info/METADATA +382 -0
  304. Kivy_examples-2.3.1.dev0.dist-info/RECORD +624 -0
  305. Kivy_examples-2.3.1.dev0.dist-info/WHEEL +5 -0
  306. Kivy_examples-2.3.1.dev0.dist-info/dependency_links.txt +1 -0
  307. Kivy_examples-2.3.1.dev0.dist-info/top_level.txt +1 -0
  308. kivy/__init__.py +519 -0
  309. kivy/_version.py +18 -0
  310. kivy/animation.py +831 -0
  311. kivy/app.py +1213 -0
  312. kivy/atlas.py +456 -0
  313. kivy/base.py +617 -0
  314. kivy/cache.py +262 -0
  315. kivy/clock.py +1172 -0
  316. kivy/compat.py +82 -0
  317. kivy/config.py +1004 -0
  318. kivy/context.py +102 -0
  319. kivy/core/__init__.py +247 -0
  320. kivy/core/audio/__init__.py +223 -0
  321. kivy/core/audio/audio_android.py +104 -0
  322. kivy/core/audio/audio_avplayer.py +82 -0
  323. kivy/core/audio/audio_ffpyplayer.py +185 -0
  324. kivy/core/audio/audio_gstplayer.py +101 -0
  325. kivy/core/audio/audio_pygame.py +127 -0
  326. kivy/core/camera/__init__.py +151 -0
  327. kivy/core/camera/camera_android.py +206 -0
  328. kivy/core/camera/camera_gi.py +170 -0
  329. kivy/core/camera/camera_opencv.py +163 -0
  330. kivy/core/camera/camera_picamera.py +96 -0
  331. kivy/core/clipboard/__init__.py +157 -0
  332. kivy/core/clipboard/_clipboard_ext.py +36 -0
  333. kivy/core/clipboard/clipboard_android.py +91 -0
  334. kivy/core/clipboard/clipboard_dbusklipper.py +41 -0
  335. kivy/core/clipboard/clipboard_dummy.py +26 -0
  336. kivy/core/clipboard/clipboard_gtk3.py +47 -0
  337. kivy/core/clipboard/clipboard_nspaste.py +44 -0
  338. kivy/core/clipboard/clipboard_pygame.py +67 -0
  339. kivy/core/clipboard/clipboard_sdl2.py +36 -0
  340. kivy/core/clipboard/clipboard_winctypes.py +107 -0
  341. kivy/core/clipboard/clipboard_xclip.py +29 -0
  342. kivy/core/clipboard/clipboard_xsel.py +26 -0
  343. kivy/core/gl/__init__.py +90 -0
  344. kivy/core/image/__init__.py +996 -0
  345. kivy/core/image/img_dds.py +40 -0
  346. kivy/core/image/img_ffpyplayer.py +87 -0
  347. kivy/core/image/img_pil.py +123 -0
  348. kivy/core/image/img_pygame.py +119 -0
  349. kivy/core/image/img_sdl2.py +66 -0
  350. kivy/core/image/img_tex.py +58 -0
  351. kivy/core/spelling/__init__.py +135 -0
  352. kivy/core/spelling/spelling_enchant.py +50 -0
  353. kivy/core/spelling/spelling_osxappkit.py +64 -0
  354. kivy/core/text/__init__.py +1090 -0
  355. kivy/core/text/markup.py +893 -0
  356. kivy/core/text/text_pango.py +145 -0
  357. kivy/core/text/text_pil.py +84 -0
  358. kivy/core/text/text_pygame.py +117 -0
  359. kivy/core/text/text_sdl2.py +50 -0
  360. kivy/core/video/__init__.py +219 -0
  361. kivy/core/video/video_ffmpeg.py +106 -0
  362. kivy/core/video/video_ffpyplayer.py +449 -0
  363. kivy/core/video/video_gstplayer.py +140 -0
  364. kivy/core/video/video_null.py +12 -0
  365. kivy/core/window/__init__.py +2515 -0
  366. kivy/core/window/window_egl_rpi.py +86 -0
  367. kivy/core/window/window_pygame.py +441 -0
  368. kivy/core/window/window_sdl2.py +999 -0
  369. kivy/deps/__init__.py +6 -0
  370. kivy/effects/__init__.py +23 -0
  371. kivy/effects/dampedscroll.py +114 -0
  372. kivy/effects/kinetic.py +193 -0
  373. kivy/effects/opacityscroll.py +27 -0
  374. kivy/effects/scroll.py +126 -0
  375. kivy/event.py +12 -0
  376. kivy/eventmanager/__init__.py +185 -0
  377. kivy/extras/__init__.py +0 -0
  378. kivy/extras/highlight.py +52 -0
  379. kivy/factory.py +190 -0
  380. kivy/factory_registers.py +201 -0
  381. kivy/garden/__init__.py +186 -0
  382. kivy/geometry.py +121 -0
  383. kivy/gesture.py +424 -0
  384. kivy/graphics/__init__.py +126 -0
  385. kivy/graphics/cgl_backend/__init__.py +0 -0
  386. kivy/input/__init__.py +39 -0
  387. kivy/input/factory.py +35 -0
  388. kivy/input/motionevent.py +587 -0
  389. kivy/input/postproc/__init__.py +27 -0
  390. kivy/input/postproc/calibration.py +200 -0
  391. kivy/input/postproc/dejitter.py +74 -0
  392. kivy/input/postproc/doubletap.py +101 -0
  393. kivy/input/postproc/ignorelist.py +47 -0
  394. kivy/input/postproc/retaintouch.py +93 -0
  395. kivy/input/postproc/tripletap.py +106 -0
  396. kivy/input/provider.py +40 -0
  397. kivy/input/providers/__init__.py +68 -0
  398. kivy/input/providers/androidjoystick.py +117 -0
  399. kivy/input/providers/hidinput.py +778 -0
  400. kivy/input/providers/leapfinger.py +113 -0
  401. kivy/input/providers/linuxwacom.py +396 -0
  402. kivy/input/providers/mactouch.py +220 -0
  403. kivy/input/providers/mouse.py +424 -0
  404. kivy/input/providers/mtdev.py +380 -0
  405. kivy/input/providers/probesysfs.py +254 -0
  406. kivy/input/providers/tuio.py +326 -0
  407. kivy/input/providers/wm_common.py +162 -0
  408. kivy/input/providers/wm_pen.py +121 -0
  409. kivy/input/providers/wm_touch.py +157 -0
  410. kivy/input/recorder.py +335 -0
  411. kivy/input/shape.py +27 -0
  412. kivy/interactive.py +335 -0
  413. kivy/lang/__init__.py +883 -0
  414. kivy/lang/builder.py +1004 -0
  415. kivy/lang/parser.py +822 -0
  416. kivy/lib/__init__.py +21 -0
  417. kivy/lib/ddsfile.py +419 -0
  418. kivy/lib/gstplayer/__init__.py +27 -0
  419. kivy/lib/mtdev.py +254 -0
  420. kivy/lib/vidcore_lite/__init__.py +0 -0
  421. kivy/loader.py +579 -0
  422. kivy/logger.py +646 -0
  423. kivy/metrics.py +421 -0
  424. kivy/modules/__init__.py +300 -0
  425. kivy/modules/_webdebugger.py +200 -0
  426. kivy/modules/console.py +1059 -0
  427. kivy/modules/cursor.py +75 -0
  428. kivy/modules/inspector.py +755 -0
  429. kivy/modules/joycursor.py +299 -0
  430. kivy/modules/keybinding.py +64 -0
  431. kivy/modules/monitor.py +90 -0
  432. kivy/modules/recorder.py +94 -0
  433. kivy/modules/screen.py +183 -0
  434. kivy/modules/showborder.py +33 -0
  435. kivy/modules/touchring.py +97 -0
  436. kivy/modules/webdebugger.py +30 -0
  437. kivy/multistroke.py +1477 -0
  438. kivy/network/__init__.py +7 -0
  439. kivy/network/urlrequest.py +794 -0
  440. kivy/parser.py +154 -0
  441. kivy/resources.py +102 -0
  442. kivy/storage/__init__.py +405 -0
  443. kivy/storage/dictstore.py +98 -0
  444. kivy/storage/jsonstore.py +92 -0
  445. kivy/storage/redisstore.py +103 -0
  446. kivy/support.py +267 -0
  447. kivy/tests/__init__.py +11 -0
  448. kivy/tests/async_common.py +568 -0
  449. kivy/tests/common.py +524 -0
  450. kivy/tests/conftest.py +33 -0
  451. kivy/tests/fixtures.py +169 -0
  452. kivy/tests/perf_test_textinput.py +195 -0
  453. kivy/tests/pyinstaller/simple_widget/main.py +12 -0
  454. kivy/tests/pyinstaller/simple_widget/project/__init__.py +0 -0
  455. kivy/tests/pyinstaller/simple_widget/project/widget.py +12 -0
  456. kivy/tests/pyinstaller/test_pyinstaller.py +115 -0
  457. kivy/tests/pyinstaller/video_widget/main.py +6 -0
  458. kivy/tests/pyinstaller/video_widget/project/__init__.py +38 -0
  459. kivy/tests/test_animations.py +415 -0
  460. kivy/tests/test_app.py +216 -0
  461. kivy/tests/test_audio.py +69 -0
  462. kivy/tests/test_benchmark.py +292 -0
  463. kivy/tests/test_clipboard.py +48 -0
  464. kivy/tests/test_clock.py +292 -0
  465. kivy/tests/test_compat.py +6 -0
  466. kivy/tests/test_config.py +35 -0
  467. kivy/tests/test_coverage.py +83 -0
  468. kivy/tests/test_doc_gallery.py +38 -0
  469. kivy/tests/test_environ_cli.py +50 -0
  470. kivy/tests/test_fbo_py2py3.py +74 -0
  471. kivy/tests/test_filechooser.py +11 -0
  472. kivy/tests/test_filechooser_unicode.py +82 -0
  473. kivy/tests/test_fonts.py +42 -0
  474. kivy/tests/test_garden.py +23 -0
  475. kivy/tests/test_graphics.py +1149 -0
  476. kivy/tests/test_graphics_svg.py +84 -0
  477. kivy/tests/test_image.py +74 -0
  478. kivy/tests/test_imageloader.py +485 -0
  479. kivy/tests/test_invalid_lang.py +42 -0
  480. kivy/tests/test_issues/test_6315.py +55 -0
  481. kivy/tests/test_issues/test_issue_1084.py +47 -0
  482. kivy/tests/test_issues/test_issue_1091.py +19 -0
  483. kivy/tests/test_issues/test_issue_599.py +21 -0
  484. kivy/tests/test_issues/test_issue_609.py +19 -0
  485. kivy/tests/test_issues/test_issue_6909.py +28 -0
  486. kivy/tests/test_issues/test_issue_883.py +12 -0
  487. kivy/tests/test_kivy_init.py +30 -0
  488. kivy/tests/test_knspace.py +247 -0
  489. kivy/tests/test_lang.py +431 -0
  490. kivy/tests/test_lang_complex.py +87 -0
  491. kivy/tests/test_lang_pre_process_and_post_process.py +394 -0
  492. kivy/tests/test_logger.py +516 -0
  493. kivy/tests/test_metrics.py +30 -0
  494. kivy/tests/test_module_inspector.py +382 -0
  495. kivy/tests/test_motion_event.py +44 -0
  496. kivy/tests/test_mouse_hover_event.py +249 -0
  497. kivy/tests/test_mouse_multitouchsim.py +554 -0
  498. kivy/tests/test_multistroke.py +389 -0
  499. kivy/tests/test_properties.py +1332 -0
  500. kivy/tests/test_resources.py +79 -0
  501. kivy/tests/test_rst_replace.py +63 -0
  502. kivy/tests/test_screen.py +38 -0
  503. kivy/tests/test_storage.py +135 -0
  504. kivy/tests/test_uix_actionbar.py +363 -0
  505. kivy/tests/test_uix_anchorlayout.py +79 -0
  506. kivy/tests/test_uix_asyncimage.py +133 -0
  507. kivy/tests/test_uix_boxlayout.py +94 -0
  508. kivy/tests/test_uix_bubble.py +425 -0
  509. kivy/tests/test_uix_carousel.py +93 -0
  510. kivy/tests/test_uix_colorpicker.py +77 -0
  511. kivy/tests/test_uix_dropdown.py +91 -0
  512. kivy/tests/test_uix_gridlayout.py +244 -0
  513. kivy/tests/test_uix_layout.py +16 -0
  514. kivy/tests/test_uix_modal.py +101 -0
  515. kivy/tests/test_uix_recyclegridlayout.py +383 -0
  516. kivy/tests/test_uix_relativelayout.py +30 -0
  517. kivy/tests/test_uix_scrollview.py +358 -0
  518. kivy/tests/test_uix_settings.py +33 -0
  519. kivy/tests/test_uix_slider.py +156 -0
  520. kivy/tests/test_uix_stacklayout.py +120 -0
  521. kivy/tests/test_uix_textinput.py +577 -0
  522. kivy/tests/test_uix_translate_coordinates.py +72 -0
  523. kivy/tests/test_uix_videoplayer.py +112 -0
  524. kivy/tests/test_uix_widget.py +137 -0
  525. kivy/tests/test_urlrequest/test_urlrequest_requests.py +378 -0
  526. kivy/tests/test_urlrequest/test_urlrequest_urllib.py +155 -0
  527. kivy/tests/test_utils.py +237 -0
  528. kivy/tests/test_vector.py +256 -0
  529. kivy/tests/test_video.py +25 -0
  530. kivy/tests/test_weakmethod.py +44 -0
  531. kivy/tests/test_widget.py +127 -0
  532. kivy/tests/test_widget_walk.py +70 -0
  533. kivy/tests/test_window_base.py +60 -0
  534. kivy/tests/test_window_info.py +29 -0
  535. kivy/tests/visual_test_label.py +291 -0
  536. kivy/tools/__init__.py +44 -0
  537. kivy/tools/benchmark.py +286 -0
  538. kivy/tools/changelog_parser.py +120 -0
  539. kivy/tools/coverage.py +135 -0
  540. kivy/tools/gallery.py +323 -0
  541. kivy/tools/generate-icons.py +117 -0
  542. kivy/tools/gles_compat/subset_gles.py +125 -0
  543. kivy/tools/highlight/__init__.py +9 -0
  544. kivy/tools/kviewer.py +80 -0
  545. kivy/tools/packaging/__init__.py +12 -0
  546. kivy/tools/packaging/cython_cfg.py +82 -0
  547. kivy/tools/packaging/factory.py +105 -0
  548. kivy/tools/packaging/pyinstaller_hooks/__init__.py +381 -0
  549. kivy/tools/packaging/pyinstaller_hooks/__main__.py +29 -0
  550. kivy/tools/packaging/pyinstaller_hooks/hook-kivy.py +9 -0
  551. kivy/tools/packaging/pyinstaller_hooks/pyi_rth_kivy.py +17 -0
  552. kivy/tools/pep8checker/pep8.py +2316 -0
  553. kivy/tools/pep8checker/pep8kivy.py +109 -0
  554. kivy/tools/report.py +223 -0
  555. kivy/tools/stub-gl-debug.py +201 -0
  556. kivy/tools/texturecompress.py +233 -0
  557. kivy/uix/__init__.py +56 -0
  558. kivy/uix/accordion.py +484 -0
  559. kivy/uix/actionbar.py +934 -0
  560. kivy/uix/anchorlayout.py +122 -0
  561. kivy/uix/behaviors/__init__.py +95 -0
  562. kivy/uix/behaviors/button.py +212 -0
  563. kivy/uix/behaviors/codenavigation.py +167 -0
  564. kivy/uix/behaviors/compoundselection.py +688 -0
  565. kivy/uix/behaviors/cover.py +160 -0
  566. kivy/uix/behaviors/drag.py +234 -0
  567. kivy/uix/behaviors/emacs.py +140 -0
  568. kivy/uix/behaviors/focus.py +595 -0
  569. kivy/uix/behaviors/knspace.py +590 -0
  570. kivy/uix/behaviors/togglebutton.py +156 -0
  571. kivy/uix/behaviors/touchripple.py +318 -0
  572. kivy/uix/boxlayout.py +331 -0
  573. kivy/uix/bubble.py +576 -0
  574. kivy/uix/button.py +137 -0
  575. kivy/uix/camera.py +118 -0
  576. kivy/uix/carousel.py +695 -0
  577. kivy/uix/checkbox.py +197 -0
  578. kivy/uix/codeinput.py +238 -0
  579. kivy/uix/colorpicker.py +486 -0
  580. kivy/uix/dropdown.py +391 -0
  581. kivy/uix/effectwidget.py +772 -0
  582. kivy/uix/filechooser.py +1134 -0
  583. kivy/uix/floatlayout.py +148 -0
  584. kivy/uix/gesturesurface.py +625 -0
  585. kivy/uix/gridlayout.py +629 -0
  586. kivy/uix/image.py +528 -0
  587. kivy/uix/label.py +1200 -0
  588. kivy/uix/layout.py +322 -0
  589. kivy/uix/modalview.py +339 -0
  590. kivy/uix/pagelayout.py +233 -0
  591. kivy/uix/popup.py +266 -0
  592. kivy/uix/progressbar.py +95 -0
  593. kivy/uix/recycleboxlayout.py +183 -0
  594. kivy/uix/recyclegridlayout.py +255 -0
  595. kivy/uix/recyclelayout.py +446 -0
  596. kivy/uix/recycleview/__init__.py +624 -0
  597. kivy/uix/recycleview/datamodel.py +194 -0
  598. kivy/uix/recycleview/layout.py +253 -0
  599. kivy/uix/recycleview/views.py +423 -0
  600. kivy/uix/relativelayout.py +324 -0
  601. kivy/uix/rst.py +1492 -0
  602. kivy/uix/sandbox.py +198 -0
  603. kivy/uix/scatter.py +645 -0
  604. kivy/uix/scatterlayout.py +93 -0
  605. kivy/uix/screenmanager.py +1275 -0
  606. kivy/uix/scrollview.py +1288 -0
  607. kivy/uix/settings.py +1343 -0
  608. kivy/uix/slider.py +421 -0
  609. kivy/uix/spinner.py +221 -0
  610. kivy/uix/splitter.py +430 -0
  611. kivy/uix/stacklayout.py +331 -0
  612. kivy/uix/stencilview.py +40 -0
  613. kivy/uix/switch.py +122 -0
  614. kivy/uix/tabbedpanel.py +880 -0
  615. kivy/uix/textinput.py +4049 -0
  616. kivy/uix/togglebutton.py +37 -0
  617. kivy/uix/treeview.py +665 -0
  618. kivy/uix/video.py +302 -0
  619. kivy/uix/videoplayer.py +697 -0
  620. kivy/uix/vkeyboard.py +879 -0
  621. kivy/uix/widget.py +1629 -0
  622. kivy/utils.py +550 -0
  623. kivy/vector.py +412 -0
  624. kivy/weakmethod.py +72 -0
kivy/multistroke.py ADDED
@@ -0,0 +1,1477 @@
1
+ '''
2
+ Multistroke gesture recognizer
3
+ ==============================
4
+
5
+ .. versionadded::
6
+ 1.9.0
7
+
8
+ .. warning::
9
+
10
+ This is experimental and subject to change as long as this warning notice
11
+ is present.
12
+
13
+ See :file:`kivy/examples/demo/multistroke/main.py` for a complete application
14
+ example.
15
+
16
+ Conceptual Overview
17
+ -------------------
18
+
19
+ This module implements the Protractor gesture recognition algorithm.
20
+
21
+ :class:`Recognizer` is the search/database API similar to
22
+ :class:`~kivy.gesture.GestureDatabase`. It maintains a list of
23
+ :class:`MultistrokeGesture` objects and allows you to search for a
24
+ user-input gestures among them.
25
+
26
+ :class:`ProgressTracker` tracks the progress of a :meth:`Recognizer.recognize`
27
+ call. It can be used to interact with the running recognizer task, for example
28
+ forcing it to stop half-way, or analyzing results as they arrive.
29
+
30
+ :class:`MultistrokeGesture` represents a gesture in the gesture database
31
+ (:attr:`Recognizer.db`). It is a container for :class:`UnistrokeTemplate`
32
+ objects, and implements the heap permute algorithm to automatically generate
33
+ all possible stroke orders (if desired).
34
+
35
+ :class:`UnistrokeTemplate` represents a single stroke path. It's typically
36
+ instantiated automatically by :class:`MultistrokeGesture`, but sometimes you
37
+ may need to create them manually.
38
+
39
+ :class:`Candidate` represents a user-input gesture that is used to search
40
+ the gesture database for matches. It is normally instantiated automatically
41
+ by calling :meth:`Recognizer.recognize`.
42
+
43
+ Usage examples
44
+ --------------
45
+
46
+ See :file:`kivy/examples/demo/multistroke/main.py` for a complete application
47
+ example.
48
+
49
+ You can bind to events on :class:`Recognizer` to track the state of all
50
+ calls to :meth:`Recognizer.recognize`. The callback function will receive an
51
+ instance of :class:`ProgressTracker` that can be used to analyze and control
52
+ various aspects of the recognition process ::
53
+
54
+ from kivy.vector import Vector
55
+ from kivy.multistroke import Recognizer
56
+
57
+ gdb = Recognizer()
58
+
59
+ def search_start(gdb, pt):
60
+ print("A search is starting with %d tasks" % (pt.tasks))
61
+
62
+ def search_stop(gdb, pt):
63
+ # This will call max() on the result dictionary, so it's best to store
64
+ # it instead of calling it 3 times consecutively
65
+ best = pt.best
66
+ print("Search ended (%s). Best is %s (score %f, distance %f)" % (
67
+ pt.status, best['name'], best['score'], best['dist'] ))
68
+
69
+ # Bind your callbacks to track all matching operations
70
+ gdb.bind(on_search_start=search_start)
71
+ gdb.bind(on_search_complete=search_stop)
72
+
73
+ # The format below is referred to as `strokes`, a list of stroke paths.
74
+ # Note that each path shown here consists of two points, ie a straight
75
+ # line; if you plot them it looks like a T, hence the name.
76
+ gdb.add_gesture('T', [
77
+ [Vector(30, 7), Vector(103, 7)],
78
+ [Vector(66, 7), Vector(66, 87)]])
79
+
80
+ # Now you can search for the 'T' gesture using similar data (user input).
81
+ # This will trigger both of the callbacks bound above.
82
+ gdb.recognize([
83
+ [Vector(45, 8), Vector(110, 12)],
84
+ [Vector(88, 9), Vector(85, 95)]])
85
+
86
+ On the next :class:`~kivy.clock.Clock` tick, the matching process starts
87
+ (and, in this case, completes).
88
+
89
+ To track individual calls to :meth:`Recognizer.recognize`, use the return
90
+ value (also a :class:`ProgressTracker` instance) ::
91
+
92
+ # Same as above, but keep track of progress using returned value
93
+ progress = gdb.recognize([
94
+ [Vector(45, 8), Vector(110, 12)],
95
+ [Vector(88, 9), Vector(85, 95)]])
96
+
97
+ progress.bind(on_progress=my_other_callback)
98
+ print(progress.progress) # = 0
99
+
100
+ # [ assuming a kivy.clock.Clock.tick() here ]
101
+
102
+ print(result.progress) # = 1
103
+
104
+ Algorithm details
105
+ -----------------
106
+
107
+ For more information about the matching algorithm, see:
108
+
109
+ "Protractor: A fast and accurate gesture recognizer" by Yang Li
110
+ http://yangl.org/pdf/protractor-chi2010.pdf
111
+
112
+ "$N-Protractor" by Lisa Anthony and Jacob O. Wobbrock
113
+ http://depts.washington.edu/aimgroup/proj/dollar/ndollar-protractor.pdf
114
+
115
+ Some of the code is derived from the JavaScript implementation here:
116
+ http://depts.washington.edu/aimgroup/proj/dollar/ndollar.html
117
+ '''
118
+
119
+ __all__ = ('Recognizer', 'ProgressTracker', 'MultistrokeGesture',
120
+ 'UnistrokeTemplate', 'Candidate')
121
+
122
+ import pickle
123
+ import base64
124
+ import zlib
125
+ from re import match as re_match
126
+ from collections import deque
127
+ from math import sqrt, pi, radians, acos, atan, atan2, pow, floor
128
+ from math import sin as math_sin, cos as math_cos
129
+ from kivy.vector import Vector
130
+ from kivy.clock import Clock
131
+ from kivy.event import EventDispatcher
132
+ from kivy.properties import ListProperty
133
+ from kivy.compat import PY2
134
+ from io import BytesIO
135
+
136
+ if not PY2:
137
+ xrange = range
138
+
139
+ # Default number of gesture matches per frame
140
+ # FIXME: relevant number
141
+ DEFAULT_GPF = 10
142
+
143
+ # Algorithm data
144
+ SQUARESIZE = 250.0
145
+ ONEDTHRESHOLD = 0.25
146
+ ORIGIN = Vector(0, 0)
147
+
148
+
149
+ class MultistrokeError(Exception):
150
+ pass
151
+
152
+
153
+ # -----------------------------------------------------------------------------
154
+ # Recognizer
155
+ # -----------------------------------------------------------------------------
156
+
157
+ class Recognizer(EventDispatcher):
158
+ ''':class:`Recognizer` provides a gesture database with matching
159
+ facilities.
160
+
161
+ :Events:
162
+ `on_search_start`
163
+ Fired when a new search is started using this Recognizer.
164
+
165
+ `on_search_complete`
166
+ Fired when a running search ends, for whatever reason.
167
+ (use :data:`ProgressTracker.status` to find out)
168
+
169
+ :Properties:
170
+ `db`
171
+ A :class:`ListProperty` that contains the available
172
+ :class:`MultistrokeGesture` objects.
173
+
174
+ :attr:`db` is a
175
+ :class:`~kivy.properties.ListProperty` and defaults to []
176
+ '''
177
+
178
+ db = ListProperty([])
179
+
180
+ def __init__(self, **kwargs):
181
+ super(Recognizer, self).__init__(**kwargs)
182
+ self.register_event_type('on_search_start')
183
+ self.register_event_type('on_search_complete')
184
+
185
+ def filter(self, **kwargs):
186
+ ''':meth:`filter` returns a subset of objects in :attr:`self.db`,
187
+ according to given criteria. This is used by many other methods of
188
+ the :class:`Recognizer`; the arguments below can for example be
189
+ used when calling :meth:`Recognizer.recognize` or
190
+ :meth:`Recognizer.export_gesture`. You normally don't need to call
191
+ this directly.
192
+
193
+ :Arguments:
194
+
195
+ `name`
196
+ Limits the returned list to gestures where
197
+ :attr:`MultistrokeGesture.name` matches given regular
198
+ expression(s). If re.match(name, MultistrokeGesture.name)
199
+ tests true, the gesture is included in the returned list.
200
+ Can be a string or an array of strings ::
201
+
202
+ gdb = Recognizer()
203
+
204
+ # Will match all names that start with a capital N
205
+ # (ie Next, New, N, Nebraska etc, but not "n" or "next")
206
+ gdb.filter(name='N')
207
+
208
+ # exactly 'N'
209
+ gdb.filter(name='N$')
210
+
211
+ # Nebraska, teletubbies, France, fraggle, N, n, etc
212
+ gdb.filter(name=['[Nn]', '(?i)T', '(?i)F'])
213
+
214
+ `priority`
215
+ Limits the returned list to gestures with certain
216
+ :attr:`MultistrokeGesture.priority` values. If specified as an
217
+ integer, only gestures with a lower priority are returned. If
218
+ specified as a list (min/max) ::
219
+
220
+ # Max priority 50
221
+ gdb.filter(priority=50)
222
+
223
+ # Max priority 50 (same result as above)
224
+ gdb.filter(priority=[0, 50])
225
+
226
+ # Min priority 50, max 100
227
+ gdb.filter(priority=[50, 100])
228
+
229
+ When this option is used, :attr:`Recognizer.db` is
230
+ automatically sorted according to priority, incurring extra
231
+ cost. You can use `force_priority_sort` to override this
232
+ behavior if your gestures are already sorted according to
233
+ priority.
234
+
235
+ `orientation_sensitive`
236
+ Limits the returned list to gestures that are
237
+ orientation sensitive (True), gestures that are not orientation
238
+ sensitive (False) or None (ignore template sensitivity, this is
239
+ the default).
240
+
241
+ `numstrokes`
242
+ Limits the returned list to gestures that have the specified
243
+ number of strokes (in :attr:`MultistrokeGesture.strokes`).
244
+ Can be a single integer or a list of integers.
245
+
246
+ `numpoints`
247
+ Limits the returned list to gestures that have specific
248
+ :attr:`MultistrokeGesture.numpoints` values. This is provided
249
+ for flexibility, do not use it unless you understand what it
250
+ does. Can be a single integer or a list of integers.
251
+
252
+ `force_priority_sort`
253
+ Can be used to override the default sort behavior. Normally
254
+ :class:`MultistrokeGesture` objects are returned in priority
255
+ order if the `priority` option is used. Setting this to True
256
+ will return gestures sorted in priority order, False will
257
+ return in the order gestures were added. None means decide
258
+ automatically (the default).
259
+
260
+ .. Note ::
261
+ For improved performance, you can load your gesture
262
+ database in priority order and set this to False when
263
+ calling :meth:`Recognizer.recognize`
264
+
265
+ `db`
266
+ Can be set if you want to filter a different list of objects
267
+ than :attr:`Recognizer.db`. You probably don't want to do this;
268
+ it is used internally by :meth:`import_gesture`.
269
+ '''
270
+ have_filters = False
271
+
272
+ kwargs_get = kwargs.get
273
+
274
+ name = kwargs_get('name', None)
275
+ if name is not None:
276
+ have_filters = True
277
+ if not isinstance(name, list):
278
+ name = [name]
279
+
280
+ priority = kwargs_get('priority', None)
281
+ min_p, max_p = None, None
282
+ if priority is not None:
283
+ have_filters = True
284
+ if isinstance(priority, list):
285
+ min_p, max_p = priority
286
+ elif isinstance(priority, int):
287
+ min_p, max_p = None, priority
288
+
289
+ numstrokes = kwargs_get('numstrokes', None)
290
+ if numstrokes is not None:
291
+ have_filters = True
292
+ if not isinstance(numstrokes, list):
293
+ numstrokes = [numstrokes]
294
+
295
+ numpoints = kwargs_get('numpoints', None)
296
+ if numpoints is not None:
297
+ have_filters = True
298
+ if not isinstance(numpoints, list):
299
+ numpoints = [numpoints]
300
+
301
+ orientation_sens = kwargs_get('orientation_sensitive', None)
302
+ if orientation_sens is not None:
303
+ have_filters = True
304
+
305
+ # Prepare a correctly sorted tasklist
306
+ force_priority_sort = kwargs.get('force_priority_sort', None)
307
+ force_sort_on = force_priority_sort and True
308
+ force_sort_off = (force_priority_sort is False) and True
309
+
310
+ db = kwargs.get('db', None) or self.db
311
+ if (force_sort_on or priority) and not force_sort_off:
312
+ tasklist = sorted(db, key=lambda n: n.priority)
313
+ else:
314
+ tasklist = db
315
+
316
+ # Now test each gesture in the database against filter criteria
317
+ out = deque()
318
+ if not have_filters:
319
+ out.extend(tasklist)
320
+ return out
321
+
322
+ out_append = out.append
323
+ for gesture in tasklist:
324
+
325
+ if (orientation_sens is not None and
326
+ orientation_sens != gesture.orientation_sens):
327
+ continue
328
+
329
+ if numpoints and gesture.numpoints not in numpoints:
330
+ continue
331
+
332
+ if numstrokes and len(gesture.strokes) not in numstrokes:
333
+ continue
334
+
335
+ if min_p is not None and gesture.priority < min_p:
336
+ continue
337
+
338
+ if max_p is not None and gesture.priority > max_p:
339
+ return out
340
+
341
+ if name:
342
+ for f in name:
343
+ if re_match(f, gesture.name):
344
+ out_append(gesture)
345
+ break
346
+ else:
347
+ out_append(gesture)
348
+
349
+ return out
350
+
351
+ def add_gesture(self, name, strokes, **kwargs):
352
+ '''Add a new gesture to the database. This will instantiate a new
353
+ :class:`MultistrokeGesture` with `strokes` and append it to self.db.
354
+
355
+ .. Note ::
356
+ If you already have instantiated a :class:`MultistrokeGesture`
357
+ object and wish to add it, append it to :attr:`Recognizer.db`
358
+ manually.
359
+ '''
360
+ if not strokes:
361
+ return False
362
+ self.db.append(
363
+ MultistrokeGesture(name=name, strokes=strokes, **kwargs)
364
+ )
365
+ return True
366
+
367
+ def parse_gesture(self, data):
368
+ '''Parse data formatted by export_gesture(). Returns a list of
369
+ :class:`MultistrokeGesture` objects. This is used internally by
370
+ :meth:`import_gesture`, you normally don't need to call this
371
+ directly.'''
372
+ io = BytesIO(zlib.decompress(base64.b64decode(data)))
373
+
374
+ p = pickle.Unpickler(io)
375
+ multistrokes = []
376
+ ms_append = multistrokes.append
377
+ for multistroke in p.load():
378
+ strokes = multistroke['strokes']
379
+ multistroke['strokes'] = [[Vector(
380
+ x, y) for x, y in line] for line in strokes]
381
+ ms_append(MultistrokeGesture(**multistroke))
382
+ return multistrokes
383
+
384
+ # FIXME: use a try block, maybe shelve or something
385
+ def export_gesture(self, filename=None, **kwargs):
386
+ '''Export a list of :class:`MultistrokeGesture` objects. Outputs a
387
+ base64-encoded string that can be decoded to a Python list with
388
+ the :meth:`parse_gesture` function or imported directly to
389
+ :attr:`self.db` using :meth:`Recognizer.import_gesture`. If
390
+ `filename` is specified, the output is written to disk, otherwise
391
+ returned.
392
+
393
+ This method accepts optional :meth:`Recognizer.filter` arguments.
394
+ '''
395
+ io = BytesIO()
396
+ p = pickle.Pickler(io, protocol=0)
397
+ multistrokes = []
398
+ defaults = {'priority': 100, 'numpoints': 16, 'stroke_sens': True,
399
+ 'orientation_sens': False, 'angle_similarity': 30.0}
400
+ dkeys = defaults.keys()
401
+
402
+ for multistroke in self.filter(**kwargs):
403
+ m = dict(defaults)
404
+ m = {'name': multistroke.name}
405
+ for attr in dkeys:
406
+ m[attr] = getattr(multistroke, attr)
407
+ m['strokes'] = tuple([(p.x, p.y) for p in line]
408
+ for line in multistroke.strokes)
409
+ multistrokes.append(m)
410
+ p.dump(multistrokes)
411
+
412
+ if filename:
413
+ f = open(filename, 'wb')
414
+ f.write(base64.b64encode(zlib.compress(io.getvalue(), 9)))
415
+ f.close()
416
+ else:
417
+ return base64.b64encode(zlib.compress(io.getvalue(), 9))
418
+
419
+ # FIXME: match them all with protractor, and don't load exacts? or
420
+ # just compare the data or something; seems better to do this on import
421
+ # than on every subsequent call to recognize(). And fix it in general,
422
+ # too.
423
+ def import_gesture(self, data=None, filename=None, **kwargs):
424
+ '''Import a list of gestures as formatted by :meth:`export_gesture`.
425
+ One of `data` or `filename` must be specified.
426
+
427
+ This method accepts optional :meth:`Recognizer.filter` arguments,
428
+ if none are specified then all gestures in specified data are
429
+ imported.'''
430
+ if filename is not None:
431
+ with open(filename, "rb") as infile:
432
+ data = infile.read()
433
+ elif data is None:
434
+ raise MultistrokeError('import_gesture needs data= or filename=')
435
+
436
+ new = self.filter(db=self.parse_gesture(data), **kwargs)
437
+ if new:
438
+ self.db.extend(new)
439
+
440
+ def transfer_gesture(self, tgt, **kwargs):
441
+ '''Transfers :class:`MultistrokeGesture` objects from
442
+ :attr:`Recognizer.db` to another :class:`Recognizer` instance `tgt`.
443
+
444
+ This method accepts optional :meth:`Recognizer.filter` arguments.
445
+ '''
446
+ if hasattr(tgt, 'db') and isinstance(tgt.db, list):
447
+ send = self.filter(**kwargs)
448
+ if send:
449
+ tgt.db.append(None)
450
+ tgt.db[-1:] = send
451
+ return True
452
+
453
+ def prepare_templates(self, **kwargs):
454
+ '''This method is used to prepare :class:`UnistrokeTemplate` objects
455
+ within the gestures in self.db. This is useful if you want to minimize
456
+ punishment of lazy resampling by preparing all vectors in advance. If
457
+ you do this before a call to :meth:`Recognizer.export_gesture`, you
458
+ will have the vectors computed when you load the data later.
459
+
460
+ This method accepts optional :meth:`Recognizer.filter` arguments.
461
+
462
+ `force_numpoints`, if specified, will prepare all templates to the
463
+ given number of points (instead of each template's preferred n; ie
464
+ :data:`UnistrokeTemplate.numpoints`). You normally don't want to
465
+ do this.'''
466
+ for gesture in self.filter(**kwargs):
467
+ for tpl in gesture:
468
+ n = kwargs.get('force_numpoints', tpl.numpoints)
469
+ tpl.prepare(n)
470
+
471
+ def recognize(self, strokes, goodscore=None, timeout=0, delay=0, **kwargs):
472
+ '''Search for gestures matching `strokes`. Returns a
473
+ :class:`ProgressTracker` instance.
474
+
475
+ This method accepts optional :meth:`Recognizer.filter` arguments.
476
+
477
+ :Arguments:
478
+
479
+ `strokes`
480
+ A list of stroke paths (list of lists of
481
+ :class:`~kivy.vector.Vector` objects) that will be matched
482
+ against gestures in the database. Can also be a
483
+ :class:`Candidate` instance.
484
+
485
+ .. Warning ::
486
+
487
+ If you manually supply a :class:`Candidate` that has a
488
+ skip-flag, make sure that the correct filter arguments
489
+ are set. Otherwise the system will attempt to load vectors
490
+ that have not been computed. For example, if you set
491
+ `skip_bounded` and do not set `orientation_sensitive` to
492
+ False, it will raise an exception if an
493
+ orientation_sensitive :class:`UnistrokeTemplate`
494
+ is encountered.
495
+
496
+ `goodscore`
497
+ If this is set (between 0.0 - 1.0) and a gesture score is
498
+ equal to or higher than the specified value, the search is
499
+ immediately halted and the on_search_complete event is
500
+ fired (+ the on_complete event of the associated
501
+ :class:`ProgressTracker` instance). Default is None (disabled).
502
+
503
+ `timeout`
504
+ Specifies a timeout (in seconds) for when the search is
505
+ aborted and the results returned. This option applies only
506
+ when `max_gpf` is not 0. Default value is 0, meaning all
507
+ gestures in the database will be tested, no matter how long
508
+ it takes.
509
+
510
+ `max_gpf`
511
+ Specifies the maximum number of :class:`MultistrokeGesture`
512
+ objects that can be processed per frame. When exceeded, will
513
+ cause the search to halt and resume work in the next frame.
514
+ Setting to 0 will complete the search immediately (and block
515
+ the UI).
516
+
517
+ .. Warning ::
518
+
519
+ This does not limit the number of
520
+ :class:`UnistrokeTemplate` objects matched! If a single
521
+ gesture has a million templates, they will all be
522
+ processed in a single frame with max_gpf=1!
523
+
524
+ `delay`
525
+ Sets an optional delay between each run of the recognizer
526
+ loop. Normally, a run is scheduled for the next frame until
527
+ the tasklist is exhausted. If you set this, there will be an
528
+ additional delay between each run (specified in seconds).
529
+ Default is 0, resume in the next frame.
530
+
531
+ `force_numpoints`
532
+ forces all templates (and candidate) to be prepared to a
533
+ certain number of points. This can be useful for example if
534
+ you are evaluating templates for optimal n (do not use this
535
+ unless you understand what it does).
536
+ '''
537
+ GPF = kwargs.get('max_gpf', DEFAULT_GPF)
538
+
539
+ # Obtain a list of MultistrokeGesture objects matching filter arguments
540
+ tasklist = self.filter(**kwargs)
541
+
542
+ # Initialize the candidate and result objects
543
+ cand = self._candidate(strokes)
544
+ result = ProgressTracker(cand, len(tasklist))
545
+
546
+ # This is done to inform caller if they bind to on_complete and there
547
+ # is nothing to do; perhaps should just return None?
548
+ if not tasklist:
549
+ result.status = 'complete'
550
+ self.dispatch('on_search_complete', result)
551
+
552
+ def result_hack(dt):
553
+ result.dispatch('on_complete')
554
+ Clock.schedule_once(result_hack)
555
+ return result
556
+
557
+ # This callback is scheduled once per frame until completed
558
+ def _recognize_tick(dt):
559
+ start_gc = result._completed
560
+ stop_now = False
561
+
562
+ while not stop_now and (tasklist and not result._break_flag) and \
563
+ (not GPF or (result._completed - start_gc < GPF)):
564
+
565
+ if (timeout and
566
+ Clock.get_time() - result._start_time >= timeout):
567
+ result.status = 'timeout'
568
+ stop_now = True
569
+ break
570
+
571
+ # Get the best distance and number of matching operations done
572
+ gesture = tasklist.popleft()
573
+ tpl, d, res, mos = gesture.match_candidate(
574
+ cand, **kwargs)
575
+
576
+ if tpl is not None:
577
+ score = result._add_result(gesture, d, tpl, res)
578
+ if goodscore is not None and score >= goodscore:
579
+ result.status = 'goodscore'
580
+ stop_now = True
581
+
582
+ result._match_ops += mos
583
+ result._completed += 1
584
+ result.dispatch('on_progress')
585
+
586
+ # The loop has ended. Prepare to dispatch 'complete'
587
+ def _dispatch():
588
+ result.dispatch('on_complete')
589
+ self.dispatch('on_search_complete', result)
590
+ return False
591
+
592
+ # Dispatch or reschedule another run
593
+ if not tasklist:
594
+ result.status = 'complete'
595
+ return _dispatch()
596
+ elif result._break_flag:
597
+ result.status = 'stop'
598
+ return _dispatch()
599
+ elif stop_now:
600
+ return _dispatch()
601
+ else:
602
+ Clock.schedule_once(_recognize_tick, delay)
603
+ return True
604
+ # End _recognize_tick()
605
+
606
+ self.dispatch('on_search_start', result)
607
+ if not GPF:
608
+ _recognize_tick(0)
609
+ else:
610
+ Clock.schedule_once(_recognize_tick, 0)
611
+
612
+ return result
613
+
614
+ def _candidate(self, strokes, **kwargs):
615
+ # recognize() helper function, do not use directly. Set up a
616
+ # Candidate object from arguments. Either use a specified object
617
+ # or make a new one from strokes and apply safe skip_* settings to
618
+ # use less resources.
619
+ if isinstance(strokes, Candidate):
620
+ return strokes
621
+
622
+ if (not isinstance(strokes, list) or not len(strokes) or not
623
+ isinstance(strokes[0], list)):
624
+ raise MultistrokeError('recognize() needs strokes= '
625
+ 'list or Candidate')
626
+
627
+ cand = Candidate(strokes)
628
+ o_filter = kwargs.get('orientation_sensitive', None)
629
+ if o_filter is False:
630
+ cand.skip_bounded = True
631
+ elif o_filter is True:
632
+ cand.skip_invariant = True
633
+
634
+ return cand
635
+
636
+ # Default event handlers
637
+ def on_search_start(self, result):
638
+ pass
639
+
640
+ def on_search_complete(self, result):
641
+ pass
642
+
643
+
644
+ # -----------------------------------------------------------------------------
645
+ # ProgressTracker
646
+ # -----------------------------------------------------------------------------
647
+
648
+ class ProgressTracker(EventDispatcher):
649
+ '''Represents an ongoing (or completed) search operation. Instantiated and
650
+ returned by the :meth:`Recognizer.recognize` method when it is called. The
651
+ `results` attribute is a dictionary that is updated as the recognition
652
+ operation progresses.
653
+
654
+ .. Note ::
655
+ You do not need to instantiate this class.
656
+
657
+ :Arguments:
658
+ `candidate`
659
+ :class:`Candidate` object to be evaluated
660
+ `tasks`
661
+ Total number of gestures in tasklist (to test against)
662
+
663
+ :Events:
664
+ `on_progress`
665
+ Fired for every gesture that is processed
666
+ `on_result`
667
+ Fired when a new result is added, and it is the first match
668
+ for the `name` so far, or a consecutive match with better score.
669
+ `on_complete`
670
+ Fired when the search is completed, for whatever reason.
671
+ (use `ProgressTracker.status` to find out)
672
+
673
+ :Attributes:
674
+ `results`
675
+ A dictionary of all results (so far). The key is the name of the
676
+ gesture (ie :attr:`UnistrokeTemplate.name` usually inherited from
677
+ :class:`MultistrokeGesture`). Each item in the dictionary is a
678
+ dict with the following entries:
679
+
680
+ `name`
681
+ Name of the matched template (redundant)
682
+ `score`
683
+ Computed score from 1.0 (perfect match) to 0.0
684
+ `dist`
685
+ Cosine distance from candidate to template (low=closer)
686
+ `gesture`
687
+ The :class:`MultistrokeGesture` object that was matched
688
+ `best_template`
689
+ Index of the best matching template (in
690
+ :attr:`MultistrokeGesture.templates`)
691
+ `template_results`
692
+ List of distances for all templates. The list index
693
+ corresponds to a :class:`UnistrokeTemplate` index in
694
+ gesture.templates.
695
+
696
+ `status`
697
+ `search`
698
+ Currently working
699
+ `stop`
700
+ Was stopped by the user (:meth:`stop` called)
701
+ `timeout`
702
+ A timeout occurred (specified as `timeout=` to recognize())
703
+ `goodscore`
704
+ The search was stopped early because a gesture with a high
705
+ enough score was found (specified as `goodscore=` to
706
+ recognize())
707
+ `complete`
708
+ The search is complete (all gestures matching filters were
709
+ tested)
710
+ '''
711
+ def __init__(self, candidate, tasks, **kwargs):
712
+ self.status = 'search'
713
+ self.candidate = candidate
714
+ self.results = {}
715
+ self.tasks = tasks
716
+ self._start_time = Clock.get_time()
717
+ self._match_ops = 0
718
+ self._completed = 0
719
+ self._break_flag = False
720
+
721
+ # fired by recognize()
722
+ self.register_event_type('on_complete')
723
+ self.register_event_type('on_progress')
724
+
725
+ # fired locally
726
+ self.register_event_type('on_result')
727
+ super(ProgressTracker, self).__init__(**kwargs)
728
+
729
+ @property
730
+ def progress(self):
731
+ '''Returns the progress as a float, 0 is 0% done, 1 is 100%. This
732
+ is a Python property.'''
733
+ if not self.tasks:
734
+ return 1
735
+ return self._completed / float(self.tasks)
736
+
737
+ @property
738
+ def best(self):
739
+ '''Return the best match found by recognize() so far. It returns a
740
+ dictionary with three keys, 'name', 'dist' and 'score' representing
741
+ the template's name, distance (from candidate path) and the
742
+ computed score value. This is a Python property.'''
743
+ results = self.results # to avoid too many self. lookups
744
+ if not results:
745
+ return {'name': None, 'dist': None, 'score': 0}
746
+ b = max(results, key=lambda r: results[r]['score'])
747
+ return {
748
+ 'name': results[b]['name'],
749
+ 'dist': results[b]['dist'],
750
+ 'score': results[b]['score']
751
+ }
752
+
753
+ def stop(self):
754
+ '''Raises a stop flag that is checked by the search process. It will
755
+ be stopped on the next clock tick (if it is still running).'''
756
+ self._break_flag = True
757
+
758
+ def _add_result(self, gesture, dist, tpl, res):
759
+ # Add a result; used internally by the recognize() function
760
+ if tpl <= len(res):
761
+ n = gesture.templates[tpl].name
762
+ else:
763
+ return 0.
764
+
765
+ if n not in self.results or dist < self.results[n]['dist']:
766
+ self.results[n] = {
767
+ 'name': n,
768
+ 'dist': dist,
769
+ 'gesture': gesture,
770
+ 'best_template': tpl,
771
+ 'template_results': res
772
+ }
773
+
774
+ if not dist:
775
+ self.results[n]['score'] = 1.0
776
+ else:
777
+ self.results[n]['score'] = 1.0 - (dist / pi)
778
+
779
+ self.dispatch('on_result', self.results[n])
780
+ return self.results[n]['score']
781
+ else:
782
+ return 0.
783
+
784
+ def on_complete(self):
785
+ pass
786
+
787
+ def on_progress(self):
788
+ pass
789
+
790
+ def on_result(self, result):
791
+ pass
792
+
793
+
794
+ # -----------------------------------------------------------------------------
795
+ # MultistrokeGesture
796
+ # -----------------------------------------------------------------------------
797
+
798
+ class MultistrokeGesture(object):
799
+ ''':class:`MultistrokeGesture` represents a gesture. It maintains a set of
800
+ `strokes` and generates unistroke (ie :class:`UnistrokeTemplate`)
801
+ permutations that are used for evaluating candidates against this gesture
802
+ later.
803
+
804
+ :Arguments:
805
+ `name`
806
+ Identifies the name of the gesture - it is returned to you in the
807
+ results of a :meth:`Recognizer.recognize` search. You can have any
808
+ number of MultistrokeGesture objects with the same name; many
809
+ definitions of one gesture. The same name is given to all the
810
+ generated unistroke permutations. Required, no default.
811
+ `strokes`
812
+ A list of paths that represents the gesture. A path is a list of
813
+ Vector objects::
814
+
815
+ gesture = MultistrokeGesture('my_gesture', strokes=[
816
+ [Vector(x1, y1), Vector(x2, y2), ...... ], # stroke 1
817
+ [Vector(), Vector(), Vector(), Vector() ] # stroke 2
818
+ #, [stroke 3], [stroke 4], ...
819
+ ])
820
+
821
+ For template matching purposes, all the strokes are combined to a
822
+ single list (unistroke). You should still specify the strokes
823
+ individually, and set `stroke_sensitive` True (whenever possible).
824
+
825
+ Once you do this, unistroke permutations are immediately generated
826
+ and stored in `self.templates` for later, unless you set the
827
+ `permute` flag to False.
828
+ `priority`
829
+ Determines when :func:`Recognizer.recognize` will attempt to match
830
+ this template, lower priorities are evaluated first (only if
831
+ a priority `filter` is used). You should use lower priority on
832
+ gestures that are more likely to match. For example, set user
833
+ templates at lower number than generic templates. Default is 100.
834
+ `numpoints`
835
+ Determines the number of points this gesture should be resampled to
836
+ (for matching purposes). The default is 16.
837
+ `stroke_sensitive`
838
+ Determines if the number of strokes (paths) in this gesture is
839
+ required to be the same in the candidate (user input) gesture
840
+ during matching. If this is False, candidates will always be
841
+ evaluated, disregarding the number of strokes. Default is True.
842
+ `orientation_sensitive`
843
+ Determines if this gesture is orientation sensitive. If True,
844
+ aligns the indicative orientation with the one of eight base
845
+ orientations that requires least rotation. Default is True.
846
+ `angle_similarity`
847
+ This is used by the :func:`Recognizer.recognize` function when a
848
+ candidate is evaluated against this gesture. If the angles between
849
+ them are too far off, the template is considered a non-match.
850
+ Default is 30.0 (degrees)
851
+ `permute`
852
+ If False, do not use Heap Permute algorithm to generate different
853
+ stroke orders when instantiated. If you set this to False, a
854
+ single UnistrokeTemplate built from `strokes` is used.
855
+ '''
856
+ def __init__(self, name, strokes=None, **kwargs):
857
+ self.name = name
858
+ self.priority = kwargs.get('priority', 100)
859
+ self.numpoints = kwargs.get('numpoints', 16)
860
+ self.stroke_sens = kwargs.get('stroke_sensitive', True)
861
+ self.orientation_sens = kwargs.get('orientation_sensitive', True)
862
+ self.angle_similarity = kwargs.get('angle_similarity', 30.0)
863
+ self.strokes = []
864
+
865
+ if strokes is not None:
866
+ self.strokes = strokes
867
+ if kwargs.get('permute', True):
868
+ self.permute()
869
+ else:
870
+ self.templates = [UnistrokeTemplate(name,
871
+ points=[i for sub in strokes for i in sub],
872
+ numpoints=self.numpoints,
873
+ orientation_sensitive=self.orientation_sens)]
874
+
875
+ def angle_similarity_threshold(self):
876
+ return radians(self.angle_similarity)
877
+
878
+ def add_stroke(self, stroke, permute=False):
879
+ '''Add a stroke to the self.strokes list. If `permute` is True, the
880
+ :meth:`permute` method is called to generate new unistroke templates'''
881
+ self.strokes.append(stroke)
882
+ if permute:
883
+ self.permute()
884
+
885
+ def get_distance(self, cand, tpl, numpoints=None):
886
+ '''Compute the distance from this Candidate to a UnistrokeTemplate.
887
+ Returns the Cosine distance between the stroke paths.
888
+
889
+ `numpoints` will prepare both the UnistrokeTemplate and Candidate path
890
+ to n points (when necessary), you probably don't want to do this.
891
+ '''
892
+ n = numpoints
893
+ if n is None or n < 2:
894
+ n = self.numpoints
895
+
896
+ # optimal_cosine_distance() inlined here for performance
897
+ v1 = tpl.get_vector(n)
898
+ v2 = cand.get_protractor_vector(n, tpl.orientation_sens)
899
+
900
+ a = 0.0
901
+ b = 0.0
902
+
903
+ for i in xrange(0, len(v1), 2):
904
+ a += (v1[i] * v2[i]) + (v1[i + 1] * v2[i + 1])
905
+ b += (v1[i] * v2[i + 1]) - (v1[i + 1] * v2[i])
906
+
907
+ angle = atan(b / a)
908
+
909
+ # If you put the below directly into math.acos(), you will get a domain
910
+ # error when a=1.0 and angle=0.0 (ie math_cos(angle)=1.0). It seems to
911
+ # be because float representation of 1.0*1.0 is >1.0 (ie 1.00000...001)
912
+ # and this is problematic for math.acos().
913
+ # If you try math.acos(1.0*1.0) in interpreter it does not happen,
914
+ # only with exact match at runtime
915
+ result = a * math_cos(angle) + b * math_sin(angle)
916
+
917
+ # FIXME: I'm sure there is a better way to do it but..
918
+ if result >= 1:
919
+ result = 1
920
+ elif result <= -1: # has not happened to me, but I leave it here.
921
+ result = -1
922
+ return acos(result)
923
+
924
+ def match_candidate(self, cand, **kwargs):
925
+ '''Match a given candidate against this MultistrokeGesture object. Will
926
+ test against all templates and report results as a list of four
927
+ items:
928
+
929
+ `index 0`
930
+ Best matching template's index (in self.templates)
931
+ `index 1`
932
+ Computed distance from the template to the candidate path
933
+ `index 2`
934
+ List of distances for all templates. The list index
935
+ corresponds to a :class:`UnistrokeTemplate` index in
936
+ self.templates.
937
+ `index 3`
938
+ Counter for the number of performed matching operations, ie
939
+ templates matched against the candidate
940
+ '''
941
+ best_d = float('infinity')
942
+ best_tpl = None
943
+ mos = 0
944
+ out = []
945
+
946
+ if (self.stroke_sens and len(self.strokes) != len(cand.strokes)):
947
+ return (best_tpl, best_d, out, mos)
948
+
949
+ skip_bounded = cand.skip_bounded
950
+ skip_invariant = cand.skip_invariant
951
+ get_distance = self.get_distance
952
+ ang_sim_threshold = self.angle_similarity_threshold()
953
+
954
+ for idx, tpl in enumerate(self.templates):
955
+ # Handle a theoretical case where a MultistrokeGesture is composed
956
+ # manually and the orientation_sensitive flag is True, and contains
957
+ # a UnistrokeTemplate that has orientation_sensitive=False (or vice
958
+ # versa). This would cause KeyError - requesting nonexistent vector
959
+ if tpl.orientation_sens:
960
+ if skip_bounded:
961
+ continue
962
+ elif skip_invariant:
963
+ continue
964
+
965
+ # Count as a match operation now, since the call to get_
966
+ # angle_similarity below will force vector calculation,
967
+ # even if it doesn't make it to get_distance
968
+ mos += 1
969
+
970
+ # Note: With this implementation, we always resample the candidate
971
+ # to *any* encountered UnistrokeTemplate numpoints here, the filter
972
+ # is only applied to MultistrokeGesture. See theoretical case
973
+ # above; should not matter normally.
974
+ n = kwargs.get('force_numpoints', tpl.numpoints)
975
+
976
+ # Skip if candidate/gesture angles are too far off
977
+ ang_sim = cand.get_angle_similarity(tpl, numpoints=n)
978
+ if ang_sim > ang_sim_threshold:
979
+ continue
980
+
981
+ # Get the distance between cand/tpl paths
982
+ d = get_distance(cand, tpl, numpoints=n)
983
+ out.append(d)
984
+
985
+ if d < best_d:
986
+ best_d = d
987
+ best_tpl = idx
988
+
989
+ return (best_tpl, best_d, out, mos)
990
+
991
+ def permute(self):
992
+ '''Generate all possible unistroke permutations from self.strokes and
993
+ save the resulting list of UnistrokeTemplate objects in self.templates.
994
+
995
+ Quote from http://faculty.washington.edu/wobbrock/pubs/gi-10.2.pdf ::
996
+
997
+ We use Heap Permute [16] (p. 179) to generate all stroke orders
998
+ in a multistroke gesture. Then, to generate stroke directions for
999
+ each order, we treat each component stroke as a dichotomous
1000
+ [0,1] variable. There are 2^N combinations for N strokes, so we
1001
+ convert the decimal values 0 to 2^N-1, inclusive, to binary
1002
+ representations and regard each bit as indicating forward (0) or
1003
+ reverse (1). This algorithm is often used to generate truth tables
1004
+ in propositional logic.
1005
+
1006
+ See section 4.1: "$N Algorithm" of the linked paper for details.
1007
+
1008
+ .. Warning ::
1009
+
1010
+ Using heap permute for gestures with more than 3 strokes
1011
+ can result in very large number of templates (a 9-stroke
1012
+ gesture = 38 million templates). If you are dealing with
1013
+ these types of gestures, you should manually compose
1014
+ all the desired stroke orders.
1015
+ '''
1016
+ # Seed with index of each stroke
1017
+ self._order = [i for i in xrange(0, len(self.strokes))]
1018
+
1019
+ # Prepare ._orders
1020
+ self._orders = []
1021
+ self._heap_permute(len(self.strokes))
1022
+ del self._order
1023
+
1024
+ # Generate unistroke permutations
1025
+ self.templates = [UnistrokeTemplate(
1026
+ self.name,
1027
+ points=permutation,
1028
+ numpoints=self.numpoints,
1029
+ orientation_sensitive=self.orientation_sens
1030
+ ) for permutation in self._make_unistrokes()]
1031
+ del self._orders
1032
+
1033
+ def _heap_permute(self, n):
1034
+ # Heap Permute algorithm
1035
+ self_order = self._order
1036
+ if n == 1:
1037
+ self._orders.append(self_order[:])
1038
+ else:
1039
+ i = 0
1040
+ for i in xrange(0, n):
1041
+ self._heap_permute(n - 1)
1042
+ if n % 2 == 1:
1043
+ tmp = self_order[0]
1044
+ self_order[0] = self_order[n - 1]
1045
+ self_order[n - 1] = tmp
1046
+ else:
1047
+ tmp = self_order[i]
1048
+ self_order[i] = self_order[n - 1]
1049
+ self_order[n - 1] = tmp
1050
+
1051
+ def _make_unistrokes(self):
1052
+ # Create unistroke permutations from self.strokes
1053
+ unistrokes = []
1054
+ unistrokes_append = unistrokes.append
1055
+ self_strokes = self.strokes
1056
+ for r in self._orders:
1057
+ b = 0
1058
+ while b < pow(2, len(r)): # use b's bits for directions
1059
+ unistroke = []
1060
+ unistroke_append = unistroke.append
1061
+ for i in xrange(0, len(r)):
1062
+ pts = self_strokes[r[i]][:]
1063
+ if (b >> i) & 1 == 1: # is b's bit at index i 1?
1064
+ pts.reverse()
1065
+ unistroke_append(None)
1066
+ unistroke[-1:] = pts
1067
+
1068
+ unistrokes_append(unistroke)
1069
+ b += 1
1070
+ return unistrokes
1071
+
1072
+
1073
+ # -----------------------------------------------------------------------------
1074
+ # UnistrokeTemplate
1075
+ # -----------------------------------------------------------------------------
1076
+
1077
+ class UnistrokeTemplate(object):
1078
+ '''Represents a (uni)stroke path as a list of Vectors. Normally, this class
1079
+ is instantiated by MultistrokeGesture and not by the programmer directly.
1080
+ However, it is possible to manually compose UnistrokeTemplate objects.
1081
+
1082
+ :Arguments:
1083
+ `name`
1084
+ Identifies the name of the gesture. This is normally inherited from
1085
+ the parent MultistrokeGesture object when a template is generated.
1086
+ `points`
1087
+ A list of points that represents a unistroke path. This is normally
1088
+ one of the possible stroke order permutations from a
1089
+ MultistrokeGesture.
1090
+ `numpoints`
1091
+ The number of points this template should (ideally) be resampled to
1092
+ before the matching process. The default is 16, but you can use a
1093
+ template-specific settings if that improves results.
1094
+ `orientation_sensitive`
1095
+ Determines if this template is orientation sensitive (True) or
1096
+ fully rotation invariant (False). The default is True.
1097
+
1098
+ .. Note::
1099
+ You will get an exception if you set a skip-flag and then attempt to
1100
+ retrieve those vectors.
1101
+ '''
1102
+ def __init__(self, name, points=None, **kwargs):
1103
+ self.name = name
1104
+ self.numpoints = kwargs.get('numpoints', 16)
1105
+ self.orientation_sens = kwargs.get('orientation_sensitive', True)
1106
+
1107
+ self.db = {}
1108
+ self.points = []
1109
+
1110
+ if points is not None:
1111
+ self.points = points
1112
+
1113
+ def add_point(self, p):
1114
+ '''Add a point to the unistroke/path. This invalidates all previously
1115
+ computed vectors.'''
1116
+ self.points.append(p)
1117
+ # All previously computed data is now void.
1118
+ self.db = {}
1119
+
1120
+ # Used to lazily prepare the template
1121
+ def _get_db_key(self, key, numpoints=None):
1122
+ n = numpoints and numpoints or self.numpoints
1123
+ if n not in self.db:
1124
+ self.prepare(n)
1125
+ return self.db[n][key]
1126
+
1127
+ def get_start_unit_vector(self, numpoints=None):
1128
+ return self._get_db_key('startvector', numpoints)
1129
+
1130
+ def get_vector(self, numpoints=None):
1131
+ return self._get_db_key('vector', numpoints)
1132
+
1133
+ def get_points(self, numpoints=None):
1134
+ return self._get_db_key('points', numpoints)
1135
+
1136
+ def prepare(self, numpoints=None):
1137
+ '''This function prepares the UnistrokeTemplate for matching given a
1138
+ target number of points (for resample). 16 is optimal.'''
1139
+
1140
+ if not self.points:
1141
+ raise MultistrokeError('prepare() called without self.points')
1142
+
1143
+ # How many points are we resampling to?
1144
+ n = numpoints or self.numpoints
1145
+ if not n or n < 2:
1146
+ raise MultistrokeError('prepare() called with invalid numpoints')
1147
+
1148
+ p = resample(self.points, n)
1149
+ radians = indicative_angle(p)
1150
+ p = rotate_by(p, -radians)
1151
+ p = scale_dim(p, SQUARESIZE, ONEDTHRESHOLD)
1152
+
1153
+ if self.orientation_sens:
1154
+ p = rotate_by(p, +radians) # restore
1155
+
1156
+ p = translate_to(p, ORIGIN)
1157
+
1158
+ # Now store it using the number of points in the resampled path as the
1159
+ # dict key. On the next call to get_*, it will be returned instead of
1160
+ # recomputed. Implicitly, you must reset self.db or call prepare() for
1161
+ # all the keys once you manipulate self.points.
1162
+ self.db[n] = {
1163
+ # Compute STARTANGLEINDEX as n/8:
1164
+ 'startvector': start_unit_vector(p, (n / 8)),
1165
+ 'vector': vectorize(p, self.orientation_sens)
1166
+ }
1167
+
1168
+
1169
+ # -----------------------------------------------------------------------------
1170
+ # Candidate
1171
+ # -----------------------------------------------------------------------------
1172
+
1173
+ class Candidate(object):
1174
+ '''Represents a set of unistroke paths of user input, ie data to be matched
1175
+ against a :class:`UnistrokeTemplate` object using the Protractor algorithm.
1176
+ By default, data is precomputed to match both rotation bounded and fully
1177
+ invariant :class:`UnistrokeTemplate` objects.
1178
+
1179
+ :Arguments:
1180
+ `strokes`
1181
+ See :data:`MultistrokeGesture.strokes` for format example. The
1182
+ Candidate strokes are simply combined to a unistroke in the order
1183
+ given. The idea is that this will match one of the unistroke
1184
+ permutations in `MultistrokeGesture.templates`.
1185
+ `numpoints`
1186
+ The Candidate's default N; this is only for a fallback, it is not
1187
+ normally used since n is driven by the UnistrokeTemplate we are
1188
+ being compared to.
1189
+ `skip_bounded`
1190
+ If True, do not generate/store rotation bounded vectors
1191
+ `skip_invariant`
1192
+ If True, do not generate/store rotation invariant vectors
1193
+
1194
+ Note that you WILL get errors if you set a skip-flag and then attempt to
1195
+ retrieve the data.'''
1196
+ def __init__(self, strokes=None, numpoints=16, **kwargs):
1197
+ self.skip_invariant = kwargs.get('skip_invariant', False)
1198
+ self.skip_bounded = kwargs.get('skip_bounded', False)
1199
+
1200
+ self.numpoints = numpoints
1201
+ self.db = {}
1202
+ self.strokes = []
1203
+
1204
+ if strokes is not None:
1205
+ self.strokes = strokes
1206
+
1207
+ def add_stroke(self, stroke):
1208
+ '''Add a stroke to the candidate; this will invalidate all
1209
+ previously computed vectors'''
1210
+ self.points.append(stroke)
1211
+ self.db = {}
1212
+
1213
+ # Used to lazily prepare the candidate
1214
+ def _get_db_key(self, key, numpoints, orientation_sens):
1215
+ n = numpoints and numpoints or self.numpoints
1216
+ if n not in self.db:
1217
+ self.prepare(n)
1218
+
1219
+ prefix = orientation_sens and 'bound_' or 'inv_'
1220
+ return self.db[n][prefix + key]
1221
+
1222
+ def get_start_unit_vector(self, numpoints, orientation_sens):
1223
+ '''(Internal use only) Get the start vector for this Candidate,
1224
+ with the path resampled to `numpoints` points. This is the first
1225
+ step in the matching process. It is compared to a
1226
+ UnistrokeTemplate object's start vector to determine angle
1227
+ similarity.'''
1228
+ return self._get_db_key('startvector', numpoints, orientation_sens)
1229
+
1230
+ def get_protractor_vector(self, numpoints, orientation_sens):
1231
+ '''(Internal use only) Return vector for comparing to a
1232
+ UnistrokeTemplate with Protractor'''
1233
+ return self._get_db_key('vector', numpoints, orientation_sens)
1234
+
1235
+ def get_angle_similarity(self, tpl, **kwargs):
1236
+ '''(Internal use only) Compute the angle similarity between this
1237
+ Candidate and a UnistrokeTemplate object. Returns a number that
1238
+ represents the angle similarity (lower is more similar).'''
1239
+ n = kwargs.get('numpoints', self.numpoints)
1240
+
1241
+ # angle_between_unit_vectors() inlined here for performance
1242
+ v1x, v1y = self.get_start_unit_vector(n, tpl.orientation_sens)
1243
+ v2x, v2y = tpl.get_start_unit_vector(n)
1244
+
1245
+ n = (v1x * v2x + v1y * v2y)
1246
+ # FIXME: Domain error on float representation of 1.0 (exact match)
1247
+ # (see comments in MultistrokeGesture.get_distance())
1248
+ if n >= 1:
1249
+ return 0.0
1250
+ if n <= -1:
1251
+ return pi
1252
+ return acos(n)
1253
+
1254
+ def prepare(self, numpoints=None):
1255
+ '''Prepare the Candidate vectors. self.strokes is combined to a single
1256
+ unistroke (connected end-to-end), resampled to :attr:`numpoints`
1257
+ points, and then the vectors are calculated and stored in self.db (for
1258
+ use by `get_distance` and `get_angle_similarity`)'''
1259
+ n = numpoints and numpoints or self.numpoints
1260
+
1261
+ # Inlined combine_strokes() for performance
1262
+ points = [i for sub in self.strokes for i in sub]
1263
+ points = resample(points, n)
1264
+ radians = indicative_angle(points)
1265
+ points = rotate_by(points, -radians)
1266
+ points = scale_dim(points, SQUARESIZE, ONEDTHRESHOLD)
1267
+
1268
+ # Compute STARTANGLEINDEX as n / 8
1269
+ angidx = n / 8
1270
+ cand = {}
1271
+
1272
+ # full rotation invariance
1273
+ if not self.skip_invariant:
1274
+ inv_points = translate_to(points, ORIGIN)
1275
+ cand['inv_startvector'] = start_unit_vector(inv_points, angidx)
1276
+ cand['inv_vector'] = vectorize(inv_points, False)
1277
+
1278
+ # rotation bounded invariance
1279
+ if not self.skip_bounded:
1280
+ bound_points = rotate_by(points, +radians) # restore
1281
+ bound_points = translate_to(bound_points, ORIGIN)
1282
+ cand['bound_startvector'] = start_unit_vector(bound_points, angidx)
1283
+ cand['bound_vector'] = vectorize(bound_points, True)
1284
+
1285
+ self.db[n] = cand
1286
+
1287
+
1288
+ # -----------------------------------------------------------------------------
1289
+ # Helper functions from this point on. This is all directly related to the
1290
+ # recognition algorithm, and is almost 100% transcription from the JavaScript
1291
+ # -----------------------------------------------------------------------------
1292
+ def resample(points, n):
1293
+ # Resample a path to `n` points
1294
+ if not len(points) or not n or n < 2:
1295
+ raise MultistrokeError('resample() called with invalid arguments')
1296
+
1297
+ interval = path_length(points) / (n - 1)
1298
+ D = 0.0
1299
+ i = 1
1300
+ newpoints = [points[0]]
1301
+ workpoints = points[:]
1302
+ newpoints_len = 1
1303
+ workpoints_len = len(points)
1304
+
1305
+ new_append = newpoints.append
1306
+ work_insert = workpoints.insert
1307
+ while i < len(workpoints):
1308
+ p1 = workpoints[i - 1]
1309
+ p2 = workpoints[i]
1310
+ d = distance(p1, p2)
1311
+
1312
+ if D + d >= interval:
1313
+ qx = p1[0] + ((interval - D) / d) * (p2[0] - p1[0])
1314
+ qy = p1[1] + ((interval - D) / d) * (p2[1] - p1[1])
1315
+ q = Vector(qx, qy)
1316
+ new_append(q)
1317
+ work_insert(i, q) # q is the next i
1318
+ newpoints_len += 1
1319
+ workpoints_len += 1
1320
+ D = 0.0
1321
+ else:
1322
+ D += d
1323
+
1324
+ i += 1
1325
+
1326
+ # rounding error; insert the last point
1327
+ if newpoints_len < n:
1328
+ new_append(points[-1])
1329
+
1330
+ return newpoints
1331
+
1332
+
1333
+ def indicative_angle(points):
1334
+ cx, cy = centroid(points)
1335
+ return atan2(cy - points[0][1], cx - points[0][0])
1336
+
1337
+
1338
+ def rotate_by(points, radians):
1339
+ # Rotate points around centroid
1340
+ cx, cy = centroid(points)
1341
+ cos = math_cos(radians)
1342
+ sin = math_sin(radians)
1343
+ newpoints = []
1344
+ newpoints_append = newpoints.append
1345
+
1346
+ for i in xrange(0, len(points)):
1347
+ qx = (points[i][0] - cx) * cos - (points[i][1] - cy) * sin + cx
1348
+ qy = (points[i][0] - cx) * sin + (points[i][1] - cy) * cos + cy
1349
+ newpoints_append(Vector(qx, qy))
1350
+
1351
+ return newpoints
1352
+
1353
+
1354
+ def scale_dim(points, size, oneDratio):
1355
+ bbox_x, bbox_y, bbox_w, bbox_h = bounding_box(points)
1356
+
1357
+ if bbox_h == 0 or bbox_w == 0:
1358
+ raise MultistrokeError(
1359
+ 'scale_dim() called with invalid points: h:{}, w:{}'
1360
+ .format(bbox_h, bbox_w))
1361
+
1362
+ # 1D or 2D gesture test
1363
+ uniformly = min(bbox_w / bbox_h, bbox_h / bbox_w) <= oneDratio
1364
+
1365
+ if uniformly:
1366
+ qx_size = size / max(bbox_w, bbox_h)
1367
+ qy_size = size / max(bbox_w, bbox_h)
1368
+ else:
1369
+ qx_size = size / bbox_w
1370
+ qy_size = size / bbox_h
1371
+
1372
+ newpoints = []
1373
+ newpoints_append = newpoints.append
1374
+
1375
+ for p in points:
1376
+ qx = p[0] * qx_size
1377
+ qy = p[1] * qy_size
1378
+ newpoints_append(Vector(qx, qy))
1379
+
1380
+ return newpoints
1381
+
1382
+
1383
+ def translate_to(points, pt):
1384
+ # Translate points around centroid
1385
+ cx, cy = centroid(points)
1386
+ ptx, pty = pt
1387
+ newpoints = []
1388
+ for p in points:
1389
+ qx = p[0] + ptx - cx
1390
+ qy = p[1] + pty - cy
1391
+ newpoints.append(Vector(qx, qy))
1392
+ return newpoints
1393
+
1394
+
1395
+ def vectorize(points, use_bounded_rotation_invariance):
1396
+ # Helper function for the Protractor algorithm
1397
+ cos = 1.0
1398
+ sin = 0.0
1399
+
1400
+ if use_bounded_rotation_invariance:
1401
+ ang = atan2(points[0][1], points[0][0])
1402
+ bo = (pi / 4.) * floor((ang + pi / 8.) / (pi / 4.))
1403
+ cos = math_cos(bo - ang)
1404
+ sin = math_sin(bo - ang)
1405
+
1406
+ sum = 0.0
1407
+ vector = []
1408
+ vector_len = 0
1409
+ vector_append = vector.append
1410
+
1411
+ for px, py in points:
1412
+ newx = px * cos - py * sin
1413
+ newy = py * cos + px * sin
1414
+ vector_append(newx)
1415
+ vector_append(newy)
1416
+ vector_len += 2
1417
+ sum += newx ** 2 + newy ** 2
1418
+
1419
+ magnitude = sqrt(sum)
1420
+ for i in xrange(0, vector_len):
1421
+ vector[i] /= magnitude
1422
+
1423
+ return vector
1424
+
1425
+
1426
+ def centroid(points):
1427
+ x = 0.0
1428
+ y = 0.0
1429
+ points_len = len(points)
1430
+
1431
+ for i in xrange(0, points_len):
1432
+ x += points[i][0]
1433
+ y += points[i][1]
1434
+
1435
+ x /= points_len
1436
+ y /= points_len
1437
+
1438
+ return Vector(x, y)
1439
+
1440
+
1441
+ def bounding_box(points):
1442
+ minx = float('infinity')
1443
+ miny = float('infinity')
1444
+ maxx = float('-infinity')
1445
+ maxy = float('-infinity')
1446
+
1447
+ for px, py in points:
1448
+ if px < minx:
1449
+ minx = px
1450
+ if px > maxx:
1451
+ maxx = px
1452
+ if py < miny:
1453
+ miny = py
1454
+ if py > maxy:
1455
+ maxy = py
1456
+
1457
+ return (minx, miny, maxx - minx + 1, maxy - miny + 1)
1458
+
1459
+
1460
+ def path_length(points):
1461
+ d = 0.0
1462
+ for i in xrange(1, len(points)):
1463
+ d += distance(points[i - 1], points[i])
1464
+ return d
1465
+
1466
+
1467
+ def distance(p1, p2):
1468
+ dx = p2[0] - p1[0]
1469
+ dy = p2[1] - p1[1]
1470
+ return sqrt(dx ** 2 + dy ** 2)
1471
+
1472
+
1473
+ def start_unit_vector(points, index):
1474
+ i = int(index)
1475
+ vx, vy = points[i][0] - points[0][0], points[i][1] - points[0][1]
1476
+ length = sqrt(vx ** 2 + vy ** 2)
1477
+ return Vector(vx / length, vy / length)