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
17 import dbus
# Used to connect to the Apertium D-Bus service
20 from glade
import GladeXML
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.
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
42 if self
.chkMarkUnknown
.get_active():
43 return {"mark_unknown": "true"}
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.
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
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
, ())
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
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
112 result
= self
.translator
.translate(options
, _input
) # Invoke the Apertium D-Bus translation service
115 """Take the output from the translation and update the output buffer"""
116 self
.output_buffer
.set_text(result
)
119 # We probably should call gtk.gdk.thread_enter here...
120 gobject
.idle_add(update_text
) # Post the function into the GTK main loop
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
):
136 def on_wndMain_destroy(self
, widget
):
139 def on_wndMain_delete_event(self
, widget
, event
):
143 def on_btnAbout_clicked(self
, widget
):
146 def on_dlgAbout_response(self
, widget
, response
):
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
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")