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