formatting: run autopep8 over all files

We have a commit hook on the repo. Get all files to match the pep8 guidelines.
This commit is contained in:
Stefan Sauer 2016-09-28 20:38:55 +02:00
parent 3da48e7d61
commit d783c9cf36
20 changed files with 2246 additions and 2021 deletions

View file

@ -23,42 +23,46 @@ import gi
from gi.repository import GObject
class Dispatcher (object):
def __call__ (self, iterator):
def __call__(self, iterator):
raise NotImplementedError ("derived classes must override this method")
raise NotImplementedError("derived classes must override this method")
def cancel (self):
def cancel(self):
pass
class DefaultDispatcher (Dispatcher):
def __call__ (self, iterator):
def __call__(self, iterator):
for x in iterator:
pass
class GSourceDispatcher (Dispatcher):
def __init__ (self):
def __init__(self):
Dispatcher.__init__ (self)
Dispatcher.__init__(self)
self.source_id = None
def __call__ (self, iterator):
def __call__(self, iterator):
if self.source_id is not None:
GObject.source_remove (self.source_id)
GObject.source_remove(self.source_id)
self.source_id = GObject.idle_add (iterator.next, priority = GObject.PRIORITY_LOW)
self.source_id = GObject.idle_add(
iterator.next, priority=GObject.PRIORITY_LOW)
def cancel (self):
def cancel(self):
if self.source_id is None:
return
GObject.source_remove (self.source_id)
GObject.source_remove(self.source_id)
self.source_id = None

View file

@ -35,105 +35,111 @@ import GstDebugViewer
from GstDebugViewer.Common import utils
from generictreemodel import GenericTreeModel
def widget_add_popup_menu (widget, menu, button = 3):
def popup_callback (widget, event):
def widget_add_popup_menu(widget, menu, button=3):
def popup_callback(widget, event):
if event.button == button:
menu.popup (None, None, None, None, event.button, event.get_time ())
menu.popup(
None, None, None, None, event.button, event.get_time())
return False
widget.connect ("button-press-event", popup_callback)
widget.connect("button-press-event", popup_callback)
class Actions (dict):
def __init__ (self):
def __init__(self):
dict.__init__ (self)
dict.__init__(self)
self.groups = {}
def __getattr__ (self, name):
def __getattr__(self, name):
try:
return self[name]
except KeyError:
if "_" in name:
try:
return self[name.replace ("_", "-")]
return self[name.replace("_", "-")]
except KeyError:
pass
raise AttributeError ("no action with name %r" % (name,))
raise AttributeError("no action with name %r" % (name,))
def add_group (self, group):
def add_group(self, group):
name = group.props.name
if name in self.groups:
raise ValueError ("already have a group named %s", name)
raise ValueError("already have a group named %s", name)
self.groups[name] = group
for action in group.list_actions ():
for action in group.list_actions():
self[action.props.name] = action
class Widgets (dict):
def __init__ (self, builder):
def __init__(self, builder):
widgets = (obj for obj in builder.get_objects ()
widgets = (obj for obj in builder.get_objects()
if isinstance(obj, Gtk.Buildable))
# Gtk.Widget.get_name() shadows out the GtkBuildable interface method
# of the same name, hence calling the unbound interface method here:
items = ((Gtk.Buildable.get_name (w), w,) for w in widgets)
items = ((Gtk.Buildable.get_name(w), w,) for w in widgets)
dict.__init__ (self, items)
dict.__init__(self, items)
def __getattr__ (self, name):
def __getattr__(self, name):
try:
return self[name]
except KeyError:
if "_" in name:
try:
return self[name.replace ("_", "-")]
return self[name.replace("_", "-")]
except KeyError:
pass
raise AttributeError ("no widget with name %r" % (name,))
raise AttributeError("no widget with name %r" % (name,))
class WidgetFactory (object):
def __init__ (self, directory):
def __init__(self, directory):
self.directory = directory
def get_builder (self, filename):
def get_builder(self, filename):
builder_filename = os.path.join (self.directory, filename)
builder_filename = os.path.join(self.directory, filename)
builder = Gtk.Builder ()
builder.set_translation_domain (GstDebugViewer.GETTEXT_DOMAIN)
builder.add_from_file (builder_filename)
builder = Gtk.Builder()
builder.set_translation_domain(GstDebugViewer.GETTEXT_DOMAIN)
builder.add_from_file(builder_filename)
return builder
def make (self, filename, widget_name, autoconnect = None):
def make(self, filename, widget_name, autoconnect=None):
builder = self.get_builder (filename)
builder = self.get_builder(filename)
if autoconnect is not None:
builder.connect_signals (autoconnect)
builder.connect_signals(autoconnect)
return Widgets (builder)
return Widgets(builder)
def make_one (self, filename, widget_name):
def make_one(self, filename, widget_name):
builder = self.get_builder (filename)
builder = self.get_builder(filename)
return builder.get_object(widget_name)
return builder.get_object (widget_name)
class UIFactory (object):
def __init__ (self, ui_filename, actions = None):
def __init__(self, ui_filename, actions=None):
self.filename = ui_filename
if actions:
@ -141,19 +147,20 @@ class UIFactory (object):
else:
self.action_groups = ()
def make (self, extra_actions = None):
def make(self, extra_actions=None):
ui_manager = Gtk.UIManager ()
for action_group in self.action_groups.values ():
ui_manager.insert_action_group (action_group, 0)
ui_manager = Gtk.UIManager()
for action_group in self.action_groups.values():
ui_manager.insert_action_group(action_group, 0)
if extra_actions:
for action_group in extra_actions.groups:
ui_manager.insert_action_group (action_group, 0)
ui_manager.add_ui_from_file (self.filename)
ui_manager.ensure_update ()
ui_manager.insert_action_group(action_group, 0)
ui_manager.add_ui_from_file(self.filename)
ui_manager.ensure_update()
return ui_manager
class MetaModel (GObjectMeta):
"""Meta class for easy setup of gtk tree models.
@ -184,73 +191,75 @@ class MetaModel (GObjectMeta):
self.COL_VALUE, "ham")
"""
def __init__ (cls, name, bases, dict):
def __init__(cls, name, bases, dict):
super (MetaModel, cls).__init__ (name, bases, dict)
super(MetaModel, cls).__init__(name, bases, dict)
spec = tuple (cls.columns)
spec = tuple(cls.columns)
column_names = spec[::2]
column_types = spec[1::2]
column_indices = range (len (column_names))
column_indices = range(len(column_names))
for col_index, col_name, in zip (column_indices, column_names):
setattr (cls, col_name, col_index)
for col_index, col_name, in zip(column_indices, column_names):
setattr(cls, col_name, col_index)
cls.column_types = column_types
cls.column_ids = tuple (column_indices)
cls.column_ids = tuple(column_indices)
class Manager (object):
"""GUI Manager base class."""
@classmethod
def iter_item_classes (cls):
def iter_item_classes(cls):
msg = "%s class does not support manager item class access"
raise NotImplementedError (msg % (cls.__name__,))
raise NotImplementedError(msg % (cls.__name__,))
@classmethod
def find_item_class (self, **kw):
def find_item_class(self, **kw):
return self.__find_by_attrs (self.iter_item_classes (), kw)
return self.__find_by_attrs(self.iter_item_classes(), kw)
def iter_items (self):
def iter_items(self):
msg = "%s object does not support manager item access"
raise NotImplementedError (msg % (type (self).__name__,))
raise NotImplementedError(msg % (type(self).__name__,))
def find_item (self, **kw):
def find_item(self, **kw):
return self.__find_by_attrs (self.iter_items (), kw)
return self.__find_by_attrs(self.iter_items(), kw)
@staticmethod
def __find_by_attrs (i, kw):
def __find_by_attrs(i, kw):
from operator import attrgetter
if len (kw) != 1:
raise ValueError ("need exactly one keyword argument")
if len(kw) != 1:
raise ValueError("need exactly one keyword argument")
attr, value = kw.items ()[0]
getter = attrgetter (attr)
attr, value = kw.items()[0]
getter = attrgetter(attr)
for item in i:
if getter (item) == value:
if getter(item) == value:
return item
else:
raise KeyError ("no item such that item.%s == %r" % (attr, value,))
raise KeyError("no item such that item.%s == %r" % (attr, value,))
class StateString (object):
"""Descriptor for binding to StateSection classes."""
def __init__ (self, option, default = None):
def __init__(self, option, default=None):
self.option = option
self.default = default
def __get__ (self, section, section_class = None):
def __get__(self, section, section_class=None):
import ConfigParser
@ -258,253 +267,262 @@ class StateString (object):
return self
try:
return self.get (section)
return self.get(section)
except (ConfigParser.NoSectionError,
ConfigParser.NoOptionError,):
return self.get_default (section)
return self.get_default(section)
def __set__ (self, section, value):
def __set__(self, section, value):
import ConfigParser
self.set (section, value)
self.set(section, value)
def get (self, section):
def get(self, section):
return section.get (self)
return section.get(self)
def get_default (self, section):
def get_default(self, section):
return self.default
def set (self, section, value):
def set(self, section, value):
if value is None:
value = ""
section.set (self, str (value))
section.set(self, str(value))
class StateBool (StateString):
"""Descriptor for binding to StateSection classes."""
def get (self, section):
def get(self, section):
return section.state._parser.getboolean(section._name, self.option)
return section.state._parser.getboolean (section._name, self.option)
class StateInt (StateString):
"""Descriptor for binding to StateSection classes."""
def get (self, section):
def get(self, section):
return section.state._parser.getint(section._name, self.option)
return section.state._parser.getint (section._name, self.option)
class StateInt4 (StateString):
"""Descriptor for binding to StateSection classes. This implements storing
a tuple of 4 integers."""
def get (self, section):
def get(self, section):
value = StateString.get (self, section)
value = StateString.get(self, section)
try:
l = value.split (",")
if len (l) != 4:
l = value.split(",")
if len(l) != 4:
return None
else:
return tuple ((int (v) for v in l))
return tuple((int(v) for v in l))
except (AttributeError, TypeError, ValueError,):
return None
def set (self, section, value):
def set(self, section, value):
if value is None:
svalue = ""
elif len (value) != 4:
raise ValueError ("value needs to be a 4-sequence, or None")
elif len(value) != 4:
raise ValueError("value needs to be a 4-sequence, or None")
else:
svalue = ", ".join ((str (v) for v in value))
svalue = ", ".join((str(v) for v in value))
return StateString.set(self, section, svalue)
return StateString.set (self, section, svalue)
class StateItem (StateString):
"""Descriptor for binding to StateSection classes. This implements storing
a class controlled by a Manager class."""
def __init__ (self, option, manager_class, default = None):
def __init__(self, option, manager_class, default=None):
StateString.__init__ (self, option, default = default)
StateString.__init__(self, option, default=default)
self.manager = manager_class
def get (self, section):
def get(self, section):
value = SectionString.get (self, section)
value = SectionString.get(self, section)
if not value:
return None
return self.parse_item (value)
return self.parse_item(value)
def set (self, section, value):
def set(self, section, value):
if value is None:
svalue = ""
else:
svalue = value.name
StateString.set (self, section, svalue)
StateString.set(self, section, svalue)
def parse_item (self, value):
def parse_item(self, value):
name = value.strip ()
name = value.strip()
try:
return self.manager.find_item_class (name = name)
return self.manager.find_item_class(name=name)
except KeyError:
return None
class StateItemList (StateItem):
"""Descriptor for binding to StateSection classes. This implements storing
an ordered set of Manager items."""
def get (self, section):
def get(self, section):
value = StateString.get (self, section)
value = StateString.get(self, section)
if not value:
return []
classes = []
for name in value.split (","):
item_class = self.parse_item (name)
for name in value.split(","):
item_class = self.parse_item(name)
if item_class is None:
continue
if not item_class in classes:
classes.append (item_class)
classes.append(item_class)
return classes
def get_default (self, section):
def get_default(self, section):
default = StateItem.get_default (self, section)
default = StateItem.get_default(self, section)
if default is None:
return []
else:
return default
def set (self, section, value):
def set(self, section, value):
if value is None:
svalue = ""
else:
svalue = ", ".join ((v.name for v in value))
svalue = ", ".join((v.name for v in value))
StateString.set(self, section, svalue)
StateString.set (self, section, svalue)
class StateSection (object):
_name = None
def __init__ (self, state):
def __init__(self, state):
self.state = state
if self._name is None:
raise NotImplementedError ("subclasses must override the _name attribute")
raise NotImplementedError(
"subclasses must override the _name attribute")
def get (self, state_string):
def get(self, state_string):
return self.state._parser.get (self._name, state_string.option)
return self.state._parser.get(self._name, state_string.option)
def set (self, state_string, value):
def set(self, state_string, value):
import ConfigParser
parser = self.state._parser
try:
parser.set (self._name, state_string.option, value)
parser.set(self._name, state_string.option, value)
except ConfigParser.NoSectionError:
parser.add_section (self._name)
parser.set (self._name, state_string.option, value)
parser.add_section(self._name)
parser.set(self._name, state_string.option, value)
class State (object):
def __init__ (self, filename, old_filenames = ()):
def __init__(self, filename, old_filenames=()):
import ConfigParser
self.sections = {}
self._filename = filename
self._parser = ConfigParser.RawConfigParser ()
success = self._parser.read ([filename])
self._parser = ConfigParser.RawConfigParser()
success = self._parser.read([filename])
if not success:
for old_filename in old_filenames:
success = self._parser.read ([old_filename])
success = self._parser.read([old_filename])
if success:
break
def add_section_class (self, section_class):
def add_section_class(self, section_class):
self.sections[section_class._name] = section_class (self)
self.sections[section_class._name] = section_class(self)
def save (self):
def save(self):
with utils.SaveWriteFile(self._filename, "wt") as fp:
self._parser.write(fp)
with utils.SaveWriteFile (self._filename, "wt") as fp:
self._parser.write (fp)
class WindowState (object):
def __init__ (self):
def __init__(self):
self.logger = logging.getLogger ("ui.window-state")
self.logger = logging.getLogger("ui.window-state")
self.is_maximized = False
def attach (self, window, state):
def attach(self, window, state):
self.window = window
self.state = state
self.window.connect ("window-state-event",
self.handle_window_state_event)
self.window.connect("window-state-event",
self.handle_window_state_event)
geometry = self.state.geometry
if geometry:
self.window.move (*geometry[:2])
self.window.set_default_size (*geometry[2:])
self.window.move(*geometry[:2])
self.window.set_default_size(*geometry[2:])
if self.state.maximized:
self.logger.debug ("initially maximized")
self.window.maximize ()
self.logger.debug("initially maximized")
self.window.maximize()
def detach (self):
def detach(self):
window = self.window
self.state.maximized = self.is_maximized
if not self.is_maximized:
position = tuple (window.get_position ())
size = tuple (window.get_size ())
position = tuple(window.get_position())
size = tuple(window.get_size())
self.state.geometry = position + size
self.window.disconnect_by_func (self.handle_window_state_event)
self.window.disconnect_by_func(self.handle_window_state_event)
self.window = None
def handle_window_state_event (self, window, event):
def handle_window_state_event(self, window, event):
if not event.changed_mask & Gdk.WindowState.MAXIMIZED:
return
if event.new_window_state & Gdk.WindowState.MAXIMIZED:
self.logger.debug ("maximized")
self.logger.debug("maximized")
self.is_maximized = True
else:
self.logger.debug ("unmaximized")
self.logger.debug("unmaximized")
self.is_maximized = False

View file

@ -31,7 +31,8 @@ from gettext import gettext as _, ngettext
import gi
from gi.repository import GObject
from gi.repository import Gtk;
from gi.repository import Gtk
class ExceptionHandler (object):
@ -41,9 +42,11 @@ class ExceptionHandler (object):
_handling_exception = False
def __call__ (self, exc_type, exc_value, exc_traceback):
def __call__(self, exc_type, exc_value, exc_traceback):
raise NotImplementedError(
"derived classes need to override this method")
raise NotImplementedError ("derived classes need to override this method")
class DefaultExceptionHandler (ExceptionHandler):
@ -57,15 +60,16 @@ class DefaultExceptionHandler (ExceptionHandler):
priority = 0
inherit_fork = True
def __init__ (self, excepthook):
def __init__(self, excepthook):
ExceptionHandler.__init__ (self)
ExceptionHandler.__init__(self)
self.excepthook = excepthook
def __call__ (self, *exc_info):
def __call__(self, *exc_info):
return self.excepthook(*exc_info)
return self.excepthook (*exc_info)
class ExitOnInterruptExceptionHandler (ExceptionHandler):
@ -75,80 +79,82 @@ class ExitOnInterruptExceptionHandler (ExceptionHandler):
exit_status = 2
def __call__ (self, *args):
def __call__(self, *args):
print >> sys.stderr, "Interrupt caught, exiting."
sys.exit (self.exit_status)
sys.exit(self.exit_status)
class MainLoopWrapper (ExceptionHandler):
priority = 95
inherit_fork = False
def __init__ (self, enter, exit):
def __init__(self, enter, exit):
ExceptionHandler.__init__ (self)
ExceptionHandler.__init__(self)
self.exc_info = (None,) * 3
self.enter = enter
self.exit = exit
def __call__ (self, *exc_info):
def __call__(self, *exc_info):
self.exc_info = exc_info
self.exit ()
self.exit()
def run (self):
def run(self):
ExceptHookManager.register_handler (self)
ExceptHookManager.register_handler(self)
try:
self.enter ()
self.enter()
finally:
ExceptHookManager.unregister_handler (self)
ExceptHookManager.unregister_handler(self)
if self.exc_info != (None,) * 3:
# Re-raise unhandled exception that occured while running the loop.
exc_type, exc_value, exc_tb = self.exc_info
raise exc_type, exc_value, exc_tb
class ExceptHookManagerClass (object):
def __init__ (self):
def __init__(self):
self._in_forked_child = False
self.handlers = []
def setup (self):
def setup(self):
if sys.excepthook == self.__excepthook:
raise ValueError ("already set up")
raise ValueError("already set up")
hook = sys.excepthook
self.__instrument_excepthook ()
self.__instrument_fork ()
self.register_handler (DefaultExceptionHandler (hook))
self.__instrument_excepthook()
self.__instrument_fork()
self.register_handler(DefaultExceptionHandler(hook))
def shutdown (self):
def shutdown(self):
if sys.excepthook != self.__excepthook:
raise ValueError ("not set up")
raise ValueError("not set up")
self.__restore_excepthook ()
self.__restore_fork ()
self.__restore_excepthook()
self.__restore_fork()
def __instrument_excepthook (self):
def __instrument_excepthook(self):
hook = sys.excepthook
self._original_excepthook = hook
sys.excepthook = self.__excepthook
def __restore_excepthook (self):
def __restore_excepthook(self):
sys.excepthook = self._original_excepthook
def __instrument_fork (self):
def __instrument_fork(self):
try:
fork = os.fork
@ -159,57 +165,57 @@ class ExceptHookManagerClass (object):
self._original_fork = fork
os.fork = self.__fork
def __restore_fork (self):
def __restore_fork(self):
if not hasattr (os, "fork"):
if not hasattr(os, "fork"):
return
os.fork = self._original_fork
def entered_forked_child (self):
def entered_forked_child(self):
self._in_forked_child = True
for handler in tuple (self.handlers):
for handler in tuple(self.handlers):
if not handler.inherit_fork:
self.handlers.remove (handler)
self.handlers.remove(handler)
def register_handler (self, handler):
def register_handler(self, handler):
if self._in_forked_child and not handler.inherit_fork:
return
self.handlers.append (handler)
self.handlers.append(handler)
def unregister_handler (self, handler):
def unregister_handler(self, handler):
self.handlers.remove (handler)
self.handlers.remove(handler)
def __fork (self):
def __fork(self):
pid = self._original_fork ()
pid = self._original_fork()
if pid == 0:
# Child process.
self.entered_forked_child ()
self.entered_forked_child()
return pid
def __excepthook (self, exc_type, exc_value, exc_traceback):
def __excepthook(self, exc_type, exc_value, exc_traceback):
for handler in sorted (self.handlers,
key = attrgetter ("priority"),
reverse = True):
for handler in sorted(self.handlers,
key=attrgetter("priority"),
reverse=True):
if handler._handling_exception:
continue
for type_ in handler.exc_types:
if issubclass (exc_type, type_):
if issubclass(exc_type, type_):
break
else:
continue
handler._handling_exception = True
handler (exc_type, exc_value, exc_traceback)
handler(exc_type, exc_value, exc_traceback)
# Not using try...finally on purpose here. If the handler itself
# fails with an exception, this prevents recursing into it again.
handler._handling_exception = False
@ -217,11 +223,12 @@ class ExceptHookManagerClass (object):
else:
from warnings import warn
warn ("ExceptHookManager: unhandled %r" % (exc_value,),
RuntimeWarning,
stacklevel = 2)
warn("ExceptHookManager: unhandled %r" % (exc_value,),
RuntimeWarning,
stacklevel=2)
ExceptHookManager = ExceptHookManagerClass()
ExceptHookManager = ExceptHookManagerClass ()
class PathsBase (object):
@ -230,70 +237,71 @@ class PathsBase (object):
locale_dir = None
@classmethod
def setup_installed (cls, data_prefix):
def setup_installed(cls, data_prefix):
"""Set up paths for running from a regular installation."""
pass
@classmethod
def setup_uninstalled (cls, source_dir):
def setup_uninstalled(cls, source_dir):
"""Set up paths for running 'uninstalled' (i.e. directly from the
source dist)."""
pass
@classmethod
def ensure_setup (cls):
def ensure_setup(cls):
"""If paths are still not set up, try to set from a fallback."""
if cls.data_dir is None:
source_dir = os.path.dirname (os.path.dirname (os.path.abspath (__file__)))
cls.setup_uninstalled (source_dir)
source_dir = os.path.dirname(
os.path.dirname(os.path.abspath(__file__)))
cls.setup_uninstalled(source_dir)
def __new__ (cls):
def __new__(cls):
raise RuntimeError("do not create instances of this class -- "
"use the class object directly")
raise RuntimeError ("do not create instances of this class -- "
"use the class object directly")
class PathsProgramBase (PathsBase):
program_name = None
@classmethod
def setup_installed (cls, data_prefix):
def setup_installed(cls, data_prefix):
if cls.program_name is None:
raise NotImplementedError ("derived classes need to set program_name attribute")
raise NotImplementedError(
"derived classes need to set program_name attribute")
cls.data_dir = os.path.join (data_prefix, "share", cls.program_name)
cls.icon_dir = os.path.join (data_prefix, "share", "icons")
cls.locale_dir = os.path.join (data_prefix, "share", "locale")
cls.data_dir = os.path.join(data_prefix, "share", cls.program_name)
cls.icon_dir = os.path.join(data_prefix, "share", "icons")
cls.locale_dir = os.path.join(data_prefix, "share", "locale")
@classmethod
def setup_uninstalled (cls, source_dir):
def setup_uninstalled(cls, source_dir):
"""Set up paths for running 'uninstalled' (i.e. directly from the
source dist)."""
# This is essential: The GUI module needs to find the .glade file.
cls.data_dir = os.path.join (source_dir, "data")
cls.data_dir = os.path.join(source_dir, "data")
# The locale data might be missing if "setup.py build" wasn't run.
cls.locale_dir = os.path.join (source_dir, "build", "mo")
cls.locale_dir = os.path.join(source_dir, "build", "mo")
# Not setting icon_dir. It is not useful since we don't employ the
# needed directory structure in the source dist.
class OptionError (Exception):
pass
class OptionParser (object):
def __init__ (self, options):
def __init__(self, options):
self.__entries = []
self.__parsers = {}
@ -304,11 +312,11 @@ class OptionParser (object):
# Remaining args parsing with pygobject does not work with glib before
# 2.13.2 (e.g. Ubuntu Feisty).
## if GObject.glib_version >= (2, 13, 2,):
## self.__entries.append ((GObject.OPTION_REMAINING, "\0", 0, "", "",))
# if GObject.glib_version >= (2, 13, 2,):
# self.__entries.append ((GObject.OPTION_REMAINING, "\0", 0, "", "",))
def add_option (self, long_name, short_name = None, description = None,
arg_name = None, arg_parser = None, hidden = False):
def add_option(self, long_name, short_name=None, description=None,
arg_name=None, arg_parser=None, hidden=False):
flags = 0
@ -327,23 +335,23 @@ class OptionParser (object):
if hidden:
flags |= GObject.OPTION_FLAG_HIDDEN
self.__entries.append ((long_name, short_name, flags, description,
arg_name,))
self.__entries.append((long_name, short_name, flags, description,
arg_name,))
def __handle_option (self, option, arg, group):
def __handle_option(self, option, arg, group):
# See __init__ for glib requirement.
## if option == GObject.OPTION_REMAINING:
## self.__remaining_args.append (arg)
## return
# if option == GObject.OPTION_REMAINING:
# self.__remaining_args.append (arg)
# return
for entry in self.__entries:
long_name, short_name = entry[:2]
arg_name = entry[-1]
if (option != "--%s" % (long_name,) and
option != "-%s" % (short_name,)):
option != "-%s" % (short_name,)):
continue
attr = long_name.replace ("-", "_")
attr = long_name.replace("-", "_")
if arg_name is None:
value = True
elif long_name in self.__parsers:
@ -353,59 +361,60 @@ class OptionParser (object):
self.options[attr] = value
break
def parse (self, argv):
def parse(self, argv):
context = GObject.OptionContext (self.get_parameter_string ())
group = GObject.OptionGroup (None, None, None, self.__handle_option)
context.set_main_group (group)
group.add_entries (self.__entries)
context = GObject.OptionContext(self.get_parameter_string())
group = GObject.OptionGroup(None, None, None, self.__handle_option)
context.set_main_group(group)
group.add_entries(self.__entries)
try:
result_argv = context.parse (argv)
result_argv = context.parse(argv)
except GObject.GError as exc:
raise OptionError (exc.message)
raise OptionError(exc.message)
self.__remaining_args = result_argv[1:]
self.handle_parse_complete (self.__remaining_args)
self.handle_parse_complete(self.__remaining_args)
def get_parameter_string (self):
def get_parameter_string(self):
raise NotImplementedError ("derived classes must override this method")
raise NotImplementedError("derived classes must override this method")
def handle_parse_complete (self, remaining_args):
def handle_parse_complete(self, remaining_args):
pass
class LogOptionParser (OptionParser):
"""Like OptionParser, but adds a --log-level option."""
def __init__ (self, *a, **kw):
def __init__(self, *a, **kw):
OptionParser.__init__ (self, *a, **kw)
OptionParser.__init__(self, *a, **kw)
# TODO: Re-evaluate usage of log levels to use less of them. Like
# unifying warning, error and critical.
self.add_option ("log-level", "l",
"%s (debug, info, warning, error, critical)"
% (_("Enable logging"),),
"LEVEL", self.parse_log_level)
self.add_option("log-level", "l",
"%s (debug, info, warning, error, critical)"
% (_("Enable logging"),),
"LEVEL", self.parse_log_level)
@staticmethod
def parse_log_level (arg):
def parse_log_level(arg):
try:
level = int (arg)
level = int(arg)
except ValueError:
level = {"off" : None,
"none" : None,
"debug" : logging.DEBUG,
"info" : logging.INFO,
"warning" : logging.WARNING,
"error" : logging.ERROR,
"critical" : logging.CRITICAL}.get (arg.strip ().lower ())
level = {"off": None,
"none": None,
"debug": logging.DEBUG,
"info": logging.INFO,
"warning": logging.WARNING,
"error": logging.ERROR,
"critical": logging.CRITICAL}.get(arg.strip().lower())
if level is None:
return None
else:
@ -415,79 +424,85 @@ class LogOptionParser (OptionParser):
level = 0
elif level > 5:
level = 5
return {0 : None,
1 : logging.DEBUG,
2 : logging.INFO,
3 : logging.WARNING,
4 : logging.ERROR,
5 : logging.CRITICAL}[level]
return {0: None,
1: logging.DEBUG,
2: logging.INFO,
3: logging.WARNING,
4: logging.ERROR,
5: logging.CRITICAL}[level]
def _init_excepthooks ():
ExceptHookManager.setup ()
ExceptHookManager.register_handler (ExitOnInterruptExceptionHandler ())
def _init_excepthooks():
def _init_paths (paths):
ExceptHookManager.setup()
ExceptHookManager.register_handler(ExitOnInterruptExceptionHandler())
paths.ensure_setup ()
def _init_locale (gettext_domain = None):
def _init_paths(paths):
paths.ensure_setup()
def _init_locale(gettext_domain=None):
if Paths.locale_dir and gettext_domain is not None:
try:
locale.setlocale (locale.LC_ALL, "")
locale.setlocale(locale.LC_ALL, "")
except locale.Error as exc:
from warnings import warn
warn ("locale error: %s" % (exc,),
RuntimeWarning,
stacklevel = 2)
warn("locale error: %s" % (exc,),
RuntimeWarning,
stacklevel=2)
Paths.locale_dir = None
else:
gettext.bindtextdomain (gettext_domain, Paths.locale_dir)
gettext.textdomain (gettext_domain)
gettext.bind_textdomain_codeset (gettext_domain, "UTF-8")
gettext.bindtextdomain(gettext_domain, Paths.locale_dir)
gettext.textdomain(gettext_domain)
gettext.bind_textdomain_codeset(gettext_domain, "UTF-8")
def _init_options (option_parser = None):
def _init_options(option_parser=None):
if option_parser is None:
return {}
try:
option_parser.parse (sys.argv)
option_parser.parse(sys.argv)
except OptionError as exc:
print >> sys.stderr, exc.args[0]
sys.exit (1)
sys.exit(1)
return option_parser.options
def _init_logging (level = None):
logging.basicConfig (level = level,
format = '%(asctime)s.%(msecs)03d %(levelname)8s %(name)20s: %(message)s',
datefmt = '%H:%M:%S')
def _init_logging(level=None):
logger = logging.getLogger ("main")
logger.debug ("logging at level %s", logging.getLevelName (level))
logger.info ("using Python %i.%i.%i %s %i", *sys.version_info)
logging.basicConfig(level=level,
format='%(asctime)s.%(msecs)03d %(levelname)8s %(name)20s: %(message)s',
datefmt='%H:%M:%S')
def main (option_parser = None, gettext_domain = None, paths = None):
logger = logging.getLogger("main")
logger.debug("logging at level %s", logging.getLevelName(level))
logger.info("using Python %i.%i.%i %s %i", *sys.version_info)
def main(option_parser=None, gettext_domain=None, paths=None):
# FIXME:
global Paths
Paths = paths
_init_excepthooks ()
_init_paths (paths)
_init_locale (gettext_domain)
options = _init_options (option_parser)
_init_excepthooks()
_init_paths(paths)
_init_locale(gettext_domain)
options = _init_options(option_parser)
try:
log_level = options["log_level"]
except KeyError:
_init_logging ()
_init_logging()
else:
_init_logging (log_level)
_init_logging(log_level)
try:
options["main"] (options)
options["main"](options)
finally:
logging.shutdown ()
logging.shutdown()

