Ajout d'un exercice de programmation Python+Gtk+Thread+Xmpp.
[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
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