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