First steps at getting rid of signal cache

This commit is contained in:
Florian Bruhin 2014-05-15 17:57:08 +02:00
parent 0d104b5813
commit d3eaeaac91
7 changed files with 53 additions and 141 deletions

View File

@ -397,14 +397,17 @@ class QuteBrowser(QApplication):
self.config.changed.connect(obj.on_config_changed)
# statusbar
tabs.currentChanged.connect(status.prog.current_changed)
tabs.cur_progress.connect(status.prog.setValue)
tabs.cur_load_finished.connect(status.prog.hide)
tabs.cur_load_started.connect(status.url.on_loading_started)
tabs.cur_load_finished.connect(status.url.on_loading_finished)
tabs.cur_ssl_errors.connect(status.url.on_ssl_errors)
tabs.cur_load_started.connect(status.prog.on_load_started)
tabs.currentChanged.connect(status.percentage.current_changed)
tabs.cur_scroll_perc_changed.connect(status.percentage.set_perc)
tabs.cur_statusbar_message.connect(status.txt.on_statusbar_message)
tabs.currentChanged.connect(status.url.current_changed)
tabs.cur_url_changed.connect(status.url.set_url)
tabs.cur_link_hovered.connect(status.url.set_hover_url)

View File

@ -61,9 +61,6 @@ class SignalFilter(QObject):
The original signal does not matter, since we get the new signal and
all args.
The current value of the signal is also stored in tab.signal_cache so
it can be emitted later when the tab changes to the current tab.
Args:
signal: The signal to emit if the sender was the current widget.
*args: The args to pass to the signal.
@ -81,7 +78,6 @@ class SignalFilter(QObject):
logging.warn("Got signal {} by {} which is no tab!".format(
dbg_signal(signal, args), sender))
return
sender.signal_cache.add(signal, args)
if self._tabs.currentWidget() == sender:
if log_signal:
logging.debug(" emitting")

View File

@ -32,7 +32,6 @@ class FakeSignal:
def __init__(self, name='fake'):
self.signal = '2{}(int, int)'.format(name)
self.emit = Mock()
class TestDebug(TestCase):
@ -50,50 +49,5 @@ class TestDebug(TestCase):
'fake(23, 42)')
class TestSignalCache(TestCase):
"""SignalCache tests."""
def setUp(self):
self.signal1 = FakeSignal('fake1')
self.signal2 = FakeSignal('fake2')
self.cache = sigutils.SignalCache()
def test_replay(self):
"""Test simple replaying."""
self.cache.add(self.signal1, [1, 2])
self.cache.add(self.signal2, [3, 4])
self.cache.replay()
self.signal1.emit.assert_called_once_with(1, 2)
self.signal2.emit.assert_called_once_with(3, 4)
def test_update(self):
"""Test replaying when a signal was updated."""
self.cache.add(self.signal1, [1, 2])
self.cache.add(self.signal2, [3, 4])
self.cache.add(self.signal1, [5, 6])
self.cache.replay()
self.signal1.emit.assert_called_once_with(5, 6)
self.signal2.emit.assert_called_once_with(3, 4)
def test_clear(self):
"""Test clearing the signal cache."""
self.cache.add(self.signal1, [1, 2])
self.cache.add(self.signal2, [3, 4])
self.cache.clear()
self.cache.add(self.signal1, [5, 6])
self.cache.replay()
self.signal1.emit.assert_called_once_with(5, 6)
def test_uncached(self):
"""Test that uncached signals actually are uncached."""
cache = sigutils.SignalCache(uncached=['fake2'])
cache.add(self.signal1, [1, 2])
cache.add(self.signal2, [3, 4])
cache.replay()
self.signal1.emit.assert_called_once_with(1, 2)
self.assertFalse(self.signal2.emit.called)
if __name__ == '__main__':
unittest.main()

View File

