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