1 # -*- coding: utf-8; mode: python; -*-
3 # GStreamer Debug Viewer - View and analyze GStreamer debug log files
5 # Copyright (C) 2007 René Stadler <mail@renestadler.de>
7 # This program is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by the Free
9 # Software Foundation; either version 3 of the License, or (at your option)
12 # This program is distributed in the hope that it will be useful, but WITHOUT
13 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 # You should have received a copy of the GNU General Public License along with
18 # this program. If not, see <http://www.gnu.org/licenses/>.
20 """GStreamer Debug Viewer GUI module."""
29 from bisect import bisect_right, bisect_left
32 from gi.repository import GObject
33 from gi.repository import Gtk
34 from gi.repository import Gdk
35 from gi.repository import GLib
37 from GstDebugViewer import Common, Data, Main
38 from GstDebugViewer.GUI.columns import LineViewColumnManager, ViewColumnManager
39 from GstDebugViewer.GUI.filters import (CategoryFilter,
45 from GstDebugViewer.GUI.models import (FilteredLogModel,
53 func.is_action_handler = True
58 def iter_actions(manager):
61 it = cls.__dict__.iteritems()
62 for name, member in it:
64 member.is_action_handler
65 except AttributeError:
68 bound_method = getattr(manager, name)
70 assert name.startswith("handle_")
71 assert name.endswith("_action_activate")
72 action_name = name[len("handle_"):-len("_action_activate")]
73 action_name = action_name.replace("_", "-")
75 yield (action_name, bound_method,)
78 class LineView (object):
82 self.column_manager = LineViewColumnManager()
84 def attach(self, window):
86 for action_name, handler in iter_actions(self):
87 action = getattr(window.actions, action_name)
88 action.connect("activate", handler)
90 self.clear_action = window.actions.clear_line_view
92 self.line_view = window.widgets.line_view
93 self.line_view.connect(
94 "row-activated", self.handle_line_view_row_activated)
96 ui = window.ui_manager
97 self.popup = ui.get_widget(
98 "/ui/context/LineViewContextMenu").get_submenu()
99 Common.GUI.widget_add_popup_menu(self.line_view, self.popup)
101 self.log_view = log_view = window.log_view
102 log_view.connect("row-activated", self.handle_log_view_row_activated)
103 sel = log_view.get_selection()
104 sel.connect("changed", self.handle_log_view_selection_changed)
106 self.clear_action.props.sensitive = False
107 self.column_manager.attach(window)
111 model = self.line_view.get_model()
116 for i in range(1, len(model)):
119 self.clear_action.props.sensitive = False
121 def handle_attach_log_file(self, window):
123 self.line_view.set_model(LineViewLogModel(window.log_model))
125 def handle_line_view_row_activated(self, view, path, column):
128 line_model = view.get_model()
129 log_model = self.log_view.get_model()
130 super_index = line_model.line_index_to_super(line_index)
131 log_index = log_model.line_index_from_super(super_index)
133 self.log_view.scroll_to_cell(path, use_align=True, row_align=.5)
134 sel = self.log_view.get_selection()
135 sel.select_path(path)
137 def handle_log_view_row_activated(self, view, path, column):
139 log_model = view.get_model()
142 super_index = log_model.line_index_to_super(line_index)
143 line_model = self.line_view.get_model()
144 if line_model is None:
148 timestamps = [row[line_model.COL_TIME] for row in line_model]
149 row = log_model[(line_index,)]
150 position = bisect_right(timestamps, row[line_model.COL_TIME])
153 if len(line_model) > 1:
154 other_index = line_model.line_index_to_super(position - 1)
157 if other_index == super_index and position != 1:
158 # Already have the line.
161 line_model.insert_line(position, super_index)
162 self.clear_action.props.sensitive = True
164 def handle_log_view_selection_changed(self, selection):
166 line_model = self.line_view.get_model()
167 if line_model is None:
170 model, tree_iter = selection.get_selected()
172 if tree_iter is None:
175 path = model.get_path(tree_iter)
176 line_index = model.line_index_to_super(path[0])
178 if len(line_model) == 0:
179 line_model.insert_line(0, line_index)
181 line_model.replace_line(0, line_index)
184 def handle_clear_line_view_action_activate(self, action):
189 class ProgressDialog (object):
191 def __init__(self, window, title=""):
194 bar.props.message_type = Gtk.MessageType.INFO
195 bar.connect("response", self.__handle_info_bar_response)
196 bar.add_button(Gtk.STOCK_CANCEL, 1)
197 area_box = bar.get_content_area()
198 box = Gtk.HBox(spacing=8)
200 box.pack_start(Gtk.Label(label=title), False, False, 0)
202 progress = Gtk.ProgressBar()
203 box.pack_start(progress, False, False, 0)
205 area_box.pack_start(box, False, False, 0)
208 self.__progress_bar = progress
210 def __handle_info_bar_response(self, info_bar, response):
214 def handle_cancel(self):
218 def update(self, progress):
220 if self.__progress_bar is None:
223 self.__progress_bar.props.fraction = progress
226 class Window (object):
228 def __init__(self, app):
230 self.logger = logging.getLogger("ui.window")
233 self.dispatcher = None
234 self.info_widget = None
235 self.progress_dialog = None
236 self.update_progress_id = None
238 self.window_state = Common.GUI.WindowState()
239 self.column_manager = ViewColumnManager(app.state_section)
241 self.actions = Common.GUI.Actions()
243 group = Gtk.ActionGroup("MenuActions")
244 group.add_actions([("AppMenuAction", None, _("_Application")),
245 ("ViewMenuAction", None, _("_View")),
246 ("ViewColumnsMenuAction", None, _("_Columns")),
247 ("HelpMenuAction", None, _("_Help")),
248 ("LineViewContextMenuAction", None, "")])
249 self.actions.add_group(group)
251 group = Gtk.ActionGroup("WindowActions")
253 [("new-window", Gtk.STOCK_NEW, _("_New Window"), "<Ctrl>N"),
254 ("open-file", Gtk.STOCK_OPEN, _(
255 "_Open File"), "<Ctrl>O"),
256 ("reload-file", Gtk.STOCK_REFRESH, _(
257 "_Reload File"), "<Ctrl>R"),
258 ("close-window", Gtk.STOCK_CLOSE, _(
259 "Close _Window"), "<Ctrl>W"),
260 ("cancel-load", Gtk.STOCK_CANCEL, None,),
261 ("clear-line-view", Gtk.STOCK_CLEAR, None),
262 ("show-about", None, _(
263 "About GStreamer Debug Viewer",)),
264 ("enlarge-text", Gtk.STOCK_ZOOM_IN, _(
265 "Enlarge Text"), "<Ctrl>plus"),
266 ("shrink-text", Gtk.STOCK_ZOOM_OUT, _(
267 "Shrink Text"), "<Ctrl>minus"),
268 ("reset-text", Gtk.STOCK_ZOOM_100, _("Normal Text Size"), "<Ctrl>0")])
269 self.actions.add_group(group)
270 self.actions.reload_file.props.sensitive = False
272 group = Gtk.ActionGroup("RowActions")
274 [("hide-before-line", None, _("Hide lines before this point")),
275 ("hide-after-line", None, _(
276 "Hide lines after this point")),
277 ("show-hidden-lines", None, _(
278 "Show hidden lines")),
279 ("edit-copy-line", Gtk.STOCK_COPY, _(
280 "Copy line"), "<Ctrl>C"),
281 ("edit-copy-message", Gtk.STOCK_COPY, _(
282 "Copy message"), ""),
283 ("set-base-time", None, _("Set base time")),
284 ("hide-log-level", None, _("Hide log level")),
285 ("hide-log-level-and-above", None, _(
286 "Hide this log level and above")),
287 ("show-only-log-level", None, _(
288 "Show only log level")),
289 ("hide-log-category", None, _(
290 "Hide log category")),
291 ("show-only-log-category", None, _(
292 "Show only log category")),
293 ("hide-thread", None, _(
295 ("show-only-thread", None, _(
296 "Show only thread")),
297 ("hide-object", None, _("Hide object")),
298 ("show-only-object", None, _(
299 "Show only object")),
300 ("hide-function", None, _("Hide function")),
301 ("show-only-function", None, _(
302 "Show only function")),
303 ("hide-filename", None, _("Hide filename")),
304 ("show-only-filename", None, _("Show only filename"))])
305 group.props.sensitive = False
306 self.actions.add_group(group)
308 self.actions.add_group(self.column_manager.action_group)
311 self.log_model = None
312 self.log_filter = None
314 self.widget_factory = Common.GUI.WidgetFactory(Main.Paths.data_dir)
315 self.widgets = self.widget_factory.make(
316 "main-window.ui", "main_window")
318 ui_filename = os.path.join(Main.Paths.data_dir, "menus.ui")
319 self.ui_factory = Common.GUI.UIFactory(ui_filename, self.actions)
321 self.ui_manager = ui = self.ui_factory.make()
322 menubar = ui.get_widget("/ui/menubar")
323 self.widgets.vbox_main.pack_start(menubar, False, False, 0)
325 self.gtk_window = self.widgets.main_window
326 self.gtk_window.add_accel_group(ui.get_accel_group())
327 self.log_view = self.widgets.log_view
328 self.log_view.drag_dest_unset()
329 self.log_view.set_search_column(-1)
330 sel = self.log_view.get_selection()
331 sel.connect("changed", self.handle_log_view_selection_changed)
333 self.view_popup = ui.get_widget(
334 "/ui/context/LogViewContextMenu").get_submenu()
335 Common.GUI.widget_add_popup_menu(self.log_view, self.view_popup)
337 # Widgets to set insensitive when the window is considered as
338 # such. This is done during loading/filtering, where we can't set the
339 # whole window insensitive because the progress info bar should be
340 # usable to allow cancellation.
341 self.main_sensitivity = [menubar]
342 self.main_sensitivity.extend(self.widgets.vbox_main.get_children())
344 self.line_view = LineView()
347 self.column_manager.attach(self.log_view)
349 def setup_model(self, model):
351 self.log_model = model
352 self.log_filter = FilteredLogModel(self.log_model)
353 self.log_filter.handle_process_finished = self.handle_log_filter_process_finished
355 def get_top_attach_point(self):
357 return self.widgets.vbox_main
359 def get_side_attach_point(self):
361 return self.widgets.hbox_view
366 zoom_percent = self.app.state_section.zoom_level
368 self.restore_zoom(float(zoom_percent) / 100.)
370 self.window_state.attach(window=self.gtk_window,
371 state=self.app.state_section)
373 self.clipboard = Gtk.Clipboard.get_for_display(
374 self.gtk_window.get_display(),
375 Gdk.SELECTION_CLIPBOARD)
377 for action_name, handler in iter_actions(self):
378 action = getattr(self.actions, action_name)
379 action.connect("activate", handler)
381 self.gtk_window.connect(
382 "delete-event", self.handle_window_delete_event)
386 for plugin_feature in self.app.iter_plugin_features():
387 feature = plugin_feature(self.app)
388 self.features.append(feature)
390 for feature in self.features:
391 feature.handle_attach_window(self)
393 # FIXME: With multiple selection mode, browsing the list with key
394 # up/down slows to a crawl! WTF is wrong with this stupid widget???
395 sel = self.log_view.get_selection()
396 sel.set_mode(Gtk.SelectionMode.BROWSE)
398 self.line_view.attach(self)
400 # Do not translate; fallback application name for e.g. gnome-shell if
401 # the desktop file is not installed:
402 self.gtk_window.set_wmclass(
403 "gst-debug-viewer", "GStreamer Debug Viewer")
405 self.gtk_window.show()
409 self.set_log_file(None)
410 for feature in self.features:
411 feature.handle_detach_window(self)
413 self.window_state.detach()
414 self.column_manager.detach()
416 def get_active_line_index(self):
418 selection = self.log_view.get_selection()
419 model, tree_iter = selection.get_selected()
420 if tree_iter is None:
421 raise ValueError("no line selected")
422 path = model.get_path(tree_iter)
425 def get_active_line(self):
427 selection = self.log_view.get_selection()
428 model, tree_iter = selection.get_selected()
429 if tree_iter is None:
430 raise ValueError("no line selected")
431 model = self.log_view.get_model()
432 return model.get(tree_iter, *LogModelBase.column_ids)
434 def close(self, *a, **kw):
436 self.logger.debug("closing window, detaching")
438 self.gtk_window.hide()
439 self.logger.debug("requesting close from app")
440 self.app.close_window(self)
442 def push_view_state(self):
444 self.default_index = None
445 self.default_start_index = None
447 model = self.log_view.get_model()
452 line_index = self.get_active_line_index()
455 self.logger.debug("no line selected")
457 super_index = model.line_index_to_super(line_index)
458 self.logger.debug("pushing selected line %i (abs %i)",
459 line_index, super_index)
461 self.default_index = super_index
463 vis_range = self.log_view.get_visible_range()
464 if vis_range is not None:
465 start_path, end_path = vis_range
466 start_index = start_path[0]
467 self.default_start_index = model.line_index_to_super(start_index)
469 def update_model(self, model=None):
472 model = self.log_view.get_model()
474 previous_model = self.log_view.get_model()
476 if previous_model == model:
478 self.log_view.set_model(None)
479 self.log_view.set_model(model)
481 def pop_view_state(self, scroll_to_selection=False):
483 model = self.log_view.get_model()
487 selected_index = self.default_index
488 start_index = self.default_start_index
490 if selected_index is not None:
493 select_index = model.line_index_from_super(selected_index)
494 except IndexError as exc:
496 "abs line index %i filtered out, not reselecting",
499 assert select_index >= 0
500 sel = self.log_view.get_selection()
501 path = (select_index,)
502 sel.select_path(path)
504 if start_index is None or scroll_to_selection:
505 self.log_view.scroll_to_cell(
506 path, use_align=True, row_align=.5)
508 if start_index is not None and not scroll_to_selection:
511 for i in xrange(start_index, len(model)):
513 for i in xrange(start_index - 1, 0, -1):
515 for current_index in traverse():
517 target_index = model.line_index_from_super(current_index)
521 path = (target_index,)
522 self.log_view.scroll_to_cell(
523 path, use_align=True, row_align=0.)
526 def update_view(self):
529 model = view.get_model()
531 start_path, end_path = view.get_visible_range()
532 start_index, end_index = start_path[0], end_path[0]
534 for line_index in range(start_index, end_index + 1):
536 tree_iter = model.get_iter(path)
537 model.row_changed(path, tree_iter)
539 def handle_log_view_selection_changed(self, selection):
542 line_index = self.get_active_line_index()
544 first_selected = True
547 first_selected = (line_index == 0)
549 line_index == len(self.log_view.get_model()) - 1)
551 self.actions.hide_before_line.props.sensitive = not first_selected
552 self.actions.hide_after_line.props.sensitive = not last_selected
554 def handle_window_delete_event(self, window, event):
556 self.actions.close_window.activate()
561 def handle_new_window_action_activate(self, action):
563 self.app.open_window()
566 def handle_open_file_action_activate(self, action):
568 dialog = Gtk.FileChooserDialog(None, self.gtk_window,
569 Gtk.FileChooserAction.OPEN,
570 (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
571 Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT,))
572 response = dialog.run()
574 if response == Gtk.ResponseType.ACCEPT:
575 self.set_log_file(dialog.get_filename())
579 def handle_reload_file_action_activate(self, action):
581 if self.log_file is None:
584 self.set_log_file(self.log_file.path)
587 def handle_cancel_load_action_activate(self, action):
589 self.logger.debug("cancelling data load")
591 self.set_log_file(None)
593 if self.progress_dialog is not None:
595 self.progress_dialog = None
596 if self.update_progress_id is not None:
597 GObject.source_remove(self.update_progress_id)
598 self.update_progress_id = None
600 self.set_sensitive(True)
603 def handle_close_window_action_activate(self, action):
608 def handle_hide_after_line_action_activate(self, action):
610 self.hide_range(after=True)
613 def handle_hide_before_line_action_activate(self, action):
615 self.hide_range(after=False)
617 def hide_range(self, after):
619 model = self.log_view.get_model()
621 filtered_line_index = self.get_active_line_index()
626 first_index = model.line_index_to_super(0)
627 last_index = model.line_index_to_super(filtered_line_index)
630 "hiding lines after %i (abs %i), first line is abs %i",
635 first_index = model.line_index_to_super(filtered_line_index)
636 last_index = model.line_index_to_super(len(model) - 1)
639 "hiding lines before %i (abs %i), last line is abs %i",
644 self.push_view_state()
645 start_index = first_index
646 stop_index = last_index + 1
647 self.log_filter.set_range(start_index, stop_index)
649 self.pop_view_state()
650 self.actions.show_hidden_lines.props.sensitive = True
655 model = view.get_model()
656 visible_range = view.get_visible_range()
657 if visible_range is None:
659 start_path, end_path = visible_range
660 if not start_path or not end_path:
662 ts1 = model.get_value(model.get_iter(start_path),
664 ts2 = model.get_value(model.get_iter(end_path),
669 def handle_show_hidden_lines_action_activate(self, action):
671 self.logger.info("restoring model filter to show all lines")
672 self.push_view_state()
673 self.log_view.set_model(None)
674 self.log_filter.reset()
675 self.update_model(self.log_filter)
676 self.pop_view_state(scroll_to_selection=True)
677 self.actions.show_hidden_lines.props.sensitive = False
680 def handle_edit_copy_line_action_activate(self, action):
682 line_index = self.get_active_line_index()
683 model = self.log_view.get_model()
684 line_offset = model.line_offsets[line_index]
686 line_text = model.access_offset(line_offset).strip()
687 line_text = Data.strip_escape(line_text)
689 self.clipboard.set_text(line_text)
692 def handle_edit_copy_message_action_activate(self, action):
694 col_id = LogModelBase.COL_MESSAGE
695 self.clipboard.set_text(self.get_active_line()[col_id])
698 def handle_enlarge_text_action_activate(self, action):
700 self.update_zoom_level(1)
703 def handle_shrink_text_action_activate(self, action):
705 self.update_zoom_level(-1)
708 def handle_reset_text_action_activate(self, action):
710 self.update_zoom_level(-self.zoom_level)
712 def restore_zoom(self, scale):
716 self.zoom_level = int(round(log(scale) / log(ZOOM_FACTOR)))
718 self.column_manager.set_zoom(scale)
720 def update_zoom_level(self, delta_step):
725 self.zoom_level += delta_step
726 scale = ZOOM_FACTOR ** self.zoom_level
728 self.column_manager.set_zoom(scale)
730 self.app.state_section.zoom_level = int(round(scale * 100.))
732 def set_sensitive(self, sensitive):
734 for widget in self.main_sensitivity:
735 widget.props.sensitive = sensitive
737 def show_info(self, widget):
741 box = self.widgets.vbox_main
742 box.pack_start(widget, False, False, 0)
743 box.reorder_child(widget, 2)
745 self.info_widget = widget
749 if self.info_widget is None:
752 self.info_widget.destroy()
753 self.info_widget = None
755 def add_model_filter(self, filter):
757 self.progress_dialog = ProgressDialog(self, _("Filtering"))
758 self.show_info(self.progress_dialog.widget)
759 self.progress_dialog.handle_cancel = self.handle_filter_progress_dialog_cancel
760 dispatcher = Common.Data.GSourceDispatcher()
762 # FIXME: Unsetting the model to keep e.g. the dispatched timeline
763 # sentinel from collecting data while we filter idly, which slows
764 # things down for nothing.
765 self.push_view_state()
766 self.log_view.set_model(None)
767 self.log_filter.add_filter(filter, dispatcher=dispatcher)
769 GObject.timeout_add(250, self.update_filter_progress)
771 self.set_sensitive(False)
773 def update_filter_progress(self):
775 if self.progress_dialog is None:
779 progress = self.log_filter.get_filter_progress()
781 self.logger.warning("no filter process running")
784 self.progress_dialog.update(progress)
788 def handle_filter_progress_dialog_cancel(self):
791 self.progress_dialog = None
793 self.log_filter.abort_process()
794 self.log_view.set_model(self.log_filter)
795 self.pop_view_state()
797 self.set_sensitive(True)
799 def handle_log_filter_process_finished(self):
802 self.progress_dialog = None
804 # No push_view_state here, did this in add_model_filter.
805 self.update_model(self.log_filter)
806 self.pop_view_state()
808 self.actions.show_hidden_lines.props.sensitive = True
810 self.set_sensitive(True)
813 def handle_set_base_time_action_activate(self, action):
815 row = self.get_active_line()
816 self.column_manager.set_base_time(row[LogModelBase.COL_TIME])
819 def handle_hide_log_level_action_activate(self, action):
821 row = self.get_active_line()
822 debug_level = row[LogModelBase.COL_LEVEL]
823 self.add_model_filter(DebugLevelFilter(debug_level))
826 def handle_hide_log_category_action_activate(self, action):
828 row = self.get_active_line()
829 category = row[LogModelBase.COL_CATEGORY]
830 self.add_model_filter(CategoryFilter(category))
833 def handle_hide_thread_action_activate(self, action):
835 row = self.get_active_line()
836 thread = row[LogModelBase.COL_THREAD]
837 self.add_model_filter(ThreadFilter(thread))
840 def handle_hide_object_action_activate(self, action):
842 row = self.get_active_line()
843 object_ = row[LogModelBase.COL_OBJECT]
844 self.add_model_filter(ObjectFilter(object_))
847 def handle_hide_function_action_activate(self, action):
849 row = self.get_active_line()
850 object_ = row[LogModelBase.COL_FUNCTION]
851 self.add_model_filter(FunctionFilter(object_))
854 def handle_hide_filename_action_activate(self, action):
856 row = self.get_active_line()
857 filename = row[LogModelBase.COL_FILENAME]
858 self.add_model_filter(FilenameFilter(filename))
861 def handle_hide_log_level_and_above_action_activate(self, action):
863 row = self.get_active_line()
864 debug_level = row[LogModelBase.COL_LEVEL]
865 self.add_model_filter(
866 DebugLevelFilter(debug_level, DebugLevelFilter.this_and_above))
869 def handle_show_only_log_level_action_activate(self, action):
871 row = self.get_active_line()
872 debug_level = row[LogModelBase.COL_LEVEL]
873 self.add_model_filter(
874 DebugLevelFilter(debug_level, DebugLevelFilter.all_but_this))
877 def handle_show_only_log_category_action_activate(self, action):
879 row = self.get_active_line()
880 category = row[LogModelBase.COL_CATEGORY]
881 self.add_model_filter(CategoryFilter(category, True))
884 def handle_show_only_thread_action_activate(self, action):
886 row = self.get_active_line()
887 thread = row[LogModelBase.COL_THREAD]
888 self.add_model_filter(ThreadFilter(thread, True))
891 def handle_show_only_object_action_activate(self, action):
893 row = self.get_active_line()
894 object_ = row[LogModelBase.COL_OBJECT]
895 self.add_model_filter(ObjectFilter(object_, True))
898 def handle_show_only_function_action_activate(self, action):
900 row = self.get_active_line()
901 object_ = row[LogModelBase.COL_FUNCTION]
902 self.add_model_filter(FunctionFilter(object_, True))
905 def handle_show_only_filename_action_activate(self, action):
907 row = self.get_active_line()
908 filename = row[LogModelBase.COL_FILENAME]
909 self.add_model_filter(FilenameFilter(filename, True))
912 def handle_show_about_action_activate(self, action):
914 from GstDebugViewer import version
916 dialog = self.widget_factory.make_one(
917 "about-dialog.ui", "about_dialog")
918 dialog.props.version = version
923 def _timestamp_cell_data_func(column, renderer, model, tree_iter):
925 ts = model.get_value(tree_iter, LogModel.COL_TIME)
926 renderer.props.text = Data.time_args(ts)
928 def _message_cell_data_func(self, column, renderer, model, tree_iter):
930 offset = model.get_value(tree_iter, LogModel.COL_MESSAGE_OFFSET)
931 self.log_file.seek(offset)
932 renderer.props.text = strip_escape(self.log_file.readline().strip())
934 def set_log_file(self, filename):
936 if self.log_file is not None:
937 for feature in self.features:
938 feature.handle_detach_log_file(self, self.log_file)
941 if self.dispatcher is not None:
942 self.dispatcher.cancel()
943 self.dispatcher = None
945 self.actions.groups["RowActions"].props.sensitive = False
947 self.logger.debug("setting log file %r", filename)
950 self.setup_model(LazyLogModel())
952 self.dispatcher = Common.Data.GSourceDispatcher()
953 self.log_file = Data.LogFile(filename, self.dispatcher)
954 except EnvironmentError as exc:
956 file_size = os.path.getsize(filename)
957 except EnvironmentError:
961 # Trying to mmap an empty file results in an invalid
963 self.show_error(_("Could not open file"),
964 _("The selected file is empty"))
966 self.handle_environment_error(exc, filename)
969 basename = os.path.basename(filename)
970 self.gtk_window.props.title = _(
971 "%s - GStreamer Debug Viewer") % (basename,)
973 self.log_file.consumers.append(self)
974 self.log_file.start_loading()
976 def handle_environment_error(self, exc, filename):
978 self.show_error(_("Could not open file"), str(exc))
980 def show_error(self, message1, message2):
983 bar.props.message_type = Gtk.MessageType.ERROR
984 box = bar.get_content_area()
986 markup = "<b>%s</b> %s" % (GLib.markup_escape_text(message1),
987 GLib.markup_escape_text(message2),)
989 label.props.use_markup = True
990 label.props.label = markup
991 label.props.selectable = True
992 box.pack_start(label, False, False, 0)
996 def handle_load_started(self):
998 self.logger.debug("load has started")
1000 self.progress_dialog = ProgressDialog(self, _("Loading log file"))
1001 self.show_info(self.progress_dialog.widget)
1002 self.progress_dialog.handle_cancel = self.handle_load_progress_dialog_cancel
1003 self.update_progress_id = GObject.timeout_add(
1004 250, self.update_load_progress)
1006 self.set_sensitive(False)
1008 def handle_load_progress_dialog_cancel(self):
1010 self.actions.cancel_load.activate()
1012 def update_load_progress(self):
1014 if self.progress_dialog is None:
1016 "progress dialog is gone, removing progress update timeout")
1017 self.update_progress_id = None
1020 progress = self.log_file.get_load_progress()
1021 self.progress_dialog.update(progress)
1025 def handle_load_finished(self):
1027 self.logger.debug("load has finshed")
1030 self.progress_dialog = None
1032 self.log_model.set_log(self.log_file)
1033 self.log_filter.reset()
1035 self.actions.reload_file.props.sensitive = True
1036 self.actions.groups["RowActions"].props.sensitive = True
1037 self.actions.show_hidden_lines.props.sensitive = False
1039 self.set_sensitive(True)
1041 if len(self.log_model) == 0:
1043 _("The file does not contain any parsable lines."),
1044 _("It is not a GStreamer log file."))
1047 self.logger.debug("idle trigger after load finished")
1048 self.log_view.set_model(self.log_filter)
1050 self.line_view.handle_attach_log_file(self)
1051 for feature in self.features:
1052 feature.handle_attach_log_file(self, self.log_file)
1053 if len(self.log_filter):
1054 sel = self.log_view.get_selection()
1055 sel.select_path((0,))
1058 GObject.idle_add(idle_set)