Adding a bit
[apertium.git] / apertium-tools / apertium-tolk / apertium-tolk.py
blob508566c7144ffc1d5a0dcf3a1fe7f5822d95821d
1 #!/usr/bin/python
3 import os
4 import new
5 import sys
6 from Queue import Queue # A queue type to communicate between threads
7 import thread # A simple thread launcher
8 from ConfigParser import ConfigParser
9 # The ConfigParser is used to store preferences of the program
11 # GTK related imports
12 import gobject
13 import gtk
14 import gtk.gdk
15 import pygtk
17 import dbus # Used to connect to the Apertium D-Bus service
19 from i18n import _
20 from glade import GladeXML
23 class MainWindow:
24 def setup_combo(self, modes, combo):
25 """Set the combo box up to display simple a list of text and
26 add everything in 'modes' to the list.
27 """
28 combo.set_model(gtk.ListStore(gobject.TYPE_STRING))
29 cell = gtk.CellRendererText() # Tell the combobox we're just rendering text
30 combo.pack_start(cell, True) # Add the text renderer to the combo box
31 combo.add_attribute(cell, "text", 0) # Tell the combobox that we just want to show text
33 for mode in modes: # Add everything in 'modes' to the combobox
34 combo.append_text(mode)
36 combo.set_active(0) # Set the combobox to the first entry
38 return combo
41 def options(self):
42 if self.chkMarkUnknown.get_active():
43 return {"mark_unknown": "true"}
44 else:
45 return {}
48 def load_config(self):
49 """Read the configuration data. First read default data from 'defaults.cfg'
50 and then user config data from '~/.apertium-simple-viewer.cfg'. Tell the
51 glade object to update stored widget properties based on the config data.
52 """
53 self.config.readfp(open('defaults.cfg'))
54 self.config.read([os.path.expanduser('~/.apertium-simple-viewer.cfg')])
55 self.glade.load_gtk_state(self.config)
57 def save_config(self):
58 """Store the configuration data to the user file '~/.apertium-simple-viewer.cfg'.
59 Before storing, tell the glade object to dump the widget state to the configuration
60 object self.config.
61 """
62 self.glade.dump_gtk_state(self.config)
63 self.config.write(open(os.path.expanduser('~/.apertium-simple-viewer.cfg'), 'w'))
65 def __init__(self, path):
66 self.config = ConfigParser() # An object which loads/stores preferences
67 self.bus = dbus.SessionBus()
68 # Create a proxy object to the D-Bus object org.apertium.info/ using the interface org.apertium.Info
69 self.info = dbus.Interface(self.bus.get_object("org.apertium.info", "/"), "org.apertium.Info")
70 self.input_queue = Queue() # Thread communication queue
71 self.glade = GladeXML(path) # Instantiate our custom Glade class which extends the gtk.glade.GladeXML class
72 self.glade.connect(self); # Try to connect the signals defined in the glade file to methods defined in self
74 # Get handles objects of interest in the glade file
75 self.buffer = self.glade.get_widget("txtInput").get_buffer()
76 self.output_buffer = self.glade.get_widget("txtOutput").get_buffer()
77 self.wndMain = self.glade.get_widget("wndMain")
78 self.dlgAbout = self.glade.get_widget("dlgAbout")
79 self.chkMarkUnknown = self.glade.get_widget("chkMarkUnknown")
80 self.comboPair = self.setup_combo(self.info.modes(), self.glade.get_widget("comboPair"))
82 self.buffer.connect("changed", self.on_buffer_changed)
84 # Start the thread which will handle
85 thread.start_new_thread(self.translator_loop, ())
87 self.load_config()
88 self.wndMain.show()
91 def translator_loop(self):
92 """This runs as a thread which invokes Apertium D-Bus methods.
94 We run this in a thread, since it takes a perceptible amount
95 of time to execute an Apertium call.
97 Only one Apertium call at most is ever active. We ensure this by
98 running only one thread. This thread receives updates via a queue.
100 While it is busy waiting for an Apertium call to return, its
101 update queue might receive requests (this happens when the user is
102 typing while Apertium is running). We're only ever interested in the
103 latest item added to the queue, so we discard everything up to that
104 point.
106 while True:
107 options, _input = self.input_queue.get() # Block waiting for something to translate
108 while not self.input_queue.empty(): # We're only interested in the latest item in the queue...
109 options, _input = self.input_queue.get() # ...so we discard items until we have the latest item
111 try:
112 result = self.translator.translate(options, _input) # Invoke the Apertium D-Bus translation service
114 def update_text():
115 """Take the output from the translation and update the output buffer"""
116 self.output_buffer.set_text(result)
117 return False
119 # We probably should call gtk.gdk.thread_enter here...
120 gobject.idle_add(update_text) # Post the function into the GTK main loop
122 except Exception, e:
123 print e # We should probably do exit(1) here
126 def on_buffer_changed(self, w):
127 self.input_queue.put([self.options(), w.get_text(w.get_start_iter(), w.get_end_iter())])
129 ###########################################################
130 # Implementations of GTK signals defined in the Glade file
132 def on_btnQuit_clicked(self, widget):
133 self.save_config()
134 gtk.main_quit()
136 def on_wndMain_destroy(self, widget):
137 gtk.main_quit()
139 def on_wndMain_delete_event(self, widget, event):
140 self.save_config()
141 return False
143 def on_btnAbout_clicked(self, widget):
144 self.dlgAbout.show()
146 def on_dlgAbout_response(self, widget, response):
147 self.dlgAbout.hide()
149 def update_translation_object(self, pair_name):
150 """Given a language pair name like ab-cd, change - to _ so that
151 one gets ab_cd. We must do this, because a D-Bus object name cannot
152 contain dashes.
154 Now, create a proxy object to the Apertium D-Bus translator object for
155 the language pair and assign the object to self.translator."""
156 dbus_pair_name = "/" + "_".join(pair_name.split("-"))
157 self.translator = dbus.Interface(self.bus.get_object("org.apertium.mode", dbus_pair_name), "org.apertium.Mode")
159 def on_comboPair_changed(self, widget):
160 pair_name = widget.get_model().get_value(widget.get_active_iter(), 0)
161 self.update_translation_object(pair_name)
162 self.buffer.emit("changed")
164 def on_chkMarkUnknown_toggled(self, widget):
165 self.buffer.emit("changed")
168 if __name__ == "__main__":
169 gtk.gdk.threads_init()
170 wnd = MainWindow("tolk.glade")
171 gtk.main();