>>> from pwb import *
>>> from components import *
>>> c1 = Integrate('integrate 1')
>>> c2 = Integrate('integrate 2')
>>> c3 = Minus('minus')
>>> c4 = Display2('display')
>>> connect(c1, c2)
>>> connect(c2, c3)
>>> connect(c3, c1)
>>> connect(c2, c4)
>>> c1.sum = 1.0
>>> run(100)
---
---
integrate 2 got 1.0 from integrate 1
minus got 0.0 from integrate 2
display got 0.0 from integrate 2
etc...

The Integrate component uses Euler's method, which is not sufficient to make the oscillation stable. However, by adding a little damping, the system can be made pseudo-stable:
>>> reset() >>> c1.dt = 0.25 >>> c2.dt = 0.25 >>> c1.damping = 0.9445 >>> c2.damping = 0.9445 >>> c1.sum = 1.0 >>> run(100) --- --- integrate 2 got 0.94450000000000001 from integrate 1 minus got 0.0 from integrate 2 display got 0.0 from integrate 2 etc...

import pwb
import components
import time
c1 = components.Integrate('integrate 1')
c2 = components.Integrate('integrate 2')
c3 = components.Minus('minus')
c4 = components.Display2('display')
pwb.connect(c1, c2)
pwb.connect(c2, c3)
pwb.connect(c3, c1)
pwb.connect(c2, c4)
c1.dt = 0.25
c2.dt = 0.25
c1.damping = 0.9445
c2.damping = 0.9445
c1.sum = 1.0
while not pwb.done():
pwb.run(1)
time.sleep(0.1)
This program can be run with:
python test1.py
Unfortunately, since none of the components in the program is ever done, the program runs forever until you kill it with control-C. One way to prevent this is by using a command line argument to set the number of loops (also in file test2.py):
import pwb import components import time import sys ... for i in xrange(int(sys.argv[1])): pwb.run(1) time.sleep(0.1)
This program can be run with:
python test2.py 1000
import pwb
import components
import time
import sys
c1 = components.Integrate('integrate 1')
c2 = components.Integrate('integrate 2')
c3 = components.Minus('minus')
c4 = components.Display2('display')
c5 = components.Slider('Damping')
pwb.connect(c1, c2)
pwb.connect(c2, c3)
pwb.connect(c3, c1)
pwb.connect(c2, c4)
c1.dt = 0.25
c2.dt = 0.25
c1.sum = 1.0
for i in xrange(int(sys.argv[1])):
pwb.run(1)
damping = 0.9 + 0.1 * c5.value
print 'Damping set to ' + repr(damping)
c1.damping = damping
c2.damping = damping
time.sleep(0.1)



class Slider2(Slider):
def __init__(self, name, n=1):
pwb.Component.__init__(self, name)
self.value = 1
# Create window, but not app
# Set number of sliders desired
self.window = controls.SliderWindow(self.name, self.handler, n)
self.window.show()
def handler(self, tag, newValue):
self.value = newValue
print self.name + ": tag = " + repr(tag) + " value = " + repr(newValue)
# Send both slider tag and value to all outputs
for o in range(len(self.outputs)):
self.send(o, (tag, self.value))
The image component must also be edited so that it can receive data directly into its input buffers, and then parse that data based on tag and value from just one input:
class ImageCircle2(pwb.Component):
def __init__(self, name, data, min=0.0, max=0.0, x=100.0, y=100.0, r=100.0):
pwb.Component.__init__(self, name)
self.x = x
self.y = y
self.r = r
# Create window, but not app
self.window = plots.ImageCircleWindow(self.name, data, min, max, self.x, self.y, self.r)
self.window.show()
def run(self):
if self.done(): return
if len(self.inputs) > 0 and len(self.inputs[0]['queue']) > 0:
# Get slider tag and value from one input
e = self.inputs[0]['queue'].pop(0)
tag = e[0]
value = e[1]
# Set x
if tag == 0:
self.x = value * self.window.pixmap.width()
# Set y
elif tag == 1:
self.y = (1.0 - value) * self.window.pixmap.height()
# Set radius
elif tag == 2:
self.r = value * sqrt(self.window.pixmap.width() * \
self.window.pixmap.width() + self.window.pixmap.height() * \
self.window.pixmap.height()) / 2.0
if self.x != self.window.x or self.y != self.window.y or self.r != self.window.r:
# Update display
self.window.x = self.x
self.window.y = self.y
self.window.r = self.r
self.window.update()
# Overload 'receive' function from Component class to put data directly into
# queue rather than intermediate buffer
def receive(self, input, data):
for i in self.inputs:
if i['component'] == input:
i['queue'].append(data)
self.run()
def reset(self):
pwb.Component.reset(self)
self.x = 100
self.y = 100
self.r = 100
self.window.x = self.x
self.window.y = self.y
self.window.r = self.r
self.window.update()
The new program (test6.py) is much simpler, and makes use of the Qt application event loop:
# Python Workbench prototype
# Draw a circle around a planet using 3 sliders
# (C)Sky Coyote, June 2007
# Import required code
import pwb
import components
from PyQt4 import QtCore, QtGui
import pyfits
app = QtGui.QApplication([])
c1 = components.Slider2('X - Y - R', 3)
c2 = components.ImageCircle2('Image', pyfits.getdata('images/c1230.en.fits'))
pwb.connect(c1, c2)
app.exec_()

İSky Coyote 2007