From 1fef2d02b94ed2aef491ba304fe6961322a6d77e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 17 Jun 2014 11:03:42 +0200 Subject: [PATCH] Add an :all-objects command and __repr__s --- qutebrowser/app.py | 19 ++++++++++++----- qutebrowser/browser/downloads.py | 12 ++++++++--- qutebrowser/commands/managers.py | 3 +++ qutebrowser/config/config.py | 7 ++++++- qutebrowser/keyinput/basekeyparser.py | 13 ++++++++---- qutebrowser/keyinput/keyparser.py | 11 ++++++++-- qutebrowser/keyinput/modeman.py | 3 +++ qutebrowser/keyinput/modeparsers.py | 6 ++++++ qutebrowser/models/downloadmodel.py | 3 +++ qutebrowser/utils/message.py | 3 +++ qutebrowser/utils/usertypes.py | 25 ++++++++++++++++++++++- qutebrowser/widgets/completion.py | 2 +- qutebrowser/widgets/downloads.py | 4 ++-- qutebrowser/widgets/mainwindow.py | 2 +- qutebrowser/widgets/statusbar/bar.py | 9 ++++---- qutebrowser/widgets/statusbar/progress.py | 2 +- qutebrowser/widgets/statusbar/prompt.py | 2 +- qutebrowser/widgets/tabbedbrowser.py | 3 ++- qutebrowser/widgets/tabwidget.py | 3 ++- 19 files changed, 104 insertions(+), 28 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 7dd640fdc..6dfac220c 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -29,7 +29,7 @@ from base64 import b64encode from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox from PyQt5.QtCore import (pyqtSlot, QTimer, QEventLoop, Qt, QStandardPaths, - qInstallMessageHandler) + qInstallMessageHandler, QObject) import qutebrowser import qutebrowser.commands.utils as cmdutils @@ -54,11 +54,11 @@ from qutebrowser.config.iniparsers import ReadWriteConfigParser from qutebrowser.config.lineparser import LineConfigParser from qutebrowser.browser.cookies import CookieJar from qutebrowser.browser.downloads import DownloadManager -from qutebrowser.models.downloadmodel import DownloadModel from qutebrowser.utils.message import MessageBridge from qutebrowser.utils.misc import (get_standard_dir, actute_warning, get_qt_args) from qutebrowser.utils.readline import ReadlineBridge +from qutebrowser.utils.usertypes import Timer from qutebrowser.utils.debug import set_trace # pylint: disable=unused-import @@ -135,7 +135,6 @@ class Application(QApplication): self.commandmanager = CommandManager() self.searchmanager = SearchManager(self) self.downloadmanager = DownloadManager(self) - self.downloadmodel = DownloadModel(self.downloadmanager) self.mainwindow = MainWindow() self.modeman.mainwindow = self.mainwindow @@ -307,7 +306,7 @@ class Application(QApplication): Python interpreter once all 500ms. """ signal(SIGINT, lambda *args: self.exit(128 + SIGINT)) - timer = QTimer(self) + timer = Timer(self, 'python_hacks') timer.start(500) timer.timeout.connect(lambda: None) self._timers.append(timer) @@ -540,7 +539,17 @@ class Application(QApplication): log.misc.debug("{} widgets".format(len(widgets))) widgets.sort(key=lambda e: repr(e)) for w in widgets: - log.misc.debug(w) + log.misc.debug(repr(w)) + + @cmdutils.register(instance='', debug=True) + def all_objects(self, obj=None, depth=0): + """Dump all children of an object recursively.""" + if obj is None: + obj = self + for kid in obj.findChildren(QObject): + log.misc.debug(' ' * depth + repr(kid)) + self.all_objects(kid, depth + 1) + @pyqtSlot() def shutdown(self): diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index 6bd34701c..e56c5883c 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -22,13 +22,13 @@ import os.path from functools import partial from collections import deque -from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QTimer +from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject from PyQt5.QtNetwork import QNetworkReply import qutebrowser.config.config as config import qutebrowser.utils.message as message from qutebrowser.utils.log import downloads as logger -from qutebrowser.utils.usertypes import PromptMode, Question +from qutebrowser.utils.usertypes import PromptMode, Question, Timer from qutebrowser.utils.misc import (interpolate_color, format_seconds, format_size) @@ -99,11 +99,14 @@ class DownloadItem(QObject): reply.finished.connect(self.on_reply_finished) reply.error.connect(self.on_reply_error) reply.readyRead.connect(self.on_ready_read) - self.timer = QTimer(self) + self.timer = Timer(self, 'speed_refresh') self.timer.timeout.connect(self.update_speed) self.timer.setInterval(self.SPEED_REFRESH_INTERVAL) self.timer.start() + def __repr__(self): + return '<{} "{}">'.format(self.__class__.__name__, self.basename) + def __str__(self): """Get the download as a string. @@ -309,6 +312,9 @@ class DownloadManager(QObject): self.downloads = [] self.questions = [] + def __repr__(self): + return '<{}>'.format(self.__class__.__name__) + def _get_filename(self, reply): """Get a suitable filename to download a file to. diff --git a/qutebrowser/commands/managers.py b/qutebrowser/commands/managers.py index d89e604f8..c45e5d85a 100644 --- a/qutebrowser/commands/managers.py +++ b/qutebrowser/commands/managers.py @@ -50,6 +50,9 @@ class SearchManager(QObject): self._text = None self._flags = 0 + def __repr__(self): + return '<{} text={}>'.format(self.__class__.__name__, self._text) + def _search(self, text, rev=False): """Search for a text on the current page. diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index a6b63b8f9..6afeef275 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -80,6 +80,7 @@ class ConfigManager(QObject): Attributes: sections: The configuration data as an OrderedDict. + _fname: The filename to be opened. _configparser: A ReadConfigParser instance to load the config. _wrapper_args: A dict with the default kwargs for the config wrappers. _configdir: The dictionary to read the config from and save it in. @@ -112,6 +113,7 @@ class ConfigManager(QObject): 'break_on_hyphens': False, } self._configdir = configdir + self._fname = fname self._interpolation = ExtendedInterpolation() self._proxies = {} for sectname in self.sections.keys(): @@ -122,6 +124,9 @@ class ConfigManager(QObject): """Get a section from the config.""" return self._proxies[key] + def __repr__(self): + return '<{} {}>'.format(self.__class__.__name__, self._fname) + def __str__(self): """Get the whole config as a string.""" lines = configdata.FIRST_COMMENT.strip('\n').splitlines() @@ -401,7 +406,7 @@ class SectionProxy(MutableMapping): self._name = name def __repr__(self): - return ''.format(self._name) + return '<{} {}>'.format(self.__class__.__name__, self._name) def __getitem__(self, key): if not self._conf.has_option(self._name, key): diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index b53c8d693..93b9bde2d 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -21,11 +21,11 @@ import re import string from functools import partial -from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QTimer +from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject from PyQt5.QtGui import QKeySequence import qutebrowser.config.config as config -from qutebrowser.utils.usertypes import enum +from qutebrowser.utils.usertypes import enum, Timer from qutebrowser.utils.log import keyboard as logger @@ -54,7 +54,7 @@ class BaseKeyParser(QObject): warn_on_keychains: Whether a warning should be logged when binding keychains in a section which does not support them. _keystring: The currently entered key sequence - _timer: QTimer for delayed execution. + _timer: Timer for delayed execution. _confsectname: The name of the configsection. _supports_count: Whether count is supported _supports_chains: Whether keychains are supported @@ -83,6 +83,11 @@ class BaseKeyParser(QObject): self.bindings = {} self.special_bindings = {} + def __repr__(self): + return '<{} supports_count={}, supports_chains={}>'.format( + self.__class__.__name__, self._supports_count, + self._supports_chains) + def _normalize_keystr(self, keystr): """Normalize a keystring like Ctrl-Q to a keystring like Ctrl+Q. @@ -273,7 +278,7 @@ class BaseKeyParser(QObject): # execute in `time' ms logger.debug("Scheduling execution of {} in {}ms".format(binding, time)) - self._timer = QTimer(self) + self._timer = Timer(self, 'ambigious_match') self._timer.setSingleShot(True) self._timer.setInterval(time) self._timer.timeout.connect(partial(self.delayed_exec, binding, diff --git a/qutebrowser/keyinput/keyparser.py b/qutebrowser/keyinput/keyparser.py index c86b60d88..11e0353b9 100644 --- a/qutebrowser/keyinput/keyparser.py +++ b/qutebrowser/keyinput/keyparser.py @@ -64,6 +64,9 @@ class PassthroughKeyParser(CommandKeyParser): """KeyChainParser which passes through normal keys. Used for insert/passthrough modes. + + Attributes: + _confsect: The config section to use. """ def __init__(self, confsect, parent=None, warn=True): @@ -75,6 +78,10 @@ class PassthroughKeyParser(CommandKeyParser): warn: Whether to warn if an ignored key was bound. """ super().__init__(parent, supports_chains=False) - if not warn: - self.warn_on_keychains = False + self.warn_on_keychains = warn self.read_config(confsect) + self._confsect = confsect + + def __repr__(self): + return '<{} confsect={}, warn={})'.format( + self.__class__.__name__, self._confsect, self.warn_on_keychains) diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index e6f12c43a..e14683882 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -90,6 +90,9 @@ class ModeManager(QObject): self._forward_unbound_keys = config.get('input', 'forward-unbound-keys') + def __repr__(self): + return '<{} mode={}>'.format(self.__class__.__name__, self.mode) + @property def mode(self): """Read-only property for the current mode.""" diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index 7b788b1ea..eacc62f0c 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -42,6 +42,9 @@ class NormalKeyParser(CommandKeyParser): super().__init__(parent, supports_count=True, supports_chains=True) self.read_config('keybind') + def __repr__(self): + return '<{}>'.format(self.__class__.__name__) + def _handle_single_key(self, e): """Override _handle_single_key to abort if the key is a startchar. @@ -68,6 +71,9 @@ class PromptKeyParser(CommandKeyParser): # abuse the keybind.prompt section. self.read_config('keybind.prompt') + def __repr__(self): + return '<{}>'.format(self.__class__.__name__) + class HintKeyParser(CommandKeyParser): diff --git a/qutebrowser/models/downloadmodel.py b/qutebrowser/models/downloadmodel.py index 1945e98b2..1542775b8 100644 --- a/qutebrowser/models/downloadmodel.py +++ b/qutebrowser/models/downloadmodel.py @@ -47,6 +47,9 @@ class DownloadModel(QAbstractListModel): self.downloadmanager.download_finished.connect(self.endRemoveRows) self.downloadmanager.data_changed.connect(self.on_data_changed) + def __repr__(self): + return '<{}>'.format(self.__class__.__name__) + @pyqtSlot(int) def on_data_changed(self, idx): """Update view when DownloadManager data changed.""" diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py index d12f3a8e6..174732996 100644 --- a/qutebrowser/utils/message.py +++ b/qutebrowser/utils/message.py @@ -149,3 +149,6 @@ class MessageBridge(QObject): text = pyqtSignal(str) set_cmd_text = pyqtSignal(str) question = pyqtSignal(Question, bool) + + def __repr__(self): + return '<{}>'.format(self.__class__.__name__) diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index ecb6ee969..93ff46846 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -24,7 +24,7 @@ Module attributes: import operator import collections.abc -from PyQt5.QtCore import pyqtSignal, QObject +from PyQt5.QtCore import pyqtSignal, QObject, QTimer from qutebrowser.utils.log import misc as logger @@ -281,6 +281,9 @@ class Question(QObject): self.answer = None self.is_aborted = False + def __repr__(self): + return '<{} "{}">'.format(self.__class__.__name__, self.text) + def abort(self): """Abort the question. @@ -289,3 +292,23 @@ class Question(QObject): """ self.is_aborted = True self.aborted.emit() + + +class Timer(QTimer): + + """A timer which has a name to show in __repr__. + + Attributes: + _name: The name of the timer. + """ + + def __init__(self, parent=None, name=None): + super().__init__(parent) + if name is None: + self._name = "unnamed" + else: + self.setObjectName(name) + self._name = name + + def __repr__(self): + return '<{} {}>'.format(self.__class__.__name__, self._name) diff --git a/qutebrowser/widgets/completion.py b/qutebrowser/widgets/completion.py index f3b4112d6..8250076fa 100644 --- a/qutebrowser/widgets/completion.py +++ b/qutebrowser/widgets/completion.py @@ -113,7 +113,7 @@ class CompletionView(QTreeView): # FIXME set elidemode def __repr__(self): - return '' + return '<{}>'.format(self.__class__.__name__) def _resize_columns(self): """Resize the completion columns based on COLUMN_WIDTHS.""" diff --git a/qutebrowser/widgets/downloads.py b/qutebrowser/widgets/downloads.py index f14f9e022..4f278a4a9 100644 --- a/qutebrowser/widgets/downloads.py +++ b/qutebrowser/widgets/downloads.py @@ -61,8 +61,8 @@ class DownloadView(QListView): self.customContextMenuRequested.connect(self.show_context_menu) def __repr__(self): - return ''.format( - self.model().rowCount()) + return '<{} with {} downloads>'.format(self.__class__.__name__, + self.model().rowCount()) @pyqtSlot('QPoint') def show_context_menu(self, point): diff --git a/qutebrowser/widgets/mainwindow.py b/qutebrowser/widgets/mainwindow.py index 435c07e8e..f2d87bb98 100644 --- a/qutebrowser/widgets/mainwindow.py +++ b/qutebrowser/widgets/mainwindow.py @@ -89,7 +89,7 @@ class MainWindow(QWidget): #QtCore.QMetaObject.connectSlotsByName(MainWindow) def __repr__(self): - return '' + return '<{}>'.format(self.__class__.__name__) def _set_default_geometry(self): """Set some sensible default geometry.""" diff --git a/qutebrowser/widgets/statusbar/bar.py b/qutebrowser/widgets/statusbar/bar.py index 1dbf3ff88..cf7f4e23c 100644 --- a/qutebrowser/widgets/statusbar/bar.py +++ b/qutebrowser/widgets/statusbar/bar.py @@ -20,7 +20,7 @@ from collections import deque from datetime import datetime -from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QTimer +from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy import qutebrowser.keyinput.modeman as modeman @@ -34,6 +34,7 @@ from qutebrowser.widgets.statusbar.percentage import Percentage from qutebrowser.widgets.statusbar.url import Url from qutebrowser.widgets.statusbar.prompt import Prompt from qutebrowser.config.style import set_register_stylesheet, get_stylesheet +from qutebrowser.utils.usertypes import Timer class StatusBar(QWidget): @@ -54,7 +55,7 @@ class StatusBar(QWidget): _stack: The QStackedLayout with cmd/txt widgets. _text_queue: A deque of (error, text) tuples to be displayed. error: True if message is an error, False otherwise - _text_pop_timer: A QTimer displaying the error messages. + _text_pop_timer: A Timer displaying the error messages. _last_text_time: The timestamp where a message was last displayed. _timer_was_active: Whether the _text_pop_timer was active before hiding the command widget. @@ -130,7 +131,7 @@ class StatusBar(QWidget): self._stack.addWidget(self.txt) self._timer_was_active = False self._text_queue = deque() - self._text_pop_timer = QTimer(self) + self._text_pop_timer = Timer(self, 'statusbar_text_pop') self._text_pop_timer.setInterval(config.get('ui', 'message-timeout')) self._text_pop_timer.timeout.connect(self._pop_text) @@ -159,7 +160,7 @@ class StatusBar(QWidget): self._hbox.addWidget(self.prog) def __repr__(self): - return '' + return '<{}>'.format(self.__class__.__name__) @pyqtProperty(bool) def error(self): diff --git a/qutebrowser/widgets/statusbar/progress.py b/qutebrowser/widgets/statusbar/progress.py index 7342423b9..73e78822d 100644 --- a/qutebrowser/widgets/statusbar/progress.py +++ b/qutebrowser/widgets/statusbar/progress.py @@ -54,7 +54,7 @@ class Progress(QProgressBar): self.hide() def __repr__(self): - return ''.format(self.value()) + return '<{} {}%>'.format(self.__class__.__name__, self.value()) @pyqtSlot() def on_load_started(self): diff --git a/qutebrowser/widgets/statusbar/prompt.py b/qutebrowser/widgets/statusbar/prompt.py index f3afaf897..585afa322 100644 --- a/qutebrowser/widgets/statusbar/prompt.py +++ b/qutebrowser/widgets/statusbar/prompt.py @@ -64,7 +64,7 @@ class Prompt(QWidget): self._hbox.addWidget(self._input) def __repr__(self): - return '' + return '<{}>'.format(self.__class__.__name__) def on_mode_left(self, mode): """Clear and reset input when the mode was left.""" diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index 2ae863525..eed94ff17 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -108,7 +108,8 @@ class TabbedBrowser(TabWidget): self.setIconSize(QSize(12, 12)) def __repr__(self): - return ''.format(self.count()) + return '<{} with {} tabs>'.format(self.__class__.__name__, + self.count()) @property def widgets(self): diff --git a/qutebrowser/widgets/tabwidget.py b/qutebrowser/widgets/tabwidget.py index 8b98248f4..e4a4c6164 100644 --- a/qutebrowser/widgets/tabwidget.py +++ b/qutebrowser/widgets/tabwidget.py @@ -124,7 +124,8 @@ class TabBar(QTabBar): """Custom tabbar to close tabs on right click.""" def __repr__(self): - return ''.format(self.count()) + return '<{} with {} tabs>'.format(self.__class__.__name__, + self.count()) def mousePressEvent(self, e): """Override mousePressEvent to emit tabCloseRequested on rightclick."""