kaxe 1.8.1.dev0__tar.gz → 1.8.1.dev4__tar.gz

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 (157) hide show
  1. {kaxe-1.8.1.dev0/src/kaxe.egg-info → kaxe-1.8.1.dev4}/PKG-INFO +41 -4
  2. kaxe-1.8.1.dev4/README.md +88 -0
  3. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/pyproject.toml +5 -1
  4. kaxe-1.8.1.dev4/src/kaxe/__main__.py +6 -0
  5. kaxe-1.8.1.dev4/src/kaxe/_require_viewer.py +8 -0
  6. kaxe-1.8.1.dev4/src/kaxe/cli.py +8 -0
  7. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/window.py +35 -3
  8. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/d3/plot3d.py +4 -0
  9. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/grid.py +35 -3
  10. kaxe-1.8.1.dev4/src/kaxe/viewer/__init__.py +11 -0
  11. kaxe-1.8.1.dev4/src/kaxe/viewer/cli.py +89 -0
  12. kaxe-1.8.1.dev4/src/kaxe/viewer/client.py +91 -0
  13. kaxe-1.8.1.dev4/src/kaxe/viewer/server.py +79 -0
  14. kaxe-1.8.1.dev4/src/kaxe/viewer/window.py +582 -0
  15. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4/src/kaxe.egg-info}/PKG-INFO +41 -4
  16. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe.egg-info/SOURCES.txt +11 -1
  17. kaxe-1.8.1.dev4/src/kaxe.egg-info/entry_points.txt +2 -0
  18. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe.egg-info/requires.txt +3 -0
  19. kaxe-1.8.1.dev4/tests/test_viewer.py +295 -0
  20. kaxe-1.8.1.dev0/README.md +0 -53
  21. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/LICENSE +0 -0
  22. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/MANIFEST.in +0 -0
  23. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/setup.cfg +0 -0
  24. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/__init__.py +0 -0
  25. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/_require_3d.py +0 -0
  26. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/chart/__init__.py +0 -0
  27. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/chart/bar.py +0 -0
  28. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/chart/box.py +0 -0
  29. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/chart/pie.py +0 -0
  30. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/chart/qqplot.py +0 -0
  31. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/__init__.py +0 -0
  32. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/axis.py +0 -0
  33. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/bounds.py +0 -0
  34. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/color.py +0 -0
  35. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/backend.py +0 -0
  36. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/camera.py +0 -0
  37. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/helper.py +0 -0
  38. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/hud.py +0 -0
  39. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/objects/__init__.py +0 -0
  40. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/objects/color.py +0 -0
  41. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/objects/line.py +0 -0
  42. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/objects/point.py +0 -0
  43. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/objects/pointer.py +0 -0
  44. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/objects/triangle.py +0 -0
  45. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/openglrender.py +0 -0
  46. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/d3/translator.py +0 -0
  47. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/draw.py +0 -0
  48. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/fileloader.py +0 -0
  49. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/helper.py +0 -0
  50. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/ipython_display.py +0 -0
  51. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/legend.py +0 -0
  52. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/line.py +0 -0
  53. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/marker.py +0 -0
  54. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/palette.py +0 -0
  55. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/profiler.py +0 -0
  56. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/progress.py +0 -0
  57. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/round.py +0 -0
  58. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/shapes.py +0 -0
  59. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/styles.py +0 -0
  60. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/svg.py +0 -0
  61. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/svg_pdf.py +0 -0
  62. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/symbol.py +0 -0
  63. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/text.py +0 -0
  64. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/core/thin.py +0 -0
  65. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/data/__init__.py +0 -0
  66. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/data/excel.py +0 -0
  67. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/__init__.py +0 -0
  68. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/_lazy.py +0 -0
  69. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/__init__.py +0 -0
  70. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/arrow.py +0 -0
  71. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/bubble.py +0 -0
  72. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/contour.py +0 -0
  73. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/equation.py +0 -0
  74. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/fill.py +0 -0
  75. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/function.py +0 -0
  76. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/guide_grid.py +0 -0
  77. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/inequality.py +0 -0
  78. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/map.py +0 -0
  79. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/parameter.py +0 -0
  80. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/pillar.py +0 -0
  81. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d2/point.py +0 -0
  82. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d3/__init__.py +0 -0
  83. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d3/base.py +0 -0
  84. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d3/function.py +0 -0
  85. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d3/mesh.py +0 -0
  86. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d3/point.py +0 -0
  87. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/d3/potato.py +0 -0
  88. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/function.py +0 -0
  89. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/legend.py +0 -0
  90. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/mapdata.py +0 -0
  91. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/point.py +0 -0
  92. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/objects/text.py +0 -0
  93. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/__init__.py +0 -0
  94. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/_lazy.py +0 -0
  95. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/box.py +0 -0
  96. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/constants.py +0 -0
  97. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/d3/__init__.py +0 -0
  98. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/d3/axes.py +0 -0
  99. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/d3/geometry.py +0 -0
  100. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/d3/variants.py +0 -0
  101. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/double.py +0 -0
  102. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/empty.py +0 -0
  103. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/log.py +0 -0
  104. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/polar.py +0 -0
  105. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/standard.py +0 -0
  106. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/themes.py +0 -0
  107. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/zoom.py +0 -0
  108. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/plot/zoom_connector.py +0 -0
  109. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/project/__init__.py +0 -0
  110. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/project/codec.py +0 -0
  111. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/project/context.py +0 -0
  112. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/project/document.py +0 -0
  113. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/project/registry.py +0 -0
  114. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/project/sample.py +0 -0
  115. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/project/sampled_curve.py +0 -0
  116. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/project/serializers.py +0 -0
  117. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/project/window.py +0 -0
  118. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/__init__.py +0 -0
  119. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/__init__.py +0 -0
  120. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.bright-oblique.ttf +0 -0
  121. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.bright-roman.ttf +0 -0
  122. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.bright-semibold.ttf +0 -0
  123. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.bright-semiboldoblique.ttf +0 -0
  124. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.classical-serif-italic.ttf +0 -0
  125. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.concrete-bold.ttf +0 -0
  126. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.concrete-bolditalic.ttf +0 -0
  127. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.concrete-italic.ttf +0 -0
  128. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.concrete-roman.ttf +0 -0
  129. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-bold.ttf +0 -0
  130. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-boldoblique.ttf +0 -0
  131. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-demi-condensed-demicondensed.ttf +0 -0
  132. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-medium.ttf +0 -0
  133. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-oblique.ttf +0 -0
  134. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.serif-bold.ttf +0 -0
  135. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.serif-bolditalic.ttf +0 -0
  136. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-boldslanted.ttf +0 -0
  137. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-romanslanted.ttf +0 -0
  138. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.serif-italic.ttf +0 -0
  139. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.serif-roman.ttf +0 -0
  140. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.serif-upright-italic-uprightitalic.ttf +0 -0
  141. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bold.ttf +0 -0
  142. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bolditalic.ttf +0 -0
  143. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-italic.ttf +0 -0
  144. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-light.ttf +0 -0
  145. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-lightoblique.ttf +0 -0
  146. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-regular.ttf +0 -0
  147. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-italic.ttf +0 -0
  148. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-medium.ttf +0 -0
  149. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/computer-modern-family/readme.txt +0 -0
  150. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe/resources/logo-small.png +0 -0
  151. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe.egg-info/dependency_links.txt +0 -0
  152. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/src/kaxe.egg-info/top_level.txt +0 -0
  153. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/tests/test.py +0 -0
  154. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/tests/test2.py +0 -0
  155. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/tests/test3.py +0 -0
  156. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/tests/test_4.py +0 -0
  157. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev4}/tests/test_5.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaxe
