gstreamer/subprojects/gst-devtools/debug-viewer/GstDebugViewer/GUI/window.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1069 lines
34 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."""
import os.path
import tempfile
import shutil
2009-08-06 23:54:10 +00:00
from bisect import bisect_right, bisect_left
import logging
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
2009-08-06 23:54:10 +00:00
from GstDebugViewer import Common, Data, Main
from GstDebugViewer.GUI.columns import LineViewColumnManager, ViewColumnManager
from GstDebugViewer.GUI.filters import (CategoryFilter,
DebugLevelFilter,
FilenameFilter,
FunctionFilter,
ThreadFilter,
2009-08-06 23:54:10 +00:00
ObjectFilter)
from GstDebugViewer.GUI.models import (FilteredLogModel,
LazyLogModel,
LineViewLogModel,
LogModelBase)
2009-08-06 23:54:10 +00:00
2018-04-14 15:04:22 +00:00
ZOOM_FACTOR = 1.15
def _(s):
return s
def action(func):
func.is_action_handler = True
return func
def iter_actions(manager):
cls = type(manager)
it = cls.__dict__.items()
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):
2009-08-06 23:54:10 +00:00
self.column_manager = LineViewColumnManager()
2009-08-06 23:54:10 +00:00
def attach(self, window):
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
self.clear_action.props.sensitive = False
self.column_manager.attach(window)
2009-08-06 23:54:10 +00:00
def clear(self):
2009-08-06 23:54:10 +00:00
model = self.line_view.get_model()
2009-08-06 23:54:10 +00:00
if len(model) == 0:
2009-08-06 23:54:10 +00:00
return
for i in range(1, len(model)):
model.remove_line(1)
2009-08-06 23:54:10 +00:00
self.clear_action.props.sensitive = False
def handle_attach_log_file(self, window):
2009-08-06 23:54:10 +00:00
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):
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
def handle_log_view_row_activated(self, view, path, column):
2009-08-06 23:54:10 +00:00
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):
2009-08-06 23:54:10 +00:00
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])
2009-08-06 23:54:10 +00:00
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):
2009-08-06 23:54:10 +00:00
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()
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
else:
line_model.replace_line(0, line_index)
2009-08-06 23:54:10 +00:00
@action
def handle_clear_line_view_action_activate(self, action):
self.clear()
2009-08-06 23:54:10 +00:00
class ProgressDialog (object):
def __init__(self, window, title=""):
2009-08-06 23:54:10 +00:00
bar = Gtk.InfoBar()
bar.props.message_type = Gtk.MessageType.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(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()
2009-08-06 23:54:10 +00:00
def handle_cancel(self):
2009-08-06 23:54:10 +00:00
pass
def update(self, progress):
2009-08-06 23:54:10 +00:00
if self.__progress_bar is None:
return
self.__progress_bar.props.fraction = progress
2009-08-06 23:54:10 +00:00
class Window (object):
def __init__(self, app):
2009-08-06 23:54:10 +00:00
self.logger = logging.getLogger("ui.window")
2009-08-06 23:54:10 +00:00
self.app = app
self.tmpfile = None
2009-08-06 23:54:10 +00:00
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")),
2018-04-14 15:04:22 +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"),
("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)
2009-08-06 23:54:10 +00:00
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-thread", None, _(
"Hide thread")),
("show-only-thread", None, _(
"Show only thread")),
("hide-object", None, _("Hide object")),
("show-only-object", None, _(
"Show only object")),
("hide-function", None, _("Hide function")),
("show-only-function", None, _(
"Show only function")),
("hide-filename", None, _("Hide filename")),
("show-only-filename", None, _("Show only filename"))])
2009-08-06 23:54:10 +00:00
group.props.sensitive = False
self.actions.add_group(group)
2009-08-06 23:54:10 +00:00
self.actions.add_group(self.column_manager.action_group)
2009-08-06 23:54:10 +00:00
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")
2009-08-06 23:54:10 +00:00
ui_filename = os.path.join(Main.Paths.data_dir, "menus.ui")
self.ui_factory = Common.GUI.UIFactory(ui_filename, self.actions)
2009-08-06 23:54:10 +00:00
self.ui_manager = ui = self.ui_factory.make()
menubar = ui.get_widget("/ui/menubar")
self.widgets.vbox_main.pack_start(menubar, False, False, 0)
2009-08-06 23:54:10 +00:00
self.gtk_window = self.widgets.main_window
self.gtk_window.add_accel_group(ui.get_accel_group())
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
self.view_popup = ui.get_widget(
"/ui/context/LogViewContextMenu").get_submenu()
Common.GUI.widget_add_popup_menu(self.log_view, self.view_popup)
2009-08-06 23:54:10 +00:00
# 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()
2009-08-06 23:54:10 +00:00
self.attach()
self.column_manager.attach(self.log_view)
2009-08-06 23:54:10 +00:00
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
def get_top_attach_point(self):
2009-08-06 23:54:10 +00:00
return self.widgets.vbox_main
def get_side_attach_point(self):
2009-08-06 23:54:10 +00:00
return self.widgets.hbox_view
def attach(self):
2009-08-06 23:54:10 +00:00
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.)
2011-11-05 22:47:47 +00:00
self.window_state.attach(window=self.gtk_window,
state=self.app.state_section)
2009-08-06 23:54:10 +00:00
self.clipboard = Gtk.Clipboard.get_for_display(
self.gtk_window.get_display(),
Gdk.SELECTION_CLIPBOARD)
2009-08-06 23:54:10 +00:00
for action_name, handler in iter_actions(self):
action = getattr(self.actions, action_name)
action.connect("activate", handler)
2009-08-06 23:54:10 +00:00
self.gtk_window.connect(
"delete-event", self.handle_window_delete_event)
2009-08-06 23:54:10 +00:00
self.features = []
for plugin_feature in self.app.iter_plugin_features():
feature = plugin_feature(self.app)
self.features.append(feature)
2009-08-06 23:54:10 +00:00
for feature in self.features:
feature.handle_attach_window(self)
2009-08-06 23:54:10 +00:00
# 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)
2009-08-06 23:54:10 +00:00
self.line_view.attach(self)
2009-08-06 23:54:10 +00:00
# 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()
2009-08-06 23:54:10 +00:00
def detach(self):
2009-08-06 23:54:10 +00:00
self.set_log_file(None)
2009-08-06 23:54:10 +00:00
for feature in self.features:
feature.handle_detach_window(self)
2009-08-06 23:54:10 +00:00
self.window_state.detach()
self.column_manager.detach()
2009-08-06 23:54:10 +00:00
def get_active_line_index(self):
2009-08-06 23:54:10 +00:00
selection = self.log_view.get_selection()
model, tree_iter = selection.get_selected()
2009-08-06 23:54:10 +00:00
if tree_iter is None:
raise ValueError("no line selected")
path = model.get_path(tree_iter)
2009-08-06 23:54:10 +00:00
return path[0]
def get_active_line(self):
2009-08-06 23:54:10 +00:00
selection = self.log_view.get_selection()
model, tree_iter = selection.get_selected()
2009-08-06 23:54:10 +00:00
if tree_iter is None:
raise ValueError("no line selected")
model = self.log_view.get_model()
return model.get(tree_iter, *LogModelBase.column_ids)
2009-08-06 23:54:10 +00:00
def close(self, *a, **kw):
2009-08-06 23:54:10 +00:00
self.logger.debug("closing window, detaching")
self.detach()
self.gtk_window.hide()
self.logger.debug("requesting close from app")
self.app.close_window(self)
2009-08-06 23:54:10 +00:00
def push_view_state(self):
2009-08-06 23:54:10 +00:00
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()
2009-08-06 23:54:10 +00:00
except ValueError:
super_index = None
self.logger.debug("no line selected")
2009-08-06 23:54:10 +00:00
else:
super_index = model.line_index_to_super(line_index)
self.logger.debug("pushing selected line %i (abs %i)",
line_index, super_index)
2009-08-06 23:54:10 +00:00
self.default_index = super_index
vis_range = self.log_view.get_visible_range()
2009-08-06 23:54:10 +00:00
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):
2009-08-06 23:54:10 +00:00
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):
2009-08-06 23:54:10 +00:00
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:
self.logger.debug(
"abs line index %i filtered out, not reselecting",
selected_index)
2009-08-06 23:54:10 +00:00
else:
assert select_index >= 0
sel = self.log_view.get_selection()
2009-08-06 23:54:10 +00:00
path = (select_index,)
sel.select_path(path)
2009-08-06 23:54:10 +00:00
if start_index is None or scroll_to_selection:
self.log_view.scroll_to_cell(
path, use_align=True, row_align=.5)
2009-08-06 23:54:10 +00:00
if start_index is not None and not scroll_to_selection:
def traverse():
for i in range(start_index, len(model)):
2009-08-06 23:54:10 +00:00
yield i
for i in range(start_index - 1, 0, -1):
2009-08-06 23:54:10 +00:00
yield i
for current_index in traverse():
2009-08-06 23:54:10 +00:00
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.)
2009-08-06 23:54:10 +00:00
break
def update_view(self):
2009-08-06 23:54:10 +00:00
view = self.log_view
model = view.get_model()
2009-08-06 23:54:10 +00:00
start_path, end_path = view.get_visible_range()
2009-08-06 23:54:10 +00:00
start_index, end_index = start_path[0], end_path[0]
for line_index in range(start_index, end_index + 1):
2009-08-06 23:54:10 +00:00
path = (line_index,)
tree_iter = model.get_iter(path)
model.row_changed(path, tree_iter)
2009-08-06 23:54:10 +00:00
def handle_log_view_selection_changed(self, selection):
2009-08-06 23:54:10 +00:00
try:
line_index = self.get_active_line_index()
2009-08-06 23:54:10 +00:00
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):
2009-08-06 23:54:10 +00:00
self.actions.close_window.activate()
2009-08-06 23:54:10 +00:00
return True
@action
def handle_new_window_action_activate(self, action):
2009-08-06 23:54:10 +00:00
self.app.open_window()
2009-08-06 23:54:10 +00:00
@action
def handle_open_file_action_activate(self, action):
dialog = Gtk.FileChooserDialog(None, self.gtk_window,
Gtk.FileChooserAction.OPEN,
2018-04-14 15:04:22 +00:00
(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()
2009-08-06 23:54:10 +00:00
@action
def handle_reload_file_action_activate(self, action):
2009-08-06 23:54:10 +00:00
if self.log_file is None:
return
self.set_log_file(self.log_file.path)
2009-08-06 23:54:10 +00:00
@action
def handle_cancel_load_action_activate(self, action):
2009-08-06 23:54:10 +00:00
self.logger.debug("cancelling data load")
2009-08-06 23:54:10 +00:00
self.set_log_file(None)
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
self.update_progress_id = None
self.set_sensitive(True)
@action
def handle_close_window_action_activate(self, action):
2009-08-06 23:54:10 +00:00
self.close()
2009-08-06 23:54:10 +00:00
@action
def handle_hide_after_line_action_activate(self, action):
2009-08-06 23:54:10 +00:00
self.hide_range(after=True)
2009-08-06 23:54:10 +00:00
@action
def handle_hide_before_line_action_activate(self, action):
2009-08-06 23:54:10 +00:00
self.hide_range(after=False)
2009-08-06 23:54:10 +00:00
def hide_range(self, after):
2009-08-06 23:54:10 +00:00
model = self.log_view.get_model()
2009-08-06 23:54:10 +00:00
try:
filtered_line_index = self.get_active_line_index()
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
self.push_view_state()
2009-08-06 23:54:10 +00:00
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()
2009-08-06 23:54:10 +00:00
self.actions.show_hidden_lines.props.sensitive = True
def get_range(self):
view = self.log_view
model = view.get_model()
visible_range = view.get_visible_range()
if visible_range is None:
return None
start_path, end_path = visible_range
if not start_path or not end_path:
return None
ts1 = model.get_value(model.get_iter(start_path),
model.COL_TIME)
ts2 = model.get_value(model.get_iter(end_path),
model.COL_TIME)
return (ts1, ts2)
@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)
2009-08-06 23:54:10 +00:00
self.actions.show_hidden_lines.props.sensitive = False
@action
def handle_edit_copy_line_action_activate(self, action):
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
self.clipboard.set_text(line_text.decode('utf8', errors='replace'), -1)
2009-08-06 23:54:10 +00:00
@action
def handle_edit_copy_message_action_activate(self, action):
2009-08-06 23:54:10 +00:00
col_id = LogModelBase.COL_MESSAGE
2018-04-14 15:04:22 +00:00
self.clipboard.set_text(self.get_active_line()[
col_id].decode('utf8'), -1)
2009-08-06 23:54:10 +00:00
@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):
2011-11-05 22:05:00 +00:00
self.update_zoom_level(-self.zoom_level)
2011-11-05 22:05:00 +00:00
def restore_zoom(self, scale):
2011-11-05 22:47:47 +00:00
from math import log
self.zoom_level = int(round(log(scale) / log(ZOOM_FACTOR)))
2011-11-05 22:47:47 +00:00
self.column_manager.set_zoom(scale)
2011-11-05 22:47:47 +00:00
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.))
2011-11-05 22:47:47 +00:00
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):
2009-08-06 23:54:10 +00:00
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()
2009-08-06 23:54:10 +00:00
# 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)
2009-08-06 23:54:10 +00:00
GObject.timeout_add(250, self.update_filter_progress)
2009-08-06 23:54:10 +00:00
self.set_sensitive(False)
def update_filter_progress(self):
2009-08-06 23:54:10 +00:00
if self.progress_dialog is None:
return False
try:
progress = self.log_filter.get_filter_progress()
2009-08-06 23:54:10 +00:00
except ValueError:
self.logger.warning("no filter process running")
2009-08-06 23:54:10 +00:00
return False
self.progress_dialog.update(progress)
2009-08-06 23:54:10 +00:00
return True
def handle_filter_progress_dialog_cancel(self):
2009-08-06 23:54:10 +00:00
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)
self.pop_view_state()
2009-08-06 23:54:10 +00:00
self.set_sensitive(True)
def handle_log_filter_process_finished(self):
2009-08-06 23:54:10 +00:00
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()
2009-08-06 23:54:10 +00:00
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):
2009-08-06 23:54:10 +00:00
row = self.get_active_line()
2009-08-06 23:54:10 +00:00
debug_level = row[LogModelBase.COL_LEVEL]
self.add_model_filter(DebugLevelFilter(debug_level))
2009-08-06 23:54:10 +00:00
@action
def handle_hide_log_category_action_activate(self, action):
2009-08-06 23:54:10 +00:00
row = self.get_active_line()
2009-08-06 23:54:10 +00:00
category = row[LogModelBase.COL_CATEGORY]
self.add_model_filter(CategoryFilter(category))
2009-08-06 23:54:10 +00:00
@action
def handle_hide_thread_action_activate(self, action):
row = self.get_active_line()
thread = row[LogModelBase.COL_THREAD]
self.add_model_filter(ThreadFilter(thread))
@action
def handle_hide_object_action_activate(self, action):
2009-08-06 23:54:10 +00:00
row = self.get_active_line()
2009-08-06 23:54:10 +00:00
object_ = row[LogModelBase.COL_OBJECT]
self.add_model_filter(ObjectFilter(object_))
2009-08-06 23:54:10 +00:00
@action
def handle_hide_function_action_activate(self, action):
row = self.get_active_line()
object_ = row[LogModelBase.COL_FUNCTION]
self.add_model_filter(FunctionFilter(object_))
@action
def handle_hide_filename_action_activate(self, action):
2009-08-06 23:54:10 +00:00
row = self.get_active_line()
2009-08-06 23:54:10 +00:00
filename = row[LogModelBase.COL_FILENAME]
self.add_model_filter(FilenameFilter(filename))
2009-08-06 23:54:10 +00:00
@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_thread_action_activate(self, action):
row = self.get_active_line()
thread = row[LogModelBase.COL_THREAD]
self.add_model_filter(ThreadFilter(thread, True))
@action
def handle_show_only_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_function_action_activate(self, action):
row = self.get_active_line()
object_ = row[LogModelBase.COL_FUNCTION]
self.add_model_filter(FunctionFilter(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):
2009-08-06 23:54:10 +00:00
from GstDebugViewer import version
dialog = self.widget_factory.make_one(
"about-dialog.ui", "about_dialog")
2009-08-06 23:54:10 +00:00
dialog.props.version = version
dialog.run()
dialog.destroy()
2009-08-06 23:54:10 +00:00
@staticmethod
def _timestamp_cell_data_func(column, renderer, model, tree_iter):
2009-08-06 23:54:10 +00:00
ts = model.get_value(tree_iter, LogModel.COL_TIME)
renderer.props.text = Data.time_args(ts)
2009-08-06 23:54:10 +00:00
def _message_cell_data_func(self, column, renderer, model, tree_iter):
2009-08-06 23:54:10 +00:00
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())
2009-08-06 23:54:10 +00:00
def set_log_file(self, filename):
2009-08-06 23:54:10 +00:00
if self.log_file is not None:
for feature in self.features:
feature.handle_detach_log_file(self, self.log_file)
2009-08-06 23:54:10 +00:00
if filename is None:
if self.dispatcher is not None:
self.dispatcher.cancel()
2009-08-06 23:54:10 +00:00
self.dispatcher = None
self.log_file = None
self.tmpfile = None
2009-08-06 23:54:10 +00:00
self.actions.groups["RowActions"].props.sensitive = False
else:
if self.tmpfile and filename != self.tmpfile.name:
self.tmpfile = tempfile.NamedTemporaryFile()
shutil.copyfile(filename, self.tmpfile.name)
filename = self.tmpfile.name
self.logger.debug("setting log file %r", filename)
2009-08-06 23:54:10 +00:00
try:
self.setup_model(LazyLogModel())
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
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"))
2009-08-06 23:54:10 +00:00
return
self.handle_environment_error(exc, filename)
2009-08-06 23:54:10 +00:00
return
basename = os.path.basename(filename)
self.gtk_window.props.title = _(
"%s - GStreamer Debug Viewer") % (basename,)
2009-08-06 23:54:10 +00:00
self.log_file.consumers.append(self)
self.log_file.start_loading()
2009-08-06 23:54:10 +00:00
def handle_environment_error(self, exc, filename):
2009-08-06 23:54:10 +00:00
self.show_error(_("Could not open file"), str(exc))
2009-08-06 23:54:10 +00:00
def show_error(self, message1, message2):
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
def handle_load_started(self):
2009-08-06 23:54:10 +00:00
self.logger.debug("load has started")
2009-08-06 23:54:10 +00:00
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)
2009-08-06 23:54:10 +00:00
self.set_sensitive(False)
def handle_load_progress_dialog_cancel(self):
2009-08-06 23:54:10 +00:00
self.actions.cancel_load.activate()
2009-08-06 23:54:10 +00:00
def update_load_progress(self):
2009-08-06 23:54:10 +00:00
if self.progress_dialog is None:
self.logger.debug(
"progress dialog is gone, removing progress update timeout")
2009-08-06 23:54:10 +00:00
self.update_progress_id = None
return False
progress = self.log_file.get_load_progress()
self.progress_dialog.update(progress)
2009-08-06 23:54:10 +00:00
return True
def handle_load_finished(self):
2009-08-06 23:54:10 +00:00
self.logger.debug("load has finshed")
2009-08-06 23:54:10 +00:00
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."))
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)
2009-08-06 23:54:10 +00:00
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,))
2009-08-06 23:54:10 +00:00
return False
GObject.idle_add(idle_set)