Core: Patcher, Box, Patchline¶
py2max.core.Patcher ¶
Bases: BoxFactoryMixin, SerializationMixin, AbstractPatcher
Core class for creating and managing Max/MSP patches.
The Patcher class provides a high-level interface for creating Max/MSP patches programmatically. It handles object positioning, connection validation, and automatic layout management.
Features
- Automatic object positioning with multiple layout managers
- Connection validation using Max object metadata
- Support for all major Max object types
- Hierarchical patch organization with subpatchers
- Export to .maxpat file format
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Optional[Union[str, Path]]
|
Output file path for the patch. |
None
|
title
|
Optional[str]
|
Optional title for the patch. |
None
|
parent
|
Optional[AbstractPatcher]
|
Parent patcher for hierarchical organization. |
None
|
classnamespace
|
Optional[str]
|
Namespace for object classes (e.g., 'rnbo'). |
None
|
reset_on_render
|
bool
|
Whether to reset layout on render. |
True
|
layout
|
str
|
Layout manager type ('horizontal', 'vertical', 'grid', 'flow', 'matrix', 'columnar'). |
'horizontal'
|
auto_hints
|
bool
|
Whether to automatically generate object hints. |
False
|
openinpresentation
|
int
|
Presentation mode setting. |
0
|
validate_connections
|
bool
|
Whether to validate patchline connections. |
False
|
validate_attrs
|
bool
|
Whether to warn (UserWarning) when an object is given a
keyword that is not a known attribute for its Max class -- catches
typos like |
False
|
flow_direction
|
str
|
Direction for flow-based layouts ('horizontal', 'vertical'). |
'horizontal'
|
cluster_connected
|
bool
|
Whether to cluster connected objects in grid layout. |
False
|
num_dimensions
|
int
|
Number of rows used by the matrix layout (also treated as column count when flow_direction='column'). |
4
|
dimension_spacing
|
float
|
Spacing between rows/columns for matrix layout variants. |
100.0
|
semantic_ids
|
bool
|
Whether to generate semantic IDs based on object names (e.g., 'cycle_1') instead of numeric IDs (e.g., 'obj-1'). Enables more readable debugging. |
False
|
Example
p = Patcher('my-patch.maxpat', layout='grid') osc = p.add_textbox('cycle~ 440') gain = p.add_textbox('gain~') p.add_line(osc, gain) p.save()
With semantic IDs¶
p = Patcher('my-patch.maxpat', semantic_ids=True) osc1 = p.add_textbox('cycle~ 440') # ID: 'cycle_1' osc2 = p.add_textbox('cycle~ 220') # ID: 'cycle_2' gain = p.add_textbox('gain~') # ID: 'gain_1'
filepath
property
¶
Path the patcher was created with, or "" if none was set.
to_dict ¶
Return the patcher as a .maxpat dictionary.
Renders first, so the result is the patcher as it stands rather than as
it stood after whatever last happened to render it. It did not, and the
failure was silent and order-dependent: to_dict() returned a patcher
with no boxes until something else called render(), and the same
call on the same object then started returning them. A test asserting
over to_dict() examined an empty patcher and passed for the wrong
reason, which is how this was found.
Rendering here also removes the asymmetry with Box.to_dict(), which
has always returned a populated box with no preparation required.
save_as ¶
Save the patch to a specified file path.
Renders all objects and connections, then saves the patch as a .maxpat JSON file (or a binary .amxd) that can be opened in Max/MSP.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Union[str, Path]
|
File path where the patch should be saved. |
required |
Raises:
| Type | Description |
|---|---|
PatcherIOError
|
If file cannot be written or path is invalid. |
save ¶
Save the patch to the default file path.
Uses the path specified during Patcher creation. If no path
was specified, this method will do nothing. Pending associated
comments are flushed during render (called by save_as), so
every serialization path emits them.
add_box ¶
registers the box and adds it to the patcher
add_associated_comment ¶
Store a comment association to be processed later during layout optimization or save.
This defers the actual comment positioning until after layout optimization, ensuring comments stay properly positioned relative to their associated boxes.
add_patchline_by_index ¶
add_patchline_by_index(
src_id: str,
dst_id: str,
dst_inlet: int = 0,
src_outlet: int = 0,
) -> "Patchline"
Patchline creation between two objects using stored indexes
add_patchline ¶
Primary patchline creation method with validation and logging.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
src_id
|
str
|
Source object ID. |
required |
src_outlet
|
int
|
Source outlet index. |
required |
dst_id
|
str
|
Destination object ID. |
required |
dst_inlet
|
int
|
Destination inlet index. |
required |
Returns:
| Type | Description |
|---|---|
'Patchline'
|
Created Patchline object. |
Raises:
| Type | Description |
|---|---|
InvalidConnectionError
|
If connection validation fails. |
add_line ¶
Create a connection between two objects.
Connects an outlet of the source object to an inlet of the destination object. Validates the connection if validation is enabled.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
src_obj
|
'Box'
|
Source object to connect from. |
required |
dst_obj
|
'Box'
|
Destination object to connect to. |
required |
inlet
|
int
|
Destination inlet index (default: 0). |
0
|
outlet
|
int
|
Source outlet index (default: 0). |
0
|
Returns:
| Type | Description |
|---|---|
'Patchline'
|
The created Patchline object. |
Raises:
| Type | Description |
|---|---|
InvalidConnectionError
|
If connection validation fails. |
Example
osc = p.add_textbox('cycle~ 440') gain = p.add_textbox('gain~') p.add_line(osc, gain) # Connect outlet 0 to inlet 0
add_textbox ¶
add_textbox(
text: str,
maxclass: Optional[str] = None,
numinlets: Optional[int] = None,
numoutlets: Optional[int] = None,
outlettype: Optional[List[str]] = None,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: "Unpack[TextboxProps]",
) -> "Box"
Add a text-based Max object to the patch.
Creates a Max object from a text specification (e.g., 'cycle~ 440'). Automatically looks up default attributes and applies appropriate maxclass based on the object type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Max object specification (e.g., 'cycle~ 440', 'gain~'). |
required |
maxclass
|
Optional[str]
|
Override the automatically determined maxclass. |
None
|
numinlets
|
Optional[int]
|
Number of input connections. |
None
|
numoutlets
|
Optional[int]
|
Number of output connections. |
None
|
outlettype
|
Optional[List[str]]
|
Types of outputs (e.g., ['signal', 'int']). |
None
|
patching_rect
|
Optional[Rect]
|
Position and size rectangle. |
None
|
id
|
Optional[str]
|
Unique identifier for the object. |
None
|
comment
|
Optional[str]
|
Optional comment text. |
None
|
comment_pos
|
Optional[str]
|
Comment position ('above', 'below', etc.). |
None
|
**kwds
|
'Unpack[TextboxProps]'
|
Additional Max object properties. |
{}
|
Returns:
| Type | Description |
|---|---|
'Box'
|
The created Box object. |
Example
osc = p.add_textbox('cycle~ 440') gain = p.add_textbox('gain~') metro = p.add_textbox('metro 500')
add ¶
generic adder: value can be a number or a list or text for an object.
add_codebox ¶
add_codebox(
code: str,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
tilde: bool = False,
**kwds: Any,
) -> "Box"
Add a codebox.
add_codebox_tilde ¶
add_codebox_tilde(
code: str,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add a codebox_tilde
add_gen_codebox ¶
add_gen_codebox(
code: str,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
numinlets: Optional[int] = None,
numoutlets: Optional[int] = None,
outlettype: Optional[List[str]] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add a standalone gen.codebox~ object.
Unlike :meth:add_codebox (which emits the codebox~ object meant to
live inside a gen~ or rnbo~ subpatcher), this creates the
self-contained gen.codebox~ object that lives directly in a regular
Max patcher -- a complete gen patch in a single box, with no subpatcher
wrapper. This is the form emitted by gen transpilers.
A gen codebox's inlet/outlet counts are dynamic: they are determined by
the highest inN / outN references in the code. They are derived
automatically when numinlets / numoutlets are not given (each
defaults to at least 1).
add_v8_bridge ¶
add_v8_bridge(
bundle: Optional[str] = None,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: "Unpack[TextboxProps]",
) -> "Box"
Add a [v8] box running the js2max bridge, and ship it with the patch.
js2max is the JavaScript counterpart to this package: it runs inside
an open patcher and can build objects into it from a patch description,
or serialize the patcher back out to a .maxpat. Adding this box is
what makes a generated patch able to do either.
The runtime is written next to the patch when it is saved -- Max
resolves a bare filename through the folder holding the patch, so the
two sitting together need no configuration. Nothing is written for a
patcher that never called this, so an ordinary save() cannot leave a
stray .js file behind.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bundle
|
Optional[str]
|
Filename of the runtime to load. Defaults to the shipped drop-in build; pass another name only if you are installing the runtime yourself under a different name. |
None
|
patching_rect
|
Optional[Rect]
|
Position and size of the box. |
None
|
id
|
Optional[str]
|
Explicit object id. |
None
|
comment
|
Optional[str]
|
Associated comment text. |
None
|
comment_pos
|
Optional[str]
|
Where to place the associated comment. |
None
|
**kwds
|
'Unpack[TextboxProps]'
|
Further box properties. |
{}
|
Returns:
| Type | Description |
|---|---|
'Box'
|
The |
Example
p = Patcher('builder.maxpat') bridge = p.add_v8_bridge() p.save() # writes builder.maxpat and js2max.v8.js beside it
add_message ¶
add_message(
text: Optional[str] = None,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: "Unpack[TextboxProps]",
) -> "Box"
Add a max message.
add_comment ¶
add_comment(
text: str,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
justify: Optional[str] = None,
**kwds: "Unpack[TextboxProps]",
) -> "Box"
Add a basic comment object.
add_intbox ¶
add_intbox(
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add an int box object.
add_floatbox ¶
add_floatbox(
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add an float box object.
add_floatparam ¶
add_floatparam(
longname: str,
initial: Optional[float] = None,
minimum: Optional[float] = None,
maximum: Optional[float] = None,
shortname: Optional[str] = None,
id: Optional[str] = None,
rect: Optional[Rect] = None,
hint: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add a float parameter object.
add_intparam ¶
add_intparam(
longname: str,
initial: Optional[int] = None,
minimum: Optional[int] = None,
maximum: Optional[int] = None,
shortname: Optional[str] = None,
id: Optional[str] = None,
rect: Optional[Rect] = None,
hint: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add an int parameter object.
add_pattrstorage ¶
Add a pattrstorage object for saving and recalling named presets.
pattrstorage stores presets of every parameter-enabled and
pattr-bound object in the patcher. Pair it with :meth:add_autopattr
(or use :meth:add_preset_system) so named objects are exposed
automatically.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
the storage name (the pattrstorage argument). |
'presets'
|
**kwds
|
Any
|
extra attributes forwarded to the box. |
{}
|
add_autopattr ¶
Add an autopattr object exposing named objects to the pattr system.
Any object with a scripting name (varname) is bound automatically,
so it participates in pattrstorage presets without a per-object
pattr.
add_preset_system ¶
Add a standard preset system: autopattr + pattrstorage, wired.
Connects autopattr to pattrstorage so that any object with a
scripting name (varname) or parameter_enable=1 participates in
presets. Returns (autopattr_box, pattrstorage_box).
enable_parameter ¶
enable_parameter(
box: "Box",
longname: str,
shortname: str = "",
ptype: int = 0,
initial: Optional[float] = None,
) -> "Box"
Mark an existing box as a Max parameter.
Parameterized objects participate in pattrstorage presets and, in a
Max for Live device, appear as automatable parameters. Use this to turn
a plain UI object (toggle, dial, slider, ...) into a parameter without
rebuilding it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
box
|
'Box'
|
the box to parameterize. |
required |
longname
|
str
|
parameter long name (its preset/automation name). |
required |
shortname
|
str
|
optional short name. |
''
|
ptype
|
int
|
parameter type (0=float, 1=int, 2=enum, 3=blob). |
0
|
initial
|
Optional[float]
|
optional initial value. |
None
|
Returns:
| Type | Description |
|---|---|
'Box'
|
The same box, for chaining. |
add_attr ¶
add_attr(
name: str,
value: float,
shortname: Optional[str] = None,
id: Optional[str] = None,
rect: Optional[Rect] = None,
hint: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
autovar: bool = True,
show_label: bool = False,
**kwds: Any,
) -> "Box"
create a param-linke attrui entry
add_subpatcher ¶
add_subpatcher(
text: str,
maxclass: Optional[str] = None,
numinlets: Optional[int] = None,
numoutlets: Optional[int] = None,
outlettype: Optional[List[str]] = None,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
patcher: Optional["Patcher"] = None,
**kwds: Any,
) -> "Box"
Add a subpatcher object.
remove_line ¶
Remove a specific patchline connection from the patcher.
disconnect ¶
disconnect(
src: "Union[Box, str]",
dst: "Union[Box, str]",
outlet: int = 0,
inlet: int = 0,
) -> int
Remove connection(s) between src and dst.
Removes every patchline from src[outlet] to dst[inlet] and
returns how many were removed (0 if none matched). src/dst may be
Box objects or their ids.
Example
p.add_line(osc, gain) p.disconnect(osc, gain) 1
remove_box ¶
Remove a box and every patchline connected to it.
Accepts a Box or its id. Drops all incident patchlines, the box's entry in the object map / node index, and any pending associated comment for it. Returns the removed Box, or None if it was not present.
Example
osc = p.add_textbox('cycle~ 440') p.remove_box(osc)
replace ¶
Replace a box in place, preserving its position and connections.
old (a Box or its id) is swapped for new -- either the text for a
replacement object (created with its Max-class defaults) or an
already-built Box. The replacement takes old's slot and window
position, and every patchline incident to old is rewired to it,
keeping the same outlet/inlet indices and connection order. Any pending
associated comment for old transfers to the replacement. Returns the
new Box.
Ports are preserved by index: if the replacement has fewer inlets/outlets than the original, wires to the now-missing ports are kept as-is rather than silently dropped here (Max drops them on load).
Example
osc = p.add('cycle~ 440') p.add_line(osc, gain) saw = p.replace(osc, 'saw~ 220') # saw~ inherits osc's wiring
encapsulate ¶
Wrap the given boxes into a subpatcher, auto-generating inlet/outlet objects for connections that cross the selection boundary.
- Connections wholly inside the selection move into the subpatcher.
- Connections crossing in/out are rewired through generated
inlet/outletobjects and the new subpatcher box's ports. - Connections wholly outside the selection are left untouched.
Inlets/outlets are de-duplicated by source port, so multiple wires from the same outlet share a single port, matching how patches are usually built by hand.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
boxes
|
'Iterable[Box]'
|
boxes belonging to this patcher to move into a subpatcher. |
required |
text
|
str
|
the subpatcher box text (e.g. |
'p subpatch'
|
**kwds
|
Any
|
forwarded to |
{}
|
Returns:
| Type | Description |
|---|---|
'Box'
|
The new subpatcher Box added to this patcher. |
add_gen ¶
Add a gen object.
add_gen_tilde ¶
Add a gen~ object.
add_mc ¶
Add a multichannel (mc.) object.
Prefixes mc. to the object name if not already present, and appends
an @chans attribute when chans is given. A single patchline
between two mc. objects carries all channels.
Example
p.add_mc("cycle~ 440", chans=4) # -> "mc.cycle~ 440 @chans 4"
add_poly ¶
Add a poly~ object hosting voices instances of target.
target is the patch (or subpatcher) name loaded into each voice.
Example
p.add_poly("mysynth", 8) # -> "poly~ mysynth 8"
add_coll ¶
add_coll(
name: Optional[str] = None,
dictionary: Optional[Dict[Any, Any]] = None,
embed: int = 1,
patching_rect: Optional[Rect] = None,
text: Optional[str] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add a coll object with option to pre-populate from a py dictionary.
add_dict ¶
add_dict(
name: Optional[str] = None,
dictionary: Optional[Dict[Any, Any]] = None,
embed: int = 1,
patching_rect: Optional[Rect] = None,
text: Optional[str] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add a dict object with option to pre-populate from a py dictionary.
add_table ¶
add_table(
name: Optional[str] = None,
array: Optional[List[Union[int, float]]] = None,
embed: int = 1,
patching_rect: Optional[Rect] = None,
text: Optional[str] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
tilde: bool = False,
**kwds: Any,
) -> "Box"
Add a table object with option to pre-populate from a py list.
add_table_tilde ¶
add_table_tilde(
name: Optional[str] = None,
array: Optional[List[Union[int, float]]] = None,
embed: int = 1,
patching_rect: Optional[Rect] = None,
text: Optional[str] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add a table~ object with option to pre-populate from a py list.
add_itable ¶
add_itable(
name: Optional[str] = None,
array: Optional[List[Union[int, float]]] = None,
patching_rect: Optional[Rect] = None,
text: Optional[str] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add a itable object with option to pre-populate from a py list.
add_umenu ¶
add_umenu(
prefix: Optional[str] = None,
autopopulate: int = 1,
items: Optional[List[str]] = None,
patching_rect: Optional[Rect] = None,
depth: Optional[int] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add a umenu object with option to pre-populate items from a py list.
add_bpatcher ¶
add_bpatcher(
name: str,
numinlets: int = 1,
numoutlets: int = 1,
outlettype: Optional[List[str]] = None,
bgmode: int = 0,
border: int = 0,
clickthrough: int = 0,
enablehscroll: int = 0,
enablevscroll: int = 0,
lockeddragscroll: int = 0,
offset: Optional[List[float]] = None,
viewvisibility: int = 1,
patching_rect: Optional[Rect] = None,
id: Optional[str] = None,
comment: Optional[str] = None,
comment_pos: Optional[str] = None,
**kwds: Any,
) -> "Box"
Add a bpatcher object -- name or patch of bpatcher .maxpat is required.
find_by_id ¶
Find a box by its ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
box_id
|
str
|
The ID of the box to find. |
required |
Returns:
| Type | Description |
|---|---|
Optional[Box]
|
The Box object if found, None otherwise. |
Example
p = Patcher('patch.maxpat') osc = p.add_textbox('cycle~ 440') found = p.find_by_id(osc.id) assert found is osc
find_by_type ¶
Find all boxes of a specific Max object type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
maxclass
|
str
|
The Max object class name (e.g., 'newobj', 'message', 'comment'). |
required |
Returns:
| Type | Description |
|---|---|
List[Box]
|
List of Box objects matching the type. |
Example
p = Patcher('patch.maxpat') p.add_textbox('cycle~ 440') p.add_textbox('saw~ 220') p.add_message('bang') oscillators = [b for b in p.find_by_type('newobj') ... if 'cycle~' in b.text or 'saw~' in b.text] assert len(oscillators) == 2
find_by_text ¶
Find all boxes whose text matches a pattern.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pattern
|
str
|
The text pattern to search for (substring match). |
required |
case_sensitive
|
bool
|
Whether the search should be case-sensitive (default: False). |
False
|
Returns:
| Type | Description |
|---|---|
List[Box]
|
List of Box objects whose text contains the pattern. |
Example
p = Patcher('patch.maxpat') p.add_textbox('cycle~ 440') p.add_textbox('saw~ 220') p.add_textbox('gain~ 0.5') oscillators = p.find_by_text('~') assert len(oscillators) == 3 just_cycle = p.find_by_text('cycle') assert len(just_cycle) == 1
apply_theme ¶
Apply a color theme to every box in this patcher (and subpatchers).
theme is a named theme ("light", "dark", "blue",
"high-contrast") or a dict with "bg" / "text" / "border"
color values (each a name, hex string, or float sequence). Returns self
for chaining.
Example
Patcher('p.maxpat').apply_theme('dark')
from_dict
classmethod
¶
create a patcher instance from a dict
from_file
classmethod
¶
create a patcher instance from a .maxpat or .amxd file
find ¶
Find box object by maxclass or text pattern.
Recursively searches through all objects in the patch (including subpatchers) to find one matching the specified maxclass or text prefix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The maxclass name or text pattern to search for. |
required |
Returns:
| Type | Description |
|---|---|
Optional[Box]
|
The first matching Box object, or None if not found. |
find_box ¶
Find a box in this patcher (non-recursive) by maxclass or text prefix.
returns box if found else None
find_box_with_index ¶
Find a box and its index by maxclass or text prefix (non-recursive).
returns (index, box) if found
render ¶
cascade convert py2max objects to dicts.
Idempotent: rendering twice produces the same patcher, not two copies of
it. That matters because to_dict() renders on its own behalf, so a
save_as() renders once for its own log line and again underneath --
and because a caller has no way to know whether a render already
happened.
self.boxes used to be appended to while self.lines was
rebuilt, so a second render duplicated every box and no line. Nothing in
the repository passed reset_on_render=False, which is the only way
to reach that path, so the asymmetry had never bitten -- but it made
rendering order-dependent in exactly the way to_dict() was.
enable_presentation ¶
Configure this patcher to open as a Max for Live device.
Sets openinpresentation=1 so Ableton Live renders the device
strip instead of the patcher view, and optionally sets
devicewidth. Ableton's device strip height is fixed at ~170 px;
only width is author-controlled.
enforce_integer_coords ¶
Round all rect coordinates in this patcher tree to integers.
Ableton renders fractional device-strip coordinates blurry on non-retina displays. Returns the number of rects that were non-integer and got rounded. Recurses into subpatchers.
to_svg ¶
to_svg(
output_path: Union[str, Path],
show_ports: bool = True,
title: Optional[str] = None,
) -> None
Export this patcher to SVG format.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
output_path
|
Union[str, Path]
|
Output file path for the SVG. |
required |
show_ports
|
bool
|
Whether to show inlet/outlet ports on boxes. |
True
|
title
|
Optional[str]
|
Optional title to display at top of SVG. |
None
|
Example
p = Patcher('my-patch.maxpat') osc = p.add_textbox('cycle~ 440') dac = p.add_textbox('ezdac~') p.add_line(osc, dac) p.to_svg('/tmp/my-patch.svg')
to_svg_string ¶
Export this patcher to SVG format as a string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
show_ports
|
bool
|
Whether to show inlet/outlet ports on boxes. |
True
|
title
|
Optional[str]
|
Optional title to display at top of SVG. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
SVG content as a string. |
Example
p = Patcher('my-patch.maxpat') osc = p.add_textbox('cycle~ 440') svg_content = p.to_svg_string()
get_id ¶
Generate object ID, optionally semantic based on object name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
object_name
|
Optional[str]
|
Optional Max object name (e.g., 'cycle~', 'gain~'). Used to generate semantic IDs like 'cycle_1' when semantic_ids mode is enabled. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Object ID string (e.g., 'obj-5' or 'cycle_1'). |
set_layout_mgr ¶
takes a name and returns an instance of a layout manager
get_pos ¶
get box rect (position) via maxclass or layout_manager
optimize_layout ¶
Arrange the whole patch based on the active layout manager.
Calls the layout manager to lay out every object, then repositions any associated comments based on the new box positions. The effect depends on the layout manager:
- FlowLayoutManager: Arranges objects by signal flow topology
- GridLayoutManager: Clusters connected objects together
- Other managers: May have limited or no effect
This is a batch, whole-patch operation intended to be called once after
all objects and connections have been added. Interactive, per-edit
relayout is out of scope for py2max (see docs/auto-layout.md in
py2max-server).
lint ¶
Return patch-level lint findings (errors and warnings), errors first.
Checks connection validity, out-of-range ports, orphaned patchlines,
duplicate IDs, overlapping objects, off-canvas objects, and unknown
object classes. See :mod:py2max.lint.
py2max.core.Box ¶
Bases: AbstractBox
Represents a Max object in a patch.
The Box class encapsulates a single Max object with its properties, position, and connections. It provides methods for introspection and help information.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
maxclass
|
Optional[str]
|
Max object class name (e.g., 'newobj', 'flonum'). |
None
|
numinlets
|
Optional[int]
|
Number of input connections. |
None
|
numoutlets
|
Optional[int]
|
Number of output connections. |
None
|
id
|
Optional[str]
|
Unique identifier for the object. |
None
|
patching_rect
|
Optional[Rect]
|
Position and size rectangle. |
None
|
**kwds
|
'Unpack[BoxProps]'
|
Additional Max object properties. |
{}
|
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Unique identifier for the object. |
|
maxclass |
Max object class name. |
|
numinlets |
Number of input connections. |
|
numoutlets |
Number of output connections. |
|
patching_rect |
Position and size as Rect. |
oid
property
¶
Trailing numeric part of the object id, or None if it has none.
Works for numeric ids (obj-5 -> 5) and semantic ids
(cycle_1 -> 1).
set_color ¶
set_color(
bg: Optional["ColorLike"] = None,
text: Optional["ColorLike"] = None,
border: Optional["ColorLike"] = None,
) -> "Box"
Set this box's colors, returning self for chaining.
Each argument accepts a named color (e.g. "red"), a hex string
("#ff8800"), or an [r, g, b(, a)] float sequence (0..1). Sets the
bgcolor / textcolor / bordercolor attributes respectively.
Example
p.add_textbox("toggle").set_color(bg="blue", text="white")
add_to_presentation ¶
Mark this box as a presentation-mode UI element (Max for Live).
Sets presentation=1 and presentation_rect on the box. Rounds
fractional coordinates to integers with a warning. Raises if the box
is M4L infrastructure (live.remote~, live.map, etc.) that
must stay hidden from the device strip.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rect
|
Any
|
[x, y, width, height] in device-strip coordinates. |
required |
strict
|
bool
|
if True, warn when this isn't a known UI class. |
False
|
help_text ¶
Get formatted help documentation for this Max object.
Returns:
| Type | Description |
|---|---|
str
|
Formatted help string with object documentation from .maxref.xml files. |
get_info ¶
Get complete object information from .maxref.xml files.
Returns:
| Type | Description |
|---|---|
Optional[Dict[str, Any]]
|
Dictionary with complete object information or None if not found. |
get_inlet_count ¶
Get the number of inlets for this object from maxref data.
Returns:
| Type | Description |
|---|---|
Optional[int]
|
Number of inlets or None if unknown. |
get_outlet_count ¶
Get the number of outlets for this object from maxref data.
Returns:
| Type | Description |
|---|---|
Optional[int]
|
Number of outlets or None if unknown. |
get_inlet_types ¶
Get the inlet types for this object from maxref data
Returns:
| Type | Description |
|---|---|
List[str]
|
List of inlet type strings |
get_outlet_types ¶
Get the outlet types for this object from maxref data
Returns:
| Type | Description |
|---|---|
List[str]
|
List of outlet type strings |
py2max.core.Patchline ¶
Bases: AbstractPatchline
Represents a connection between two Max objects.
A Patchline connects an outlet of one object to an inlet of another, enabling signal or message flow between objects in a patch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Optional[List[Any]]
|
Source connection as [object_id, outlet_index]. |
None
|
destination
|
Optional[List[Any]]
|
Destination connection as [object_id, inlet_index]. |
None
|
**kwds
|
Any
|
Additional patchline properties. |
{}
|
Attributes:
| Name | Type | Description |
|---|---|---|
source |
Source object ID and outlet index. |
|
destination |
Destination object ID and inlet index. |