Commit | Line | Data |
---|---|---|
4c412ef3 | 1 | #!/usr/bin/env python |
e603e417 P |
2 | # -*- coding: utf-8 -*- |
3 | """ | |
4 | PygTranslator - pas de doc cette fois-ci... :-P | |
5 | ||
6 | Copyright : Agence universitaire de la Francophonie | |
7 | Licence : GNU General Public Licence, version 2 | |
8 | Auteur : Jean Christophe André | |
9 | Date de création : 28 septembre 2009 | |
10 | """ | |
f682a3c3 | 11 | import sys |
ed507d05 | 12 | from getpass import getpass |
e603e417 P |
13 | from threading import Thread |
14 | from Queue import Queue | |
f682a3c3 P |
15 | try: |
16 | import xmpp | |
17 | import pygtk | |
18 | pygtk.require('2.0') | |
19 | import gtk | |
20 | import gobject | |
21 | except ImportError: | |
4c412ef3 | 22 | modules = ['python-xmpp','python-gtk2','python-gobject'] |
f682a3c3 | 23 | print u"Ce programme requiert l'installation des paquets Debian suivants :" |
4c412ef3 P |
24 | print u" " + u" ".join(modules) |
25 | print u"Essayez ceci : sudo apt-get install " + u" ".join(modules) | |
f682a3c3 P |
26 | sys.exit(-1) |
27 | ||
28 | debug = False | |
e603e417 | 29 | def DEBUG(message): |
f682a3c3 P |
30 | if debug: |
31 | print "\x1b[31;1mDEBUG\x1b[m:", message | |
e603e417 P |
32 | return |
33 | ||
34 | class XmppTranslator(Thread): | |
35 | ||
36 | def __init__(self, jid, password): | |
37 | Thread.__init__(self) | |
38 | self.setDaemon(True) | |
69eefce0 P |
39 | self._quit = False |
40 | ||
e603e417 P |
41 | self._input_queue = Queue(0) |
42 | self._output_queue = Queue(0) | |
69eefce0 | 43 | self._last_text_to_translate = {} |
e603e417 P |
44 | |
45 | self._jid = xmpp.JID(jid) | |
46 | self._password = password | |
47 | if self._jid.getResource() == '': | |
48 | self._jid.setResource('translator') | |
49 | ||
50 | self._client = xmpp.Client(self._jid.getDomain(), debug=[]) | |
51 | ||
52 | def __del__(self): | |
53 | if self._client and not self._client.isConnected(): | |
54 | return | |
55 | self._client.sendPresence(typ='unavailable') | |
56 | self._client.disconnect() | |
57 | ||
58 | def _connect(self): | |
59 | try: | |
60 | if not self._client.connect(): | |
61 | raise IOError | |
62 | except IOError: | |
63 | raise IOError, "can't connect to '%s'" % self._jid.getDomain() | |
64 | self._client.RegisterHandler('message', self._messageHandler) | |
65 | ||
66 | auth = self._client.auth(self._jid.getNode(), self._password, | |
67 | self._jid.getResource()) | |
68 | if not auth: | |
69 | raise IOError, "unable to authenticate '%s'" % self._jid.getNode() | |
70 | self._client.sendInitPresence(requestRoster=0) | |
71 | ||
72 | def _messageHandler(self, client, message): | |
73 | if message.getType() == 'error': | |
74 | return | |
75 | translation_path = message.getFrom().getNode() | |
76 | text = message.getBody() | |
77 | self._output_queue.put( (translation_path, text) ) | |
78 | raise xmpp.NodeProcessed | |
79 | ||
80 | def _process_input_queue(self): | |
81 | if self._input_queue.empty(): | |
82 | return | |
83 | # process pending translation job | |
84 | translation_path, text = self._input_queue.get() | |
85 | jid = '%s@bot.talk.google.com' % translation_path | |
86 | message = xmpp.Message(to=jid, typ='chat', body=text) | |
87 | self._client.send(message) | |
88 | DEBUG("sent [%s] to '%s'." % (text, jid)) | |
89 | self._input_queue.task_done() | |
90 | ||
91 | def run(self): | |
92 | self._connect() | |
93 | self._client.Process(1) | |
94 | while not self._quit: | |
95 | self._process_input_queue() | |
96 | self._client.Process(1) | |
97 | ||
98 | def request_translation(self, translation_path, text): | |
69eefce0 P |
99 | text = text.strip().replace('\r\n','\n') |
100 | # skip consecutive identical requests | |
101 | if text == self._last_text_to_translate.get(translation_path): | |
102 | return | |
103 | self._last_text_to_translate[translation_path] = text | |
e603e417 P |
104 | # remove any pending translation job |
105 | while not self._input_queue.empty(): | |
106 | self._input_queue.get() | |
107 | DEBUG("removed a pending translation job") | |
108 | self._input_queue.task_done() | |
109 | # put a new translation job | |
110 | self._input_queue.put( (translation_path, text) ) | |
111 | ||
112 | def translation_result(self): | |
113 | if self._output_queue.empty(): | |
114 | return None | |
115 | translation_path, text = self._output_queue.get() | |
116 | return translation_path, text | |
117 | ||
118 | def translation_done(self): | |
119 | self._output_queue.task_done() | |
120 | ||
121 | class GtkTranslator(object): | |
122 | ||
123 | def __init__(self, translator): | |
124 | self._translator = translator | |
125 | ||
126 | self._textbuf = {} | |
127 | ||
128 | self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) | |
129 | self.window.set_title(u"PygTranslator") | |
130 | self.window.set_border_width(0) | |
131 | self.window.connect("destroy", lambda w: gtk.main_quit()) | |
69eefce0 | 132 | self.window.connect("delete_event", self._delete_event) |
e603e417 P |
133 | |
134 | vbox = gtk.VBox(True, 0) | |
135 | vbox.show() | |
136 | ||
137 | textview = gtk.TextView() | |
138 | textview.set_size_request(400, 100) | |
139 | textview.set_wrap_mode(gtk.WRAP_WORD) | |
140 | textview.show() | |
141 | scroll = gtk.ScrolledWindow() | |
142 | scroll.add(textview) | |
143 | scroll.show() | |
144 | frame = gtk.Frame(u"Texte en français") | |
69eefce0 P |
145 | frame.connect("key_release_event", self._text_change, 'fr') |
146 | frame.connect("button_release_event", self._text_change, 'fr') | |
e603e417 P |
147 | frame.add(scroll) |
148 | frame.show() | |
149 | vbox.pack_start(frame, True, True, 0) | |
150 | self._textbuf['fr'] = textview.get_buffer() | |
151 | ||
152 | textview = gtk.TextView() | |
153 | textview.set_size_request(400, 100) | |
154 | textview.set_wrap_mode(gtk.WRAP_WORD) | |
155 | textview.show() | |
156 | scroll = gtk.ScrolledWindow() | |
157 | scroll.add(textview) | |
158 | scroll.show() | |
159 | frame = gtk.Frame(u"Text in English") | |
69eefce0 P |
160 | frame.connect("key_release_event", self._text_change, 'en') |
161 | frame.connect("button_release_event", self._text_change, 'en') | |
e603e417 P |
162 | frame.add(scroll) |
163 | frame.show() | |
164 | vbox.pack_start(frame, True, True, 0) | |
165 | self._textbuf['en'] = textview.get_buffer() | |
166 | ||
167 | self.window.add(vbox) | |
168 | self.window.show() | |
169 | ||
69eefce0 | 170 | def _delete_event(self, widget, event, data=None): |
e603e417 P |
171 | DEBUG("caught delete event") |
172 | gtk.main_quit() | |
173 | return False | |
174 | ||
69eefce0 | 175 | def _text_change(self, widget, event, lang): |
e603e417 P |
176 | start, end = self._textbuf[lang].get_bounds() |
177 | text = self._textbuf[lang].get_text(start, end) | |
178 | other_lang = (set(self._textbuf.keys()) - set((lang,))).pop() | |
179 | translation_path = lang + '2' + other_lang | |
180 | self._translator.request_translation(translation_path, text) | |
181 | ||
69eefce0 | 182 | def _process_translation_result(self): |
e603e417 P |
183 | result = {} |
184 | # FIXME: to transform into an iterator... | |
185 | while True: | |
186 | r = self._translator.translation_result() | |
187 | if r is None: | |
188 | break | |
189 | translation_path, text = r | |
190 | lang = translation_path[3:] | |
191 | if lang in self._textbuf: | |
192 | result[lang] = text | |
193 | else: | |
194 | DEBUG("unknown destination language '%s'" % lang) | |
195 | self._translator.translation_done() | |
196 | for lang, text in result.items(): | |
197 | self._textbuf[lang].set_text(text) | |
69eefce0 P |
198 | gobject.timeout_add(300, self._process_translation_result) |
199 | ||
200 | def run(self): | |
201 | DEBUG("running graphic interface") | |
202 | gobject.timeout_add(300, self._process_translation_result) | |
203 | gtk.gdk.threads_init() | |
204 | gtk.main() | |
e603e417 P |
205 | |
206 | if __name__ == "__main__": | |
f682a3c3 P |
207 | if len(sys.argv) > 1 and sys.argv[1] == '-d': |
208 | debug = True | |
209 | del(sys.argv[1]) | |
ed507d05 P |
210 | if len(sys.argv) > 1: |
211 | jid = sys.argv[1] | |
212 | del(sys.argv[1]) | |
213 | if len(sys.argv) > 1: | |
214 | password = sys.argv[1] | |
215 | del(sys.argv[1]) | |
216 | else: | |
217 | password = getpass() | |
218 | if len(sys.argv) != 1: | |
219 | print "Usage: %s [-d] <jid> [<password>]" % sys.argv[0] | |
e603e417 P |
220 | sys.exit(1) |
221 | ||
ed507d05 | 222 | translator = XmppTranslator(jid, password) |
e603e417 P |
223 | translator.start() |
224 | ||
225 | pygtranslator = GtkTranslator(translator) | |
226 | try: | |
227 | pygtranslator.run() | |
228 | except KeyboardInterrupt: | |
229 | pass | |
230 | ||
231 | translator._quit = True | |
232 | translator.join() | |
233 | ||
234 | sys.exit(0) | |
235 |