Initial implementation of new messages
This commit is contained in:
parent
5bef7dc74c
commit
f16b96aa28
@ -180,8 +180,7 @@ def _process_args(args):
|
|||||||
try:
|
try:
|
||||||
config_obj.set('temp', sect, opt, val)
|
config_obj.set('temp', sect, opt, val)
|
||||||
except (configexc.Error, configparser.Error) as e:
|
except (configexc.Error, configparser.Error) as e:
|
||||||
message.error('current', "set: {} - {}".format(
|
message.error("set: {} - {}".format(e.__class__.__name__, e))
|
||||||
e.__class__.__name__, e))
|
|
||||||
|
|
||||||
if not args.override_restore:
|
if not args.override_restore:
|
||||||
_load_session(args.session)
|
_load_session(args.session)
|
||||||
@ -216,10 +215,9 @@ def _load_session(name):
|
|||||||
try:
|
try:
|
||||||
session_manager.load(name)
|
session_manager.load(name)
|
||||||
except sessions.SessionNotFoundError:
|
except sessions.SessionNotFoundError:
|
||||||
message.error('current', "Session {} not found!".format(name))
|
message.error("Session {} not found!".format(name))
|
||||||
except sessions.SessionError as e:
|
except sessions.SessionError as e:
|
||||||
message.error('current', "Failed to load session {}: {}".format(
|
message.error("Failed to load session {}: {}".format(name, e))
|
||||||
name, e))
|
|
||||||
try:
|
try:
|
||||||
del state_config['general']['session']
|
del state_config['general']['session']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -271,8 +269,8 @@ def process_pos_args(args, via_ipc=False, cwd=None, target_arg=None):
|
|||||||
try:
|
try:
|
||||||
url = urlutils.fuzzy_url(cmd, cwd, relative=True)
|
url = urlutils.fuzzy_url(cmd, cwd, relative=True)
|
||||||
except urlutils.InvalidUrlError as e:
|
except urlutils.InvalidUrlError as e:
|
||||||
message.error('current', "Error in startup argument '{}': "
|
message.error("Error in startup argument '{}': {}".format(
|
||||||
"{}".format(cmd, e))
|
cmd, e))
|
||||||
else:
|
else:
|
||||||
background = open_target in ['tab-bg', 'tab-bg-silent']
|
background = open_target in ['tab-bg', 'tab-bg-silent']
|
||||||
tabbed_browser.tabopen(url, background=background,
|
tabbed_browser.tabopen(url, background=background,
|
||||||
@ -301,8 +299,7 @@ def _open_startpage(win_id=None):
|
|||||||
try:
|
try:
|
||||||
url = urlutils.fuzzy_url(urlstr, do_search=False)
|
url = urlutils.fuzzy_url(urlstr, do_search=False)
|
||||||
except urlutils.InvalidUrlError as e:
|
except urlutils.InvalidUrlError as e:
|
||||||
message.error('current', "Error when opening startpage: "
|
message.error("Error when opening startpage: {}".format(e))
|
||||||
"{}".format(e))
|
|
||||||
tabbed_browser.tabopen(QUrl('about:blank'))
|
tabbed_browser.tabopen(QUrl('about:blank'))
|
||||||
else:
|
else:
|
||||||
tabbed_browser.tabopen(url)
|
tabbed_browser.tabopen(url)
|
||||||
@ -370,6 +367,9 @@ def _init_modules(args, crash_handler):
|
|||||||
crash_handler: The CrashHandler instance.
|
crash_handler: The CrashHandler instance.
|
||||||
"""
|
"""
|
||||||
# pylint: disable=too-many-statements
|
# pylint: disable=too-many-statements
|
||||||
|
log.init.debug("Initializing messages...")
|
||||||
|
message.init()
|
||||||
|
|
||||||
log.init.debug("Initializing save manager...")
|
log.init.debug("Initializing save manager...")
|
||||||
save_manager = savemanager.SaveManager(qApp)
|
save_manager = savemanager.SaveManager(qApp)
|
||||||
objreg.register('save-manager', save_manager)
|
objreg.register('save-manager', save_manager)
|
||||||
|
@ -174,12 +174,10 @@ class HostBlocker:
|
|||||||
args = objreg.get('args')
|
args = objreg.get('args')
|
||||||
if (config.get('content', 'host-block-lists') is not None and
|
if (config.get('content', 'host-block-lists') is not None and
|
||||||
args.basedir is None):
|
args.basedir is None):
|
||||||
message.info('current',
|
message.info("Run :adblock-update to get adblock lists.")
|
||||||
"Run :adblock-update to get adblock lists.")
|
|
||||||
|
|
||||||
@cmdutils.register(instance='host-blocker')
|
@cmdutils.register(instance='host-blocker')
|
||||||
@cmdutils.argument('win_id', win_id=True)
|
def adblock_update(self):
|
||||||
def adblock_update(self, win_id):
|
|
||||||
"""Update the adblock block lists.
|
"""Update the adblock block lists.
|
||||||
|
|
||||||
This updates `~/.local/share/qutebrowser/blocked-hosts` with downloaded
|
This updates `~/.local/share/qutebrowser/blocked-hosts` with downloaded
|
||||||
@ -201,12 +199,12 @@ class HostBlocker:
|
|||||||
try:
|
try:
|
||||||
fileobj = open(url.path(), 'rb')
|
fileobj = open(url.path(), 'rb')
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
message.error(win_id, "adblock: Error while reading {}: "
|
message.error("adblock: Error while reading {}: {}".format(
|
||||||
"{}".format(url.path(), e.strerror))
|
url.path(), e.strerror))
|
||||||
continue
|
continue
|
||||||
download = FakeDownload(fileobj)
|
download = FakeDownload(fileobj)
|
||||||
self._in_progress.append(download)
|
self._in_progress.append(download)
|
||||||
self.on_download_finished(download, win_id)
|
self.on_download_finished(download)
|
||||||
else:
|
else:
|
||||||
fobj = io.BytesIO()
|
fobj = io.BytesIO()
|
||||||
fobj.name = 'adblock: ' + url.host()
|
fobj.name = 'adblock: ' + url.host()
|
||||||
@ -215,8 +213,7 @@ class HostBlocker:
|
|||||||
auto_remove=True)
|
auto_remove=True)
|
||||||
self._in_progress.append(download)
|
self._in_progress.append(download)
|
||||||
download.finished.connect(
|
download.finished.connect(
|
||||||
functools.partial(self.on_download_finished, download,
|
functools.partial(self.on_download_finished, download))
|
||||||
win_id))
|
|
||||||
|
|
||||||
def _merge_file(self, byte_io):
|
def _merge_file(self, byte_io):
|
||||||
"""Read and merge host files.
|
"""Read and merge host files.
|
||||||
@ -233,8 +230,8 @@ class HostBlocker:
|
|||||||
f = get_fileobj(byte_io)
|
f = get_fileobj(byte_io)
|
||||||
except (OSError, UnicodeDecodeError, zipfile.BadZipFile,
|
except (OSError, UnicodeDecodeError, zipfile.BadZipFile,
|
||||||
zipfile.LargeZipFile) as e:
|
zipfile.LargeZipFile) as e:
|
||||||
message.error('current', "adblock: Error while reading {}: {} - "
|
message.error("adblock: Error while reading {}: {} - {}".format(
|
||||||
"{}".format(byte_io.name, e.__class__.__name__, e))
|
byte_io.name, e.__class__.__name__, e))
|
||||||
return
|
return
|
||||||
for line in f:
|
for line in f:
|
||||||
line_count += 1
|
line_count += 1
|
||||||
@ -262,16 +259,16 @@ class HostBlocker:
|
|||||||
self._blocked_hosts.add(host)
|
self._blocked_hosts.add(host)
|
||||||
log.misc.debug("{}: read {} lines".format(byte_io.name, line_count))
|
log.misc.debug("{}: read {} lines".format(byte_io.name, line_count))
|
||||||
if error_count > 0:
|
if error_count > 0:
|
||||||
message.error('current', "adblock: {} read errors for {}".format(
|
message.error("adblock: {} read errors for {}".format(error_count,
|
||||||
error_count, byte_io.name))
|
byte_io.name))
|
||||||
|
|
||||||
def on_lists_downloaded(self, win_id):
|
def on_lists_downloaded(self):
|
||||||
"""Install block lists after files have been downloaded."""
|
"""Install block lists after files have been downloaded."""
|
||||||
with open(self._local_hosts_file, 'w', encoding='utf-8') as f:
|
with open(self._local_hosts_file, 'w', encoding='utf-8') as f:
|
||||||
for host in sorted(self._blocked_hosts):
|
for host in sorted(self._blocked_hosts):
|
||||||
f.write(host + '\n')
|
f.write(host + '\n')
|
||||||
message.info(win_id, "adblock: Read {} hosts from {} sources."
|
message.info("adblock: Read {} hosts from {} sources.".format(
|
||||||
.format(len(self._blocked_hosts), self._done_count))
|
len(self._blocked_hosts), self._done_count))
|
||||||
|
|
||||||
@config.change_filter('content', 'host-block-lists')
|
@config.change_filter('content', 'host-block-lists')
|
||||||
def on_config_changed(self):
|
def on_config_changed(self):
|
||||||
@ -285,12 +282,11 @@ class HostBlocker:
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
log.misc.exception("Failed to delete hosts file: {}".format(e))
|
log.misc.exception("Failed to delete hosts file: {}".format(e))
|
||||||
|
|
||||||
def on_download_finished(self, download, win_id):
|
def on_download_finished(self, download):
|
||||||
"""Check if all downloads are finished and if so, trigger reading.
|
"""Check if all downloads are finished and if so, trigger reading.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
download: The finished DownloadItem.
|
download: The finished DownloadItem.
|
||||||
win_id: The window ID in which :adblock-update was called
|
|
||||||
"""
|
"""
|
||||||
self._in_progress.remove(download)
|
self._in_progress.remove(download)
|
||||||
if download.successful:
|
if download.successful:
|
||||||
@ -301,6 +297,6 @@ class HostBlocker:
|
|||||||
download.fileobj.close()
|
download.fileobj.close()
|
||||||
if not self._in_progress:
|
if not self._in_progress:
|
||||||
try:
|
try:
|
||||||
self.on_lists_downloaded(win_id)
|
self.on_lists_downloaded()
|
||||||
except OSError:
|
except OSError:
|
||||||
log.misc.exception("Failed to write host block list!")
|
log.misc.exception("Failed to write host block list!")
|
||||||
|
@ -620,7 +620,7 @@ class AbstractTab(QWidget):
|
|||||||
|
|
||||||
if not url.isValid():
|
if not url.isValid():
|
||||||
msg = urlutils.get_errstring(url, "Invalid link clicked")
|
msg = urlutils.get_errstring(url, "Invalid link clicked")
|
||||||
message.error(self.win_id, msg)
|
message.error(msg)
|
||||||
self.data.open_target = usertypes.ClickTarget.normal
|
self.data.open_target = usertypes.ClickTarget.normal
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -303,7 +303,7 @@ class CommandDispatcher:
|
|||||||
except urlutils.InvalidUrlError as e:
|
except urlutils.InvalidUrlError as e:
|
||||||
# We don't use cmdexc.CommandError here as this can be
|
# We don't use cmdexc.CommandError here as this can be
|
||||||
# called async from edit_url
|
# called async from edit_url
|
||||||
message.error(self._win_id, str(e))
|
message.error(str(e))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _parse_url_input(self, url):
|
def _parse_url_input(self, url):
|
||||||
@ -719,7 +719,7 @@ class CommandDispatcher:
|
|||||||
caret = self._current_widget().caret
|
caret = self._current_widget().caret
|
||||||
s = caret.selection()
|
s = caret.selection()
|
||||||
if not caret.has_selection() or not s:
|
if not caret.has_selection() or not s:
|
||||||
message.info(self._win_id, "Nothing to yank")
|
message.info("Nothing to yank")
|
||||||
return
|
return
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
raise ValueError("Invalid value {!r} for `what'.".format(what))
|
raise ValueError("Invalid value {!r} for `what'.".format(what))
|
||||||
@ -732,10 +732,9 @@ class CommandDispatcher:
|
|||||||
|
|
||||||
utils.set_clipboard(s, selection=sel)
|
utils.set_clipboard(s, selection=sel)
|
||||||
if what != 'selection':
|
if what != 'selection':
|
||||||
message.info(self._win_id, "Yanked {} to {}: {}".format(
|
message.info("Yanked {} to {}: {}".format(what, target, s))
|
||||||
what, target, s))
|
|
||||||
else:
|
else:
|
||||||
message.info(self._win_id, "{} {} yanked to {}".format(
|
message.info("{} {} yanked to {}".format(
|
||||||
len(s), "char" if len(s) == 1 else "chars", target))
|
len(s), "char" if len(s) == 1 else "chars", target))
|
||||||
if not keep:
|
if not keep:
|
||||||
modeman.maybe_leave(self._win_id, KeyMode.caret,
|
modeman.maybe_leave(self._win_id, KeyMode.caret,
|
||||||
@ -754,7 +753,7 @@ class CommandDispatcher:
|
|||||||
perc = tab.zoom.offset(count)
|
perc = tab.zoom.offset(count)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise cmdexc.CommandError(e)
|
raise cmdexc.CommandError(e)
|
||||||
message.info(self._win_id, "Zoom level: {}%".format(perc))
|
message.info("Zoom level: {}%".format(perc))
|
||||||
|
|
||||||
@cmdutils.register(instance='command-dispatcher', scope='window')
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
||||||
@cmdutils.argument('count', count=True)
|
@cmdutils.argument('count', count=True)
|
||||||
@ -769,7 +768,7 @@ class CommandDispatcher:
|
|||||||
perc = tab.zoom.offset(-count)
|
perc = tab.zoom.offset(-count)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise cmdexc.CommandError(e)
|
raise cmdexc.CommandError(e)
|
||||||
message.info(self._win_id, "Zoom level: {}%".format(perc))
|
message.info("Zoom level: {}%".format(perc))
|
||||||
|
|
||||||
@cmdutils.register(instance='command-dispatcher', scope='window')
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
||||||
@cmdutils.argument('count', count=True)
|
@cmdutils.argument('count', count=True)
|
||||||
@ -793,7 +792,7 @@ class CommandDispatcher:
|
|||||||
tab.zoom.set_factor(float(level) / 100)
|
tab.zoom.set_factor(float(level) / 100)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise cmdexc.CommandError("Can't zoom {}%!".format(level))
|
raise cmdexc.CommandError("Can't zoom {}%!".format(level))
|
||||||
message.info(self._win_id, "Zoom level: {}%".format(level))
|
message.info("Zoom level: {}%".format(level))
|
||||||
|
|
||||||
@cmdutils.register(instance='command-dispatcher', scope='window')
|
@cmdutils.register(instance='command-dispatcher', scope='window')
|
||||||
def tab_only(self, left=False, right=False):
|
def tab_only(self, left=False, right=False):
|
||||||
@ -1073,8 +1072,7 @@ class CommandDispatcher:
|
|||||||
self._run_userscript(cmd, *args, verbose=verbose)
|
self._run_userscript(cmd, *args, verbose=verbose)
|
||||||
else:
|
else:
|
||||||
cmd = os.path.expanduser(cmd)
|
cmd = os.path.expanduser(cmd)
|
||||||
proc = guiprocess.GUIProcess(self._win_id, what='command',
|
proc = guiprocess.GUIProcess(what='command', verbose=verbose,
|
||||||
verbose=verbose,
|
|
||||||
parent=self._tabbed_browser)
|
parent=self._tabbed_browser)
|
||||||
if detach:
|
if detach:
|
||||||
proc.start_detached(cmd, args)
|
proc.start_detached(cmd, args)
|
||||||
@ -1211,8 +1209,7 @@ class CommandDispatcher:
|
|||||||
raise cmdexc.CommandError(str(e))
|
raise cmdexc.CommandError(str(e))
|
||||||
else:
|
else:
|
||||||
msg = "Bookmarked {}!" if was_added else "Removed bookmark {}!"
|
msg = "Bookmarked {}!" if was_added else "Removed bookmark {}!"
|
||||||
message.info(self._win_id,
|
message.info(msg.format(url.toDisplayString()))
|
||||||
msg.format(url.toDisplayString()))
|
|
||||||
|
|
||||||
@cmdutils.register(instance='command-dispatcher', scope='window',
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
||||||
maxsplit=0)
|
maxsplit=0)
|
||||||
@ -1304,9 +1301,8 @@ class CommandDispatcher:
|
|||||||
mhtml_: Download the current page and all assets as mhtml file.
|
mhtml_: Download the current page and all assets as mhtml file.
|
||||||
"""
|
"""
|
||||||
if dest_old is not None:
|
if dest_old is not None:
|
||||||
message.warning(self._win_id,
|
message.warning(":download [url] [dest] is deprecated - use "
|
||||||
":download [url] [dest] is deprecated - use"
|
":download --dest [dest] [url]")
|
||||||
" download --dest [dest] [url]")
|
|
||||||
if dest is not None:
|
if dest is not None:
|
||||||
raise cmdexc.CommandError("Can't give two destinations for the"
|
raise cmdexc.CommandError("Can't give two destinations for the"
|
||||||
" download.")
|
" download.")
|
||||||
@ -1379,7 +1375,7 @@ class CommandDispatcher:
|
|||||||
try:
|
try:
|
||||||
current_url = self._current_url()
|
current_url = self._current_url()
|
||||||
except cmdexc.CommandError as e:
|
except cmdexc.CommandError as e:
|
||||||
message.error(self._win_id, str(e))
|
message.error(str(e))
|
||||||
return
|
return
|
||||||
new_tab = self._tabbed_browser.tabopen(explicit=True)
|
new_tab = self._tabbed_browser.tabopen(explicit=True)
|
||||||
new_tab.set_html(highlighted, current_url)
|
new_tab.set_html(highlighted, current_url)
|
||||||
@ -1404,10 +1400,9 @@ class CommandDispatcher:
|
|||||||
with open(dest, 'w', encoding='utf-8') as f:
|
with open(dest, 'w', encoding='utf-8') as f:
|
||||||
f.write(data)
|
f.write(data)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
message.error(self._win_id,
|
message.error('Could not write page: {}'.format(e))
|
||||||
'Could not write page: {}'.format(e))
|
|
||||||
else:
|
else:
|
||||||
message.info(self._win_id, "Dumped page to {}.".format(dest))
|
message.info("Dumped page to {}.".format(dest))
|
||||||
|
|
||||||
tab.dump_async(callback, plain=plain)
|
tab.dump_async(callback, plain=plain)
|
||||||
|
|
||||||
@ -1477,14 +1472,14 @@ class CommandDispatcher:
|
|||||||
def _open_editor_cb(self, elem):
|
def _open_editor_cb(self, elem):
|
||||||
"""Open editor after the focus elem was found in open_editor."""
|
"""Open editor after the focus elem was found in open_editor."""
|
||||||
if elem is None:
|
if elem is None:
|
||||||
message.error(self._win_id, "No element focused!")
|
message.error("No element focused!")
|
||||||
return
|
return
|
||||||
if not elem.is_editable(strict=True):
|
if not elem.is_editable(strict=True):
|
||||||
message.error(self._win_id, "Focused element is not editable!")
|
message.error("Focused element is not editable!")
|
||||||
return
|
return
|
||||||
|
|
||||||
text = elem.text(use_js=True)
|
text = elem.text(use_js=True)
|
||||||
ed = editor.ExternalEditor(self._win_id, self._tabbed_browser)
|
ed = editor.ExternalEditor(self._tabbed_browser)
|
||||||
ed.editing_finished.connect(functools.partial(
|
ed.editing_finished.connect(functools.partial(
|
||||||
self.on_editing_finished, elem))
|
self.on_editing_finished, elem))
|
||||||
ed.edit(text)
|
ed.edit(text)
|
||||||
@ -1539,12 +1534,12 @@ class CommandDispatcher:
|
|||||||
|
|
||||||
def _insert_text_cb(elem):
|
def _insert_text_cb(elem):
|
||||||
if elem is None:
|
if elem is None:
|
||||||
message.error(self._win_id, "No element focused!")
|
message.error("No element focused!")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
elem.insert_text(text)
|
elem.insert_text(text)
|
||||||
except webelem.Error as e:
|
except webelem.Error as e:
|
||||||
message.error(self._win_id, str(e))
|
message.error(str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
tab.elements.find_focused(_insert_text_cb)
|
tab.elements.find_focused(_insert_text_cb)
|
||||||
@ -1571,22 +1566,21 @@ class CommandDispatcher:
|
|||||||
def single_cb(elem):
|
def single_cb(elem):
|
||||||
"""Click a single element."""
|
"""Click a single element."""
|
||||||
if elem is None:
|
if elem is None:
|
||||||
message.error(self._win_id, "No element found!")
|
message.error("No element found!")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
elem.click(target)
|
elem.click(target)
|
||||||
except webelem.Error as e:
|
except webelem.Error as e:
|
||||||
message.error(self._win_id, str(e))
|
message.error(str(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
# def multiple_cb(elems):
|
# def multiple_cb(elems):
|
||||||
# """Click multiple elements (with only one expected)."""
|
# """Click multiple elements (with only one expected)."""
|
||||||
# if not elems:
|
# if not elems:
|
||||||
# message.error(self._win_id, "No element found!")
|
# message.error("No element found!")
|
||||||
# return
|
# return
|
||||||
# elif len(elems) != 1:
|
# elif len(elems) != 1:
|
||||||
# message.error(self._win_id, "{} elements found!".format(
|
# message.error("{} elements found!".format(len(elems)))
|
||||||
# len(elems)))
|
|
||||||
# return
|
# return
|
||||||
# elems[0].click(target)
|
# elems[0].click(target)
|
||||||
|
|
||||||
@ -1616,14 +1610,11 @@ class CommandDispatcher:
|
|||||||
if found:
|
if found:
|
||||||
# Check if the scroll position got smaller and show info.
|
# Check if the scroll position got smaller and show info.
|
||||||
if not going_up and tab.scroller.pos_px().y() < old_scroll_pos.y():
|
if not going_up and tab.scroller.pos_px().y() < old_scroll_pos.y():
|
||||||
message.info(self._win_id, "Search hit BOTTOM, continuing "
|
message.info("Search hit BOTTOM, continuing at TOP")
|
||||||
"at TOP", immediately=True)
|
|
||||||
elif going_up and tab.scroller.pos_px().y() > old_scroll_pos.y():
|
elif going_up and tab.scroller.pos_px().y() > old_scroll_pos.y():
|
||||||
message.info(self._win_id, "Search hit TOP, continuing at "
|
message.info("Search hit TOP, continuing at BOTTOM")
|
||||||
"BOTTOM", immediately=True)
|
|
||||||
else:
|
else:
|
||||||
message.warning(self._win_id, "Text '{}' not found on "
|
message.warning("Text '{}' not found on page!".format(text))
|
||||||
"page!".format(text), immediately=True)
|
|
||||||
|
|
||||||
@cmdutils.register(instance='command-dispatcher', scope='window',
|
@cmdutils.register(instance='command-dispatcher', scope='window',
|
||||||
maxsplit=0)
|
maxsplit=0)
|
||||||
@ -1941,7 +1932,7 @@ class CommandDispatcher:
|
|||||||
# BrowserPage.javaScriptConsoleMessage(), but
|
# BrowserPage.javaScriptConsoleMessage(), but
|
||||||
# distinguishing between :jseval errors and errors from the
|
# distinguishing between :jseval errors and errors from the
|
||||||
# webpage is not trivial...
|
# webpage is not trivial...
|
||||||
message.info(self._win_id, 'No output or error')
|
message.info('No output or error')
|
||||||
else:
|
else:
|
||||||
# The output can be a string, number, dict, array, etc. But
|
# The output can be a string, number, dict, array, etc. But
|
||||||
# *don't* output too much data, as this will make
|
# *don't* output too much data, as this will make
|
||||||
@ -1949,7 +1940,7 @@ class CommandDispatcher:
|
|||||||
out = str(out)
|
out = str(out)
|
||||||
if len(out) > 5000:
|
if len(out) > 5000:
|
||||||
out = out[:5000] + ' [...trimmed...]'
|
out = out[:5000] + ' [...trimmed...]'
|
||||||
message.info(self._win_id, out)
|
message.info(out)
|
||||||
|
|
||||||
widget = self._current_widget()
|
widget = self._current_widget()
|
||||||
widget.run_js_async(js_code, callback=jseval_cb, world=world)
|
widget.run_js_async(js_code, callback=jseval_cb, world=world)
|
||||||
@ -2016,7 +2007,7 @@ class CommandDispatcher:
|
|||||||
|
|
||||||
old_url = self._current_url().toString()
|
old_url = self._current_url().toString()
|
||||||
|
|
||||||
ed = editor.ExternalEditor(self._win_id, self._tabbed_browser)
|
ed = editor.ExternalEditor(self._tabbed_browser)
|
||||||
|
|
||||||
# Passthrough for openurl args (e.g. -t, -b, -w)
|
# Passthrough for openurl args (e.g. -t, -b, -w)
|
||||||
ed.editing_finished.connect(functools.partial(
|
ed.editing_finished.connect(functools.partial(
|
||||||
|
@ -238,7 +238,7 @@ class HintActions:
|
|||||||
msg = "Yanked URL to {}: {}".format(
|
msg = "Yanked URL to {}: {}".format(
|
||||||
"primary selection" if sel else "clipboard",
|
"primary selection" if sel else "clipboard",
|
||||||
urlstr)
|
urlstr)
|
||||||
message.info(self._win_id, msg)
|
message.info(msg)
|
||||||
|
|
||||||
def run_cmd(self, url, context):
|
def run_cmd(self, url, context):
|
||||||
"""Run the command based on a hint URL.
|
"""Run the command based on a hint URL.
|
||||||
@ -411,7 +411,7 @@ class HintManager(QObject):
|
|||||||
try:
|
try:
|
||||||
return self._word_hinter.hint(elems)
|
return self._word_hinter.hint(elems)
|
||||||
except HintingError as e:
|
except HintingError as e:
|
||||||
message.error(self._win_id, str(e), immediately=True)
|
message.error(str(e))
|
||||||
# falls back on letter hints
|
# falls back on letter hints
|
||||||
if hint_mode == 'number':
|
if hint_mode == 'number':
|
||||||
chars = '0123456789'
|
chars = '0123456789'
|
||||||
@ -570,7 +570,7 @@ class HintManager(QObject):
|
|||||||
filterfunc = webelem.FILTERS.get(self._context.group, lambda e: True)
|
filterfunc = webelem.FILTERS.get(self._context.group, lambda e: True)
|
||||||
elems = [e for e in elems if filterfunc(e)]
|
elems = [e for e in elems if filterfunc(e)]
|
||||||
if not elems:
|
if not elems:
|
||||||
message.error(self._win_id, "No elements found.", immediately=True)
|
message.error("No elements found.")
|
||||||
return
|
return
|
||||||
strings = self._hint_strings(elems)
|
strings = self._hint_strings(elems)
|
||||||
log.hints.debug("hints: {}".format(', '.join(strings)))
|
log.hints.debug("hints: {}".format(', '.join(strings)))
|
||||||
@ -662,8 +662,8 @@ class HintManager(QObject):
|
|||||||
raise cmdexc.CommandError("No WebView available yet!")
|
raise cmdexc.CommandError("No WebView available yet!")
|
||||||
if (tab.backend == usertypes.Backend.QtWebEngine and
|
if (tab.backend == usertypes.Backend.QtWebEngine and
|
||||||
target == Target.download):
|
target == Target.download):
|
||||||
message.error(self._win_id, "The download target is not available "
|
message.error("The download target is not available yet with "
|
||||||
"yet with QtWebEngine.", immediately=True)
|
"QtWebEngine.")
|
||||||
return
|
return
|
||||||
|
|
||||||
mode_manager = objreg.get('mode-manager', scope='window',
|
mode_manager = objreg.get('mode-manager', scope='window',
|
||||||
@ -849,9 +849,7 @@ class HintManager(QObject):
|
|||||||
elem = self._context.labels[keystr].elem
|
elem = self._context.labels[keystr].elem
|
||||||
|
|
||||||
if not elem.has_frame():
|
if not elem.has_frame():
|
||||||
message.error(self._win_id,
|
message.error("This element has no webframe.")
|
||||||
"This element has no webframe.",
|
|
||||||
immediately=True)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._context.target in elem_handlers:
|
if self._context.target in elem_handlers:
|
||||||
@ -860,9 +858,7 @@ class HintManager(QObject):
|
|||||||
elif self._context.target in url_handlers:
|
elif self._context.target in url_handlers:
|
||||||
url = elem.resolve_url(self._context.baseurl)
|
url = elem.resolve_url(self._context.baseurl)
|
||||||
if url is None:
|
if url is None:
|
||||||
message.error(self._win_id,
|
message.error("No suitable link found for this element.")
|
||||||
"No suitable link found for this element.",
|
|
||||||
immediately=True)
|
|
||||||
return
|
return
|
||||||
handler = functools.partial(url_handlers[self._context.target],
|
handler = functools.partial(url_handlers[self._context.target],
|
||||||
url, self._context)
|
url, self._context)
|
||||||
@ -882,7 +878,7 @@ class HintManager(QObject):
|
|||||||
try:
|
try:
|
||||||
handler()
|
handler()
|
||||||
except HintingError as e:
|
except HintingError as e:
|
||||||
message.error(self._win_id, str(e), immediately=True)
|
message.error(str(e))
|
||||||
|
|
||||||
@cmdutils.register(instance='hintmanager', scope='tab', hide=True,
|
@cmdutils.register(instance='hintmanager', scope='tab', hide=True,
|
||||||
modes=[usertypes.KeyMode.hint])
|
modes=[usertypes.KeyMode.hint])
|
||||||
|
@ -125,7 +125,7 @@ class MouseEventFilter(QObject):
|
|||||||
if factor < 0:
|
if factor < 0:
|
||||||
return False
|
return False
|
||||||
perc = int(100 * factor)
|
perc = int(100 * factor)
|
||||||
message.info(self._tab.win_id, "Zoom level: {}%".format(perc))
|
message.info("Zoom level: {}%".format(perc))
|
||||||
self._tab.zoom.set_factor(factor)
|
self._tab.zoom.set_factor(factor)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -191,15 +191,13 @@ class MouseEventFilter(QObject):
|
|||||||
if self._tab.history.can_go_back():
|
if self._tab.history.can_go_back():
|
||||||
self._tab.history.back()
|
self._tab.history.back()
|
||||||
else:
|
else:
|
||||||
message.error(self._tab.win_id, "At beginning of history.",
|
message.error("At beginning of history.")
|
||||||
immediately=True)
|
|
||||||
elif e.button() in [Qt.XButton2, Qt.RightButton]:
|
elif e.button() in [Qt.XButton2, Qt.RightButton]:
|
||||||
# Forward button on mice which have it, or rocker gesture
|
# Forward button on mice which have it, or rocker gesture
|
||||||
if self._tab.history.can_go_forward():
|
if self._tab.history.can_go_forward():
|
||||||
self._tab.history.forward()
|
self._tab.history.forward()
|
||||||
else:
|
else:
|
||||||
message.error(self._tab.win_id, "At end of history.",
|
message.error("At end of history.")
|
||||||
immediately=True)
|
|
||||||
|
|
||||||
def _mousepress_opentarget(self, e):
|
def _mousepress_opentarget(self, e):
|
||||||
"""Set the open target when something was clicked.
|
"""Set the open target when something was clicked.
|
||||||
|
@ -116,11 +116,11 @@ def prevnext(*, browsertab, win_id, baseurl, prev=False,
|
|||||||
word = 'prev' if prev else 'forward'
|
word = 'prev' if prev else 'forward'
|
||||||
|
|
||||||
if elem is None:
|
if elem is None:
|
||||||
message.error(win_id, "No {} links found!".format(word))
|
message.error("No {} links found!".format(word))
|
||||||
return
|
return
|
||||||
url = elem.resolve_url(baseurl)
|
url = elem.resolve_url(baseurl)
|
||||||
if url is None:
|
if url is None:
|
||||||
message.error(win_id, "No {} links found!".format(word))
|
message.error("No {} links found!".format(word))
|
||||||
return
|
return
|
||||||
qtutils.ensure_valid(url)
|
qtutils.ensure_valid(url)
|
||||||
|
|
||||||
|
@ -245,8 +245,8 @@ def qute_help(url):
|
|||||||
else:
|
else:
|
||||||
urlpath = urlpath.lstrip('/')
|
urlpath = urlpath.lstrip('/')
|
||||||
if not docutils.docs_up_to_date(urlpath):
|
if not docutils.docs_up_to_date(urlpath):
|
||||||
message.error('current', "Your documentation is outdated! Please "
|
message.error("Your documentation is outdated! Please re-run "
|
||||||
"re-run scripts/asciidoc2html.py.")
|
"scripts/asciidoc2html.py.")
|
||||||
path = 'html/doc/{}'.format(urlpath)
|
path = 'html/doc/{}'.format(urlpath)
|
||||||
if urlpath.endswith('.png'):
|
if urlpath.endswith('.png'):
|
||||||
return 'image/png', utils.read_file(path, binary=True)
|
return 'image/png', utils.read_file(path, binary=True)
|
||||||
|
@ -155,7 +155,7 @@ class QuickmarkManager(UrlMarkManager):
|
|||||||
try:
|
try:
|
||||||
key, url = line.rsplit(maxsplit=1)
|
key, url = line.rsplit(maxsplit=1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
message.error('current', "Invalid quickmark '{}'".format(line))
|
message.error("Invalid quickmark '{}'".format(line))
|
||||||
else:
|
else:
|
||||||
self.marks[key] = url
|
self.marks[key] = url
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ class QuickmarkManager(UrlMarkManager):
|
|||||||
url: The quickmark url as a QUrl.
|
url: The quickmark url as a QUrl.
|
||||||
"""
|
"""
|
||||||
if not url.isValid():
|
if not url.isValid():
|
||||||
urlutils.invalid_url_error(win_id, url, "save quickmark")
|
urlutils.invalid_url_error(url, "save quickmark")
|
||||||
return
|
return
|
||||||
urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded)
|
urlstr = url.toString(QUrl.RemovePassword | QUrl.FullyEncoded)
|
||||||
message.ask_async(
|
message.ask_async(
|
||||||
@ -190,10 +190,10 @@ class QuickmarkManager(UrlMarkManager):
|
|||||||
# We don't raise cmdexc.CommandError here as this can be called async
|
# We don't raise cmdexc.CommandError here as this can be called async
|
||||||
# via prompt_save.
|
# via prompt_save.
|
||||||
if not name:
|
if not name:
|
||||||
message.error(win_id, "Can't set mark with empty name!")
|
message.error("Can't set mark with empty name!")
|
||||||
return
|
return
|
||||||
if not url:
|
if not url:
|
||||||
message.error(win_id, "Can't set mark with empty URL!")
|
message.error("Can't set mark with empty URL!")
|
||||||
return
|
return
|
||||||
|
|
||||||
def set_mark():
|
def set_mark():
|
||||||
|
@ -568,7 +568,7 @@ class DownloadItem(QObject):
|
|||||||
args.append(filename)
|
args.append(filename)
|
||||||
log.downloads.debug("Opening {} with {}"
|
log.downloads.debug("Opening {} with {}"
|
||||||
.format(filename, [cmd] + args))
|
.format(filename, [cmd] + args))
|
||||||
proc = guiprocess.GUIProcess(self._win_id, what='download')
|
proc = guiprocess.GUIProcess(what='download')
|
||||||
proc.start_detached(cmd, args)
|
proc.start_detached(cmd, args)
|
||||||
|
|
||||||
def set_filename(self, filename):
|
def set_filename(self, filename):
|
||||||
@ -599,7 +599,6 @@ class DownloadItem(QObject):
|
|||||||
# may be set for XDG_DOWNLOAD_DIR
|
# may be set for XDG_DOWNLOAD_DIR
|
||||||
if self._filename is None:
|
if self._filename is None:
|
||||||
message.error(
|
message.error(
|
||||||
self._win_id,
|
|
||||||
"XDG_DOWNLOAD_DIR points to a relative path - please check"
|
"XDG_DOWNLOAD_DIR points to a relative path - please check"
|
||||||
" your ~/.config/user-dirs.dirs. The download is saved in"
|
" your ~/.config/user-dirs.dirs. The download is saved in"
|
||||||
" your home directory.",
|
" your home directory.",
|
||||||
@ -843,7 +842,7 @@ class DownloadManager(QObject):
|
|||||||
The created DownloadItem.
|
The created DownloadItem.
|
||||||
"""
|
"""
|
||||||
if not url.isValid():
|
if not url.isValid():
|
||||||
urlutils.invalid_url_error(self._win_id, url, "start download")
|
urlutils.invalid_url_error(url, "start download")
|
||||||
return
|
return
|
||||||
req = QNetworkRequest(url)
|
req = QNetworkRequest(url)
|
||||||
return self.get_request(req, **kwargs)
|
return self.get_request(req, **kwargs)
|
||||||
@ -1005,7 +1004,7 @@ class DownloadManager(QObject):
|
|||||||
fobj = tmp_manager.get_tmpfile(suggested_filename)
|
fobj = tmp_manager.get_tmpfile(suggested_filename)
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
msg = "Download error: {}".format(exc)
|
msg = "Download error: {}".format(exc)
|
||||||
message.error(self._win_id, msg)
|
message.error(msg)
|
||||||
download.cancel()
|
download.cancel()
|
||||||
return
|
return
|
||||||
download.finished.connect(
|
download.finished.connect(
|
||||||
@ -1056,7 +1055,7 @@ class DownloadManager(QObject):
|
|||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def _on_error(self, msg):
|
def _on_error(self, msg):
|
||||||
"""Display error message on download errors."""
|
"""Display error message on download errors."""
|
||||||
message.error(self._win_id, "Download error: {}".format(msg))
|
message.error("Download error: {}".format(msg))
|
||||||
|
|
||||||
def has_downloads_with_nam(self, nam):
|
def has_downloads_with_nam(self, nam):
|
||||||
"""Check if the DownloadManager has any downloads with the given QNAM.
|
"""Check if the DownloadManager has any downloads with the given QNAM.
|
||||||
|
@ -447,11 +447,10 @@ class _Downloader:
|
|||||||
with open(self.dest, 'wb') as file_output:
|
with open(self.dest, 'wb') as file_output:
|
||||||
self.writer.write_to(file_output)
|
self.writer.write_to(file_output)
|
||||||
except OSError as error:
|
except OSError as error:
|
||||||
message.error(self._win_id,
|
message.error("Could not save file: {}".format(error))
|
||||||
"Could not save file: {}".format(error))
|
|
||||||
return
|
return
|
||||||
log.downloads.debug("File successfully written.")
|
log.downloads.debug("File successfully written.")
|
||||||
message.info(self._win_id, "Page saved as {}".format(self.dest))
|
message.info("Page saved as {}".format(self.dest))
|
||||||
|
|
||||||
def _collect_zombies(self):
|
def _collect_zombies(self):
|
||||||
"""Collect done downloads and add their data to the MHTML file.
|
"""Collect done downloads and add their data to the MHTML file.
|
||||||
@ -528,8 +527,7 @@ def start_download_checked(dest, tab):
|
|||||||
# saving the file anyway.
|
# saving the file anyway.
|
||||||
if not os.path.isdir(os.path.dirname(path)):
|
if not os.path.isdir(os.path.dirname(path)):
|
||||||
folder = os.path.dirname(path)
|
folder = os.path.dirname(path)
|
||||||
message.error(tab.win_id,
|
message.error("Directory {} does not exist.".format(folder))
|
||||||
"Directory {} does not exist.".format(folder))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if not os.path.isfile(path):
|
if not os.path.isfile(path):
|
||||||
|
@ -299,8 +299,7 @@ class NetworkManager(QNetworkAccessManager):
|
|||||||
for err in errors:
|
for err in errors:
|
||||||
# FIXME we might want to use warn here (non-fatal error)
|
# FIXME we might want to use warn here (non-fatal error)
|
||||||
# https://github.com/The-Compiler/qutebrowser/issues/114
|
# https://github.com/The-Compiler/qutebrowser/issues/114
|
||||||
message.error(self._win_id, 'SSL error: {}'.format(
|
message.error('SSL error: {}'.format(err.errorString()))
|
||||||
err.errorString()))
|
|
||||||
reply.ignoreSslErrors()
|
reply.ignoreSslErrors()
|
||||||
self._accepted_ssl_errors[host_tpl] += errors
|
self._accepted_ssl_errors[host_tpl] += errors
|
||||||
|
|
||||||
|
@ -77,13 +77,13 @@ class JSBridge(QObject):
|
|||||||
# https://github.com/The-Compiler/qutebrowser/issues/727
|
# https://github.com/The-Compiler/qutebrowser/issues/727
|
||||||
if ((sectname, optname) == ('content', 'allow-javascript') and
|
if ((sectname, optname) == ('content', 'allow-javascript') and
|
||||||
value == 'false'):
|
value == 'false'):
|
||||||
message.error('current', "Refusing to disable javascript via "
|
message.error("Refusing to disable javascript via qute:settings "
|
||||||
"qute:settings as it needs javascript support.")
|
"as it needs javascript support.")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
objreg.get('config').set('conf', sectname, optname, value)
|
objreg.get('config').set('conf', sectname, optname, value)
|
||||||
except (configexc.Error, configparser.Error) as e:
|
except (configexc.Error, configparser.Error) as e:
|
||||||
message.error('current', e)
|
message.error(e)
|
||||||
|
|
||||||
|
|
||||||
@qutescheme.add_handler('settings', backend=usertypes.Backend.QtWebKit)
|
@qutescheme.add_handler('settings', backend=usertypes.Backend.QtWebKit)
|
||||||
|
@ -249,8 +249,8 @@ class BrowserPage(QWebPage):
|
|||||||
def on_print_requested(self, frame):
|
def on_print_requested(self, frame):
|
||||||
"""Handle printing when requested via javascript."""
|
"""Handle printing when requested via javascript."""
|
||||||
if not qtutils.check_print_compat():
|
if not qtutils.check_print_compat():
|
||||||
message.error(self._win_id, "Printing on Qt < 5.3.0 on Windows is "
|
message.error("Printing on Qt < 5.3.0 on Windows is broken, "
|
||||||
"broken, please upgrade!", immediately=True)
|
"please upgrade!")
|
||||||
return
|
return
|
||||||
printdiag = QPrintDialog()
|
printdiag = QPrintDialog()
|
||||||
printdiag.setAttribute(Qt.WA_DeleteOnClose)
|
printdiag.setAttribute(Qt.WA_DeleteOnClose)
|
||||||
|
@ -174,8 +174,8 @@ class Command:
|
|||||||
"backend.".format(self.name, self.backend.name))
|
"backend.".format(self.name, self.backend.name))
|
||||||
|
|
||||||
if self.deprecated:
|
if self.deprecated:
|
||||||
message.warning(win_id, '{} is deprecated - {}'.format(
|
message.warning('{} is deprecated - {}'.format(self.name,
|
||||||
self.name, self.deprecated))
|
self.deprecated))
|
||||||
|
|
||||||
def _check_func(self):
|
def _check_func(self):
|
||||||
"""Make sure the function parameters don't violate any rules."""
|
"""Make sure the function parameters don't violate any rules."""
|
||||||
@ -507,7 +507,7 @@ class Command:
|
|||||||
try:
|
try:
|
||||||
self.namespace = self.parser.parse_args(args)
|
self.namespace = self.parser.parse_args(args)
|
||||||
except argparser.ArgumentParserError as e:
|
except argparser.ArgumentParserError as e:
|
||||||
message.error(win_id, '{}: {}'.format(self.name, e),
|
message.error('{}: {}'.format(self.name, e),
|
||||||
stack=traceback.format_exc())
|
stack=traceback.format_exc())
|
||||||
return
|
return
|
||||||
except argparser.ArgumentParserExit as e:
|
except argparser.ArgumentParserExit as e:
|
||||||
|
@ -320,8 +320,7 @@ class CommandRunner(QObject):
|
|||||||
try:
|
try:
|
||||||
self.run(text, count)
|
self.run(text, count)
|
||||||
except (cmdexc.CommandMetaError, cmdexc.CommandError) as e:
|
except (cmdexc.CommandMetaError, cmdexc.CommandError) as e:
|
||||||
message.error(self._win_id, e, immediately=True,
|
message.error(e, stack=traceback.format_exc())
|
||||||
stack=traceback.format_exc())
|
|
||||||
|
|
||||||
@pyqtSlot(str, int)
|
@pyqtSlot(str, int)
|
||||||
def run_safely_init(self, text, count=None):
|
def run_safely_init(self, text, count=None):
|
||||||
@ -333,4 +332,4 @@ class CommandRunner(QObject):
|
|||||||
try:
|
try:
|
||||||
self.run(text, count)
|
self.run(text, count)
|
||||||
except (cmdexc.CommandMetaError, cmdexc.CommandError) as e:
|
except (cmdexc.CommandMetaError, cmdexc.CommandError) as e:
|
||||||
message.error(self._win_id, e, stack=traceback.format_exc())
|
message.error(e, stack=traceback.format_exc())
|
||||||
|
@ -84,7 +84,6 @@ class _BaseUserscriptRunner(QObject):
|
|||||||
Attributes:
|
Attributes:
|
||||||
_filepath: The path of the file/FIFO which is being read.
|
_filepath: The path of the file/FIFO which is being read.
|
||||||
_proc: The GUIProcess which is being executed.
|
_proc: The GUIProcess which is being executed.
|
||||||
_win_id: The window ID this runner is associated with.
|
|
||||||
_cleaned_up: Whether temporary files were cleaned up.
|
_cleaned_up: Whether temporary files were cleaned up.
|
||||||
_text_stored: Set when the page text was stored async.
|
_text_stored: Set when the page text was stored async.
|
||||||
_html_stored: Set when the page html was stored async.
|
_html_stored: Set when the page html was stored async.
|
||||||
@ -99,10 +98,9 @@ class _BaseUserscriptRunner(QObject):
|
|||||||
got_cmd = pyqtSignal(str)
|
got_cmd = pyqtSignal(str)
|
||||||
finished = pyqtSignal()
|
finished = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, win_id, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._cleaned_up = False
|
self._cleaned_up = False
|
||||||
self._win_id = win_id
|
|
||||||
self._filepath = None
|
self._filepath = None
|
||||||
self._proc = None
|
self._proc = None
|
||||||
self._env = {}
|
self._env = {}
|
||||||
@ -151,7 +149,7 @@ class _BaseUserscriptRunner(QObject):
|
|||||||
self._env['QUTE_FIFO'] = self._filepath
|
self._env['QUTE_FIFO'] = self._filepath
|
||||||
if env is not None:
|
if env is not None:
|
||||||
self._env.update(env)
|
self._env.update(env)
|
||||||
self._proc = guiprocess.GUIProcess(self._win_id, 'userscript',
|
self._proc = guiprocess.GUIProcess('userscript',
|
||||||
additional_env=self._env,
|
additional_env=self._env,
|
||||||
verbose=verbose, parent=self)
|
verbose=verbose, parent=self)
|
||||||
self._proc.finished.connect(self.on_proc_finished)
|
self._proc.finished.connect(self.on_proc_finished)
|
||||||
@ -175,8 +173,7 @@ class _BaseUserscriptRunner(QObject):
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
# NOTE: Do not replace this with "raise CommandError" as it's
|
# NOTE: Do not replace this with "raise CommandError" as it's
|
||||||
# executed async.
|
# executed async.
|
||||||
message.error(
|
message.error("Failed to delete tempfile {} ({})!".format(
|
||||||
self._win_id, "Failed to delete tempfile {} ({})!".format(
|
|
||||||
fn, e))
|
fn, e))
|
||||||
self._filepath = None
|
self._filepath = None
|
||||||
self._proc = None
|
self._proc = None
|
||||||
@ -223,8 +220,8 @@ class _POSIXUserscriptRunner(_BaseUserscriptRunner):
|
|||||||
_reader: The _QtFIFOReader instance.
|
_reader: The _QtFIFOReader instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, win_id, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(win_id, parent)
|
super().__init__(parent)
|
||||||
self._reader = None
|
self._reader = None
|
||||||
|
|
||||||
def prepare_run(self, *args, **kwargs):
|
def prepare_run(self, *args, **kwargs):
|
||||||
@ -242,8 +239,7 @@ class _POSIXUserscriptRunner(_BaseUserscriptRunner):
|
|||||||
# pylint: disable=no-member,useless-suppression
|
# pylint: disable=no-member,useless-suppression
|
||||||
os.mkfifo(self._filepath)
|
os.mkfifo(self._filepath)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
message.error(self._win_id,
|
message.error("Error while creating FIFO: {}".format(e))
|
||||||
"Error while creating FIFO: {}".format(e))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
self._reader = _QtFIFOReader(self._filepath)
|
self._reader = _QtFIFOReader(self._filepath)
|
||||||
@ -315,8 +311,7 @@ class _WindowsUserscriptRunner(_BaseUserscriptRunner):
|
|||||||
handle.close()
|
handle.close()
|
||||||
self._filepath = handle.name
|
self._filepath = handle.name
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
message.error(self._win_id, "Error while creating tempfile: "
|
message.error("Error while creating tempfile: {}".format(e))
|
||||||
"{}".format(e))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@ -348,9 +343,9 @@ def run_async(tab, cmd, *args, win_id, env, verbose=False):
|
|||||||
commandrunner = runners.CommandRunner(win_id, parent=tabbed_browser)
|
commandrunner = runners.CommandRunner(win_id, parent=tabbed_browser)
|
||||||
|
|
||||||
if os.name == 'posix':
|
if os.name == 'posix':
|
||||||
runner = _POSIXUserscriptRunner(win_id, tabbed_browser)
|
runner = _POSIXUserscriptRunner(tabbed_browser)
|
||||||
elif os.name == 'nt': # pragma: no cover
|
elif os.name == 'nt': # pragma: no cover
|
||||||
runner = _WindowsUserscriptRunner(win_id, tabbed_browser)
|
runner = _WindowsUserscriptRunner(tabbed_browser)
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
raise UnsupportedError
|
raise UnsupportedError
|
||||||
|
|
||||||
|
@ -816,8 +816,7 @@ class ConfigManager(QObject):
|
|||||||
if print_:
|
if print_:
|
||||||
with self._handle_config_error():
|
with self._handle_config_error():
|
||||||
val = self.get(section_, option, transformed=False)
|
val = self.get(section_, option, transformed=False)
|
||||||
message.info(win_id, "{} {} = {}".format(
|
message.info("{} {} = {}".format(section_, option, val))
|
||||||
section_, option, val), immediately=True)
|
|
||||||
|
|
||||||
def set(self, layer, sectname, optname, value, validate=True):
|
def set(self, layer, sectname, optname, value, validate=True):
|
||||||
"""Set an option.
|
"""Set an option.
|
||||||
|
@ -1069,22 +1069,6 @@ def data(readonly=False):
|
|||||||
SettingValue(typ.QssColor(), 'black'),
|
SettingValue(typ.QssColor(), 'black'),
|
||||||
"Background color of the statusbar."),
|
"Background color of the statusbar."),
|
||||||
|
|
||||||
('statusbar.fg.error',
|
|
||||||
SettingValue(typ.QssColor(), '${statusbar.fg}'),
|
|
||||||
"Foreground color of the statusbar if there was an error."),
|
|
||||||
|
|
||||||
('statusbar.bg.error',
|
|
||||||
SettingValue(typ.QssColor(), 'red'),
|
|
||||||
"Background color of the statusbar if there was an error."),
|
|
||||||
|
|
||||||
('statusbar.fg.warning',
|
|
||||||
SettingValue(typ.QssColor(), '${statusbar.fg}'),
|
|
||||||
"Foreground color of the statusbar if there is a warning."),
|
|
||||||
|
|
||||||
('statusbar.bg.warning',
|
|
||||||
SettingValue(typ.QssColor(), 'darkorange'),
|
|
||||||
"Background color of the statusbar if there is a warning."),
|
|
||||||
|
|
||||||
('statusbar.fg.prompt',
|
('statusbar.fg.prompt',
|
||||||
SettingValue(typ.QssColor(), '${statusbar.fg}'),
|
SettingValue(typ.QssColor(), '${statusbar.fg}'),
|
||||||
"Foreground color of the statusbar if there is a prompt."),
|
"Foreground color of the statusbar if there is a prompt."),
|
||||||
@ -1280,6 +1264,42 @@ def data(readonly=False):
|
|||||||
SettingValue(typ.QssColor(), 'rgba(0, 0, 0, 80%)'),
|
SettingValue(typ.QssColor(), 'rgba(0, 0, 0, 80%)'),
|
||||||
"Background color of the keyhint widget."),
|
"Background color of the keyhint widget."),
|
||||||
|
|
||||||
|
('messages.fg.error',
|
||||||
|
SettingValue(typ.QssColor(), 'white'),
|
||||||
|
"Foreground color of an error message."),
|
||||||
|
|
||||||
|
('messages.bg.error',
|
||||||
|
SettingValue(typ.QssColor(), 'red'),
|
||||||
|
"Background color of an error message."),
|
||||||
|
|
||||||
|
('messages.border.error',
|
||||||
|
SettingValue(typ.QssColor(), '#bb0000'),
|
||||||
|
"Border color of an error message."),
|
||||||
|
|
||||||
|
('messages.fg.warning',
|
||||||
|
SettingValue(typ.QssColor(), 'white'),
|
||||||
|
"Foreground color a warning message."),
|
||||||
|
|
||||||
|
('messages.bg.warning',
|
||||||
|
SettingValue(typ.QssColor(), 'darkorange'),
|
||||||
|
"Background color of a warning message."),
|
||||||
|
|
||||||
|
('messages.border.warning',
|
||||||
|
SettingValue(typ.QssColor(), '#d47300'),
|
||||||
|
"Border color of an error message."),
|
||||||
|
|
||||||
|
('messages.fg.info',
|
||||||
|
SettingValue(typ.QssColor(), 'white'),
|
||||||
|
"Foreground color an info message."),
|
||||||
|
|
||||||
|
('messages.bg.info',
|
||||||
|
SettingValue(typ.QssColor(), 'black'),
|
||||||
|
"Background color of an info message."),
|
||||||
|
|
||||||
|
('messages.border.info',
|
||||||
|
SettingValue(typ.QssColor(), '#333333'),
|
||||||
|
"Border color of an info message."),
|
||||||
|
|
||||||
readonly=readonly
|
readonly=readonly
|
||||||
)),
|
)),
|
||||||
|
|
||||||
@ -1369,6 +1389,18 @@ def data(readonly=False):
|
|||||||
SettingValue(typ.Font(), DEFAULT_FONT_SIZE + ' ${_monospace}'),
|
SettingValue(typ.Font(), DEFAULT_FONT_SIZE + ' ${_monospace}'),
|
||||||
"Font used in the keyhint widget."),
|
"Font used in the keyhint widget."),
|
||||||
|
|
||||||
|
('messages.error',
|
||||||
|
SettingValue(typ.Font(), DEFAULT_FONT_SIZE + ' ${_monospace}'),
|
||||||
|
"Font used for error messages."),
|
||||||
|
|
||||||
|
('messages.warning',
|
||||||
|
SettingValue(typ.Font(), DEFAULT_FONT_SIZE + ' ${_monospace}'),
|
||||||
|
"Font used for warning messages."),
|
||||||
|
|
||||||
|
('messages.info',
|
||||||
|
SettingValue(typ.Font(), DEFAULT_FONT_SIZE + ' ${_monospace}'),
|
||||||
|
"Font used for info messages."),
|
||||||
|
|
||||||
readonly=readonly
|
readonly=readonly
|
||||||
)),
|
)),
|
||||||
])
|
])
|
||||||
|
@ -152,9 +152,8 @@ class KeyConfigParser(QObject):
|
|||||||
|
|
||||||
@cmdutils.register(instance='key-config', maxsplit=1, no_cmd_split=True,
|
@cmdutils.register(instance='key-config', maxsplit=1, no_cmd_split=True,
|
||||||
no_replace_variables=True)
|
no_replace_variables=True)
|
||||||
@cmdutils.argument('win_id', win_id=True)
|
|
||||||
@cmdutils.argument('command', completion=usertypes.Completion.bind)
|
@cmdutils.argument('command', completion=usertypes.Completion.bind)
|
||||||
def bind(self, key, win_id, command=None, *, mode='normal', force=False):
|
def bind(self, key, command=None, *, mode='normal', force=False):
|
||||||
"""Bind a key to a command.
|
"""Bind a key to a command.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -172,11 +171,10 @@ class KeyConfigParser(QObject):
|
|||||||
if command is None:
|
if command is None:
|
||||||
cmd = self.get_bindings_for(mode).get(key, None)
|
cmd = self.get_bindings_for(mode).get(key, None)
|
||||||
if cmd is None:
|
if cmd is None:
|
||||||
message.info(win_id, "{} is unbound in {} mode".format(
|
message.info("{} is unbound in {} mode".format(key, mode))
|
||||||
key, mode))
|
|
||||||
else:
|
else:
|
||||||
message.info(win_id, "{} is bound to '{}' in {} mode".format(
|
message.info("{} is bound to '{}' in {} mode".format(key, cmd,
|
||||||
key, cmd, mode))
|
mode))
|
||||||
return
|
return
|
||||||
|
|
||||||
mode = self._normalize_sectname(mode)
|
mode = self._normalize_sectname(mode)
|
||||||
|
@ -43,8 +43,7 @@ class CommandKeyParser(BaseKeyParser):
|
|||||||
try:
|
try:
|
||||||
self._commandrunner.run(cmdstr, count)
|
self._commandrunner.run(cmdstr, count)
|
||||||
except (cmdexc.CommandMetaError, cmdexc.CommandError) as e:
|
except (cmdexc.CommandMetaError, cmdexc.CommandError) as e:
|
||||||
message.error(self._win_id, e, immediately=True,
|
message.error(str(e), stack=traceback.format_exc())
|
||||||
stack=traceback.format_exc())
|
|
||||||
|
|
||||||
|
|
||||||
class PassthroughKeyParser(CommandKeyParser):
|
class PassthroughKeyParser(CommandKeyParser):
|
||||||
|
@ -30,7 +30,7 @@ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication
|
|||||||
from qutebrowser.commands import runners, cmdutils
|
from qutebrowser.commands import runners, cmdutils
|
||||||
from qutebrowser.config import config
|
from qutebrowser.config import config
|
||||||
from qutebrowser.utils import message, log, usertypes, qtutils, objreg, utils
|
from qutebrowser.utils import message, log, usertypes, qtutils, objreg, utils
|
||||||
from qutebrowser.mainwindow import tabbedbrowser
|
from qutebrowser.mainwindow import tabbedbrowser, messageview
|
||||||
from qutebrowser.mainwindow.statusbar import bar
|
from qutebrowser.mainwindow.statusbar import bar
|
||||||
from qutebrowser.completion import completionwidget, completer
|
from qutebrowser.completion import completionwidget, completer
|
||||||
from qutebrowser.keyinput import modeman
|
from qutebrowser.keyinput import modeman
|
||||||
@ -177,6 +177,7 @@ class MainWindow(QWidget):
|
|||||||
partial_match=True)
|
partial_match=True)
|
||||||
|
|
||||||
self._keyhint = keyhintwidget.KeyHintView(self.win_id, self)
|
self._keyhint = keyhintwidget.KeyHintView(self.win_id, self)
|
||||||
|
self._messageview = messageview.MessageView(parent=self)
|
||||||
|
|
||||||
log.init.debug("Initializing modes...")
|
log.init.debug("Initializing modes...")
|
||||||
modeman.init(self.win_id, self)
|
modeman.init(self.win_id, self)
|
||||||
@ -195,8 +196,7 @@ class MainWindow(QWidget):
|
|||||||
# When we're here the statusbar might not even really exist yet, so
|
# When we're here the statusbar might not even really exist yet, so
|
||||||
# resizing will fail. Therefore, we use singleShot QTimers to make sure
|
# resizing will fail. Therefore, we use singleShot QTimers to make sure
|
||||||
# we defer this until everything else is initialized.
|
# we defer this until everything else is initialized.
|
||||||
QTimer.singleShot(0, self._connect_resize_completion)
|
QTimer.singleShot(0, self._connect_resize_signals)
|
||||||
QTimer.singleShot(0, self._connect_resize_keyhint)
|
|
||||||
objreg.get('config').changed.connect(self.on_config_changed)
|
objreg.get('config').changed.connect(self.on_config_changed)
|
||||||
|
|
||||||
objreg.get("app").new_window.emit(self)
|
objreg.get("app").new_window.emit(self)
|
||||||
@ -303,15 +303,14 @@ class MainWindow(QWidget):
|
|||||||
log.init.warning("Error while loading geometry.")
|
log.init.warning("Error while loading geometry.")
|
||||||
self._set_default_geometry()
|
self._set_default_geometry()
|
||||||
|
|
||||||
def _connect_resize_completion(self):
|
def _connect_resize_signals(self):
|
||||||
"""Connect the resize_completion signal and resize it once."""
|
"""Connect the resize signal and resize everything once."""
|
||||||
self._completion.resize_completion.connect(self.resize_completion)
|
self._completion.resize_completion.connect(self.resize_completion)
|
||||||
self.resize_completion()
|
|
||||||
|
|
||||||
def _connect_resize_keyhint(self):
|
|
||||||
"""Connect the reposition_keyhint signal and resize it once."""
|
|
||||||
self._keyhint.reposition_keyhint.connect(self.reposition_keyhint)
|
self._keyhint.reposition_keyhint.connect(self.reposition_keyhint)
|
||||||
|
self._messageview.reposition.connect(self._reposition_messageview)
|
||||||
|
self.resize_completion()
|
||||||
self.reposition_keyhint()
|
self.reposition_keyhint()
|
||||||
|
self._reposition_messageview()
|
||||||
|
|
||||||
def _set_default_geometry(self):
|
def _set_default_geometry(self):
|
||||||
"""Set some sensible default geometry."""
|
"""Set some sensible default geometry."""
|
||||||
@ -360,9 +359,9 @@ class MainWindow(QWidget):
|
|||||||
key_config.changed.connect(obj.on_keyconfig_changed)
|
key_config.changed.connect(obj.on_keyconfig_changed)
|
||||||
|
|
||||||
# messages
|
# messages
|
||||||
message_bridge.s_error.connect(status.disp_error)
|
message.global_bridge.show_message.connect(
|
||||||
message_bridge.s_warning.connect(status.disp_warning)
|
self._messageview.show_message)
|
||||||
message_bridge.s_info.connect(status.disp_temp_text)
|
|
||||||
message_bridge.s_set_text.connect(status.set_text)
|
message_bridge.s_set_text.connect(status.set_text)
|
||||||
message_bridge.s_maybe_reset_text.connect(status.txt.maybe_reset_text)
|
message_bridge.s_maybe_reset_text.connect(status.txt.maybe_reset_text)
|
||||||
message_bridge.s_set_cmd_text.connect(cmd.set_cmd_text)
|
message_bridge.s_set_cmd_text.connect(cmd.set_cmd_text)
|
||||||
@ -391,6 +390,23 @@ class MainWindow(QWidget):
|
|||||||
completion_obj.on_clear_completion_selection)
|
completion_obj.on_clear_completion_selection)
|
||||||
cmd.hide_completion.connect(completion_obj.hide)
|
cmd.hide_completion.connect(completion_obj.hide)
|
||||||
|
|
||||||
|
def _get_overlay_position(self, height):
|
||||||
|
"""Get the position for a full-width overlay with the given height."""
|
||||||
|
status_position = config.get('ui', 'status-position')
|
||||||
|
if status_position == 'bottom':
|
||||||
|
top = self.height() - self.status.height() - height
|
||||||
|
top = qtutils.check_overflow(top, 'int', fatal=False)
|
||||||
|
topleft = QPoint(0, top)
|
||||||
|
bottomright = self.status.geometry().topRight()
|
||||||
|
elif status_position == 'top':
|
||||||
|
topleft = self.status.geometry().bottomLeft()
|
||||||
|
bottom = self.status.height() + height
|
||||||
|
bottom = qtutils.check_overflow(bottom, 'int', fatal=False)
|
||||||
|
bottomright = QPoint(self.width(), bottom)
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid position {}!".format(status_position))
|
||||||
|
return QRect(topleft, bottomright)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def resize_completion(self):
|
def resize_completion(self):
|
||||||
"""Adjust completion according to config."""
|
"""Adjust completion according to config."""
|
||||||
@ -414,20 +430,7 @@ class MainWindow(QWidget):
|
|||||||
height = contents_height
|
height = contents_height
|
||||||
else:
|
else:
|
||||||
contents_height = -1
|
contents_height = -1
|
||||||
status_position = config.get('ui', 'status-position')
|
rect = self._get_overlay_position(height)
|
||||||
if status_position == 'bottom':
|
|
||||||
top = self.height() - self.status.height() - height
|
|
||||||
top = qtutils.check_overflow(top, 'int', fatal=False)
|
|
||||||
topleft = QPoint(0, top)
|
|
||||||
bottomright = self.status.geometry().topRight()
|
|
||||||
elif status_position == 'top':
|
|
||||||
topleft = self.status.geometry().bottomLeft()
|
|
||||||
bottom = self.status.height() + height
|
|
||||||
bottom = qtutils.check_overflow(bottom, 'int', fatal=False)
|
|
||||||
bottomright = QPoint(self.width(), bottom)
|
|
||||||
else:
|
|
||||||
raise ValueError("Invalid position {}!".format(status_position))
|
|
||||||
rect = QRect(topleft, bottomright)
|
|
||||||
log.misc.debug('completion rect: {}'.format(rect))
|
log.misc.debug('completion rect: {}'.format(rect))
|
||||||
if rect.isValid():
|
if rect.isValid():
|
||||||
self._completion.setGeometry(rect)
|
self._completion.setGeometry(rect)
|
||||||
@ -450,6 +453,17 @@ class MainWindow(QWidget):
|
|||||||
if rect.isValid():
|
if rect.isValid():
|
||||||
self._keyhint.setGeometry(rect)
|
self._keyhint.setGeometry(rect)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _reposition_messageview(self):
|
||||||
|
"""Position the message view correctly."""
|
||||||
|
if not self._messageview.isVisible():
|
||||||
|
return
|
||||||
|
height = self._messageview.message_height()
|
||||||
|
rect = self._get_overlay_position(height)
|
||||||
|
log.misc.debug('messageview rect: {}'.format(rect))
|
||||||
|
if rect.isValid():
|
||||||
|
self._messageview.setGeometry(rect)
|
||||||
|
|
||||||
@cmdutils.register(instance='main-window', scope='window')
|
@cmdutils.register(instance='main-window', scope='window')
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def close(self):
|
def close(self):
|
||||||
|
118
qutebrowser/mainwindow/messageview.py
Normal file
118
qutebrowser/mainwindow/messageview.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
|
||||||
|
# Copyright 2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||||
|
#
|
||||||
|
# This file is part of qutebrowser.
|
||||||
|
#
|
||||||
|
# qutebrowser is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# qutebrowser is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Showing messages above the statusbar."""
|
||||||
|
|
||||||
|
|
||||||
|
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QTimer, Qt
|
||||||
|
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
|
||||||
|
|
||||||
|
from qutebrowser.config import config, style
|
||||||
|
from qutebrowser.utils import usertypes, objreg
|
||||||
|
|
||||||
|
|
||||||
|
class Message(QLabel):
|
||||||
|
|
||||||
|
"""A single error/warning/info message."""
|
||||||
|
|
||||||
|
def __init__(self, level, text, parent=None):
|
||||||
|
super().__init__(text, parent)
|
||||||
|
self.setAttribute(Qt.WA_StyledBackground, True)
|
||||||
|
stylesheet = """
|
||||||
|
padding-top: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
"""
|
||||||
|
if level == usertypes.MessageLevel.error:
|
||||||
|
stylesheet += """
|
||||||
|
background-color: {{ color['messages.bg.error'] }};
|
||||||
|
color: {{ color['messages.fg.error'] }};
|
||||||
|
font: {{ font['messages.error'] }};
|
||||||
|
border-bottom: 1px solid {{ color['messages.border.error'] }};
|
||||||
|
"""
|
||||||
|
elif level == usertypes.MessageLevel.warning:
|
||||||
|
stylesheet += """
|
||||||
|
background-color: {{ color['messages.bg.warning'] }};
|
||||||
|
color: {{ color['messages.fg.warning'] }};
|
||||||
|
font: {{ font['messages.warning'] }};
|
||||||
|
border-bottom:
|
||||||
|
1px solid {{ color['messages.border.warning'] }};
|
||||||
|
"""
|
||||||
|
elif level == usertypes.MessageLevel.info:
|
||||||
|
stylesheet += """
|
||||||
|
background-color: {{ color['messages.bg.info'] }};
|
||||||
|
color: {{ color['messages.fg.info'] }};
|
||||||
|
font: {{ font['messages.info'] }};
|
||||||
|
border-bottom: 1px solid {{ color['messages.border.info'] }}
|
||||||
|
"""
|
||||||
|
else: # pragma: no cover
|
||||||
|
raise ValueError("Invalid level {!r}".format(level))
|
||||||
|
# We don't bother with set_register_stylesheet here as it's short-lived
|
||||||
|
# anyways.
|
||||||
|
self.setStyleSheet(style.get_stylesheet(stylesheet))
|
||||||
|
|
||||||
|
|
||||||
|
class MessageView(QWidget):
|
||||||
|
|
||||||
|
reposition = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self._vbox = QVBoxLayout(self)
|
||||||
|
self._vbox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self._vbox.setSpacing(0)
|
||||||
|
|
||||||
|
self._clear_timer = QTimer()
|
||||||
|
self._clear_timer.timeout.connect(self._clear_messages)
|
||||||
|
self._set_clear_timer_interval()
|
||||||
|
objreg.get('config').changed.connect(self._set_clear_timer_interval)
|
||||||
|
|
||||||
|
self._last_text = None
|
||||||
|
self._messages = []
|
||||||
|
|
||||||
|
@config.change_filter('ui', 'message-timeout')
|
||||||
|
def _set_clear_timer_interval(self):
|
||||||
|
self._clear_timer.setInterval(config.get('ui', 'message-timeout'))
|
||||||
|
|
||||||
|
def message_height(self):
|
||||||
|
return sum(label.sizeHint().height() for label in self._messages)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def _clear_messages(self):
|
||||||
|
for widget in self._messages:
|
||||||
|
self._vbox.removeWidget(widget)
|
||||||
|
widget.hide()
|
||||||
|
widget.deleteLater()
|
||||||
|
self._messages = []
|
||||||
|
self._last_text = None
|
||||||
|
self.hide()
|
||||||
|
self._clear_timer.stop()
|
||||||
|
|
||||||
|
@pyqtSlot(usertypes.MessageLevel, str)
|
||||||
|
def show_message(self, level, text):
|
||||||
|
if text == self._last_text:
|
||||||
|
return
|
||||||
|
|
||||||
|
widget = Message(level, text, parent=self)
|
||||||
|
self._vbox.addWidget(widget)
|
||||||
|
widget.show()
|
||||||
|
self._clear_timer.start()
|
||||||
|
self._messages.append(widget)
|
||||||
|
self._last_text = text
|
||||||
|
self.show()
|
||||||
|
self.reposition.emit()
|
@ -33,9 +33,6 @@ from qutebrowser.mainwindow.statusbar import (command, progress, keystring,
|
|||||||
from qutebrowser.mainwindow.statusbar import text as textwidget
|
from qutebrowser.mainwindow.statusbar import text as textwidget
|
||||||
|
|
||||||
|
|
||||||
PreviousWidget = usertypes.enum('PreviousWidget', ['none', 'prompt',
|
|
||||||
'command'])
|
|
||||||
Severity = usertypes.enum('Severity', ['normal', 'warning', 'error'])
|
|
||||||
CaretMode = usertypes.enum('CaretMode', ['off', 'on', 'selection'])
|
CaretMode = usertypes.enum('CaretMode', ['off', 'on', 'selection'])
|
||||||
|
|
||||||
|
|
||||||
@ -52,22 +49,9 @@ class StatusBar(QWidget):
|
|||||||
cmd: The Command widget in the statusbar.
|
cmd: The Command widget in the statusbar.
|
||||||
_hbox: The main QHBoxLayout.
|
_hbox: The main QHBoxLayout.
|
||||||
_stack: The QStackedLayout with cmd/txt widgets.
|
_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 Timer displaying the error messages.
|
|
||||||
_stopwatch: A QTime for the last displayed message.
|
|
||||||
_timer_was_active: Whether the _text_pop_timer was active before hiding
|
|
||||||
the command widget.
|
|
||||||
_previous_widget: A PreviousWidget member - the widget which was
|
|
||||||
displayed when an error interrupted it.
|
|
||||||
_win_id: The window ID the statusbar is associated with.
|
_win_id: The window ID the statusbar is associated with.
|
||||||
|
|
||||||
Class attributes:
|
Class attributes:
|
||||||
_severity: The severity of the current message, a Severity member.
|
|
||||||
|
|
||||||
For some reason we need to have this as class attribute so
|
|
||||||
pyqtProperty works correctly.
|
|
||||||
|
|
||||||
_prompt_active: If we're currently in prompt-mode.
|
_prompt_active: If we're currently in prompt-mode.
|
||||||
|
|
||||||
For some reason we need to have this as class attribute
|
For some reason we need to have this as class attribute
|
||||||
@ -129,20 +113,6 @@ class StatusBar(QWidget):
|
|||||||
background-color: {{ color['statusbar.bg.caret-selection'] }};
|
background-color: {{ color['statusbar.bg.caret-selection'] }};
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget#StatusBar[severity="error"],
|
|
||||||
QWidget#StatusBar[severity="error"] QLabel,
|
|
||||||
QWidget#StatusBar[severity="error"] QLineEdit {
|
|
||||||
color: {{ color['statusbar.fg.error'] }};
|
|
||||||
background-color: {{ color['statusbar.bg.error'] }};
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget#StatusBar[severity="warning"],
|
|
||||||
QWidget#StatusBar[severity="warning"] QLabel,
|
|
||||||
QWidget#StatusBar[severity="warning"] QLineEdit {
|
|
||||||
color: {{ color['statusbar.fg.warning'] }};
|
|
||||||
background-color: {{ color['statusbar.bg.warning'] }};
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget#StatusBar[prompt_active="true"],
|
QWidget#StatusBar[prompt_active="true"],
|
||||||
QWidget#StatusBar[prompt_active="true"] QLabel,
|
QWidget#StatusBar[prompt_active="true"] QLabel,
|
||||||
QWidget#StatusBar[prompt_active="true"] QLineEdit {
|
QWidget#StatusBar[prompt_active="true"] QLineEdit {
|
||||||
@ -177,7 +147,6 @@ class StatusBar(QWidget):
|
|||||||
|
|
||||||
self._win_id = win_id
|
self._win_id = win_id
|
||||||
self._option = None
|
self._option = None
|
||||||
self._stopwatch = QTime()
|
|
||||||
|
|
||||||
self._hbox = QHBoxLayout(self)
|
self._hbox = QHBoxLayout(self)
|
||||||
self.set_hbox_padding()
|
self.set_hbox_padding()
|
||||||
@ -195,16 +164,9 @@ class StatusBar(QWidget):
|
|||||||
|
|
||||||
self.txt = textwidget.Text()
|
self.txt = textwidget.Text()
|
||||||
self._stack.addWidget(self.txt)
|
self._stack.addWidget(self.txt)
|
||||||
self._timer_was_active = False
|
|
||||||
self._text_queue = collections.deque()
|
|
||||||
self._text_pop_timer = usertypes.Timer(self, 'statusbar_text_pop')
|
|
||||||
self._text_pop_timer.timeout.connect(self._pop_text)
|
|
||||||
self.set_pop_timer_interval()
|
|
||||||
objreg.get('config').changed.connect(self.set_pop_timer_interval)
|
|
||||||
|
|
||||||
self.prompt = prompt.Prompt(win_id)
|
self.prompt = prompt.Prompt(win_id)
|
||||||
self._stack.addWidget(self.prompt)
|
self._stack.addWidget(self.prompt)
|
||||||
self._previous_widget = PreviousWidget.none
|
|
||||||
|
|
||||||
self.cmd.show_cmd.connect(self._show_cmd_widget)
|
self.cmd.show_cmd.connect(self._show_cmd_widget)
|
||||||
self.cmd.hide_cmd.connect(self._hide_cmd_widget)
|
self.cmd.hide_cmd.connect(self._hide_cmd_widget)
|
||||||
@ -252,40 +214,6 @@ class StatusBar(QWidget):
|
|||||||
padding = config.get('ui', 'statusbar-padding')
|
padding = config.get('ui', 'statusbar-padding')
|
||||||
self._hbox.setContentsMargins(padding.left, 0, padding.right, 0)
|
self._hbox.setContentsMargins(padding.left, 0, padding.right, 0)
|
||||||
|
|
||||||
@pyqtProperty(str)
|
|
||||||
def severity(self):
|
|
||||||
"""Getter for self.severity, so it can be used as Qt property.
|
|
||||||
|
|
||||||
Return:
|
|
||||||
The severity as a string (!)
|
|
||||||
"""
|
|
||||||
if self._severity is None:
|
|
||||||
return ""
|
|
||||||
else:
|
|
||||||
return self._severity.name
|
|
||||||
|
|
||||||
def _set_severity(self, severity):
|
|
||||||
"""Set the severity for the current message.
|
|
||||||
|
|
||||||
Re-set the stylesheet after setting the value, so everything gets
|
|
||||||
updated by Qt properly.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
severity: A Severity member.
|
|
||||||
"""
|
|
||||||
if self._severity == severity:
|
|
||||||
# This gets called a lot (e.g. if the completion selection was
|
|
||||||
# changed), and setStyleSheet is relatively expensive, so we ignore
|
|
||||||
# this if there's nothing to change.
|
|
||||||
return
|
|
||||||
log.statusbar.debug("Setting severity to {}".format(severity))
|
|
||||||
self._severity = severity
|
|
||||||
self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))
|
|
||||||
if severity != Severity.normal:
|
|
||||||
# If we got an error while command/prompt was shown, raise the text
|
|
||||||
# widget.
|
|
||||||
self._stack.setCurrentWidget(self.txt)
|
|
||||||
|
|
||||||
@pyqtProperty(bool)
|
@pyqtProperty(bool)
|
||||||
def prompt_active(self):
|
def prompt_active(self):
|
||||||
"""Getter for self.prompt_active, so it can be used as Qt property."""
|
"""Getter for self.prompt_active, so it can be used as Qt property."""
|
||||||
@ -349,51 +277,14 @@ class StatusBar(QWidget):
|
|||||||
text = "-- {} MODE --".format(mode.upper())
|
text = "-- {} MODE --".format(mode.upper())
|
||||||
self.txt.set_text(self.txt.Text.normal, text)
|
self.txt.set_text(self.txt.Text.normal, text)
|
||||||
|
|
||||||
def _pop_text(self):
|
|
||||||
"""Display a text in the statusbar and pop it from _text_queue."""
|
|
||||||
try:
|
|
||||||
severity, text = self._text_queue.popleft()
|
|
||||||
except IndexError:
|
|
||||||
self._set_severity(Severity.normal)
|
|
||||||
self.txt.set_text(self.txt.Text.temp, '')
|
|
||||||
self._text_pop_timer.stop()
|
|
||||||
# If a previous widget was interrupted by an error, restore it.
|
|
||||||
if self._previous_widget == PreviousWidget.prompt:
|
|
||||||
self._stack.setCurrentWidget(self.prompt)
|
|
||||||
elif self._previous_widget == PreviousWidget.command:
|
|
||||||
self._stack.setCurrentWidget(self.cmd)
|
|
||||||
elif self._previous_widget == PreviousWidget.none:
|
|
||||||
self.maybe_hide()
|
|
||||||
else:
|
|
||||||
raise AssertionError("Unknown _previous_widget!")
|
|
||||||
return
|
|
||||||
self.show()
|
|
||||||
log.statusbar.debug("Displaying message: {} (severity {})".format(
|
|
||||||
text, severity))
|
|
||||||
log.statusbar.debug("Remaining: {}".format(self._text_queue))
|
|
||||||
self._set_severity(severity)
|
|
||||||
self.txt.set_text(self.txt.Text.temp, text)
|
|
||||||
|
|
||||||
def _show_cmd_widget(self):
|
def _show_cmd_widget(self):
|
||||||
"""Show command widget instead of temporary text."""
|
"""Show command widget instead of temporary text."""
|
||||||
self._set_severity(Severity.normal)
|
|
||||||
self._previous_widget = PreviousWidget.command
|
|
||||||
if self._text_pop_timer.isActive():
|
|
||||||
self._timer_was_active = True
|
|
||||||
self._text_pop_timer.stop()
|
|
||||||
self._stack.setCurrentWidget(self.cmd)
|
self._stack.setCurrentWidget(self.cmd)
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
def _hide_cmd_widget(self):
|
def _hide_cmd_widget(self):
|
||||||
"""Show temporary text instead of command widget."""
|
"""Show temporary text instead of command widget."""
|
||||||
log.statusbar.debug("Hiding cmd widget, queue: {}".format(
|
log.statusbar.debug("Hiding cmd widget")
|
||||||
self._text_queue))
|
|
||||||
self._previous_widget = PreviousWidget.none
|
|
||||||
if self._timer_was_active:
|
|
||||||
# Restart the text pop timer if it was active before hiding.
|
|
||||||
self._pop_text()
|
|
||||||
self._text_pop_timer.start()
|
|
||||||
self._timer_was_active = False
|
|
||||||
self._stack.setCurrentWidget(self.txt)
|
self._stack.setCurrentWidget(self.txt)
|
||||||
self.maybe_hide()
|
self.maybe_hide()
|
||||||
|
|
||||||
@ -401,112 +292,17 @@ class StatusBar(QWidget):
|
|||||||
"""Show prompt widget instead of temporary text."""
|
"""Show prompt widget instead of temporary text."""
|
||||||
if self._stack.currentWidget() is self.prompt:
|
if self._stack.currentWidget() is self.prompt:
|
||||||
return
|
return
|
||||||
self._set_severity(Severity.normal)
|
|
||||||
self._set_prompt_active(True)
|
self._set_prompt_active(True)
|
||||||
self._previous_widget = PreviousWidget.prompt
|
|
||||||
if self._text_pop_timer.isActive():
|
|
||||||
self._timer_was_active = True
|
|
||||||
self._text_pop_timer.stop()
|
|
||||||
self._stack.setCurrentWidget(self.prompt)
|
self._stack.setCurrentWidget(self.prompt)
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
def _hide_prompt_widget(self):
|
def _hide_prompt_widget(self):
|
||||||
"""Show temporary text instead of prompt widget."""
|
"""Show temporary text instead of prompt widget."""
|
||||||
self._set_prompt_active(False)
|
self._set_prompt_active(False)
|
||||||
self._previous_widget = PreviousWidget.none
|
log.statusbar.debug("Hiding prompt widget")
|
||||||
log.statusbar.debug("Hiding prompt widget, queue: {}".format(
|
|
||||||
self._text_queue))
|
|
||||||
if self._timer_was_active:
|
|
||||||
# Restart the text pop timer if it was active before hiding.
|
|
||||||
self._pop_text()
|
|
||||||
self._text_pop_timer.start()
|
|
||||||
self._timer_was_active = False
|
|
||||||
self._stack.setCurrentWidget(self.txt)
|
self._stack.setCurrentWidget(self.txt)
|
||||||
self.maybe_hide()
|
self.maybe_hide()
|
||||||
|
|
||||||
def _disp_text(self, text, severity, immediately=False):
|
|
||||||
"""Inner logic for disp_error and disp_temp_text.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: The message to display.
|
|
||||||
severity: The severity of the messages.
|
|
||||||
immediately: If set, message gets displayed immediately instead of
|
|
||||||
queued.
|
|
||||||
"""
|
|
||||||
log.statusbar.debug("Displaying text: {} (severity={})".format(
|
|
||||||
text, severity))
|
|
||||||
mindelta = config.get('ui', 'message-timeout')
|
|
||||||
if self._stopwatch.isNull():
|
|
||||||
delta = None
|
|
||||||
self._stopwatch.start()
|
|
||||||
else:
|
|
||||||
delta = self._stopwatch.restart()
|
|
||||||
log.statusbar.debug("queue: {} / delta: {}".format(
|
|
||||||
self._text_queue, delta))
|
|
||||||
if not self._text_queue and (delta is None or delta > mindelta):
|
|
||||||
# If the queue is empty and we didn't print messages for long
|
|
||||||
# enough, we can take the short route and display the message
|
|
||||||
# immediately. We then start the pop_timer only to restore the
|
|
||||||
# normal state in 2 seconds.
|
|
||||||
log.statusbar.debug("Displaying immediately")
|
|
||||||
self._set_severity(severity)
|
|
||||||
self.show()
|
|
||||||
self.txt.set_text(self.txt.Text.temp, text)
|
|
||||||
self._text_pop_timer.start()
|
|
||||||
elif self._text_queue and self._text_queue[-1] == (severity, text):
|
|
||||||
# If we get the same message multiple times in a row and we're
|
|
||||||
# still displaying it *anyways* we ignore the new one
|
|
||||||
log.statusbar.debug("ignoring")
|
|
||||||
elif immediately:
|
|
||||||
# This message is a reaction to a keypress and should be displayed
|
|
||||||
# immediately, temporarily interrupting the message queue.
|
|
||||||
# We display this immediately and restart the timer.to clear it and
|
|
||||||
# display the rest of the queue later.
|
|
||||||
log.statusbar.debug("Moving to beginning of queue")
|
|
||||||
self._set_severity(severity)
|
|
||||||
self.show()
|
|
||||||
self.txt.set_text(self.txt.Text.temp, text)
|
|
||||||
self._text_pop_timer.start()
|
|
||||||
else:
|
|
||||||
# There are still some messages to be displayed, so we queue this
|
|
||||||
# up.
|
|
||||||
log.statusbar.debug("queueing")
|
|
||||||
self._text_queue.append((severity, text))
|
|
||||||
self._text_pop_timer.start()
|
|
||||||
|
|
||||||
@pyqtSlot(str, bool)
|
|
||||||
def disp_error(self, text, immediately=False):
|
|
||||||
"""Display an error in the statusbar.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: The message to display.
|
|
||||||
immediately: If set, message gets displayed immediately instead of
|
|
||||||
queued.
|
|
||||||
"""
|
|
||||||
self._disp_text(text, Severity.error, immediately)
|
|
||||||
|
|
||||||
@pyqtSlot(str, bool)
|
|
||||||
def disp_warning(self, text, immediately=False):
|
|
||||||
"""Display a warning in the statusbar.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: The message to display.
|
|
||||||
immediately: If set, message gets displayed immediately instead of
|
|
||||||
queued.
|
|
||||||
"""
|
|
||||||
self._disp_text(text, Severity.warning, immediately)
|
|
||||||
|
|
||||||
@pyqtSlot(str, bool)
|
|
||||||
def disp_temp_text(self, text, immediately):
|
|
||||||
"""Display a temporary text in the statusbar.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: The message to display.
|
|
||||||
immediately: If set, message gets displayed immediately instead of
|
|
||||||
queued.
|
|
||||||
"""
|
|
||||||
self._disp_text(text, Severity.normal, immediately)
|
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def set_text(self, val):
|
def set_text(self, val):
|
||||||
"""Set a normal (persistent) text in the status bar."""
|
"""Set a normal (persistent) text in the status bar."""
|
||||||
@ -539,11 +335,6 @@ class StatusBar(QWidget):
|
|||||||
usertypes.KeyMode.caret]:
|
usertypes.KeyMode.caret]:
|
||||||
self.set_mode_active(old_mode, False)
|
self.set_mode_active(old_mode, False)
|
||||||
|
|
||||||
@config.change_filter('ui', 'message-timeout')
|
|
||||||
def set_pop_timer_interval(self):
|
|
||||||
"""Update message timeout when config changed."""
|
|
||||||
self._text_pop_timer.setInterval(config.get('ui', 'message-timeout'))
|
|
||||||
|
|
||||||
def resizeEvent(self, e):
|
def resizeEvent(self, e):
|
||||||
"""Extend resizeEvent of QWidget to emit a resized signal afterwards.
|
"""Extend resizeEvent of QWidget to emit a resized signal afterwards.
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
# We display a warnings for URLs which are not empty but invalid -
|
# We display a warnings for URLs which are not empty but invalid -
|
||||||
# but we don't return here because we want the tab to close either
|
# but we don't return here because we want the tab to close either
|
||||||
# way.
|
# way.
|
||||||
urlutils.invalid_url_error(self._win_id, tab.url(), "saving tab")
|
urlutils.invalid_url_error(tab.url(), "saving tab")
|
||||||
tab.shutdown()
|
tab.shutdown()
|
||||||
self.removeTab(idx)
|
self.removeTab(idx)
|
||||||
tab.deleteLater()
|
tab.deleteLater()
|
||||||
@ -650,7 +650,7 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
except qtutils.QtValueError:
|
except qtutils.QtValueError:
|
||||||
# show an error only if the mark is not automatically set
|
# show an error only if the mark is not automatically set
|
||||||
if key != "'":
|
if key != "'":
|
||||||
message.error(self._win_id, "Failed to set mark: url invalid")
|
message.error("Failed to set mark: url invalid")
|
||||||
return
|
return
|
||||||
point = self.currentWidget().scroller.pos_px()
|
point = self.currentWidget().scroller.pos_px()
|
||||||
|
|
||||||
@ -687,9 +687,9 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
self.openurl(url, newtab=False)
|
self.openurl(url, newtab=False)
|
||||||
self.cur_load_finished.connect(callback)
|
self.cur_load_finished.connect(callback)
|
||||||
else:
|
else:
|
||||||
message.error(self._win_id, "Mark {} is not set".format(key))
|
message.error("Mark {} is not set".format(key))
|
||||||
elif urlkey is None:
|
elif urlkey is None:
|
||||||
message.error(self._win_id, "Current URL is invalid!")
|
message.error("Current URL is invalid!")
|
||||||
elif urlkey in self._local_marks and key in self._local_marks[urlkey]:
|
elif urlkey in self._local_marks and key in self._local_marks[urlkey]:
|
||||||
point = self._local_marks[urlkey][key]
|
point = self._local_marks[urlkey][key]
|
||||||
|
|
||||||
@ -700,4 +700,4 @@ class TabbedBrowser(tabwidget.TabWidget):
|
|||||||
|
|
||||||
tab.scroller.to_point(point)
|
tab.scroller.to_point(point)
|
||||||
else:
|
else:
|
||||||
message.error(self._win_id, "Mark {} is not set".format(key))
|
message.error("Mark {} is not set".format(key))
|
||||||
|
@ -38,17 +38,15 @@ class ExternalEditor(QObject):
|
|||||||
_file: The file handle as tempfile.NamedTemporaryFile. Note that this
|
_file: The file handle as tempfile.NamedTemporaryFile. Note that this
|
||||||
handle will be closed after the initial file has been created.
|
handle will be closed after the initial file has been created.
|
||||||
_proc: The GUIProcess of the editor.
|
_proc: The GUIProcess of the editor.
|
||||||
_win_id: The window ID the ExternalEditor is associated with.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
editing_finished = pyqtSignal(str)
|
editing_finished = pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, win_id, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._text = None
|
self._text = None
|
||||||
self._file = None
|
self._file = None
|
||||||
self._proc = None
|
self._proc = None
|
||||||
self._win_id = win_id
|
|
||||||
|
|
||||||
def _cleanup(self):
|
def _cleanup(self):
|
||||||
"""Clean up temporary files after the editor closed."""
|
"""Clean up temporary files after the editor closed."""
|
||||||
@ -61,8 +59,7 @@ class ExternalEditor(QObject):
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
# NOTE: Do not replace this with "raise CommandError" as it's
|
# NOTE: Do not replace this with "raise CommandError" as it's
|
||||||
# executed async.
|
# executed async.
|
||||||
message.error(self._win_id,
|
message.error("Failed to delete tempfile... ({})".format(e))
|
||||||
"Failed to delete tempfile... ({})".format(e))
|
|
||||||
|
|
||||||
@pyqtSlot(int, QProcess.ExitStatus)
|
@pyqtSlot(int, QProcess.ExitStatus)
|
||||||
def on_proc_closed(self, exitcode, exitstatus):
|
def on_proc_closed(self, exitcode, exitstatus):
|
||||||
@ -85,8 +82,7 @@ class ExternalEditor(QObject):
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
# NOTE: Do not replace this with "raise CommandError" as it's
|
# NOTE: Do not replace this with "raise CommandError" as it's
|
||||||
# executed async.
|
# executed async.
|
||||||
message.error(self._win_id, "Failed to read back edited file: "
|
message.error("Failed to read back edited file: {}".format(e))
|
||||||
"{}".format(e))
|
|
||||||
return
|
return
|
||||||
log.procs.debug("Read back: {}".format(text))
|
log.procs.debug("Read back: {}".format(text))
|
||||||
self.editing_finished.emit(text)
|
self.editing_finished.emit(text)
|
||||||
@ -119,11 +115,9 @@ class ExternalEditor(QObject):
|
|||||||
fobj.write(text)
|
fobj.write(text)
|
||||||
self._file = fobj
|
self._file = fobj
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
message.error(self._win_id, "Failed to create initial file: "
|
message.error("Failed to create initial file: {}".format(e))
|
||||||
"{}".format(e))
|
|
||||||
return
|
return
|
||||||
self._proc = guiprocess.GUIProcess(self._win_id, what='editor',
|
self._proc = guiprocess.GUIProcess(what='editor', parent=self)
|
||||||
parent=self)
|
|
||||||
self._proc.finished.connect(self.on_proc_closed)
|
self._proc.finished.connect(self.on_proc_closed)
|
||||||
self._proc.error.connect(self.on_proc_error)
|
self._proc.error.connect(self.on_proc_error)
|
||||||
editor = config.get('general', 'editor')
|
editor = config.get('general', 'editor')
|
||||||
|
@ -50,7 +50,6 @@ class GUIProcess(QObject):
|
|||||||
verbose: Whether to show more messages.
|
verbose: Whether to show more messages.
|
||||||
_started: Whether the underlying process is started.
|
_started: Whether the underlying process is started.
|
||||||
_proc: The underlying QProcess.
|
_proc: The underlying QProcess.
|
||||||
_win_id: The window ID this process is used in.
|
|
||||||
_what: What kind of thing is spawned (process/editor/userscript/...).
|
_what: What kind of thing is spawned (process/editor/userscript/...).
|
||||||
Used in messages.
|
Used in messages.
|
||||||
|
|
||||||
@ -62,10 +61,9 @@ class GUIProcess(QObject):
|
|||||||
finished = pyqtSignal(int, QProcess.ExitStatus)
|
finished = pyqtSignal(int, QProcess.ExitStatus)
|
||||||
started = pyqtSignal()
|
started = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, win_id, what, *, verbose=False, additional_env=None,
|
def __init__(self, what, *, verbose=False, additional_env=None,
|
||||||
parent=None):
|
parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._win_id = win_id
|
|
||||||
self._what = what
|
self._what = what
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
self._started = False
|
self._started = False
|
||||||
@ -90,8 +88,7 @@ class GUIProcess(QObject):
|
|||||||
def on_error(self, error):
|
def on_error(self, error):
|
||||||
"""Show a message if there was an error while spawning."""
|
"""Show a message if there was an error while spawning."""
|
||||||
msg = ERROR_STRINGS[error]
|
msg = ERROR_STRINGS[error]
|
||||||
message.error(self._win_id, "Error while spawning {}: {}".format(
|
message.error("Error while spawning {}: {}".format(self._what, msg))
|
||||||
self._what, msg), immediately=True)
|
|
||||||
|
|
||||||
@pyqtSlot(int, QProcess.ExitStatus)
|
@pyqtSlot(int, QProcess.ExitStatus)
|
||||||
def on_finished(self, code, status):
|
def on_finished(self, code, status):
|
||||||
@ -100,18 +97,16 @@ class GUIProcess(QObject):
|
|||||||
log.procs.debug("Process finished with code {}, status {}.".format(
|
log.procs.debug("Process finished with code {}, status {}.".format(
|
||||||
code, status))
|
code, status))
|
||||||
if status == QProcess.CrashExit:
|
if status == QProcess.CrashExit:
|
||||||
message.error(self._win_id,
|
message.error("{} crashed!".format(self._what.capitalize()))
|
||||||
"{} crashed!".format(self._what.capitalize()),
|
|
||||||
immediately=True)
|
|
||||||
elif status == QProcess.NormalExit and code == 0:
|
elif status == QProcess.NormalExit and code == 0:
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
message.info(self._win_id, "{} exited successfully.".format(
|
message.info("{} exited successfully.".format(
|
||||||
self._what.capitalize()))
|
self._what.capitalize()))
|
||||||
else:
|
else:
|
||||||
assert status == QProcess.NormalExit
|
assert status == QProcess.NormalExit
|
||||||
# We call this 'status' here as it makes more sense to the user -
|
# We call this 'status' here as it makes more sense to the user -
|
||||||
# it's actually 'code'.
|
# it's actually 'code'.
|
||||||
message.error(self._win_id, "{} exited with status {}.".format(
|
message.error("{} exited with status {}.".format(
|
||||||
self._what.capitalize(), code))
|
self._what.capitalize(), code))
|
||||||
|
|
||||||
stderr = bytes(self._proc.readAllStandardError()).decode('utf-8')
|
stderr = bytes(self._proc.readAllStandardError()).decode('utf-8')
|
||||||
@ -137,7 +132,7 @@ class GUIProcess(QObject):
|
|||||||
fake_cmdline = ' '.join(shlex.quote(e) for e in [cmd] + list(args))
|
fake_cmdline = ' '.join(shlex.quote(e) for e in [cmd] + list(args))
|
||||||
log.procs.debug("Executing: {}".format(fake_cmdline))
|
log.procs.debug("Executing: {}".format(fake_cmdline))
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
message.info(self._win_id, 'Executing: ' + fake_cmdline)
|
message.info('Executing: ' + fake_cmdline)
|
||||||
|
|
||||||
def start(self, cmd, args, mode=None):
|
def start(self, cmd, args, mode=None):
|
||||||
"""Convenience wrapper around QProcess::start."""
|
"""Convenience wrapper around QProcess::start."""
|
||||||
@ -158,8 +153,8 @@ class GUIProcess(QObject):
|
|||||||
log.procs.debug("Process started.")
|
log.procs.debug("Process started.")
|
||||||
self._started = True
|
self._started = True
|
||||||
else:
|
else:
|
||||||
message.error(self._win_id, "Error while spawning {}: {}.".format(
|
message.error("Error while spawning {}: {}.".format(self._what,
|
||||||
self._what, self._proc.error()), immediately=True)
|
self._proc.error()))
|
||||||
|
|
||||||
def exit_status(self):
|
def exit_status(self):
|
||||||
return self._proc.exitStatus()
|
return self._proc.exitStatus()
|
||||||
|
@ -180,17 +180,14 @@ class SaveManager(QObject):
|
|||||||
try:
|
try:
|
||||||
saveable.save(silent=True)
|
saveable.save(silent=True)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
message.error('current', "Failed to auto-save {}: "
|
message.error("Failed to auto-save {}: {}".format(key, e))
|
||||||
"{}".format(key, e))
|
|
||||||
|
|
||||||
@cmdutils.register(instance='save-manager', name='save',
|
@cmdutils.register(instance='save-manager', name='save',
|
||||||
star_args_optional=True)
|
star_args_optional=True)
|
||||||
@cmdutils.argument('win_id', win_id=True)
|
def save_command(self, *what):
|
||||||
def save_command(self, win_id, *what):
|
|
||||||
"""Save configs and state.
|
"""Save configs and state.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
win_id: The window this command is executed in.
|
|
||||||
*what: What to save (`config`/`key-config`/`cookies`/...).
|
*what: What to save (`config`/`key-config`/`cookies`/...).
|
||||||
If not given, everything is saved.
|
If not given, everything is saved.
|
||||||
"""
|
"""
|
||||||
@ -201,12 +198,10 @@ class SaveManager(QObject):
|
|||||||
explicit = False
|
explicit = False
|
||||||
for key in what:
|
for key in what:
|
||||||
if key not in self.saveables:
|
if key not in self.saveables:
|
||||||
message.error(win_id, "{} is nothing which can be "
|
message.error("{} is nothing which can be saved".format(key))
|
||||||
"saved".format(key))
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self.save(key, explicit=explicit, force=True)
|
self.save(key, explicit=explicit, force=True)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
message.error(win_id, "Could not save {}: "
|
message.error("Could not save {}: {}".format(key, e))
|
||||||
"{}".format(key, e))
|
|
||||||
log.save.debug(":save saved {}".format(', '.join(what)))
|
log.save.debug(":save saved {}".format(', '.join(what)))
|
||||||
|
@ -429,14 +429,12 @@ class SessionManager(QObject):
|
|||||||
win.close()
|
win.close()
|
||||||
|
|
||||||
@cmdutils.register(name=['session-save', 'w'], instance='session-manager')
|
@cmdutils.register(name=['session-save', 'w'], instance='session-manager')
|
||||||
@cmdutils.argument('win_id', win_id=True)
|
|
||||||
@cmdutils.argument('name', completion=usertypes.Completion.sessions)
|
@cmdutils.argument('name', completion=usertypes.Completion.sessions)
|
||||||
def session_save(self, win_id, name: str=default, current=False,
|
def session_save(self, name: str=default, current=False, quiet=False,
|
||||||
quiet=False, force=False):
|
force=False):
|
||||||
"""Save a session.
|
"""Save a session.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
win_id: The current window ID.
|
|
||||||
name: The name of the session. If not given, the session configured
|
name: The name of the session. If not given, the session configured
|
||||||
in general -> session-default-name is saved.
|
in general -> session-default-name is saved.
|
||||||
current: Save the current session instead of the default.
|
current: Save the current session instead of the default.
|
||||||
@ -460,8 +458,7 @@ class SessionManager(QObject):
|
|||||||
.format(e))
|
.format(e))
|
||||||
else:
|
else:
|
||||||
if not quiet:
|
if not quiet:
|
||||||
message.info(win_id, "Saved session {}.".format(name),
|
message.info("Saved session {}.".format(name))
|
||||||
immediately=True)
|
|
||||||
|
|
||||||
@cmdutils.register(instance='session-manager')
|
@cmdutils.register(instance='session-manager')
|
||||||
@cmdutils.argument('name', completion=usertypes.Completion.sessions)
|
@cmdutils.argument('name', completion=usertypes.Completion.sessions)
|
||||||
|
@ -87,36 +87,33 @@ def repeat(times: int, command, win_id):
|
|||||||
|
|
||||||
|
|
||||||
@cmdutils.register(hide=True)
|
@cmdutils.register(hide=True)
|
||||||
@cmdutils.argument('win_id', win_id=True)
|
def message_error(text):
|
||||||
def message_error(win_id, text):
|
|
||||||
"""Show an error message in the statusbar.
|
"""Show an error message in the statusbar.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
text: The text to show.
|
text: The text to show.
|
||||||
"""
|
"""
|
||||||
message.error(win_id, text)
|
message.error(text)
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register(hide=True)
|
@cmdutils.register(hide=True)
|
||||||
@cmdutils.argument('win_id', win_id=True)
|
def message_info(text):
|
||||||
def message_info(win_id, text):
|
|
||||||
"""Show an info message in the statusbar.
|
"""Show an info message in the statusbar.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
text: The text to show.
|
text: The text to show.
|
||||||
"""
|
"""
|
||||||
message.info(win_id, text)
|
message.info(text)
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register(hide=True)
|
@cmdutils.register(hide=True)
|
||||||
@cmdutils.argument('win_id', win_id=True)
|
def message_warning(text):
|
||||||
def message_warning(win_id, text):
|
|
||||||
"""Show a warning message in the statusbar.
|
"""Show a warning message in the statusbar.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
text: The text to show.
|
text: The text to show.
|
||||||
"""
|
"""
|
||||||
message.warning(win_id, text)
|
message.warning(text)
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register(debug=True)
|
@cmdutils.register(debug=True)
|
||||||
|
@ -37,6 +37,14 @@ QueuedMsg = collections.namedtuple(
|
|||||||
'QueuedMsg', ['time', 'win_id', 'method_name', 'text', 'args', 'kwargs'])
|
'QueuedMsg', ['time', 'win_id', 'method_name', 'text', 'args', 'kwargs'])
|
||||||
|
|
||||||
|
|
||||||
|
global_bridge = None
|
||||||
|
|
||||||
|
|
||||||
|
def init():
|
||||||
|
global global_bridge
|
||||||
|
global_bridge = GlobalMessageBridge()
|
||||||
|
|
||||||
|
|
||||||
def _log_stack(typ, stack):
|
def _log_stack(typ, stack):
|
||||||
"""Log the given message stacktrace.
|
"""Log the given message stacktrace.
|
||||||
|
|
||||||
@ -132,12 +140,11 @@ def on_focus_changed():
|
|||||||
getattr(bridge, msg.method_name)(text, *msg.args, **msg.kwargs)
|
getattr(bridge, msg.method_name)(text, *msg.args, **msg.kwargs)
|
||||||
|
|
||||||
|
|
||||||
def error(win_id, message, immediately=False, *, stack=None):
|
def error(message, *, stack=None):
|
||||||
"""Convenience function to display an error message in the statusbar.
|
"""Convenience function to display an error message in the statusbar.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
win_id: The ID of the window which is calling this function.
|
message: The message to show
|
||||||
others: See MessageBridge.error.
|
|
||||||
stack: The stack trace to show.
|
stack: The stack trace to show.
|
||||||
"""
|
"""
|
||||||
if stack is None:
|
if stack is None:
|
||||||
@ -146,28 +153,18 @@ def error(win_id, message, immediately=False, *, stack=None):
|
|||||||
else:
|
else:
|
||||||
typ = 'error (from exception)'
|
typ = 'error (from exception)'
|
||||||
_log_stack(typ, stack)
|
_log_stack(typ, stack)
|
||||||
_wrapper(win_id, 'error', message, immediately)
|
global_bridge.show_message.emit(usertypes.MessageLevel.error, message)
|
||||||
|
|
||||||
|
|
||||||
def warning(win_id, message, immediately=False):
|
def warning(message):
|
||||||
"""Convenience function to display a warning message in the statusbar.
|
"""Convenience function to display a warning message in the statusbar."""
|
||||||
|
|
||||||
Args:
|
|
||||||
win_id: The ID of the window which is calling this function.
|
|
||||||
others: See MessageBridge.warning.
|
|
||||||
"""
|
|
||||||
_log_stack('warning', traceback.format_stack())
|
_log_stack('warning', traceback.format_stack())
|
||||||
_wrapper(win_id, 'warning', message, immediately)
|
global_bridge.show_message.emit(usertypes.MessageLevel.warning, message)
|
||||||
|
|
||||||
|
|
||||||
def info(win_id, message, immediately=True):
|
def info(message):
|
||||||
"""Convenience function to display an info message in the statusbar.
|
"""Convenience function to display an info message in the statusbar."""
|
||||||
|
global_bridge.show_message.emit(usertypes.MessageLevel.info, message)
|
||||||
Args:
|
|
||||||
win_id: The ID of the window which is calling this function.
|
|
||||||
others: See MessageBridge.info.
|
|
||||||
"""
|
|
||||||
_wrapper(win_id, 'info', message, immediately)
|
|
||||||
|
|
||||||
|
|
||||||
def set_cmd_text(win_id, txt):
|
def set_cmd_text(win_id, txt):
|
||||||
@ -252,19 +249,24 @@ def confirm_async(win_id, message, yes_action, no_action=None, default=None):
|
|||||||
_wrapper(win_id, 'ask', q, blocking=False)
|
_wrapper(win_id, 'ask', q, blocking=False)
|
||||||
|
|
||||||
|
|
||||||
|
class GlobalMessageBridge(QObject):
|
||||||
|
|
||||||
|
"""Global (not per-window) message bridge for errors/infos/warnings.
|
||||||
|
|
||||||
|
Signals:
|
||||||
|
show_message: Show a message
|
||||||
|
arg 0: A MessageLevel member
|
||||||
|
arg 1: The text to show
|
||||||
|
"""
|
||||||
|
|
||||||
|
show_message = pyqtSignal(usertypes.MessageLevel, str)
|
||||||
|
|
||||||
|
|
||||||
class MessageBridge(QObject):
|
class MessageBridge(QObject):
|
||||||
|
|
||||||
"""Bridge for messages to be shown in the statusbar.
|
"""Bridge for messages to be shown in the statusbar.
|
||||||
|
|
||||||
Signals:
|
Signals:
|
||||||
s_error: Display an error message.
|
|
||||||
arg 0: The error message to show.
|
|
||||||
arg 1: Whether to show it immediately (True) or queue it
|
|
||||||
(False).
|
|
||||||
s_info: Display an info message.
|
|
||||||
args: See s_error.
|
|
||||||
s_warning: Display a warning message.
|
|
||||||
args: See s_error.
|
|
||||||
s_set_text: Set a persistent text in the statusbar.
|
s_set_text: Set a persistent text in the statusbar.
|
||||||
arg: The text to set.
|
arg: The text to set.
|
||||||
s_maybe_reset_text: Reset the text if it hasn't been changed yet.
|
s_maybe_reset_text: Reset the text if it hasn't been changed yet.
|
||||||
@ -280,9 +282,6 @@ class MessageBridge(QObject):
|
|||||||
Qt.DirectConnection!
|
Qt.DirectConnection!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
s_error = pyqtSignal(str, bool)
|
|
||||||
s_warning = pyqtSignal(str, bool)
|
|
||||||
s_info = pyqtSignal(str, bool)
|
|
||||||
s_set_text = pyqtSignal(str)
|
s_set_text = pyqtSignal(str)
|
||||||
s_maybe_reset_text = pyqtSignal(str)
|
s_maybe_reset_text = pyqtSignal(str)
|
||||||
s_set_cmd_text = pyqtSignal(str)
|
s_set_cmd_text = pyqtSignal(str)
|
||||||
@ -291,55 +290,6 @@ class MessageBridge(QObject):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return utils.get_repr(self)
|
return utils.get_repr(self)
|
||||||
|
|
||||||
def error(self, msg, immediately=False, *, log_stack=True):
|
|
||||||
"""Display an error in the statusbar.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg: The message to show.
|
|
||||||
immediately: Whether to display the message immediately (True) or
|
|
||||||
queue it for displaying when all other messages are
|
|
||||||
displayed (False). Messages resulting from direct user
|
|
||||||
input should be displayed immediately, all other
|
|
||||||
messages should be queued.
|
|
||||||
log_stack: Log the stacktrace of the error.
|
|
||||||
"""
|
|
||||||
msg = str(msg)
|
|
||||||
log.message.error(msg)
|
|
||||||
if log_stack:
|
|
||||||
_log_stack('error (delayed)', traceback.format_stack())
|
|
||||||
self.s_error.emit(msg, immediately)
|
|
||||||
|
|
||||||
def warning(self, msg, immediately=False, *, log_stack=True):
|
|
||||||
"""Display a warning in the statusbar.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
msg: The message to show.
|
|
||||||
immediately: Whether to display the message immediately (True) or
|
|
||||||
queue it for displaying when all other messages are
|
|
||||||
displayed (False). Messages resulting from direct user
|
|
||||||
input should be displayed immediately, all other
|
|
||||||
messages should be queued.
|
|
||||||
log_stack: Log the stacktrace of the warning.
|
|
||||||
"""
|
|
||||||
msg = str(msg)
|
|
||||||
log.message.warning(msg)
|
|
||||||
if log_stack:
|
|
||||||
_log_stack('warning (delayed)', traceback.format_stack())
|
|
||||||
self.s_warning.emit(msg, immediately)
|
|
||||||
|
|
||||||
def info(self, msg, immediately=True, *, log_stack=False):
|
|
||||||
"""Display an info text in the statusbar.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
See error(). Note immediately is True by default, because messages
|
|
||||||
do rarely happen without user interaction.
|
|
||||||
|
|
||||||
log_stack is ignored.
|
|
||||||
"""
|
|
||||||
msg = str(msg)
|
|
||||||
log.message.info(msg)
|
|
||||||
self.s_info.emit(msg, immediately)
|
|
||||||
|
|
||||||
def set_cmd_text(self, text, *, log_stack=False):
|
def set_cmd_text(self, text, *, log_stack=False):
|
||||||
"""Set the command text of the statusbar.
|
"""Set the command text of the statusbar.
|
||||||
|
|
||||||
|
@ -321,11 +321,10 @@ def qurl_from_user_input(urlstr):
|
|||||||
return QUrl('http://[{}]{}'.format(ipstr, rest))
|
return QUrl('http://[{}]{}'.format(ipstr, rest))
|
||||||
|
|
||||||
|
|
||||||
def invalid_url_error(win_id, url, action):
|
def invalid_url_error(url, action):
|
||||||
"""Display an error message for a URL.
|
"""Display an error message for a URL.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
win_id: The window ID to show the error message in.
|
|
||||||
action: The action which was interrupted by the error.
|
action: The action which was interrupted by the error.
|
||||||
"""
|
"""
|
||||||
if url.isValid():
|
if url.isValid():
|
||||||
@ -333,7 +332,7 @@ def invalid_url_error(win_id, url, action):
|
|||||||
url.toDisplayString()))
|
url.toDisplayString()))
|
||||||
errstring = get_errstring(
|
errstring = get_errstring(
|
||||||
url, "Trying to {} with invalid URL".format(action))
|
url, "Trying to {} with invalid URL".format(action))
|
||||||
message.error(win_id, errstring)
|
message.error(errstring)
|
||||||
|
|
||||||
|
|
||||||
def raise_cmdexc_if_invalid(url):
|
def raise_cmdexc_if_invalid(url):
|
||||||
|
@ -265,6 +265,9 @@ arg2backend = {
|
|||||||
JsWorld = enum('JsWorld', ['main', 'application', 'user', 'jseval'])
|
JsWorld = enum('JsWorld', ['main', 'application', 'user', 'jseval'])
|
||||||
|
|
||||||
|
|
||||||
|
MessageLevel = enum('MessageLevel', ['error', 'warning', 'info'])
|
||||||
|
|
||||||
|
|
||||||
# Where a download should be saved
|
# Where a download should be saved
|
||||||
class DownloadTarget:
|
class DownloadTarget:
|
||||||
|
|
||||||
|
@ -274,21 +274,6 @@ def format_seconds(total_seconds):
|
|||||||
return prefix + ':'.join(chunks)
|
return prefix + ':'.join(chunks)
|
||||||
|
|
||||||
|
|
||||||
def format_timedelta(td):
|
|
||||||
"""Format a timedelta to get a "1h 5m 1s" string."""
|
|
||||||
prefix = '-' if td.total_seconds() < 0 else ''
|
|
||||||
hours, rem = divmod(abs(round(td.total_seconds())), 3600)
|
|
||||||
minutes, seconds = divmod(rem, 60)
|
|
||||||
chunks = []
|
|
||||||
if hours:
|
|
||||||
chunks.append('{}h'.format(hours))
|
|
||||||
if minutes:
|
|
||||||
chunks.append('{}m'.format(minutes))
|
|
||||||
if seconds or not chunks:
|
|
||||||
chunks.append('{}s'.format(seconds))
|
|
||||||
return prefix + ' '.join(chunks)
|
|
||||||
|
|
||||||
|
|
||||||
def format_size(size, base=1024, suffix=''):
|
def format_size(size, base=1024, suffix=''):
|
||||||
"""Format a byte size so it's human readable.
|
"""Format a byte size so it's human readable.
|
||||||
|
|
||||||
|
@ -83,12 +83,7 @@ class LogLine(testprocess.Line):
|
|||||||
if self.function is None and self.line == 0:
|
if self.function is None and self.line == 0:
|
||||||
self.line = None
|
self.line = None
|
||||||
self.traceback = line.get('traceback')
|
self.traceback = line.get('traceback')
|
||||||
|
self.message = line['message']
|
||||||
self.full_message = line['message']
|
|
||||||
msg_match = re.match(r'^(\[(?P<prefix>\d+s ago)\] )?(?P<message>.*)',
|
|
||||||
self.full_message, re.DOTALL)
|
|
||||||
self.prefix = msg_match.group('prefix')
|
|
||||||
self.message = msg_match.group('message')
|
|
||||||
|
|
||||||
self.expected = is_ignored_qt_message(self.message)
|
self.expected = is_ignored_qt_message(self.message)
|
||||||
self.use_color = False
|
self.use_color = False
|
||||||
|
@ -211,13 +211,6 @@ def test_quteprocess_quitting(qtbot, quteproc_process):
|
|||||||
'line': 233,
|
'line': 233,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
(
|
|
||||||
# With [2s ago] marker
|
|
||||||
'{"created": 0, "msecs": 0, "levelname": "DEBUG", "name": "foo", '
|
|
||||||
'"module": "foo", "funcName": "foo", "lineno": 0, "levelno": 10, '
|
|
||||||
'"message": "[2s ago] test"}',
|
|
||||||
{'prefix': '2s ago', 'message': 'test'}
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
# ResourceWarning
|
# ResourceWarning
|
||||||
'{"created": 0, "msecs": 0, "levelname": "WARNING", '
|
'{"created": 0, "msecs": 0, "levelname": "WARNING", '
|
||||||
@ -228,8 +221,7 @@ def test_quteprocess_quitting(qtbot, quteproc_process):
|
|||||||
{'category': 'py.warnings'}
|
{'category': 'py.warnings'}
|
||||||
),
|
),
|
||||||
], ids=['normal', 'vdebug', 'unknown module', 'expected message',
|
], ids=['normal', 'vdebug', 'unknown module', 'expected message',
|
||||||
'weird Qt location', 'QXcbXSettings', '2s ago marker',
|
'weird Qt location', 'QXcbXSettings', 'resourcewarning'])
|
||||||
'resourcewarning'])
|
|
||||||
def test_log_line_parse(data, attrs):
|
def test_log_line_parse(data, attrs):
|
||||||
line = quteprocess.LogLine(data)
|
line = quteprocess.LogLine(data)
|
||||||
for name, expected in attrs.items():
|
for name, expected in attrs.items():
|
||||||
|
@ -43,7 +43,7 @@ def patch_things(config_stub, message_mock, monkeypatch, stubs):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def editor():
|
def editor():
|
||||||
ed = editormod.ExternalEditor(0)
|
ed = editormod.ExternalEditor()
|
||||||
ed.editing_finished = mock.Mock()
|
ed.editing_finished = mock.Mock()
|
||||||
yield ed
|
yield ed
|
||||||
ed._cleanup()
|
ed._cleanup()
|
||||||
|
@ -37,7 +37,7 @@ def guiprocess_message_mock(message_mock):
|
|||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def proc(qtbot):
|
def proc(qtbot):
|
||||||
"""A fixture providing a GUIProcess and cleaning it up after the test."""
|
"""A fixture providing a GUIProcess and cleaning it up after the test."""
|
||||||
p = guiprocess.GUIProcess(0, 'testprocess')
|
p = guiprocess.GUIProcess('testprocess')
|
||||||
yield p
|
yield p
|
||||||
if p._proc.state() == QProcess.Running:
|
if p._proc.state() == QProcess.Running:
|
||||||
with qtbot.waitSignal(p.finished, timeout=10000,
|
with qtbot.waitSignal(p.finished, timeout=10000,
|
||||||
@ -50,7 +50,7 @@ def proc(qtbot):
|
|||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def fake_proc(monkeypatch, stubs):
|
def fake_proc(monkeypatch, stubs):
|
||||||
"""A fixture providing a GUIProcess with a mocked QProcess."""
|
"""A fixture providing a GUIProcess with a mocked QProcess."""
|
||||||
p = guiprocess.GUIProcess(0, 'testprocess')
|
p = guiprocess.GUIProcess('testprocess')
|
||||||
monkeypatch.setattr(p, '_proc', stubs.fake_qprocess())
|
monkeypatch.setattr(p, '_proc', stubs.fake_qprocess())
|
||||||
return p
|
return p
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ def test_start_verbose(proc, qtbot, guiprocess_message_mock, py_proc):
|
|||||||
def test_start_env(monkeypatch, qtbot, py_proc):
|
def test_start_env(monkeypatch, qtbot, py_proc):
|
||||||
monkeypatch.setenv('QUTEBROWSER_TEST_1', '1')
|
monkeypatch.setenv('QUTEBROWSER_TEST_1', '1')
|
||||||
env = {'QUTEBROWSER_TEST_2': '2'}
|
env = {'QUTEBROWSER_TEST_2': '2'}
|
||||||
proc = guiprocess.GUIProcess(0, 'testprocess', additional_env=env)
|
proc = guiprocess.GUIProcess('testprocess', additional_env=env)
|
||||||
|
|
||||||
argv = py_proc("""
|
argv = py_proc("""
|
||||||
import os
|
import os
|
||||||
|
@ -431,11 +431,11 @@ def test_invalid_url_error(urlutils_message_mock, url, valid, has_err_string):
|
|||||||
assert qurl.isValid() == valid
|
assert qurl.isValid() == valid
|
||||||
if valid:
|
if valid:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
urlutils.invalid_url_error(0, qurl, '')
|
urlutils.invalid_url_error(qurl, '')
|
||||||
assert not urlutils_message_mock.messages
|
assert not urlutils_message_mock.messages
|
||||||
else:
|
else:
|
||||||
assert bool(qurl.errorString()) == has_err_string
|
assert bool(qurl.errorString()) == has_err_string
|
||||||
urlutils.invalid_url_error(0, qurl, 'frozzle')
|
urlutils.invalid_url_error(qurl, 'frozzle')
|
||||||
|
|
||||||
msg = urlutils_message_mock.getmsg(urlutils_message_mock.Level.error)
|
msg = urlutils_message_mock.getmsg(urlutils_message_mock.Level.error)
|
||||||
if has_err_string:
|
if has_err_string:
|
||||||
|
@ -377,25 +377,6 @@ def test_format_seconds(seconds, out):
|
|||||||
assert utils.format_seconds(seconds) == out
|
assert utils.format_seconds(seconds) == out
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('td, out', [
|
|
||||||
(datetime.timedelta(seconds=-1), '-1s'),
|
|
||||||
(datetime.timedelta(seconds=0), '0s'),
|
|
||||||
(datetime.timedelta(seconds=59), '59s'),
|
|
||||||
(datetime.timedelta(seconds=120), '2m'),
|
|
||||||
(datetime.timedelta(seconds=60.4), '1m'),
|
|
||||||
(datetime.timedelta(seconds=63), '1m 3s'),
|
|
||||||
(datetime.timedelta(seconds=-64), '-1m 4s'),
|
|
||||||
(datetime.timedelta(seconds=3599), '59m 59s'),
|
|
||||||
(datetime.timedelta(seconds=3600), '1h'),
|
|
||||||
(datetime.timedelta(seconds=3605), '1h 5s'),
|
|
||||||
(datetime.timedelta(seconds=3723), '1h 2m 3s'),
|
|
||||||
(datetime.timedelta(seconds=3780), '1h 3m'),
|
|
||||||
(datetime.timedelta(seconds=36000), '10h'),
|
|
||||||
])
|
|
||||||
def test_format_timedelta(td, out):
|
|
||||||
assert utils.format_timedelta(td) == out
|
|
||||||
|
|
||||||
|
|
||||||
class TestFormatSize:
|
class TestFormatSize:
|
||||||
|
|
||||||
"""Tests for format_size.
|
"""Tests for format_size.
|
||||||
|
Loading…
Reference in New Issue
Block a user