Make SignalCache an own class.

This commit is contained in:
Florian Bruhin 2014-02-11 14:03:26 +01:00
parent d4e69c955e
commit c77589a821
3 changed files with 98 additions and 61 deletions

View File

@ -17,8 +17,6 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
import re
from PyQt5.QtCore import pyqtRemoveInputHook
try:
@ -27,22 +25,6 @@ except ImportError:
from pdb import set_trace as pdb_set_trace
def signal_name(sig):
"""Return a cleaned up name of a signal."""
m = re.match(r'[0-9]+(.*)\(.*\)', sig.signal)
return m.group(1)
def dbg_signal(sig, args):
"""Return a string representation of a signal for debugging.
sig -- A pyqtSignal.
args -- The arguments as list of strings.
"""
return '{}({})'.format(signal_name(sig), ', '.join(map(str, args)))
def set_trace():
"""
Set a tracepoint in the Python debugger that works with Qt.

View File

@ -0,0 +1,91 @@
"""Utilities regarding signals."""
# Copyright 2014 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
import re
import logging
from collections import OrderedDict
from PyQt5.QtCore import QObject
def signal_name(sig):
"""Return a cleaned up name of a signal."""
m = re.match(r'[0-9]+(.*)\(.*\)', sig.signal)
return m.group(1)
def dbg_signal(sig, args):
"""Return a string representation of a signal for debugging.
sig -- A pyqtSignal.
args -- The arguments as list of strings.
"""
return '{}({})'.format(signal_name(sig), ', '.join(map(str, args)))
class SignalCache(QObject):
"""Cache signals emitted by an object, and re-emit them later."""
uncached = None
signal_dict = None
def __init__(self, uncached=None):
"""Create a new SignalCache.
uncached -- A list of signal names (as string) which should never be
cached.
"""
super().__init__()
if uncached is None:
self.uncached = []
else:
self.uncached = uncached
self.signal_dict = OrderedDict()
def add(self, sig, args):
"""Add a new signal to the signal cache.
If the signal doesn't need caching it will be ignored.
If it's already in the cache, it'll be updated and moved to the front.
If not, it will be added.
"""
if not self._signal_needs_caching(sig):
return
had_signal = sig.signal in self.signal_dict
self.signal_dict[sig.signal] = (sig, args)
if had_signal:
self.signal_dict.move_to_end(sig.signal)
def clear(self):
"""Clear/purge the signal cache."""
self.signal_dict.clear()
def replay(self):
"""Replay all cached signals."""
for (signal, args) in self.signal_dict.values():
logging.debug('emitting {}'.format(dbg_signal(signal, args)))
signal.emit(*args)
def _signal_needs_caching(self, signal):
"""Return True if a signal should be cached, false otherwise."""
return not signal_name(signal) in self.uncached

View File

@ -24,7 +24,6 @@ containing BrowserTabs).
import logging
import functools
from collections import OrderedDict
from PyQt5.QtWidgets import QShortcut, QApplication, QSizePolicy
from PyQt5.QtCore import pyqtSignal, Qt, QEvent
@ -36,7 +35,7 @@ import qutebrowser.utils.about as about
import qutebrowser.utils.config as config
import qutebrowser.utils.url as urlutils
from qutebrowser.widgets.tabbar import TabWidget
from qutebrowser.utils.misc import dbg_signal, signal_name
from qutebrowser.utils.signals import dbg_signal, SignalCache
class TabbedBrowser(TabWidget):
@ -69,7 +68,8 @@ class TabbedBrowser(TabWidget):
def __init__(self, parent):
super().__init__(parent)
self.currentChanged.connect(self._currentChanged_handler)
self.currentChanged.connect(lambda idx:
self.widget(idx).signal_cache.replay())
space = QShortcut(self)
space.setKey(Qt.Key_Space)
space.setContext(Qt.WidgetWithChildrenShortcut)
@ -92,7 +92,8 @@ class TabbedBrowser(TabWidget):
tab.linkHovered.connect(self._filter_factory(self.cur_link_hovered))
tab.loadProgress.connect(self._filter_factory(self.cur_progress))
tab.loadFinished.connect(self._filter_factory(self.cur_load_finished))
tab.loadStarted.connect(self._clear_signal_cache)
tab.loadStarted.connect(lambda: # pylint: disable=unnecessary-lambda
self.sender().signal_cache.clear())
tab.loadStarted.connect(self._filter_factory(self.cur_load_started))
tab.statusBarMessage.connect(
self._filter_factory(self.cur_statusbar_message))
@ -412,19 +413,7 @@ class TabbedBrowser(TabWidget):
logging.warn('Got signal {} by {} which is no tab!'.format(
dbg_signal(signal, args), sender))
return
if (signal.signal in sender.signal_cache and
self._signal_needs_caching(signal)):
if log_signal:
logging.debug(" Moving to the end of signal cache")
sender.signal_cache[signal.signal] = (signal, args)
sender.signal_cache.move_to_end(signal.signal)
elif self._signal_needs_caching(signal):
if log_signal:
logging.debug(" Adding to signal cache")
sender.signal_cache[signal.signal] = (signal, args)
else:
if log_signal:
logging.debug(" Ignoring for signal cache")
sender.signal_cache.add(signal, args)
if self.currentWidget() == sender:
if log_signal:
logging.debug(' emitting')
@ -433,31 +422,6 @@ class TabbedBrowser(TabWidget):
if log_signal:
logging.debug(' ignoring')
def _currentChanged_handler(self, idx):
"""Update status bar values when a tab was changed.
Populates all signals from the signal cache.
Slot for the currentChanged signal.
"""
for (signal, args) in self.widget(idx).signal_cache.values():
logging.debug('signal cache: emitting {} for tab {}'.format(
dbg_signal(signal, args), idx))
signal.emit(*args)
def _clear_signal_cache(self):
"""Clear the signal cache of the sender of the signal."""
sender = self.sender()
logging.debug("Clearing signal cache of tab {}".format(self.indexOf(
sender)))
sender.signal_cache.clear()
def _signal_needs_caching(self, signal):
"""Return True if a signal should be cached, false otherwise."""
ignore_signals = ['linkHovered']
return not signal_name(signal) in ignore_signals
class BrowserTab(QWebView):
@ -478,7 +442,7 @@ class BrowserTab(QWebView):
def __init__(self, parent):
super().__init__(parent)
self.signal_cache = OrderedDict()
self.signal_cache = SignalCache(uncached=['linkHovered'])
self.loadProgress.connect(self.set_progress)
self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
self.page().linkHovered.connect(self.linkHovered)