kaxe 1.8.1.dev0__tar.gz → 1.8.1.dev2__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 (156) hide show
  1. {kaxe-1.8.1.dev0/src/kaxe.egg-info → kaxe-1.8.1.dev2}/PKG-INFO +3 -1
  2. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/pyproject.toml +5 -1
  3. kaxe-1.8.1.dev2/src/kaxe/__main__.py +6 -0
  4. kaxe-1.8.1.dev2/src/kaxe/_require_viewer.py +8 -0
  5. kaxe-1.8.1.dev2/src/kaxe/cli.py +8 -0
  6. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/window.py +3 -0
  7. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/grid.py +3 -0
  8. kaxe-1.8.1.dev2/src/kaxe/viewer/__init__.py +11 -0
  9. kaxe-1.8.1.dev2/src/kaxe/viewer/cli.py +89 -0
  10. kaxe-1.8.1.dev2/src/kaxe/viewer/client.py +82 -0
  11. kaxe-1.8.1.dev2/src/kaxe/viewer/server.py +75 -0
  12. kaxe-1.8.1.dev2/src/kaxe/viewer/window.py +339 -0
  13. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2/src/kaxe.egg-info}/PKG-INFO +3 -1
  14. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe.egg-info/SOURCES.txt +11 -1
  15. kaxe-1.8.1.dev2/src/kaxe.egg-info/entry_points.txt +2 -0
  16. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe.egg-info/requires.txt +3 -0
  17. kaxe-1.8.1.dev2/tests/test_viewer.py +140 -0
  18. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/LICENSE +0 -0
  19. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/MANIFEST.in +0 -0
  20. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/README.md +0 -0
  21. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/setup.cfg +0 -0
  22. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/__init__.py +0 -0
  23. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/_require_3d.py +0 -0
  24. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/chart/__init__.py +0 -0
  25. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/chart/bar.py +0 -0
  26. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/chart/box.py +0 -0
  27. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/chart/pie.py +0 -0
  28. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/chart/qqplot.py +0 -0
  29. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/__init__.py +0 -0
  30. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/axis.py +0 -0
  31. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/bounds.py +0 -0
  32. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/color.py +0 -0
  33. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/backend.py +0 -0
  34. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/camera.py +0 -0
  35. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/helper.py +0 -0
  36. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/hud.py +0 -0
  37. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/objects/__init__.py +0 -0
  38. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/objects/color.py +0 -0
  39. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/objects/line.py +0 -0
  40. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/objects/point.py +0 -0
  41. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/objects/pointer.py +0 -0
  42. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/objects/triangle.py +0 -0
  43. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/openglrender.py +0 -0
  44. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/d3/translator.py +0 -0
  45. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/draw.py +0 -0
  46. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/fileloader.py +0 -0
  47. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/helper.py +0 -0
  48. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/ipython_display.py +0 -0
  49. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/legend.py +0 -0
  50. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/line.py +0 -0
  51. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/marker.py +0 -0
  52. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/palette.py +0 -0
  53. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/profiler.py +0 -0
  54. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/progress.py +0 -0
  55. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/round.py +0 -0
  56. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/shapes.py +0 -0
  57. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/styles.py +0 -0
  58. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/svg.py +0 -0
  59. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/svg_pdf.py +0 -0
  60. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/symbol.py +0 -0
  61. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/text.py +0 -0
  62. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/core/thin.py +0 -0
  63. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/data/__init__.py +0 -0
  64. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/data/excel.py +0 -0
  65. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/__init__.py +0 -0
  66. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/_lazy.py +0 -0
  67. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/__init__.py +0 -0
  68. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/arrow.py +0 -0
  69. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/bubble.py +0 -0
  70. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/contour.py +0 -0
  71. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/equation.py +0 -0
  72. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/fill.py +0 -0
  73. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/function.py +0 -0
  74. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/guide_grid.py +0 -0
  75. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/inequality.py +0 -0
  76. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/map.py +0 -0
  77. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/parameter.py +0 -0
  78. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/pillar.py +0 -0
  79. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d2/point.py +0 -0
  80. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d3/__init__.py +0 -0
  81. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d3/base.py +0 -0
  82. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d3/function.py +0 -0
  83. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d3/mesh.py +0 -0
  84. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d3/point.py +0 -0
  85. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/d3/potato.py +0 -0
  86. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/function.py +0 -0
  87. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/legend.py +0 -0
  88. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/mapdata.py +0 -0
  89. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/point.py +0 -0
  90. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/objects/text.py +0 -0
  91. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/__init__.py +0 -0
  92. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/_lazy.py +0 -0
  93. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/box.py +0 -0
  94. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/constants.py +0 -0
  95. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/d3/__init__.py +0 -0
  96. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/d3/axes.py +0 -0
  97. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/d3/geometry.py +0 -0
  98. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/d3/plot3d.py +0 -0
  99. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/d3/variants.py +0 -0
  100. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/double.py +0 -0
  101. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/empty.py +0 -0
  102. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/log.py +0 -0
  103. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/polar.py +0 -0
  104. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/standard.py +0 -0
  105. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/themes.py +0 -0
  106. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/zoom.py +0 -0
  107. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/plot/zoom_connector.py +0 -0
  108. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/project/__init__.py +0 -0
  109. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/project/codec.py +0 -0
  110. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/project/context.py +0 -0
  111. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/project/document.py +0 -0
  112. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/project/registry.py +0 -0
  113. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/project/sample.py +0 -0
  114. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/project/sampled_curve.py +0 -0
  115. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/project/serializers.py +0 -0
  116. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/project/window.py +0 -0
  117. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/__init__.py +0 -0
  118. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/__init__.py +0 -0
  119. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-oblique.ttf +0 -0
  120. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-roman.ttf +0 -0
  121. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-semibold.ttf +0 -0
  122. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.bright-semiboldoblique.ttf +0 -0
  123. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.classical-serif-italic.ttf +0 -0
  124. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-bold.ttf +0 -0
  125. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-bolditalic.ttf +0 -0
  126. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-italic.ttf +0 -0
  127. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.concrete-roman.ttf +0 -0
  128. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-bold.ttf +0 -0
  129. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-boldoblique.ttf +0 -0
  130. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-demi-condensed-demicondensed.ttf +0 -0
  131. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-medium.ttf +0 -0
  132. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.sans-serif-oblique.ttf +0 -0
  133. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-bold.ttf +0 -0
  134. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-bolditalic.ttf +0 -0
  135. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-boldslanted.ttf +0 -0
  136. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-extra-romanslanted.ttf +0 -0
  137. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-italic.ttf +0 -0
  138. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-roman.ttf +0 -0
  139. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.serif-upright-italic-uprightitalic.ttf +0 -0
  140. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bold.ttf +0 -0
  141. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-bolditalic.ttf +0 -0
  142. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-italic.ttf +0 -0
  143. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-light.ttf +0 -0
  144. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-lightoblique.ttf +0 -0
  145. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-regular.ttf +0 -0
  146. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-italic.ttf +0 -0
  147. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/cmu.typewriter-text-variable-width-medium.ttf +0 -0
  148. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/computer-modern-family/readme.txt +0 -0
  149. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe/resources/logo-small.png +0 -0
  150. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe.egg-info/dependency_links.txt +0 -0
  151. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/src/kaxe.egg-info/top_level.txt +0 -0
  152. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/tests/test.py +0 -0
  153. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/tests/test2.py +0 -0
  154. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/tests/test3.py +0 -0
  155. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/tests/test_4.py +0 -0
  156. {kaxe-1.8.1.dev0 → kaxe-1.8.1.dev2}/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.dev2
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
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "kaxe"
3
- version = "1.8.1.dev0"
3
+ version = "1.8.1.dev2"
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
 