@ -48,58 +48,3 @@ def dbg_signal(sig, args):
A human-readable string representation of signal/args.
"""
return '{}({})'.format(signal_name(sig), ', '.join(map(str, args)))
class SignalCache(QObject):
"""Cache signals emitted by an object, and re-emit them later.
Attributes:
_uncached: A list of signals which should not be cached.
_signal_dict: The internal mapping of signals we got.
"""
def __init__(self, uncached=None):
"""Create a new SignalCache.
Args:
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.
Args:
sig: The pyqtSignal.
args: A list of arguments.
Emit:
Cached signals.
"""
if signal_name(sig) in self._uncached:
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)

View File

@ -44,8 +44,6 @@ class TabbedBrowser(TabWidget):
in the currently visible tab.
For all tab-specific signals (cur_*) emitted by a tab, this happens:
- the signal gets added to a signal_cache of the tab, so it can be
emitted again if the current tab changes.
- the signal gets filtered with _filter_signals and self.cur_* gets
emitted if the signal occured in the current tab.
@ -62,7 +60,6 @@ class TabbedBrowser(TabWidget):
cur_progress: Progress of the current tab changed (loadProgress).
cur_load_started: Current tab started loading (loadStarted)
cur_load_finished: Current tab finished loading (loadFinished)
cur_ssl_errors: Current tab encountered SSL errors.
cur_statusbar_message: Current tab got a statusbar message
(statusBarMessage)
cur_url_changed: Current URL changed (urlChanged)
@ -86,7 +83,6 @@ class TabbedBrowser(TabWidget):
cur_url_changed = pyqtSignal('QUrl')
cur_link_hovered = pyqtSignal(str, str, str)
cur_scroll_perc_changed = pyqtSignal(int, int)
cur_ssl_errors = pyqtSignal('QNetworkReply*', 'QList<QSslError>')
hint_strings_updated = pyqtSignal(list)
shutdown_complete = pyqtSignal()
quit = pyqtSignal()
@ -141,7 +137,6 @@ class TabbedBrowser(TabWidget):
tab.linkHovered.connect(self._filter.create(self.cur_link_hovered))
tab.loadProgress.connect(self._filter.create(self.cur_progress))
tab.loadFinished.connect(self._filter.create(self.cur_load_finished))
tab.ssl_errors.connect(self._filter.create(self.cur_ssl_errors))
tab.page().mainFrame().loadStarted.connect(partial(
self.on_load_started, tab))
tab.loadStarted.connect(self._filter.create(self.cur_load_started))
@ -499,7 +494,6 @@ class TabbedBrowser(TabWidget):
Args:
tab: The tab where the signal belongs to.
"""
tab.signal_cache.clear()
self.setTabIcon(self.indexOf(tab), EmptyTabIcon())
@pyqtSlot(str)
@ -545,7 +539,6 @@ class TabbedBrowser(TabWidget):
def on_current_changed(self, idx):
"""Set last_focused and replay signal cache if focus changed."""
tab = self.widget(idx)
tab.signal_cache.replay()
self.last_focused = self.now_focused
self.now_focused = tab

View File

