def test_identity_filter_rerange(self): full_model = Model() ranged_model = RangeFilteredLogModel(full_model) # FIXME: Call to .reset should not be needed. ranged_model.reset() filtered_model = FilteredLogModel(ranged_model) row_list = self.__row_list self.assertEquals(row_list(full_model), range(20)) self.assertEquals(row_list(ranged_model), range(20)) self.assertEquals(row_list(filtered_model), range(20)) filtered_model.add_filter(IdentityFilter(), Common.Data.DefaultDispatcher()) ranged_model.set_range(5, 16) filtered_model.super_model_changed_range() self.assertEquals(row_list(ranged_model), range(5, 16)) self.assertEquals(row_list(filtered_model), range(5, 16)) self.assertEquals( [filtered_model.line_index_from_super(i) for i in range(11)], range(11)) self.assertEquals( [filtered_model.line_index_to_super(i) for i in range(11)], range(11)) self.assertEquals( [filtered_model.line_index_from_top(i) for i in range(5, 16)], range(11)) self.assertEquals( [filtered_model.line_index_to_top(i) for i in range(11)], range(5, 16))
def test_identity_filter_rerange (self): full_model = Model () ranged_model = RangeFilteredLogModel (full_model) # FIXME: Call to .reset should not be needed. ranged_model.reset () filtered_model = FilteredLogModel (ranged_model) row_list = self.__row_list self.assertEquals (row_list (full_model), range (20)) self.assertEquals (row_list (ranged_model), range (20)) self.assertEquals (row_list (filtered_model), range (20)) filtered_model.add_filter (IdentityFilter (), Common.Data.DefaultDispatcher ()) ranged_model.set_range (5, 16) filtered_model.super_model_changed_range () self.assertEquals (row_list (ranged_model), range (5, 16)) self.assertEquals (row_list (filtered_model), range (5, 16)) self.assertEquals ([filtered_model.line_index_from_super (i) for i in range (11)], range (11)) self.assertEquals ([filtered_model.line_index_to_super (i) for i in range (11)], range (11)) self.assertEquals ([filtered_model.line_index_from_top (i) for i in range (5, 16)], range (11)) self.assertEquals ([filtered_model.line_index_to_top (i) for i in range (11)], range (5, 16))
def test_random_filtered_range_refilter (self): full_model = Model () ranged_model = RangeFilteredLogModel (full_model) # FIXME: Call to .reset should not be needed. ranged_model.reset () filtered_model = FilteredLogModel (ranged_model) row_list = self.__row_list self.assertEquals (row_list (full_model), range (20)) self.assertEquals (row_list (ranged_model), range (20)) self.assertEquals (row_list (filtered_model), range (20)) filtered_model.add_filter (RandomFilter (538295943), Common.Data.DefaultDispatcher ()) random_rows = row_list (filtered_model) self.__dump_model (filtered_model) ranged_model.set_range (10, 20) self.__dump_model (ranged_model, "ranged_model (10, 20)") self.assertEquals (row_list (ranged_model), range (10, 20)) filtered_model.super_model_changed_range () self.__dump_model (filtered_model) self.assertEquals (row_list (filtered_model), [x for x in range (10, 20) if x in random_rows]) ranged_model.set_range (0, 20) self.assertEquals (row_list (ranged_model), range (0, 20)) ranged_model = RangeFilteredLogModel (full_model) # FIXME: Call to .reset should not be needed. ranged_model.reset () filtered_model = FilteredLogModel (ranged_model) filtered_model.add_filter (RandomFilter (538295943), Common.Data.DefaultDispatcher ()) self.__dump_model (filtered_model, "filtered model") self.assertEquals (row_list (filtered_model), random_rows) ranged_model.set_range (0, 10) self.__dump_model (ranged_model, "ranged model (0, 10)") filtered_model.super_model_changed_range () self.assertEquals (row_list (ranged_model), range (0, 10)) self.__dump_model (filtered_model) self.assertEquals (row_list (filtered_model), [x for x in range (0, 10) if x in random_rows])
def test_filtered_range_refilter (self): full_model = Model () ranged_model = RangeFilteredLogModel (full_model) # FIXME: Call to .reset should not be needed. ranged_model.reset () filtered_model = FilteredLogModel (ranged_model) row_list = self.__row_list rows = row_list (full_model) rows_ranged = row_list (ranged_model) rows_filtered = row_list (filtered_model) self.__dump_model (full_model, "full model") ## self.__dump_model (ranged_model, "ranged model") ## self.__dump_model (filtered_model, "filtered model") self.assertEquals (rows, rows_ranged) self.assertEquals (rows, rows_filtered) self.assertEquals ([ranged_model.line_index_from_super (i) for i in range (20)], range (20)) self.assertEquals ([ranged_model.line_index_to_super (i) for i in range (20)], range (20)) self.assertEquals ([ranged_model.line_index_from_top (i) for i in range (20)], range (20)) self.assertEquals ([ranged_model.line_index_to_top (i) for i in range (20)], range (20)) self.assertEquals ([filtered_model.line_index_from_super (i) for i in range (20)], range (20)) self.assertEquals ([filtered_model.line_index_to_super (i) for i in range (20)], range (20)) self.assertEquals ([filtered_model.line_index_from_top (i) for i in range (20)], range (20)) self.assertEquals ([filtered_model.line_index_to_top (i) for i in range (20)], range (20)) ranged_model.set_range (5, 16) self.__dump_model (ranged_model, "ranged model (5, 16)") filtered_model.super_model_changed_range () rows_ranged = row_list (ranged_model) self.assertEquals (rows_ranged, range (5, 16)) self.__dump_model (filtered_model, "filtered model (nofilter, 5, 15)") rows_filtered = row_list (filtered_model) self.assertEquals (rows_ranged, rows_filtered) self.assertEquals ([ranged_model.line_index_from_super (i) for i in range (5, 16)], range (11)) self.assertEquals ([ranged_model.line_index_to_super (i) for i in range (11)], range (5, 16)) self.assertEquals ([ranged_model.line_index_from_top (i) for i in range (5, 16)], range (11)) self.assertEquals ([ranged_model.line_index_to_top (i) for i in range (11)], range (5, 16)) self.assertEquals ([filtered_model.line_index_from_super (i) for i in range (11)], range (11)) self.assertEquals ([filtered_model.line_index_to_super (i) for i in range (11)], range (11)) self.assertEquals ([filtered_model.line_index_from_top (i) for i in range (5, 16)], range (11)) self.assertEquals ([filtered_model.line_index_to_top (i) for i in range (11)], range (5, 16)) filtered_model.add_filter (CategoryFilter ("EVEN"), Common.Data.DefaultDispatcher ()) rows_filtered = row_list (filtered_model) self.assertEquals (rows_filtered, range (5, 16, 2)) self.__dump_model (filtered_model, "filtered model") self.assertEquals ([filtered_model.line_index_from_super (i) for i in range (0, 11, 2)], range (6)) self.assertEquals ([filtered_model.line_index_from_top (i) for i in range (5, 16, 2)], range (6)) ranged_model.set_range (7, 13) self.__dump_model (ranged_model, "ranged model (7, 13)") filtered_model.super_model_changed_range () self.assertEquals (row_list (ranged_model), range (7, 13)) self.assertEquals ([ranged_model.line_index_from_super (i) for i in range (7, 13)], range (6)) self.assertEquals ([ranged_model.line_index_to_super (i) for i in range (6)], range (7, 13)) self.assertEquals ([ranged_model.line_index_from_top (i) for i in range (7, 13)], range (6)) self.assertEquals ([ranged_model.line_index_to_top (i) for i in range (6)], range (7, 13)) self.__dump_model (filtered_model, "filtered model (ranged 7, 12)") self.assertEquals ([filtered_model.line_index_from_super (i) for i in range (0, 6, 2)], range (3)) self.assertEquals ([filtered_model.line_index_to_super (i) for i in range (3)], range (0, 6, 2)) self.assertEquals ([filtered_model.line_index_from_top (i) for i in range (7, 12, 2)], range (3)) self.assertEquals ([filtered_model.line_index_to_top (i) for i in range (3)], range (7, 12, 2)) rows_filtered = row_list (filtered_model) self.assertEquals (rows_filtered, range (7, 13, 2))
def test_filtered_range_refilter_skip (self): full_model = Model () ranged_model = RangeFilteredLogModel (full_model) # FIXME: Call to .reset should not be needed. ranged_model.reset () filtered_model = FilteredLogModel (ranged_model) row_list = self.__row_list filtered_model.add_filter (CategoryFilter ("EVEN"), Common.Data.DefaultDispatcher ()) self.__dump_model (filtered_model, "filtered") self.assertEquals (row_list (filtered_model), range (1, 20, 2)) self.assertEquals ([filtered_model.line_index_from_super (i) for i in range (1, 20, 2)], range (10)) self.assertEquals ([filtered_model.line_index_to_super (i) for i in range (10)], range (1, 20, 2)) self.assertEquals ([filtered_model.line_index_from_top (i) for i in range (1, 20, 2)], range (10)) self.assertEquals ([filtered_model.line_index_to_top (i) for i in range (10)], range (1, 20, 2)) ranged_model.set_range (1, 20) self.__dump_model (ranged_model, "ranged (1, 20)") filtered_model.super_model_changed_range () self.__dump_model (filtered_model, "filtered range") self.assertEquals ([filtered_model.line_index_from_super (i) for i in range (0, 19, 2)], range (10)) self.assertEquals ([filtered_model.line_index_to_super (i) for i in range (10)], range (0, 19, 2)) self.assertEquals ([filtered_model.line_index_from_top (i) for i in range (1, 20, 2)], range (10)) self.assertEquals ([filtered_model.line_index_to_top (i) for i in range (10)], range (1, 20, 2)) ranged_model.set_range (2, 20) self.__dump_model (ranged_model, "ranged (2, 20)") filtered_model.super_model_changed_range () self.__dump_model (filtered_model, "filtered range") self.assertEquals (row_list (filtered_model), range (3, 20, 2)) self.assertEquals ([filtered_model.line_index_from_super (i) for i in range (1, 18, 2)], range (9)) self.assertEquals ([filtered_model.line_index_to_super (i) for i in range (9)], range (1, 18, 2)) self.assertEquals ([filtered_model.line_index_from_top (i) for i in range (3, 20, 2)], range (9)) self.assertEquals ([filtered_model.line_index_to_top (i) for i in range (9)], range (3, 20, 2))
def setup_model(self, model): self.log_model = model self.log_filter = FilteredLogModel(self.log_model) self.log_filter.handle_process_finished = self.handle_log_filter_process_finished
class Window(object): def __init__(self, app): self.logger = logging.getLogger("ui.window") self.app = app self.dispatcher = None self.info_widget = None self.progress_dialog = None self.update_progress_id = None self.window_state = Common.GUI.WindowState() self.column_manager = ViewColumnManager(app.state_section) self.actions = Common.GUI.Actions() group = Gtk.ActionGroup("MenuActions") group.add_actions([("AppMenuAction", None, _("_Application")), ("ViewMenuAction", None, _("_View")), ("ViewColumnsMenuAction", None, _("_Columns")), ("HelpMenuAction", None, _("_Help")), ("LineViewContextMenuAction", None, "")]) self.actions.add_group(group) group = Gtk.ActionGroup("WindowActions") group.add_actions([ ("new-window", Gtk.STOCK_NEW, _("_New Window"), "<Ctrl>N"), ("open-file", Gtk.STOCK_OPEN, _("_Open File"), "<Ctrl>O"), ("reload-file", Gtk.STOCK_REFRESH, _("_Reload File"), "<Ctrl>R"), ("close-window", Gtk.STOCK_CLOSE, _("Close _Window"), "<Ctrl>W"), ( "cancel-load", Gtk.STOCK_CANCEL, None, ), ("clear-line-view", Gtk.STOCK_CLEAR, None), ("show-about", None, _("About GStreamer Debug Viewer", )), ("enlarge-text", Gtk.STOCK_ZOOM_IN, _("Enlarge Text"), "<Ctrl>plus"), ("shrink-text", Gtk.STOCK_ZOOM_OUT, _("Shrink Text"), "<Ctrl>minus"), ("reset-text", Gtk.STOCK_ZOOM_100, _("Normal Text Size"), "<Ctrl>0") ]) self.actions.add_group(group) self.actions.reload_file.props.sensitive = False group = Gtk.ActionGroup("RowActions") group.add_actions([ ("hide-before-line", None, _("Hide lines before this point")), ("hide-after-line", None, _("Hide lines after this point")), ("show-hidden-lines", None, _("Show hidden lines")), ("edit-copy-line", Gtk.STOCK_COPY, _("Copy line"), "<Ctrl>C"), ("edit-copy-message", Gtk.STOCK_COPY, _("Copy message"), ""), ("set-base-time", None, _("Set base time")), ("hide-log-level", None, _("Hide log level")), ("hide-log-level-and-above", None, _("Hide this log level and above")), ("show-only-log-level", None, _("Show only log level")), ("hide-log-category", None, _("Hide log category")), ("show-only-log-category", None, _("Show only log category")), ("hide-log-object", None, _("Hide object")), ("show-only-log-object", None, _("Show only object")), ("hide-filename", None, _("Hide filename")), ("show-only-filename", None, _("Show only filename")) ]) group.props.sensitive = False self.actions.add_group(group) self.actions.add_group(self.column_manager.action_group) self.log_file = None self.log_model = None self.log_filter = None self.widget_factory = Common.GUI.WidgetFactory(Main.Paths.data_dir) self.widgets = self.widget_factory.make("main-window.ui", "main_window") ui_filename = os.path.join(Main.Paths.data_dir, "menus.ui") self.ui_factory = Common.GUI.UIFactory(ui_filename, self.actions) self.ui_manager = ui = self.ui_factory.make() menubar = ui.get_widget("/ui/menubar") self.widgets.vbox_main.pack_start(menubar, False, False, 0) self.gtk_window = self.widgets.main_window self.gtk_window.add_accel_group(ui.get_accel_group()) self.log_view = self.widgets.log_view self.log_view.drag_dest_unset() self.log_view.set_search_column(-1) sel = self.log_view.get_selection() sel.connect("changed", self.handle_log_view_selection_changed) self.view_popup = ui.get_widget( "/ui/context/LogViewContextMenu").get_submenu() Common.GUI.widget_add_popup_menu(self.log_view, self.view_popup) # Widgets to set insensitive when the window is considered as # such. This is done during loading/filtering, where we can't set the # whole window insensitive because the progress info bar should be # usable to allow cancellation. self.main_sensitivity = [menubar] self.main_sensitivity.extend(self.widgets.vbox_main.get_children()) self.line_view = LineView() self.attach() self.column_manager.attach(self.log_view) def setup_model(self, model): self.log_model = model self.log_filter = FilteredLogModel(self.log_model) self.log_filter.handle_process_finished = self.handle_log_filter_process_finished def get_top_attach_point(self): return self.widgets.vbox_main def get_side_attach_point(self): return self.widgets.hbox_view def attach(self): self.zoom_level = 0 zoom_percent = self.app.state_section.zoom_level if zoom_percent: self.restore_zoom(float(zoom_percent) / 100.) self.window_state.attach(window=self.gtk_window, state=self.app.state_section) self.clipboard = Gtk.Clipboard.get_for_display( self.gtk_window.get_display(), Gdk.SELECTION_CLIPBOARD) for action_name, handler in iter_actions(self): action = getattr(self.actions, action_name) action.connect("activate", handler) self.gtk_window.connect("delete-event", self.handle_window_delete_event) self.features = [] for plugin_feature in self.app.iter_plugin_features(): feature = plugin_feature(self.app) self.features.append(feature) for feature in self.features: feature.handle_attach_window(self) # FIXME: With multiple selection mode, browsing the list with key # up/down slows to a crawl! WTF is wrong with this stupid widget??? sel = self.log_view.get_selection() sel.set_mode(Gtk.SelectionMode.BROWSE) self.line_view.attach(self) # Do not translate; fallback application name for e.g. gnome-shell if # the desktop file is not installed: self.gtk_window.set_wmclass("gst-debug-viewer", "GStreamer Debug Viewer") self.gtk_window.show() def detach(self): self.set_log_file(None) for feature in self.features: feature.handle_detach_window(self) self.window_state.detach() self.column_manager.detach() def get_active_line_index(self): selection = self.log_view.get_selection() model, tree_iter = selection.get_selected() if tree_iter is None: raise ValueError("no line selected") path = model.get_path(tree_iter) return path[0] def get_active_line(self): selection = self.log_view.get_selection() model, tree_iter = selection.get_selected() if tree_iter is None: raise ValueError("no line selected") model = self.log_view.get_model() return model.get(tree_iter, *LogModelBase.column_ids) def close(self, *a, **kw): self.logger.debug("closing window, detaching") self.detach() self.gtk_window.hide() self.logger.debug("requesting close from app") self.app.close_window(self) def push_view_state(self): self.default_index = None self.default_start_index = None model = self.log_view.get_model() if model is None: return try: line_index = self.get_active_line_index() except ValueError: super_index = None self.logger.debug("no line selected") else: super_index = model.line_index_to_super(line_index) self.logger.debug("pushing selected line %i (abs %i)", line_index, super_index) self.default_index = super_index vis_range = self.log_view.get_visible_range() if vis_range is not None: start_path, end_path = vis_range start_index = start_path[0] self.default_start_index = model.line_index_to_super(start_index) def update_model(self, model=None): if model is None: model = self.log_view.get_model() previous_model = self.log_view.get_model() if previous_model == model: # Force update. self.log_view.set_model(None) self.log_view.set_model(model) def pop_view_state(self, scroll_to_selection=False): model = self.log_view.get_model() if model is None: return selected_index = self.default_index start_index = self.default_start_index if selected_index is not None: try: select_index = model.line_index_from_super(selected_index) except IndexError as exc: self.logger.debug( "abs line index %i filtered out, not reselecting", selected_index) else: assert select_index >= 0 sel = self.log_view.get_selection() path = (select_index, ) sel.select_path(path) if start_index is None or scroll_to_selection: self.log_view.scroll_to_cell(path, use_align=True, row_align=.5) if start_index is not None and not scroll_to_selection: def traverse(): for i in xrange(start_index, len(model)): yield i for i in xrange(start_index - 1, 0, -1): yield i for current_index in traverse(): try: target_index = model.line_index_from_super(current_index) except IndexError: continue else: path = (target_index, ) self.log_view.scroll_to_cell(path, use_align=True, row_align=0.) break def update_view(self): view = self.log_view model = view.get_model() start_path, end_path = view.get_visible_range() start_index, end_index = start_path[0], end_path[0] for line_index in range(start_index, end_index + 1): path = (line_index, ) tree_iter = model.get_iter(path) model.row_changed(path, tree_iter) def handle_log_view_selection_changed(self, selection): try: line_index = self.get_active_line_index() except ValueError: first_selected = True last_selected = True else: first_selected = (line_index == 0) last_selected = (line_index == len(self.log_view.get_model()) - 1) self.actions.hide_before_line.props.sensitive = not first_selected self.actions.hide_after_line.props.sensitive = not last_selected def handle_window_delete_event(self, window, event): self.actions.close_window.activate() return True @action def handle_new_window_action_activate(self, action): self.app.open_window() @action def handle_open_file_action_activate(self, action): dialog = Gtk.FileChooserDialog(None, self.gtk_window, Gtk.FileChooserAction.OPEN, ( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT, )) response = dialog.run() dialog.hide() if response == Gtk.ResponseType.ACCEPT: self.set_log_file(dialog.get_filename()) dialog.destroy() @action def handle_reload_file_action_activate(self, action): if self.log_file is None: return self.set_log_file(self.log_file.path) @action def handle_cancel_load_action_activate(self, action): self.logger.debug("cancelling data load") self.set_log_file(None) if self.progress_dialog is not None: self.hide_info() self.progress_dialog = None if self.update_progress_id is not None: GObject.source_remove(self.update_progress_id) self.update_progress_id = None self.set_sensitive(True) @action def handle_close_window_action_activate(self, action): self.close() @action def handle_hide_after_line_action_activate(self, action): self.hide_range(after=True) @action def handle_hide_before_line_action_activate(self, action): self.hide_range(after=False) def hide_range(self, after): model = self.log_view.get_model() try: filtered_line_index = self.get_active_line_index() except ValueError: return if after: first_index = model.line_index_to_super(0) last_index = model.line_index_to_super(filtered_line_index) self.logger.info( "hiding lines after %i (abs %i), first line is abs %i", filtered_line_index, last_index, first_index) else: first_index = model.line_index_to_super(filtered_line_index) last_index = model.line_index_to_super(len(model) - 1) self.logger.info( "hiding lines before %i (abs %i), last line is abs %i", filtered_line_index, first_index, last_index) self.push_view_state() start_index = first_index stop_index = last_index + 1 self.log_filter.set_range(start_index, stop_index) self.update_model() self.pop_view_state() self.actions.show_hidden_lines.props.sensitive = True @action def handle_show_hidden_lines_action_activate(self, action): self.logger.info("restoring model filter to show all lines") self.push_view_state() self.log_view.set_model(None) self.log_filter.reset() self.update_model(self.log_filter) self.pop_view_state(scroll_to_selection=True) self.actions.show_hidden_lines.props.sensitive = False @action def handle_edit_copy_line_action_activate(self, action): line_index = self.get_active_line_index() model = self.log_view.get_model() line_offset = model.line_offsets[line_index] line_text = model.access_offset(line_offset).strip() line_text = Data.strip_escape(line_text) self.clipboard.set_text(line_text) @action def handle_edit_copy_message_action_activate(self, action): col_id = LogModelBase.COL_MESSAGE self.clipboard.set_text(self.get_active_line()[col_id]) @action def handle_enlarge_text_action_activate(self, action): self.update_zoom_level(1) @action def handle_shrink_text_action_activate(self, action): self.update_zoom_level(-1) @action def handle_reset_text_action_activate(self, action): self.update_zoom_level(-self.zoom_level) def restore_zoom(self, scale): from math import log self.zoom_level = int(round(log(scale) / log(ZOOM_FACTOR))) self.column_manager.set_zoom(scale) def update_zoom_level(self, delta_step): if not delta_step: return self.zoom_level += delta_step scale = ZOOM_FACTOR**self.zoom_level self.column_manager.set_zoom(scale) self.app.state_section.zoom_level = int(round(scale * 100.)) def set_sensitive(self, sensitive): for widget in self.main_sensitivity: widget.props.sensitive = sensitive def show_info(self, widget): self.hide_info() box = self.widgets.vbox_main box.pack_start(widget, False, False, 0) box.reorder_child(widget, 2) widget.show_all() self.info_widget = widget def hide_info(self): if self.info_widget is None: return self.info_widget.destroy() self.info_widget = None def add_model_filter(self, filter): self.progress_dialog = ProgressDialog(self, _("Filtering")) self.show_info(self.progress_dialog.widget) self.progress_dialog.handle_cancel = self.handle_filter_progress_dialog_cancel dispatcher = Common.Data.GSourceDispatcher() self.filter_dispatcher = dispatcher # FIXME: Unsetting the model to keep e.g. the dispatched timeline # sentinel from collecting data while we filter idly, which slows # things down for nothing. self.push_view_state() self.log_view.set_model(None) self.log_filter.add_filter(filter, dispatcher=dispatcher) GObject.timeout_add(250, self.update_filter_progress) self.set_sensitive(False) def update_filter_progress(self): if self.progress_dialog is None: return False try: progress = self.log_filter.get_filter_progress() except ValueError: self.logger.warning("no filter process running") return False self.progress_dialog.update(progress) return True def handle_filter_progress_dialog_cancel(self): self.hide_info() self.progress_dialog = None self.log_filter.abort_process() self.log_view.set_model(self.log_filter) self.pop_view_state() self.set_sensitive(True) def handle_log_filter_process_finished(self): self.hide_info() self.progress_dialog = None # No push_view_state here, did this in add_model_filter. self.update_model(self.log_filter) self.pop_view_state() self.actions.show_hidden_lines.props.sensitive = True self.set_sensitive(True) @action def handle_set_base_time_action_activate(self, action): row = self.get_active_line() self.column_manager.set_base_time(row[LogModelBase.COL_TIME]) @action def handle_hide_log_level_action_activate(self, action): row = self.get_active_line() debug_level = row[LogModelBase.COL_LEVEL] self.add_model_filter(DebugLevelFilter(debug_level)) @action def handle_hide_log_category_action_activate(self, action): row = self.get_active_line() category = row[LogModelBase.COL_CATEGORY] self.add_model_filter(CategoryFilter(category)) @action def handle_hide_log_object_action_activate(self, action): row = self.get_active_line() object_ = row[LogModelBase.COL_OBJECT] self.add_model_filter(ObjectFilter(object_)) @action def handle_hide_filename_action_activate(self, action): row = self.get_active_line() filename = row[LogModelBase.COL_FILENAME] self.add_model_filter(FilenameFilter(filename)) @action def handle_hide_log_level_and_above_action_activate(self, action): row = self.get_active_line() debug_level = row[LogModelBase.COL_LEVEL] self.add_model_filter( DebugLevelFilter(debug_level, DebugLevelFilter.this_and_above)) @action def handle_show_only_log_level_action_activate(self, action): row = self.get_active_line() debug_level = row[LogModelBase.COL_LEVEL] self.add_model_filter( DebugLevelFilter(debug_level, DebugLevelFilter.all_but_this)) @action def handle_show_only_log_category_action_activate(self, action): row = self.get_active_line() category = row[LogModelBase.COL_CATEGORY] self.add_model_filter(CategoryFilter(category, True)) @action def handle_show_only_log_object_action_activate(self, action): row = self.get_active_line() object_ = row[LogModelBase.COL_OBJECT] self.add_model_filter(ObjectFilter(object_, True)) @action def handle_show_only_filename_action_activate(self, action): row = self.get_active_line() filename = row[LogModelBase.COL_FILENAME] self.add_model_filter(FilenameFilter(filename, True)) @action def handle_show_about_action_activate(self, action): from GstDebugViewer import version dialog = self.widget_factory.make_one("about-dialog.ui", "about_dialog") dialog.props.version = version dialog.run() dialog.destroy() @staticmethod def _timestamp_cell_data_func(column, renderer, model, tree_iter): ts = model.get_value(tree_iter, LogModel.COL_TIME) renderer.props.text = Data.time_args(ts) def _message_cell_data_func(self, column, renderer, model, tree_iter): offset = model.get_value(tree_iter, LogModel.COL_MESSAGE_OFFSET) self.log_file.seek(offset) renderer.props.text = strip_escape(self.log_file.readline().strip()) def set_log_file(self, filename): if self.log_file is not None: for feature in self.features: feature.handle_detach_log_file(self, self.log_file) if filename is None: if self.dispatcher is not None: self.dispatcher.cancel() self.dispatcher = None self.log_file = None self.actions.groups["RowActions"].props.sensitive = False else: self.logger.debug("setting log file %r", filename) try: self.setup_model(LazyLogModel()) self.dispatcher = Common.Data.GSourceDispatcher() self.log_file = Data.LogFile(filename, self.dispatcher) except EnvironmentError as exc: try: file_size = os.path.getsize(filename) except EnvironmentError: pass else: if file_size == 0: # Trying to mmap an empty file results in an invalid # argument error. self.show_error(_("Could not open file"), _("The selected file is empty")) return self.handle_environment_error(exc, filename) return basename = os.path.basename(filename) self.gtk_window.props.title = _("%s - GStreamer Debug Viewer") % ( basename, ) self.log_file.consumers.append(self) self.log_file.start_loading() def handle_environment_error(self, exc, filename): self.show_error(_("Could not open file"), str(exc)) def show_error(self, message1, message2): bar = Gtk.InfoBar() bar.props.message_type = Gtk.MessageType.ERROR box = bar.get_content_area() markup = "<b>%s</b> %s" % ( GLib.markup_escape_text(message1), GLib.markup_escape_text(message2), ) label = Gtk.Label() label.props.use_markup = True label.props.label = markup label.props.selectable = True box.pack_start(label, False, False, 0) self.show_info(bar) def handle_load_started(self): self.logger.debug("load has started") self.progress_dialog = ProgressDialog(self, _("Loading log file")) self.show_info(self.progress_dialog.widget) self.progress_dialog.handle_cancel = self.handle_load_progress_dialog_cancel self.update_progress_id = GObject.timeout_add( 250, self.update_load_progress) self.set_sensitive(False) def handle_load_progress_dialog_cancel(self): self.actions.cancel_load.activate() def update_load_progress(self): if self.progress_dialog is None: self.logger.debug( "progress dialog is gone, removing progress update timeout") self.update_progress_id = None return False progress = self.log_file.get_load_progress() self.progress_dialog.update(progress) return True def handle_load_finished(self): self.logger.debug("load has finshed") self.hide_info() self.progress_dialog = None self.log_model.set_log(self.log_file) self.log_filter.reset() self.actions.reload_file.props.sensitive = True self.actions.groups["RowActions"].props.sensitive = True self.actions.show_hidden_lines.props.sensitive = False self.set_sensitive(True) if len(self.log_model) == 0: self.show_error(_("The file does not contain any parsable lines."), _("It is not a GStreamer log file.")) def idle_set(): self.logger.debug("idle trigger after load finished") self.log_view.set_model(self.log_filter) self.line_view.handle_attach_log_file(self) for feature in self.features: feature.handle_attach_log_file(self, self.log_file) if len(self.log_filter): sel = self.log_view.get_selection() sel.select_path((0, )) return False GObject.idle_add(idle_set)
def setup_model (self, model): self.log_model = model self.log_filter = FilteredLogModel (self.log_model) self.log_filter.handle_process_finished = self.handle_log_filter_process_finished
class Window (object): def __init__ (self, app): self.logger = logging.getLogger ("ui.window") self.app = app self.dispatcher = None self.info_widget = None self.progress_dialog = None self.update_progress_id = None self.window_state = Common.GUI.WindowState () self.column_manager = ViewColumnManager (app.state_section) self.actions = Common.GUI.Actions () group = Gtk.ActionGroup ("MenuActions") group.add_actions ([("AppMenuAction", None, _("_Application")), ("ViewMenuAction", None, _("_View")), ("ViewColumnsMenuAction", None, _("_Columns")), ("HelpMenuAction", None, _("_Help")), ("LineViewContextMenuAction", None, "")]) self.actions.add_group (group) group = Gtk.ActionGroup ("WindowActions") group.add_actions ([("new-window", Gtk.STOCK_NEW, _("_New Window"), "<Ctrl>N"), ("open-file", Gtk.STOCK_OPEN, _("_Open File"), "<Ctrl>O"), ("reload-file", Gtk.STOCK_REFRESH, _("_Reload File"), "<Ctrl>R"), ("close-window", Gtk.STOCK_CLOSE, _("Close _Window"), "<Ctrl>W"), ("cancel-load", Gtk.STOCK_CANCEL, None,), ("clear-line-view", Gtk.STOCK_CLEAR, None), ("show-about", None, _("About GStreamer Debug Viewer",)), ("enlarge-text", Gtk.STOCK_ZOOM_IN, _("Enlarge Text"), "<Ctrl>plus"), ("shrink-text", Gtk.STOCK_ZOOM_OUT, _("Shrink Text"), "<Ctrl>minus"), ("reset-text", Gtk.STOCK_ZOOM_100, _("Normal Text Size"), "<Ctrl>0")]) self.actions.add_group (group) self.actions.reload_file.props.sensitive = False group = Gtk.ActionGroup ("RowActions") group.add_actions ([("hide-before-line", None, _("Hide lines before this point")), ("hide-after-line", None, _("Hide lines after this point")), ("show-hidden-lines", None, _("Show hidden lines")), ("edit-copy-line", Gtk.STOCK_COPY, _("Copy line"), "<Ctrl>C"), ("edit-copy-message", Gtk.STOCK_COPY, _("Copy message"), ""), ("set-base-time", None, _("Set base time")), ("hide-log-level", None, _("Hide log level")), ("hide-log-level-and-above", None, _("Hide this log level and above")), ("show-only-log-level", None, _("Show only log level")), ("hide-log-category", None, _("Hide log category")), ("show-only-log-category", None, _("Show only log category")), ("hide-log-object", None, _("Hide object")), ("show-only-log-object", None, _("Show only object")), ("hide-filename", None, _("Hide filename")), ("show-only-filename", None, _("Show only filename"))]) group.props.sensitive = False self.actions.add_group (group) self.actions.add_group (self.column_manager.action_group) self.log_file = None self.log_model = None self.log_filter = None self.widget_factory = Common.GUI.WidgetFactory (Main.Paths.data_dir) self.widgets = self.widget_factory.make ("main-window.ui", "main_window") ui_filename = os.path.join (Main.Paths.data_dir, "menus.ui") self.ui_factory = Common.GUI.UIFactory (ui_filename, self.actions) self.ui_manager = ui = self.ui_factory.make () menubar = ui.get_widget ("/ui/menubar") self.widgets.vbox_main.pack_start (menubar, False, False, 0) self.gtk_window = self.widgets.main_window self.gtk_window.add_accel_group (ui.get_accel_group ()) self.log_view = self.widgets.log_view self.log_view.drag_dest_unset () self.log_view.set_search_column (-1) sel = self.log_view.get_selection () sel.connect ("changed", self.handle_log_view_selection_changed) self.view_popup = ui.get_widget ("/ui/context/LogViewContextMenu").get_submenu () Common.GUI.widget_add_popup_menu (self.log_view, self.view_popup) # Widgets to set insensitive when the window is considered as # such. This is done during loading/filtering, where we can't set the # whole window insensitive because the progress info bar should be # usable to allow cancellation. self.main_sensitivity = [menubar] self.main_sensitivity.extend (self.widgets.vbox_main.get_children ()) self.line_view = LineView () self.attach () self.column_manager.attach (self.log_view) def setup_model (self, model): self.log_model = model self.log_filter = FilteredLogModel (self.log_model) self.log_filter.handle_process_finished = self.handle_log_filter_process_finished def get_top_attach_point (self): return self.widgets.vbox_main def get_side_attach_point (self): return self.widgets.hbox_view def attach (self): self.zoom_level = 0 zoom_percent = self.app.state_section.zoom_level if zoom_percent: self.restore_zoom (float (zoom_percent) / 100.) self.window_state.attach (window = self.gtk_window, state = self.app.state_section) self.clipboard = Gtk.Clipboard.get_for_display (self.gtk_window.get_display (), Gdk.SELECTION_CLIPBOARD) for action_name, handler in iter_actions (self): action = getattr (self.actions, action_name) action.connect ("activate", handler) self.gtk_window.connect ("delete-event", self.handle_window_delete_event) self.features = [] for plugin_feature in self.app.iter_plugin_features (): feature = plugin_feature (self.app) self.features.append (feature) for feature in self.features: feature.handle_attach_window (self) # FIXME: With multiple selection mode, browsing the list with key # up/down slows to a crawl! WTF is wrong with this stupid widget??? sel = self.log_view.get_selection () sel.set_mode (Gtk.SelectionMode.BROWSE) self.line_view.attach (self) # Do not translate; fallback application name for e.g. gnome-shell if # the desktop file is not installed: self.gtk_window.set_wmclass ("gst-debug-viewer", "GStreamer Debug Viewer") self.gtk_window.show () def detach (self): self.set_log_file (None) for feature in self.features: feature.handle_detach_window (self) self.window_state.detach () self.column_manager.detach () def get_active_line_index (self): selection = self.log_view.get_selection () model, tree_iter = selection.get_selected () if tree_iter is None: raise ValueError ("no line selected") path = model.get_path (tree_iter) return path[0] def get_active_line (self): selection = self.log_view.get_selection () model, tree_iter = selection.get_selected () if tree_iter is None: raise ValueError ("no line selected") model = self.log_view.get_model () return model.get (tree_iter, *LogModelBase.column_ids) def close (self, *a, **kw): self.logger.debug ("closing window, detaching") self.detach () self.gtk_window.hide () self.logger.debug ("requesting close from app") self.app.close_window (self) def push_view_state (self): self.default_index = None self.default_start_index = None model = self.log_view.get_model () if model is None: return try: line_index = self.get_active_line_index () except ValueError: super_index = None self.logger.debug ("no line selected") else: super_index = model.line_index_to_super (line_index) self.logger.debug ("pushing selected line %i (abs %i)", line_index, super_index) self.default_index = super_index vis_range = self.log_view.get_visible_range () if vis_range is not None: start_path, end_path = vis_range start_index = start_path[0] self.default_start_index = model.line_index_to_super (start_index) def update_model (self, model = None): if model is None: model = self.log_view.get_model () previous_model = self.log_view.get_model () if previous_model == model: # Force update. self.log_view.set_model (None) self.log_view.set_model (model) def pop_view_state (self, scroll_to_selection = False): model = self.log_view.get_model () if model is None: return selected_index = self.default_index start_index = self.default_start_index if selected_index is not None: try: select_index = model.line_index_from_super (selected_index) except IndexError as exc: self.logger.debug ("abs line index %i filtered out, not reselecting", selected_index) else: assert select_index >= 0 sel = self.log_view.get_selection () path = (select_index,) sel.select_path (path) if start_index is None or scroll_to_selection: self.log_view.scroll_to_cell (path, use_align = True, row_align = .5) if start_index is not None and not scroll_to_selection: def traverse (): for i in xrange (start_index, len (model)): yield i for i in xrange (start_index - 1, 0, -1): yield i for current_index in traverse (): try: target_index = model.line_index_from_super (current_index) except IndexError: continue else: path = (target_index,) self.log_view.scroll_to_cell (path, use_align = True, row_align = 0.) break def update_view (self): view = self.log_view model = view.get_model () start_path, end_path = view.get_visible_range () start_index, end_index = start_path[0], end_path[0] for line_index in range (start_index, end_index + 1): path = (line_index,) tree_iter = model.get_iter (path) model.row_changed (path, tree_iter) def handle_log_view_selection_changed (self, selection): try: line_index = self.get_active_line_index () except ValueError: first_selected = True last_selected = True else: first_selected = (line_index == 0) last_selected = (line_index == len (self.log_view.get_model ()) - 1) self.actions.hide_before_line.props.sensitive = not first_selected self.actions.hide_after_line.props.sensitive = not last_selected def handle_window_delete_event (self, window, event): self.actions.close_window.activate () return True @action def handle_new_window_action_activate (self, action): self.app.open_window () @action def handle_open_file_action_activate (self, action): dialog = Gtk.FileChooserDialog (None, self.gtk_window, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT,)) response = dialog.run () dialog.hide () if response == Gtk.ResponseType.ACCEPT: self.set_log_file (dialog.get_filename ()) dialog.destroy () @action def handle_reload_file_action_activate (self, action): if self.log_file is None: return self.set_log_file (self.log_file.path) @action def handle_cancel_load_action_activate (self, action): self.logger.debug ("cancelling data load") self.set_log_file (None) if self.progress_dialog is not None: self.hide_info () self.progress_dialog = None if self.update_progress_id is not None: GObject.source_remove (self.update_progress_id) self.update_progress_id = None self.set_sensitive (True) @action def handle_close_window_action_activate (self, action): self.close () @action def handle_hide_after_line_action_activate (self, action): self.hide_range (after = True) @action def handle_hide_before_line_action_activate (self, action): self.hide_range (after = False) def hide_range (self, after): model = self.log_view.get_model () try: filtered_line_index = self.get_active_line_index () except ValueError: return if after: first_index = model.line_index_to_super (0) last_index = model.line_index_to_super (filtered_line_index) self.logger.info ("hiding lines after %i (abs %i), first line is abs %i", filtered_line_index, last_index, first_index) else: first_index = model.line_index_to_super (filtered_line_index) last_index = model.line_index_to_super (len (model) - 1) self.logger.info ("hiding lines before %i (abs %i), last line is abs %i", filtered_line_index, first_index, last_index) self.push_view_state () start_index = first_index stop_index = last_index + 1 self.log_filter.set_range (start_index, stop_index) self.update_model () self.pop_view_state () self.actions.show_hidden_lines.props.sensitive = True @action def handle_show_hidden_lines_action_activate (self, action): self.logger.info ("restoring model filter to show all lines") self.push_view_state () self.log_view.set_model (None) self.log_filter.reset () self.update_model (self.log_filter) self.pop_view_state (scroll_to_selection = True) self.actions.show_hidden_lines.props.sensitive = False @action def handle_edit_copy_line_action_activate (self, action): line_index = self.get_active_line_index () model = self.log_view.get_model () line_offset = model.line_offsets[line_index] line_text = model.access_offset (line_offset).strip () line_text = Data.strip_escape (line_text) self.clipboard.set_text (line_text) @action def handle_edit_copy_message_action_activate (self, action): col_id = LogModelBase.COL_MESSAGE self.clipboard.set_text (self.get_active_line ()[col_id]) @action def handle_enlarge_text_action_activate (self, action): self.update_zoom_level (1) @action def handle_shrink_text_action_activate (self, action): self.update_zoom_level (-1) @action def handle_reset_text_action_activate (self, action): self.update_zoom_level (-self.zoom_level) def restore_zoom (self, scale): from math import log self.zoom_level = int (round (log (scale) / log (ZOOM_FACTOR))) self.column_manager.set_zoom (scale) def update_zoom_level (self, delta_step): if not delta_step: return self.zoom_level += delta_step scale = ZOOM_FACTOR ** self.zoom_level self.column_manager.set_zoom (scale) self.app.state_section.zoom_level = int (round (scale * 100.)) def set_sensitive (self, sensitive): for widget in self.main_sensitivity: widget.props.sensitive = sensitive def show_info (self, widget): self.hide_info () box = self.widgets.vbox_main box.pack_start (widget, False, False, 0) box.reorder_child (widget, 2) widget.show_all () self.info_widget = widget def hide_info (self): if self.info_widget is None: return self.info_widget.destroy () self.info_widget = None def add_model_filter (self, filter): self.progress_dialog = ProgressDialog (self, _("Filtering")) self.show_info (self.progress_dialog.widget) self.progress_dialog.handle_cancel = self.handle_filter_progress_dialog_cancel dispatcher = Common.Data.GSourceDispatcher () self.filter_dispatcher = dispatcher # FIXME: Unsetting the model to keep e.g. the dispatched timeline # sentinel from collecting data while we filter idly, which slows # things down for nothing. self.push_view_state () self.log_view.set_model (None) self.log_filter.add_filter (filter, dispatcher = dispatcher) GObject.timeout_add (250, self.update_filter_progress) self.set_sensitive (False) def update_filter_progress (self): if self.progress_dialog is None: return False try: progress = self.log_filter.get_filter_progress () except ValueError: self.logger.warning ("no filter process running") return False self.progress_dialog.update (progress) return True def handle_filter_progress_dialog_cancel (self): self.hide_info () self.progress_dialog = None self.log_filter.abort_process () self.log_view.set_model (self.log_filter) self.pop_view_state () self.set_sensitive (True) def handle_log_filter_process_finished (self): self.hide_info () self.progress_dialog = None # No push_view_state here, did this in add_model_filter. self.update_model (self.log_filter) self.pop_view_state () self.actions.show_hidden_lines.props.sensitive = True self.set_sensitive (True) @action def handle_set_base_time_action_activate (self, action): row = self.get_active_line () self.column_manager.set_base_time (row[LogModelBase.COL_TIME]) @action def handle_hide_log_level_action_activate (self, action): row = self.get_active_line () debug_level = row[LogModelBase.COL_LEVEL] self.add_model_filter (DebugLevelFilter (debug_level)) @action def handle_hide_log_category_action_activate (self, action): row = self.get_active_line () category = row[LogModelBase.COL_CATEGORY] self.add_model_filter (CategoryFilter (category)) @action def handle_hide_log_object_action_activate (self, action): row = self.get_active_line () object_ = row[LogModelBase.COL_OBJECT] self.add_model_filter (ObjectFilter (object_)) @action def handle_hide_filename_action_activate (self, action): row = self.get_active_line () filename = row[LogModelBase.COL_FILENAME] self.add_model_filter (FilenameFilter (filename)) @action def handle_hide_log_level_and_above_action_activate (self, action): row = self.get_active_line () debug_level = row[LogModelBase.COL_LEVEL] self.add_model_filter (DebugLevelFilter (debug_level, DebugLevelFilter.this_and_above)) @action def handle_show_only_log_level_action_activate (self, action): row = self.get_active_line () debug_level = row[LogModelBase.COL_LEVEL] self.add_model_filter (DebugLevelFilter (debug_level, DebugLevelFilter.all_but_this)) @action def handle_show_only_log_category_action_activate (self, action): row = self.get_active_line () category = row[LogModelBase.COL_CATEGORY] self.add_model_filter (CategoryFilter (category, True)) @action def handle_show_only_log_object_action_activate (self, action): row = self.get_active_line () object_ = row[LogModelBase.COL_OBJECT] self.add_model_filter (ObjectFilter (object_, True)) @action def handle_show_only_filename_action_activate (self, action): row = self.get_active_line () filename = row[LogModelBase.COL_FILENAME] self.add_model_filter (FilenameFilter (filename, True)) @action def handle_show_about_action_activate (self, action): from GstDebugViewer import version dialog = self.widget_factory.make_one ("about-dialog.ui", "about_dialog") dialog.props.version = version dialog.run () dialog.destroy () @staticmethod def _timestamp_cell_data_func (column, renderer, model, tree_iter): ts = model.get_value (tree_iter, LogModel.COL_TIME) renderer.props.text = Data.time_args (ts) def _message_cell_data_func (self, column, renderer, model, tree_iter): offset = model.get_value (tree_iter, LogModel.COL_MESSAGE_OFFSET) self.log_file.seek (offset) renderer.props.text = strip_escape (self.log_file.readline ().strip ()) def set_log_file (self, filename): if self.log_file is not None: for feature in self.features: feature.handle_detach_log_file (self, self.log_file) if filename is None: if self.dispatcher is not None: self.dispatcher.cancel () self.dispatcher = None self.log_file = None self.actions.groups["RowActions"].props.sensitive = False else: self.logger.debug ("setting log file %r", filename) try: self.setup_model (LazyLogModel ()) self.dispatcher = Common.Data.GSourceDispatcher () self.log_file = Data.LogFile (filename, self.dispatcher) except EnvironmentError as exc: try: file_size = os.path.getsize (filename) except EnvironmentError: pass else: if file_size == 0: # Trying to mmap an empty file results in an invalid # argument error. self.show_error (_("Could not open file"), _("The selected file is empty")) return self.handle_environment_error (exc, filename) return basename = os.path.basename (filename) self.gtk_window.props.title = _("%s - GStreamer Debug Viewer") % (basename,) self.log_file.consumers.append (self) self.log_file.start_loading () def handle_environment_error (self, exc, filename): self.show_error (_("Could not open file"), str (exc)) def show_error (self, message1, message2): bar = Gtk.InfoBar () bar.props.message_type = Gtk.MessageType.ERROR box = bar.get_content_area () markup = "<b>%s</b> %s" % (GLib.markup_escape_text (message1), GLib.markup_escape_text (message2),) label = Gtk.Label () label.props.use_markup = True label.props.label = markup label.props.selectable = True box.pack_start (label, False, False, 0) self.show_info (bar) def handle_load_started (self): self.logger.debug ("load has started") self.progress_dialog = ProgressDialog (self, _("Loading log file")) self.show_info (self.progress_dialog.widget) self.progress_dialog.handle_cancel = self.handle_load_progress_dialog_cancel self.update_progress_id = GObject.timeout_add (250, self.update_load_progress) self.set_sensitive (False) def handle_load_progress_dialog_cancel (self): self.actions.cancel_load.activate () def update_load_progress (self): if self.progress_dialog is None: self.logger.debug ("progress dialog is gone, removing progress update timeout") self.update_progress_id = None return False progress = self.log_file.get_load_progress () self.progress_dialog.update (progress) return True def handle_load_finished (self): self.logger.debug ("load has finshed") self.hide_info () self.progress_dialog = None self.log_model.set_log (self.log_file) self.log_filter.reset () self.actions.reload_file.props.sensitive = True self.actions.groups["RowActions"].props.sensitive = True self.actions.show_hidden_lines.props.sensitive = False self.set_sensitive (True) if len (self.log_model) == 0: self.show_error (_("The file does not contain any parsable lines."), _("It is not a GStreamer log file.")) def idle_set (): self.logger.debug ("idle trigger after load finished") self.log_view.set_model (self.log_filter) self.line_view.handle_attach_log_file (self) for feature in self.features: feature.handle_attach_log_file (self, self.log_file) if len (self.log_filter): sel = self.log_view.get_selection () sel.select_path ((0,)) return False GObject.idle_add (idle_set)
def test_random_filtered_range_refilter(self): full_model = Model() ranged_model = RangeFilteredLogModel(full_model) # FIXME: Call to .reset should not be needed. ranged_model.reset() filtered_model = FilteredLogModel(ranged_model) row_list = self.__row_list self.assertEquals(row_list(full_model), range(20)) self.assertEquals(row_list(ranged_model), range(20)) self.assertEquals(row_list(filtered_model), range(20)) filtered_model.add_filter(RandomFilter(538295943), Common.Data.DefaultDispatcher()) random_rows = row_list(filtered_model) self.__dump_model(filtered_model) ranged_model.set_range(10, 20) self.__dump_model(ranged_model, "ranged_model (10, 20)") self.assertEquals(row_list(ranged_model), range(10, 20)) filtered_model.super_model_changed_range() self.__dump_model(filtered_model) self.assertEquals(row_list(filtered_model), [x for x in range(10, 20) if x in random_rows]) ranged_model.set_range(0, 20) self.assertEquals(row_list(ranged_model), range(0, 20)) ranged_model = RangeFilteredLogModel(full_model) # FIXME: Call to .reset should not be needed. ranged_model.reset() filtered_model = FilteredLogModel(ranged_model) filtered_model.add_filter(RandomFilter(538295943), Common.Data.DefaultDispatcher()) self.__dump_model(filtered_model, "filtered model") self.assertEquals(row_list(filtered_model), random_rows) ranged_model.set_range(0, 10) self.__dump_model(ranged_model, "ranged model (0, 10)") filtered_model.super_model_changed_range() self.assertEquals(row_list(ranged_model), range(0, 10)) self.__dump_model(filtered_model) self.assertEquals(row_list(filtered_model), [x for x in range(0, 10) if x in random_rows])
def test_filtered_range_refilter(self): full_model = Model() ranged_model = RangeFilteredLogModel(full_model) # FIXME: Call to .reset should not be needed. ranged_model.reset() filtered_model = FilteredLogModel(ranged_model) row_list = self.__row_list rows = row_list(full_model) rows_ranged = row_list(ranged_model) rows_filtered = row_list(filtered_model) self.__dump_model(full_model, "full model") ## self.__dump_model (ranged_model, "ranged model") ## self.__dump_model (filtered_model, "filtered model") self.assertEquals(rows, rows_ranged) self.assertEquals(rows, rows_filtered) self.assertEquals( [ranged_model.line_index_from_super(i) for i in range(20)], range(20)) self.assertEquals( [ranged_model.line_index_to_super(i) for i in range(20)], range(20)) self.assertEquals( [ranged_model.line_index_from_top(i) for i in range(20)], range(20)) self.assertEquals( [ranged_model.line_index_to_top(i) for i in range(20)], range(20)) self.assertEquals( [filtered_model.line_index_from_super(i) for i in range(20)], range(20)) self.assertEquals( [filtered_model.line_index_to_super(i) for i in range(20)], range(20)) self.assertEquals( [filtered_model.line_index_from_top(i) for i in range(20)], range(20)) self.assertEquals( [filtered_model.line_index_to_top(i) for i in range(20)], range(20)) ranged_model.set_range(5, 16) self.__dump_model(ranged_model, "ranged model (5, 16)") filtered_model.super_model_changed_range() rows_ranged = row_list(ranged_model) self.assertEquals(rows_ranged, range(5, 16)) self.__dump_model(filtered_model, "filtered model (nofilter, 5, 15)") rows_filtered = row_list(filtered_model) self.assertEquals(rows_ranged, rows_filtered) self.assertEquals( [ranged_model.line_index_from_super(i) for i in range(5, 16)], range(11)) self.assertEquals( [ranged_model.line_index_to_super(i) for i in range(11)], range(5, 16)) self.assertEquals( [ranged_model.line_index_from_top(i) for i in range(5, 16)], range(11)) self.assertEquals( [ranged_model.line_index_to_top(i) for i in range(11)], range(5, 16)) self.assertEquals( [filtered_model.line_index_from_super(i) for i in range(11)], range(11)) self.assertEquals( [filtered_model.line_index_to_super(i) for i in range(11)], range(11)) self.assertEquals( [filtered_model.line_index_from_top(i) for i in range(5, 16)], range(11)) self.assertEquals( [filtered_model.line_index_to_top(i) for i in range(11)], range(5, 16)) filtered_model.add_filter(CategoryFilter("EVEN"), Common.Data.DefaultDispatcher()) rows_filtered = row_list(filtered_model) self.assertEquals(rows_filtered, range(5, 16, 2)) self.__dump_model(filtered_model, "filtered model") self.assertEquals( [filtered_model.line_index_from_super(i) for i in range(0, 11, 2)], range(6)) self.assertEquals( [filtered_model.line_index_from_top(i) for i in range(5, 16, 2)], range(6)) ranged_model.set_range(7, 13) self.__dump_model(ranged_model, "ranged model (7, 13)") filtered_model.super_model_changed_range() self.assertEquals(row_list(ranged_model), range(7, 13)) self.assertEquals( [ranged_model.line_index_from_super(i) for i in range(7, 13)], range(6)) self.assertEquals( [ranged_model.line_index_to_super(i) for i in range(6)], range(7, 13)) self.assertEquals( [ranged_model.line_index_from_top(i) for i in range(7, 13)], range(6)) self.assertEquals( [ranged_model.line_index_to_top(i) for i in range(6)], range(7, 13)) self.__dump_model(filtered_model, "filtered model (ranged 7, 12)") self.assertEquals( [filtered_model.line_index_from_super(i) for i in range(0, 6, 2)], range(3)) self.assertEquals( [filtered_model.line_index_to_super(i) for i in range(3)], range(0, 6, 2)) self.assertEquals( [filtered_model.line_index_from_top(i) for i in range(7, 12, 2)], range(3)) self.assertEquals( [filtered_model.line_index_to_top(i) for i in range(3)], range(7, 12, 2)) rows_filtered = row_list(filtered_model) self.assertEquals(rows_filtered, range(7, 13, 2))
def test_filtered_range_refilter_skip(self): full_model = Model() ranged_model = RangeFilteredLogModel(full_model) # FIXME: Call to .reset should not be needed. ranged_model.reset() filtered_model = FilteredLogModel(ranged_model) row_list = self.__row_list filtered_model.add_filter(CategoryFilter("EVEN"), Common.Data.DefaultDispatcher()) self.__dump_model(filtered_model, "filtered") self.assertEquals(row_list(filtered_model), range(1, 20, 2)) self.assertEquals( [filtered_model.line_index_from_super(i) for i in range(1, 20, 2)], range(10)) self.assertEquals( [filtered_model.line_index_to_super(i) for i in range(10)], range(1, 20, 2)) self.assertEquals( [filtered_model.line_index_from_top(i) for i in range(1, 20, 2)], range(10)) self.assertEquals( [filtered_model.line_index_to_top(i) for i in range(10)], range(1, 20, 2)) ranged_model.set_range(1, 20) self.__dump_model(ranged_model, "ranged (1, 20)") filtered_model.super_model_changed_range() self.__dump_model(filtered_model, "filtered range") self.assertEquals( [filtered_model.line_index_from_super(i) for i in range(0, 19, 2)], range(10)) self.assertEquals( [filtered_model.line_index_to_super(i) for i in range(10)], range(0, 19, 2)) self.assertEquals( [filtered_model.line_index_from_top(i) for i in range(1, 20, 2)], range(10)) self.assertEquals( [filtered_model.line_index_to_top(i) for i in range(10)], range(1, 20, 2)) ranged_model.set_range(2, 20) self.__dump_model(ranged_model, "ranged (2, 20)") filtered_model.super_model_changed_range() self.__dump_model(filtered_model, "filtered range") self.assertEquals(row_list(filtered_model), range(3, 20, 2)) self.assertEquals( [filtered_model.line_index_from_super(i) for i in range(1, 18, 2)], range(9)) self.assertEquals( [filtered_model.line_index_to_super(i) for i in range(9)], range(1, 18, 2)) self.assertEquals( [filtered_model.line_index_from_top(i) for i in range(3, 20, 2)], range(9)) self.assertEquals( [filtered_model.line_index_to_top(i) for i in range(9)], range(3, 20, 2))