Skip to content
Snippets Groups Projects
Commit 4898b8a9 authored by Benoit Favre's avatar Benoit Favre
Browse files

refactor actions; push words through osc

parent 6d59f30b
No related branches found
No related tags found
No related merge requests found
...@@ -12,33 +12,41 @@ copy libgstkaldionline2.so to ./asr/ or change GST_PLUGIN_PATH in main.py to poi ...@@ -12,33 +12,41 @@ copy libgstkaldionline2.so to ./asr/ or change GST_PLUGIN_PATH in main.py to poi
Run: Run:
python2 main.py - The main program:
./start.sh
Doc: - The osc server:
python2 osc.py
Doc:
developing with pygtk3: http://lazka.github.io/pgi-docs/, https://python-gtk-3-tutorial.readthedocs.org/en/latest/ developing with pygtk3: http://lazka.github.io/pgi-docs/, https://python-gtk-3-tutorial.readthedocs.org/en/latest/
Todo: Todo:
DONE configuration for osc
DONE non intrusive animated scrolling
DONE make configuration box options persistant
DONE push words through osc
DONE global action send which deals with
DONE - show a warning (optional)
DONE - send action through osc
DONE - show an action performed message (message log with timing?)
events = click action or words to resynchronize ? events = click action or words to resynchronize ?
click line = synchronize to that line click line = synchronize to that line
click section = select that section click section = select that section
click action = perform action and synchronize to the next line click action = perform action and synchronize to the next line
non intrusive animated scrolling
global action send which deals with
- show a warning (optional)
- sned action through osc
- show an action performed message (message log with timing?)
add global timer which shows elapsed time add global timer which shows elapsed time
change xml view to reflect already performed actions, already recognized text change xml view to reflect already performed actions, already recognized text
configuration for osc move slu to asr
make selector a proper window make selector a proper window
make configuration box options persistant
add thread for slu add thread for slu
allow sequence advance in slu, add UI for that allow sequence advance in slu, add UI for that
remove section changer UI
add global keybindings (1-9 for sections, y/n)...
from gi.repository import Gtk
class ActionView(Gtk.HBox):
def __init__(self):
super(ActionView, self).__init__()
self.actions = set(['next', 'previous', 'light-on', 'light-off'])
self.pack_start(Gtk.Label('Actions:'), False, False, 5)
for action in self.actions:
button = Gtk.Button(action)
button.connect('clicked', self._perform)
self.pack_start(button, False, False, 5)
self.confiermer = None
def _perform(self, button):
action = button.get_label()
if self.confirmer:
self.confirmer.confirm('Perform action "%s"?' % action, 4, lambda: self.perform(action))
else:
self.perform(action)
def perform(self, action):
if action in self.actions:
print 'PERFORM', action
return True
return False
def actions(self):
return self.actions
def set_confirmer(self, confirmer):
self.confirmer = confirmer
import osc, log
class Action:
def __init__(self, text, **kwargs):
self.text = text
for key, value in kwargs:
setattr(self, key, value)
class ActionManager:
def __init__(self, host, port, confirmer, highlighter, logger=log.ConsoleLogger()):
#self.client = osc.Client(host, port)
self.confirmer = confirmer
self.highlighter = highlighter
self.logger = logger
def confirmed_perform(self, action):
osc.client.send_action(action)
self.highlighter.highlight(action)
self.logger.log(action)
def perform(self, action, confirm=True, timeout=3):
if confirm:
self.confirmer.confirm('Perform action "%s"?' % action.text, time, lambda: self.confirmed_perform(action))
else:
self.confirmed_perform(action)
manager = None
def setup(confirmer, highlighter, logger):
global manager
manager = ActionManager(confirmer, highlighter, logger)
def perform_action(action, confirm=True, timeout=3):
global manager
manager.perform(action, confirm, timeout)
...@@ -9,13 +9,13 @@ def cancel(): ...@@ -9,13 +9,13 @@ def cancel():
LINEAR=1 LINEAR=1
DECELERATE=2 DECELERATE=2
def animate_value(callback, current, target, policy=DECELERATE): def animate_value(callback, current, target, policy=DECELERATE, speed=32):
global timer global timer
if current != target: if current != target:
if policy == DECELERATE: if policy == DECELERATE:
delta = abs(target - current) / 2 delta = abs(target - current) / 2
else: else:
delta = 32 delta = speed
if current > target: if current > target:
current -= delta current -= delta
else: else:
......
...@@ -8,6 +8,8 @@ GObject.threads_init() ...@@ -8,6 +8,8 @@ GObject.threads_init()
Gdk.threads_init() Gdk.threads_init()
Gst.init(None) Gst.init(None)
import osc
class ASR(Gtk.HBox): class ASR(Gtk.HBox):
def __init__(self, asr_config, hyp_callback = None, partial_hyp_callback = None): def __init__(self, asr_config, hyp_callback = None, partial_hyp_callback = None):
super(ASR, self).__init__() super(ASR, self).__init__()
...@@ -110,6 +112,10 @@ class ASR(Gtk.HBox): ...@@ -110,6 +112,10 @@ class ASR(Gtk.HBox):
Gdk.threads_enter() Gdk.threads_enter()
if len(self.hyp) == 0: if len(self.hyp) == 0:
self.hyp = [''] self.hyp = ['']
if hyp != self.hyp[-1]:
osc.client.send_words(len(self.hyp), hyp)
self.hyp[-1] = hyp self.hyp[-1] = hyp
if self.partial_hyp_callback: if self.partial_hyp_callback:
self.partial_hyp_callback(self.hyp) self.partial_hyp_callback(self.hyp)
......
...@@ -31,6 +31,7 @@ class ConfirmationBox(Gtk.HBox): ...@@ -31,6 +31,7 @@ class ConfirmationBox(Gtk.HBox):
self.yes_button.get_child().set_text("YES (%d)" % self.counter) self.yes_button.get_child().set_text("YES (%d)" % self.counter)
self.timer = GObject.timeout_add_seconds(1, self.countdown) self.timer = GObject.timeout_add_seconds(1, self.countdown)
self.yes_button.grab_focus()
self.show() self.show()
def click_yes(self, button = None): def click_yes(self, button = None):
......
log.py 0 → 100644
from gi.repository import Gtk
import actions
class ConsoleLogger:
def __init__(self):
pass
def log(self, action):
print 'Performed:', action.text
class GtkLogger(Gtk.HBox):
def __init__(self):
super(Log, self).__init__()
self.text = Gtk.TextView()
self.text.set_editable(False)
self.text.set_cursor_visible(False)
self.buffer = self.text.get_buffer()
self.scrolled = Gtk.ScrolledWindow()
self.scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS)
self.scrolled.add_with_viewport(self.text)
#self.scrolled.set_size_request(-1, 100)
self.pack_start(self.scrolled, True, True, 5)
def log(self, action):
self.buffer.insert_at_cursor('Performed: %s\n' % action.text)
adj = self.scrolled.get_vadjustment()
adj.set_value(adj.get_upper() - adj.get_page_size())
if __name__ == '__main__':
window = Gtk.Window()
window.connect("destroy", Gtk.main_quit)
logger = GtkLogger()
window.add(logger)
window.show_all()
logger.log(actions.Action('coucou'))
Gtk.main()
...@@ -5,8 +5,10 @@ import os ...@@ -5,8 +5,10 @@ import os
import glob, re import glob, re
# set to location of libgstkaldionline2.so # set to location of libgstkaldionline2.so
os.environ['GST_PLUGIN_PATH'] = './asr/' directory = os.path.dirname(__file__) or '.'
os.environ['GST_PLUGIN_PATH'] = directory + '/asr/'
os.environ['GTK_THEME'] = 'light' os.environ['GTK_THEME'] = 'light'
print 'gst plugin path =', os.environ['GST_PLUGIN_PATH']
# import gtk stuff # import gtk stuff
from threading import Thread from threading import Thread
...@@ -22,21 +24,23 @@ import signal ...@@ -22,21 +24,23 @@ import signal
signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL)
# import local stuff # import local stuff
import confirm, asr, action, xmlview_widgets import confirm, asr, actions, xmlview_widgets
import levenstein, SLU import sections
import levenstein, SLU, osc
class ScriptedASR(Gtk.Window): class ScriptedASR(Gtk.Window):
def __init__(self, xml_filename, asr_config_file): def __init__(self, xml_filename, asr_config_file, osc_host, osc_port):
super(ScriptedASR, self).__init__() super(ScriptedASR, self).__init__()
osc.setup(osc_host, osc_port)
self.connect("destroy", self.quit) self.connect("destroy", self.quit)
self.set_default_size(800,600) self.set_default_size(1024, 768)
self.set_border_width(10) self.set_border_width(10)
self.set_title('ScriptedASR [%s]' % xml_filename) self.set_title('ASR Transcript [xml=%s asr=%s osc=%s:%s]' % (xml_filename, asr_config_file, osc_host, osc_port))
vbox = Gtk.VBox() vbox = Gtk.VBox()
import section self.sections = sections.SectionManager()
self.sections = section.SectionManager()
vbox.pack_start(self.sections, False, True, 5) vbox.pack_start(self.sections, False, True, 5)
self.xmlview = xmlview_widgets.XmlView(xml_filename) self.xmlview = xmlview_widgets.XmlView(xml_filename)
...@@ -140,8 +144,8 @@ if __name__ == '__main__': ...@@ -140,8 +144,8 @@ if __name__ == '__main__':
xml_filename = sys.argv[1] xml_filename = sys.argv[1]
if len(sys.argv) > 2: if len(sys.argv) > 2:
asr_config_file = sys.argv[2] asr_config_file = sys.argv[2]
xml_filename, asr_config_file = selector.ModelSelector(xml_filename, asr_config_file).run() xml_filename, asr_config_file, osc_host, osc_port = selector.ModelSelector(xml_filename, asr_config_file).run()
if xml_filename == None or asr_config_file == None: if xml_filename == None or asr_config_file == None or osc_host == None or osc_port == None:
sys.exit(0) sys.exit(0)
app = ScriptedASR(xml_filename, asr_config_file) app = ScriptedASR(xml_filename, asr_config_file, osc_host, osc_port)
Gtk.main() Gtk.main()
import liblo, sys import liblo, sys
class ActionSender: class Client:
def __init__(self, host = '127.0.0.1', port = 1234): def __init__(self, host = '127.0.0.1', port = 1234):
try: try:
self.target = liblo.Address(host, port) self.target = liblo.Address(host, port)
...@@ -13,24 +13,35 @@ class ActionSender: ...@@ -13,24 +13,35 @@ class ActionSender:
except: except:
print >>sys.stderr, 'OSC: failed to send message [%s]' % str(message) print >>sys.stderr, 'OSC: failed to send message [%s]' % str(message)
class ActionReceiver: def send_action(self, action):
self.send('ACTION: %s' % action.text)
def send_words(self, start, words):
self.send('WORDS(%d): %s' % (start, words))
class Server:
def __init__(self, host = '127.0.0.1', port = 1234): def __init__(self, host = '127.0.0.1', port = 1234):
print 'OSC: Creating server at %s:%d' % (host, port) print 'OSC: Creating server at %s:%d' % (host, port)
self.server = liblo.Server(port) self.server = liblo.Server(port)
self.server.add_method(None, None, self.callback) self.server.add_method(None, None, self.callback)
def callback(self, message): def callback(self, message):
print 'OSC: Recieved [%s]' % message print 'OSC: Received [%s]' % message
def run(self): def run(self):
print 'OSC: Waiting for messages' print 'OSC: Waiting for messages'
while True: while True:
self.server.recv(100) self.server.recv(100)
client = None
def setup(host, port):
global client
client = Client(host, port)
if __name__ == '__main__': if __name__ == '__main__':
if len(sys.argv) > 1: if len(sys.argv) > 1:
client = ActionSender() client = Client()
client.send(' '.join(sys.argv[1:])) client.send(' '.join(sys.argv[1:]))
else: else:
server = ActionReceiver() server = Server()
server.run() server.run()
File moved
from gi.repository import GObject, Gtk, Gdk from gi.repository import GObject, Gtk, Gdk
import os, sys, glob import os, sys, glob, re
import config import config
class ModelSelector(Gtk.Dialog): class ModelSelector(Gtk.Dialog):
def __init__(self, xml_filename = '', asr_model = ''): def __init__(self, xml_filename = '', asr_model = ''):
super(ModelSelector, self).__init__() super(ModelSelector, self).__init__()
self.options = {'xml_filename': xml_filename, 'asr_model': asr_model, 'osc_host': '127.0.0.1', 'osc_port': '1234'}
self.load_options()
self.set_title('Configuration') self.set_title('Configuration')
self.set_border_width(10)
self.add_button("Cancel", Gtk.ResponseType.CANCEL) self.add_button("Cancel", Gtk.ResponseType.CANCEL)
self.add_button("OK", Gtk.ResponseType.OK) self.add_button("OK", Gtk.ResponseType.OK)
box = self.get_content_area() box = self.get_content_area()
xml_box = Gtk.HBox() xml_box = Gtk.HBox()
xml_box.pack_start(Gtk.Label('XML file:'), False, False, 10) xml_box.pack_start(Gtk.Label('XML file:'), False, False, 5)
xml_entry = Gtk.Entry() xml_entry = Gtk.Entry()
xml_entry.set_text(xml_filename) xml_entry.set_text(self.options['xml_filename'])
xml_entry.set_width_chars(len(xml_filename)) xml_entry.set_width_chars(len(self.options['xml_filename']))
self.xml_entry = xml_entry self.xml_entry = xml_entry
xml_box.pack_start(xml_entry, True, True, 10) xml_box.pack_start(xml_entry, True, True, 5)
xml_button = Gtk.Button("Choose...") xml_button = Gtk.Button("Choose...")
xml_button.connect('clicked', self.show_filechooser) xml_button.connect('clicked', self.show_filechooser)
xml_box.pack_start(xml_button, False, False, 10) xml_box.pack_start(xml_button, False, False, 5)
box.pack_start(xml_box, False, False, 5) box.pack_start(xml_box, False, False, 5)
model_box = Gtk.HBox() model_box = Gtk.HBox()
model_box.pack_start(Gtk.Label('ASR model:'), False, False, 10) model_box.pack_start(Gtk.Label('ASR model:'), False, False, 5)
model_chooser = Gtk.ComboBoxText() model_chooser = Gtk.ComboBoxText()
model_chooser.set_entry_text_column(0) model_chooser.set_entry_text_column(0)
target_index = 0 target_index = 0
for i, model in enumerate(self.list_models()): for i, model in enumerate(self.list_models()):
model_chooser.append_text(model) model_chooser.append_text(model)
if asr_model == self.models[i]: if self.options['asr_model'] == self.models[i]:
target_index = i target_index = i
model_chooser.set_active(target_index) model_chooser.set_active(target_index)
self.model_chooser = model_chooser self.model_chooser = model_chooser
model_box.pack_start(model_chooser, True, True, 10) model_box.pack_start(model_chooser, True, True, 5)
box.pack_start(model_box, False, False, 5) box.pack_start(model_box, False, False, 5)
osc_box = Gtk.HBox() osc_box = Gtk.HBox()
osc_box.pack_start(Gtk.Label('OSC host:'), False, False, 10) osc_box.pack_start(Gtk.Label('OSC host:'), False, False, 5)
osc_host = Gtk.Entry() osc_host = Gtk.Entry()
osc_host.set_text('127.0.0.1') osc_host.set_text(self.options['osc_host'])
osc_host.set_width_chars(len(osc_host.get_text())) osc_host.set_width_chars(len(osc_host.get_text()))
osc_box.pack_start(osc_host, True, True, 10) self.osc_host = osc_host
osc_box.pack_start(Gtk.Label('Port:'), False, False, 10) osc_box.pack_start(osc_host, True, True, 5)
osc_box.pack_start(Gtk.Label('Port:'), False, False, 5)
osc_port = Gtk.Entry() osc_port = Gtk.Entry()
osc_port.set_text('1234') osc_port.set_text(self.options['osc_port'])
osc_port.set_width_chars(len(osc_port.get_text())) osc_port.set_width_chars(len(osc_port.get_text()))
osc_box.pack_start(osc_port, True, True, 10) self.osc_port = osc_port
osc_box.pack_start(osc_port, True, True, 5)
box.pack_start(osc_box, False, False, 5) box.pack_start(osc_box, False, False, 5)
...@@ -56,16 +62,38 @@ class ModelSelector(Gtk.Dialog): ...@@ -56,16 +62,38 @@ class ModelSelector(Gtk.Dialog):
okButton = self.get_widget_for_response(response_id=Gtk.ResponseType.OK) okButton = self.get_widget_for_response(response_id=Gtk.ResponseType.OK)
okButton.set_can_default(True) okButton.set_can_default(True)
okButton.grab_default() okButton.grab_default()
okButton.grab_focus()
self.show_all() self.show_all()
def save_options(self):
try:
with open('.options.txt', 'w') as fp:
for key, value in self.options.items():
print >>fp, '%s: %s' % (key, str(value))
except:
print 'Warning: could not save options'
def load_options(self):
try:
with open('.options.txt') as fp:
for line in fp:
line = line.strip()
found = re.search('^([^:]*):(.*)', line)
if found:
key = found.group(1).strip()
value = found.group(2).strip()
self.options[key] = value
except:
print 'Warning: could not load options'
def show_filechooser(self, button): def show_filechooser(self, button):
dialog = Gtk.FileChooserDialog("Please choose a file", self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog = Gtk.FileChooserDialog("Please choose a file", self, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
filter_text = Gtk.FileFilter() filter_text = Gtk.FileFilter()
filter_text.set_name("XML files") filter_text.set_name("XML files")
filter_text.add_mime_type("text/xml") filter_text.add_mime_type("text/xml")
dialog.add_filter(filter_text) dialog.add_filter(filter_text)
dialog.set_current_folder('%s/data' % os.path.dirname(__file__)) dialog.set_current_folder('%s/data' % (os.path.dirname(__file__) or '.'))
response = dialog.run() response = dialog.run()
if response == Gtk.ResponseType.OK: if response == Gtk.ResponseType.OK:
...@@ -90,9 +118,12 @@ class ModelSelector(Gtk.Dialog): ...@@ -90,9 +118,12 @@ class ModelSelector(Gtk.Dialog):
def run(self): def run(self):
response = super(ModelSelector, self).run() response = super(ModelSelector, self).run()
if response != Gtk.ResponseType.OK: if response != Gtk.ResponseType.OK:
return None, None return None, None, None, None
asr_model = self.models[self.model_chooser.get_active()] self.options['asr_model'] = self.models[self.model_chooser.get_active()]
xml_filename = self.xml_entry.get_text() self.options['xml_filename'] = self.xml_entry.get_text()
self.options['osc_host'] = self.osc_host.get_text()
self.options['osc_port'] = self.osc_port.get_text()
self.save_options()
self.destroy() self.destroy()
return xml_filename, asr_model return self.options['xml_filename'], self.options['asr_model'], self.options['osc_host'], self.options['osc_port']
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment