First steps at getting rid of signal cache
This commit is contained in:
parent
0d104b5813
commit
d3eaeaac91
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user