@ -94,29 +94,17 @@ class Url(TextBase):
self._urltype = val
self.setStyleSheet(get_stylesheet(self.STYLESHEET))
@pyqtSlot()
def on_loading_started(self):
"""Slot to clear SSL errors when loading started."""
self._ssl_errors = False
@pyqtSlot('QNetworkReply*', 'QList<QSslError>')
def on_ssl_errors(self, _reply, _errors):
"""Set a flag to get a warning when there were SSL errors."""
self._ssl_errors = True
@pyqtSlot(bool)
def on_loading_finished(self, ok):
"""Slot for cur_loading_finished. Colors the URL according to ok.
@pyqtSlot(str)
def on_load_status_changed(self, status):
"""Slot for load_status_changed. Sets URL color accordingly.
Args:
ok: Whether loading finished successfully (True) or not (False).
status: The LoadStatus as string.
"""
if ok and not self._ssl_errors:
self.urltype = 'success'
elif ok:
self.urltype = 'warn'
if status in ['success', 'error', 'warn']:
self.urltype = status
else:
self.urltype = 'error'
self.urltype = 'normal'
@pyqtSlot(str)
def set_url(self, s):

View File

@ -32,12 +32,12 @@ import qutebrowser.utils.message as message
import qutebrowser.utils.webelem as webelem
from qutebrowser.browser.webpage import BrowserPage
from qutebrowser.browser.hints import HintManager
from qutebrowser.utils.signals import SignalCache
from qutebrowser.utils.usertypes import NeighborList, enum
from qutebrowser.commands.exceptions import CommandError
Target = enum('normal', 'tab', 'bgtab')
LoadStatus = enum('none', 'success', 'error', 'warn', 'loading')
class WebView(QWebView):
@ -48,11 +48,16 @@ class WebView(QWebView):
Attributes:
page_: The QWebPage behind the view
signal_cache: The signal cache associated with the view.
hintmanager: The HintManager instance for this view.
tabbedbrowser: The TabbedBrowser this WebView is part of.
We need this rather than signals to make createWindow
work.
progress: loading progress of this page.
scroll_pos: The current scroll position as (x%, y%) tuple.
url: The current URL as QUrl.
_load_status: loading status of this page (index into LoadStatus)
Accessed via load_status property.
_has_ssl_errors: Whether SSL errors occured during loading.
_zoom: A NeighborList with the zoom levels.
_scroll_pos: The old scroll position.
_shutdown_callback: Callback to be called after shutdown.
@ -66,14 +71,16 @@ class WebView(QWebView):
arg 1: x-position in %.
arg 2: y-position in %.
linkHovered: QWebPages linkHovered signal exposed.
load_status_changed: The loading status changed
"""
scroll_pos_changed = pyqtSignal(int, int)
linkHovered = pyqtSignal(str, str, str)
ssl_errors = pyqtSignal('QNetworkReply*', 'QList<QSslError>')
load_status_changed = pyqtSignal(str)
def __init__(self, parent):
super().__init__(parent)
self._load_status = LoadStatus.none
self.tabbedbrowser = parent
self._scroll_pos = (-1, -1)
self._shutdown_callback = None
@ -82,21 +89,37 @@ class WebView(QWebView):
self._destroyed = {}
self._zoom = None
self._init_neighborlist()
self.progress = 0
self.loadProgress.connect(lambda p: setattr(self, 'progress', p))
self.page_.networkAccessManager().sslErrors.connect(
lambda *args: setattr(self, '_has_ssl_errors', True))
self.page_ = BrowserPage(self)
self.setPage(self.page_)
self.hintmanager = HintManager(self)
self.hintmanager.mouse_event.connect(self.on_mouse_event)
self.hintmanager.set_open_target.connect(self.set_force_open_target)
self.signal_cache = SignalCache(
uncached=['linkHovered', 'cur_ssl_errors'])
self.page_.setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
self.page_.linkHovered.connect(self.linkHovered)
self.page_.networkAccessManager().sslErrors.connect(self.ssl_errors)
self.linkClicked.connect(self.on_link_clicked)
self.page_.mainFrame().loadStarted.connect(self.on_load_started)
self.loadFinished.connect(self.on_load_finished)
# FIXME find some way to hide scrollbars without setScrollBarPolicy
@property
def load_status(self):
"""Getter for load_status."""
return self._load_status
@load_status.setter
def load_status(self, val):
"""Setter for load_status.
Emit:
load_status_changed
"""
self._load_status = val
self.load_status_changed.emit(LoadStatus[val])
def _init_neighborlist(self):
"""Initialize the _zoom neighborlist."""
self._zoom = NeighborList(
@ -340,13 +363,22 @@ class WebView(QWebView):
@pyqtSlot()
def on_load_started(self):
"""Leave insert/hint mode when a new page is loading."""
"""Leave insert/hint mode and set vars when a new page is loading."""
for mode in ['insert', 'hint']:
modeman.maybe_leave(mode, 'load started')
self.progress = 0
self._has_ssl_errors = False
self.load_status = LoadStatus.loading
@pyqtSlot(bool)
def on_load_finished(self, ok):
"""Handle auto-insert-mode after loading finished."""
if ok and not self._has_ssl_errors:
self.urltype = 'success'
elif ok:
self.urltype = 'warn'
else:
self.urltype = 'error'
if not config.get('input', 'auto-insert-mode'):
return
if modeman.instance().mode == 'insert' or not ok:
@ -424,6 +456,7 @@ class WebView(QWebView):
frame.scrollBarMaximum(Qt.Vertical))
perc = (round(100 * new_pos[0] / m[0]) if m[0] != 0 else 0,
round(100 * new_pos[1] / m[1]) if m[1] != 0 else 0)
self.scroll_pos = perc
self.scroll_pos_changed.emit(*perc)
# Let superclass handle the event
return super().paintEvent(e)