def _ask(self, text, mode, owner=None): """Ask a blocking question in the statusbar. Args: text: The text to display to the user. mode: A PromptMode. owner: An object which will abort the question if destroyed, or None. Return: The answer the user gave or None if the prompt was cancelled. """ q = usertypes.Question() q.text = text q.mode = mode self.shutting_down.connect(q.abort) if owner is not None: owner.destroyed.connect(q.abort) # This might be a generic network manager, e.g. one belonging to a # DownloadManager. In this case, just skip the webview thing. if self._tab_id is not None: tab = objreg.get('tab', scope='tab', window=self._win_id, tab=self._tab_id) tab.load_started.connect(q.abort) bridge = objreg.get('message-bridge', scope='window', window=self._win_id) bridge.ask(q, blocking=True) q.deleteLater() return q.answer
def fetch(self, reply): """Download a QNetworkReply to disk. Args: reply: The QNetworkReply to download. """ _inline, suggested_filename = http.parse_content_disposition(reply) log.downloads.debug("fetch: {} -> {}".format(reply.url(), suggested_filename)) download = DownloadItem(reply, self) download.finished.connect( functools.partial(self.on_finished, download)) download.data_changed.connect( functools.partial(self.on_data_changed, download)) download.error.connect(self.on_error) download.basename = suggested_filename idx = len(self.downloads) + 1 self.beginInsertRows(QModelIndex(), idx, idx) self.downloads.append(download) self.endInsertRows() q = usertypes.Question(self) q.text = "Save file to:" q.mode = usertypes.PromptMode.text q.default = suggested_filename q.answered.connect(download.set_filename) q.cancelled.connect(download.cancel) q.completed.connect(q.deleteLater) q.destroyed.connect(functools.partial(self.questions.remove, q)) self.questions.append(q) download.cancelled.connect(q.abort) message_bridge = objreg.get('message-bridge', scope='window', window=self._win_id) message_bridge.ask(q, blocking=False)
def _ask(self, text, mode, owner=None): """Ask a blocking question in the statusbar. Args: text: The text to display to the user. mode: A PromptMode. owner: An object which will abort the question if destroyed, or None. Return: The answer the user gave or None if the prompt was cancelled. """ q = usertypes.Question() q.text = text q.mode = mode self.shutting_down.connect(q.abort) if owner is not None: owner.destroyed.connect(q.abort) webview = objreg.get('webview', scope='tab', window=self._win_id, tab=self._tab_id) webview.loadStarted.connect(q.abort) bridge = objreg.get('message-bridge', scope='window', window=self._win_id) bridge.ask(q, blocking=True) q.deleteLater() return q.answer
def _build_question(title: str, text: str = None, *, mode: usertypes.PromptMode, default: Union[None, bool, str] = None, abort_on: Iterable[pyqtBoundSignal] = (), url: str = None, option: bool = None) -> usertypes.Question: """Common function for ask/ask_async.""" question = usertypes.Question() question.title = title question.text = text question.mode = mode question.default = default question.url = url if option is not None: if mode != usertypes.PromptMode.yesno: raise ValueError("Can only 'option' with PromptMode.yesno") if url is None: raise ValueError("Need 'url' given when 'option' is given") question.option = option for sig in abort_on: sig.connect(question.abort) return question
def on_feature_permission_requested(self, frame, feature): """Ask the user for approval for geolocation/notifications.""" if not isinstance(frame, QWebFrame): # pragma: no cover # This makes no sense whatsoever, but someone reported this being # called with a QBuffer... log.misc.error("on_feature_permission_requested got called with " "{!r}!".format(frame)) return options = { QWebPage.Notifications: ('content', 'notifications'), QWebPage.Geolocation: ('content', 'geolocation'), } config_val = config.get(*options[feature]) if config_val == 'ask': bridge = objreg.get('message-bridge', scope='window', window=self._win_id) q = usertypes.Question(bridge) q.mode = usertypes.PromptMode.yesno msgs = { QWebPage.Notifications: 'show notifications', QWebPage.Geolocation: 'access your location', } host = frame.url().host() if host: q.text = "Allow the website at {} to {}?".format( frame.url().host(), msgs[feature]) else: q.text = "Allow the website to {}?".format(msgs[feature]) yes_action = functools.partial(self.setFeaturePermission, frame, feature, QWebPage.PermissionGrantedByUser) q.answered_yes.connect(yes_action) no_action = functools.partial(self.setFeaturePermission, frame, feature, QWebPage.PermissionDeniedByUser) q.answered_no.connect(no_action) q.cancelled.connect(no_action) self.shutting_down.connect(q.abort) q.completed.connect(q.deleteLater) self.featurePermissionRequestCanceled.connect( functools.partial(self.on_feature_permission_cancelled, q, frame, feature)) self.loadStarted.connect(q.abort) bridge.ask(q, blocking=False) elif config_val: self.setFeaturePermission(frame, feature, QWebPage.PermissionGrantedByUser) else: self.setFeaturePermission(frame, feature, QWebPage.PermissionDeniedByUser)
def _prepare_question(self): """Prepare a Question object to be asked.""" q = usertypes.Question(self) q.text = "Save file to:" q.mode = usertypes.PromptMode.text q.completed.connect(q.deleteLater) q.destroyed.connect(functools.partial(self.questions.remove, q)) self.questions.append(q) return q
def fetch(self, reply, fileobj=None, filename=None): """Download a QNetworkReply to disk. Args: reply: The QNetworkReply to download. fileobj: The file object to write the answer to. filename: A path to write the data to. Return: The created DownloadItem. """ if fileobj is not None and filename is not None: raise TypeError("Only one of fileobj/filename may be given!") if filename is not None: suggested_filename = os.path.basename(filename) elif fileobj is not None and getattr(fileobj, 'name', None): suggested_filename = fileobj.name else: _inline, suggested_filename = http.parse_content_disposition(reply) log.downloads.debug("fetch: {} -> {}".format(reply.url(), suggested_filename)) download = DownloadItem(reply, self) download.finished.connect(functools.partial(self.on_finished, download)) download.data_changed.connect( functools.partial(self.on_data_changed, download)) download.error.connect(self.on_error) download.redirected.connect( functools.partial(self.on_redirect, download)) download.basename = suggested_filename idx = len(self.downloads) + 1 self.beginInsertRows(QModelIndex(), idx, idx) self.downloads.append(download) self.endInsertRows() if filename is not None: download.set_filename(filename) elif fileobj is not None: download.set_fileobj(fileobj) download.autoclose = False else: q = usertypes.Question(self) q.text = "Save file to:" q.mode = usertypes.PromptMode.text q.default = suggested_filename q.answered.connect(download.set_filename) q.cancelled.connect(download.cancel) q.completed.connect(q.deleteLater) q.destroyed.connect(functools.partial(self.questions.remove, q)) self.questions.append(q) download.cancelled.connect(q.abort) message_bridge = objreg.get('message-bridge', scope='window', window=self._win_id) message_bridge.ask(q, blocking=False) return download
def _ask_overwrite_question(self): """Create a Question object to be asked.""" q = usertypes.Question(self) q.text = self._filename + " already exists. Overwrite? (y/n)" q.mode = usertypes.PromptMode.yesno q.answered_yes.connect(self._create_fileobj) q.answered_no.connect(functools.partial(self.cancel, False)) q.cancelled.connect(functools.partial(self.cancel, False)) message_bridge = objreg.get('message-bridge', scope='window', window=self._win_id) message_bridge.ask(q, blocking=False)
def _ask_confirm_question(self, title, msg): no_action = functools.partial(self.cancel, remove_data=False) question = usertypes.Question() question.title = title question.text = msg question.mode = usertypes.PromptMode.yesno question.answered_yes.connect(self._after_set_filename) question.answered_no.connect(no_action) question.cancelled.connect(no_action) self.cancelled.connect(question.abort) self.error.connect(question.abort) message.global_bridge.ask(question, blocking=True)
def _build_question(title, text=None, *, mode, default=None, abort_on=()): """Common function for ask/ask_async.""" if not isinstance(mode, usertypes.PromptMode): raise TypeError("Mode {} is no PromptMode member!".format(mode)) question = usertypes.Question() question.title = title question.text = text question.mode = mode question.default = default for sig in abort_on: sig.connect(question.abort) return question
def alert(win_id, message): """Display an alert which needs to be confirmed. Args: win_id: The ID of the window which is calling this function. message: The message to show. """ q = usertypes.Question() q.text = message q.mode = usertypes.PromptMode.alert _wrapper(win_id, 'ask', q, blocking=True) q.deleteLater()
def _get_prompt_func(path): question = usertypes.Question() question.title = "test" question.default = path prompt = promptmod.DownloadFilenamePrompt(question) qtbot.add_widget(prompt) with qtbot.wait_signal(prompt._file_model.directoryLoaded): pass assert prompt._lineedit.text() == path return prompt
def start_download_checked(target, tab): """First check if dest is already a file, then start the download. Args: target: The DownloadTarget where the resulting file should be saved. tab: Specify the tab whose page should be loaded. """ if not isinstance(target, downloads.FileDownloadTarget): _start_download(target, tab) return # The default name is 'page title.mhtml' title = tab.title() default_name = utils.sanitize_filename(title + '.mhtml') # Remove characters which cannot be expressed in the file system encoding encoding = sys.getfilesystemencoding() default_name = utils.force_encoding(default_name, encoding) dest = utils.force_encoding(target.filename, encoding) dest = os.path.expanduser(dest) # See if we already have an absolute path path = downloads.create_full_filename(default_name, dest) if path is None: # We still only have a relative path, prepend download_dir and # try again. path = downloads.create_full_filename( default_name, os.path.join(downloads.download_dir(), dest)) downloads.last_used_directory = os.path.dirname(path) # Avoid downloading files if we can't save the output anyway... # Yes, this is prone to race conditions, but we're checking again before # saving the file anyway. if not os.path.isdir(os.path.dirname(path)): folder = os.path.dirname(path) message.error("Directory {} does not exist.".format(folder)) return target = downloads.FileDownloadTarget(path) if not os.path.isfile(path): _start_download(target, tab=tab) return q = usertypes.Question() q.mode = usertypes.PromptMode.yesno q.title = "Overwrite existing file?" q.text = "<b>{}</b> already exists. Overwrite?".format( html.escape(path)) q.completed.connect(q.deleteLater) q.answered_yes.connect(functools.partial( _start_download, target, tab=tab)) message.global_bridge.ask(q, blocking=False)
def _ask_confirm_question(self, msg): """Create a Question object to be asked.""" q = usertypes.Question(self) q.text = msg q.mode = usertypes.PromptMode.yesno q.answered_yes.connect(self._create_fileobj) q.answered_no.connect(functools.partial(self.cancel, False)) q.cancelled.connect(functools.partial(self.cancel, False)) self.cancelled.connect(q.abort) self.error.connect(q.abort) message_bridge = objreg.get('message-bridge', scope='window', window=self._win_id) message_bridge.ask(q, blocking=False)
def on_feature_permission_requested(self, frame, feature): """Ask the user for approval for geolocation/notifications.""" options = { QWebPage.Notifications: ('content', 'notifications'), QWebPage.Geolocation: ('content', 'geolocation'), } config_val = config.get(*options[feature]) if config_val == 'ask': bridge = objreg.get('message-bridge', scope='window', window=self._win_id) q = usertypes.Question(bridge) q.mode = usertypes.PromptMode.yesno msgs = { QWebPage.Notifications: 'show notifications', QWebPage.Geolocation: 'access your location', } host = frame.url().host() if host: q.text = "Allow the website at {} to {}?".format( frame.url().host(), msgs[feature]) else: q.text = "Allow the website to {}?".format(msgs[feature]) yes_action = functools.partial(self.setFeaturePermission, frame, feature, QWebPage.PermissionGrantedByUser) q.answered_yes.connect(yes_action) no_action = functools.partial(self.setFeaturePermission, frame, feature, QWebPage.PermissionDeniedByUser) q.answered_no.connect(no_action) q.cancelled.connect(no_action) q.completed.connect(q.deleteLater) self.featurePermissionRequestCanceled.connect( functools.partial(self.on_feature_permission_cancelled, q, frame, feature)) self.loadStarted.connect(q.abort) bridge.ask(q, blocking=False) elif config_val: self.setFeaturePermission(frame, feature, QWebPage.PermissionGrantedByUser) else: self.setFeaturePermission(frame, feature, QWebPage.PermissionDeniedByUser)
def start_download_checked(dest, web_view): """First check if dest is already a file, then start the download. Args: dest: The filename where the resulting file should be saved. web_view: Specify the webview whose page should be loaded. """ # The default name is 'page title.mht' title = web_view.title() default_name = utils.sanitize_filename(title + '.mht') # Remove characters which cannot be expressed in the file system encoding encoding = sys.getfilesystemencoding() default_name = utils.force_encoding(default_name, encoding) dest = utils.force_encoding(dest, encoding) dest = os.path.expanduser(dest) # See if we already have an absolute path path = downloads.create_full_filename(default_name, dest) if path is None: # We still only have a relative path, prepend download_dir and # try again. path = downloads.create_full_filename( default_name, os.path.join(downloads.download_dir(), dest)) downloads.last_used_directory = os.path.dirname(path) # Avoid downloading files if we can't save the output anyway... # Yes, this is prone to race conditions, but we're checking again before # saving the file anyway. if not os.path.isdir(os.path.dirname(path)): folder = os.path.dirname(path) message.error(web_view.win_id, "Directory {} does not exist.".format(folder)) return if not os.path.isfile(path): _start_download(path, web_view=web_view) return q = usertypes.Question() q.mode = usertypes.PromptMode.yesno q.text = "{} exists. Overwrite?".format(path) q.completed.connect(q.deleteLater) q.answered_yes.connect( functools.partial(_start_download, path, web_view=web_view)) message_bridge = objreg.get('message-bridge', scope='window', window=web_view.win_id) message_bridge.ask(q, blocking=False)
def _ask_create_parent_question(self, title, msg, force_overwrite, remember_directory): no_action = functools.partial(self.cancel, remove_data=False) question = usertypes.Question() question.title = title question.text = msg question.mode = usertypes.PromptMode.yesno question.answered_yes.connect(lambda: self._after_create_parent_question( force_overwrite, remember_directory)) question.answered_no.connect(no_action) question.cancelled.connect(no_action) self.cancelled.connect(question.abort) self.error.connect(question.abort) message.global_bridge.ask(question, blocking=True)
def ask_for_filename(suggested_filename, win_id, *, parent=None, prompt_download_directory=None): """Prepare a question for a download-path. If a filename can be determined directly, it is returned instead. Returns a (filename, question)-namedtuple, in which one component is None. filename is a string, question is a usertypes.Question. The question has a special .ask() method that takes no arguments for convenience, as this function does not yet ask the question, it only prepares it. Args: suggested_filename: The "default"-name that is pre-entered as path. win_id: The window where the question will be asked. parent: The parent of the question (a QObject). prompt_download_directory: If this is something else than None, it will overwrite the storage->prompt-download-directory setting. """ if prompt_download_directory is None: prompt_download_directory = config.get('storage', 'prompt-download-directory') if not prompt_download_directory: return DownloadPath(filename=download_dir(), question=None) encoding = sys.getfilesystemencoding() suggested_filename = utils.force_encoding(suggested_filename, encoding) q = usertypes.Question(parent) q.text = "Save file to:" q.mode = usertypes.PromptMode.text q.completed.connect(q.deleteLater) q.default = path_suggestion(suggested_filename) message_bridge = objreg.get('message-bridge', scope='window', window=win_id) q.ask = lambda: message_bridge.ask(q, blocking=False) return DownloadPath(filename=None, question=q)
def ask(win_id, message, mode, default=None): """Ask a modular question in the statusbar (blocking). Args: win_id: The ID of the window which is calling this function. message: 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 = usertypes.Question() q.text = message q.mode = mode q.default = default _get_bridge(win_id).ask(q, blocking=True) q.deleteLater() return q.answer
def get_filename_question(*, suggested_filename, url, parent=None): """Get a Question object for a download-path. Args: suggested_filename: The "default"-name that is pre-entered as path. url: The URL the download originated from. parent: The parent of the question (a QObject). """ encoding = sys.getfilesystemencoding() suggested_filename = utils.force_encoding(suggested_filename, encoding) q = usertypes.Question(parent) q.title = "Save file to:" q.text = "Please enter a location for <b>{}</b>".format( html.escape(url.toDisplayString())) q.mode = usertypes.PromptMode.download q.completed.connect(q.deleteLater) q.default = _path_suggestion(suggested_filename) return q
def confirm_async(win_id, message, yes_action, no_action=None, default=None): """Ask a yes/no question to the user and execute the given actions. Args: win_id: The ID of the window which is calling this function. message: The message to display to the user. yes_action: Callable to be called when the user answered yes. no_action: Callable to be called when the user answered no. default: True/False to set a default value, or None. """ q = usertypes.Question() q.text = message q.mode = usertypes.PromptMode.yesno q.default = default q.answered_yes.connect(yes_action) if no_action is not None: q.answered_no.connect(no_action) q.completed.connect(q.deleteLater) _wrapper(win_id, 'ask', q, blocking=False)
def ask_async(win_id, message, mode, handler, default=None): """Ask an async question in the statusbar. Args: win_id: The ID of the window which is calling this function. message: The message to display to the user. mode: A PromptMode. handler: The function to get called with the answer as argument. default: The default value to display. """ if not isinstance(mode, usertypes.PromptMode): raise TypeError("Mode {} is no PromptMode member!".format(mode)) q = usertypes.Question() q.text = message q.mode = mode q.default = default q.answered.connect(handler) q.completed.connect(q.deleteLater) _wrapper(win_id, 'ask', q, blocking=False)
def _build_question(title, text=None, *, mode, default=None, abort_on=(), url=None, option=None): """Common function for ask/ask_async.""" if not isinstance(mode, usertypes.PromptMode): raise TypeError("Mode {} is no PromptMode member!".format(mode)) question = usertypes.Question() question.title = title question.text = text question.mode = mode question.default = default question.url = url if option is not None: if mode != usertypes.PromptMode.yesno: raise ValueError("Can only 'option' with PromptMode.yesno") if url is None: raise ValueError("Need 'url' given when 'option' is given") question.option = option for sig in abort_on: sig.connect(question.abort) return question
def _ask(self, text, mode, default=None): """Ask a blocking question in the statusbar. Args: text: The text 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 = usertypes.Question() q.text = text q.mode = mode q.default = default self.loadStarted.connect(q.abort) self.shutting_down.connect(q.abort) bridge = objreg.get('message-bridge', scope='window', window=self._win_id) bridge.ask(q, blocking=True) q.deleteLater() return q.answer
def question(): return usertypes.Question()