Quelques correctifs au passage en Django 1.1…
[progfou.git] / pygtranslator / pygtranslator.py
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 import sys
12 from threading import Thread
13 from Queue import Queue
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
27 def DEBUG(message):
28 if debug:
29 print "\x1b[31;1mDEBUG\x1b[m:", message
30 return
31
32 class XmppTranslator(Thread):
33
34 def __init__(self, jid, password):
35 Thread.__init__(self)
36 self.setDaemon(True)
37 self._quit = False
38
39 self._input_queue = Queue(0)
40 self._output_queue = Queue(0)
41 self._last_text_to_translate = {}
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):
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
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())
130 self.window.connect("delete_event", self._delete_event)
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")
143 frame.connect("key_release_event", self._text_change, 'fr')
144 frame.connect("button_release_event", self._text_change, 'fr')
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")
158 frame.connect("key_release_event", self._text_change, 'en')
159 frame.connect("button_release_event", self._text_change, 'en')
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
168 def _delete_event(self, widget, event, data=None):
169 DEBUG("caught delete event")
170 gtk.main_quit()
171 return False
172
173 def _text_change(self, widget, event, lang):
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
180 def _process_translation_result(self):
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)
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()
203
204 if __name__ == "__main__":
205 if len(sys.argv) > 1 and sys.argv[1] == '-d':
206 debug = True
207 del(sys.argv[1])
208 if len(sys.argv) != 3:
209 print "Usage: %s [-d] <jid> <password>" % sys.argv[0]
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