Added code to make apertium-view use the normal gtk Textview in the absence of the...
[apertium.git] / apertium-tools / apertium-view / apertium-view.py
blob4ef39eb2cce0f7936067d7e073d40735f51b20cb
1 #!/usr/bin/env python
3 # GTK oriented packages
4 import gtk, sys
5 import gtk.glade
6 import pygtk
7 pygtk.require('2.0')
9 try:
10 import gtksourceview2 as sourceview
12 except:
13 import SourceViewDummy as sourceview
16 # Logging
17 import logging
19 # Process oriented packages
20 from subprocess import Popen, PIPE
21 import threading
22 from Queue import Queue
24 # XML packages
25 import xml
26 try:
27 import xml.etree.ElementTree as ET # Python 2.5
28 except:
29 import elementtree.ElementTree as ET
31 # Our config file
32 import config
34 # Custom widget with horizontal pane allowing for the easy
35 # vertical resizing of boxes
36 from VSizerPane import VSizerPane
38 # Global variables
39 class Globals:
40 lang_code = ''
41 mode = ''
42 marcar = 0
43 stages = None # Linked list of stages
44 pipeline_executor = None
45 source_lang_manager = None
46 source_style_manager = None
49 def text_window(title, text_buffer):
50 wTree = gtk.glade.XML("TextWindow.glade")
52 wnd = wTree.get_widget("text_window")
53 wnd.set_title(title)
55 def close(widget, data = None):
56 wnd.hide()
57 wnd.destroy()
59 wnd.connect("destroy", close)
61 text_view = wTree.get_widget("text_view")
62 text_view.set_buffer(text_buffer)
64 wTree.signal_autoconnect({'on_btn_close_clicked': close})
66 wnd.show()
69 class View(gtk.HBox):
71 class Expander(gtk.Expander):
72 """
73 A GTK expander containing a scrollable text window and a
74 VSizerPane at the bottom
75 """
76 window = None # if the text box is detached into a window
78 def __init__(self, label, text_buffer):
79 gtk.Expander.__init__(self, label)
81 text_view = sourceview.View(text_buffer)
82 text_view.set_editable(True)
83 text_view.set_wrap_mode(gtk.WRAP_WORD)
84 text_view.show()
86 scrolled_window = gtk.ScrolledWindow()
87 scrolled_window.show()
88 scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
89 scrolled_window.add_with_viewport(text_view)
90 scrolled_window.set_size_request(-1, 100)
92 sizer_pane = VSizerPane('handle.xpm', scrolled_window)
93 sizer_pane.show()
95 vbox = gtk.VBox(homogeneous = False)
96 vbox.show()
97 vbox.pack_start(scrolled_window, expand = True, fill = True)
98 vbox.pack_start(sizer_pane, expand = False, fill = True)
100 self.add(vbox)
101 self.set_expanded(True)
103 def __init__(self, label, text_buffer):
104 gtk.HBox.__init__(self)
106 self.expander = View.Expander(label, text_buffer)
107 self.expander.show()
109 popout_button = gtk.Button("p")
110 popout_button.connect("clicked", self.open_text_window, label, text_buffer)
111 popout_button.show()
113 vbox = gtk.VBox(homogeneous = False)
114 vbox.show()
116 vbox.pack_start(popout_button, expand = False, fill = True)
118 self.pack_start(self.expander, expand = True, fill = True)
119 self.pack_start(vbox, expand = False, fill = True)
121 def open_text_window(self, widget, label, text_buffer):
122 self.set_expanded(False)
123 text_window(label, text_buffer)
125 def set_expanded(self, val):
126 return self.expander.set_expanded(val)
129 class PipelineExecutor(threading.Thread):
130 def __init__(self):
131 threading.Thread.__init__(self)
132 self.queue = Queue()
133 self.setDaemon(True)
135 def run(self):
136 while True:
137 stage = self.queue.get()
139 while self.queue.qsize() > 0:
140 stage = self.queue.get()
142 stage.run()
144 def add(self, stage):
145 self.queue.put(stage)
148 class Stage:
149 def __init__(self, command_line = None):
150 logging.debug('creating stage with command_line %s' % str(command_line))
151 self.command_line = command_line
152 self.next = None
153 self.preproc_cmdline = None
154 self.postproc_cmdline = None
156 self.text_buffer = sourceview.Buffer()
158 #self.text_buffer.set_language(Globals.source_lang_manager.get_lang)
159 self.text_buffer.set_language(Globals.source_lang_manager.get_language('apertium'))
160 self.text_buffer.set_style_scheme(Globals.source_style_manager.get_scheme('tango'))
161 self.text_buffer.set_highlight_syntax(True)
162 self.text_buffer.set_highlight_matching_brackets(False)
164 self.update_handler = self.text_buffer.connect("changed", self.update)
166 def run(self):
167 if self.next == None:
168 return
170 # set the marking (on or off)
171 if len(self.next.command_line) > 1 and self.next.command_line[1] == '$1' and Globals.marcar == 1:
172 cmdline = self.next.command_line;
173 cmdline = (cmdline[0], '-g', cmdline[2]);
174 elif len(self.next.command_line) > 1 and self.next.command_line[1] == '$1' and Globals.marcar == 0:
175 cmdline = self.next.command_line;
176 cmdline = (cmdline[0], '-n', cmdline[2]);
177 else:
178 cmdline = self.next.command_line;
180 try:
181 def call(cmdline, buffer):
182 proc = Popen(cmdline, stdin = PIPE, stdout = PIPE)
183 return proc.communicate(buffer)
185 buffer_text = self.text_buffer.get_text(self.text_buffer.get_start_iter(),
186 self.text_buffer.get_end_iter())
188 for _cmd in [self.preproc_cmdline, cmdline, self.postproc_cmdline]:
189 if _cmd != None:
190 buffer_text, err = call(_cmd, buffer_text)
192 gtk.gdk.threads_enter()
193 self.next.text_buffer.handler_block(self.next.update_handler)
194 self.next.text_buffer.set_text(buffer_text.strip())
195 self.next.text_buffer.handler_unblock(self.next.update_handler)
196 gtk.gdk.threads_leave()
198 self.next.run()
200 except Exception, e:
201 logging.error("Cripes! %s" % str(e))
203 def update(self, widget, *args):
204 Globals.pipeline_executor.add(self)
206 def __iter__(self):
207 def itr(stage):
208 while stage != None:
209 yield stage
210 stage = stage.next
212 return itr(self)
215 def menu_item(label, item = None):
216 menu_item = gtk.MenuItem(label)
217 menu_item.show()
219 if type(item) == type(lambda: 2):
220 menu_item.connect("activate", item)
222 elif type(item) == gtk.Menu:
223 menu_item.set_submenu(item)
225 return menu_item
227 def menu(*items):
228 menu = gtk.Menu()
230 for item in items:
231 menu.append(item)
233 return menu
235 def menubar():
236 bar_items = (menu_item("File", menu(menu_item("Open", menu_file_open),
237 menu_item("Exit", quit))),
238 menu_item("Compile"))
240 menu_bar = gtk.MenuBar()
241 for item in bar_items:
242 menu_bar.append(item)
243 menu_bar.show()
245 return menu_bar
249 def load_mode_file(filename):
250 pass
252 def menu_file_open(widget, data = None):
253 if "open_dialog" not in self.__dict__:
254 self.open_dialog = gtk.FileSelection("Select the modes file")
255 self.open_dialog.ok_button.connect("clicked",
256 load_mode_file(self.open_dialog.get_filename()))
257 self.open_dialog.ok_button.connect("clicked",
258 lambda w: self.open_dialog.hide())
259 self.open_dialog.cancel_button.connect("clicked",
260 lambda w: self.open_dialog.hide())
262 self.open_dialog.show()
266 def quit(widget, data = None):
267 gtk.main_quit()
269 def delete_event(widget, event, data = None):
270 return False
273 # There is probably a much nicer way of doing this.
275 def checkbox_event(widget, *args):
276 if Globals.marcar == 1:
277 Globals.marcar = 0
278 else:
279 Globals.marcar = 1
281 Globals.stages.update(widget);
283 def main_window():
284 def make_stage_name(command_line):
285 if command_line != None:
286 s = " ".join(command_line)
287 else:
288 s = "N/A"
290 if len(s) > 64:
291 return s[0:64] + "..."
292 else:
293 return s
295 def make_handle_box(stages):
296 view_box = gtk.VBox(homogeneous = False)
298 view_list = []
300 for stage in stages:
301 view = View(make_stage_name(stage.command_line), stage.text_buffer)
302 view_list.append(view)
303 view.show()
304 view_box.pack_start(view, expand = False, fill = True)
306 for view in view_list[1:-1]:
307 view.set_expanded(False)
309 return view_box
311 view_box = make_handle_box(Globals.stages)
312 view_box.show()
314 vbox = gtk.VBox(homogeneous = False, spacing = 5)
315 vbox_options = gtk.VBox(homogeneous = False, spacing = 5)
316 vbox.show()
318 vbox.pack_start(menubar(), expand = False)
319 # There is probably a much nicer way of doing this.
320 vbox.pack_start(vbox_options, expand = False)
322 marcar_box = gtk.CheckButton('Mark unknown words')
323 vbox_options.show()
324 marcar_box.show()
325 marcar_box.connect("clicked", checkbox_event);
326 vbox_options.pack_end(marcar_box, expand = False, fill = True)
328 vbox.pack_start(view_box, expand = True)
331 scrolled_window = gtk.ScrolledWindow()
332 scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
333 scrolled_window.show()
334 scrolled_window.add_with_viewport(vbox)
336 window = gtk.Window(gtk.WINDOW_TOPLEVEL)
337 window.add(scrolled_window)
339 window.connect("delete_event", delete_event)
340 window.connect("destroy", quit)
342 window.resize(400, 500)
344 window.show()
348 def apertium_program(program):
349 # TODO: Make this search for the Apertium path
350 return "%s/%s" % (config.apertium_bin_path, program)
352 def apertium_dictionary(dictionary):
353 # TODO: Make this search for the Apertium path
354 return "%s/apertium-%s/%s" % (config.apertium_dict_path, Globals.lang_code, dictionary)
356 def setup_mode(mode):
357 stages = Stage()
359 def add(stages):
360 for program in mode.findall('.//program'):
361 command = apertium_program(program.attrib['name']).split(' ')
363 for param in program.findall('.//file'):
364 command.append(apertium_dictionary(param.attrib['name']))
366 stages.next = Stage(tuple(command))
367 stages = stages.next
369 add(stages)
371 start_stage = stages
372 last_stage = [stage for stage in stages if stage.next != None and stage.next.next == None][0]
374 start_stage.preproc_cmdline = (apertium_program('apertium-destxt'),)
375 last_stage.postproc_cmdline = (apertium_program('apertium-retxt'),)
377 return stages
381 def setup_logging():
382 logging.basicConfig(level=logging.DEBUG,
383 format='%(asctime)s %(levelname)-8s %(message)s',
384 datefmt='%a, %d %b %Y %H:%M:%S',
385 filename='apertium.log',
386 filemode='w')
390 def print_usage():
391 print "usage: apertium-view [<modes file>] [<language code>]"
393 def process_command_line():
394 import sys
396 logging.debug('Command line is %s' % sys.argv)
397 Globals.lang_code = sys.argv[1]
399 def process_modes(mode_file, mode_code = None):
400 logging.debug("Using mode file %s" % mode_file)
401 tree = ET.parse(mode_file)
403 def find_mode():
404 if mode_code != None:
405 logging.debug('Looking for mode code %s' % mode_code)
406 for mode in tree.findall('mode'):
407 if mode.attrib['name'] == mode_code:
408 return mode
410 print_usage()
412 else:
413 logging.debug('No mode code specified, using first one in file')
414 return tree.find('mode')
416 return find_mode()
418 Globals.mode = process_modes(*sys.argv[2:4])
419 logging.debug('Parsed command line')
423 def init():
424 setup_logging()
425 process_command_line()
426 Globals.stages = setup_mode(Globals.mode)
427 Globals.pipeline_executor = PipelineExecutor()
428 Globals.pipeline_executor.start()
429 main_window()
431 if __name__ == "__main__":
432 if len(sys.argv) < 3: #{
433 print 'Usage: apertium-view.py <pair name> <modes file> [direction]';
434 sys.exit(-1);
437 Globals.source_lang_manager = sourceview.language_manager_get_default()
438 Globals.source_style_manager = sourceview.style_scheme_manager_get_default()
440 gtk.gdk.threads_init()
441 init()
442 logging.debug('Completed init phase')
443 gtk.main()
444 logging.debug('Graceful shutdown')