View file

@ -19,5 +19,7 @@
"""GStreamer Development Utilities Common package."""
import Data, GUI, Main, utils
import Data
import GUI
import Main
import utils

View file

@ -68,6 +68,7 @@ def handle_exception(default_return):
class GenericTreeModel(GObject.GObject, Gtk.TreeModel):
"""A base implementation of a Gtk.TreeModel for python.
The GenericTreeModel eases implementing the Gtk.TreeModel interface in Python.
@ -118,7 +119,8 @@ class GenericTreeModel(GObject.GObject, Gtk.TreeModel):
it = stack.popleft()
if it is not None:
yield self.get_user_data(it)
children = [self.iter_nth_child(it, i) for i in range(self.iter_n_children(it))]
children = [self.iter_nth_child(it, i)
for i in range(self.iter_n_children(it))]
stack.extendleft(reversed(children))
def invalidate_iter(self, iter):

View file

@ -23,29 +23,30 @@ import os
import logging
import subprocess as _subprocess
class SingletonMeta (type):
def __init__ (cls, name, bases, dict_):
def __init__(cls, name, bases, dict_):
from weakref import WeakValueDictionary
super (SingletonMeta, cls).__init__ (name, bases, dict_)
super(SingletonMeta, cls).__init__(name, bases, dict_)
cls._singleton_instances = WeakValueDictionary ()
cls._singleton_instances = WeakValueDictionary()
def __call__ (cls, *a, **kw):
def __call__(cls, *a, **kw):
kw_key = tuple (sorted (kw.iteritems ()))
kw_key = tuple(sorted(kw.iteritems()))
try:
obj = cls._singleton_instances[a + kw_key]
except KeyError:
obj = super (SingletonMeta, cls).__call__ (*a, **kw)
obj = super(SingletonMeta, cls).__call__(*a, **kw)
cls._singleton_instances[a + kw_key] = obj
return obj
def gettext_cache ():
def gettext_cache():
"""Return a callable object that operates like gettext.gettext, but is much
faster when a string is looked up more than once. This is very useful in
loops, where calling gettext.gettext can quickly become a major performance
@ -55,192 +56,197 @@ def gettext_cache ():
d = {}
def gettext_cache_access (s):
def gettext_cache_access(s):
if not s in d:
d[s] = gettext (s)
d[s] = gettext(s)
return d[s]
return gettext_cache_access
class ClassProperty (property):
"Like the property class, but also invokes the getter for class access."
def __init__ (self, fget = None, fset = None, fdel = None, doc = None):
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
property.__init__ (self, fget, fset, fdel, doc)
property.__init__(self, fget, fset, fdel, doc)
self.__fget = fget
def __get__ (self, obj, obj_class = None):
def __get__(self, obj, obj_class=None):
ret = property.__get__ (self, obj, obj_class)
ret = property.__get__(self, obj, obj_class)
if ret == self:
return self.__fget (None)
return self.__fget(None)
else:
return ret
class _XDGClass (object):
"""Partial implementation of the XDG Base Directory specification v0.6.
http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html"""
def __init__ (self):
def __init__(self):
self._add_base_dir ("DATA_HOME", "~/.local/share")
self._add_base_dir ("CONFIG_HOME", "~/.config")
self._add_base_dir ("CACHE_HOME", "~/.cache")
self._add_base_dir("DATA_HOME", "~/.local/share")
self._add_base_dir("CONFIG_HOME", "~/.config")
self._add_base_dir("CACHE_HOME", "~/.cache")
def _add_base_dir (self, name, default):
def _add_base_dir(self, name, default):
dir = os.environ.get ("XDG_%s" % (name,))
dir = os.environ.get("XDG_%s" % (name,))
if not dir:
dir = os.path.expanduser (os.path.join (*default.split ("/")))
dir = os.path.expanduser(os.path.join(*default.split("/")))
setattr (self, name, dir)
setattr(self, name, dir)
XDG = _XDGClass()
XDG = _XDGClass ()
class SaveWriteFile (object):
def __init__ (self, filename, mode = "wt"):
def __init__(self, filename, mode="wt"):
from tempfile import mkstemp
self.logger = logging.getLogger ("tempfile")
self.logger = logging.getLogger("tempfile")
dir = os.path.dirname (filename)
base_name = os.path.basename (filename)
dir = os.path.dirname(filename)
base_name = os.path.basename(filename)
temp_prefix = "%s-tmp" % (base_name,)
if dir:
# Destination dir differs from current directory, ensure that it
# exists:
try:
os.makedirs (dir)
os.makedirs(dir)
except OSError:
pass
self.clean_stale (dir, temp_prefix)
self.clean_stale(dir, temp_prefix)
fd, temp_name = mkstemp (dir = dir, prefix = temp_prefix)
fd, temp_name = mkstemp(dir=dir, prefix=temp_prefix)
self.target_name = filename
self.temp_name = temp_name
self.real_file = os.fdopen (fd, mode)
self.real_file = os.fdopen(fd, mode)
def __enter__ (self):
def __enter__(self):
return self
def __exit__ (self, *exc_args):
def __exit__(self, *exc_args):
if exc_args == (None, None, None,):
self.close ()
self.close()
else:
self.discard ()
self.discard()
def __del__ (self):
def __del__(self):
try:
self.discard ()
self.discard()
except AttributeError:
# If __init__ failed, self has no real_file attribute.
pass
def __close_real (self):
def __close_real(self):
if self.real_file:
self.real_file.close ()
self.real_file.close()
self.real_file = None
def clean_stale (self, dir, temp_prefix):
def clean_stale(self, dir, temp_prefix):
from time import time
from glob import glob
now = time ()
pattern = os.path.join (dir, "%s*" % (temp_prefix,))
now = time()
pattern = os.path.join(dir, "%s*" % (temp_prefix,))
for temp_filename in glob (pattern):
mtime = os.stat (temp_filename).st_mtime
for temp_filename in glob(pattern):
mtime = os.stat(temp_filename).st_mtime
if now - mtime > 3600:
self.logger.info ("deleting stale temporary file %s",
temp_filename)
self.logger.info("deleting stale temporary file %s",
temp_filename)
try:
os.unlink (temp_filename)
os.unlink(temp_filename)
except EnvironmentError as exc:
self.logger.warning ("deleting stale temporary file "
"failed: %s", exc)
self.logger.warning("deleting stale temporary file "
"failed: %s", exc)
def tell (self, *a, **kw):
def tell(self, *a, **kw):
return self.real_file.tell (*a, **kw)
return self.real_file.tell(*a, **kw)
def write (self, *a, **kw):
def write(self, *a, **kw):
return self.real_file.write (*a, **kw)
return self.real_file.write(*a, **kw)
def close (self):
def close(self):
self.__close_real ()
self.__close_real()
if self.temp_name:
try:
os.rename (self.temp_name, self.target_name)
os.rename(self.temp_name, self.target_name)
except OSError as exc:
import errno
if exc.errno == errno.EEXIST:
# We are probably on windows.
os.unlink (self.target_name)
os.rename (self.temp_name, self.target_name)
os.unlink(self.target_name)
os.rename(self.temp_name, self.target_name)
self.temp_name = None
def discard (self):
def discard(self):
self.__close_real ()
self.__close_real()
if self.temp_name:
try:
os.unlink (self.temp_name)
os.unlink(self.temp_name)
except EnvironmentError as exc:
self.logger.warning ("deleting temporary file failed: %s", exc)
self.logger.warning("deleting temporary file failed: %s", exc)
self.temp_name = None
class TeeWriteFile (object):
# TODO Py2.5: Add context manager methods.
def __init__ (self, *file_objects):
def __init__(self, *file_objects):
self.files = list (file_objects)
self.files = list(file_objects)
def close (self):
def close(self):
for file in self.files:
file.close ()
file.close()
def flush (self):
def flush(self):
for file in self.files:
file.flush ()
file.flush()
def write (self, string):
def write(self, string):
for file in self.files:
file.write (string)
file.write(string)
def writelines (self, lines):
def writelines(self, lines):
for file in self.files:
file.writelines (lines)
file.writelines(lines)
class FixedPopen (_subprocess.Popen):
def __init__ (self, args, **kw):
def __init__(self, args, **kw):
# Unconditionally specify all descriptors as redirected, to
# work around Python bug #1358527 (which is triggered for
@ -249,76 +255,78 @@ class FixedPopen (_subprocess.Popen):
close = []
for name in ("stdin", "stdout", "stderr",):
target = kw.get (name)
target = kw.get(name)
if not target:
kw[name] = _subprocess.PIPE
close.append (name)
close.append(name)
_subprocess.Popen.__init__ (self, args, **kw)
_subprocess.Popen.__init__(self, args, **kw)
for name in close:
fp = getattr (self, name)
fp.close ()
setattr (self, name, None)
fp = getattr(self, name)
fp.close()
setattr(self, name, None)
class DevhelpError (EnvironmentError):
pass
class DevhelpUnavailableError (DevhelpError):
pass
class DevhelpClient (object):
def available (self):
def available(self):
try:
self.version ()
self.version()
except DevhelpUnavailableError:
return False
else:
return True
def version (self):
def version(self):
return self._invoke ("--version")
return self._invoke("--version")
def search (self, entry):
def search(self, entry):
self._invoke_no_interact ("-s", entry)
self._invoke_no_interact("-s", entry)
def _check_os_error (self, exc):
def _check_os_error(self, exc):
import errno
if exc.errno == errno.ENOENT:
raise DevhelpUnavailableError ()
raise DevhelpUnavailableError()
def _invoke (self, *args):
def _invoke(self, *args):
from subprocess import PIPE
try:
proc = FixedPopen (("devhelp",) + args,
stdout = PIPE)
proc = FixedPopen(("devhelp",) + args,
stdout=PIPE)
except OSError as exc:
self._check_os_error (exc)
self._check_os_error(exc)
raise
out, err = proc.communicate ()
out, err = proc.communicate()
if proc.returncode is not None and proc.returncode != 0:
raise DevhelpError ("devhelp exited with status %i"
% (proc.returncode,))
raise DevhelpError("devhelp exited with status %i"
% (proc.returncode,))
return out
def _invoke_no_interact (self, *args):
def _invoke_no_interact(self, *args):
from subprocess import PIPE
try:
proc = FixedPopen (("devhelp",) + args)
proc = FixedPopen(("devhelp",) + args)
except OSError as exc:
self._check_os_error (exc)
self._check_os_error(exc)
raise

