output.py

Go to the documentation of this file.
00001 # Copyright 2005, 2006  Timo Savola
00002 #
00003 # This program is free software; you can redistribute it and/or modify
00004 # it under the terms of the GNU Lesser General Public License as published
00005 # by the Free Software Foundation; either version 2.1 of the License, or
00006 # (at your option) any later version.
00007 
00008 import os
00009 import re
00010 import gobject
00011 import gtk
00012 import gtk.glade
00013 from gettext import gettext
00014 
00015 import view
00016 import extension
00017 import execution
00018 import config
00019 import environment
00020 import settings
00021 
00022 datadir = None
00023 views = []
00024 
00025 def init_extension(d):
00026         global datadir
00027         datadir = d
00028 
00029 def get_views():
00030         global views
00031         return [i for i in views if not i.is_busy()]
00032 
00033 ##
00034 ## Output
00035 
00036 class Output (environment.OutputExtension):
00037         def __init__(self):
00038                 environment.OutputExtension.__init__(self)
00039 
00040         def get_view(self):
00041                 views = get_views()
00042                 if views:
00043                         return views[0]
00044                 else:
00045                         view = View()
00046                         environment.add_view(view, 'bottom')
00047                         return view
00048 
00049         def set_batch(self, batch):
00050                 view = self.get_view()
00051                 view.set_batch(batch)
00052                 environment.show_view(view, focus=False)
00053 
00054 gobject.type_register(Output)
00055 
00056 ##
00057 ## Launcher
00058 
00059 class Launcher (environment.LauncherExtension):
00060         def __init__(self):
00061                 environment.LauncherExtension.__init__(self)
00062 
00063                 label = gettext('Output window')
00064                 self.action = gtk.Action(None, label, None, None)
00065                 self.action.set_visible(True)
00066 
00067         def get_action(self):
00068                 return self.action
00069 
00070         def create(self):
00071                 return View()
00072 
00073 gobject.type_register(Launcher)
00074 
00075 ##
00076 ## View
00077 
00078 class View (view.AbstractView):
00079         def __init__(self):
00080                 view.AbstractView.__init__(self)
00081 
00082                 path = os.path.join(datadir, 'encode', 'output-view.glade')
00083                 self.glade = gtk.glade.XML(path)
00084                 self.glade.signal_autoconnect(self)
00085 
00086                 self._set_title('idle')
00087 
00088                 buffer = self._get_buffer()
00089                 buffer.connect_after('insert-text', self.on_buffer_insert)
00090 
00091                 self.tag_common = buffer.create_tag()
00092 
00093                 self.tag_execute = buffer.create_tag()
00094                 self.tag_execute.set_property('foreground', 'blue')
00095 
00096                 self.tag_error = buffer.create_tag()
00097                 self.tag_error.set_property('foreground', 'red3')
00098 
00099                 self.tag_success = buffer.create_tag()
00100                 self.tag_success.set_property('foreground', 'green4')
00101 
00102                 self.tag_failure = buffer.create_tag()
00103                 self.tag_failure.set_property('foreground', 'red')
00104 
00105                 self.conf = config.Directory('encode/output')
00106                 self.conf.add_string('font', lambda font: self.tag_common.set_property('font', font))
00107 
00108                 self.formats = []
00109                 self.conf.add_strings('formats', self.set_formats)
00110 
00111                 self.batch = None
00112                 self.handlers = None
00113                 self.jobs_by_line = []
00114 
00115         def set_formats(self, strings):
00116                 self.formats = [re.compile(s) for s in strings]
00117 
00118         def _get_job(self, line):
00119                 for start_line, job in self.jobs_by_line:
00120                         if start_line <= line:
00121                                 return job
00122                 return None
00123 
00124         def _get_buffer(self):
00125                 textview = self.glade.get_widget('textview')
00126                 return textview.get_buffer()
00127 
00128         def _set_title(self, name):
00129                 data_label = self.glade.get_widget('title_%s' % name)
00130                 text = data_label.get_label()
00131 
00132                 display_label = self.glade.get_widget('title')
00133                 display_label.set_label(text)
00134 
00135         def _ensure_newline(self):
00136                 buffer = self._get_buffer()
00137                 end = buffer.get_end_iter()
00138 
00139                 if not end.is_start():
00140                         last = end.copy()
00141                         last.backward_char()
00142 
00143                         if last.get_char() != '\n':
00144                                 buffer.insert(end, '\n')
00145 
00146         def _append(self, text, tags=[]):
00147                 buffer = self._get_buffer()
00148 
00149                 end = buffer.get_end_iter()
00150                 buffer.insert(end, text)
00151 
00152                 start = buffer.get_iter_at_mark(self.mark)
00153 
00154                 for tag in [self.tag_common] + tags:
00155                         buffer.apply_tag(tag, start, end)
00156 
00157                 buffer.move_mark(self.mark, end)
00158 
00159         def _append_message(self, tag, name, params=()):
00160                 label = self.glade.get_widget('message_%s' % name)
00161                 format = label.get_label()
00162 
00163                 text = format % params
00164 
00165                 self._ensure_newline()
00166                 self._append(text, [tag])
00167 
00168         def on_textview_popup(self, textview, menu):
00169                 item = self.glade.get_widget('stop_item')
00170                 menu.insert(item, 0)
00171                 menu.connect('destroy', lambda menu: menu.remove(item))
00172 
00173                 separator = gtk.SeparatorMenuItem()
00174                 separator.show()
00175                 menu.insert(separator, 1)
00176 
00177         def on_textview_release(self, textview, event):
00178                 if event.button == 1:
00179                         buffer = self._get_buffer()
00180                         if not buffer.get_selection_bounds():
00181                                 buf_x, buf_y = textview.window_to_buffer_coords(gtk.TEXT_WINDOW_WIDGET, int(event.x), int(event.y))
00182                                 line_iter, line_y = textview.get_line_at_y(buf_y)
00183 
00184                                 self._edit_source(line_iter)
00185 
00186         def on_scrolledwindow_parent_set(self, scrolledwindow, old_parent):
00187                 global views
00188                 if scrolledwindow.get_parent():
00189                         if not old_parent:
00190                                 views.append(self)
00191                 else:
00192                         if old_parent:
00193                                 views.remove(self)
00194 
00195         def on_scrolledwindow_destroy(self, scrolledwindow):
00196                 self.conf.remove_all()
00197                 self.conf = None
00198 
00199                 if self.batch:
00200                         self.batch.stop()
00201                         self.batch = None
00202 
00203         # Execution
00204 
00205         def set_batch(self, batch):
00206                 if self.batch:
00207                         for id in self.handlers:
00208                                 self.batch.disconnect(id)
00209 
00210                 self.batch = batch
00211                 self.handlers = (
00212                         self.batch.connect('job-started', self.on_batch_started),
00213                         self.batch.connect('finished', self.on_batch_finished)
00214                 )
00215 
00216                 self.batch.get_output().consumers.add(self._consume_output)
00217                 self.batch.get_error().consumers.add(self._consume_error)
00218 
00219                 self.jobs_by_line = []
00220 
00221                 self._set_title('busy')
00222 
00223                 buffer = self._get_buffer()
00224                 buffer.set_text('')
00225 
00226                 start = buffer.get_start_iter()
00227                 self.mark = buffer.create_mark(None, start, left_gravity=True)
00228 
00229         def is_busy(self):
00230                 if not self.get_widget():
00231                         return True
00232 
00233                 if self.batch:
00234                         return self.batch.is_alive()
00235                 else:
00236                         return False
00237 
00238         def _consume_output(self, text, tags=[]):
00239                 self._append(text, tags)
00240 
00241         def _consume_error(self, text):
00242                 self._consume_output(text, [self.tag_error])
00243 
00244         def on_batch_started(self, batch, job):
00245                 # Job
00246 
00247                 self._ensure_newline()
00248 
00249                 buffer = self._get_buffer()
00250                 iter = buffer.get_end_iter()
00251                 line = iter.get_line()
00252 
00253                 self.jobs_by_line.append((line, job))
00254 
00255                 # Message
00256 
00257                 info = ''
00258 
00259                 if isinstance(job, execution.Job):
00260                         if job.vars:
00261                                 for key in job.vars:
00262                                         info += 'Environ: %s=%s\n' % (key, job.vars[key])
00263 
00264                         if job.workdir:
00265                                 info += 'Workdir: %s\n' % job.workdir
00266 
00267                         info += 'Command:'
00268                         for arg in job.args:
00269                                 arg = arg.replace(' ', '\\ ')
00270                                 info = '%s %s' % (info, arg)
00271                 else:
00272                         info += 'Hook: %s' % job
00273 
00274                 info += '\n'
00275 
00276                 self._append_message(self.tag_execute, 'execute', (info))
00277                 self._ensure_newline()
00278 
00279                 item = self.glade.get_widget('stop_item')
00280                 item.set_sensitive(True)
00281 
00282         def on_batch_finished(self, batch, status):
00283                 self._set_title('idle')
00284 
00285                 if status == 0:
00286                         self._append_message(self.tag_success, 'success')
00287                 else:
00288                         signum = status & 0xff
00289                         retval = status >> 8
00290 
00291                         if signum:
00292                                 self._append_message(self.tag_failure, 'signal', (signum))
00293                         else:
00294                                 self._append_message(self.tag_failure, 'error', (retval))
00295 
00296                 item = self.glade.get_widget('stop_item')
00297                 item.set_sensitive(False)
00298 
00299         def on_buffer_insert(self, buffer, iter, text, length):
00300                 textview = self.glade.get_widget('textview')
00301                 textview.scroll_to_mark(self.mark, within_margin=0.0, use_align=True, xalign=0.0, yalign=0.0)
00302 
00303         def on_stop_activate(self, item):
00304                 self.batch.stop()
00305 
00306                 self._set_title('idle')
00307                 self._append_message(self.tag_failure, 'stop')
00308 
00309                 item.set_sensitive(False)
00310 
00311         # Edit source
00312 
00313         def _edit_source(self, iter):
00314                 line = iter.get_line()
00315 
00316                 job = self._get_job(line)
00317                 if job:
00318                         buffer = self._get_buffer()
00319 
00320                         end_iter = buffer.get_iter_at_line(line + 1)
00321                         if iter == end_iter:
00322                                 end_iter = buffer.get_end_iter()
00323 
00324                         text = buffer.get_text(iter, end_iter)
00325 
00326                         location = self._match(text)
00327                         if location:
00328                                 filename, line = location
00329 
00330                                 path = os.path.join(job.workdir, filename)
00331                                 environment.edit(path, job.workdir, line)
00332 
00333         def _match(self, text):
00334                 for regex in self.formats:
00335                         match = regex.match(text)
00336                         if match:
00337                                 filename, line = match.group(1, 2)
00338                                 return filename, int(line)
00339                 return None
00340 
00341         # View
00342 
00343         def get_label(self):
00344                 return self.glade.get_widget('title')
00345 
00346         def get_widget(self):
00347                 return self.glade.get_widget('scrolledwindow')
00348 
00349 gobject.type_register(View)
00350 
00351 ##
00352 ## Settings
00353 
00354 if 'set' not in __builtins__:
00355         import sets
00356         set = sets.Set
00357 
00358 class Settings (settings.SettingsExtension):
00359         def __init__(self):
00360                 settings.SettingsExtension.__init__(self)
00361 
00362                 path = os.path.join(datadir, 'encode', 'output-settings.glade')
00363                 self.glade = gtk.glade.XML(path)
00364                 self.glade.signal_autoconnect(self)
00365 
00366                 self.conf = config.Directory('encode/output')
00367 
00368                 self.dirty = set((
00369                         'font',
00370                 ))
00371                 self.revert()
00372 
00373         def get_name(self):
00374                 return gettext('Output')
00375 
00376         def get_title(self):
00377                 return gettext('Output window')
00378 
00379         def get_widget(self):
00380                 return self.glade.get_widget('table')
00381 
00382         def apply(self):
00383                 if 'font' in self.dirty:
00384                         fontbutton = self.glade.get_widget('font_fontbutton')
00385                         font = fontbutton.get_font_name()
00386 
00387                         self.conf.set_string('font', font)
00388 
00389                 self.dirty.clear()
00390 
00391         def revert(self):
00392                 if 'font' in self.dirty:
00393                         font = self.conf.get_string('font')
00394 
00395                         fontbutton = self.glade.get_widget('font_fontbutton')
00396                         fontbutton.set_font_name(font)
00397 
00398                 self.dirty.clear()
00399 
00400         def on_font_set(self, fontbutton):
00401                 self.dirty.add('font')
00402                 self.modified()
00403 
00404 gobject.type_register(Settings)

Generated on Thu Jan 18 09:47:40 2007 for Encode by  doxygen 1.4.7