Skip to content

Commit 689e318

Browse files
committed
release 0.8.0a2
1 parent 42425dd commit 689e318

33 files changed

+1295
-245
lines changed

README.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# py5
22

3-
[![py5 downloads](https://pepy.tech/badge/py5/month)](https://pepy.tech/project/py5)
3+
[![py5 montly downloads](https://pepy.tech/badge/py5/month)](https://pepy.tech/project/py5)
44

5-
[![Downloads](https://pepy.tech/badge/py5/week)](https://pepy.tech/project/py5)
5+
[![py5 weekly downloads](https://pepy.tech/badge/py5/week)](https://pepy.tech/project/py5)
66

7-
[![mybinder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/hx2A/py5examples/HEAD?urlpath=lab)
7+
[![mybinder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/py5coding/py5examples/HEAD?urlpath=lab)
88

9-
py5 is a new version of [**Processing**][processing] for Python 3.8+. It makes the Java [**Processing**][processing] jars available to the CPython interpreter using [**JPype**][jpype]. It can do just about everything [**Processing**][processing] can do, except with Python instead of Java code.
9+
py5 is a new version of [**Processing**][processing] for Python 3.8+. It makes the Java [**Processing**][processing] jars available to the CPython interpreter using [**JPype**][jpype]. It can do just about all of the 2D and 3D drawing [**Processing**][processing] can do, except with Python instead of Java code.
1010

11-
The goal of py5 is to create a new version of Processing that is integrated into the Python ecosystem. Built into the library are thoughtful choices about how to best get py5 to work with other popular Python libraries such as [numpy](https://www.numpy.org/) or [Pillow](https://python-pillow.org/).
11+
The goal of py5 is to create a new version of Processing that is integrated into the Python ecosystem. Built into the library are thoughtful choices about how to best get py5 to work with other popular Python libraries and tools such as [Jupyter](https://jupyter.org/), [numpy](https://www.numpy.org/), and [Pillow](https://python-pillow.org/).
1212

1313
## Simple Example
1414

@@ -19,14 +19,18 @@ import py5
1919

2020

2121
def setup():
22-
py5.size(200, 200)
22+
py5.size(400, 400)
2323
py5.rect_mode(py5.CENTER)
2424

2525

2626
def draw():
2727
py5.square(py5.mouse_x, py5.mouse_y, 10)
2828

2929

30+
def mouse_clicked():
31+
py5.fill(py5.random_int(255), py5.random_int(255), py5.random_int(255))
32+
33+
3034
py5.run_sketch()
3135
```
3236

@@ -49,19 +53,19 @@ There are currently four basic ways to use py5. They are:
4953
* **imported mode**: simplified code that omits the `py5.` prefix. This mode is supported by the py5 Jupyter notebook kernel and the `run_sketch` command line utility.
5054
* **static mode**: functionless code to create static images. This mode is supported by the py5bot Jupyter notebook kernel, the `%%py5bot` IPython magic, and the `run_sketch` command line utility.
5155

52-
The documentation website, [https://py5.ixora.io/](https://py5.ixora.io/), is a work in progress. The reference documentation is solid but the how-to's and tutorials are a work in progress.
56+
The documentation website, [https://py5.ixora.io/](https://py5.ixora.io/), is a work in progress. The reference documentation is solid but the how-to's and tutorials are incomplete.
5357

5458
[py5generator][py5_generator_repo] is a meta-programming project that creates the py5 library. To view the actual installed py5 library code, look at the [py5 repository][py5_repo]. All py5 library development is done through py5generator.
5559

5660
## Get In Touch
5761

5862
Have a comment or question? We'd love to hear from you! The best ways to reach out are:
5963

60-
* github [discussions](https://github.com/hx2A/py5generator/discussions) and [issues](https://github.com/hx2A/py5generator/issues)
64+
* github [discussions](https://github.com/py5coding/py5generator/discussions) and [issues](https://github.com/py5coding/py5generator/issues)
6165
* twitter [@py5coding](https://twitter.com/py5coding)
6266
* [processing foundation discourse](https://discourse.processing.org/)
6367

64-
[py5_repo]: https://github.com/hx2A/py5
65-
[py5_generator_repo]: https://github.com/hx2A/py5generator
68+
[py5_repo]: https://github.com/py5coding/py5
69+
[py5_generator_repo]: https://github.com/py5coding/py5generator
6670
[processing]: https://github.com/processing/processing4
6771
[jpype]: https://github.com/jpype-project/jpype

py5/__init__.py

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"""
2424
from __future__ import annotations
2525

26+
import os
2627
import sys
2728
from pathlib import Path
2829
from io import BytesIO
@@ -45,6 +46,10 @@
4546
py5_tools.add_jars(str(base_path / 'jars'))
4647
# if the cwd has a jars subdirectory, add that next
4748
py5_tools.add_jars(Path('jars'))
49+
# if the PY5_CLASSPATH environment variable exists, add those jars
50+
if (py5_classpath := os.environ.get('PY5_JARS')):
51+
py5_tools.add_jars(Path(py5_classpath))
52+
4853
try:
4954
py5_tools.jvm._start_jvm()
5055
started_jvm = True
@@ -67,7 +72,7 @@
6772
raise RuntimeError("py5 is unable to start Java 17 Virtual Machine")
6873

6974
from .methods import register_exception_msg # noqa
70-
from .sketch import Sketch, Py5Surface, Py5Graphics, Py5Image, Py5Shader, Py5Shape, Py5Font, Py5Promise # noqa
75+
from .sketch import Sketch, Py5Surface, Py5Graphics, Py5Image, Py5Shader, Py5Shape, Py5Font, Py5KeyEvent, Py5MouseEvent, Py5Promise # noqa
7176
from .render_helper import render_frame, render_frame_sequence, render, render_sequence # noqa
7277
from .create_font_tool import create_font_file # noqa
7378
from .image_conversion import register_image_conversion, NumpyImageArray # noqa
@@ -82,7 +87,7 @@
8287
pass
8388

8489

85-
__version__ = '0.7.2a0'
90+
__version__ = '0.8.0a2'
8691

8792
_PY5_USE_IMPORTED_MODE = py5_tools.get_imported_mode()
8893

@@ -297,6 +302,13 @@
297302
pixel_width: int = None
298303
pmouse_x: int = None
299304
pmouse_y: int = None
305+
ratio_left: float = None
306+
ratio_scale: float = None
307+
ratio_top: float = None
308+
rheight: int = None
309+
rmouse_x: int = None
310+
rmouse_y: int = None
311+
rwidth: int = None
300312
width: int = None
301313
window_x: int = None
302314
window_y: int = None
@@ -3499,6 +3511,24 @@ def circle(x: float, y: float, extent: float, /) -> None:
34993511
return _py5sketch.circle(x, y, extent)
35003512

35013513

3514+
def clear() -> None:
3515+
"""Clear the drawing surface by setting every pixel to black.
3516+
3517+
Underlying Processing method: Sketch.clear
3518+
3519+
Notes
3520+
-----
3521+
3522+
Clear the drawing surface by setting every pixel to black. Calling this method
3523+
is the same as passing ``0`` to the ``background()`` method, as in
3524+
``background(0)``.
3525+
3526+
This method behaves differently than ``Py5Graphics.clear()`` because
3527+
``Py5Graphics`` objects allow transparent pixels.
3528+
"""
3529+
return _py5sketch.clear()
3530+
3531+
35023532
def clip(a: float, b: float, c: float, d: float, /) -> None:
35033533
"""Limits the rendering to the boundaries of a rectangle defined by the parameters.
35043534

@@ -17014,6 +17044,52 @@ def window_move(x: int, y: int, /) -> None:
1701417044
return _py5sketch.window_move(x, y)
1701517045

1701617046

17047+
def window_ratio(wide: int, high: int, /) -> None:
17048+
"""Set a window ratio to enable scale invariant drawing.
17049+
17050+
Underlying Processing method: Sketch.windowRatio
17051+
17052+
Parameters
17053+
----------
17054+
17055+
high: int
17056+
height of scale invariant display window
17057+
17058+
wide: int
17059+
width of scale invariant display window
17060+
17061+
Notes
17062+
-----
17063+
17064+
Set a window ratio to enable scale invariant drawing. If the Sketch window is
17065+
resizable, drawing in a consistent way can be challenging as the window changes
17066+
size. This method activates some transformations to let the user draw to the
17067+
window in a way that will be consistent for all window sizes.
17068+
17069+
The usefulness of this feature is demonstrated in the example code. The size of
17070+
the text will change as the window changes size. Observe the example makes two
17071+
calls to ``text_size()`` with fixed values of ``200`` and ``100``. Without this
17072+
feature, calculating the appropriate text size for all window sizes would be
17073+
difficult. Similarly, positioning the text in the same relative location would
17074+
also involve several calculations. Using ``window_ratio()`` makes resizable
17075+
Sketches that resize well easier to create.
17076+
17077+
When using this feature, use ``rmouse_x`` and ``rmouse_y`` to get the cursor
17078+
coordinates. The transformations involve calls to ``translate()`` and
17079+
``scale()``, and the parameters to those methods can be accessed with
17080+
``ratio_top``, ``ratio_left``, and ``ratio_scale``. The transformed coordinates
17081+
enabled with this feature can be negative for the top and left areas of the
17082+
window that do not fit the desired aspect ratio. Experimenting with the example
17083+
and seeing how the numbers change will provide more understanding than what can
17084+
be explained with words.
17085+
17086+
When calling this method, it is better to do so with values like
17087+
``window_ratio(1280, 720)`` and not ``window_ratio(16, 9)``. The aspect ratio is
17088+
the same for both but the latter might result in floating point accuracy issues.
17089+
"""
17090+
return _py5sketch.window_ratio(wide, high)
17091+
17092+
1701717093
def window_resizable(resizable: bool, /) -> None:
1701817094
"""Set the Sketch window as resizable by the user.
1701917095

@@ -17035,7 +17111,7 @@ def window_resizable(resizable: bool, /) -> None:
1703517111
Changing the window size will clear the drawing canvas. If you do this, the
1703617112
``width`` and ``height`` variables will change.
1703717113

17038-
This method provides the same funcationality as ``Py5Surface.set_resizable()``
17114+
This method provides the same functionality as ``Py5Surface.set_resizable()``
1703917115
but without the need to interact directly with the ``Py5Surface`` object.
1704017116
"""
1704117117
return _py5sketch.window_resizable(resizable)
@@ -17064,7 +17140,7 @@ def window_resize(new_width: int, new_height: int, /) -> None:
1706417140
Changing the window size will clear the drawing canvas. If you do this, the
1706517141
``width`` and ``height`` variables will change.
1706617142

17067-
This method provides the same funcationality as ``Py5Surface.set_size()`` but
17143+
This method provides the same functionality as ``Py5Surface.set_size()`` but
1706817144
without the need to interact directly with the ``Py5Surface`` object.
1706917145
"""
1707017146
return _py5sketch.window_resize(new_width, new_height)
@@ -17087,7 +17163,7 @@ def window_title(title: str, /) -> None:
1708717163
Set the Sketch window's title. This will typically appear at the window's title
1708817164
bar. The default window title is "Sketch".
1708917165

17090-
This method provides the same funcationality as ``Py5Surface.set_title()`` but
17166+
This method provides the same functionality as ``Py5Surface.set_title()`` but
1709117167
without the need to interact directly with the ``Py5Surface`` object.
1709217168
"""
1709317169
return _py5sketch.window_title(title)
@@ -20246,12 +20322,8 @@ def run_sketch(block: bool = None, *,
2024620322
mixed in with the Jupyter Kernel logs."""
2024720323
caller_globals = inspect.stack()[1].frame.f_globals
2024820324
caller_locals = inspect.stack()[1].frame.f_locals
20249-
if sketch_functions:
20250-
functions = dict([(e, sketch_functions[e])
20251-
for e in reference.METHODS if e in sketch_functions and callable(sketch_functions[e])])
20252-
else:
20253-
functions = dict([(e, caller_locals[e])
20254-
for e in reference.METHODS if e in caller_locals and callable(caller_locals[e])])
20325+
functions, function_param_counts = methods._extract_py5_user_function_data(
20326+
sketch_functions if sketch_functions else caller_locals)
2025520327
functions = _split_setup.transform(
2025620328
functions,
2025720329
caller_globals,
@@ -20278,6 +20350,7 @@ def run_sketch(block: bool = None, *,
2027820350

2027920351
_py5sketch._run_sketch(
2028020352
functions,
20353+
function_param_counts,
2028120354
block,
2028220355
py5_options,
2028320356
sketch_args,

py5/jars/core.jar

-105 Bytes
Binary file not shown.

py5/jars/dxf/dxf.jar

-1 Bytes
Binary file not shown.

py5/jars/pdf/pdf.jar

-1 Bytes
Binary file not shown.

py5/jars/py5.jar

3.49 KB
Binary file not shown.

py5/jars/svg/svg.jar

-1 Bytes
Binary file not shown.

py5/java_conversion.py

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,47 @@
1818
#
1919
# *****************************************************************************
2020
import numpy as np
21-
from jpype import _jcustomizer
21+
from jpype import JClass, JArray, _jcustomizer
2222

23-
from .sketch import Sketch, Py5Graphics, Py5Image, Py5Font, Py5Shape, Py5Shader
23+
from .sketch import Sketch, Py5Graphics, Py5Image, Py5Font, Py5Shape, Py5Shader, Py5KeyEvent, Py5MouseEvent
2424
from .pmath import _py5vector_to_pvector_converter, _numpy_to_pvector_converter, _numpy_to_pmatrix_converter
2525
from .vector import Py5Vector
2626

2727

28+
JCONVERSION_CLASS_MAP = [
29+
("processing.core.PImage", Py5Image),
30+
("processing.core.PImage", Py5Graphics),
31+
("processing.core.PGraphics", Py5Graphics),
32+
("processing.core.PFont", Py5Font),
33+
("processing.core.PShape", Py5Shape),
34+
("processing.opengl.PShader", Py5Shader),
35+
("processing.event.Event", Py5KeyEvent),
36+
("processing.event.KeyEvent", Py5KeyEvent),
37+
("processing.event.Event", Py5MouseEvent),
38+
("processing.event.MouseEvent", Py5MouseEvent),
39+
("processing.core.PApplet", Sketch),
40+
("py5.core.Sketch", Sketch),
41+
]
42+
43+
PROCESSING_TO_PY5_CLASS_MAP = [
44+
(JClass("processing.core.PImage"), Py5Image),
45+
(JClass("processing.core.PGraphics"), Py5Graphics),
46+
(JClass("processing.core.PFont"), Py5Font),
47+
(JClass("processing.core.PShape"), Py5Shape),
48+
(JClass("processing.opengl.PShader"), Py5Shader),
49+
(JClass("processing.event.KeyEvent"), Py5KeyEvent),
50+
(JClass("processing.event.MouseEvent"), Py5MouseEvent),
51+
# TODO: this won't work for Sketch. should it?
52+
# (JClass("py5.core.Sketch"), Sketch),
53+
]
54+
55+
2856
def init_jpype_converters():
29-
data = [
30-
("processing.core.PImage", Py5Image),
31-
("processing.core.PImage", Py5Graphics),
32-
("processing.core.PGraphics", Py5Graphics),
33-
("processing.core.PFont", Py5Font),
34-
("processing.core.PShape", Py5Shape),
35-
("processing.opengl.PShader", Py5Shader),
36-
("processing.core.PApplet", Sketch),
37-
("py5.core.Sketch", Sketch),
38-
]
3957

4058
def convert(jcls, obj):
4159
return obj._instance
4260

43-
for javaname, cls_ in data:
61+
for javaname, cls_ in JCONVERSION_CLASS_MAP:
4462
_jcustomizer.JConversion(javaname, cls_)(convert)
4563

4664
_jcustomizer.JConversion(
@@ -52,3 +70,16 @@ def convert(jcls, obj):
5270
_jcustomizer.JConversion(
5371
'processing.core.PMatrix',
5472
np.ndarray)(_numpy_to_pmatrix_converter)
73+
74+
75+
def convert_to_python_types(params):
76+
for p in params:
77+
for jclass, py5class in PROCESSING_TO_PY5_CLASS_MAP:
78+
if isinstance(p, jclass):
79+
yield py5class(p)
80+
break
81+
else:
82+
if isinstance(p, JArray):
83+
yield np.asarray(p)
84+
else:
85+
yield p

0 commit comments

Comments
 (0)