def connect(self): """Connect to the selected log file tailer.""" try: cmd_tmpl = self.cmd_tmpls[self.filename] except (KeyError, TypeError): cmd_tmpl = None self.t = Tailer(self.logview, self.filename, cmd_tmpl=cmd_tmpl) self.t.start()
def connect(self): """Run the tailer command.""" cmd = self.cmd_tmpl % {'rotation': self.rotation, 'suite_name': self.suite_name, 'suite_log': self.suite_log} self.t = Tailer( self.logview, cmd, filters=[f for f in [self.task_filter, self.custom_filter] if f]) self.t.start()
def run(self): proc = Popen( self.command, stdin=open(os.devnull), stdout=self.stdoutfile, stderr=STDOUT, shell=True) self.proc = proc gobject.timeout_add(40, self.pulse_proc_progress) tail_cmd_tmpl = glbl_cfg().get_host_item("tail command template") tail_cmd = tail_cmd_tmpl % {'filename': self.stdoutfile.name} self.stdout_updater = Tailer(self.textview, tail_cmd, pollable=proc) self.stdout_updater.start()
def update_view(self): self.t.stop() logbuffer = self.logview.get_buffer() s, e = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(s, e) self.log_label.set_text(self.path()) self.t = Tailer( self.logview, self.path(), filters=[f for f in [self.task_filter, self.custom_filter] if f]) self.t.start()
def connect(self): """Connect to the selected log file tailer.""" cmd = self.cmd_tmpl % { 'subnum': self.nsubmit, 'suite_name': self.suite_name, 'task_id': self.task_id, 'job_log': self.choice } self.log_label.set_text(self.choice) self.t = Tailer(self.logview, cmd) self.t.start()
def run(self): proc = None if not self.ignore_command: proc = subprocess.Popen( self.command, stdout=self.stdout, stderr=subprocess.STDOUT, shell=True) self.proc = proc gobject.timeout_add(40, self.pulse_proc_progress) self.stdout_updater = Tailer( self.textview, self.stdout.name, pollable=proc) self.stdout_updater.start()
def update_view(self): self.t.stop() logbuffer = self.logview.get_buffer() s, e = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(s, e) self.log_label.set_text(self.path()) self.t = Tailer(self.logview, self.path(), filters=[f for f in [self.task_filter, self.custom_filter] if f]) self.t.start()
def connect(self): """Connect to the selected log file tailer.""" cmd = self.cmd_tmpl % {'subnum': self.nsubmit, 'suite_name': self.suite_name, 'task_id': self.task_id, 'job_log': self.choice} self.log_label.set_text(self.choice) self.t = Tailer(self.logview, cmd) self.t.start()
class cylc_logviewer(logviewer): def __init__(self, name, dirname, task_list): self.task_list = task_list self.main_log = name self.dirname = dirname self.level = 0 self.task_filter = None self.custom_filter = None logviewer.__init__(self, name, dirname, name) def create_gui_panel(self): logviewer.create_gui_panel(self) self.window = gtk.Window() # self.window.set_border_width(5) self.window.set_title("log viewer") self.window.set_size_request(800, 400) self.window.set_icon(get_icon()) combobox = gtk.combo_box_new_text() combobox.append_text('Task') combobox.append_text('all') for task in self.task_list: combobox.append_text(task) combobox.connect("changed", self.filter_log) combobox.set_active(0) newer = gtk.Button("_newer") newer.connect("clicked", self.rotate_log, False) self.hbox.pack_end(newer, False) older = gtk.Button("_older") older.connect("clicked", self.rotate_log, True) self.hbox.pack_end(older, False) self.hbox.pack_end(combobox, False) filterbox = gtk.HBox() entry = gtk.Entry() entry.connect("activate", self.custom_filter_log) label = gtk.Label('Filter') filterbox.pack_start(label, True) filterbox.pack_start(entry, True) self.hbox.pack_end(filterbox, False) close = gtk.Button("_Close") close.connect("clicked", self.shutdown, None, self.window) self.hbox.pack_start(close, False) self.window.add(self.vbox) self.window.connect("delete_event", self.shutdown, self.window) self.window.show_all() def shutdown(self, w, e, wind): self.quit() wind.destroy() def filter_log(self, cb): model = cb.get_model() index = cb.get_active() if index == 0: return False task = model[index][0] if task == 'all': filter = None else: filter = '\\[' + task + '%\d+\\]' self.task_filter = filter self.update_view() # TODO - CHECK ALL BOOLEAN RETURN VALUES THROUGHOUT THE GUI return False def custom_filter_log(self, e): txt = e.get_text() if txt == '': filter = None else: filter = txt self.custom_filter = filter self.update_view() return False def current_log(self): try: return get_logs(self.dirname, self.main_log, False)[self.level] except IndexError: return None def rotate_log(self, bt, go_older): if go_older: self.level += 1 else: self.level -= 1 if self.level < 0: warning_dialog(""" At newest rotation; reloading in case the suite has been restarted.""", self.window).warn() self.level = 0 # but update view in case user started suite after gui if self.current_log() not in os.listdir(self.dirname): if go_older: warning_dialog("Older log not available", self.window).warn() self.level -= 1 return else: warning_dialog("Newer log not available", self.window).warn() self.level += 1 return else: self.filename = self.current_log() self.update_view() def update_view(self): self.t.stop() logbuffer = self.logview.get_buffer() s, e = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(s, e) self.log_label.set_text(self.path()) self.t = Tailer( self.logview, self.path(), filters=[f for f in [self.task_filter, self.custom_filter] if f]) self.t.start()
class gcapture(object): """ Run a command as a subprocess and capture its stdout and stderr in real time, to display in a GUI window. Examples: $ capture "echo foo" $ capture "echo hello && sleep 5 && echo bye" Lines containing: 'CRITICAL', 'WARNING', 'ERROR' are displayed in red. $ capture "echo foo && echox bar" """ def __init__(self, command, stdoutfile, width=400, height=400, standalone=False, ignore_command=False, title=None): self.standalone = standalone self.command = command self.ignore_command = ignore_command self.stdout = stdoutfile self.stdout_updater = None self.proc = None self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_border_width(5) if title is None: self.window.set_title('Command Output') else: self.window.set_title(title) self.window.connect("delete_event", self.quit) self.window.set_default_size(width, height) self.window.set_icon(get_icon()) self.quit_already = False self.find_current = None self.find_current_iter = None self.search_warning_done = False sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) sw.show() self.textview = gtk.TextView() self.textview.set_editable(False) self.textview.set_wrap_mode(gtk.WRAP_WORD) # Use a monospace font. This is safe - by testing - setting an # illegal font description has no effect. self.textview.modify_font(pango.FontDescription("monospace")) tb = self.textview.get_buffer() self.textview.show() self.ftag = tb.create_tag(None, background="#70FFA9") vbox = gtk.VBox() vbox.show() if not self.ignore_command: self.progress_bar = gtk.ProgressBar() self.progress_bar.set_text(command) self.progress_bar.set_pulse_step(0.04) self.progress_bar.show() vbox.pack_start(self.progress_bar, expand=False) self.command_label = gtk.Label(self.command) if self.ignore_command: self.command_label.show() vbox.pack_start(self.command_label, expand=False) sw.add(self.textview) frame = gtk.Frame() frame.add(sw) frame.show() vbox.add(frame) save_button = gtk.Button("Save As") save_button.connect("clicked", self.save, self.textview) save_button.show() hbox = gtk.HBox() hbox.pack_start(save_button, False) hbox.show() output_label = gtk.Label('output : ' + stdoutfile.name) output_label.show() hbox.pack_start(output_label, expand=True) self.freeze_button = gtk.ToggleButton("_Disconnect") self.freeze_button.set_active(False) self.freeze_button.connect("toggled", self.freeze) self.freeze_button.show() searchbox = gtk.HBox() searchbox.show() entry = gtk.Entry() entry.show() entry.connect("activate", self.enter_clicked) searchbox.pack_start(entry, True) b = gtk.Button("Find Next") b.connect_object('clicked', self.on_find_clicked, entry) b.show() searchbox.pack_start(b, False) searchbox.pack_start(self.freeze_button, False) close_button = gtk.Button("_Close") close_button.connect("clicked", self.quit, None, None) close_button.show() hbox.pack_end(close_button, False) vbox.pack_start(searchbox, False) vbox.pack_start(hbox, False) self.window.add(vbox) close_button.grab_focus() self.window.show() def run(self): proc = None if not self.ignore_command: proc = Popen(self.command, stdin=open(os.devnull), stdout=self.stdout, stderr=STDOUT, shell=True) self.proc = proc gobject.timeout_add(40, self.pulse_proc_progress) self.stdout_updater = Tailer(self.textview, self.stdout.name, pollable=proc) self.stdout_updater.start() def pulse_proc_progress(self): """While the process is running, pulse the progress bar a bit.""" self.progress_bar.pulse() self.proc.poll() if self.proc.returncode is None and not self.stdout_updater.freeze: # Continue gobject.timeout_add loop. return True self.progress_bar.set_fraction(1.0) gobject.idle_add(self._handle_proc_completed) # Break gobject.timeout_add loop. return False def freeze(self, b): if b.get_active(): self.stdout_updater.freeze = True b.set_label('_Reconnect') else: self.stdout_updater.freeze = False b.set_label('_Disconnect') def save(self, w, tv): tb = tv.get_buffer() start = tb.get_start_iter() end = tb.get_end_iter() txt = tb.get_text(start, end) dialog = gtk.FileChooserDialog( title='Save As', action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)) filter_ = gtk.FileFilter() filter_.set_name("any") filter_.add_pattern("*") dialog.add_filter(filter_) response = dialog.run() if response != gtk.RESPONSE_OK: dialog.destroy() return False fname = dialog.get_filename() dialog.destroy() try: f = open(fname, 'wb') except IOError, x: warning_dialog(str(x), self.window).warn() else:
class cylc_logviewer(logviewer): def __init__(self, name, dirname, task_list): self.task_list = task_list self.main_log = name self.dirname = dirname self.level = 0 self.task_filter = None self.custom_filter = None logviewer.__init__(self, name, dirname, name) def create_gui_panel(self): logviewer.create_gui_panel(self) self.window = gtk.Window() # self.window.set_border_width(5) self.window.set_title("log viewer") self.window.set_size_request(800, 400) self.window.set_icon(get_icon()) combobox = gtk.combo_box_new_text() combobox.append_text('Task') combobox.append_text('all') for task in self.task_list: combobox.append_text(task) combobox.connect("changed", self.filter_log) combobox.set_active(0) newer = gtk.Button("_newer") newer.connect("clicked", self.rotate_log, False) self.hbox.pack_end(newer, False) older = gtk.Button("_older") older.connect("clicked", self.rotate_log, True) self.hbox.pack_end(older, False) self.hbox.pack_end(combobox, False) filterbox = gtk.HBox() entry = gtk.Entry() entry.connect("activate", self.custom_filter_log) label = gtk.Label('Filter') filterbox.pack_start(label, True) filterbox.pack_start(entry, True) self.hbox.pack_end(filterbox, False) close = gtk.Button("_Close") close.connect("clicked", self.shutdown, None, self.window) self.hbox.pack_start(close, False) self.window.add(self.vbox) self.window.connect("delete_event", self.shutdown, self.window) self.window.show_all() def shutdown(self, w, e, wind): self.quit() wind.destroy() def filter_log(self, cb): model = cb.get_model() index = cb.get_active() if index == 0: return False task = model[index][0] if task == 'all': filter_ = None else: filter_ = '\\[' + task + '%\d+\\]' self.task_filter = filter_ self.update_view() # TODO - CHECK ALL BOOLEAN RETURN VALUES THROUGHOUT THE GUI return False def custom_filter_log(self, e): txt = e.get_text() if txt == '': filter_ = None else: filter_ = txt self.custom_filter = filter_ self.update_view() return False def current_log(self): try: return get_logs(self.dirname, self.main_log, False)[self.level] except IndexError: return None def rotate_log(self, bt, go_older): if go_older: self.level += 1 else: self.level -= 1 if self.level < 0: warning_dialog( """ At newest rotation; reloading in case the suite has been restarted.""", self.window).warn() self.level = 0 # but update view in case user started suite after gui if self.current_log() not in os.listdir(self.dirname): if go_older: warning_dialog("Older log not available", self.window).warn() self.level -= 1 return else: warning_dialog("Newer log not available", self.window).warn() self.level += 1 return else: self.filename = self.current_log() self.update_view() def update_view(self): self.t.stop() logbuffer = self.logview.get_buffer() s, e = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(s, e) self.log_label.set_text(self.path()) self.t = Tailer( self.logview, self.path(), filters=[f for f in [self.task_filter, self.custom_filter] if f]) self.t.start()
class SuiteLogViewer(logviewer): """A popup window to view suite logs. Implemented using "cylc cat-log". """ LABEL_ALL_LINES = "(all lines)" LABEL_ALL_TASKS = "(all tasks)" def __init__(self, suite_name, suite_log, remote_run_opts, task_list=None): """Initialise the suite log viewer.""" if task_list is None: self.task_list = [] else: self.task_list = task_list self.suite_name = suite_name self.suite_log = suite_log self.rotation = 0 self.cmd_tmpl = "cylc cat-log %s" % remote_run_opts + ( " -m t -r %(rotation)s -f %(suite_log)s %(suite_name)s") self.task_filter = None self.custom_filter = None logviewer.__init__(self) def create_gui_panel(self): """Create the GUI panel.""" logviewer.create_gui_panel(self) self.window = gtk.Window() # self.window.set_border_width(5) self.window.set_title("log viewer") self.window.set_size_request(800, 400) self.window.set_icon(get_icon()) combobox = gtk.combo_box_new_text() combobox.append_text(self.LABEL_ALL_LINES) combobox.append_text(self.LABEL_ALL_TASKS) for task in self.task_list: combobox.append_text(task) combobox.connect("changed", self.filter_log) combobox.set_active(0) newer = gtk.Button("_newer") newer.connect("clicked", self.rotate_log, False) self.hbox.pack_end(newer, False) older = gtk.Button("_older") older.connect("clicked", self.rotate_log, True) self.hbox.pack_end(older, False) self.hbox.pack_end(combobox, False) filterbox = gtk.HBox() entry = gtk.Entry() entry.connect("activate", self.custom_filter_log) label = gtk.Label('Filter') filterbox.pack_start(label, True) filterbox.pack_start(entry, True) self.hbox.pack_end(filterbox, False) close = gtk.Button("_Close") close.connect("clicked", self.shutdown, None, self.window) self.hbox.pack_start(close, False) self.window.add(self.vbox) self.window.connect("delete_event", self.shutdown, self.window) self.window.show_all() def shutdown(self, w, e, wind): """Quite the suite log viewer.""" self.quit() wind.destroy() def filter_log(self, cb): """Filter for task names.""" model = cb.get_model() index = cb.get_active() task = model[index][0] # Good enough to match "[task.CYCLE]"? if task == self.LABEL_ALL_LINES: filter_ = None elif task == self.LABEL_ALL_TASKS: filter_ = r'\[' + TaskID.ID_RE + r'\]' else: filter_ = r'\[' + task + TaskID.DELIM_RE + TaskID.POINT_RE + r'\]' self.task_filter = filter_ self.update_view() return False def custom_filter_log(self, e): """Filter for arbitrary text.""" txt = e.get_text() if txt == '': filter_ = None else: filter_ = txt self.custom_filter = filter_ self.update_view() return False def rotate_log(self, bt, go_older): """Switch to other log rotations.""" if go_older: self.rotation += 1 elif self.rotation > 0: self.rotation -= 1 self.update_view() def connect(self): """Run the tailer command.""" cmd = self.cmd_tmpl % { 'rotation': self.rotation, 'suite_name': self.suite_name, 'suite_log': self.suite_log } self.t = Tailer( self.logview, cmd, filters=[f for f in [self.task_filter, self.custom_filter] if f]) self.t.start() def update_view(self): """Restart the log view on another log.""" if self.t is None: return False self.t.stop() logbuffer = self.logview.get_buffer() s, e = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(s, e) label = "log (rot %d)" % self.rotation self.log_label.set_text(label) self.connect()
class ComboLogViewer(logviewer): """Implement a viewer for task jobs in the "cylc gui". It has a a combo box for log file selection. task_id -- The NAME.POINT of a task proxy. filenames -- The names of the task job logs. cmd_tmpls -- A dict to map file names and alternate commands to tail follow the file. init_active_index -- The index for selecting the initial log file. """ LABEL_TEXT = "Choose Log File: " def __init__(self, task_id, filenames, cmd_tmpls, init_active_index): self.filenames = OrderedDict() name_str, point_str = TaskID.split(task_id) for filename in filenames: try: f_point_str, f_name_str, f_submit_num_str, f_base_name = ( filename.rsplit(os.sep, 4)[1:]) if (f_point_str == point_str and f_name_str == name_str and int(f_submit_num_str) and f_base_name): name = f_submit_num_str + os.sep + f_base_name if ":" in filename: name += " (%s)" % (filename.split(":", 1)[0]) except ValueError: name = filename self.filenames[name] = filename self.init_active_index = init_active_index self.cmd_tmpls = cmd_tmpls logviewer.__init__(self, task_id, None, filenames[self.init_active_index]) def connect(self): """Connect to the selected log file tailer.""" try: cmd_tmpl = self.cmd_tmpls[self.filename] except (KeyError, TypeError): cmd_tmpl = None self.t = Tailer(self.logview, self.filename, cmd_tmpl=cmd_tmpl) self.t.start() def create_gui_panel(self): """Create the panel.""" logviewer.create_gui_panel(self) label = gtk.Label(self.LABEL_TEXT) combobox = gtk.combo_box_new_text() for name in self.filenames: combobox.append_text(name) combobox.connect("changed", self.switch_log) if self.init_active_index: combobox.set_active(self.init_active_index) else: combobox.set_active(0) self.hbox.pack_end(combobox, False) self.hbox.pack_end(label, False) def switch_log(self, callback): """Switch to another file, if necessary.""" if self.t is None: return False model = callback.get_model() index = callback.get_active() name = model[index][0] filename = self.filenames[name] if filename != self.filename: self.filename = filename self.t.stop() self.t.join() logbuffer = self.logview.get_buffer() pos_start, pos_end = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(pos_start, pos_end) self.log_label.set_text(name) self.connect() return False
class gcapture(object): """ Run a command as a subprocess and capture its stdout and stderr in real time, to display in a GUI window. Examples: $ capture "echo foo" $ capture "echo hello && sleep 5 && echo bye" Lines containing: 'CRITICAL', 'WARNING', 'ERROR' are displayed in red. $ capture "echo foo && echox bar" """ def __init__(self, command, stdoutfile, width=400, height=400, standalone=False, ignore_command=False, title=None): self.standalone = standalone self.command = command self.ignore_command = ignore_command self.stdout = stdoutfile self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_border_width(5) if title is None: self.window.set_title('Command Output') else: self.window.set_title(title) self.window.connect("delete_event", self.quit) self.window.set_default_size(width, height) self.window.set_icon(get_icon()) self.quit_already = False self.find_current = None self.find_current_iter = None self.search_warning_done = False sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) sw.show() self.textview = gtk.TextView() self.textview.set_editable(False) self.textview.set_wrap_mode(gtk.WRAP_WORD) # Use a monospace font. This is safe - by testing - setting an # illegal font description has no effect. self.textview.modify_font(pango.FontDescription("monospace")) tb = self.textview.get_buffer() self.textview.show() self.ftag = tb.create_tag(None, background="#70FFA9") vbox = gtk.VBox() vbox.show() if not self.ignore_command: self.progress_bar = gtk.ProgressBar() self.progress_bar.set_text(command) self.progress_bar.set_pulse_step(0.04) self.progress_bar.show() vbox.pack_start(self.progress_bar, expand=False) self.command_label = gtk.Label(self.command) if self.ignore_command: self.command_label.show() vbox.pack_start(self.command_label, expand=False) sw.add(self.textview) frame = gtk.Frame() frame.add(sw) frame.show() vbox.add(frame) save_button = gtk.Button("Save As") save_button.connect("clicked", self.save, self.textview) save_button.show() hbox = gtk.HBox() hbox.pack_start(save_button, False) hbox.show() output_label = gtk.Label('output : ' + stdoutfile.name) output_label.show() hbox.pack_start(output_label, expand=True) self.freeze_button = gtk.ToggleButton("_Disconnect") self.freeze_button.set_active(False) self.freeze_button.connect("toggled", self.freeze) self.freeze_button.show() searchbox = gtk.HBox() searchbox.show() entry = gtk.Entry() entry.show() entry.connect("activate", self.enter_clicked) searchbox.pack_start(entry, True) b = gtk.Button("Find Next") b.connect_object('clicked', self.on_find_clicked, entry) b.show() searchbox.pack_start(b, False) searchbox.pack_start(self.freeze_button, False) close_button = gtk.Button("_Close") close_button.connect("clicked", self.quit, None, None) close_button.show() hbox.pack_end(close_button, False) vbox.pack_start(searchbox, False) vbox.pack_start(hbox, False) self.window.add(vbox) close_button.grab_focus() self.window.show() def run(self): proc = None if not self.ignore_command: proc = subprocess.Popen( self.command, stdout=self.stdout, stderr=subprocess.STDOUT, shell=True) self.proc = proc gobject.timeout_add(40, self.pulse_proc_progress) self.stdout_updater = Tailer( self.textview, self.stdout.name, pollable=proc) self.stdout_updater.start() def pulse_proc_progress(self): """While the process is running, pulse the progress bar a bit.""" self.progress_bar.pulse() self.proc.poll() if self.proc.returncode is None and not self.stdout_updater.freeze: # Continue gobject.timeout_add loop. return True self.progress_bar.set_fraction(1.0) gobject.idle_add(self._handle_proc_completed) # Break gobject.timeout_add loop. return False def freeze(self, b): if b.get_active(): self.stdout_updater.freeze = True b.set_label('_Reconnect') else: self.stdout_updater.freeze = False b.set_label('_Disconnect') def save(self, w, tv): tb = tv.get_buffer() start = tb.get_start_iter() end = tb.get_end_iter() txt = tb.get_text(start, end) dialog = gtk.FileChooserDialog( title='Save As', action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK)) filter = gtk.FileFilter() filter.set_name("any") filter.add_pattern("*") dialog.add_filter(filter) response = dialog.run() if response != gtk.RESPONSE_OK: dialog.destroy() return False fname = dialog.get_filename() dialog.destroy() try: f = open(fname, 'wb') except IOError, x: warning_dialog(str(x), self.window).warn() else:
class logviewer(object): def __init__(self, name, dirname, filename): self.name = name self.dirname = dirname self.filename = filename self.t = None self.find_current = None self.find_current_iter = None self.search_warning_done = False self.create_gui_panel() self.logview.get_buffer() self.connect() def clear_and_reconnect(self): self.t.stop() self.clear() self.connect() def clear(self): logbuffer = self.logview.get_buffer() s, e = logbuffer.get_bounds() logbuffer.delete(s, e) def path(self): if self.dirname and not os.path.isabs(self.filename): return os.path.join(self.dirname, self.filename) else: return self.filename def connect(self): self.t = Tailer(self.logview, self.path()) self.t.start() def quit_w_e(self, w, e): self.t.stop() def quit(self): self.t.stop() def get_widget(self): return self.vbox def reset_logbuffer(self): # clear log buffer iters and tags logbuffer = self.logview.get_buffer() s, e = logbuffer.get_bounds() logbuffer.remove_all_tags(s, e) self.find_current_iter = None self.find_current = None def enter_clicked(self, e, tv): self.on_find_clicked(tv, e) def on_find_clicked(self, tv, e): needle = e.get_text() if not needle: return self.t.freeze = True self.freeze_button.set_active(True) self.freeze_button.set_label('Reconnect') if not self.search_warning_done: warning_dialog( "Find Next disconnects the live feed;" + " click Reconnect when you're done").warn() self.search_warning_done = True tb = tv.get_buffer() if needle == self.find_current: s = self.find_current_iter else: s, e = tb.get_bounds() tb.remove_all_tags(s, e) s = tb.get_end_iter() tv.scroll_to_iter(s, 0) try: f, l = s.backward_search(needle, gtk.TEXT_SEARCH_TEXT_ONLY) except: warning_dialog('"' + needle + '"' + " not found").warn() else: tag = tb.create_tag(None, background="#70FFA9") tb.apply_tag(tag, f, l) self.find_current_iter = f self.find_current = needle tv.scroll_to_iter(f, 0) def freeze_log(self, b): # TODO - HANDLE MORE STUFF IN THREADS LIKE THIS, RATHER THAN # PASSING IN ARGUMENTS? if b.get_active(): self.t.freeze = True b.set_label('Re_connect') self.reset_logbuffer() else: self.t.freeze = False b.set_label('Dis_connect') return False def create_gui_panel(self): self.logview = gtk.TextView() self.logview.set_editable(False) # Use a monospace font. This is safe - by testing - setting an # illegal font description has no effect. self.logview.modify_font(pango.FontDescription("monospace")) searchbox = gtk.HBox() entry = gtk.Entry() entry.connect("activate", self.enter_clicked, self.logview) searchbox.pack_start(entry, True) b = gtk.Button("Find Next") b.connect_object('clicked', self.on_find_clicked, self.logview, entry) searchbox.pack_start(b, False) self.hbox = gtk.HBox() self.freeze_button = gtk.ToggleButton("Dis_connect") self.freeze_button.set_active(False) self.freeze_button.connect("toggled", self.freeze_log) searchbox.pack_end(self.freeze_button, False) sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) sw.add(self.logview) self.logview.set_border_width(5) self.logview.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#fff")) self.vbox = gtk.VBox() self.log_label = gtk.Label(self.path()) self.log_label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#00a")) self.vbox.pack_start(self.log_label, False) self.vbox.pack_start(sw, True) self.vbox.pack_start(searchbox, False) self.vbox.pack_start(self.hbox, False)
class ComboLogViewer(logviewer): """Implement a viewer for task job logs in the GUI, via "cylc cat-log". It has a a combo box for log file selection. """ LABEL_TEXT = "File: " LABEL_TEXT2 = "Submit: " def __init__(self, suite, task_id, choice, extra_logs, nsubmits, remote_run_opts): self.suite_name = suite self.task_id = task_id self.nsubmits = nsubmits self.nsubmit = nsubmits self.extra_logs = extra_logs self.suite = suite self.choice = choice self.cmd_tmpl = "cylc cat-log %s" % remote_run_opts + ( " -m t -s %(subnum)s -f %(job_log)s %(suite_name)s %(task_id)s") logviewer.__init__(self) def connect(self): """Connect to the selected log file tailer.""" cmd = self.cmd_tmpl % { 'subnum': self.nsubmit, 'suite_name': self.suite_name, 'task_id': self.task_id, 'job_log': self.choice } self.log_label.set_text(self.choice) self.t = Tailer(self.logview, cmd) self.t.start() def create_gui_panel(self): """Create the panel.""" logviewer.create_gui_panel(self) label2 = gtk.Label(self.LABEL_TEXT2) combobox2 = gtk.combo_box_new_text() snums = range(1, self.nsubmits + 1) for snum in snums: combobox2.append_text(str(snum)) combobox2.connect("changed", self.switch_snum) if self.nsubmit in snums: combobox2.set_active(snums.index(self.nsubmit)) self.hbox.pack_end(combobox2, False) self.hbox.pack_end(label2, False) label = gtk.Label(self.LABEL_TEXT) combobox = gtk.combo_box_new_text() names = JOB_LOG_OPTS.values() + self.extra_logs for name in names: combobox.append_text(name) combobox.connect("changed", self.switch_log) combobox.set_active(names.index(self.choice)) self.hbox.pack_end(combobox, False) self.hbox.pack_end(label, False) def switch_log(self, callback): """Switch to another file.""" if self.t is None: return False model = callback.get_model() index = callback.get_active() filename = model[index][0] if filename != self.choice: self.choice = filename self.t.stop() self.t.join() logbuffer = self.logview.get_buffer() pos_start, pos_end = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(pos_start, pos_end) self.connect() return False def switch_snum(self, callback): """Switch to another file.""" if self.t is None: return False model = callback.get_model() index = callback.get_active() snum = model[index][0] if snum != self.nsubmit: self.nsubmit = snum self.t.stop() self.t.join() logbuffer = self.logview.get_buffer() pos_start, pos_end = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(pos_start, pos_end) self.connect() return False
class ComboLogViewer(logviewer): """Implement a viewer for task jobs in the "cylc gui". It has a a combo box for log file selection. task_id -- The NAME.POINT of a task proxy. filenames -- The names of the task job logs. cmd_tmpls -- A dict to map file names and alternate commands to tail follow the file. init_active_index -- The index for selecting the initial log file. """ LABEL_TEXT = "Choose Log File: " def __init__(self, task_id, filenames, cmd_tmpls, init_active_index): self.filenames = OrderedDict() name_str, point_str = TaskID.split(task_id) for filename in filenames: try: f_point_str, f_name_str, f_submit_num_str, f_base_name = ( filename.rsplit(os.sep, 4)[1:]) if (f_point_str == point_str and f_name_str == name_str and int(f_submit_num_str) and f_base_name): name = f_submit_num_str + os.sep + f_base_name if ":" in filename: name += " (%s)" % (filename.split(":", 1)[0]) except ValueError: name = filename self.filenames[name] = filename self.init_active_index = init_active_index self.cmd_tmpls = cmd_tmpls logviewer.__init__( self, task_id, None, filenames[self.init_active_index]) def connect(self): """Connect to the selected log file tailer.""" try: cmd_tmpl = self.cmd_tmpls[self.filename] except (KeyError, TypeError): cmd_tmpl = None self.t = Tailer(self.logview, self.filename, cmd_tmpl=cmd_tmpl) self.t.start() def create_gui_panel(self): """Create the panel.""" logviewer.create_gui_panel(self) label = gtk.Label(self.LABEL_TEXT) combobox = gtk.combo_box_new_text() for name in self.filenames: combobox.append_text(name) combobox.connect("changed", self.switch_log) if self.init_active_index: combobox.set_active(self.init_active_index) else: combobox.set_active(0) self.hbox.pack_end(combobox, False) self.hbox.pack_end(label, False) def switch_log(self, callback): """Switch to another file, if necessary.""" if self.t is None: return False model = callback.get_model() index = callback.get_active() name = model[index][0] filename = self.filenames[name] if filename != self.filename: self.filename = filename self.t.stop() self.t.join() logbuffer = self.logview.get_buffer() pos_start, pos_end = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(pos_start, pos_end) self.log_label.set_text(name) self.connect() return False
class SuiteLogViewer(logviewer): """A popup window to view suite logs. Implemented using "cylc cat-log". """ LABEL_ALL_LINES = "(all lines)" LABEL_ALL_TASKS = "(all tasks)" def __init__(self, suite_name, suite_log, remote_run_opts, task_list=None): """Initialise the suite log viewer.""" if task_list is None: self.task_list = [] else: self.task_list = task_list self.suite_name = suite_name self.suite_log = suite_log self.suite_log_name = SUITE_LOG_OPTS[suite_log] self.rotation = 0 self.cmd_tmpl = "cylc cat-log %s" % remote_run_opts + ( " -m t -r %(rotation)s -f %(suite_log)s %(suite_name)s") self.task_filter = None self.custom_filter = None logviewer.__init__(self) def create_gui_panel(self): """Create the GUI panel.""" logviewer.create_gui_panel(self) self.window = gtk.Window() # self.window.set_border_width(5) self.window.set_title("log viewer") self.window.set_size_request(800, 400) self.window.set_icon(get_icon()) combobox = gtk.combo_box_new_text() combobox.append_text(self.LABEL_ALL_LINES) combobox.append_text(self.LABEL_ALL_TASKS) for task in self.task_list: combobox.append_text(task) combobox.connect("changed", self.filter_log) combobox.set_active(0) newer = gtk.Button("_newer") newer.connect("clicked", self.rotate_log, False) self.hbox.pack_end(newer, False) older = gtk.Button("_older") older.connect("clicked", self.rotate_log, True) self.hbox.pack_end(older, False) self.hbox.pack_end(combobox, False) filterbox = gtk.HBox() entry = gtk.Entry() entry.connect("activate", self.custom_filter_log) label = gtk.Label('Filter') filterbox.pack_start(label, True) filterbox.pack_start(entry, True) self.hbox.pack_end(filterbox, False) close = gtk.Button("_Close") close.connect("clicked", self.shutdown, None, self.window) self.hbox.pack_start(close, False) self.window.add(self.vbox) self.window.connect("delete_event", self.shutdown, self.window) self.window.show_all() def shutdown(self, w, e, wind): """Quite the suite log viewer.""" self.quit() wind.destroy() def filter_log(self, cb): """Filter for task names.""" model = cb.get_model() index = cb.get_active() task = model[index][0] # Good enough to match "[task.CYCLE]"? if task == self.LABEL_ALL_LINES: filter_ = None elif task == self.LABEL_ALL_TASKS: filter_ = r'\[' + TaskID.ID_RE + r'\]' else: filter_ = r'\[' + task + TaskID.DELIM_RE + TaskID.POINT_RE + r'\]' self.task_filter = filter_ self.update_view() return False def custom_filter_log(self, e): """Filter for arbitrary text.""" txt = e.get_text() if txt == '': filter_ = None else: filter_ = txt self.custom_filter = filter_ self.update_view() return False def rotate_log(self, bt, go_older): """Switch to other log rotations.""" if go_older: self.rotation += 1 elif self.rotation > 0: self.rotation -= 1 self.update_view() def connect(self): """Run the tailer command.""" cmd = self.cmd_tmpl % {'rotation': self.rotation, 'suite_name': self.suite_name, 'suite_log': self.suite_log} self.t = Tailer( self.logview, cmd, filters=[f for f in [self.task_filter, self.custom_filter] if f]) self.t.start() def update_view(self): """Restart the log view on another log.""" if self.t is None: return False self.t.stop() logbuffer = self.logview.get_buffer() s, e = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(s, e) label = "%s (rot %d)" % (self.suite_log_name, self.rotation) self.log_label.set_text(label) self.connect()
class ComboLogViewer(logviewer): """Implement a viewer for task jobs in the "cylc gui". It has a a combo box for log file selection. task_id -- The NAME.POINT of a task proxy. filenames -- The names of the task job logs. cmd_tmpls -- A dict to map file names and alternate commands to tail follow the file. init_active_index -- The index for selecting the initial log file. """ LABEL_TEXT = "Choose Log File: " def __init__(self, task_id, filenames, cmd_tmpls, init_active_index): self.filenames = filenames self.init_active_index = init_active_index self.cmd_tmpls = cmd_tmpls self.common_dir = os.path.dirname(os.path.commonprefix(self.filenames)) logviewer.__init__( self, task_id, None, self.filenames[self.init_active_index]) def connect(self): """Connect to the selected log file tailer.""" try: cmd_tmpl = self.cmd_tmpls[self.filename] except (KeyError, TypeError): cmd_tmpl = None self.t = Tailer(self.logview, self.filename, cmd_tmpl=cmd_tmpl) self.t.start() def create_gui_panel(self): """Create the panel.""" logviewer.create_gui_panel(self) label = gtk.Label(self.LABEL_TEXT) combobox = gtk.combo_box_new_text() for filename in self.filenames: relpath = os.path.relpath(filename, self.common_dir) if len(relpath) < len(filename): combobox.append_text(relpath) else: combobox.append_text(filename) combobox.connect("changed", self.switch_log) if self.init_active_index: combobox.set_active(self.init_active_index) else: combobox.set_active(0) self.hbox.pack_end(combobox, False) self.hbox.pack_end(label, False) def switch_log(self, callback): """Switch to another file, if necessary.""" if self.t is None: return False model = callback.get_model() index = callback.get_active() name = model[index][0] if name in self.filenames: filename = name else: filename = os.path.join(self.common_dir, name) if filename != self.filename: self.filename = filename self.t.stop() self.t.join() logbuffer = self.logview.get_buffer() pos_start, pos_end = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(pos_start, pos_end) self.log_label.set_text(name) self.connect() return False
class ComboLogViewer(logviewer): """Implement a viewer for task job logs in the GUI, via "cylc cat-log". It has a a combo box for log file selection. """ LABEL_TEXT = "File: " LABEL_TEXT2 = "Submit: " def __init__(self, suite, task_id, choice, extra_logs, nsubmits, remote_run_opts): self.suite_name = suite self.task_id = task_id self.nsubmits = nsubmits self.nsubmit = nsubmits self.extra_logs = extra_logs self.suite = suite self.choice = choice self.cmd_tmpl = "cylc cat-log %s" % remote_run_opts + ( " -m t -s %(subnum)s -f %(job_log)s %(suite_name)s %(task_id)s") logviewer.__init__(self) def connect(self): """Connect to the selected log file tailer.""" cmd = self.cmd_tmpl % {'subnum': self.nsubmit, 'suite_name': self.suite_name, 'task_id': self.task_id, 'job_log': self.choice} self.log_label.set_text(self.choice) self.t = Tailer(self.logview, cmd) self.t.start() def create_gui_panel(self): """Create the panel.""" logviewer.create_gui_panel(self) label2 = gtk.Label(self.LABEL_TEXT2) combobox2 = gtk.combo_box_new_text() snums = range(1, self.nsubmits + 1) for snum in snums: combobox2.append_text(str(snum)) combobox2.connect("changed", self.switch_snum) combobox2.set_active(snums.index(self.nsubmit)) self.hbox.pack_end(combobox2, False) self.hbox.pack_end(label2, False) label = gtk.Label(self.LABEL_TEXT) combobox = gtk.combo_box_new_text() names = JOB_LOG_OPTS.values() + self.extra_logs for name in names: combobox.append_text(name) combobox.connect("changed", self.switch_log) combobox.set_active(names.index(self.choice)) self.hbox.pack_end(combobox, False) self.hbox.pack_end(label, False) def switch_log(self, callback): """Switch to another file.""" if self.t is None: return False model = callback.get_model() index = callback.get_active() filename = model[index][0] if filename != self.choice: self.choice = filename self.t.stop() self.t.join() logbuffer = self.logview.get_buffer() pos_start, pos_end = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(pos_start, pos_end) self.connect() return False def switch_snum(self, callback): """Switch to another file.""" if self.t is None: return False model = callback.get_model() index = callback.get_active() snum = model[index][0] if snum != self.nsubmit: self.nsubmit = snum self.t.stop() self.t.join() logbuffer = self.logview.get_buffer() pos_start, pos_end = logbuffer.get_bounds() self.reset_logbuffer() logbuffer.delete(pos_start, pos_end) self.connect() return False
class logviewer(object): def __init__(self, name, dirname, filename): self.name = name self.dirname = dirname self.filename = filename self.t = None self.find_current = None self.find_current_iter = None self.search_warning_done = False self.freeze_button = None self.log_label = None self.logview = None self.hbox = None self.vbox = None self.create_gui_panel() self.logview.get_buffer() self.connect() def clear_and_reconnect(self): self.t.stop() self.clear() self.connect() def clear(self): logbuffer = self.logview.get_buffer() s, e = logbuffer.get_bounds() logbuffer.delete(s, e) def path(self): if self.dirname and not os.path.isabs(self.filename): return os.path.join(self.dirname, self.filename) else: return self.filename def connect(self): self.t = Tailer(self.logview, self.path()) self.t.start() def quit_w_e(self, w, e): self.t.stop() def quit(self): self.t.stop() def get_widget(self): return self.vbox def reset_logbuffer(self): # clear log buffer iters and tags logbuffer = self.logview.get_buffer() s, e = logbuffer.get_bounds() logbuffer.remove_all_tags(s, e) self.find_current_iter = None self.find_current = None def enter_clicked(self, e, tv): self.on_find_clicked(tv, e) def on_find_clicked(self, tv, e): needle = e.get_text() if not needle: return self.t.freeze = True self.freeze_button.set_active(True) self.freeze_button.set_label('Reconnect') if not self.search_warning_done: warning_dialog("Find Next disconnects the live feed;" + " click Reconnect when you're done").warn() self.search_warning_done = True tb = tv.get_buffer() if needle == self.find_current: s = self.find_current_iter else: s, e = tb.get_bounds() tb.remove_all_tags(s, e) s = tb.get_end_iter() tv.scroll_to_iter(s, 0) try: start, end = s.backward_search(needle, gtk.TEXT_SEARCH_TEXT_ONLY) except TypeError: # No search results. warning_dialog('"' + needle + '"' + " not found").warn() else: tag = tb.create_tag(None, background="#70FFA9") tb.apply_tag(tag, start, end) self.find_current_iter = start self.find_current = needle tv.scroll_to_iter(start, 0) def freeze_log(self, b): # TODO - HANDLE MORE STUFF IN THREADS LIKE THIS, RATHER THAN # PASSING IN ARGUMENTS? if b.get_active(): self.t.freeze = True b.set_label('Re_connect') self.reset_logbuffer() else: self.t.freeze = False b.set_label('Dis_connect') return False def create_gui_panel(self): self.logview = gtk.TextView() self.logview.set_editable(False) # Use a monospace font. This is safe - by testing - setting an # illegal font description has no effect. self.logview.modify_font(pango.FontDescription("monospace")) searchbox = gtk.HBox() entry = gtk.Entry() entry.connect("activate", self.enter_clicked, self.logview) searchbox.pack_start(entry, True) b = gtk.Button("Find Next") b.connect_object('clicked', self.on_find_clicked, self.logview, entry) searchbox.pack_start(b, False) self.hbox = gtk.HBox() self.freeze_button = gtk.ToggleButton("Dis_connect") self.freeze_button.set_active(False) self.freeze_button.connect("toggled", self.freeze_log) searchbox.pack_end(self.freeze_button, False) sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) sw.add(self.logview) self.logview.set_border_width(5) self.logview.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#fff")) self.vbox = gtk.VBox() self.log_label = gtk.Label(self.path()) self.log_label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#00a")) self.vbox.pack_start(self.log_label, False) self.vbox.pack_start(sw, True) self.vbox.pack_start(searchbox, False) self.vbox.pack_start(self.hbox, False)
def connect(self): self.t = Tailer(self.logview, self.path()) self.t.start()