View file

@ -26,30 +26,33 @@ import re
# Nanosecond resolution (like Gst.SECOND)
SECOND = 1000000000
def time_args (ts):
def time_args(ts):
secs = ts // SECOND
return "%i:%02i:%02i.%09i" % (secs // 60**2,
return "%i:%02i:%02i.%09i" % (secs // 60 ** 2,
secs // 60 % 60,
secs % 60,
ts % SECOND,)
def time_diff_args (time_diff):
def time_diff_args(time_diff):
if time_diff >= 0:
sign = "+"
else:
sign = "-"
secs = abs (time_diff) // SECOND
secs = abs(time_diff) // SECOND
return "%s%02i:%02i.%09i" % (sign,
secs // 60,
secs % 60,
abs (time_diff) % SECOND,)
abs(time_diff) % SECOND,)
def time_args_no_hours (ts):
def time_args_no_hours(ts):
secs = ts // SECOND
@ -57,64 +60,66 @@ def time_args_no_hours (ts):
secs % 60,
ts % SECOND,)
def parse_time (st):
def parse_time(st):
"""Parse time strings that look like "0:00:00.0000000"."""
h, m, s = st.split (":")
secs, subsecs = s.split (".")
h, m, s = st.split(":")
secs, subsecs = s.split(".")
return (long((int(h) * 60 ** 2 + int(m) * 60) * SECOND) +
long(secs) * SECOND + long(subsecs))
return (long ((int (h) * 60**2 + int (m) * 60) * SECOND) +
long (secs) * SECOND + long (subsecs))
class DebugLevel (int):
__names = ["NONE", "ERROR", "WARN", "INFO", "DEBUG", "LOG", "FIXME", "TRACE"]
__names = ["NONE", "ERROR", "WARN",
"INFO", "DEBUG", "LOG", "FIXME", "TRACE"]
__instances = {}
def __new__ (cls, level):
def __new__(cls, level):
try:
level_int = int (level)
level_int = int(level)
except (ValueError, TypeError,):
try:
level_int = cls.__names.index (level.upper ())
level_int = cls.__names.index(level.upper())
except ValueError:
raise ValueError ("no debug level named %r" % (level,))
raise ValueError("no debug level named %r" % (level,))
if level_int in cls.__instances:
return cls.__instances[level_int]
else:
new_instance = int.__new__ (cls, level_int)
new_instance = int.__new__(cls, level_int)
new_instance.name = cls.__names[level_int]
cls.__instances[level_int] = new_instance
return new_instance
def __repr__ (self):
def __repr__(self):
return "<%s %s (%i)>" % (type (self).__name__, self.__names[self], self,)
return "<%s %s (%i)>" % (type(self).__name__, self.__names[self], self,)
def higher_level (self):
def higher_level(self):
if self == len (self.__names) - 1:
raise ValueError ("already the highest debug level")
if self == len(self.__names) - 1:
raise ValueError("already the highest debug level")
return DebugLevel (self + 1)
return DebugLevel(self + 1)
def lower_level (self):
def lower_level(self):
if self == 0:
raise ValueError ("already the lowest debug level")
raise ValueError("already the lowest debug level")
return DebugLevel (self - 1)
return DebugLevel(self - 1)
debug_level_none = DebugLevel ("NONE")
debug_level_error = DebugLevel ("ERROR")
debug_level_warning = DebugLevel ("WARN")
debug_level_info = DebugLevel ("INFO")
debug_level_debug = DebugLevel ("DEBUG")
debug_level_log = DebugLevel ("LOG")
debug_level_fixme = DebugLevel ("FIXME")
debug_level_trace = DebugLevel ("TRACE")
debug_level_none = DebugLevel("NONE")
debug_level_error = DebugLevel("ERROR")
debug_level_warning = DebugLevel("WARN")
debug_level_info = DebugLevel("INFO")
debug_level_debug = DebugLevel("DEBUG")
debug_level_log = DebugLevel("LOG")
debug_level_fixme = DebugLevel("FIXME")
debug_level_trace = DebugLevel("TRACE")
debug_levels = [debug_level_none,
debug_level_trace,
debug_level_fixme,
@ -125,24 +130,27 @@ debug_levels = [debug_level_none,
debug_level_error]
# For stripping color codes:
_escape = re.compile ("\x1b\\[[0-9;]*m")
def strip_escape (s):
_escape = re.compile("\x1b\\[[0-9;]*m")
def strip_escape(s):
# FIXME: This can be optimized further!
while "\x1b" in s:
s = _escape.sub ("", s)
s = _escape.sub("", s)
return s
def default_log_line_regex_ ():
def default_log_line_regex_():
# "DEBUG "
LEVEL = "([A-Z]+)\s+"
# "0x8165430 "
THREAD = r"(0x[0-9a-f]+)\s+" #r"\((0x[0-9a-f]+) - "
THREAD = r"(0x[0-9a-f]+)\s+" # r"\((0x[0-9a-f]+) - "
# "0:00:00.777913000 "
TIME = r"(\d+:\d\d:\d\d\.\d+)\s+"
CATEGORY = "([A-Za-z0-9_-]+)\s+" # "GST_REFCOUNTING ", "flacdec "
CATEGORY = "([A-Za-z0-9_-]+)\s+" # "GST_REFCOUNTING ", "flacdec "
# " 3089 "
PID = r"(\d+)\s*"
FILENAME = r"([^:]*):"
@ -160,58 +168,61 @@ def default_log_line_regex_ ():
CATEGORY, FILENAME, LINE, FUNCTION, ANSI,
OBJECT, ANSI, MESSAGE]
# Old log format:
## expressions = [LEVEL, THREAD, TIME, CATEGORY, PID, FILENAME, LINE,
## FUNCTION, OBJECT, MESSAGE]
# expressions = [LEVEL, THREAD, TIME, CATEGORY, PID, FILENAME, LINE,
# FUNCTION, OBJECT, MESSAGE]
return expressions
def default_log_line_regex ():
expressions = default_log_line_regex_ ()
return re.compile ("".join (expressions))
def default_log_line_regex():
expressions = default_log_line_regex_()
return re.compile("".join(expressions))
class Producer (object):
def __init__ (self):
def __init__(self):
self.consumers = []
def have_load_started (self):
def have_load_started(self):
for consumer in self.consumers:
consumer.handle_load_started ()
consumer.handle_load_started()
def have_load_finished (self):
def have_load_finished(self):
for consumer in self.consumers:
consumer.handle_load_finished ()
consumer.handle_load_finished()
class SortHelper (object):
def __init__ (self, fileobj, offsets):
def __init__(self, fileobj, offsets):
self._gen = self.__gen (fileobj, offsets)
self._gen.next ()
self._gen = self.__gen(fileobj, offsets)
self._gen.next()
# Override in the instance, for performance (this gets called in an
# inner loop):
self.find_insert_position = self._gen.send
@staticmethod
def find_insert_position (insert_time_string):
def find_insert_position(insert_time_string):
# Stub for documentary purposes.
pass
@staticmethod
def __gen (fileobj, offsets):
def __gen(fileobj, offsets):
from math import floor
tell = fileobj.tell
seek = fileobj.seek
read = fileobj.read
time_len = len (time_args (0))
time_len = len(time_args(0))
# We remember the previous insertion point. This gives a nice speed up
# for larger bubbles which are already sorted. TODO: In practice, log
@ -225,11 +236,11 @@ class SortHelper (object):
while True:
insert_time_string = (yield insert_pos)
save_offset = tell ()
save_offset = tell()
if pos_time_string <= insert_time_string:
lo = pos
hi = len (offsets)
hi = len(offsets)
else:
lo = 0
hi = pos
@ -239,9 +250,9 @@ class SortHelper (object):
# logs are "mostly sorted", so the insertion point is much more
# likely to be at the end anyways:
while lo < hi:
mid = int (floor (lo * 0.1 + hi * 0.9))
seek (offsets[mid])
mid_time_string = read (time_len)
mid = int(floor(lo * 0.1 + hi * 0.9))
seek(offsets[mid])
mid_time_string = read(time_len)
if insert_time_string < mid_time_string:
hi = mid
else:
@ -253,54 +264,55 @@ class SortHelper (object):
insert_pos = pos
seek (save_offset)
seek(save_offset)
class LineCache (Producer):
_lines_per_iteration = 50000
def __init__ (self, fileobj, dispatcher):
def __init__(self, fileobj, dispatcher):
Producer.__init__ (self)
Producer.__init__(self)
self.logger = logging.getLogger ("linecache")
self.logger = logging.getLogger("linecache")
self.dispatcher = dispatcher
self.__fileobj = fileobj
self.__fileobj.seek (0, 2)
self.__file_size = self.__fileobj.tell ()
self.__fileobj.seek (0)
self.__fileobj.seek(0, 2)
self.__file_size = self.__fileobj.tell()
self.__fileobj.seek(0)
self.offsets = []
self.levels = [] # FIXME
self.levels = [] # FIXME
def start_loading (self):
def start_loading(self):
self.logger.debug ("dispatching load process")
self.have_load_started ()
self.dispatcher (self.__process ())
self.logger.debug("dispatching load process")
self.have_load_started()
self.dispatcher(self.__process())
def get_progress (self):
def get_progress(self):
return float (self.__fileobj.tell ()) / self.__file_size
return float(self.__fileobj.tell()) / self.__file_size
def __process (self):
def __process(self):
offsets = self.offsets
levels = self.levels
dict_levels = {"T" : debug_level_trace, "F" : debug_level_fixme,
"L" : debug_level_log, "D" : debug_level_debug,
"I" : debug_level_info, "W" : debug_level_warning,
"E" : debug_level_error, " " : debug_level_none}
dict_levels = {"T": debug_level_trace, "F": debug_level_fixme,
"L": debug_level_log, "D": debug_level_debug,
"I": debug_level_info, "W": debug_level_warning,
"E": debug_level_error, " ": debug_level_none}
ANSI = "(?:\x1b\\[[0-9;]*m)?"
ANSI_PATTERN = (r"\d:\d\d:\d\d\.\d+ " + ANSI +
r" *\d+" + ANSI +
r" +0x[0-9a-f]+ +" + ANSI +
r"([TFLDIEW ])")
BARE_PATTERN = ANSI_PATTERN.replace (ANSI, "")
rexp_bare = re.compile (BARE_PATTERN)
rexp_ansi = re.compile (ANSI_PATTERN)
BARE_PATTERN = ANSI_PATTERN.replace(ANSI, "")
rexp_bare = re.compile(BARE_PATTERN)
rexp_ansi = re.compile(ANSI_PATTERN)
rexp = rexp_bare
# Moving attribute lookups out of the loop:
@ -311,11 +323,11 @@ class LineCache (Producer):
offsets_append = offsets.append
dict_levels_get = dict_levels.get
self.__fileobj.seek (0)
self.__fileobj.seek(0)
limit = self._lines_per_iteration
last_line = ""
i = 0
sort_helper = SortHelper (self.__fileobj, offsets)
sort_helper = SortHelper(self.__fileobj, offsets)
find_insert_position = sort_helper.find_insert_position
while True:
i += 1
@ -323,16 +335,16 @@ class LineCache (Producer):
i = 0
yield True
offset = tell ()
line = readline ()
offset = tell()
line = readline()
if not line:
break
match = rexp_match (line)
match = rexp_match(line)
if match is None:
if rexp is rexp_ansi or not "\x1b" in line:
continue
match = rexp_ansi.match (line)
match = rexp_ansi.match(line)
if match is None:
continue
# Switch to slower ANSI parsing:
@ -344,125 +356,130 @@ class LineCache (Producer):
# time to integer. We also don't have to take a substring here,
# which would be a useless memcpy.
if line >= last_line:
levels_append (dict_levels_get (match.group (1), debug_level_none))
offsets_append (offset)
levels_append(
dict_levels_get(match.group(1), debug_level_none))
offsets_append(offset)
last_line = line
else:
pos = find_insert_position (line)
levels.insert (pos, dict_levels_get (match.group (1), debug_level_none))
offsets.insert (pos, offset)
pos = find_insert_position(line)
levels.insert(
pos, dict_levels_get(match.group(1), debug_level_none))
offsets.insert(pos, offset)
self.have_load_finished ()
self.have_load_finished()
yield False
class LogLine (list):
_line_regex = default_log_line_regex ()
_line_regex = default_log_line_regex()
@classmethod
def parse_full (cls, line_string):
def parse_full(cls, line_string):
match = cls._line_regex.match (line_string)
match = cls._line_regex.match(line_string)
if match is None:
## raise ValueError ("not a valid log line (%r)" % (line_string,))
# raise ValueError ("not a valid log line (%r)" % (line_string,))
groups = [0, 0, 0, 0, "", "", 0, "", "", 0]
return cls (groups)
return cls(groups)
line = cls (match.groups ())
line = cls(match.groups())
# Timestamp.
line[0] = parse_time (line[0])
line[0] = parse_time(line[0])
# PID.
line[1] = int (line[1])
line[1] = int(line[1])
# Thread.
line[2] = int (line[2], 16)
line[2] = int(line[2], 16)
# Level (this is handled in LineCache).
line[3] = 0
# Line.
line[6] = int (line[6])
line[6] = int(line[6])
# Message start offset.
line[9] = match.start (9 + 1)
line[9] = match.start(9 + 1)
for col_id in (4, # COL_CATEGORY
5, # COL_FILENAME
7, # COL_FUNCTION,
8,): # COL_OBJECT
line[col_id] = intern (line[col_id] or "")
8,): # COL_OBJECT
line[col_id] = intern(line[col_id] or "")
return line
class LogLines (object):
def __init__ (self, fileobj, line_cache):
def __init__(self, fileobj, line_cache):
self.__fileobj = fileobj
self.__line_cache = line_cache
def __len__ (self):
def __len__(self):
return len (self.__line_cache.offsets)
return len(self.__line_cache.offsets)
def __getitem__ (self, line_index):
def __getitem__(self, line_index):
offset = self.__line_cache.offsets[line_index]
self.__fileobj.seek (offset)
line_string = self.__fileobj.readline ()
line = LogLine.parse_full (line_string)
self.__fileobj.seek(offset)
line_string = self.__fileobj.readline()
line = LogLine.parse_full(line_string)
msg = line_string[line[-1]:]
line[-1] = msg
return line
def __iter__ (self):
def __iter__(self):
l = len (self)
l = len(self)
i = 0
while i < l:
yield self[i]
i += 1
class LogFile (Producer):
def __init__ (self, filename, dispatcher):
def __init__(self, filename, dispatcher):
import mmap
Producer.__init__ (self)
Producer.__init__(self)
self.logger = logging.getLogger ("logfile")
self.logger = logging.getLogger("logfile")
self.path = os.path.normpath (os.path.abspath (filename))
self.__real_fileobj = file (filename, "rb")
self.fileobj = mmap.mmap (self.__real_fileobj.fileno (), 0, access = mmap.ACCESS_READ)
self.line_cache = LineCache (self.fileobj, dispatcher)
self.line_cache.consumers.append (self)
self.path = os.path.normpath(os.path.abspath(filename))
self.__real_fileobj = file(filename, "rb")
self.fileobj = mmap.mmap(
self.__real_fileobj.fileno(), 0, access=mmap.ACCESS_READ)
self.line_cache = LineCache(self.fileobj, dispatcher)
self.line_cache.consumers.append(self)
def get_full_line (self, line_index):
def get_full_line(self, line_index):
offset = self.line_cache.offsets[line_index]
self.fileobj.seek (offset)
line_string = self.fileobj.readline ()
line = LogLine.parse_full (line_string)
self.fileobj.seek(offset)
line_string = self.fileobj.readline()
line = LogLine.parse_full(line_string)
msg = line_string[line[-1]:]
line[-1] = msg
return line
def start_loading (self):
def start_loading(self):
self.logger.debug ("starting load")
self.line_cache.start_loading ()
self.logger.debug("starting load")
self.line_cache.start_loading()
def get_load_progress (self):
def get_load_progress(self):
return self.line_cache.get_progress ()
return self.line_cache.get_progress()
def handle_load_started (self):
def handle_load_started(self):
# Chain up to our consumers:
self.have_load_started ()
self.have_load_started()
def handle_load_finished (self):
self.logger.debug ("finish loading")
self.lines = LogLines (self.fileobj, self.line_cache)
def handle_load_finished(self):
self.logger.debug("finish loading")
self.lines = LogLines(self.fileobj, self.line_cache)
# Chain up to our consumers:
self.have_load_finished ()
self.have_load_finished()

View file

@ -26,19 +26,20 @@ import gi
from GstDebugViewer.GUI.app import App
def main (options):
def main(options):
args = options["args"]
app = App ()
app = App()
# TODO: Once we support more than one window, open one window for each
# supplied filename.
window = app.windows[0]
if len (args) > 0:
window.set_log_file (args[0])
if len(args) > 0:
window.set_log_file(args[0])
app.run ()
app.run()
if __name__ == "__main__":
main ()
main()

View file

@ -28,61 +28,67 @@ from GstDebugViewer import Common
from GstDebugViewer.GUI.columns import ViewColumnManager
from GstDebugViewer.GUI.window import Window
class AppStateSection (Common.GUI.StateSection):
_name = "state"
geometry = Common.GUI.StateInt4 ("window-geometry")
maximized = Common.GUI.StateBool ("window-maximized")
geometry = Common.GUI.StateInt4("window-geometry")
maximized = Common.GUI.StateBool("window-maximized")
column_order = Common.GUI.StateItemList ("column-order", ViewColumnManager)
columns_visible = Common.GUI.StateItemList ("columns-visible", ViewColumnManager)
column_order = Common.GUI.StateItemList("column-order", ViewColumnManager)
columns_visible = Common.GUI.StateItemList(
"columns-visible", ViewColumnManager)
zoom_level = Common.GUI.StateInt("zoom-level")
zoom_level = Common.GUI.StateInt ("zoom-level")
class AppState (Common.GUI.State):
def __init__ (self, *a, **kw):
def __init__(self, *a, **kw):
Common.GUI.State.__init__ (self, *a, **kw)
Common.GUI.State.__init__(self, *a, **kw)
self.add_section_class(AppStateSection)
self.add_section_class (AppStateSection)
class App (object):
def __init__ (self):
def __init__(self):
self.attach ()
self.attach()
def load_plugins (self):
def load_plugins(self):
from GstDebugViewer import Plugins
plugin_classes = list (Plugins.load ([os.path.dirname (Plugins.__file__)]))
plugin_classes = list(
Plugins.load([os.path.dirname(Plugins.__file__)]))
self.plugins = []
for plugin_class in plugin_classes:
plugin = plugin_class (self)
self.plugins.append (plugin)
plugin = plugin_class(self)
self.plugins.append(plugin)
def iter_plugin_features (self):
def iter_plugin_features(self):
for plugin in self.plugins:
for feature in plugin.features:
yield feature
def attach (self):
def attach(self):
config_home = Common.utils.XDG.CONFIG_HOME
state_filename = os.path.join (config_home, "gst-debug-viewer", "state")
state_filename = os.path.join(
config_home, "gst-debug-viewer", "state")
self.state = AppState (state_filename)
self.state = AppState(state_filename)
self.state_section = self.state.sections["state"]
self.load_plugins ()
self.load_plugins()
self.windows = []
# we override expander size because of:
# https://bugzilla.gnome.org/show_bug.cgi?id=615985
rcstring = """
@ -91,39 +97,39 @@ class App (object):
#GtkTreeView::vertical-separator = 0
GtkWidget::focus-line-width = 0
}
widget "*.log_view" style "no-expander-treeview-style"
"""
Gtk.rc_parse_string (rcstring)
Gtk.rc_parse_string(rcstring)
self.open_window ()
self.open_window()
def detach (self):
def detach(self):
# TODO: If we take over deferred saving from the inspector, specify now
# = True here!
self.state.save ()
self.state.save()
def run (self):
def run(self):
try:
Common.Main.MainLoopWrapper (Gtk.main, Gtk.main_quit).run ()
Common.Main.MainLoopWrapper(Gtk.main, Gtk.main_quit).run()
except:
raise
else:
self.detach ()
self.detach()
def open_window (self):
def open_window(self):
self.windows.append (Window (self))
self.windows.append(Window(self))
def close_window (self, window):
def close_window(self, window):
self.windows.remove (window)
self.windows.remove(window)
if not self.windows:
# GtkTreeView takes some time to go down for large files. Let's block
# until the window is hidden:
GObject.idle_add (Gtk.main_quit)
Gtk.main ()
GObject.idle_add(Gtk.main_quit)
Gtk.main()
Gtk.main_quit ()
Gtk.main_quit()

View file

@ -24,130 +24,138 @@ from gi.repository import Gdk
from GstDebugViewer import Data
class Color (object):
def __init__ (self, hex_24):
def __init__(self, hex_24):
if hex_24.startswith ("#"):
if hex_24.startswith("#"):
s = hex_24[1:]
else:
s = hex_24
self._fields = tuple ((int (hs, 16) for hs in (s[:2], s[2:4], s[4:],)))
self._fields = tuple((int(hs, 16) for hs in (s[:2], s[2:4], s[4:],)))
def gdk_color (self):
def gdk_color(self):
return Gdk.color_parse (self.hex_string ())
return Gdk.color_parse(self.hex_string())
def hex_string (self):
def hex_string(self):
return "#%02x%02x%02x" % self._fields
def float_tuple (self):
def float_tuple(self):
return tuple ((float (x) / 255 for x in self._fields))
return tuple((float(x) / 255 for x in self._fields))
def byte_tuple (self):
def byte_tuple(self):
return self._fields
def short_tuple (self):
def short_tuple(self):
return tuple((x << 8 for x in self._fields))
return tuple ((x << 8 for x in self._fields))
class ColorPalette (object):
@classmethod
def get (cls):
def get(cls):
try:
return cls._instance
except AttributeError:
cls._instance = cls ()
cls._instance = cls()
return cls._instance
class TangoPalette (ColorPalette):
def __init__ (self):
def __init__(self):
for name, r, g, b in [("black", 0, 0, 0,),
("white", 255, 255, 255,),
("butter1", 252, 233, 79),
("butter2", 237, 212, 0),
("butter3", 196, 160, 0),
("chameleon1", 138, 226, 52),
("chameleon2", 115, 210, 22),
("chameleon3", 78, 154, 6),
("orange1", 252, 175, 62),
("orange2", 245, 121, 0),
("orange3", 206, 92, 0),
("skyblue1", 114, 159, 207),
("skyblue2", 52, 101, 164),
("skyblue3", 32, 74, 135),
("plum1", 173, 127, 168),
("plum2", 117, 80, 123),
("plum3", 92, 53, 102),
("chocolate1", 233, 185, 110),
("chocolate2", 193, 125, 17),
("chocolate3", 143, 89, 2),
("scarletred1", 239, 41, 41),
("scarletred2", 204, 0, 0),
("scarletred3", 164, 0, 0),
("aluminium1", 238, 238, 236),
("aluminium2", 211, 215, 207),
("aluminium3", 186, 189, 182),
("aluminium4", 136, 138, 133),
("aluminium5", 85, 87, 83),
("aluminium6", 46, 52, 54)]:
setattr(self, name, Color("%02x%02x%02x" % (r, g, b,)))
for name, r, g, b in [("black", 0, 0, 0,),
("white", 255, 255, 255,),
("butter1", 252, 233, 79),
("butter2", 237, 212, 0),
("butter3", 196, 160, 0),
("chameleon1", 138, 226, 52),
("chameleon2", 115, 210, 22),
("chameleon3", 78, 154, 6),
("orange1", 252, 175, 62),
("orange2", 245, 121, 0),
("orange3", 206, 92, 0),
("skyblue1", 114, 159, 207),
("skyblue2", 52, 101, 164),
("skyblue3", 32, 74, 135),
("plum1", 173, 127, 168),
("plum2", 117, 80, 123),
("plum3", 92, 53, 102),
("chocolate1", 233, 185, 110),
("chocolate2", 193, 125, 17),
("chocolate3", 143, 89, 2),
("scarletred1", 239, 41, 41),
("scarletred2", 204, 0, 0),
("scarletred3", 164, 0, 0),
("aluminium1", 238, 238, 236),
("aluminium2", 211, 215, 207),
("aluminium3", 186, 189, 182),
("aluminium4", 136, 138, 133),
("aluminium5", 85, 87, 83),
("aluminium6", 46, 52, 54)]:
setattr (self, name, Color ("%02x%02x%02x" % (r, g, b,)))
class ColorTheme (object):
def __init__ (self):
def __init__(self):
self.colors = {}
def add_color (self, key, *colors):
def add_color(self, key, *colors):
self.colors[key] = colors
class LevelColorTheme (ColorTheme):
pass
class LevelColorThemeTango (LevelColorTheme):
def __init__ (self):
def __init__(self):
LevelColorTheme.__init__ (self)
LevelColorTheme.__init__(self)
p = TangoPalette.get()
self.add_color(Data.debug_level_none, None, None, None)
self.add_color(Data.debug_level_trace, p.black, p.aluminium2)
self.add_color(Data.debug_level_fixme, p.black, p.butter3)
self.add_color(Data.debug_level_log, p.black, p.plum1)
self.add_color(Data.debug_level_debug, p.black, p.skyblue1)
self.add_color(Data.debug_level_info, p.black, p.chameleon1)
self.add_color(Data.debug_level_warning, p.black, p.orange1)
self.add_color(Data.debug_level_error, p.white, p.scarletred1)
p = TangoPalette.get ()
self.add_color (Data.debug_level_none, None, None, None)
self.add_color (Data.debug_level_trace, p.black, p.aluminium2)
self.add_color (Data.debug_level_fixme, p.black, p.butter3)
self.add_color (Data.debug_level_log, p.black, p.plum1)
self.add_color (Data.debug_level_debug, p.black, p.skyblue1)
self.add_color (Data.debug_level_info, p.black, p.chameleon1)
self.add_color (Data.debug_level_warning, p.black, p.orange1)
self.add_color (Data.debug_level_error, p.white, p.scarletred1)
class ThreadColorTheme (ColorTheme):
pass
class ThreadColorThemeTango (ThreadColorTheme):
def __init__ (self):
def __init__(self):
ThreadColorTheme.__init__ (self)
ThreadColorTheme.__init__(self)
t = TangoPalette.get ()
for i, color in enumerate ([t.butter2,
t.orange2,
t.chocolate3,
t.chameleon2,
t.skyblue1,
t.plum1,
t.scarletred1,
t.aluminium6]):
self.add_color (i, color)
t = TangoPalette.get()
for i, color in enumerate([t.butter2,
t.orange2,
t.chocolate3,
t.chameleon2,
t.skyblue1,
t.plum1,
t.scarletred1,
t.aluminium6]):
self.add_color(i, color)

View file

@ -19,7 +19,8 @@
"""GStreamer Debug Viewer GUI module."""
def _ (s):
def _(s):
return s
import logging
@ -31,6 +32,8 @@ from GstDebugViewer.GUI.colors import LevelColorThemeTango
from GstDebugViewer.GUI.models import LazyLogModel, LogModelBase
# Sync with gst-inspector!
class Column (object):
"""A single list view column, managed by a ColumnManager instance."""
@ -42,33 +45,36 @@ class Column (object):
get_data_func = None
get_sort_func = None
def __init__ (self):
def __init__(self):
view_column = Gtk.TreeViewColumn (self.label_header)
view_column = Gtk.TreeViewColumn(self.label_header)
view_column.props.reorderable = True
self.view_column = view_column
class SizedColumn (Column):
default_size = None
def compute_default_size (self):
def compute_default_size(self):
return None
# Sync with gst-inspector?
class TextColumn (SizedColumn):
font_family = None
def __init__ (self):
def __init__(self):
Column.__init__ (self)
Column.__init__(self)
column = self.view_column
cell = Gtk.CellRendererText ()
column.pack_start (cell, True)
cell = Gtk.CellRendererText()
column.pack_start(cell, True)
cell.props.yalign = 0.
cell.props.ypad = 0
@ -78,56 +84,58 @@ class TextColumn (SizedColumn):
cell.props.family_set = True
if self.get_data_func:
data_func = self.get_data_func ()
data_func = self.get_data_func()
assert data_func
id_ = self.id
if id_ is not None:
def cell_data_func (column, cell, model, tree_iter, user_data):
data_func (cell.props, model.get_value (tree_iter, id_))
def cell_data_func(column, cell, model, tree_iter, user_data):
data_func(cell.props, model.get_value(tree_iter, id_))
else:
cell_data_func = data_func
column.set_cell_data_func (cell, cell_data_func)
column.set_cell_data_func(cell, cell_data_func)
elif not self.get_modify_func:
column.add_attribute (cell, "text", self.id)
column.add_attribute(cell, "text", self.id)
else:
self.update_modify_func (column, cell)
self.update_modify_func(column, cell)
column.props.resizable = True
def update_modify_func (self, column, cell):
def update_modify_func(self, column, cell):
modify_func = self.get_modify_func ()
modify_func = self.get_modify_func()
id_ = self.id
def cell_data_func (column, cell, model, tree_iter, user_data):
cell.props.text = modify_func (model.get_value (tree_iter, id_))
column.set_cell_data_func (cell, cell_data_func)
def compute_default_size (self):
def cell_data_func(column, cell, model, tree_iter, user_data):
cell.props.text = modify_func(model.get_value(tree_iter, id_))
column.set_cell_data_func(cell, cell_data_func)
values = self.get_values_for_size ()
def compute_default_size(self):
values = self.get_values_for_size()
if not values:
return SizedColumn.compute_default_size (self)
return SizedColumn.compute_default_size(self)
cell = self.view_column.get_cells ()[0]
cell = self.view_column.get_cells()[0]
if self.get_modify_func is not None:
format = self.get_modify_func ()
format = self.get_modify_func()
else:
def identity (x):
def identity(x):
return x
format = identity
max_width = 0
for value in values:
cell.props.text = format (value)
x, y, w, h = self.view_column.cell_get_size ()
max_width = max (max_width, w)
cell.props.text = format(value)
x, y, w, h = self.view_column.cell_get_size()
max_width = max(max_width, w)
return max_width
def get_values_for_size (self):
def get_values_for_size(self):
return ()
class TimeColumn (TextColumn):
name = "time"
@ -135,43 +143,46 @@ class TimeColumn (TextColumn):
id = LazyLogModel.COL_TIME
font_family = "monospace"
def __init__ (self, *a, **kw):
def __init__(self, *a, **kw):
self.base_time = 0
TextColumn.__init__ (self, *a, **kw)
TextColumn.__init__(self, *a, **kw)
def get_modify_func (self):
def get_modify_func(self):
if self.base_time:
time_diff_args = Data.time_diff_args
base_time = self.base_time
def format_time (value):
def format_time(value):
# TODO: Hard coded to omit trailing zeroes, see below.
return time_diff_args (value - base_time)[:-3]
return time_diff_args(value - base_time)[:-3]
else:
time_args = Data.time_args
def format_time (value):
def format_time(value):
# TODO: This is hard coded to omit hours as well as the last 3
# digits at the end, since current gst uses g_get_current_time,
# which has microsecond precision only.
return time_args (value)[2:-3]
return time_args(value)[2:-3]
return format_time
def get_values_for_size (self):
def get_values_for_size(self):
values = [0]
return values
def set_base_time (self, base_time):
def set_base_time(self, base_time):
self.base_time = base_time
column = self.view_column
cell = column.get_cells ()[0]
self.update_modify_func (column, cell)
cell = column.get_cells()[0]
self.update_modify_func(column, cell)
class LevelColumn (TextColumn):
@ -179,30 +190,31 @@ class LevelColumn (TextColumn):
label_header = _("L")
id = LazyLogModel.COL_LEVEL
def __init__ (self):
def __init__(self):
TextColumn.__init__ (self)
TextColumn.__init__(self)
cell = self.view_column.get_cells ()[0]
cell = self.view_column.get_cells()[0]
cell.props.xalign = .5
@staticmethod
def get_modify_func ():
def get_modify_func():
def format_level (value):
def format_level(value):
return value.name[0]
return format_level
@staticmethod
def get_data_func ():
def get_data_func():
theme = LevelColorThemeTango ()
colors = dict ((level, tuple ((c.gdk_color ()
for c in theme.colors[level])),)
for level in Data.debug_levels
if level != Data.debug_level_none)
def level_data_func (cell_props, level):
theme = LevelColorThemeTango()
colors = dict((level, tuple((c.gdk_color()
for c in theme.colors[level])),)
for level in Data.debug_levels
if level != Data.debug_level_none)
def level_data_func(cell_props, level):
cell_props.text = level.name[0]
if level in colors:
cell_colors = colors[level]
@ -213,7 +225,7 @@ class LevelColumn (TextColumn):
return level_data_func
def get_values_for_size (self):
def get_values_for_size(self):
values = [Data.debug_level_log, Data.debug_level_debug,
Data.debug_level_info, Data.debug_level_warning,
@ -221,6 +233,7 @@ class LevelColumn (TextColumn):
return values
class PidColumn (TextColumn):
name = "pid"
@ -229,14 +242,15 @@ class PidColumn (TextColumn):
font_family = "monospace"
@staticmethod
def get_modify_func ():
def get_modify_func():
return str
def get_values_for_size (self):
def get_values_for_size(self):
return ["999999"]
class ThreadColumn (TextColumn):
name = "thread"
@ -245,16 +259,17 @@ class ThreadColumn (TextColumn):
font_family = "monospace"
@staticmethod
def get_modify_func ():
def get_modify_func():
def format_thread (value):
def format_thread(value):
return "0x%07x" % (value,)
return format_thread
def get_values_for_size (self):
def get_values_for_size(self):
return [int("ffffff", 16)]
return [int ("ffffff", 16)]
class CategoryColumn (TextColumn):
@ -262,10 +277,11 @@ class CategoryColumn (TextColumn):
label_header = _("Category")
id = LazyLogModel.COL_CATEGORY
def get_values_for_size (self):
def get_values_for_size(self):
return ["GST_LONG_CATEGORY", "somelongelement"]
class CodeColumn (TextColumn):
name = "code"
@ -273,71 +289,75 @@ class CodeColumn (TextColumn):
id = None
@staticmethod
def get_data_func ():
def get_data_func():
filename_id = LogModelBase.COL_FILENAME
line_number_id = LogModelBase.COL_LINE_NUMBER
def filename_data_func (column, cell, model, tree_iter, user_data):
args = model.get (tree_iter, filename_id, line_number_id)
def filename_data_func(column, cell, model, tree_iter, user_data):
args = model.get(tree_iter, filename_id, line_number_id)
cell.props.text = "%s:%i" % args
return filename_data_func
def get_values_for_size (self):
def get_values_for_size(self):
return ["gstsomefilename.c:1234"]
class FunctionColumn (TextColumn):
name = "function"
label_header = _("Function")
id = LazyLogModel.COL_FUNCTION
def get_values_for_size (self):
def get_values_for_size(self):
return ["gst_this_should_be_enough"]
class ObjectColumn (TextColumn):
name = "object"
label_header = _("Object")
id = LazyLogModel.COL_OBJECT
def get_values_for_size (self):
def get_values_for_size(self):
return ["longobjectname00"]
class MessageColumn (TextColumn):
name = "message"
label_header = _("Message")
id = None
def __init__ (self, *a, **kw):
def __init__(self, *a, **kw):
self.highlighters = {}
TextColumn.__init__ (self, *a, **kw)
TextColumn.__init__(self, *a, **kw)
def get_data_func (self):
def get_data_func(self):
highlighters = self.highlighters
id_ = LazyLogModel.COL_MESSAGE
def message_data_func (column, cell, model, tree_iter, user_data):
def message_data_func(column, cell, model, tree_iter, user_data):
msg = model.get_value (tree_iter, id_)
msg = model.get_value(tree_iter, id_)
if not highlighters:
cell.props.text = msg
return
if len (highlighters) > 1:
raise NotImplementedError ("FIXME: Support more than one...")
if len(highlighters) > 1:
raise NotImplementedError("FIXME: Support more than one...")
highlighter = highlighters.values ()[0]
highlighter = highlighters.values()[0]
row = model[tree_iter]
ranges = highlighter (row)
ranges = highlighter(row)
if not ranges:
cell.props.text = msg
else:
@ -346,44 +366,46 @@ class MessageColumn (TextColumn):
end = None
for start, end in ranges:
if prev_end < start:
tags.append (GLib.markup_escape_text (msg[prev_end:start]))
msg_escape = GLib.markup_escape_text (msg[start:end])
tags.append ("<span foreground=\'#FFFFFF\'"
" background=\'#0000FF\'>%s</span>" % (msg_escape,))
tags.append(
GLib.markup_escape_text(msg[prev_end:start]))
msg_escape = GLib.markup_escape_text(msg[start:end])
tags.append("<span foreground=\'#FFFFFF\'"
" background=\'#0000FF\'>%s</span>" % (msg_escape,))
prev_end = end
if end is not None:
tags.append (GLib.markup_escape_text (msg[end:]))
cell.props.markup = "".join (tags)
tags.append(GLib.markup_escape_text(msg[end:]))
cell.props.markup = "".join(tags)
return message_data_func
def get_values_for_size (self):
def get_values_for_size(self):
values = ["Just some good minimum size"]
return values
class ColumnManager (Common.GUI.Manager):
column_classes = ()
@classmethod
def iter_item_classes (cls):
def iter_item_classes(cls):
return iter (cls.column_classes)
return iter(cls.column_classes)
def __init__ (self):
def __init__(self):
self.view = None
self.actions = None
self.zoom = 1.0
self.__columns_changed_id = None
self.columns = []
self.column_order = list (self.column_classes)
self.column_order = list(self.column_classes)
self.action_group = Gtk.ActionGroup ("ColumnActions")
self.action_group = Gtk.ActionGroup("ColumnActions")
def make_entry (col_class):
def make_entry(col_class):
return ("show-%s-column" % (col_class.name,),
None,
col_class.label_header,
@ -392,115 +414,115 @@ class ColumnManager (Common.GUI.Manager):
None,
True,)
entries = [make_entry (cls) for cls in self.column_classes]
self.action_group.add_toggle_actions (entries)
entries = [make_entry(cls) for cls in self.column_classes]
self.action_group.add_toggle_actions(entries)
def iter_items (self):
def iter_items(self):
return iter (self.columns)
return iter(self.columns)
def attach (self):
def attach(self):
for col_class in self.column_classes:
action = self.get_toggle_action (col_class)
action = self.get_toggle_action(col_class)
if action.props.active:
self._add_column (col_class ())
action.connect ("toggled",
self.__handle_show_column_action_toggled,
col_class.name)
self._add_column(col_class())
action.connect("toggled",
self.__handle_show_column_action_toggled,
col_class.name)
self.__columns_changed_id = self.view.connect ("columns-changed",
self.__handle_view_columns_changed)
self.__columns_changed_id = self.view.connect("columns-changed",
self.__handle_view_columns_changed)
def detach (self):
def detach(self):
if self.__columns_changed_id is not None:
self.view.disconnect (self.__columns_changed_id)
self.view.disconnect(self.__columns_changed_id)
self.__columns_changed_id = None
def attach_sort (self):
def attach_sort(self):
sort_model = self.view.get_model ()
sort_model = self.view.get_model()
# Inform the sorted tree model of any custom sorting functions.
for col_class in self.column_classes:
if col_class.get_sort_func:
sort_func = col_class.get_sort_func ()
sort_model.set_sort_func (col_class.id, sort_func)
sort_func = col_class.get_sort_func()
sort_model.set_sort_func(col_class.id, sort_func)
def enable_sort (self):
def enable_sort(self):
sort_model = self.view.get_model ()
sort_model = self.view.get_model()
if sort_model:
self.logger.debug ("activating sort")
sort_model.set_sort_column_id (*self.default_sort)
self.logger.debug("activating sort")
sort_model.set_sort_column_id(*self.default_sort)
self.default_sort = None
else:
self.logger.debug ("not activating sort (no model set)")
self.logger.debug("not activating sort (no model set)")
def disable_sort (self):
def disable_sort(self):
self.logger.debug ("deactivating sort")
self.logger.debug("deactivating sort")
sort_model = self.view.get_model ()
sort_model = self.view.get_model()
self.default_sort = tree_sortable_get_sort_column_id (sort_model)
self.default_sort = tree_sortable_get_sort_column_id(sort_model)
sort_model.set_sort_column_id (TREE_SORTABLE_UNSORTED_COLUMN_ID,
Gtk.SortType.ASCENDING)
sort_model.set_sort_column_id(TREE_SORTABLE_UNSORTED_COLUMN_ID,
Gtk.SortType.ASCENDING)
def set_zoom (self, scale):
def set_zoom(self, scale):
for column in self.columns:
cell = column.view_column.get_cells ()[0]
cell = column.view_column.get_cells()[0]
cell.props.scale = scale
column.view_column.queue_resize ()
column.view_column.queue_resize()
self.zoom = scale
def set_base_time (self, base_time):
def set_base_time(self, base_time):
try:
time_column = self.find_item (name = TimeColumn.name)
time_column = self.find_item(name=TimeColumn.name)
except KeyError:
return
time_column.set_base_time (base_time)
self.size_column (time_column)
time_column.set_base_time(base_time)
self.size_column(time_column)
def get_toggle_action (self, column_class):
def get_toggle_action(self, column_class):
action_name = "show-%s-column" % (column_class.name,)
return self.action_group.get_action (action_name)
return self.action_group.get_action(action_name)
def get_initial_column_order (self):
def get_initial_column_order(self):
return tuple (self.column_classes)
return tuple(self.column_classes)
def _add_column (self, column):
def _add_column(self, column):
name = column.name
pos = self.__get_column_insert_position (column)
pos = self.__get_column_insert_position(column)
if self.view.props.fixed_height_mode:
column.view_column.props.sizing = Gtk.TreeViewColumnSizing.FIXED
cell = column.view_column.get_cells ()[0]
cell = column.view_column.get_cells()[0]
cell.props.scale = self.zoom
self.columns.insert (pos, column)
self.view.insert_column (column.view_column, pos)
self.columns.insert(pos, column)
self.view.insert_column(column.view_column, pos)
def _remove_column (self, column):
def _remove_column(self, column):
self.columns.remove (column)
self.view.remove_column (column.view_column)
self.columns.remove(column)
self.view.remove_column(column.view_column)
def __get_column_insert_position (self, column):
def __get_column_insert_position(self, column):
col_class = self.find_item_class (name = column.name)
pos = self.column_order.index (col_class)
col_class = self.find_item_class(name=column.name)
pos = self.column_order.index(col_class)
before = self.column_order[:pos]
shown_names = [col.name for col in self.columns]
for col_class in before:
@ -508,111 +530,114 @@ class ColumnManager (Common.GUI.Manager):
pos -= 1
return pos
def __iter_next_hidden (self, column_class):
def __iter_next_hidden(self, column_class):
pos = self.column_order.index (column_class)
pos = self.column_order.index(column_class)
rest = self.column_order[pos + 1:]
for next_class in rest:
try:
self.find_item (name = next_class.name)
self.find_item(name=next_class.name)
except KeyError:
# No instance -- the column is hidden.
yield next_class
else:
break
def __handle_show_column_action_toggled (self, toggle_action, name):
def __handle_show_column_action_toggled(self, toggle_action, name):
if toggle_action.props.active:
try:
# This should fail.
column = self.find_item (name = name)
column = self.find_item(name=name)
except KeyError:
col_class = self.find_item_class (name = name)
self._add_column (col_class ())
col_class = self.find_item_class(name=name)
self._add_column(col_class())
else:
# Out of sync for some reason.
return
else:
try:
column = self.find_item (name = name)
column = self.find_item(name=name)
except KeyError:
# Out of sync for some reason.
return
else:
self._remove_column (column)
self._remove_column(column)
def __handle_view_columns_changed (self, element_view):
def __handle_view_columns_changed(self, element_view):
view_columns = element_view.get_columns ()
new_visible = [self.find_item (view_column = column)
view_columns = element_view.get_columns()
new_visible = [self.find_item(view_column=column)
for column in view_columns]
# We only care about reordering here.
if len (new_visible) != len (self.columns):
if len(new_visible) != len(self.columns):
return
if new_visible != self.columns:
new_order = []
for column in new_visible:
col_class = self.find_item_class (name = column.name)
new_order.append (col_class)
new_order.extend (self.__iter_next_hidden (col_class))
col_class = self.find_item_class(name=column.name)
new_order.append(col_class)
new_order.extend(self.__iter_next_hidden(col_class))
names = (column.name for column in new_visible)
self.logger.debug ("visible columns reordered: %s",
", ".join (names))
self.logger.debug("visible columns reordered: %s",
", ".join(names))
self.columns[:] = new_visible
self.column_order[:] = new_order
class ViewColumnManager (ColumnManager):
column_classes = (TimeColumn, LevelColumn, PidColumn, ThreadColumn, CategoryColumn,
CodeColumn, FunctionColumn, ObjectColumn, MessageColumn,)
column_classes = (
TimeColumn, LevelColumn, PidColumn, ThreadColumn, CategoryColumn,
CodeColumn, FunctionColumn, ObjectColumn, MessageColumn,)
default_column_classes = (TimeColumn, LevelColumn, CategoryColumn, CodeColumn,
FunctionColumn, ObjectColumn, MessageColumn,)
default_column_classes = (
TimeColumn, LevelColumn, CategoryColumn, CodeColumn,
FunctionColumn, ObjectColumn, MessageColumn,)
def __init__ (self, state):
def __init__(self, state):
ColumnManager.__init__ (self)
ColumnManager.__init__(self)
self.logger = logging.getLogger ("ui.columns")
self.logger = logging.getLogger("ui.columns")
self.state = state
def attach (self, view):
def attach(self, view):
self.view = view
view.connect ("notify::model", self.__handle_notify_model)
view.connect("notify::model", self.__handle_notify_model)
order = self.state.column_order
if len (order) == len (self.column_classes):
if len(order) == len(self.column_classes):
self.column_order[:] = order
visible = self.state.columns_visible
if not visible:
visible = self.default_column_classes
for col_class in self.column_classes:
action = self.get_toggle_action (col_class)
action = self.get_toggle_action(col_class)
action.props.active = (col_class in visible)
ColumnManager.attach (self)
ColumnManager.attach(self)
self.columns_sized = False
def detach (self):
def detach(self):
self.state.column_order = self.column_order
self.state.columns_visible = self.columns
return ColumnManager.detach (self)
return ColumnManager.detach(self)
def set_zoom (self, scale):
def set_zoom(self, scale):
ColumnManager.set_zoom (self, scale)
ColumnManager.set_zoom(self, scale)
if self.view is None:
return
@ -625,92 +650,95 @@ class ViewColumnManager (ColumnManager):
ThreadColumn.name)
for column in self.columns:
if column.name in names:
self.size_column (column)
self.size_column(column)
def size_column (self, column):
def size_column(self, column):
if column.default_size is None:
default_size = column.compute_default_size ()
default_size = column.compute_default_size()
else:
default_size = column.default_size
# FIXME: Abstract away fixed size setting in Column class!
if default_size is None:
# Dummy fallback:
column.view_column.props.fixed_width = 50
self.logger.warning ("%s column does not implement default size", column.name)
self.logger.warning(
"%s column does not implement default size", column.name)
else:
column.view_column.props.fixed_width = default_size
def _add_column (self, column):
def _add_column(self, column):
result = ColumnManager._add_column (self, column)
self.size_column (column)
result = ColumnManager._add_column(self, column)
self.size_column(column)
return result
def _remove_column (self, column):
def _remove_column(self, column):
column.default_size = column.view_column.props.fixed_width
return ColumnManager._remove_column (self, column)
return ColumnManager._remove_column(self, column)
def __handle_notify_model (self, view, gparam):
def __handle_notify_model(self, view, gparam):
if self.columns_sized:
# Already sized.
return
model = self.view.get_model ()
model = self.view.get_model()
if model is None:
return
self.logger.debug ("model changed, sizing columns")
for column in self.iter_items ():
self.size_column (column)
self.logger.debug("model changed, sizing columns")
for column in self.iter_items():
self.size_column(column)
self.columns_sized = True
class WrappingMessageColumn (MessageColumn):
def wrap_to_width (self, width):
def wrap_to_width(self, width):
col = self.view_column
col.props.max_width = width
col.get_cells ()[0].props.wrap_width = width
col.queue_resize ()
col.get_cells()[0].props.wrap_width = width
col.queue_resize()
class LineViewColumnManager (ColumnManager):
column_classes = (TimeColumn, WrappingMessageColumn,)
def __init__ (self):
def __init__(self):
ColumnManager.__init__ (self)
ColumnManager.__init__(self)
def attach (self, window):
def attach(self, window):
self.__size_update = None
self.view = window.widgets.line_view
self.view.set_size_request (0, 0)
self.view.connect_after ("size-allocate", self.__handle_size_allocate)
ColumnManager.attach (self)
self.view.set_size_request(0, 0)
self.view.connect_after("size-allocate", self.__handle_size_allocate)
ColumnManager.attach(self)
def __update_sizes (self):
def __update_sizes(self):
view_width = self.view.get_allocation ().width
view_width = self.view.get_allocation().width
if view_width == self.__size_update:
# Prevent endless recursion.
return
self.__size_update = view_width
col = self.find_item (name = "time")
col = self.find_item(name="time")
other_width = col.view_column.props.width
try:
col = self.find_item (name = "message")
col = self.find_item(name="message")
except KeyError:
return
width = view_width - other_width
col.wrap_to_width (width)
col.wrap_to_width(width)
def __handle_size_allocate (self, self_, allocation):
def __handle_size_allocate(self, self_, allocation):
self.__update_sizes ()
self.__update_sizes()

View file

@ -21,59 +21,69 @@
from GstDebugViewer.GUI.models import LogModelBase
def get_comparison_function (all_but_this):
def get_comparison_function(all_but_this):
if (all_but_this):
return lambda x, y : x == y
return lambda x, y: x == y
else:
return lambda x, y : x != y
return lambda x, y: x != y
class Filter (object):
pass
class DebugLevelFilter (Filter):
only_this, all_but_this, this_and_above = range(3)
def __init__ (self, debug_level, mode = 0):
def __init__(self, debug_level, mode=0):
col_id = LogModelBase.COL_LEVEL
if mode == self.this_and_above:
comparison_function = lambda x, y : x < y
comparison_function = lambda x, y: x < y
else:
comparison_function = get_comparison_function (mode == self.all_but_this)
def filter_func (row):
return comparison_function (row[col_id], debug_level)
comparison_function = get_comparison_function(
mode == self.all_but_this)
def filter_func(row):
return comparison_function(row[col_id], debug_level)
self.filter_func = filter_func
class CategoryFilter (Filter):
def __init__ (self, category, all_but_this = False):
def __init__(self, category, all_but_this=False):
col_id = LogModelBase.COL_CATEGORY
comparison_function = get_comparison_function (all_but_this)
def category_filter_func (row):
comparison_function = get_comparison_function(all_but_this)
def category_filter_func(row):
return comparison_function(row[col_id], category)
self.filter_func = category_filter_func
class ObjectFilter (Filter):
def __init__ (self, object_, all_but_this = False):
def __init__(self, object_, all_but_this=False):
col_id = LogModelBase.COL_OBJECT
comparison_function = get_comparison_function (all_but_this)
def object_filter_func (row):
return comparison_function (row[col_id], object_)
comparison_function = get_comparison_function(all_but_this)
def object_filter_func(row):
return comparison_function(row[col_id], object_)
self.filter_func = object_filter_func
class FilenameFilter (Filter):
def __init__ (self, filename, all_but_this = False):
def __init__(self, filename, all_but_this=False):
col_id = LogModelBase.COL_FILENAME
comparison_function = get_comparison_function (all_but_this)
def filename_filter_func (row):
return comparison_function (row[col_id], filename)
self.filter_func = filename_filter_func
comparison_function = get_comparison_function(all_but_this)
def filename_filter_func(row):
return comparison_function(row[col_id], filename)
self.filter_func = filename_filter_func

View file

@ -44,76 +44,76 @@ class LogModelBase (Common.GUI.GenericTreeModel):
"COL_OBJECT", str,
"COL_MESSAGE", str,)
def __init__ (self):
def __init__(self):
Common.GUI.GenericTreeModel.__init__ (self)
Common.GUI.GenericTreeModel.__init__(self)
##self.props.leak_references = False
# self.props.leak_references = False
self.line_offsets = array ("I")
self.line_levels = [] # FIXME: Not so nice!
self.line_offsets = array("I")
self.line_levels = [] # FIXME: Not so nice!
self.line_cache = {}
def ensure_cached (self, line_offset):
def ensure_cached(self, line_offset):
raise NotImplementedError ("derived classes must override this method")
raise NotImplementedError("derived classes must override this method")
def access_offset (self, offset):
def access_offset(self, offset):
raise NotImplementedError ("derived classes must override this method")
raise NotImplementedError("derived classes must override this method")
def iter_rows_offset (self):
def iter_rows_offset(self):
ensure_cached = self.ensure_cached
line_cache = self.line_cache
line_levels = self.line_levels
COL_LEVEL = self.COL_LEVEL
for i, offset in enumerate (self.line_offsets):
ensure_cached (offset)
for i, offset in enumerate(self.line_offsets):
ensure_cached(offset)
row = line_cache[offset]
row[COL_LEVEL] = line_levels[i] # FIXME
row[COL_LEVEL] = line_levels[i] # FIXME
yield (row, offset,)
def on_get_flags (self):
def on_get_flags(self):
flags = Gtk.TreeModelFlags.LIST_ONLY | Gtk.TreeModelFlags.ITERS_PERSIST
return flags
def on_get_n_columns (self):
def on_get_n_columns(self):
return len (self.column_types)
return len(self.column_types)
def on_get_column_type (self, col_id):
def on_get_column_type(self, col_id):
return self.column_types[col_id]
def on_get_iter (self, path):
def on_get_iter(self, path):
if not path:
return
if len (path) > 1:
if len(path) > 1:
# Flat model.
return None
line_index = path[0]
if line_index > len (self.line_offsets) - 1:
if line_index > len(self.line_offsets) - 1:
return None
return line_index
def on_get_path (self, rowref):
def on_get_path(self, rowref):
line_index = rowref
return (line_index,)
def on_get_value (self, line_index, col_id):
def on_get_value(self, line_index, col_id):
last_index = len (self.line_offsets) - 1
last_index = len(self.line_offsets) - 1
if line_index > last_index:
return None
@ -122,281 +122,288 @@ class LogModelBase (Common.GUI.GenericTreeModel):
return self.line_levels[line_index]
line_offset = self.line_offsets[line_index]
self.ensure_cached (line_offset)
self.ensure_cached(line_offset)
value = self.line_cache[line_offset][col_id]
if col_id == self.COL_MESSAGE:
message_offset = value
value = self.access_offset (line_offset + message_offset).strip ()
value = self.access_offset(line_offset + message_offset).strip()
return value
def get_value_range (self, col_id, start, stop):
def get_value_range(self, col_id, start, stop):
if col_id != self.COL_LEVEL:
raise NotImplementedError ("XXX FIXME")
raise NotImplementedError("XXX FIXME")
return self.line_levels[start:stop]
def on_iter_next (self, line_index):
def on_iter_next(self, line_index):
last_index = len (self.line_offsets) - 1
last_index = len(self.line_offsets) - 1
if line_index >= last_index:
return None
else:
return line_index + 1
def on_iter_children (self, parent):
def on_iter_children(self, parent):
return self.on_iter_nth_child (parent, 0)
return self.on_iter_nth_child(parent, 0)
def on_iter_has_child (self, rowref):
def on_iter_has_child(self, rowref):
return False
def on_iter_n_children (self, rowref):
def on_iter_n_children(self, rowref):
if rowref is not None:
return 0
return len (self.line_offsets)
return len(self.line_offsets)
def on_iter_nth_child (self, parent, n):
def on_iter_nth_child(self, parent, n):
last_index = len (self.line_offsets) - 1
last_index = len(self.line_offsets) - 1
if parent or n > last_index:
return None
return n
def on_iter_parent (self, child):
def on_iter_parent(self, child):
return None
## def on_ref_node (self, rowref):
# def on_ref_node (self, rowref):
## pass
# pass
## def on_unref_node (self, rowref):
# def on_unref_node (self, rowref):
# pass
## pass
class LazyLogModel (LogModelBase):
def __init__ (self, log_obj = None):
def __init__(self, log_obj=None):
LogModelBase.__init__ (self)
LogModelBase.__init__(self)
self.__log_obj = log_obj
if log_obj:
self.set_log (log_obj)
self.set_log(log_obj)
def set_log (self, log_obj):
def set_log(self, log_obj):
self.__fileobj = log_obj.fileobj
self.line_cache.clear ()
self.line_cache.clear()
self.line_offsets = log_obj.line_cache.offsets
self.line_levels = log_obj.line_cache.levels
def access_offset (self, offset):
def access_offset(self, offset):
# TODO: Implement using one slice access instead of seek+readline.
self.__fileobj.seek (offset)
return self.__fileobj.readline ()
self.__fileobj.seek(offset)
return self.__fileobj.readline()
def ensure_cached (self, line_offset):
def ensure_cached(self, line_offset):
if line_offset in self.line_cache:
return
if len (self.line_cache) > 10000:
self.line_cache.clear ()
if len(self.line_cache) > 10000:
self.line_cache.clear()
self.__fileobj.seek (line_offset)
line = self.__fileobj.readline ()
self.__fileobj.seek(line_offset)
line = self.__fileobj.readline()
self.line_cache[line_offset] = Data.LogLine.parse_full(line)
self.line_cache[line_offset] = Data.LogLine.parse_full (line)
class FilteredLogModelBase (LogModelBase):
def __init__ (self, super_model):
def __init__(self, super_model):
LogModelBase.__init__ (self)
LogModelBase.__init__(self)
self.logger = logging.getLogger ("filter-model-base")
self.logger = logging.getLogger("filter-model-base")
self.super_model = super_model
self.access_offset = super_model.access_offset
self.ensure_cached = super_model.ensure_cached
self.line_cache = super_model.line_cache
def line_index_to_super (self, line_index):
def line_index_to_super(self, line_index):
raise NotImplementedError ("index conversion not supported")
raise NotImplementedError("index conversion not supported")
def line_index_from_super (self, super_line_index):
def line_index_from_super(self, super_line_index):
raise NotImplementedError("index conversion not supported")
raise NotImplementedError ("index conversion not supported")
class FilteredLogModel (FilteredLogModelBase):
def __init__ (self, super_model):
def __init__(self, super_model):
FilteredLogModelBase.__init__ (self, super_model)
FilteredLogModelBase.__init__(self, super_model)
self.logger = logging.getLogger ("filtered-log-model")
self.logger = logging.getLogger("filtered-log-model")
self.filters = []
self.reset ()
self.reset()
self.__active_process = None
self.__filter_progress = 0.
def reset (self):
def reset(self):
self.logger.debug ("reset filter")
self.logger.debug("reset filter")
self.line_offsets = self.super_model.line_offsets
self.line_levels = self.super_model.line_levels
self.super_index = xrange (len (self.line_offsets))
self.super_index = xrange(len(self.line_offsets))
del self.filters[:]
def __filter_process (self, filter):
def __filter_process(self, filter):
YIELD_LIMIT = 10000
self.logger.debug ("preparing new filter")
new_line_offsets = array ("I")
self.logger.debug("preparing new filter")
new_line_offsets = array("I")
new_line_levels = []
new_super_index = array ("I")
new_super_index = array("I")
level_id = self.COL_LEVEL
func = filter.filter_func
def enum ():
def enum():
i = 0
for row, offset in self.iter_rows_offset ():
for row, offset in self.iter_rows_offset():
line_index = self.super_index[i]
yield (line_index, row, offset,)
i += 1
self.logger.debug ("running filter")
self.logger.debug("running filter")
progress = 0.
progress_full = float (len (self))
progress_full = float(len(self))
y = YIELD_LIMIT
for i, row, offset in enum ():
if func (row):
new_line_offsets.append (offset)
new_line_levels.append (row[level_id])
new_super_index.append (i)
for i, row, offset in enum():
if func(row):
new_line_offsets.append(offset)
new_line_levels.append(row[level_id])
new_super_index.append(i)
y -= 1
if y == 0:
progress += float (YIELD_LIMIT)
progress += float(YIELD_LIMIT)
self.__filter_progress = progress / progress_full
y = YIELD_LIMIT
yield True
self.line_offsets = new_line_offsets
self.line_levels = new_line_levels
self.super_index = new_super_index
self.logger.debug ("filtering finished")
self.logger.debug("filtering finished")
self.__filter_progress = 1.
self.__handle_filter_process_finished ()
self.__handle_filter_process_finished()
yield False
def add_filter (self, filter, dispatcher):
def add_filter(self, filter, dispatcher):
if self.__active_process is not None:
raise ValueError ("dispatched a filter process already")
raise ValueError("dispatched a filter process already")
self.logger.debug ("adding filter")
self.logger.debug("adding filter")
self.filters.append (filter)
self.filters.append(filter)
self.__dispatcher = dispatcher
self.__active_process = self.__filter_process (filter)
dispatcher (self.__active_process)
self.__active_process = self.__filter_process(filter)
dispatcher(self.__active_process)
def abort_process (self):
def abort_process(self):
if self.__active_process is None:
raise ValueError ("no filter process running")
raise ValueError("no filter process running")
self.__dispatcher.cancel ()
self.__dispatcher.cancel()
self.__active_process = None
self.__dispatcher = None
del self.filters[-1]
def get_filter_progress (self):
def get_filter_progress(self):
if self.__active_process is None:
raise ValueError ("no filter process running")
raise ValueError("no filter process running")
return self.__filter_progress
def __handle_filter_process_finished (self):
def __handle_filter_process_finished(self):
self.__active_process = None
self.handle_process_finished ()
self.handle_process_finished()
def handle_process_finished (self):
def handle_process_finished(self):
pass
def line_index_from_super (self, super_line_index):
def line_index_from_super(self, super_line_index):
return bisect_left (self.super_index, super_line_index)
return bisect_left(self.super_index, super_line_index)
def line_index_to_super (self, line_index):
def line_index_to_super(self, line_index):
return self.super_index[line_index]
def set_range (self, super_start, super_stop):
def set_range(self, super_start, super_stop):
old_super_start = self.line_index_to_super (0)
old_super_stop = self.line_index_to_super (len (self.super_index) - 1) + 1
old_super_start = self.line_index_to_super(0)
old_super_stop = self.line_index_to_super(
len(self.super_index) - 1) + 1
self.logger.debug ("set range (%i, %i), current (%i, %i)",
super_start, super_stop, old_super_start, old_super_stop)
self.logger.debug("set range (%i, %i), current (%i, %i)",
super_start, super_stop, old_super_start, old_super_stop)
if len (self.filters) == 0:
if len(self.filters) == 0:
# Identity.
self.super_index = xrange (super_start, super_stop)
self.line_offsets = SubRange (self.super_model.line_offsets,
super_start, super_stop)
self.line_levels = SubRange (self.super_model.line_levels,
self.super_index = xrange(super_start, super_stop)
self.line_offsets = SubRange(self.super_model.line_offsets,
super_start, super_stop)
self.line_levels = SubRange(self.super_model.line_levels,
super_start, super_stop)
return
if super_start < old_super_start:
# TODO:
raise NotImplementedError ("Only handling further restriction of the range"
" (start offset = %i)" % (start_offset,))
raise NotImplementedError("Only handling further restriction of the range"
" (start offset = %i)" % (start_offset,))
if super_stop > old_super_stop:
# TODO:
raise NotImplementedError ("Only handling further restriction of the range"
" (end offset = %i)" % (stop_offset,))
raise NotImplementedError("Only handling further restriction of the range"
" (end offset = %i)" % (stop_offset,))
start = self.line_index_from_super (super_start)
stop = self.line_index_from_super (super_stop)
start = self.line_index_from_super(super_start)
stop = self.line_index_from_super(super_stop)
self.super_index = SubRange(self.super_index, start, stop)
self.line_offsets = SubRange(self.line_offsets, start, stop)
self.line_levels = SubRange(self.line_levels, start, stop)
self.super_index = SubRange (self.super_index, start, stop)
self.line_offsets = SubRange (self.line_offsets, start, stop)
self.line_levels = SubRange (self.line_levels, start, stop)
class SubRange (object):
__slots__ = ("l", "start", "stop",)
def __init__ (self, l, start, stop):
def __init__(self, l, start, stop):
if start > stop:
raise ValueError ("need start <= stop (got %r, %r)" % (start, stop,))
raise ValueError(
"need start <= stop (got %r, %r)" % (start, stop,))
if type (l) == type (self):
if type(l) == type(self):
# Another SubRange, don't stack:
start += l.start
stop += l.start
@ -406,9 +413,9 @@ class SubRange (object):
self.start = start
self.stop = stop
def __getitem__ (self, i):
def __getitem__(self, i):
if isinstance (i, slice):
if isinstance(i, slice):
stop = i.stop
if stop >= 0:
stop += self.start
@ -419,50 +426,51 @@ class SubRange (object):
else:
return self.l[i + self.start]
def __len__ (self):
def __len__(self):
return self.stop - self.start
def __iter__ (self):
def __iter__(self):
l = self.l
for i in xrange (self.start, self.stop):
for i in xrange(self.start, self.stop):
yield l[i]
class LineViewLogModel (FilteredLogModelBase):
def __init__ (self, super_model):
def __init__(self, super_model):
FilteredLogModelBase.__init__ (self, super_model)
FilteredLogModelBase.__init__(self, super_model)
self.line_offsets = []
self.line_levels = []
self.parent_indices = []
def reset (self):
def reset(self):
del self.line_offsets[:]
del self.line_levels[:]
def line_index_to_super (self, line_index):
def line_index_to_super(self, line_index):
return self.parent_indices[line_index]
def insert_line (self, position, super_line_index):
def insert_line(self, position, super_line_index):
if position == -1:
position = len (self.line_offsets)
position = len(self.line_offsets)
li = super_line_index
self.line_offsets.insert (position, self.super_model.line_offsets[li])
self.line_levels.insert (position, self.super_model.line_levels[li])
self.parent_indices.insert (position, super_line_index)
self.line_offsets.insert(position, self.super_model.line_offsets[li])
self.line_levels.insert(position, self.super_model.line_levels[li])
self.parent_indices.insert(position, super_line_index)
path = (position,)
tree_iter = self.get_iter (path)
self.row_inserted (path, tree_iter)
tree_iter = self.get_iter(path)
self.row_inserted(path, tree_iter)
def replace_line (self, line_index, super_line_index):
def replace_line(self, line_index, super_line_index):
li = line_index
self.line_offsets[li] = self.super_model.line_offsets[super_line_index]
@ -470,10 +478,10 @@ class LineViewLogModel (FilteredLogModelBase):
self.parent_indices[li] = super_line_index
path = (line_index,)
tree_iter = self.get_iter (path)
self.row_changed (path, tree_iter)
tree_iter = self.get_iter(path)
self.row_changed(path, tree_iter)
def remove_line (self, line_index):
def remove_line(self, line_index):
for l in (self.line_offsets,
self.line_levels,
@ -481,4 +489,4 @@ class LineViewLogModel (FilteredLogModelBase):
del l[line_index]
path = (line_index,)
self.row_deleted (path)
self.row_deleted(path)

File diff suppressed because it is too large Load diff

View file

@ -27,40 +27,43 @@ Common = GstDebugViewer.Common
GETTEXT_DOMAIN = "gst-debug-viewer"
def main_version ():
def main_version():
from GstDebugViewer import version
print "GStreamer Debug Viewer %s" % (version,)
class Paths (Common.Main.PathsProgramBase):
program_name = "gst-debug-viewer"
class OptionParser (Common.Main.LogOptionParser):
def __init__ (self, options):
def __init__(self, options):
Common.Main.LogOptionParser.__init__ (self, options)
Common.Main.LogOptionParser.__init__(self, options)
options["main"] = None
options["args"] = []
self.add_option ("version", None, _("Display version and exit"))
self.add_option("version", None, _("Display version and exit"))
def get_parameter_string (self):
def get_parameter_string(self):
return _("[FILENAME] - Display and analyze GStreamer debug log files")
def handle_parse_complete (self, remaining_args):
def handle_parse_complete(self, remaining_args):
try:
version = self.options["version"]
except KeyError:
pass
else:
main_version ()
sys.exit (0)
main_version()
sys.exit(0)
if self.options["main"] is None:
from GstDebugViewer import GUI
@ -68,11 +71,12 @@ class OptionParser (Common.Main.LogOptionParser):
self.options["args"][:] = remaining_args
def main ():
def main():
options = {}
parser = OptionParser (options)
parser = OptionParser(options)
Common.Main.main (option_parser = parser,
gettext_domain = GETTEXT_DOMAIN,
paths = Paths)
Common.Main.main(option_parser=parser,
gettext_domain=GETTEXT_DOMAIN,
paths=Paths)

View file

@ -21,42 +21,45 @@
from GstDebugViewer.Plugins import FeatureBase, PluginBase
class ColorizeLevels (FeatureBase):
def attach (self, window):
def attach(self, window):
pass
def detach (self, window):
def detach(self, window):
pass
class LevelColorSentinel (object):
def processor (self, proc):
def processor(self, proc):
for row in proc:
yield None
class ColorizeCategories (FeatureBase):
def attach (self, window):
def attach(self, window):
pass
def detach (self, window):
def detach(self, window):
pass
class CategoryColorSentinel (object):
def processor (self):
def processor(self):
pass
class Plugin (PluginBase):
features = [ColorizeLevels, ColorizeCategories]

View file

@ -26,39 +26,45 @@ from GstDebugViewer.Plugins import FeatureBase, PluginBase
from gettext import gettext as _
from gi.repository import Gtk
class FilePropertiesSentinel (object):
pass
class FilePropertiesDialog (Gtk.Dialog):
pass
class FilePropertiesFeature (FeatureBase):
def __init__ (self, *a, **kw):
def __init__(self, *a, **kw):
self.action_group = Gtk.ActionGroup ("FilePropertiesActions")
self.action_group.add_actions ([("show-file-properties", Gtk.STOCK_PROPERTIES,
_("_Properties"), "<Ctrl>P")])
self.action_group = Gtk.ActionGroup("FilePropertiesActions")
self.action_group.add_actions(
[("show-file-properties", Gtk.STOCK_PROPERTIES,
_("_Properties"), "<Ctrl>P")])
def attach (self, window):
def attach(self, window):
ui = window.ui_manager
ui.insert_action_group (self.action_group, 0)
ui.insert_action_group(self.action_group, 0)
self.merge_id = ui.new_merge_id ()
ui.add_ui (self.merge_id, "/menubar/FileMenu/FileMenuAdditions",
"FileProperties", "show-file-properties",
Gtk.UIManagerItemType.MENUITEM, False)
self.merge_id = ui.new_merge_id()
ui.add_ui(self.merge_id, "/menubar/FileMenu/FileMenuAdditions",
"FileProperties", "show-file-properties",
Gtk.UIManagerItemType.MENUITEM, False)
handler = self.handle_action_activate
self.action_group.get_action ("show-file-properties").connect ("activate", handler)
self.action_group.get_action(
"show-file-properties").connect("activate", handler)
def handle_action_activate (self, action):
def handle_action_activate(self, action):
pass
class Plugin (PluginBase):
features = (FilePropertiesFeature,)

View file

@ -28,9 +28,10 @@ from gettext import gettext as _
from gi.repository import GObject, GLib
from gi.repository import Gtk
class SearchOperation (object):
def __init__ (self, model, search_text, search_forward = True, start_position = None):
def __init__(self, model, search_text, search_forward=True, start_position=None):
self.model = model
self.search_text = search_text
@ -38,19 +39,19 @@ class SearchOperation (object):
self.start_position = start_position
col_id = GUI.models.LogModelBase.COL_MESSAGE
len_search_text = len (search_text)
len_search_text = len(search_text)
def match_func (model_row):
def match_func(model_row):
message = model_row[col_id]
if search_text in message:
ranges = []
start = 0
while True:
pos = message.find (search_text, start)
pos = message.find(search_text, start)
if pos == -1:
break
ranges.append ((pos, pos + len_search_text,))
ranges.append((pos, pos + len_search_text,))
start = pos + len_search_text
return ranges
else:
@ -58,25 +59,26 @@ class SearchOperation (object):
self.match_func = match_func
class SearchSentinel (object):
def __init__ (self):
def __init__(self):
self.dispatcher = Common.Data.GSourceDispatcher ()
self.dispatcher = Common.Data.GSourceDispatcher()
self.cancelled = False
def run_for (self, operation):
def run_for(self, operation):
self.dispatcher.cancel ()
self.dispatcher (self.__process (operation))
self.dispatcher.cancel()
self.dispatcher(self.__process(operation))
self.cancelled = False
def abort (self):
def abort(self):
self.dispatcher.cancel ()
self.dispatcher.cancel()
self.cancelled = True
def __process (self, operation):
def __process(self, operation):
model = operation.model
@ -85,9 +87,9 @@ class SearchSentinel (object):
elif operation.search_forward:
start_pos = 0
else:
start_pos = len (model) - 1
start_pos = len(model) - 1
start_iter = model.iter_nth_child (None, start_pos)
start_iter = model.iter_nth_child(None, start_pos)
match_func = operation.match_func
if operation.search_forward:
@ -95,13 +97,15 @@ class SearchSentinel (object):
else:
# FIXME: This is really ugly.
nth_child = model.iter_nth_child
def iter_next_ ():
for i in xrange (start_pos, -1, -1):
yield nth_child (None, i)
def iter_next_():
for i in xrange(start_pos, -1, -1):
yield nth_child(None, i)
yield None
it_ = iter_next_ ()
def iter_next (it):
return it_.next ()
it_ = iter_next_()
def iter_next(it):
return it_.next()
YIELD_LIMIT = 1000
i = YIELD_LIMIT
@ -112,110 +116,112 @@ class SearchSentinel (object):
yield True
i = YIELD_LIMIT
row = model[tree_iter]
if match_func (row):
self.handle_match_found (model, tree_iter)
tree_iter = iter_next (tree_iter)
if match_func(row):
self.handle_match_found(model, tree_iter)
tree_iter = iter_next(tree_iter)
if not self.cancelled:
self.handle_search_complete ()
self.handle_search_complete()
yield False
def handle_match_found (self, model, tree_iter):
def handle_match_found(self, model, tree_iter):
pass
def handle_search_complete (self):
def handle_search_complete(self):
pass
class FindBarWidget (Gtk.HBox):
__status = {"no-match-found" : _N("No match found"),
"searching" : _N("Searching...")}
__status = {"no-match-found": _N("No match found"),
"searching": _N("Searching...")}
def __init__ (self, action_group):
def __init__(self, action_group):
GObject.GObject.__init__ (self)
GObject.GObject.__init__(self)
label = Gtk.Label(label=_("Find:"))
self.pack_start (label, False, False, 2)
self.pack_start(label, False, False, 2)
self.entry = Gtk.Entry ()
self.pack_start (self.entry, True, True, 0)
self.entry = Gtk.Entry()
self.pack_start(self.entry, True, True, 0)
prev_action = action_group.get_action ("goto-previous-search-result")
prev_button = Gtk.Button ()
prev_button.set_related_action (prev_action)
self.pack_start (prev_button, False, False, 0)
prev_action = action_group.get_action("goto-previous-search-result")
prev_button = Gtk.Button()
prev_button.set_related_action(prev_action)
self.pack_start(prev_button, False, False, 0)
next_action = action_group.get_action ("goto-next-search-result")
next_button = Gtk.Button ()
next_button.set_related_action (next_action)
self.pack_start (next_button, False, False, 0)
next_action = action_group.get_action("goto-next-search-result")
next_button = Gtk.Button()
next_button.set_related_action(next_action)
self.pack_start(next_button, False, False, 0)
self.status_label = Gtk.Label ()
self.status_label = Gtk.Label()
self.status_label.props.xalign = 0.
self.status_label.props.use_markup = True
self.pack_start (self.status_label, False, False, 6)
self.__compute_status_size ()
self.status_label.connect ("notify::style", self.__handle_notify_style)
self.pack_start(self.status_label, False, False, 6)
self.__compute_status_size()
self.status_label.connect("notify::style", self.__handle_notify_style)
self.show_all ()
self.show_all()
def __compute_status_size (self):
def __compute_status_size(self):
label = self.status_label
old_markup = label.props.label
label.set_size_request (-1, -1)
label.set_size_request(-1, -1)
max_width = 0
try:
for status in self.__status.values ():
self.__set_status (_(status))
req = label.size_request ()
max_width = max (max_width, req.width)
label.set_size_request (max_width, -1)
for status in self.__status.values():
self.__set_status(_(status))
req = label.size_request()
max_width = max(max_width, req.width)
label.set_size_request(max_width, -1)
finally:
label.props.label = old_markup
def __handle_notify_style (self, *a, **kw):
def __handle_notify_style(self, *a, **kw):
self.__compute_status_size ()
self.__compute_status_size()
def __set_status (self, text):
def __set_status(self, text):
markup = "<b>%s</b>" % (GLib.markup_escape_text (text),)
markup = "<b>%s</b>" % (GLib.markup_escape_text(text),)
self.status_label.props.label = markup
def status_no_match_found (self):
def status_no_match_found(self):
self.__set_status (_(self.__status["no-match-found"]))
self.__set_status(_(self.__status["no-match-found"]))
def status_searching (self):
def status_searching(self):
self.__set_status (_(self.__status["searching"]))
self.__set_status(_(self.__status["searching"]))
def clear_status (self):
def clear_status(self):
self.__set_status("")
self.__set_status ("")
class FindBarFeature (FeatureBase):
def __init__ (self, app):
def __init__(self, app):
FeatureBase.__init__ (self, app)
FeatureBase.__init__(self, app)
self.logger = logging.getLogger ("ui.findbar")
self.logger = logging.getLogger("ui.findbar")
self.action_group = Gtk.ActionGroup ("FindBarActions")
self.action_group.add_toggle_actions ([("show-find-bar",
None,
_("Find Bar"),
"<Ctrl>F")])
self.action_group.add_actions ([("goto-next-search-result",
None, _("Goto Next Match"),
"<Ctrl>G"),
("goto-previous-search-result",
self.action_group = Gtk.ActionGroup("FindBarActions")
self.action_group.add_toggle_actions([("show-find-bar",
None,
_("Find Bar"),
"<Ctrl>F")])
self.action_group.add_actions([("goto-next-search-result",
None, _("Goto Next Match"),
"<Ctrl>G"),
("goto-previous-search-result",
None, _("Goto Previous Match"),
"<Ctrl><Shift>G")])
@ -226,176 +232,181 @@ class FindBarFeature (FeatureBase):
self.prev_match = None
self.scroll_match = False
self.sentinel = SearchSentinel ()
self.sentinel = SearchSentinel()
self.sentinel.handle_match_found = self.handle_match_found
self.sentinel.handle_search_complete = self.handle_search_complete
def scroll_view_to_line (self, line_index):
def scroll_view_to_line(self, line_index):
view = self.log_view
path = Gtk.TreePath((line_index,))
start_path, end_path = view.get_visible_range ()
start_path, end_path = view.get_visible_range()
if path >= start_path and path <= end_path:
self.logger.debug ("line index %i already visible, not scrolling", line_index)
self.logger.debug(
"line index %i already visible, not scrolling", line_index)
return
self.logger.debug ("scrolling to line_index %i", line_index)
view.scroll_to_cell (path, use_align = True, row_align = .5)
self.logger.debug("scrolling to line_index %i", line_index)
view.scroll_to_cell(path, use_align=True, row_align=.5)
def handle_attach_window (self, window):
def handle_attach_window(self, window):
self.window = window
ui = window.ui_manager
ui.insert_action_group (self.action_group, 0)
ui.insert_action_group(self.action_group, 0)
self.log_view = window.log_view
self.merge_id = ui.new_merge_id ()
self.merge_id = ui.new_merge_id()
for name, action_name in [("ViewFindBar", "show-find-bar",),
("ViewNextResult", "goto-next-search-result",),
("ViewNextResult",
"goto-next-search-result",),
("ViewPrevResult", "goto-previous-search-result",)]:
ui.add_ui (self.merge_id, "/menubar/ViewMenu/ViewMenuAdditions",
name, action_name, Gtk.UIManagerItemType.MENUITEM, False)
ui.add_ui(self.merge_id, "/menubar/ViewMenu/ViewMenuAdditions",
name, action_name, Gtk.UIManagerItemType.MENUITEM, False)
box = window.widgets.vbox_view
self.bar = FindBarWidget (self.action_group)
box.pack_end (self.bar, False, False, 0)
self.bar.hide ()
self.bar = FindBarWidget(self.action_group)
box.pack_end(self.bar, False, False, 0)
self.bar.hide()
action = self.action_group.get_action ("show-find-bar")
action = self.action_group.get_action("show-find-bar")
handler = self.handle_show_find_bar_action_toggled
action.connect ("toggled", handler)
action.connect("toggled", handler)
action = self.action_group.get_action ("goto-previous-search-result")
action = self.action_group.get_action("goto-previous-search-result")
handler = self.handle_goto_previous_search_result_action_activate
action.props.sensitive = False
action.connect ("activate", handler)
action.connect("activate", handler)
action = self.action_group.get_action ("goto-next-search-result")
action = self.action_group.get_action("goto-next-search-result")
handler = self.handle_goto_next_search_result_action_activate
action.props.sensitive = False
action.connect ("activate", handler)
action.connect("activate", handler)
self.bar.entry.connect ("changed", self.handle_entry_changed)
self.bar.entry.connect("changed", self.handle_entry_changed)
def handle_detach_window (self, window):
def handle_detach_window(self, window):
self.window = None
window.ui_manager.remove_ui (self.merge_id)
window.ui_manager.remove_ui(self.merge_id)
self.merge_id = None
def handle_show_find_bar_action_toggled (self, action):
def handle_show_find_bar_action_toggled(self, action):
if action.props.active:
self.bar.show ()
self.bar.entry.grab_focus ()
self.update_search ()
self.bar.show()
self.bar.entry.grab_focus()
self.update_search()
else:
try:
column = self.window.column_manager.find_item (name = "message")
column = self.window.column_manager.find_item(
name="message")
del column.highlighters[self]
except KeyError:
pass
self.bar.clear_status ()
self.bar.hide ()
self.bar.clear_status()
self.bar.hide()
for action_name in ["goto-next-search-result",
"goto-previous-search-result"]:
self.action_group.get_action (action_name).props.sensitive = False
self.action_group.get_action(
action_name).props.sensitive = False
def handle_goto_previous_search_result_action_activate (self, action):
def handle_goto_previous_search_result_action_activate(self, action):
if self.prev_match is None:
self.logger.warning ("inconsistent action sensitivity")
self.logger.warning("inconsistent action sensitivity")
return
self.scroll_view_to_line (self.prev_match)
self.scroll_view_to_line(self.prev_match)
self.prev_match = None
start_path = self.log_view.get_visible_range ()[0]
start_path = self.log_view.get_visible_range()[0]
new_position = start_path[0] - 1
self.start_search_operation (start_position = new_position,
forward = False)
self.start_search_operation(start_position=new_position,
forward=False)
# FIXME
def handle_goto_next_search_result_action_activate (self, action):
def handle_goto_next_search_result_action_activate(self, action):
if self.next_match is None:
self.logger.warning ("inconsistent action sensitivity")
self.logger.warning("inconsistent action sensitivity")
return
self.scroll_view_to_line (self.next_match)
self.scroll_view_to_line(self.next_match)
self.next_match = None
end_path = self.log_view.get_visible_range ()[1]
end_path = self.log_view.get_visible_range()[1]
new_position = end_path[0] + 1
self.start_search_operation (start_position = new_position,
forward = True)
self.start_search_operation(start_position=new_position,
forward=True)
# FIXME: Finish.
## model = self.log_view.get_model ()
# model = self.log_view.get_model ()
## start_path, end_path = self.log_view.get_visible_range ()
## start_index, end_index = start_path[0], end_path[0]
# start_path, end_path = self.log_view.get_visible_range ()
# start_index, end_index = start_path[0], end_path[0]
## for line_index in self.matches:
## if line_index > end_index:
## break
## else:
## return
# for line_index in self.matches:
# if line_index > end_index:
# break
# else:
# return
## self.scroll_view_to_line (line_index)
# self.scroll_view_to_line (line_index)
def handle_entry_changed (self, entry):
def handle_entry_changed(self, entry):
self.update_search ()
self.update_search()
def update_search (self):
def update_search(self):
model = self.log_view.get_model ()
model = self.log_view.get_model()
search_text = self.bar.entry.props.text
column = self.window.column_manager.find_item (name = "message")
column = self.window.column_manager.find_item(name="message")
if search_text == "":
self.logger.debug ("search string set to '', aborting search")
self.logger.debug("search string set to '', aborting search")
self.search_state = None
self.next_match = None
self.prev_match = None
self.update_sensitivity ()
self.sentinel.abort ()
self.update_sensitivity()
self.sentinel.abort()
try:
del column.highlighters[self]
except KeyError:
pass
else:
self.logger.debug ("starting search for %r", search_text)
self.logger.debug("starting search for %r", search_text)
self.next_match = None
self.prev_match = None
self.update_sensitivity ()
self.update_sensitivity()
self.scroll_match = True
start_path = self.log_view.get_visible_range ()[0]
self.start_search_operation (search_text, start_position = start_path[0])
self.bar.status_searching ()
start_path = self.log_view.get_visible_range()[0]
self.start_search_operation(
search_text, start_position=start_path[0])
self.bar.status_searching()
column.highlighters[self] = self.operation.match_func
self.window.update_view ()
self.window.update_view()
def update_sensitivity (self):
def update_sensitivity(self):
for name, value in (("goto-next-search-result", self.next_match,),
("goto-previous-search-result", self.prev_match,),):
action = self.action_group.get_action (name)
action = self.action_group.get_action(name)
action.props.sensitive = (value is not None)
def start_search_operation (self, search_text = None, forward = True, start_position = None):
def start_search_operation(self, search_text=None, forward=True, start_position=None):
model = self.log_view.get_model ()
model = self.log_view.get_model()
if forward:
self.search_state = "search-forward"
@ -404,75 +415,79 @@ class FindBarFeature (FeatureBase):
else:
self.search_state = "search-backward"
if start_position is None:
start_position = len (model) - 1
start_position = len(model) - 1
if search_text is None:
operation = self.operation
if operation is None:
raise ValueError ("search_text not given but have no previous search operation")
raise ValueError(
"search_text not given but have no previous search operation")
search_text = operation.search_text
self.operation = SearchOperation (model, search_text,
start_position = start_position,
search_forward = forward)
self.sentinel.run_for (self.operation)
self.operation = SearchOperation(model, search_text,
start_position=start_position,
search_forward=forward)
self.sentinel.run_for(self.operation)
def handle_match_found (self, model, tree_iter):
def handle_match_found(self, model, tree_iter):
if not self.search_state in ("search-forward", "search-backward",):
self.logger.warning ("inconsistent search state %r", self.search_state)
self.logger.warning(
"inconsistent search state %r", self.search_state)
return
line_index = model.get_path (tree_iter)[0]
line_index = model.get_path(tree_iter)[0]
forward_search = (self.search_state == "search-forward")
if forward_search:
self.logger.debug ("forward search for %r matches line %i",
self.operation.search_text, line_index)
self.logger.debug("forward search for %r matches line %i",
self.operation.search_text, line_index)
else:
self.logger.debug ("backward search for %r matches line %i",
self.operation.search_text, line_index)
self.logger.debug("backward search for %r matches line %i",
self.operation.search_text, line_index)
self.sentinel.abort ()
self.sentinel.abort()
if self.scroll_match:
self.logger.debug ("scrolling to matching line")
self.scroll_view_to_line (line_index)
self.logger.debug("scrolling to matching line")
self.scroll_view_to_line(line_index)
# Now search for the next one:
self.scroll_match = False
# FIXME: Start with first line that is outside of the visible range.
self.start_search_operation (start_position = line_index + 1,
forward = forward_search)
# FIXME: Start with first line that is outside of the visible
# range.
self.start_search_operation(start_position=line_index + 1,
forward=forward_search)
else:
if forward_search:
self.next_match = line_index
self.search_state = "search-backward"
self.start_search_operation (forward = False,
start_position = line_index - 1)
self.start_search_operation(forward=False,
start_position=line_index - 1)
else:
self.prev_match = line_index
self.update_sensitivity ()
self.bar.clear_status ()
self.update_sensitivity()
self.bar.clear_status()
def handle_search_complete (self):
def handle_search_complete(self):
if self.search_state == "search-forward":
self.logger.debug ("forward search for %r reached last line",
self.operation.search_text)
self.logger.debug("forward search for %r reached last line",
self.operation.search_text)
self.next_match = None
elif self.search_state == "search-backward":
self.logger.debug ("backward search for %r reached first line",
self.operation.search_text)
self.logger.debug("backward search for %r reached first line",
self.operation.search_text)
self.prev_match = None
else:
self.logger.warning ("inconsistent search state %r",
self.search_state)
self.logger.warning("inconsistent search state %r",
self.search_state)
return
self.update_sensitivity ()
self.update_sensitivity()
if self.prev_match is None and self.next_match is None:
self.bar.status_no_match_found ()
self.bar.status_no_match_found()
class Plugin (PluginBase):

File diff suppressed because it is too large Load diff

View file

@ -23,56 +23,62 @@ __all__ = ["_", "_N", "FeatureBase", "PluginBase"]
import os.path
def _N (s): return s
def load (paths = ()):
def _N(s):
return s
def load(paths=()):
for path in paths:
for plugin_module in _load_plugins (path):
for plugin_module in _load_plugins(path):
yield plugin_module.Plugin
def _load_plugins (path):
import imp, glob
def _load_plugins(path):
files = glob.glob (os.path.join (path, "*.py"))
import imp
import glob
files = glob.glob(os.path.join(path, "*.py"))
for filename in files:
name = os.path.basename (os.path.splitext (filename)[0])
name = os.path.basename(os.path.splitext(filename)[0])
if name == "__init__":
continue
fp, pathname, description = imp.find_module (name, [path])
module = imp.load_module (name, fp, pathname, description)
fp, pathname, description = imp.find_module(name, [path])
module = imp.load_module(name, fp, pathname, description)
yield module
class FeatureBase (object):
def __init__ (self, app):
def __init__(self, app):
pass
def handle_attach_window (self, window):
def handle_attach_window(self, window):
pass
def handle_attach_log_file (self, window, log_file):
def handle_attach_log_file(self, window, log_file):
pass
def handle_detach_log_file (self, window, log_file):
def handle_detach_log_file(self, window, log_file):
pass
def handle_detach_window (self, window):
def handle_detach_window(self, window):
pass
class PluginBase (object):
features = ()
def __init__ (self, app):
def __init__(self, app):
pass