Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Minor <j@lux.vu>2008-02-11 08:59:40 (GMT)
committer Joshua Minor <j@lux.vu>2008-02-11 08:59:40 (GMT)
commitc9450cfdf43b638b2d65b5aad7362a259cae3e57 (patch)
tree7f12677722afb735abebc5316ee97b69cdb03deb
parentea1c1ca1883a597f82ee7f2c8f7ec66777d698ee (diff)
Eyes now track the text cursor
Eyes now face ahead when speaking Added text history popup + arrow key navigation Fixed mouth corners so they match Text doesn't disappear when you press Enter
-rwxr-xr-xSpeak.activity/activity.py126
-rw-r--r--Speak.activity/activity/activity.info2
-rw-r--r--Speak.activity/eye.py37
-rw-r--r--Speak.activity/glasses.py5
-rw-r--r--Speak.activity/mouth.py1
-rw-r--r--dist/speak.xobin6966 -> 28911 bytes
6 files changed, 129 insertions, 42 deletions
diff --git a/Speak.activity/activity.py b/Speak.activity/activity.py
index fbe96ad..bac8f89 100755
--- a/Speak.activity/activity.py
+++ b/Speak.activity/activity.py
@@ -68,9 +68,12 @@ class SpeakActivity(activity.Activity):
#self.proc = None
# make a box to type into
- self.entry = gtk.Entry()
+ self.entrycombo = gtk.combo_box_entry_new_text()
+ self.entrycombo.connect("changed", self._combo_changed_cb)
+ self.entry = self.entrycombo.child
self.entry.set_editable(True)
- self.entry.connect('activate', self.entry_activate_cb)
+ self.entry.connect('activate', self._entry_activate_cb)
+ self.entry.connect("key-press-event", self._entry_key_press_cb)
self.input_font = pango.FontDescription(str='sans bold 24')
self.entry.modify_font(self.input_font)
@@ -86,7 +89,7 @@ class SpeakActivity(activity.Activity):
box = gtk.VBox(homogeneous=False)
box.pack_start(self.eyebox, expand=False)
box.pack_start(self.mouthbox)
- box.pack_start(self.entry, expand=False)
+ box.pack_start(self.entrycombo, expand=False)
self.set_canvas(box)
box.show_all()
@@ -112,18 +115,62 @@ class SpeakActivity(activity.Activity):
# make the text box active right away
self.entry.grab_focus()
-
- # start polling for audio
- #gobject.timeout_add(100, self._timeout_cb)
+ self.entry.connect("move-cursor", self._cursor_moved_cb)
+ self.entry.connect("changed", self._cursor_moved_cb)
+
+ # try to catch all mouse-moved events so the eyes will track wherever you go
+ # this doesn't work for some reason I don't understand
+ # it gets mouse motion over lots of stuff, but not sliders or comboboxes
+ # import time
+ # self.window.set_events(self.window.get_events() | gtk.gdk.POINTER_MOTION_MASK)
+ # def event_filter(event, user_data=None):
+ # map(lambda w: w.queue_draw(), self.eyes)
+ # print time.asctime(), time.time(), event.get_coords(), event.get_root_coords()
+ # return gtk.gdk.FILTER_CONTINUE
+ # self.window.add_filter(event_filter)
+ # map(lambda c: c.forall(lambda w: w.add_events(gtk.gdk.POINTER_MOTION_MASK)), self.window.get_children())
+
+ # start polling for mouse movement
+ # self.mouseX = None
+ # self.mouseY = None
+ # def poll_mouse():
+ # display = gtk.gdk.display_get_default()
+ # screen, mouseX, mouseY, modifiers = display.get_pointer()
+ # if self.mouseX != mouseX or self.mouseY != mouseY:
+ # self.mouseX = mouseX
+ # self.mouseY = mouseY
+ # map(lambda w: w.queue_draw(), self.eyes)
+ # return True
+ # gobject.timeout_add(100, poll_mouse)
+
+ # start with the eyes straight ahead
+ map(lambda e: e.look_ahead(), self.eyes)
+
# say hello to the user
self.active = True
presenceService = presenceservice.get_instance()
xoOwner = presenceService.get_owner()
- self.say("Hello %s, my name is XO. Type something." % xoOwner.props.nick)
+ self.say("Hello %s. Type something." % xoOwner.props.nick)
+
+ def _cursor_moved_cb(self, entry, *ignored):
+ # make the eyes track the motion of the text cursor
+ index = entry.props.cursor_position
+ layout = entry.get_layout()
+ pos = layout.get_cursor_pos(index)
+ x = pos[0][0] / pango.SCALE - entry.props.scroll_offset
+ y = entry.get_allocation().y
+ map(lambda e, x=x, y=y: e.look_at(x,y), self.eyes)
+
+ def get_mouse(self):
+ display = gtk.gdk.display_get_default()
+ screen, mouseX, mouseY, modifiers = display.get_pointer()
+ return mouseX, mouseY
def _mouse_moved_cb(self, widget, event):
- map(lambda w: w.queue_draw(), self.eyes)
+ # make the eyes track the motion of the mouse cursor
+ x,y = self.get_mouse()
+ map(lambda e, x=x, y=y: e.look_at(x,y), self.eyes)
def _mouse_clicked_cb(self, widget, event):
pass
@@ -186,18 +233,11 @@ class SpeakActivity(activity.Activity):
def rate_adjusted_cb(self, get, data=None):
self.say("rate adjusted")
-
def make_face_bar(self):
facebar = gtk.Toolbar()
self.numeyesadj = None
- # button = ToolButton('change-voice')
- # button.set_tooltip("Change Voice")
- # button.connect('clicked', self.change_voice_cb)
- # facebar.insert(button, -1)
- # button.show()
-
combo = ComboBox()
combo.connect('changed', self.mouth_changed_cb)
combo.append_item(mouth.Mouth, "Simple")
@@ -264,23 +304,53 @@ class SpeakActivity(activity.Activity):
# this SegFaults: self.say(self.eye_shape_combo.get_active_text())
self.say("eyes changed")
-
- def _timeout_cb(self):
- # make the mouth update with the latest waveform
- # ideally we would only do this when the audio is actually playing
- if self.mouth:
- self.mouth.queue_draw();
- return True
-
- def entry_activate_cb(self, entry):
+
+ def _combo_changed_cb(self, combo):
+ # when a new item is chosen, make sure the text is selected
+ if not self.entry.is_focus():
+ self.entry.grab_focus()
+ self.entry.select_region(0,-1)
+
+ def _entry_key_press_cb(self, combo, event):
+ # make the up/down arrows navigate through our history
+ keyname = gtk.gdk.keyval_name(event.keyval)
+ if keyname == "Up":
+ index = self.entrycombo.get_active()
+ if index>0:
+ index-=1
+ self.entrycombo.set_active(index)
+ self.entry.select_region(0,-1)
+ return True
+ elif keyname == "Down":
+ index = self.entrycombo.get_active()
+ if index<len(self.entrycombo.get_model())-1:
+ index+=1
+ self.entrycombo.set_active(index)
+ self.entry.select_region(0,-1)
+ return True
+ return False
+
+ def _entry_activate_cb(self, entry):
# the user pressed Return, say the text and clear it out
text = entry.props.text
if text:
+ # look ahead
+ map(lambda e: e.look_ahead(), self.eyes)
+
+ # speak the text
self.say(text)
- # ideally we would clear it after we finish saying it
- # so that you would be able to compare the audio to the text
- # without having to remember what you typed
- entry.props.text = ''
+
+ # add this text to our history unless it is the same as the last item
+ history = self.entrycombo.get_model()
+ if len(history)==0 or history[-1][0] != text:
+ self.entrycombo.append_text(text)
+ # don't let the history get too big
+ while len(history)>20:
+ self.entrycombo.remove_text(0)
+ # select the new item
+ self.entrycombo.set_active(len(history)-1)
+ # select the whole text
+ entry.select_region(0,-1)
def say(self, something):
if self.audio is None or not self.active:
diff --git a/Speak.activity/activity/activity.info b/Speak.activity/activity/activity.info
index 6029cec..9fffe88 100644
--- a/Speak.activity/activity/activity.info
+++ b/Speak.activity/activity/activity.info
@@ -3,5 +3,5 @@ name = Speak
service_name = vu.lux.olpc.Speak
class = activity.SpeakActivity
icon = activity-speak
-activity_version = 3
+activity_version = 4
show_launcher = yes
diff --git a/Speak.activity/eye.py b/Speak.activity/eye.py
index 72d9170..689a6d0 100644
--- a/Speak.activity/eye.py
+++ b/Speak.activity/eye.py
@@ -30,9 +30,10 @@ import math
class Eye(gtk.DrawingArea):
def __init__(self):
gtk.DrawingArea.__init__(self)
- self.connect("expose_event",self.expose)
+ self.connect("expose_event", self.expose)
self.frame = 0
self.blink = False
+ self.x, self.y = 0,0
# listen for clicks
self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
@@ -59,22 +60,40 @@ class Eye(gtk.DrawingArea):
self.blink = False
self.queue_draw()
- def get_mouse(self):
- display = gtk.gdk.display_get_default()
- screen, mouseX, mouseY, modifiers = display.get_pointer()
- return mouseX, mouseY
+ def look_at(self, x, y):
+ self.x = x
+ self.y = y
+ self.queue_draw()
+
+ def look_ahead(self):
+ self.x = None
+ self.y = None
+ self.queue_draw()
+
+ def pupil_position(self):
+ bounds = self.get_allocation()
+ abs_x, abs_y = self.translate_coordinates(self.get_toplevel(), 0, 0)
+ if self.x is not None and self.y is not None:
+ dir_x, dir_y = self.x - abs_x, self.y - abs_y
+ else:
+ # look ahead, but not *directly* in the middle
+ if bounds.x+bounds.width/2 < self.parent.get_allocation().width/2:
+ dir_x = bounds.width * 0.6
+ else:
+ dir_x = bounds.width * 0.4
+ dir_y = bounds.height * 0.6
+ pupilX = max(min(dir_x, bounds.width), 0)
+ pupilY = max(min(dir_y, bounds.height), 0)
+ return pupilX, pupilY
def expose(self, widget, event):
self.frame += 1
bounds = self.get_allocation()
- mouseX, mouseY = self.get_mouse()
-
eyeSize = min(bounds.width, bounds.height)
outlineWidth = eyeSize/20.0
pupilSize = eyeSize/10.0
- pupilX = max(min(mouseX - bounds.x, bounds.width), 0)
- pupilY = max(min(mouseY - bounds.y, bounds.height), 0)
+ pupilX, pupilY = self.pupil_position()
dX = pupilX - bounds.width/2.
dY = pupilY - bounds.height/2.
distance = math.sqrt(dX*dX + dY*dY)
diff --git a/Speak.activity/glasses.py b/Speak.activity/glasses.py
index afc68ef..61965a6 100644
--- a/Speak.activity/glasses.py
+++ b/Speak.activity/glasses.py
@@ -29,13 +29,10 @@ class Glasses(Eye):
def expose(self, widget, event):
bounds = self.get_allocation()
- mouseX, mouseY = self.get_mouse()
-
eyeSize = min(bounds.width, bounds.height)
outlineWidth = eyeSize/20.0
pupilSize = eyeSize/10.0
- pupilX = max(min(mouseX - bounds.x, bounds.width), 0)
- pupilY = max(min(mouseY - bounds.y, bounds.height), 0)
+ pupilX, pupilY = self.pupil_position()
dX = pupilX - bounds.width/2.
dY = pupilY - bounds.height/2.
distance = math.sqrt(dX*dX + dY*dY)
diff --git a/Speak.activity/mouth.py b/Speak.activity/mouth.py
index 453675d..7fb7197 100644
--- a/Speak.activity/mouth.py
+++ b/Speak.activity/mouth.py
@@ -88,6 +88,7 @@ class Mouth(gtk.DrawingArea):
self.context.curve_to(Tx,Ty, Tx,Ty, Rx,Ry)
self.context.curve_to(Bx,By, Bx,By, Lx,Ly)
self.context.set_source_rgb(0,0,0)
+ self.context.close_path()
self.context.stroke()
return True
diff --git a/dist/speak.xo b/dist/speak.xo
index 9ee0ec3..a240395 100644
--- a/dist/speak.xo
+++ b/dist/speak.xo
Binary files differ