gstreamer/debug-viewer/GstDebugViewer/GUI/window.py

918 lines
30 KiB
Python
Raw Normal View History

2009-08-06 23:54:10 +00:00
# -*- coding: utf-8; mode: python; -*-
#
# GStreamer Debug Viewer - View and analyze GStreamer debug log files
#
# Copyright (C) 2007 René Stadler <mail@renestadler.de>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
"""GStreamer Debug Viewer GUI module."""
ZOOM_FACTOR = 1.15
2009-08-06 23:54:10 +00:00
def _ (s):
return s
import os.path
from bisect import bisect_right, bisect_left
import logging
import pango
2009-08-06 23:54:10 +00:00
import gobject
import gtk
from GstDebugViewer import Common, Data, Main
from GstDebugViewer.GUI.columns import LineViewColumnManager, ViewColumnManager
from GstDebugViewer.GUI.filters import (CategoryFilter,
DebugLevelFilter,
FilenameFilter,
ObjectFilter)
from GstDebugViewer.GUI.models import (FilteredLogModel,
LazyLogModel,
LineViewLogModel,
LogModelBase)
2009-08-06 23:54:10 +00:00
def action (func):
func.is_action_handler = True
return func
def iter_actions (manager):
cls = type (manager)
it = cls.__dict__.iteritems ()
for name, member in it:
try:
member.is_action_handler
except AttributeError:
continue
bound_method = getattr (manager, name)
assert name.startswith ("handle_")
assert name.endswith ("_action_activate")
action_name = name[len ("handle_"):-len ("_action_activate")]
action_name = action_name.replace ("_", "-")
yield (action_name, bound_method,)
2009-08-06 23:54:10 +00:00
class LineView (object):
def __init__ (self):
self.column_manager = LineViewColumnManager ()
def attach (self, window):
for action_name, handler in iter_actions (self):
action = getattr (window.actions, action_name)
action.connect ("activate", handler)
2009-08-06 23:54:10 +00:00
self.clear_action = window.actions.clear_line_view
self.line_view = window.widgets.line_view
self.line_view.connect ("row-activated", self.handle_line_view_row_activated)
ui = window.ui_manager
self.popup = ui.get_widget ("/ui/context/LineViewContextMenu").get_submenu ()
Common.GUI.widget_add_popup_menu (self.line_view, self.popup)
self.log_view = log_view = window.log_view
log_view.connect ("row-activated", self.handle_log_view_row_activated)
sel = log_view.get_selection ()
sel.connect ("changed", self.handle_log_view_selection_changed)
self.clear_action.props.sensitive = False
self.column_manager.attach (window)
def clear (self):
model = self.line_view.get_model ()
2009-08-06 23:54:10 +00:00
if len (model) == 0:
return
for i in range (1, len (model)):
model.remove_line (1)
self.clear_action.props.sensitive = False
def handle_attach_log_file (self, window):
self.line_view.set_model (LineViewLogModel (window.log_model))
2009-08-06 23:54:10 +00:00
def handle_line_view_row_activated (self, view, path, column):
line_index = path[0]
line_model = view.get_model ()
log_model = self.log_view.get_model ()
super_index = line_model.line_index_to_super (line_index)
log_index = log_model.line_index_from_super (super_index)
2009-08-06 23:54:10 +00:00
path = (log_index,)
self.log_view.scroll_to_cell (path, use_align = True, row_align = .5)
sel = self.log_view.get_selection ()
sel.select_path (path)
def handle_log_view_row_activated (self, view, path, column):
log_model = view.get_model ()
2009-08-06 23:54:10 +00:00
line_index = path[0]
super_index = log_model.line_index_to_super (line_index)
line_model = self.line_view.get_model ()
2009-08-06 23:54:10 +00:00
if line_model is None:
return
if len (line_model):
timestamps = [row[line_model.COL_TIME] for row in line_model]
row = log_model[(line_index,)]
position = bisect_right (timestamps, row[line_model.COL_TIME])
else:
position = 0
if len (line_model) > 1:
other_index = line_model.line_index_to_super (position - 1)
2009-08-06 23:54:10 +00:00
else:
other_index = -1
if other_index == super_index and position != 1:
2009-08-06 23:54:10 +00:00
# Already have the line.
pass
else:
line_model.insert_line (position, super_index)
2009-08-06 23:54:10 +00:00
self.clear_action.props.sensitive = True
def handle_log_view_selection_changed (self, selection):
line_model = self.line_view.get_model ()
2009-08-06 23:54:10 +00:00
if line_model is None:
return
model, tree_iter = selection.get_selected ()
if tree_iter is None:
return
path = model.get_path (tree_iter)
line_index = model.line_index_to_super (path[0])
2009-08-06 23:54:10 +00:00
if len (line_model) == 0:
line_model.insert_line (0, line_index)
else:
line_model.replace_line (0, line_index)
@action
2009-08-06 23:54:10 +00:00
def handle_clear_line_view_action_activate (self, action):
self.clear ()
class ProgressDialog (object):
def __init__ (self, window, title = ""):
bar = gtk.InfoBar ()
bar.props.message_type = gtk.MESSAGE_INFO
bar.connect ("response", self.__handle_info_bar_response)
bar.add_button (gtk.STOCK_CANCEL, 1)
area_box = bar.get_content_area ()
box = gtk.HBox (spacing = 8)
2009-08-06 23:54:10 +00:00
box.pack_start (gtk.Label (title), False, False, 0)
2009-08-06 23:54:10 +00:00
progress = gtk.ProgressBar ()
box.pack_start (progress, False, False, 0)
2009-08-06 23:54:10 +00:00
area_box.pack_start (box, False, False, 0)
self.widget = bar
self.__progress_bar = progress
def __handle_info_bar_response (self, info_bar, response):
2009-08-06 23:54:10 +00:00
self.handle_cancel ()
def handle_cancel (self):
pass
def update (self, progress):
if self.__progress_bar is None:
return
self.__progress_bar.props.fraction = progress
class Window (object):
def __init__ (self, app):
self.logger = logging.getLogger ("ui.window")
self.app = app
self.dispatcher = None
self.info_widget = None
2009-08-06 23:54:10 +00:00
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")),
2009-08-06 23:54:10 +00:00
("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"),
2011-11-05 22:05:00 +00:00
("shrink-text", gtk.STOCK_ZOOM_OUT, _("Shrink Text"), "<Ctrl>minus"),
("reset-text", gtk.STOCK_ZOOM_100, _("Normal Text Size"), "<Ctrl>0")])
2009-08-06 23:54:10 +00:00
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")),
2009-08-06 23:54:10 +00:00
("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")),
2009-08-06 23:54:10 +00:00
("hide-log-level", None, _("Hide log level")),
("hide-log-category", None, _("Hide log category")),
("hide-log-object", None, _("Hide object")),
("hide-filename", None, _("Hide 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
2009-08-06 23:54:10 +00:00
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 ())
2009-08-06 23:54:10 +00:00
self.line_view = LineView ()
self.attach ()
self.column_manager.attach (self.log_view)
def setup_model (self, model):
2009-08-06 23:54:10 +00:00
self.log_model = model
self.log_filter = FilteredLogModel (self.log_model)
self.log_filter.handle_process_finished = self.handle_log_filter_process_finished
2009-08-06 23:54:10 +00:00
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
2011-11-05 22:47:47 +00:00
zoom_percent = self.app.state_section.zoom_level
if zoom_percent:
self.restore_zoom (float (zoom_percent) / 100.)
2009-08-06 23:54:10 +00:00
self.window_state.attach (window = self.gtk_window,
state = self.app.state_section)
self.clipboard = gtk.Clipboard (self.gtk_window.get_display (),
gtk.gdk.SELECTION_CLIPBOARD)
for action_name, handler in iter_actions (self):
action = getattr (self.actions, action_name)
2009-08-06 23:54:10 +00:00
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.SELECTION_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")
2009-08-06 23:54:10 +00:00
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 ()
2009-08-06 23:54:10 +00:00
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 ()
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
def update_model (self, model = None):
if model is None:
model = self.log_view.get_model ()
2009-08-06 23:54:10 +00:00
previous_model = self.log_view.get_model ()
2009-08-06 23:54:10 +00:00
if previous_model == model:
# Force update.
self.log_view.set_model (None)
self.log_view.set_model (model)
2009-08-06 23:54:10 +00:00
def pop_view_state (self, scroll_to_selection = False):
model = self.log_view.get_model ()
2009-08-06 23:54:10 +00:00
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:
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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 ()
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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
2009-08-06 23:54:10 +00:00
def handle_new_window_action_activate (self, action):
self.app.open_window ()
@action
2009-08-06 23:54:10 +00:00
def handle_open_file_action_activate (self, action):
dialog = gtk.FileChooserDialog (None, self.gtk_window,
gtk.FILE_CHOOSER_ACTION_OPEN,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT,))
response = dialog.run ()
dialog.hide ()
if response == gtk.RESPONSE_ACCEPT:
self.set_log_file (dialog.get_filename ())
dialog.destroy ()
@action
2009-08-06 23:54:10 +00:00
def handle_reload_file_action_activate (self, action):
if self.log_file is None:
return
self.set_log_file (self.log_file.path)
@action
2009-08-06 23:54:10 +00:00
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 ()
2009-08-06 23:54:10 +00:00
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
2009-08-06 23:54:10 +00:00
def handle_close_window_action_activate (self, action):
self.close ()
@action
2009-08-06 23:54:10 +00:00
def handle_hide_after_line_action_activate (self, action):
self.hide_range (after = True)
@action
2009-08-06 23:54:10 +00:00
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 ()
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
self.update_model ()
self.pop_view_state ()
self.actions.show_hidden_lines.props.sensitive = True
@action
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
self.pop_view_state (scroll_to_selection = True)
self.actions.show_hidden_lines.props.sensitive = False
@action
2009-08-06 23:54:10 +00:00
def handle_edit_copy_line_action_activate (self, action):
# TODO: Should probably copy the _exact_ line as taken from the file.
line = self.get_active_line ()
log_line = Data.LogLine (line)
self.clipboard.set_text (log_line.line_string ())
@action
2009-08-06 23:54:10 +00:00
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
2011-11-05 22:05:00 +00:00
def handle_reset_text_action_activate (self, action):
self.update_zoom_level (-self.zoom_level)
2011-11-05 22:47:47 +00:00
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)
2011-11-05 22:47:47 +00:00
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
2009-08-06 23:54:10 +00:00
def add_model_filter (self, filter):
self.progress_dialog = ProgressDialog (self, _("Filtering"))
self.show_info (self.progress_dialog.widget)
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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 ()
2009-08-06 23:54:10 +00:00
self.progress_dialog = None
self.log_filter.abort_process ()
self.log_view.set_model (self.log_filter)
2009-08-06 23:54:10 +00:00
self.pop_view_state ()
self.set_sensitive (True)
2009-08-06 23:54:10 +00:00
def handle_log_filter_process_finished (self):
self.hide_info ()
2009-08-06 23:54:10 +00:00
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
2009-08-06 23:54:10 +00:00
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
2009-08-06 23:54:10 +00:00
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
2009-08-06 23:54:10 +00:00
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
2009-08-06 23:54:10 +00:00
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
2009-08-06 23:54:10 +00:00
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:
2009-08-06 23:54:10 +00:00
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.MESSAGE_ERROR
box = bar.get_content_area ()
attrs = pango.AttrList ()
attrs.insert (pango.AttrWeight (pango.WEIGHT_BOLD, 0, len (message1)))
label = gtk.Label ()
label.props.label = "%s %s" % (message1, message2)
label.props.attributes = attrs
label.props.selectable = True
box.pack_start (label, False, False, 0)
self.show_info (bar)
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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 ()
2009-08-06 23:54:10 +00:00
self.progress_dialog = None
self.log_model.set_log (self.log_file)
self.log_filter.reset ()
2009-08-06 23:54:10 +00:00
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."))
2009-08-06 23:54:10 +00:00
def idle_set ():
self.log_view.set_model (self.log_filter)
2009-08-06 23:54:10 +00:00
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):
2009-08-06 23:54:10 +00:00
sel = self.log_view.get_selection ()
sel.select_path ((0,))
return False
gobject.idle_add (idle_set)