Getting started¶
Install¶
py2tosc has no runtime dependencies and needs Python 3.10 or newer.
Loading a layout¶
load accepts either format the TouchOSC editor writes: the compressed .tosc a layout is saved as, and the .xml it exports. You do not have to say which you have.
import py2tosc
doc = py2tosc.load("mixer.tosc")
print(doc)
#> <Document version=6 root=<GROUP, 3 children> (24 controls)>
A Document holds one root control -- always a group -- and the format version. Everything else hangs off the root.
Finding controls¶
find returns the first match anywhere beneath the root; find_all returns every match. Both take a name, a control type, or both.
doc.find("cutoff") # by name
doc.find_all(type="FADER") # by type
doc.find("cutoff", type="FADER") # both
doc.root.children # direct children only
list(doc.walk()) # every control, depth first
Reading and writing properties¶
Properties are attributes. Python names are snake_case; the camelCase keys the file format uses are handled for you.
fader = doc.find("cutoff")
fader.name #> 'cutoff'
fader.frame #> Frame(x=77, y=60, w=50, h=200)
fader.color #> Color(r=1.0, g=0.0, b=0.0, a=1.0)
fader.corner_radius #> 1.0 (the file's "cornerRadius")
fader.grid_steps #> 13 (the file's "gridSteps")
fader.color = "#e76f51"
fader.frame = (10, 20, 50, 200)
fader.visible = False
Colours accept floats, 0-255 integers or hex strings, and frames accept any four-item sequence. Both come back as named tuples that still compare as plain tuples:
Reading a property that is not set raises AttributeError. Use get when absence is expected:
Creating controls¶
There is a factory function per control type, each applying that type's defaults. Any property can be passed as a keyword argument.
import py2tosc
f = py2tosc.fader(name="cutoff", frame=(0, 0, 50, 200), color="#e76f51")
g = py2tosc.group(name="panel")
g.add(f)
Available: box, button, label, text, fader, xy, radial, encoder, radar, radio, group, pager, grid.
Copying¶
copy duplicates a control and everything beneath it, with fresh ids so the result is still a valid layout.
strip = doc.find("channel1")
for channel in range(2, 9):
doc.add(strip.copy(name=f"channel{channel}"))
Saving¶
The extension decides the format: .xml writes the readable export, anything else writes a compressed .tosc.
doc.save("mixer.tosc") # what TouchOSC opens
doc.save("mixer.xml") # readable, useful in diffs
text = doc.dumps() # the XML as a string
A complete example¶
import py2tosc
from py2tosc import layout
doc = py2tosc.Document.new(frame=(0, 0, 1024, 768))
top, bottom = layout.column(doc.root, sizes=(3, 1), colors=("#264653", "#2a9d8f"))
for index, fader in enumerate(layout.row(top, "FADER", sizes=8)):
fader.name = f"ch{index + 1}"
fader.messages.append(py2tosc.OscMessage())
for index, button in enumerate(layout.row(bottom, "BUTTON", sizes=8)):
button.name = f"mute{index + 1}"
doc.save("mixer.tosc")