From f43549d45281b7cd4c25600707b36085c29e0879 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 20 May 2014 11:03:55 +0200 Subject: [PATCH] Clean up prompt code --- qutebrowser/app.py | 9 ++-- qutebrowser/network/networkmanager.py | 25 +++++++++ qutebrowser/utils/message.py | 22 ++++++++ qutebrowser/utils/usertypes.py | 50 ++++++++++++++++++ qutebrowser/widgets/statusbar/_prompt.py | 67 +++++++----------------- qutebrowser/widgets/statusbar/bar.py | 14 +---- 6 files changed, 120 insertions(+), 67 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 9927cab37..5439ba81c 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -40,8 +40,7 @@ from argparse import ArgumentParser from base64 import b64encode from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox -from PyQt5.QtCore import pyqtSlot, QTimer, QEventLoop -from PyQt5.QtCore import QStandardPaths +from PyQt5.QtCore import pyqtSlot, QTimer, QEventLoop, Qt, QStandardPaths import qutebrowser import qutebrowser.commands.utils as cmdutils @@ -366,10 +365,6 @@ class QuteBrowser(QApplication): self.lastWindowClosed.connect(self.shutdown) tabs.quit.connect(self.shutdown) tabs.currentChanged.connect(self.mainwindow.update_inspector) - self.networkmanager.authenticationRequired.connect( - status.on_authentication_required) - self.networkmanager.proxyAuthenticationRequired.connect( - status.on_proxy_authentication_required) # status bar self.modeman.entered.connect(status.on_mode_entered) @@ -396,6 +391,8 @@ class QuteBrowser(QApplication): self.messagebridge.info.connect(status.disp_temp_text) self.messagebridge.text.connect(status.set_text) self.messagebridge.set_cmd_text.connect(cmd.set_cmd_text) + self.messagebridge.question.connect(status.prompt.ask_question, + Qt.DirectConnection) # config self.config.style_changed.connect(style.invalidate_caches) diff --git a/qutebrowser/network/networkmanager.py b/qutebrowser/network/networkmanager.py index 0168773fe..266fff459 100644 --- a/qutebrowser/network/networkmanager.py +++ b/qutebrowser/network/networkmanager.py @@ -23,6 +23,7 @@ from PyQt5.QtNetwork import QNetworkAccessManager import qutebrowser.config.config as config import qutebrowser.utils.message as message from qutebrowser.network.qutescheme import QuteSchemeHandler +from qutebrowser.utils.usertypes import PromptMode class NetworkManager(QNetworkAccessManager): @@ -44,6 +45,9 @@ class NetworkManager(QNetworkAccessManager): if cookiejar is not None: self.setCookieJar(cookiejar) self.sslErrors.connect(self.on_ssl_errors) + self.authenticationRequired.connect(self.on_authentication_required) + self.proxyAuthenticationRequired.connect( + self.on_proxy_authentication_required) def abort_requests(self): """Abort all running requests.""" @@ -68,6 +72,27 @@ class NetworkManager(QNetworkAccessManager): queue=True) reply.ignoreSslErrors() + @pyqtSlot('QNetworkReply', 'QAuthenticator') + def on_authentication_required(self, reply, authenticator): + """Called when a website needs authentication.""" + answer = message.modular_question( + text="Username ({}):".format(authenticator.realm()), + mode=PromptMode.user_pwd) + if answer is not None: + user, password = answer + authenticator.setUser(user) + authenticator.setPassword(password) + + @pyqtSlot('QNetworkProxy', 'QAuthenticator') + def on_proxy_authentication_required(self, proxy, authenticator): + answer = message.modular_question( + text="Proxy username ({}):".format(authenticator.realm()), + mode=PromptMode.user_pwd) + if answer is not None: + user, password = answer + authenticator.setUser(user) + authenticator.setPassword(password) + def createRequest(self, op, req, outgoing_data): """Return a new QNetworkReply object. diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py index cbf085388..f5fcd8de8 100644 --- a/qutebrowser/utils/message.py +++ b/qutebrowser/utils/message.py @@ -21,6 +21,8 @@ import logging from PyQt5.QtCore import QObject, pyqtSignal, QCoreApplication +from qutebrowser.utils.usertypes import Question + def instance(): """Get the global messagebridge instance.""" @@ -60,6 +62,25 @@ def text(message): instance().text.emit(message) +def modular_question(text, mode, default=None): + """Ask a modular question in the statusbar. + + Args: + text: The message to display to the user. + mode: A PromptMode. + default: The default value to display. + + Return: + The answer the user gave or None if the prompt was cancelled. + """ + q = Question() + q.text = text + q.mode = mode + q.default = default + instance().question.emit(q, True) + return q.answer + + def clear(): """Clear a persistent message in the statusbar.""" instance().text.emit('') @@ -78,3 +99,4 @@ class MessageBridge(QObject): info = pyqtSignal(str, bool) text = pyqtSignal(str) set_cmd_text = pyqtSignal(str) + question = pyqtSignal(Question, bool) diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 54387d7fe..6b948126d 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -25,6 +25,9 @@ import operator import logging import collections.abc +from PyQt5.QtCore import pyqtSignal, QObject + + _UNSET = object() @@ -241,3 +244,50 @@ class FakeDict: def __repr__(self): return "FakeDict('{}')".format(self._val) + + +# The mode of a Question. +PromptMode = enum('yesno', 'text', 'user_pwd') + + +class Question(QObject): + + """A question asked to the user, e.g. via the status bar. + + Attributes: + mode: A PromptMode enum member. + yesno: A question which can be answered with yes/no. + text: A question which requires a free text answer. + user_pwd: A question for an username and password. + default: The default value. + For yesno, None (no default), True or False. + For text, a default text as string. + For user_pwd, a default username as string. + text: The prompt text to display to the user. + user: The value the user entered as username. + answer: The value the user entered (as password for user_pwd). + + Signals: + answered: Emitted when the question has been answered by the user. + """ + + answered = pyqtSignal() + + def __init__(self, parent=None): + super().__init__(parent) + self.mode = None + self.default = None + self.text = None + self.user = None + self._answer = None + + @property + def answer(self): + """Getter for answer so we can define a setter.""" + return self._answer + + @answer.setter + def answer(self, val): + """Setter for answer to emit the answered signal after setting.""" + self._answer = val + self.answered.emit() diff --git a/qutebrowser/widgets/statusbar/_prompt.py b/qutebrowser/widgets/statusbar/_prompt.py index e114c504c..df2ec11f1 100644 --- a/qutebrowser/widgets/statusbar/_prompt.py +++ b/qutebrowser/widgets/statusbar/_prompt.py @@ -17,59 +17,14 @@ """Prompt shown in the statusbar.""" -from PyQt5.QtCore import pyqtSignal, QEventLoop, QObject +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QEventLoop from PyQt5.QtWidgets import QHBoxLayout, QWidget, QLineEdit import qutebrowser.keyinput.modeman as modeman import qutebrowser.commands.utils as cmdutils from qutebrowser.widgets.statusbar._textbase import TextBase from qutebrowser.widgets.misc import MinimalLineEdit -from qutebrowser.utils.usertypes import enum - -PromptMode = enum('yesno', 'text', 'user_pwd') - - -class Question(QObject): - - """A question asked to the user via the status bar. - - Attributes: - mode: A PromptMode enum member. - yesno: A question which can be answered with yes/no. - text: A question which requires a free text answer. - user_pwd: A question for an username and password. - default: The default value. - For yesno, None (no default), True or False. - For text, a default text as string. - For user_pwd, a default username as string. - text: The prompt text to display to the user. - user: The value the user entered as username. - answer: The value the user entered (as password for user_pwd). - - Signals: - answered: Emitted when the question has been answered by the user. - """ - - answered = pyqtSignal() - - def __init__(self, parent=None): - super().__init__(parent) - self.mode = None - self.default = None - self.text = None - self.user = None - self._answer = None - - @property - def answer(self): - """Getter for answer so we can define a setter.""" - return self._answer - - @answer.setter - def answer(self, val): - """Setter for answer to emit the answered signal after setting.""" - self._answer = val - self.answered.emit() +from qutebrowser.utils.usertypes import PromptMode, Question class Prompt(QWidget): @@ -181,13 +136,29 @@ class Prompt(QWidget): self._input.setFocus() self.show_prompt.emit() + @pyqtSlot(Question, bool) + def ask_question(self, question, blocking): + """Slot which is called when there's a question to ask to the user. + + Return: + The answer of the user when blocking=True. + None if blocking=False. + + Args: + question: The Question object to ask. + blocking: If True, exec_ is called and the result is returned. + """ + self.question = question + self.display() + if blocking: + return self.exec_() + def exec_(self): """Local eventloop to spin in for a blocking question. Return: The answer to the question. No, it's not always 42. """ - self.display() self.question.answered.connect(self.loop.quit) self.cancelled.connect(self.loop.quit) self.loop.exec_() diff --git a/qutebrowser/widgets/statusbar/bar.py b/qutebrowser/widgets/statusbar/bar.py index 6549efdf2..3cd8598fb 100644 --- a/qutebrowser/widgets/statusbar/bar.py +++ b/qutebrowser/widgets/statusbar/bar.py @@ -32,7 +32,7 @@ from qutebrowser.widgets.statusbar._text import Text from qutebrowser.widgets.statusbar._keystring import KeyString from qutebrowser.widgets.statusbar._percentage import Percentage from qutebrowser.widgets.statusbar._url import Url -from qutebrowser.widgets.statusbar._prompt import Prompt, PromptMode, Question +from qutebrowser.widgets.statusbar._prompt import Prompt from qutebrowser.config.style import set_register_stylesheet, get_stylesheet @@ -317,18 +317,6 @@ class StatusBar(QWidget): self._text_pop_timer.setInterval(config.get('ui', 'message-timeout')) - @pyqtSlot('QNetworkReply', 'QAuthenticator') - def on_authentication_required(self, reply, authenticator): - q = Question() - q.mode = PromptMode.user_pwd - q.text = "Username ({}):".format(authenticator.realm()) - self.prompt.question = q - answer = self.prompt.exec_() - if answer is not None: - user, password = answer - authenticator.setUser(user) - authenticator.setPassword(password) - @pyqtSlot('QNetworkProxy', 'QAuthenticator') def on_proxy_authentication_required(self, proxy, authenticator): q = Question()