bebraw
1/14/2010 - 5:04 PM

poll_example.py

import threading
import visual

# FIXME: closing VPython window directly kills the whole app!
# TODO: check if it's possible to catch "tab" for autocompletion

class Poller:
    def __init__(self, thread_class, interpreter):
        assert hasattr(interpreter, '__call__')

        self.thread_class = thread_class
        self.interpreter = interpreter

    def poll(self):
        self._set_up_thread()

        while True:
            while not self.thread.data_ready.isSet():
                pass

            try:
                self.interpreter(self.thread.data)
            except SystemExit:
                break
            else:
                self._set_up_thread()

    def _set_up_thread(self):
        self.thread = self.thread_class()
        self.thread.start()

class KeyboardThread(threading.Thread):
    def __init__(self):
        self.data_ready = threading.Event()

        super(KeyboardThread, self).__init__()

    def run(self):
        self.data = self.get_data()
        self.data_ready.set()

class KeyboardInput(KeyboardThread):
    def get_data(self):
        return raw_input('>>> ')

class RepeatingTimer:
    def __init__(self, interval, callback):
        def repeat_callback():
            callback()

            if self.running:
                self._timer = self._create_timer()
                self._timer.start()

        self.interval = interval
        self.repeat_callback = repeat_callback

    def start(self):
        self._timer = self._create_timer()
        self.running = True
        self._timer.start()

    def _create_timer(self):
        timer = threading.Timer(self.interval, self.repeat_callback)

        # make sure thread gets killed on app quit
        timer.daemon = True

        return timer

    def cancel(self):
        self.running = False

class Bounce:
    def __init__(self):
        self.timer = RepeatingTimer(0.01, self._update)
        self._init_done = False

    def _update(self):
        delta = 0.01
        self.ball.pos += self.ball.velocity * delta

        if self.ball.y < 1:
            self.ball.velocity.y *= -1
        else:
            self.ball.velocity.y -= 9.8 * delta

    def start(self):
        if not self._init_done:
            self._init()

        self.timer.start()

    def _init(self):
        self.floor = visual.box(length=4, height=0.5, width=4,
            color=visual.color.blue)

        self.ball = visual.sphere(pos=(0,4,0), color=visual.color.red)
        self.ball.velocity = visual.vector(0,-1,0)

        self._init_done = True

    def cancel(self):
        self.timer.cancel()

def main():
    def interpreter(user_input):
        def bounce():
            visual.scene.visible = True
            v_bounce.start()

        def kill_bounce():
            visual.scene.visible = False
            v_bounce.cancel()

        def hello():
            print 'Hello dude!'

        def quit():
            print 'Bye bye!'

            # kill VPython window
            visual.scene.visible = False

            raise SystemExit

        handlers = {}

        for k, v in locals().items():
            if hasattr(v, '__call__'):
                command = k.replace('_', ' ')
                handlers[command] = v

        if user_input in handlers:
            handlers[user_input]()
        else:
            print 'Invalid input!'

    v_bounce = Bounce()
    poller = Poller(KeyboardInput, interpreter)
    poller.poll()

if __name__ == "__main__" :
    main()