@@ -660,6 +661,8 @@ class Window(AttrObject):
660
661
  data = to_png_bytes(self)
661
662
  if is_notebook():
662
663
  display_png_bytes(data)
664
+ elif try_show_in_viewer(data, title=getattr(self, "titleText", "")):
665
+ return
663
666
  else:
664
667
  Image.open(BytesIO(data)).show()
665
668
 
@@ -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,
@@ -503,6 +504,8 @@ class Grid(AttrObject):
503
504
  data = to_png_bytes(self)
504
505
  if is_notebook():
505
506
  display_png_bytes(data)
507
+ elif try_show_in_viewer(data, title="Grid"):
508
+ return
506
509
  else:
507
510
  Image.open(BytesIO(data)).show()
508
511
 
@@ -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,82 @@
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
+ port: Optional[int] = None,
50
+ timeout: float = 2.0,
51
+ ) -> bool:
52
+ """Send image PNG bytes to the active Kaxe Viewer over HTTP POST.
53
+
54
+ Returns True if successfully received, False otherwise.
55
+ """
56
+ if port is None:
57
+ port = get_viewer_port()
58
+
59
+ url = f"http://127.0.0.1:{port}/show"
60
+ safe_title = urllib.parse.quote(title or "")
61
+ headers = {
62
+ "Content-Type": "image/png",
63
+ "X-Kaxe-Title": safe_title,
64
+ }
65
+
66
+ req = urllib.request.Request(url, data=data, headers=headers, method="POST")
67
+ try:
68
+ with urllib.request.urlopen(req, timeout=timeout) as response:
69
+ return response.status == 200
70
+ except (urllib.error.URLError, OSError):
71
+ return False
72
+
73
+
74
+ def try_show_in_viewer(data: bytes, title: str = "") -> bool:
75
+ """Convenience helper: probe port and send image if viewer is alive.
76
+
77
+ Returns True if sent to viewer, False if fallback is needed.
78
+ """
79
+ port = get_viewer_port()
80
+ if is_viewer_active(port):
81
+ return send_to_viewer(data, title=title, port=port)
82
+ return False
@@ -0,0 +1,75 @@
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
+ item: Dict[str, Any] = {
44
+ "data": img_bytes,
45
+ "title": title,
46
+ }
47
+ self.plot_queue.put(item)
48
+
49
+ self.send_response(200)
50
+ self.send_header("Content-Type", "text/plain")
51
+ self.end_headers()
52
+ self.wfile.write(b"OK")
53
+ else:
54
+ self.send_response(404)
55
+ self.end_headers()
56
+
57
+ def log_message(self, format: str, *args):
58
+ # Format messages cleanly in terminal
59
+ first_arg = args[0] if args else ""
60
+ if "POST /show" in str(first_arg):
61
+ print(f"[Kaxe Viewer] New plot received ({format % args})")
62
+ # Suppress noisy health checks in console
63
+
64
+
65
+ def start_server_thread(
66
+ port: int,
67
+ plot_queue: queue.Queue,
68
+ host: str = "127.0.0.1",
69
+ ) -> tuple[ThreadingHTTPServer, threading.Thread]:
70
+ """Start the HTTP server on a daemon background thread."""
71
+ PlotRequestHandler.plot_queue = plot_queue
72
+ server = ThreadingHTTPServer((host, port), PlotRequestHandler)
73
+ thread = threading.Thread(target=server.serve_forever, daemon=True)
74
+ thread.start()
75
+ return server, thread