> a = []
-> []
> a = [5, 6, "hi", 'symb', 3.4]
-> [5, 6, "hi", 'symb', 3.4]
> a[3]
-> symb
> subseq(a, 3)
-> ['symb', 3.4]
> subseq(a, 1, 3)
-> [6, "hi"]
> a.last()
-> 3.4
> a.insert(1, "new")
-> [5, "new", 6, "hi", 'symb', 3.4]
> a.append("newer")
-> [5, "new", 6, "hi", 'symb', 3.4, "newer"]
> a.unappend()
-> "newer"
> a
-> [5, "new", 6, "hi", 'symb', 3.4]
> a.setlen(3)
-> [5, "new", 6]
> len(a)
-> 3
> a.uninsert(1)
-> [5, 6]
> a.reverse()
-> [6, 5]
> a
-> [6, 5]
> b = a
-> [6, 5]
> a is b
-> t
> a.append(4)
-> [6, 5, 4] a is changed
> b
-> [6, 5, 4] note that b was also changed
> b = a.copy()
-> [6, 5, 4]
> a is b
-> nil now, b is a different array
> a.reverse()
-> [4, 5, 6] changing a does not change b
> b
-> [6, 5, 4]
wxSerpent has some built-in dialog boxes for opening files, opening directories, getting numbers and text, and displaying messages. This example shows how to select a file and display a message. Note that wxs_file_selector returns a path to the file -- it does not open the file. wxs_message_box returns a return code, which is one of WXS_MSG_YES, WXS_MSG_NO, WXS_MSG_CANCEL, or WXS_MSG_OK.
# dialog example
require "strparse.srp"
require "debug.srp"
require "wxserpent.srp"
print "calling wxs_file_selector(\"This is an example call";
print " to wxs_file_selector.\", \".\", \"data\", \"txt\",";
print " \"*\", WXS_FILE_OPEN, WXS_DEFAULT_WINDOW)"
print
file_sel = wxs_file_selector(
"This is an example call to wxs_file_selector.",
".", "data", "txt", "*", WXS_FILE_OPEN,
WXS_DEFAULT_WINDOW)
print "wxs_file_selector returned this value:"
print " ", file_sel
print
print "calling wxs_message_box(\"This message box has style";
print " WXS_STYLE_INFORMATION.\", \"This is an example call";
print " to wxs_message_box.\", WXS_STYLE_INFORMATION,";
print " WXS_DEFAULT_WINDOW)"
print
msg_box = wxs_message_box(
"This message box has style WXS_STYLE_INFORMATION.",
"This is an example call to wxs_message_box.",
WXS_STYLE_INFORMATION, WXS_DEFAULT_WINDOW)
print "wxs_message_box returned this value:"
print " ", msg_box
display "Return values", WXS_MSG_YES, WXS_MSG_NO,
display WXS_MSG_CANCEL, WXS_MSG_OK
> d = {}
-> {}
> d['address'] = "5000 Forbes Ave."
-> 5000 Forbes Ave.
> d['city'] = "Pittsburgh"
-> Pittsburgh
> d['state'] = "PA"
-> PA
> d['zip'] = "15213"
-> 15213
> print d['address']; "\n"; d['city']; ",", d['state'], d['zip']
5000 Forbes Ave.
Pittsburgh, PA 15213
-> nil
> d.keys()
-> ['address', 'city', 'state', 'zip']
> d['country']
runtime exception handler called
exception is: bad key
frame variables: {}
frame pc: 4
frame method: <immediate command 0>
frame class: nil
bad key, debugger invoked.
Method: <immediate command 0>, PC: 4, Line: 10, File: <stdin>
1> >
debugger reads >
Resume execution...
> >
> d.get('country', "USA") // provides default for attribute
-> USA
>
def chars()
# make characters from numbers
var char_a = ord("a") // ascii code for "a"
var alphabet = ""
for i = 0 to 26
alphabet = alphabet + chr(char_a + i)
print alphabet
# strings can be accessed as arrays
display "string access", "abcd"[2]
# some more string functions (apply to chars too of course)
display "string fns", toupper("abcd"), tolower("NeXT")
# find a substring
display "find", find(alphabet, "qrst"), find(alphabet, "qed")
# make a number from a string
display "convert to number", int("123"), real("123.456")
data = ["This", "is", "an", "array", "of", "words", "to", "search."]
# linear search with for loop, return index of word in data or -1
#
def search_with_for(data, word)
for i = 0 to len(data)
if data[i] == word
return i
return -1
# for-in loop
#
def search_with_for_in(data, word)
var i = 0
for w in data
if w == word
return i
i = i + 1
return -1
# special form of for eliminates call to len
#
def search_with_for_at(data, word)
for w at i in data // i is the index of w in data
if w == word
return i
return -1
# for simple equality searching, use index:
#
def search_with_index(data, word)
return data.index(word)
# test it
#
display "test 1", search_with_for(data, "of")
display "test 2", search_with_for_in(data, "of")
display "test 3", search_with_for_at(data, "of")
display "test 4", search_with_index(data, "of")
# --- init.srp ---
load "debug" // the ".srp" extension is assumed
load "sequencer"
You can of course split your program into multiple modules and load
multiple files from init.srp.# --- sequencer.srp ---
require "strparse" // the ".srp" extension is assumed
... other requires ...
... class and function definitions ...
If all modules use this convention, strparse.srp will be loaded the
first time it is needed and no more, making initialization faster.class Account:
var balance
# init() is called automatically when an Account is created
def init(initial_deposit)
balance = initial_deposit
def deposit(x)
balance = balance + x
# note: you could also write this.balance = ...
def withdraw(x)
if balance >= x
balance = balance - x
return balance
else
return false
# test
#
def account_test()
account = Account(0)
account.deposit(5)
display "account_test", account, account.balance
if not account.withdraw(10)
print "don't have $10"
# run it ...
> account_test()
account_test: account = <obj@0x205a48>, account.balance = 5
don't have $10
-> nil
>
class Account:
var balance
def init(initial_deposit)
balance = initial_deposit
def deposit(x)
balance = balance + x
def withdraw(x)
if balance >= x
balance = balance - x
return balance
else
return false
# extend the Account class with a name and show method
class Named_account(Account)
var name
def init(a_name, initial_deposit)
# first call superclass's init
super.init(initial_deposit)
name = a_name
def show()
print name, "has a balance of $"; balance
# test
#
def account_test()
account = Account(0)
account.deposit(5)
display "account_test", account, account.balance
if not account.withdraw(10)
print "don't have $10"
def named_account_test()
named_account = Named_account("Klaatu", 1000)
named_account.show()
named_account.withdraw(300) // inherited method
named_account.show()
# run it ...
> account_test()
account_test: account = <obj@0x205a48>, account.balance = 5
don't have $10
-> nil
> named_account_test()
Klaatu has a balance of $1000
Klaatu has a balance of $700
(setq
auto-mode-alist (cons '("\\.srp$" . serpent-mode) auto-mode-alist))
(setq interpreter-mode-alist (cons '("serpent" .
serpent-mode) interpreter-mode-alist))
(autoload 'serpent-mode "serpent-mode" "Serpent editing
mode." t)
def string_gtr(s1, s2)
s1 > s2
def filesort(in_file, out_file)
var inf = open(in_file, "r")
if not inf
return "Could not open " + in_file
var outf = open(out_file, "w")
if not outf
return "Could not open " + out_file
var content = inf.readlines()
inf.close()
content.sort('string_gtr')
outf.writelines(content)
outf.close()
# test it
#
filesort("unsorted.txt", "sorted.txt")
# another version that sorts by line length
#
def longer(s1, s2)
len(s1) > len(s2)
def filesort_by_len(in_file, out_file)
var inf = open(in_file, "r")
if not inf
return "Could not open " + in_file
var outf = open(out_file, "w")
if not outf
return "Could not open " + out_file
var content = inf.readlines()
inf.close()
content.sort('longer')
outf.writelines(content)
outf.close()
# test it
#
filesort_by_len("unsorted.txt", "by-length.txt")
Example input (unsorted.txt)
and output files (sorted.txt
and by-length.txt)
are online.# optargs.srp -- optional parameter examples
#
# Roger B. Dannenberg
# Jan, 2010
#---- example of "rest" parameter ----
#
# form sum of all arguments
#
def sum(rest a)
var s = 0
for x in a
s = s + x
s // just "s", or you can say "return s"
# test it
#
display "call sum", sum(1, 2, 3, 4, 5)
#---- example of optional parameter ----
#
# convert to string and print with arbitrary "quote" strings
#
def print_quoted(s, optional quote = "\"")
print quote + str(s) + quote
# test it
#
def print_quoted_tests()
print "test: print_quoted(23) prints ... ";
print_quoted(23)
print "test: print_quoted(23, \"'\") prints ... ";
print_quoted(23, "'")
print "test: print_quoted(23, \"\") prints ... ";
print_quoted(23, "")
print "test: print_quoted(\"hello world\", \"|\") prints ... ";
print_quoted("hello world", "|")
print_quoted_tests()
#---- example of keyword parameter ----
#
# print the time
#
def print_within(s, keyword prefix = "", keyword suffix = "")
print prefix; str(s); suffix
# test it
#
def print_within_tests()
print "test: print_within(23) prints ... ";
print_within(23)
print "test: print_within(\"My Paragraph\", prefix = \"<p>\") prints ... ";
print_within("My Paragraph", prefix = "<p>")
print "test: print_within(\"My Heading\", "
print " prefix = \"<h1>\", suffix = \"</h1>\") prints ... ";
print_within(23, prefix = "<h1>", suffix = "</h1>")
print_within_tests()
#---- example of dictionary parameter ----
#
# print keywords and their values
#
def print_args(required_parameter, dictionary d)
var keys = d.keys()
print "required_parameter =", required_parameter
for k in keys
print k, "=", d[k]
# test it
#
def print_args_test()
print "calling print_args(a = 1, b = 2, c = 3) ..."
print_args(123, a = 1, b = 2, c = 3)
print "... done"
print_args_test()
// String_parser is not built-in, so you must load the
// code if it has not already been loaded...
require "strparse"
# return an array of fields from an input string
#
def fields(s)
var sp = String_parse(s) // create and initialize the
// String_parser object
var result = [] // create empty array to accumulate results
sp.skip_space() // this is not necessary -- just illustrating
var field = sp.get_nonspace() // parse out a field
while field != "" // get all the fields
result.append(field) // append each field to array
field = sp.get_nonspace()
return result
# test it
#
print "fields(\"This is a test 123 %$&!\") returns",
print fields("This is a test 123 %$&!")
> def adder(x, y) // define a simple function
> x + y
> apply('adder', [5, 7]) // function is a Symbol, args are in an array
-> 12
> funcall('adder', 5, 7) // function is a Symbol, args in ordinary list
-> 12
> fn = 'adder' // you can of course use variables so that functions
-> 'adder'
> args = [4, 9] // and arguments depend on computation...
-> [4, 9]
> apply(fn, args)
-> 13
> require "strparse" // load a class
> s = String_parse("test string")
> meth = 'get_nonspace'
> send(s, meth)
-> test
> sendapply(s, meth, []) // pass an empty array if there are no arguments
-> string
# the fast way to build a big string
def flatten_example()
var text = []
for i = 0 to 1000
text.append(str(i)) // appending to arrays is efficient
text.append(",")
// now "flatten" the strings in the array to one string
return flatten(text)
# test it
#
print flatten_example()
# nested arrays can be flattened too
print flatten(["a", "b", ["c", "d"], "e"])
def str_repr()
display "str", str("abc"), len(str("abc"))
display "repr", repr("abc"), len(repr("abc"))
// the backslash is used to insert special characters in a string
// \n is newline, \t is tab, \\ is one backslash, \r is return
// \" is a double quote, \' is a single quote
var tricky = "quoted: \"abc\"" // the string is: quoted: "abc"
display "embedded quotes problem", repr(tricky)
// repr(tricky) does not escape the quotes, so the result is not
// machine readable
// print a quoted string with escaped quotes:
display "a solution", string_escape("quoted:\"abc\"", "\"")
// how to print a symbol with embedded escaped quotes:
display "symbol", string_escape(str('it\'s'), "'")
Try it out:> load "str-repr"
str: str("abc") = abc, len(str("abc")) = 3
repr: repr("abc") = "abc", len(repr("abc")) = 5
embedded quotes problem: repr(tricky) = "quoted: "abc""
a solution: string_escape("quoted:\"abc\"", "\"") = "quoted:\"abc\""
symbol: string_escape(str('it\'s'), "'") = 'it\'s'
-> nil
require "wxserpent"
// WXS_DEFAULT_WINDOW is the initial window created automatically
// when you run wxserpent. It's the parent of my_button.
// Parameters are parent, label, x, y, width, height:
my_button = Button(WXS_DEFAULT_WINDOW, "Click Me", 25, 5, 70, 25)
// Tell button who to call when pressed:
my_button.method = 'my_button_handler' // function to call
// Every handler takes 4 parameters:
def my_button_handler(obj, event, x, y)
// but for buttons, parameters are not so useful
print "my_button was clicked. As if you didn't know."
Buttons can invoke methods as well as call functions:require "wxserpent"
// declare a simple class and instantiate an object
class Counter
var count
def init()
count = 0
def increment(rest ignore) // ignore all parameters
count = count + 1
display "Counter", count
counter = Counter() // create an instance
// make a button to call the increment method
// notice that increment method must accept the 4 parameters
// passed to all graphical control handlers
obj_button = Button(WXS_DEFAULT_WINDOW, "Invoke a Method", 25, 75, 150, 25)
obj_button.target = counter
obj_button.method = 'increment'
require "wxserpent"
// WXS_DEFAULT_WINDOW is the initial window created automatically
// when you run wxserpent. It's the parent of my_slider, but you
// could open another window and put the slider there instead.
// Parameters are parent, minimum value (an integer), maximum value
// (an integer), initial value (an integer), x, y, width, height
// (all coordinates and sizes are integers):
my_slider = Slider(WXS_DEFAULT_WINDOW, 0, 127, 0, 5, 150, 100, 20)
my_slider.method = 'my_slider_handler' // function to call
def my_slider_handler(obj, event, x, y)
print "my_slider hit", event, "value:", x
my_gauge.set_value(x) // forward reference to object created next...
// A Gauge is an "analog" value indicator something like one bar of a
// bar graph. The parameters are parent window, range (an integer), and
// x, y, width, height bounding box coordinates (all integers):
my_gauge = Gauge(WXS_DEFAULT_WINDOW, 127, 5, 175, 100, 10)
Notice that 'my_slider_handler'
is used (assigned to my_slider.method)
before the function is defined. This is valid because 'my_slider_handler'
is just a symbol. It exists as soon as the compiler sees it. The slider
cannot generate any events or handler calls until the file is fully
loaded and compiled because Serpent is non-preemptive: there is no
other thread to make the call. By the time a handler can be called, the
function my_slider_handler()
will be defined.require "debug"
require "wxserpent"
// WXS_DEFAULT_WINDOW is the initial window created automatically
// when you run wxserpent. It's the parent of my_button.
Statictext(WXS_DEFAULT_WINDOW, "Single selection Listbox", 5, 5, 100, 20)
listbox = Listbox(WXS_DEFAULT_WINDOW, 5, 25, 100, 50, false)
def load_strings(lb):
# strings in listbox'es are added using append():
lb.append("cello")
lb.append("flute")
lb.append("trumpet")
lb.append("violin")
load_strings(listbox)
listbox.method = 'listbox_handler' // function to call for user actions
print "There are", listbox.get_count(), "items in the Listbox:"
for i = 0 to listbox.get_count():
print " ", listbox.get_string(i)
def listbox_handler(obj, event, x, y)
# handle listbox interaction events.
# Every handler takes 4 parameters:
# obj - the listbox
# event - the interaction event type
# event is WXS_LISTBOX_SELECTED
# when there is a new selection
# x, y - depend on the event
display "listbox event", obj, event, x, y, obj.get_count()
display "handler", obj.get_selections(), obj.value()
// Listbox'es can allow multiple selections. Here is another listbox
// to illustrate this feature and how to use it
Statictext(WXS_DEFAULT_WINDOW, "Multiple selection Listbox", 5, 125, 100, 20)
listbox_m = Listbox(WXS_DEFAULT_WINDOW, 5, 150, 100, 50, true)
load_strings(listbox_m) // put the same strings in it
listbox_m.method = 'listbox_handler' // function to call for user actions
print "There are", listbox_m.get_count(), "items in the Listbox:"
for i = 0 to listbox_m.get_count():
print " ", listbox_m.get_string(i)
// Listbox'es can allow multiple selections. Here is another listbox
// to illustrate this feature and how to use it
Statictext(WXS_DEFAULT_WINDOW, "Multiple selection Listbox", 5, 125, 100, 20)
listbox_m = Listbox(WXS_DEFAULT_WINDOW, 5, 150, 100, 50, true)
load_strings(listbox_m) // put the same strings in it
listbox_m.method = 'listbox_handler' // function to call for user actions
Notice that 'listbox_handler'
is used (assigned to listbox.method
and listbox_m.method) before the function is
defined. This is valid because 'listbox_handler'
is just a symbol. It exists as soon as the compiler sees it. The
listbox cannot generate any events or handler calls until the file is
fully loaded and compiled because Serpent is non-preemptive: there is
no other thread to make the call. By the time a handler can be called,
the function listbox_handler()
will be defined.require "wxserpent"
// WXS_DEFAULT_WINDOW is the initial window created automatically
// when you run wxserpent. It is the parent of my_menu, but you
// could open another window and put the menu there instead.
Statictext(WXS_DEFAULT_WINDOW, "Please try the Command menu",
10, 10, 300, 30)
// Create a Menu; parameters are parent and menu name.
my_menu = Menu(WXS_DEFAULT_WINDOW, "Command")
my_menu.method = 'my_menu_handler' // function to call
// Add items to a menu using the item method. Parameters
// are name (string), help string, and checkable (boolean)
my_menu.item("Hello World", "print Hello World", false)
// The separator() method inserts a separator into the menu
my_menu.separator()
// If checkable is true, the menu item has a boolean state
// and will display a check mark when the state is true
my_menu.item("Trace", "print some trace info", true)
// this flag will be controlled by the Trace menu item
menu_trace_flag = false
def my_menu_handler(obj, event, x, y)
if menu_trace_flag
display "my_menu_handler", obj, event, x, y
if event != WXS_MENU_SELECTED
return // ignore anything other than menu item selection
if x == 0 // first menu item
print "Hello World"
elif x == 1 // the separator
print "This never prints because you cannot select a separator"
elif x == 2 // second menu item
// y == 0 => not checked; y == 1 => checked
menu_trace_flag = (y == 1)
print "trace"
require "wxserpent"
// WXS_DEFAULT_WINDOW is the initial window created automatically
// when you run wxserpent. It's the parent of my_textctrl.
// Parameters are parent, initial text, x, y, width, height:
my_textctrl = Textctrl(WXS_DEFAULT_WINDOW, "Hello World", 25, 5, 200, 25)
// Tell Textctrl who to call when pressed:
my_textctrl.method = 'my_textctrl_handler' // function to call
// Every handler takes 4 parameters:
def my_textctrl_handler(obj, event, x, y)
// for Textctrl, parameters are not so useful, but get_value()
// can be used to get the text in the box. Not the use of "\""
// which is a string containing a quote character ("\" is the
// "escape" character that quotes the next character):
print "my_textctrl has \""; my_textctrl.value(); "\""
require "debug"
require "wxserpent"
MY_CANVAS_WIDTH = 500
MY_CANVAS_HEIGHT = 2000
// create a subclass of Scrolled_canvas
class My_canvas (Scrolled_canvas)
def init(parent, x, y, w, h)
super.init(parent, x, y, w, h)
set_virtualsize(MY_CANVAS_WIDTH, MY_CANVAS_HEIGHT)
def paint(flag):
for x = 50 to MY_CANVAS_WIDTH by 150:
for y = 50 to MY_CANVAS_HEIGHT by 100:
set_brush_color("YELLOW") // so text is visible
draw_rectangle(x, y, 100, 20)
draw_text(x + 3, y + 3, "(" + str(x) + ", " + str(y) + ")")
// implement the action of the button. Catch all parameters in "rest" so
// you can call do_jump() with no parameters, OR call it as an event
// handler that gets obj, event, x, and y parameters...
def do_jump(rest ignore):
scroll(int(MY_CANVAS_WIDTH / 2), int(MY_CANVAS_HEIGHT / 2))
my_canvas = My_canvas(WXS_DEFAULT_WINDOW, 0, 0, 200, 200)
// make a button that jumps to a middle location
jump = Button(WXS_DEFAULT_WINDOW, "Jump", 220, 20, 100, 30)
jump.method = 'do_jump'
jump.target = my_canvas
require "debug"
require "wxserpent"
// create a subclass of Canvas -- you need to subclass Canvas in
// order to override the paint() method, which is how you make
// custom graphics
//
class Drawing (Canvas)
// the state is an array of lines, where each line is an array
// of x1, y1, x2, y2 coordinates for the endpoints of lines
var lines
var mouse_is_down
// override init() in order to intialize the state of this
// subclass, but pass the usuall parent, x, y, w, h parameters
// to the init() method defined in the Canvas class so that
// the Canvas state is also initialized.
//
def init(parent, x, y, w, h)
super.init(parent, x, y, w, h)
lines = []
// override paint to do custom drawing
//
def paint(x)
for line in lines
draw_line(line[0], line[1], line[2], line[3])
// override handle to handle mouse events
//
def handle(event, x, y)
var line
if event == WXS_LEFT_DOWN
if mouse_is_down
return
line = [x, y, x, y]
lines.append(line)
mouse_is_down = true
elif event == WXS_MOVE and mouse_is_down
// change the end-point of the last line to follow the mouse
line = lines.last()
line[2] = x
line[3] = y
// It might be possible to receive a mouse up without a mouse down
// E.g. the mouse down might be captured by the window system to
// change the focus to this canvas, and the mouse down might not
// be delivered to the canvas. In that case, ignore the mouse-up
// event because we have not received a mouse down yet.
elif event == WXS_LEFT_UP and mouse_is_down
line = lines.last()
line[2] = x
line[3] = y
mouse_is_down = false
else // could be keyboard input
return // no state change, so return
// State has changed. Request redraw.
refresh(true)
// create a canvas to draw on
drawing = Drawing(WXS_DEFAULT_WINDOW, 0, 0, 200, 200)
require "debug"
require "wxserpent"
class My_window (Window):
var canvas
def init(title, x, y, w, h):
super.init(title, x, y, w, h)
canvas = My_canvas(this.id, 0, 0, w, h)
canvas.refresh(t)
def on_size(x, y)
display "on_size", x, y
canvas.set_size(x, y)
canvas.refresh(t)
// create a subclass of Canvas
class My_canvas (Canvas)
def paint(flag):
var x = get_width()
var y = get_height()
draw_text(30, 3, "(" + str(x) + ", " + str(y) + ")")
// draw an arrow pointing to lower right corner
draw_line(0, 0, x, y)
draw_line(x - 30, y - 15, x, y)
draw_line(x - 15, y - 30, x, y)
my_window = My_window("Resizable Window With Canvas", 100, 100, 400, 400)
osc
functions.o2_initialize()
.
O2 messages are directed to services, so our client needs a service
that delgates to an OSC server, and our server needs to create a port
for incoming OSC messages. Note that if you really wanted to send from
process to process, you'd be better off just using O2 and sending
directly to the remote service, eliminating OSC altogether. But if you
want to receive from an OSC sender or send to an OSC receiver, these
examples show how.
The server is created using o2_osc_port_new() which can listen on a TCP or a UDP port. The client is created using o2_osc_delgate()
which creates an O2 service that forwards incoming messages to an OSC server.
The following is a simple OSC client.
def startup()
o2_initialize("o2osc", t) // string is the O2 application name, t for debug
// parameters are: service name, IP address (string), port (int), use TCP flag
o2_osc_delegate("oscsend", "localhost", 7770, false) // use UDP
def send_afloat()
o2_send_start()
o2_add_float(3.14159)
// parameters are: timestamp (0 for now), address (note that it starts with
// the service name, and reliable (tcp) transmission flag (in this case,
// the ultimate transmission is controlled by the flag to o2_osc_delegate,
// which says send via UDP to the OSC server. The OSC server is probably
// using UDP; if so, you must delegate via UDP or you'll never connect.
o2_send_finish(0.0, "/oscsend/afloat", false)
def run()
startup()
while true
send_afloat()
time_sleep(3.0)
run()
def osc_handler(timestamp, path, types, rest parameters)
display "osc_handler", timestamp, path, types, parameters
def startup()
// note that the first parameter can be any application name -- it does
// not have to match that of the sender
display "oscserver", o2_initialize("o2osc", t)
display "oscserver", o2_service_new("oscrecv") // create service
// attach a handler to the service, parameters are the path to handle,
// the types expected, the handler (function name, a symbol), and coerce
// flag that tells O2 to convert to the specified type(s) if possible
display "oscserver", o2_method_new("/oscrecv/afloat", "f", 'osc_handler', true)
// create receive port for OSC messages, which are forwarded to the service
// named by the first parameter. Port number is 7770, and false means UDP.
display "oscserver", o2_osc_port_new("oscrecv", 7770, false)
def run()
startup()
while true
o2_server_poll()
time_sleep(0.1)
run()
The server prepares for messages by registering address
strings using osc_server_method(). This
associates an address string with a type string denoting the expected
number and types of values, an object to handle the message, and a
method to invoke.
The following is a simple OSC client.
def startup()
addr = osc_create_address("", "7770", false)
def send_afloat()
osc_send_start()
osc_add_float(3.14159)
osc_send(addr, "/afloat")
def run()
startup()
while true
send_afloat()
time_sleep(3.0)
run()
def osc_handler(path, rest parameters)
display "osc_handler", path, parameters
def startup()
display "osctest", osc_server_init("7770", t)
display "osctest", osc_server_method("/afloat", "f", nil, 'osc_handler')
def run()
startup()
while true
osc_server_poll()
time_sleep(0.1)
run()
def main()
zmq_init() // prepare to use zmq
var socket = zmq_open_reply()
display "bind step", zmq_bind(socket, "tcp", "*", 5555)
for i = 0 to 10
var req = zmq_recv_block(socket)
print "Server:", req, "-> World"
zmq_send(socket, "World")
zmq_close(socket)
zmq_term()
main()
exit()
def main()
zmq_init() // prepare to use zmq
var socket = zmq_open_request()
display "connect step", zmq_connect(socket, "tcp", "localhost", 5555)
for i = 0 to 10
zmq_send(socket, "Hello")
var result = zmq_recv_block(socket)
print "Client: Hello ->", result
zmq_close(socket)
zmq_term()
main()
exit()
ZeroMQ assumes that communication patterns are regular. The
Request/Reply pattern illustrated by the client/server code above is
one example. Other patterns supported by ZeroMQ are the Push/Pull
pattern, where a sender produces messages that are consumed by a
receiver, and the Publish/Subscribe pattern, where messages are
"broadcast" to any number of receivers that can selectively receive
messages by topic. Examples of these can be found in
serpent/programs/zmq*.srp. Some details on the corresponding functions
can be found in the Serpent documentation. Most of these functions
correspond closely to the ZeroMQ API for the C programming language,
and extensive ZeroMQ documentation is available online.
When a program exits or a window closes, you may want to
prompt the user to save data or perform other cleanup actions. In the
following little program, a handler is installed for the File:Quit menu
item and another handler is installed for the default window.
The quit_handler for the File:Quit menu
is only called when Quit is selected. The handler pops up a dialog box
to get confirmation from the user. If the user clicks "yes," we want to
really quit. However, since Quit is "handled" in this code, wxserpent
does not normally pass the event on to the built-in handler which will
actually exit the application. To get wxserpent to invoke the built-in
handler, we have to tell wxserpent explicitly that this event was not
handled. This is accomplished by calling wxs_event_not_handled().
Note that we only call wxs_event_not_handled() if
the user answers "yes" in the dialog box.
The win_handler handles every
event to the window that is not first captured by some component within
the window. We are only interested in the WXS_CLOSE_WINDOW event, so we
test for it. When found, we use the same strategy as quit_handler:
get confirmation with a dialog box, and if the user really wants to
close, use wxs_event_not_handled() to tell
wxserpent to pass the event on to the built-in close-window event
handler.
require "debug"
require "wxserpent"
win = default_window
win.method = 'win_handler'
file_menu = win.get_menu("File")
// parameters are Menu item name, Help string, Checkable, Target, Method:
file_menu.item("Quit", "quit the application", nil, nil, 'quit_handler')
display "file quit", file_menu.map, file_menu.menu
def quit_handler(obj, event, x, y):
display "quit_handler", obj, event, x, y
var quit = wxs_message_box("Do you really want to quit?",
"Quit selected", WXS_STYLE_YES_NO, win.id)
display "message box returns", quit, WXS_MSG_NO, WXS_MSG_YES
// The default is that this handler performed the request, so no
// further action (e.g. really quit the application) is performed.
// We override this default by calling wxs_event_not_handled().
if quit == WXS_MSG_YES:
wxs_event_not_handled()
def win_handler(obj, event, x, y):
display "win_handler", obj, event, x, y, WXS_CLOSE_WINDOW
if event == WXS_CLOSE_WINDOW:
var close = wxs_message_box("Do you really want to close?",
"Close", WXS_STYLE_YES_NO, win.id)
if close == WXS_MSG_YES:
wxs_event_not_handled()