#!/usr/bin/env python # -*- coding: utf-8 -*- # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import commands import gi from gi.repository import Gtk from gi.repository import Gdk from gi.repository import Pango from gi.repository import GdkPixbuf from gi.repository import GObject from face import Face BASE = os.path.dirname(__file__) PITCH_MAX = 200 RATE_MAX = 200 def get_voices(): """Devuelve un diccionario del tipo: {"portugal": "pt-pt", . . .}""" ret = commands.getoutput('espeak --voices') voces = [] for linea in ret .split("\n"): voz = linea.split(" ") vv = [] for valor in voz: if valor: vv.append(valor) voces.append(vv) voces = voces[1:] voices = {} for voz in voces: if voz[2] != "M": voices[voz[2]] = voz[1] else: voices[voz[3]] = voz[1] return voices def get_separador(draw = False, ancho = 0, expand = False): """ Devuelve un separador generico.""" separador = Gtk.SeparatorToolItem() separador.props.draw = draw separador.set_size_request(ancho, -1) separador.set_expand(expand) return separador def get_boton(archivo, flip = False, color = Gdk.Color(65000, 65000, 65000)): """ Devuelve un toolbarbutton generico.""" boton = Gtk.ToolButton() imagen = Gtk.Image() pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(archivo, 32, 32) if flip: pixbuf = pixbuf.flip(True) imagen.set_from_pixbuf(pixbuf) imagen.modify_bg(0, color) boton.set_icon_widget(imagen) imagen.show() boton.show() return boton class Speaking(Gtk.Box): """Cara con Entrada de texto.""" __gsignals__ = {"speak":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING, ))} def __init__(self): Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL) self.entrycombo = None self.entry = None self.face = None self.entrycombo = Gtk.ComboBoxText.new_with_entry() self.entry = self.entrycombo.get_child() self.entry.can_activate_accel(True) font = Pango.FontDescription('sans bold 24') self.entry.modify_font(font) self.face = Face() self.pack_start(self.face, True, True, 0) self.pack_start(self.entrycombo, False, True, 0) self.show_all() self.entrycombo.connect("changed", self._combo_changed_cb) self.entry.connect('activate', self._entry_activate_cb) self.entry.connect("key-press-event", self._entry_key_press_cb) self.entry.connect("move-cursor", self._cursor_moved_cb) self.entry.connect("changed", self._cursor_moved_cb) def _cursor_moved_cb(self, entry): """Mira lo que el usuario escribe.""" index = entry.get_property('cursor_position') layout = entry.get_layout() pos = layout.get_cursor_pos(index) x = pos[0].x / Pango.SCALE - entry.get_property('scroll_offset') y = entry.get_allocation().y self.face.look_at(pos=(x, y)) def _combo_changed_cb(self, combo): if not self.entry.is_focus(): self.entry.grab_focus() self.entry.select_region(0, -1) def look_at(self, pos): """Le dice a la cara hacia donde mirar.""" self.face.look_at(pos) def _entry_activate_cb(self, entry): """Cuando el usuario presiona enter en la entrada de texto.""" text = entry.get_text() if text: self.emit('speak', text) history = self.entrycombo.get_model() if len(history) == 0 or history[-1][0] != text: self.entrycombo.append_text(text) while len(history) > 20: self.entrycombo.remove_text(0) self.entrycombo.set_active(len(history) - 1) entry.select_region(0, -1) def _entry_key_press_cb(self, combo, event): """Para navegar el historial de entradas en la entrada de texto, con el teclado.""" keyname = Gdk.keyval_name(event.keyval) index = self.entrycombo.get_active() if keyname == "Up": if index > 0: index -= 1 self.entrycombo.set_active(index) self.entry.select_region(0, -1) return True elif keyname == "Down": 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 class Toolbar(Gtk.Toolbar): """Toolbar Principal.""" __gsignals__ = {"switch-voice":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING, )), "switch-desktop":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING, )), "switch-toolbar":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING, ))} def __init__(self): Gtk.Toolbar.__init__(self) self.modify_bg(0, Gdk.Color(0, 0, 0)) self.voices = get_voices() self.insert(get_separador(draw = False, ancho = 32, expand = False), -1) archivo = os.path.join(BASE, "icons", "face.svg") boton = get_boton(archivo, flip = False, color = Gdk.Color(0, 0, 0)) self.insert(boton, -1) archivo = os.path.join(BASE, "icons", "mode-type.svg") boton = get_boton(archivo, flip = False, color = Gdk.Color(0, 0, 0)) self.insert(boton, -1) #boton.connect("clicked", self.emit_switch_desktop, "speaking") archivo = os.path.join(BASE, "icons", "mode-robot.svg") boton = get_boton(archivo, flip = False, color = Gdk.Color(0, 0, 0)) self.insert(boton, -1) #boton.connect("clicked", self.emit_switch_desktop, "robot") archivo = os.path.join(BASE, "icons", "mode-chat.svg") boton = get_boton(archivo, flip = False, color = Gdk.Color(0, 0, 0)) self.insert(boton, -1) #boton.connect("clicked", self.emit_switch_desktop, "chat") item = Gtk.ToolItem() combo = Gtk.ComboBoxText() voces = self.voices.keys() for key in sorted(voces): combo.append_text(key) combo.set_active(0) combo.connect('changed', self.emit_switch_voice) item.add(combo) self.insert(item, -1) archivo = os.path.join(BASE, "icons", "voice.svg") boton = get_boton(archivo, flip = False, color = Gdk.Color(0, 0, 0)) self.insert(boton, -1) boton.connect("clicked", self.emit_switch_toolbar, "pitch-rate") archivo = os.path.join(BASE, "icons", "face.svg") boton = get_boton(archivo, flip = False, color = Gdk.Color(0, 0, 0)) self.insert(boton, -1) boton.connect("clicked", self.emit_switch_toolbar, "face") self.show_all() def emit_switch_desktop(self, widget, value): """Cambiar entre chat, speaking y robot.""" self.emit("switch-desktop", value) def emit_switch_toolbar(self, widget, value): """Para que se muestre la toolbar pitch-rate o face.""" self.emit("switch-toolbar", value) def emit_switch_voice(self, widget): """Cuando cambia el lenguaje.""" self.emit("switch-voice", self.voices[widget.get_active_text()]) class ToolbarPitchRate(Gtk.Toolbar): """Toolbar con opciones pitch y rate.""" __gsignals__ = {"pitch":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_INT, )), "rate":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_INT, ))} def __init__(self): Gtk.Toolbar.__init__(self) self.modify_bg(0, Gdk.Color(0, 0, 0)) self.pitch = 0 self.rate = 0 self.insert(get_separador(draw = False, ancho = 32, expand = False), -1) item = Gtk.ToolItem() label = Gtk.Label("Pitch:") label.modify_fg(0, Gdk.Color(65000, 65000, 65000)) item.add(label) self.insert(item, -1) self.insert(get_separador(draw = False, ancho = 5, expand = False), -1) item = Gtk.ToolItem() pitchadj = Gtk.Adjustment(self.pitch, 0, PITCH_MAX, 1, PITCH_MAX / 10, 0) pitchbar = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL) pitchbar.set_adjustment(pitchadj) pitchbar = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL) pitchbar.set_adjustment(pitchadj) pitchbar.set_draw_value(False) pitchbar.set_size_request(240, 15) item.add(pitchbar) self.insert(item, -1) self.insert(get_separador(draw = False, ancho = 5, expand = False), -1) item = Gtk.ToolItem() label = Gtk.Label("Rate:") label.modify_fg(0, Gdk.Color(65000, 65000, 65000)) item.add(label) self.insert(item, -1) self.insert(get_separador(draw = False, ancho = 5, expand = False), -1) item = Gtk.ToolItem() rateadj = Gtk.Adjustment(self.rate, 0, RATE_MAX, 1, RATE_MAX / 10, 0) ratebar = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL) ratebar.set_adjustment(rateadj) ratebar.set_draw_value(False) ratebar.set_size_request(240, 15) item.add(ratebar) self.insert(item, -1) self.show_all() pitchadj.connect("value_changed", self.pitch_adjusted) rateadj.connect("value_changed", self.rate_adjusted) def pitch_adjusted(self, widget): """Cuando cambia pitch.""" self.pitch = int(widget.get_value()) self.emit('pitch', self.pitch) #self.face.say_notification(_("pitch adjusted")) def rate_adjusted(self, widget): """Cuando cambia rate.""" self.rate = int(widget.get_value()) self.emit('rate', self.rate) #self.face.say_notification(_("rate adjusted")) class ToolbarFace(Gtk.Toolbar): """Toolbar con opciones para la cara.""" __gsignals__ = {"number-eyes":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_INT, )), "switch-type-mouth":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING, )), "switch-type-eyes":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING, ))} def __init__(self): Gtk.Toolbar.__init__(self) self.modify_bg(0, Gdk.Color(0, 0, 0)) self.insert(get_separador(draw = False, ancho = 32, expand = False), -1) item = Gtk.ToolItem() label = Gtk.Label("Mouth:") label.modify_fg(0, Gdk.Color(65000, 65000, 65000)) item.add(label) self.insert(item, -1) self.insert(get_separador(draw = False, ancho = 5, expand = False), -1) item = Gtk.ToolItem() combo = Gtk.ComboBoxText() combo.append_text("Simple") combo.append_text("Waveform") combo.append_text("Frequency") combo.set_active(0) combo.connect('changed', self.emit_switch_type_mouth) item.add(combo) self.insert(item, -1) self.insert(get_separador(draw = False, ancho = 5, expand = False), -1) item = Gtk.ToolItem() label = Gtk.Label("Eyes:") label.modify_fg(0, Gdk.Color(65000, 65000, 65000)) item.add(label) self.insert(item, -1) self.insert(get_separador(draw = False, ancho = 5, expand = False), -1) item = Gtk.ToolItem() combo = Gtk.ComboBoxText() combo.append_text("Round") combo.append_text("Glasses") combo.set_active(0) combo.connect('changed', self.emit_switch_type_eyes) item.add(combo) self.insert(item, -1) self.insert(get_separador(draw = False, ancho = 5, expand = False), -1) item = Gtk.ToolItem() label = Gtk.Label("Eyes number:") label.modify_fg(0, Gdk.Color(65000, 65000, 65000)) item.add(label) self.insert(item, -1) self.insert(get_separador(draw = False, ancho = 5, expand = False), -1) item = Gtk.ToolItem() numeyesadj = Gtk.Adjustment(2, 1, 5, 1, 1, 0) numeyesbar = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL) numeyesbar.set_adjustment(numeyesadj) numeyesbar.set_draw_value(False) numeyesbar.set_size_request(240, 15) item.add(numeyesbar) self.insert(item, -1) self.show_all() numeyesadj.connect("value_changed", self.eyes_changed_number) def emit_switch_type_mouth(self, widget): """Cuando cambia el tipo de boca.""" self.emit("switch-type-mouth", widget.get_active_text()) def emit_switch_type_eyes(self, widget): """Cuando cambia el tipo de ojos.""" self.emit("switch-type-eyes", widget.get_active_text()) def eyes_changed_number(self, widget): """Cuando se cambia la cantidad de ojos.""" self.emit('number-eyes', int(widget.get_value())) #self.face.say_notification(_("eyes changed"))