3
- Version: 1.8.1.dev0
3
+ Version: 1.8.1.dev4
4
4
  Summary: A small graphing tool for functions, points, equations and more
5
5
  Author-email: Valter Yde Daugberg <valteryde@hotmail.com>
6
6
  Project-URL: Homepage, https://github.com/valteryde/kaxe
@@ -27,6 +27,8 @@ Requires-Dist: psutil; extra == "3d"
27
27
  Provides-Extra: pdf
28
28
  Requires-Dist: reportlab>=4.0; extra == "pdf"
29
29
  Requires-Dist: fonttools>=4.0; extra == "pdf"
30
+ Provides-Extra: viewer
31
+ Requires-Dist: pyglet>=2.0; extra == "viewer"
30
32
  Dynamic: license-file
31
33
 
32
34
 
@@ -42,9 +44,9 @@ Documentation: [kaxe.readthedocs.io](https://kaxe.readthedocs.io/en/latest/)
42
44
 
43
45
  ```bash
44
46
  pip install kaxe # 2D plotting (default)
45
- pip install kaxe[3d] # add 3D plotting (OpenGL)
46
- pip install kaxe[pdf] # add PDF export
47
- pip install kaxe[3d,pdf] # 3D + PDF
47
+ pip install kaxe[viewer] # add interactive desktop viewer (pyglet)
48
+ pip install kaxe[3d] # add 3D plotting (OpenGL)
49
+ pip install kaxe[pdf] # add PDF export
48
50
  ```
49
51
 
50
52
  ## Quick example
@@ -57,10 +59,45 @@ plt.add(kaxe.Function2D(lambda x: x**2 - 4))
57
59
  plt.add(kaxe.Points2D([1, 2, 3], [1, 4, 9]))
58
60
  plt.theme(kaxe.Themes.A4Medium)
59
61
  plt.title("$x^2 - 4$")
62
+ plt.show() # streams to viewer if open, or opens system viewer
60
63
  plt.save("figure.png")
61
64
  plt.save("figure.svg")
62
65
  ```
63
66
 
67
+ ## Interactive Plot Viewer (Infinite Canvas)
68
+
69
+ Kaxe includes a dedicated, lightweight OpenGL workspace powered by [pyglet](https://pyglet.org) so you can arrange, inspect, and compare multiple plots on an infinite canvas without cluttering your desktop with preview windows.
70
+
71
+ 1. **Start the viewer** in a terminal:
72
+ ```bash
73
+ kaxe viewer
74
+ ```
75
+ 2. **Tag and show your plots**:
76
+ ```python
77
+ # Tag plots with an ID to manage them on the board:
78
+ plt1 = kaxe.Plot().id("loss").title("Training Loss")
79
+ plt1.show()
80
+
81
+ plt2 = kaxe.Plot().id("accuracy").title("Validation Accuracy")
82
+ plt2.show()
83
+ ```
84
+
85
+ When the viewer is open, `.show()` instantly streams plots to cards on your canvas. If you re-run code with the same ID, the card updates in-place and preserves previous runs as versions (`v1`, `v2`, etc.). If the viewer is closed, `.show()` automatically falls back to your operating system's default viewer.
86
+
87
+ **Canvas & Card Controls:**
88
+ * **Click & Drag on a Card**: Move that card freely across the canvas
89
+ * **Click & Drag on Empty Space**: Pan the camera across the workspace
90
+ * **Mouse Scroll**: Smooth zoom anchored to cursor
91
+ * **`[A]`**: Auto-arrange all cards in a neat row
92
+ * **`[Space]` or `[R]`**: Fit all cards on the canvas into view
93
+ * **`[1]`**: View canvas at 100% native pixel scale
94
+ * **`[←]` / `[→]`**: Cycle through versions (`v1`, `v2`, ...) of the selected card
95
+ * **`[Backspace]` / `[Delete]`**: Remove the selected card from the board
96
+ * **`[C]`** *(or Cmd/Ctrl+C)*: Copy selected plot to system clipboard
97
+ * **`[S]`** *(or Cmd/Ctrl+S)*: Save selected plot to timestamped PNG
98
+ * **`[H]`**: Toggle status & shortcuts HUD
99
+
100
+
64
101
  See the [getting started guide](https://kaxe.readthedocs.io/en/latest/pages/start.html), [recipes](https://kaxe.readthedocs.io/en/latest/pages/recipes.html), [styling guide](https://kaxe.readthedocs.io/en/latest/pages/styling.html), and [export guide](https://kaxe.readthedocs.io/en/latest/pages/export.html) for the full API.
65
102
 
66
103
  ## Goals
@@ -0,0 +1,88 @@
1
+
2
+ # Kaxe
3
+
4
+ ![BoxPlot](logo.png)
5
+
6
+ Kaxe is a lightweight, pure-Python plotting library for publication-quality figures in LaTeX documents. Create plots and charts with a simple object-oriented API, style them for A4 pages, and export to PNG or SVG.
7
+
8
+ Documentation: [kaxe.readthedocs.io](https://kaxe.readthedocs.io/en/latest/)
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ pip install kaxe # 2D plotting (default)
14
+ pip install kaxe[viewer] # add interactive desktop viewer (pyglet)
15
+ pip install kaxe[3d] # add 3D plotting (OpenGL)
16
+ pip install kaxe[pdf] # add PDF export
17
+ ```
18
+
19
+ ## Quick example
20
+
21
+ ```python
22
+ import kaxe
23
+
24
+ plt = kaxe.Plot([-5, 5, -5, 5])
25
+ plt.add(kaxe.Function2D(lambda x: x**2 - 4))
26
+ plt.add(kaxe.Points2D([1, 2, 3], [1, 4, 9]))
27
+ plt.theme(kaxe.Themes.A4Medium)
28
+ plt.title("$x^2 - 4$")
29
+ plt.show() # streams to viewer if open, or opens system viewer
30
+ plt.save("figure.png")
31
+ plt.save("figure.svg")
32
+ ```
33
+
34
+ ## Interactive Plot Viewer (Infinite Canvas)
35
+
36
+ Kaxe includes a dedicated, lightweight OpenGL workspace powered by [pyglet](https://pyglet.org) so you can arrange, inspect, and compare multiple plots on an infinite canvas without cluttering your desktop with preview windows.
37
+
38
+ 1. **Start the viewer** in a terminal:
39
+ ```bash
40
+ kaxe viewer
41
+ ```
42
+ 2. **Tag and show your plots**:
43
+ ```python
44
+ # Tag plots with an ID to manage them on the board:
45
+ plt1 = kaxe.Plot().id("loss").title("Training Loss")
46
+ plt1.show()
47
+
48
+ plt2 = kaxe.Plot().id("accuracy").title("Validation Accuracy")
49
+ plt2.show()
50
+ ```
51
+
52
+ When the viewer is open, `.show()` instantly streams plots to cards on your canvas. If you re-run code with the same ID, the card updates in-place and preserves previous runs as versions (`v1`, `v2`, etc.). If the viewer is closed, `.show()` automatically falls back to your operating system's default viewer.
53
+
54
+ **Canvas & Card Controls:**
55
+ * **Click & Drag on a Card**: Move that card freely across the canvas
56
+ * **Click & Drag on Empty Space**: Pan the camera across the workspace
57
+ * **Mouse Scroll**: Smooth zoom anchored to cursor
58
+ * **`[A]`**: Auto-arrange all cards in a neat row
59
+ * **`[Space]` or `[R]`**: Fit all cards on the canvas into view
60
+ * **`[1]`**: View canvas at 100% native pixel scale
61
+ * **`[←]` / `[→]`**: Cycle through versions (`v1`, `v2`, ...) of the selected card
62
+ * **`[Backspace]` / `[Delete]`**: Remove the selected card from the board
63
+ * **`[C]`** *(or Cmd/Ctrl+C)*: Copy selected plot to system clipboard
64
+ * **`[S]`** *(or Cmd/Ctrl+S)*: Save selected plot to timestamped PNG
65
+ * **`[H]`**: Toggle status & shortcuts HUD
66
+
67
+
68
+ See the [getting started guide](https://kaxe.readthedocs.io/en/latest/pages/start.html), [recipes](https://kaxe.readthedocs.io/en/latest/pages/recipes.html), [styling guide](https://kaxe.readthedocs.io/en/latest/pages/styling.html), and [export guide](https://kaxe.readthedocs.io/en/latest/pages/export.html) for the full API.
69
+
70
+ ## Goals
71
+
72
+ Kaxe was made to create simple, aesthetic graphs for articles, reports, and other academic work in LaTeX:
73
+
74
+ * Plots that fit the look of LaTeX documents (Computer Modern math via [fondi](https://github.com/valteryde/fondi))
75
+ * A straightforward object-oriented interface: create a window, add objects, style, save
76
+
77
+ ## Examples of plots
78
+
79
+ ![BoxPlot](tests/images/boxed.png)
80
+ ![Polar plot](tests/images/polar.png)
81
+ ![PrettyPlot3D2](tests/images/3d-function-pretty-2.png)
82
+ ![Contour grid](tests/images/contourgrid.png)
83
+ ![Contour same](tests/images/contour3d.png)
84
+ ![AllLegends](tests/images/vectorimagearrow3d.png)
85
+ ![Functions](tests/images/function.png)
86
+ ![Points](tests/images/labels.png)
87
+ ![Equations](tests/images/equation.png)
88
+ ![Globallight](tests/images/lightfunction3d.png)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kaxe"
3
- version = "1.8.1.dev0"
3
+ version = "1.8.1.dev4"
4
4
  authors = [
5
5
  { name="Valter Yde Daugberg", email="valteryde@hotmail.com" },
6
6
  ]
@@ -31,6 +31,10 @@ core = []
31
31
  "psutil",
32
32
  ]
33
33
  pdf = ["reportlab>=4.0", "fonttools>=4.0"]
34
+ viewer = ["pyglet>=2.0"]
35
+
36
+ [project.scripts]
37
+ kaxe = "kaxe.cli:main"
34
38
 
35
39
  [project.urls]
36
40
  Homepage = "https://github.com/valteryde/kaxe"
@@ -0,0 +1,6 @@
1
+ """Support running `python -m kaxe viewer`."""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,8 @@
1
+ _MSG = "Viewer features require optional dependencies. Install with: pip install kaxe[viewer]"
2
+
3
+
4
+ def require_viewer():
5
+ try:
6
+ import pyglet # noqa: F401
7
+ except ImportError as exc:
8
+ raise ImportError(_MSG) from exc
@@ -0,0 +1,8 @@
1
+ """Top-level CLI module for Kaxe."""
2
+
3
+ from .viewer.cli import main
4
+
5
+ __all__ = ["main"]
6
+
7
+ if __name__ == "__main__":
8
+ main()
@@ -11,6 +11,7 @@ from .svg import SvgDocument, infer_format, is_file_path
11
11
  from .progress import make_progress_bar
12
12
  from .ipython_display import display_png_bytes, is_notebook, to_png_bytes
13
13
  from .styles import assign_series_color, _palette_color_at
14
+ from ..viewer.client import try_show_in_viewer
14
15
  from PIL import Image
15
16
 
16
17
 
@@ -646,23 +647,54 @@ class Window(AttrObject):
646
647
  logging.info('Total time to save {}s'.format(str(round(time.time() - totStartTime, 4))))
647
648
 
648
649
 
649
- def show(self):
650
+ def id(self, plot_id: str):
651
+ """Assign a persistent identifier to this plot for the Kaxe Viewer."""
652
+ self.plot_id = str(plot_id)
653
+ return self
654
+
655
+ def show(self, id: Optional[str] = None):
650
656
  """
651
- Show the current image using `Pillow.Image.Show`
657
+ Show the current image using `Pillow.Image.Show` or the Kaxe Viewer.
652
658
 
653
659
  If a cached image is available, it will be used to save the file.
654
660
  Otherwise, the image will be generated and then saved.
655
661
 
662
+ Parameters
663
+ ----------
664
+ id : str, optional
665
+ Identifier for this plot when sent to the Kaxe Viewer canvas.
666
+
656
667
  Examples
657
668
  --------
658
- >>> plt.show( )
669
+ >>> plt.show()
670
+ >>> plt.show(id="my_graph_1")
659
671
  """
672
+ if id is not None:
673
+ self.plot_id = str(id)
674
+
660
675
  data = to_png_bytes(self)
661
676
  if is_notebook():
662
677
  display_png_bytes(data)
678
+ elif try_show_in_viewer(
679
+ data,
680
+ title=getattr(self, "titleText", ""),
681
+ plot_id=getattr(self, "plot_id", None),
682
+ ):
683
+ return
663
684
  else:
664
685
  Image.open(BytesIO(data)).show()
665
686
 
687
+ def open(self, id: Optional[str] = None):
688
+ """
689
+ Show the current image or stream to viewer (alias for show()).
690
+
691
+ Examples
692
+ --------
693
+ >>> plt.open()
694
+ >>> plt.open(id="my_graph_1")
695
+ """
696
+ return self.show(id=id)
697
+
666
698
  def _repr_png_(self):
667
699
  if not is_notebook():
668
700
  return None
@@ -547,6 +547,10 @@ class Plot3D(Plot3DAxesMixin, Window):
547
547
  else:
548
548
  self.save(None).show()
549
549
 
550
+ def open(self, gui=True):
551
+ """Show the current plot (alias for show())."""
552
+ return self.show(gui=gui)
553
+
550
554
  def title(self, firstAxis=None, secondAxis=None, thirdAxis=None):
551
555
  """
552
556
  Adds title to the plot.
@@ -4,6 +4,7 @@ from typing import Optional, Union
4
4
  from ..core.styles import AttrObject, AttrMap
5
5
  from ..core.window import *
6
6
  from ..core.ipython_display import display_png_bytes, is_notebook, to_png_bytes
7
+ from ..viewer.client import try_show_in_viewer
7
8
  from ..core.legend import LegendBox
8
9
  from ..core.svg import (
9
10
  SvgDocument,
@@ -489,23 +490,54 @@ class Grid(AttrObject):
489
490
  img.save(fname, format="png")
490
491
 
491
492
 
492
- def show(self):
493
+ def id(self, plot_id: str):
494
+ """Assign a persistent identifier to this grid for the Kaxe Viewer."""
495
+ self.plot_id = str(plot_id)
496
+ return self
497
+
498
+ def show(self, id: Optional[str] = None):
493
499
  """
494
- Show the current image using `Pillow.Image.Show`
500
+ Show the current image using `Pillow.Image.Show` or the Kaxe Viewer.
495
501
 
496
502
  If a cached image is available, it will be used to save the file.
497
503
  Otherwise, the image will be generated and then saved.
498
504
 
505
+ Parameters
506
+ ----------
507
+ id : str, optional
508
+ Identifier for this grid when sent to the Kaxe Viewer canvas.
509
+
499
510
  Examples
500
511
  --------
501
- >>> plt.show( )
512
+ >>> grid.show()
513
+ >>> grid.show(id="my_grid_1")
502
514
  """
515
+ if id is not None:
516
+ self.plot_id = str(id)
517
+
503
518
  data = to_png_bytes(self)
504
519
  if is_notebook():
505
520
  display_png_bytes(data)
521
+ elif try_show_in_viewer(
522
+ data,
523
+ title="Grid",
524
+ plot_id=getattr(self, "plot_id", None),
525
+ ):
526
+ return
506
527
  else:
507
528
  Image.open(BytesIO(data)).show()
508
529
 
530
+ def open(self, id: Optional[str] = None):
531
+ """
532
+ Show the current grid or stream to viewer (alias for show()).
533
+
534
+ Examples
535
+ --------
536
+ >>> grid.open()
537
+ >>> grid.open(id="my_grid_1")
538
+ """
539
+ return self.show(id=id)
540
+
509
541
  def _repr_png_(self):
510
542
  if not is_notebook():
511
543
  return None
@@ -0,0 +1,11 @@
1
+ """Kaxe Viewer package."""
2
+
3
+ from .client import DEFAULT_PORT, get_viewer_port, is_viewer_active, send_to_viewer, try_show_in_viewer
4
+
5
+ __all__ = [
6
+ "DEFAULT_PORT",
7
+ "get_viewer_port",
8
+ "is_viewer_active",
9
+ "send_to_viewer",
10
+ "try_show_in_viewer",
11
+ ]
@@ -0,0 +1,89 @@
1
+ """Command line interface for launching Kaxe Viewer."""
2
+
3
+ import argparse
4
+ import queue
5
+ import sys
6
+
7
+ from .client import get_viewer_port, is_viewer_active
8
+ from .server import start_server_thread
9
+
10
+
11
+ def run_viewer(port: int = 41783):
12
+ """Launch the Kaxe Viewer Pyglet window and HTTP server."""
13
+ # Ensure optional dependency is installed
14
+ from .._require_viewer import require_viewer
15
+
16
+ try:
17
+ require_viewer()
18
+ except ImportError as e:
19
+ print(f"\n[Kaxe] {e}\n", file=sys.stderr)
20
+ sys.exit(1)
21
+
22
+ import pyglet
23
+ from .window import KaxeViewerWindow
24
+
25
+ # Check if a viewer is already running on this port
26
+ if is_viewer_active(port):
27
+ print(f"\n[Kaxe Viewer] Port {port} is already in use.", file=sys.stderr)
28
+ print("A Kaxe Viewer instance is likely already open.\n", file=sys.stderr)
29
+ sys.exit(1)
30
+
31
+ plot_queue = queue.Queue()
32
+
33
+ # Start HTTP server on background thread
34
+ server, thread = start_server_thread(port, plot_queue)
35
+
36
+ print("=" * 64)
37
+ print(f" Kaxe Viewer running on http://127.0.0.1:{port}")
38
+ print(" Keep this terminal open while working.")
39
+ print(" In another terminal, run your scripts calling .show()!")
40
+ print()
41
+ print(" Controls:")
42
+ print(" • Mouse Wheel : Zoom (centered at cursor)")
43
+ print(" • Left Click Drag : Pan image")
44
+ print(" • [R] or [Space] : Fit plot to window")
45
+ print(" • [1] : 100% native resolution")
46
+ print(" • [←] / [→] : Cycle through previous plots")
47
+ print(" • [C] : Copy image to clipboard")
48
+ print(" • [S] : Save image to file")
49
+ print(" • [H] : Toggle HUD overlay")
50
+ print("=" * 64)
51
+
52
+ # Create Pyglet window and run Cocoa / OpenGL event loop on main thread
53
+ window = KaxeViewerWindow(plot_queue)
54
+ pyglet.app.run()
55
+
56
+
57
+ def main(argv=None):
58
+ """Entrypoint for the `kaxe` CLI command."""
59
+ if argv is None:
60
+ argv = sys.argv[1:]
61
+
62
+ parser = argparse.ArgumentParser(
63
+ prog="kaxe",
64
+ description="Kaxe graphing & plotting toolkit CLI",
65
+ )
66
+ subparsers = parser.add_subparsers(dest="subcommand", help="Available subcommands")
67
+
68
+ # `kaxe viewer` command
69
+ viewer_parser = subparsers.add_parser(
70
+ "viewer",
71
+ help="Start the standalone Kaxe interactive plot viewer",
72
+ )
73
+ viewer_parser.add_argument(
74
+ "--port",
75
+ type=int,
76
+ default=get_viewer_port(),
77
+ help=f"Port to listen on (default: {get_viewer_port()})",
78
+ )
79
+
80
+ args = parser.parse_args(argv)
81
+
82
+ if args.subcommand == "viewer":
83
+ run_viewer(port=args.port)
84
+ else:
85
+ parser.print_help()
86
+
87
+
88
+ if __name__ == "__main__":
89
+ main()
@@ -0,0 +1,91 @@
1
+ """Client-side probe and transmitter for Kaxe Viewer.
2
+
3
+ This module intentionally uses ONLY Python standard library modules
4
+ (socket, urllib, os) so that calling `plt.show()` never requires
5
+ pyglet or any other viewer dependency.
6
+ """
7
+
8
+ import os
9
+ import socket
10
+ import urllib.error
11
+ import urllib.parse
12
+ import urllib.request
13
+ from typing import Optional
14
+
15
+ DEFAULT_PORT = 41783
16
+
17
+
18
+ def get_viewer_port() -> int:
19
+ """Return the configured viewer port from environment or the default."""
20
+ env_port = os.getenv("KAXE_VIEWER_PORT")
21
+ if env_port:
22
+ try:
23
+ return int(env_port)
24
+ except ValueError:
25
+ pass
26
+ return DEFAULT_PORT
27
+
28
+
29
+ def is_viewer_active(port: Optional[int] = None, timeout: float = 0.03) -> bool:
30
+ """Check if the Kaxe Viewer is currently listening on localhost.
31
+
32
+ Uses a fast socket connection test with a short timeout (~30ms).
33
+ When no viewer is running, the OS kernel rejects the connection
34
+ virtually instantaneously (<1ms).
35
+ """
36
+ if port is None:
37
+ port = get_viewer_port()
38
+
39
+ try:
40
+ with socket.create_connection(("127.0.0.1", port), timeout=timeout):
41
+ return True
42
+ except (OSError, ConnectionRefusedError):
43
+ return False
44
+
45
+
46
+ def send_to_viewer(
47
+ data: bytes,
48
+ title: str = "",
49
+ plot_id: Optional[str] = None,
50
+ port: Optional[int] = None,
51
+ timeout: float = 2.0,
52
+ ) -> bool:
53
+ """Send image PNG bytes to the active Kaxe Viewer over HTTP POST.
54
+
55
+ Returns True if successfully received, False otherwise.
56
+ """
57
+ if port is None:
58
+ port = get_viewer_port()
59
+
60
+ url = f"http://127.0.0.1:{port}/show"
61
+ safe_title = urllib.parse.quote(title or "")
62
+ safe_id = urllib.parse.quote(plot_id or "")
63
+ headers = {
64
+ "Content-Type": "image/png",
65
+ "X-Kaxe-Title": safe_title,
66
+ "X-Kaxe-Id": safe_id,
67
+ }
68
+
69
+ req = urllib.request.Request(url, data=data, headers=headers, method="POST")
70
+ try:
71
+ with urllib.request.urlopen(req, timeout=timeout) as response:
72
+ return response.status == 200
73
+ except (urllib.error.URLError, OSError):
74
+ return False
75
+
76
+
77
+ def try_show_in_viewer(
78
+ data: bytes,
79
+ title: str = "",
80
+ plot_id: Optional[str] = None,
81
+ ) -> bool:
82
+ """Convenience helper: probe port and send image if viewer is alive.
83
+
84
+ Returns True if sent to viewer, False if fallback is needed.
85
+ """
86
+ port = get_viewer_port()
87
+ if is_viewer_active(port):
88
+ if send_to_viewer(data, title=title, plot_id=plot_id, port=port):
89
+ print("[Kaxe] Plot streamed to viewer", flush=True)
90
+ return True
91
+ return False
@@ -0,0 +1,79 @@
1
+ """Lightweight HTTP server embedded inside Kaxe Viewer.
2
+
3
+ Uses Python standard library `http.server.ThreadingHTTPServer` to receive
4
+ plot images and metadata from client scripts without external web frameworks.
5
+ """
6
+
7
+ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
8
+ import queue
9
+ import threading
10
+ import urllib.parse
11
+ from typing import Any, Dict
12
+
13
+
14
+ class PlotRequestHandler(BaseHTTPRequestHandler):
15
+ """Handles incoming /show POST requests and /ping health checks."""
16
+
17
+ # Reference to the shared Queue injected by start_server
18
+ plot_queue: queue.Queue = queue.Queue()
19
+
20
+ def do_GET(self):
21
+ if self.path in ("/ping", "/health"):
22
+ self.send_response(200)
23
+ self.send_header("Content-Type", "text/plain")
24
+ self.end_headers()
25
+ self.wfile.write(b"OK")
26
+ else:
27
+ self.send_response(404)
28
+ self.end_headers()
29
+
30
+ def do_POST(self):
31
+ if self.path == "/show":
32
+ content_length = int(self.headers.get("Content-Length", 0))
33
+ if content_length <= 0:
34
+ self.send_response(400)
35
+ self.end_headers()
36
+ self.wfile.write(b"Empty body")
37
+ return
38
+
39
+ img_bytes = self.rfile.read(content_length)
40
+ raw_title = self.headers.get("X-Kaxe-Title", "")
41
+ title = urllib.parse.unquote(raw_title) if raw_title else ""
42
+
43
+ raw_id = self.headers.get("X-Kaxe-Id", "")
44
+ plot_id = urllib.parse.unquote(raw_id) if raw_id else ""
45
+
46
+ item: Dict[str, Any] = {
47
+ "data": img_bytes,
48
+ "title": title,
49
+ "plot_id": plot_id,
50
+ }
51
+ self.plot_queue.put(item)
52
+
53
+ self.send_response(200)
54
+ self.send_header("Content-Type", "text/plain")
55
+ self.end_headers()
56
+ self.wfile.write(b"OK")
57
+ else:
58
+ self.send_response(404)
59
+ self.end_headers()
60
+
61
+ def log_message(self, format: str, *args):
62
+ # Format messages cleanly in terminal
63
+ first_arg = args[0] if args else ""
64
+ if "POST /show" in str(first_arg):
65
+ print(f"[Kaxe Viewer] New plot received ({format % args})")
66
+ # Suppress noisy health checks in console
67
+
68
+
69
+ def start_server_thread(
70
+ port: int,
71
+ plot_queue: queue.Queue,
72
+ host: str = "127.0.0.1",
73
+ ) -> tuple[ThreadingHTTPServer, threading.Thread]:
74
+ """Start the HTTP server on a daemon background thread."""
75
+ PlotRequestHandler.plot_queue = plot_queue
76
+ server = ThreadingHTTPServer((host, port), PlotRequestHandler)
77
+ thread = threading.Thread(target=server.serve_forever, daemon=True)
78
+ thread.start()
79
+ return server, thread