Merge branch 'ninja'
This commit is contained in:
commit
d059c468af
3
.gitignore
vendored
3
.gitignore
vendored
@ -13,4 +13,5 @@ __pycache__
|
||||
.ropeproject
|
||||
# We can probably remove these later
|
||||
*.asciidoc
|
||||
*.html
|
||||
/doc/*.html
|
||||
/README.html
|
||||
|
@ -74,6 +74,7 @@ The following software and libraries are required to run qutebrowser:
|
||||
(5.3.1 recommended) for Python 3
|
||||
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
|
||||
* http://fdik.org/pyPEG/[pyPEG2]
|
||||
* http://jinja.pocoo.org/[jinja2]
|
||||
|
||||
The following libraries are optional and provide colored logging in the
|
||||
console:
|
||||
@ -85,7 +86,8 @@ On Debian
|
||||
~~~~~~~~~
|
||||
|
||||
----
|
||||
# apt-get install python3-pyqt5 python3-pyqt5.qtwebkit python3-pkg-resources python3-pip
|
||||
# apt-get install python3-pyqt5 python3-pyqt5.qtwebkit python3-pkg-resources
|
||||
python3-pip python3-jinja2
|
||||
# pip3 install pypeg2 --allow-external pypeg2 --allow-unverified pypeg2
|
||||
----
|
||||
|
||||
@ -129,6 +131,7 @@ to get Qt and PyQt5.
|
||||
* Get pip as described http://stackoverflow.com/a/12476379[on Stack Overflow].
|
||||
* Run +pip install --allow-external pypeg2 --allow-unverified pypeg2 pypeg2+ to
|
||||
install pypeg2.
|
||||
* Run +pip install jinja2+ to install jinja2.
|
||||
|
||||
As soon as v0.1 is out, a standalone .exe (built with
|
||||
http://cx-freeze.sourceforge.net/[cx_Freeze]) will be provided. In the
|
||||
|
@ -9,7 +9,7 @@ arch=(any)
|
||||
url="http://www.qutebrowser.org/"
|
||||
license=('GPL')
|
||||
depends=('python>=3.4' 'python-setuptools' 'python-pyqt5>=5.2' 'qt5-base>=5.2'
|
||||
'qt5-webkit>=5.2' 'libxkbcommon-x11' 'python-pypeg2')
|
||||
'qt5-webkit>=5.2' 'libxkbcommon-x11' 'python-pypeg2' 'python-jinja')
|
||||
makedepends=('python' 'python-setuptools')
|
||||
optdepends=('python-colorlog: colored logging output')
|
||||
options=(!emptydirs)
|
||||
|
@ -394,7 +394,7 @@ class Application(QApplication):
|
||||
status.prompt.prompter.ask_question, Qt.DirectConnection)
|
||||
|
||||
# config
|
||||
self.config.style_changed.connect(style.invalidate_caches)
|
||||
self.config.style_changed.connect(style.get_stylesheet.cache_clear)
|
||||
for obj in (tabs, completion, self.mainwindow, self.cmd_history,
|
||||
websettings, kp[utypes.KeyMode.normal], self.modeman,
|
||||
status, status.txt):
|
||||
|
@ -19,10 +19,10 @@
|
||||
|
||||
"""The main browser widgets."""
|
||||
|
||||
from functools import partial
|
||||
import functools
|
||||
|
||||
import sip
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, PYQT_VERSION, Qt, QTimer
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, PYQT_VERSION, Qt
|
||||
from PyQt5.QtNetwork import QNetworkReply
|
||||
from PyQt5.QtWidgets import QFileDialog
|
||||
from PyQt5.QtPrintSupport import QPrintDialog
|
||||
@ -30,7 +30,7 @@ from PyQt5.QtWebKitWidgets import QWebPage
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.network import networkmanager
|
||||
from qutebrowser.utils import message, usertypes, log, http, utils, qtutils
|
||||
from qutebrowser.utils import message, usertypes, log, http, jinja, qtutils
|
||||
|
||||
|
||||
class BrowserPage(QWebPage):
|
||||
@ -118,7 +118,8 @@ class BrowserPage(QWebPage):
|
||||
log.webview.debug("Error domain: {}, error code: {}".format(
|
||||
info.domain, info.error))
|
||||
title = "Error loading page: {}".format(urlstr)
|
||||
errpage.content = utils.read_file('html/error.html').format(
|
||||
template = jinja.env.get_template('error.html')
|
||||
errpage.content = template.render( # pylint: disable=maybe-no-member
|
||||
title=title, url=urlstr, error=info.errorString, icon='')
|
||||
return True
|
||||
|
||||
@ -197,8 +198,8 @@ class BrowserPage(QWebPage):
|
||||
if reply.isFinished():
|
||||
self.display_content(reply, 'image/jpeg')
|
||||
else:
|
||||
reply.finished.connect(
|
||||
partial(self.display_content, reply, 'image/jpeg'))
|
||||
reply.finished.connect(functools.partial(
|
||||
self.display_content, reply, 'image/jpeg'))
|
||||
else:
|
||||
# Unknown mimetype, so download anyways.
|
||||
self.start_download.emit(reply)
|
||||
@ -240,19 +241,16 @@ class BrowserPage(QWebPage):
|
||||
log.webview.warning("Extension {} not supported!".format(ext))
|
||||
return super().extension(ext, opt, out)
|
||||
return handler(opt, out)
|
||||
except BaseException as e:
|
||||
except: # pylint: disable=bare-except
|
||||
# Due to a bug in PyQt, exceptions inside extension() get swallowed
|
||||
# for some reason. Here we set up a single-shot QTimer to re-raise
|
||||
# them when we're back in the mainloop.
|
||||
# for some reason.
|
||||
# http://www.riverbankcomputing.com/pipermail/pyqt/2014-August/034722.html
|
||||
#
|
||||
# Note we somehow can't re-raise with the correct traceback here.
|
||||
# Using "raise from" or ".with_traceback()" just ignores the
|
||||
# exception again.
|
||||
exc = e # needed for the closure
|
||||
def raise_():
|
||||
raise exc
|
||||
QTimer.singleShot(0, raise_)
|
||||
# We used to re-raise the exception with a single-shot QTimer here,
|
||||
# but that lead to a strange proble with a KeyError with some
|
||||
# random jinja template stuff as content. For now, we only log it,
|
||||
# so it doesn't pass 100% silently.
|
||||
log.webview.exception("Error inside WebPage::extension")
|
||||
return False
|
||||
|
||||
def javaScriptAlert(self, _frame, msg):
|
||||
|
@ -17,41 +17,32 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Utilities related to the look&feel of qutebrowser.
|
||||
|
||||
Module attributes:
|
||||
_colordict: The global cached ColorDict.
|
||||
_fontdict: The global cached FontDict.
|
||||
"""
|
||||
"""Utilities related to the look&feel of qutebrowser."""
|
||||
|
||||
import functools
|
||||
|
||||
import jinja2
|
||||
from PyQt5.QtGui import QColor
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import log, utils
|
||||
|
||||
|
||||
_colordict = None
|
||||
_fontdict = None
|
||||
|
||||
|
||||
def get_stylesheet(template):
|
||||
@functools.lru_cache(maxsize=16)
|
||||
def get_stylesheet(template_str):
|
||||
"""Format a stylesheet based on a template.
|
||||
|
||||
Args:
|
||||
template: The stylesheet template as string.
|
||||
template_str: The stylesheet template as string.
|
||||
|
||||
Return:
|
||||
The formatted template as string.
|
||||
"""
|
||||
global _colordict, _fontdict
|
||||
if _colordict is None:
|
||||
_colordict = ColorDict(config.section('colors'))
|
||||
if _fontdict is None:
|
||||
_fontdict = FontDict(config.section('fonts'))
|
||||
return template.strip().format(color=_colordict, font=_fontdict,
|
||||
config=config.instance())
|
||||
colordict = ColorDict(config.section('colors'))
|
||||
fontdict = FontDict(config.section('fonts'))
|
||||
template = jinja2.Template(template_str)
|
||||
return template.render(color=colordict, font=fontdict,
|
||||
config=config.instance())
|
||||
|
||||
|
||||
def set_register_stylesheet(obj):
|
||||
@ -78,15 +69,6 @@ def _update_stylesheet(obj, _section, _option):
|
||||
obj.setStyleSheet(get_stylesheet(obj.STYLESHEET))
|
||||
|
||||
|
||||
def invalidate_caches(section, _option):
|
||||
"""Invalidate cached dicts."""
|
||||
global _colordict, _fontdict
|
||||
if section == 'colors':
|
||||
_colordict = None
|
||||
elif section == 'fonts':
|
||||
_fontdict = None
|
||||
|
||||
|
||||
class ColorDict(dict):
|
||||
|
||||
"""A dict aimed at Qt stylesheet colors."""
|
||||
@ -123,20 +105,6 @@ class ColorDict(dict):
|
||||
else:
|
||||
return val
|
||||
|
||||
def getraw(self, key):
|
||||
"""Get a value without the transformations done in __getitem__.
|
||||
|
||||
Args:
|
||||
key: The key to get from the dict.
|
||||
|
||||
Return:
|
||||
A value, or None if the value wasn't found.
|
||||
"""
|
||||
try:
|
||||
return super().__getitem__(key)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
class FontDict(dict):
|
||||
|
||||
@ -160,17 +128,3 @@ class FontDict(dict):
|
||||
return ''
|
||||
else:
|
||||
return 'font: {};'.format(val)
|
||||
|
||||
def getraw(self, key):
|
||||
"""Get a value without the transformations done in __getitem__.
|
||||
|
||||
Args:
|
||||
key: The key to get from the dict.
|
||||
|
||||
Return:
|
||||
A value, or None if the value wasn't found.
|
||||
"""
|
||||
try:
|
||||
return super().__getitem__(key)
|
||||
except KeyError:
|
||||
return None
|
||||
|
31
qutebrowser/html/base.html
Normal file
31
qutebrowser/html/base.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
vim: ft=html fileencoding=utf-8 sts=4 sw=4 et:
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{ title }}</title>
|
||||
{% if icon %}
|
||||
<link rel='icon' type='image/png' href="{{ icon }}">
|
||||
{% endif %}
|
||||
<style type="text/css">
|
||||
{% block style %}
|
||||
body {
|
||||
background-color: #fff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
{% endblock %}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
{% block script %}
|
||||
{% endblock %}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
@ -1,67 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
vim: ft=html fileencoding=utf-8 sts=4 sw=4 et:
|
||||
Based on html/error.html from dwb
|
||||
-->
|
||||
{% extends "base.html" %}
|
||||
{% block style %}
|
||||
{{ super() }}
|
||||
#errorContainer {
|
||||
background: #fff;
|
||||
min-width: 35em;
|
||||
max-width: 35em;
|
||||
position: absolute;
|
||||
top: 2em;
|
||||
left: 1em;
|
||||
padding: 10px;
|
||||
border: 2px solid #eee;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{title}</title>
|
||||
<!--<link rel='icon' type='image/png' href="{icon}">-->
|
||||
<style type="text/css">
|
||||
body {{
|
||||
background-color: #fff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}}
|
||||
#errorTitleText {
|
||||
font-size: 118%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#errorContainer {{
|
||||
background: #fff;
|
||||
min-width: 35em;
|
||||
max-width: 35em;
|
||||
position: absolute;
|
||||
top: 2em;
|
||||
left: 1em;
|
||||
padding: 10px;
|
||||
border: 2px solid #eee;
|
||||
-webkit-border-radius: 5px;
|
||||
}}
|
||||
#errorMessageText {
|
||||
font-size: 80%;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
#errorTitleText {{
|
||||
font-size: 118%;
|
||||
font-weight: bold;
|
||||
}}
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
function tryagain()
|
||||
{
|
||||
location.reload();
|
||||
}
|
||||
function searchFor(uri) {
|
||||
location.href = uri;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
#errorMessageText {{
|
||||
font-size: 80%;
|
||||
}}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
function tryagain()
|
||||
{{
|
||||
location.reload();
|
||||
}}
|
||||
function searchFor(uri) {{
|
||||
location.href = uri;
|
||||
}}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="errorContainer">
|
||||
<div id="errorTitle">
|
||||
<p id="errorTitleText">Unable to load page</p>
|
||||
</div>
|
||||
<div id="errorMessage">
|
||||
<p>Problem occurred while loading the URL {url}</p>
|
||||
<p id="errorMessageText">{error}</p>
|
||||
</p>
|
||||
</div>
|
||||
{% block content %}
|
||||
<div id="errorContainer">
|
||||
<div id="errorTitle">
|
||||
<p id="errorTitleText">Unable to load page</p>
|
||||
</div>
|
||||
<div id="errorMessage">
|
||||
<p>Problem occurred while loading the URL {{ url }}</p>
|
||||
<p id="errorMessageText">{{ error }}</p>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form name="bl">
|
||||
<input type="button" value="Try again" onclick="javascript:tryagain()" />
|
||||
<!--<input type="button" value="Search" style="visibility:%s" onclick="javascript:searchFor('%s')" />-->
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<form name="bl">
|
||||
<input type="button" value="Try again" onclick="javascript:tryagain()" />
|
||||
<!--<input type="button" value="Search" style="visibility:%s" onclick="javascript:searchFor('%s')" />-->
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
35
qutebrowser/html/log.html
Normal file
35
qutebrowser/html/log.html
Normal file
@ -0,0 +1,35 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block style %}
|
||||
body {
|
||||
background-color: black;
|
||||
color: white;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid grey;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid grey;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ super() }}
|
||||
{% if content %}
|
||||
<table>
|
||||
{{ content | safe() }}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>Log output was disabled.</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
7
qutebrowser/html/pre.html
Normal file
7
qutebrowser/html/pre.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
{{ super() }}
|
||||
<pre>
|
||||
{{ content }}
|
||||
</pre>
|
||||
{% endblock %}
|
26
qutebrowser/html/version.html
Normal file
26
qutebrowser/html/version.html
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
{{ super() }}
|
||||
<h1>Version info</h1>
|
||||
<pre>{{ version }}</pre>
|
||||
|
||||
<h1>Copyright info</h1>
|
||||
<p>{{ copyright }}</p>
|
||||
<p>
|
||||
This program 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.
|
||||
</p>
|
||||
<p>
|
||||
This program 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.
|
||||
</p>
|
||||
<p>
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <a href="http://www.gnu.org/licenses/">
|
||||
http://www.gnu.org/licenses/</a> or open <a href="qute:gpl">qute:gpl</a>.
|
||||
</p>
|
||||
{% endblock %}
|
@ -16,60 +16,28 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# pylint complains when using .render() on jinja templates, so we make it shut
|
||||
# up for this whole module.
|
||||
# pylint: disable=maybe-no-member
|
||||
|
||||
"""Handler functions for different qute:... pages.
|
||||
|
||||
Module attributes:
|
||||
_HTML_TEMPLATE: The HTML boilerplate used to convert text into html.
|
||||
pyeval_output: The output of the last :pyeval command.
|
||||
"""
|
||||
|
||||
import html as pyhtml
|
||||
|
||||
from PyQt5.QtNetwork import QNetworkReply
|
||||
|
||||
import qutebrowser
|
||||
from qutebrowser.network import schemehandler
|
||||
from qutebrowser.utils import version, utils
|
||||
from qutebrowser.utils import version, utils, jinja
|
||||
from qutebrowser.utils import log as logutils
|
||||
|
||||
|
||||
_HTML_TEMPLATE = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{title}</title>
|
||||
{head}
|
||||
</head>
|
||||
<body>
|
||||
{body}
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
pyeval_output = ":pyeval was never called"
|
||||
|
||||
|
||||
def _get_html(title, snippet, head=None):
|
||||
"""Add HTML boilerplate to a html snippet.
|
||||
|
||||
Args:
|
||||
title: The title the page should have.
|
||||
snippet: The html snippet.
|
||||
head: Additional stuff to put in <head>
|
||||
|
||||
Return:
|
||||
HTML content as bytes.
|
||||
"""
|
||||
if head is None:
|
||||
head = ""
|
||||
html = _HTML_TEMPLATE.format(title=title, body=snippet, head=head).encode(
|
||||
'UTF-8', errors='xmlcharrefreplace')
|
||||
return html
|
||||
|
||||
|
||||
class QuteSchemeHandler(schemehandler.SchemeHandler):
|
||||
|
||||
"""Scheme handler for qute: URLs."""
|
||||
@ -110,61 +78,39 @@ class QuteHandlers:
|
||||
@classmethod
|
||||
def pyeval(cls):
|
||||
"""Handler for qute:pyeval. Return HTML content as bytes."""
|
||||
text = pyhtml.escape(pyeval_output)
|
||||
return _get_html('pyeval', '<pre>{}</pre>'.format(text))
|
||||
html = jinja.env.get_template('pre.html').render(
|
||||
title='pyeval', content=pyeval_output)
|
||||
return html.encode('UTF-8', errors='xmlcharrefreplace')
|
||||
|
||||
@classmethod
|
||||
def version(cls):
|
||||
"""Handler for qute:version. Return HTML content as bytes."""
|
||||
text = pyhtml.escape(version.version())
|
||||
html = '<h1>Version info</h1>'
|
||||
html += '<p>{}</p>'.format(text.replace('\n', '<br/>'))
|
||||
html += '<h1>Copyright info</h1>'
|
||||
html += '<p>{}</p>'.format(qutebrowser.__copyright__)
|
||||
html += version.GPL_BOILERPLATE_HTML
|
||||
return _get_html('Version', html)
|
||||
html = jinja.env.get_template('version.html').render(
|
||||
title='Version info', version=version.version(),
|
||||
copyright=qutebrowser.__copyright__)
|
||||
return html.encode('UTF-8', errors='xmlcharrefreplace')
|
||||
|
||||
@classmethod
|
||||
def plainlog(cls):
|
||||
"""Handler for qute:log. Return HTML content as bytes."""
|
||||
"""Handler for qute:plainlog. Return HTML content as bytes."""
|
||||
if logutils.ram_handler is None:
|
||||
text = "Log output was disabled."
|
||||
else:
|
||||
text = pyhtml.escape(logutils.ram_handler.dump_log())
|
||||
return _get_html('log', '<pre>{}</pre>'.format(text))
|
||||
text = logutils.ram_handler.dump_log()
|
||||
html = jinja.env.get_template('pre.html').render(
|
||||
title='log', content=text)
|
||||
return html.encode('UTF-8', errors='xmlcharrefreplace')
|
||||
|
||||
@classmethod
|
||||
def log(cls):
|
||||
"""Handler for qute:log. Return HTML content as bytes."""
|
||||
style = """
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color: black;
|
||||
color: white;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid grey;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid grey;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
</style>
|
||||
"""
|
||||
if logutils.ram_handler is None:
|
||||
html = "<p>Log output was disabled.</p>"
|
||||
html_log = None
|
||||
else:
|
||||
html = logutils.ram_handler.dump_log(html=True)
|
||||
return _get_html('log', html, head=style)
|
||||
html_log = logutils.ram_handler.dump_log(html=True)
|
||||
html = jinja.env.get_template('log.html').render(
|
||||
title='log', content=html_log)
|
||||
return html.encode('UTF-8', errors='xmlcharrefreplace')
|
||||
|
||||
@classmethod
|
||||
def gpl(cls):
|
||||
|
@ -112,6 +112,7 @@ def main():
|
||||
earlyinit.check_pyqt_webkit()
|
||||
earlyinit.check_pkg_resources()
|
||||
earlyinit.check_pypeg2()
|
||||
earlyinit.check_jinja2()
|
||||
# We do this import late as we need to fix harfbuzz first.
|
||||
from qutebrowser import app
|
||||
from qutebrowser.utils import debug
|
||||
|
@ -29,7 +29,7 @@ from PyQt5.QtCore import pyqtRemoveInputHook, QEvent, QCoreApplication
|
||||
|
||||
from qutebrowser.utils import log, utils
|
||||
from qutebrowser.commands import cmdutils
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.config import config, style
|
||||
|
||||
|
||||
@cmdutils.register(debug=True, name='debug-set-trace')
|
||||
@ -87,9 +87,11 @@ def debug_all_objects():
|
||||
|
||||
@cmdutils.register(debug=True)
|
||||
def debug_cache_stats():
|
||||
"""Print config LRU cache stats."""
|
||||
info = config.instance().get.cache_info()
|
||||
log.misc.debug(info)
|
||||
"""Print LRU cache stats."""
|
||||
config_info = config.instance().get.cache_info()
|
||||
style_info = style.get_stylesheet.cache_info()
|
||||
log.misc.debug('config: {}'.format(config_info))
|
||||
log.misc.debug('style: {}'.format(style_info))
|
||||
|
||||
|
||||
def log_events(klass):
|
||||
|
@ -248,3 +248,17 @@ def check_pypeg2():
|
||||
pip="pypeg2 --allow-external pypeg2 "
|
||||
"--allow-unverified pypeg2")
|
||||
_die(text)
|
||||
|
||||
|
||||
def check_jinja2():
|
||||
"""Check if jinja2 is installed."""
|
||||
try:
|
||||
import jinja2 # pylint: disable=unused-variable
|
||||
except ImportError:
|
||||
text = _missing_str("jinja2",
|
||||
debian="apt-get install python3-jinja2",
|
||||
arch="Install python-jinja from the AUR",
|
||||
windows="Install from http://www.lfd.uci.edu/"
|
||||
"~gohlke/pythonlibs/#jinja2 or via pip.",
|
||||
pip="jinja2")
|
||||
_die(text)
|
||||
|
58
qutebrowser/utils/jinja.py
Normal file
58
qutebrowser/utils/jinja.py
Normal file
@ -0,0 +1,58 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014 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/>.
|
||||
|
||||
"""Utilities related to jinja2. """
|
||||
|
||||
import os.path
|
||||
|
||||
import jinja2
|
||||
|
||||
from qutebrowser.utils import utils
|
||||
|
||||
|
||||
class Loader(jinja2.BaseLoader):
|
||||
|
||||
"""Jinja loader which uses utils.read_file to load templates."""
|
||||
|
||||
def __init__(self, subdir):
|
||||
self.subdir = subdir
|
||||
|
||||
def get_source(self, _env, template):
|
||||
path = os.path.join(self.subdir, template)
|
||||
try:
|
||||
source = utils.read_file(path)
|
||||
except FileNotFoundError:
|
||||
raise jinja2.TemplateNotFound(template)
|
||||
# Currently we don't implement auto-reloading, so we always return True
|
||||
# for up-to-date.
|
||||
return source, path, lambda: True
|
||||
|
||||
|
||||
def _guess_autoescape(template_name):
|
||||
"""Turns autoescape on/off based on the filetype.
|
||||
|
||||
Based on http://jinja.pocoo.org/docs/dev/api/#autoescaping
|
||||
"""
|
||||
if template_name is None or '.' not in template_name:
|
||||
return False
|
||||
ext = template_name.rsplit('.', 1)[1]
|
||||
return ext in ('html', 'htm', 'xml')
|
||||
|
||||
|
||||
env = jinja2.Environment(loader=Loader('html'), autoescape=_guess_autoescape)
|
@ -341,13 +341,12 @@ class RAMHandler(logging.Handler):
|
||||
self.data.append(record)
|
||||
|
||||
def dump_log(self, html=False):
|
||||
"""Dump the complete formatted log data as as string."""
|
||||
if html:
|
||||
fmt = self.html_formatter.format
|
||||
lines = ['<table>']
|
||||
else:
|
||||
fmt = self.format
|
||||
lines = []
|
||||
"""Dump the complete formatted log data as as string.
|
||||
|
||||
FIXME: We should do all the HTML formatter via jinja2.
|
||||
"""
|
||||
lines = []
|
||||
fmt = self.html_formatter.format if html else self.format
|
||||
self.acquire()
|
||||
try:
|
||||
records = list(self.data)
|
||||
@ -355,8 +354,6 @@ class RAMHandler(logging.Handler):
|
||||
self.release()
|
||||
for record in records:
|
||||
lines.append(fmt(record))
|
||||
if html:
|
||||
lines.append('</table>')
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
|
@ -48,25 +48,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/> or use
|
||||
:open qute:gpl.
|
||||
"""
|
||||
|
||||
GPL_BOILERPLATE_HTML = """
|
||||
<p>
|
||||
This program 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.
|
||||
</p>
|
||||
<p>
|
||||
This program 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.
|
||||
</p>
|
||||
<p>
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <a href="http://www.gnu.org/licenses/">
|
||||
http://www.gnu.org/licenses/</a> or open <a href="qute:gpl">qute:gpl</a>.
|
||||
"""
|
||||
|
||||
|
||||
def _git_str():
|
||||
"""Try to find out git version.
|
||||
@ -179,6 +160,13 @@ def _module_versions():
|
||||
else:
|
||||
ver = getattr(pypeg2, '__version__', 'yes')
|
||||
lines.append('pypeg2: {}'.format(ver))
|
||||
try:
|
||||
import jinja2
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
ver = getattr(jinja2, '__version__', 'yes')
|
||||
lines.append('jinja2: {}'.format(ver))
|
||||
return lines
|
||||
|
||||
|
||||
|
@ -42,7 +42,6 @@ class CompletionView(QTreeView):
|
||||
Highlights completions based on marks in the Role.marks data.
|
||||
|
||||
Class attributes:
|
||||
STYLESHEET: The stylesheet template for the CompletionView.
|
||||
COLUMN_WIDTHS: A list of column widths, in percent.
|
||||
|
||||
Attributes:
|
||||
@ -59,29 +58,31 @@ class CompletionView(QTreeView):
|
||||
# Drawing the item foreground will be done by CompletionItemDelegate, so we
|
||||
# don't define that in this stylesheet.
|
||||
STYLESHEET = """
|
||||
QTreeView {{
|
||||
{font[completion]}
|
||||
{color[completion.bg]}
|
||||
QTreeView {
|
||||
{{ font['completion'] }}
|
||||
{{ color['completion.bg'] }}
|
||||
outline: 0;
|
||||
}}
|
||||
}
|
||||
|
||||
QTreeView::item:disabled {{
|
||||
{color[completion.category.bg]}
|
||||
border-top: 1px solid {color[completion.category.border.top]};
|
||||
QTreeView::item:disabled {
|
||||
{{ color['completion.category.bg'] }}
|
||||
border-top: 1px solid
|
||||
{{ color['completion.category.border.top'] }};
|
||||
border-bottom: 1px solid
|
||||
{color[completion.category.border.bottom]};
|
||||
}}
|
||||
{{ color['completion.category.border.bottom'] }};
|
||||
}
|
||||
|
||||
QTreeView::item:selected, QTreeView::item:selected:hover {{
|
||||
border-top: 1px solid {color[completion.item.selected.border.top]};
|
||||
QTreeView::item:selected, QTreeView::item:selected:hover {
|
||||
border-top: 1px solid
|
||||
{{ color['completion.item.selected.border.top'] }};
|
||||
border-bottom: 1px solid
|
||||
{color[completion.item.selected.border.bottom]};
|
||||
{color[completion.item.selected.bg]}
|
||||
}}
|
||||
{{ color['completion.item.selected.border.bottom'] }};
|
||||
{{ color['completion.item.selected.bg'] }}
|
||||
}
|
||||
|
||||
QTreeView:item::hover {{
|
||||
QTreeView:item::hover {
|
||||
border: 0px;
|
||||
}}
|
||||
}
|
||||
"""
|
||||
COLUMN_WIDTHS = (20, 70, 10)
|
||||
|
||||
|
@ -189,9 +189,9 @@ class CompletionItemDelegate(QStyledItemDelegate):
|
||||
self._doc.setDefaultFont(self._opt.font)
|
||||
self._doc.setDefaultTextOption(text_option)
|
||||
self._doc.setDefaultStyleSheet(style.get_stylesheet("""
|
||||
.highlight {{
|
||||
{color[completion.match.fg]}
|
||||
}}
|
||||
.highlight {
|
||||
{{ color['completion.match.fg'] }}
|
||||
}
|
||||
"""))
|
||||
self._doc.setDocumentMargin(2)
|
||||
|
||||
|
@ -37,14 +37,14 @@ class DownloadView(QListView):
|
||||
"""
|
||||
|
||||
STYLESHEET = """
|
||||
QListView {{
|
||||
{color[downloads.bg.bar]}
|
||||
{font[downloads]}
|
||||
}}
|
||||
QListView {
|
||||
{{ color['downloads.bg.bar'] }}
|
||||
{{ font['downloads'] }}
|
||||
}
|
||||
|
||||
QListView::item {{
|
||||
QListView::item {
|
||||
padding-right: 2px;
|
||||
}}
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
|
@ -40,9 +40,6 @@ class StatusBar(QWidget):
|
||||
|
||||
"""The statusbar at the bottom of the mainwindow.
|
||||
|
||||
Class attributes:
|
||||
STYLESHEET: The stylesheet template.
|
||||
|
||||
Attributes:
|
||||
cmd: The Command widget in the statusbar.
|
||||
txt: The Text widget in the statusbar.
|
||||
@ -96,26 +93,26 @@ class StatusBar(QWidget):
|
||||
_insert_active = False
|
||||
|
||||
STYLESHEET = """
|
||||
QWidget#StatusBar {{
|
||||
{color[statusbar.bg]}
|
||||
}}
|
||||
QWidget#StatusBar {
|
||||
{{ color['statusbar.bg'] }}
|
||||
}
|
||||
|
||||
QWidget#StatusBar[insert_active="true"] {{
|
||||
{color[statusbar.bg.insert]}
|
||||
}}
|
||||
QWidget#StatusBar[insert_active="true"] {
|
||||
{{ color['statusbar.bg.insert'] }}
|
||||
}
|
||||
|
||||
QWidget#StatusBar[prompt_active="true"] {{
|
||||
{color[statusbar.bg.prompt]}
|
||||
}}
|
||||
QWidget#StatusBar[prompt_active="true"] {
|
||||
{{ color['statusbar.bg.prompt'] }}
|
||||
}
|
||||
|
||||
QWidget#StatusBar[error="true"] {{
|
||||
{color[statusbar.bg.error]}
|
||||
}}
|
||||
QWidget#StatusBar[error="true"] {
|
||||
{{ color['statusbar.bg.error'] }}
|
||||
}
|
||||
|
||||
QWidget {{
|
||||
{color[statusbar.fg]}
|
||||
{font[statusbar]}
|
||||
}}
|
||||
QWidget {
|
||||
{{ color['statusbar.fg'] }}
|
||||
{{ font['statusbar'] }}
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
|
@ -28,24 +28,20 @@ from qutebrowser.config import style
|
||||
|
||||
class Progress(QProgressBar):
|
||||
|
||||
"""The progress bar part of the status bar.
|
||||
|
||||
Class attributes:
|
||||
STYLESHEET: The stylesheet template.
|
||||
"""
|
||||
"""The progress bar part of the status bar."""
|
||||
|
||||
# FIXME for some reason, margin-left is not shown
|
||||
STYLESHEET = """
|
||||
QProgressBar {{
|
||||
QProgressBar {
|
||||
border-radius: 0px;
|
||||
border: 2px solid transparent;
|
||||
margin-left: 1px;
|
||||
background-color: transparent;
|
||||
}}
|
||||
}
|
||||
|
||||
QProgressBar::chunk {{
|
||||
{color[statusbar.progress.bg]}
|
||||
}}
|
||||
QProgressBar::chunk {
|
||||
{{ color['statusbar.progress.bg'] }}
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
|
@ -36,9 +36,6 @@ class UrlText(textbase.TextBase):
|
||||
|
||||
"""URL displayed in the statusbar.
|
||||
|
||||
Class attributes:
|
||||
STYLESHEET: The stylesheet template.
|
||||
|
||||
Attributes:
|
||||
normal_url: The normal URL to be displayed as a UrlType instance.
|
||||
normal_url_type: The type of the normal URL as a UrlType instance.
|
||||
@ -56,25 +53,25 @@ class UrlText(textbase.TextBase):
|
||||
_urltype = None
|
||||
|
||||
STYLESHEET = """
|
||||
QLabel#UrlText[urltype="normal"] {{
|
||||
{color[statusbar.url.fg]}
|
||||
}}
|
||||
QLabel#UrlText[urltype="normal"] {
|
||||
{{ color['statusbar.url.fg'] }}
|
||||
}
|
||||
|
||||
QLabel#UrlText[urltype="success"] {{
|
||||
{color[statusbar.url.fg.success]}
|
||||
}}
|
||||
QLabel#UrlText[urltype="success"] {
|
||||
{{ color['statusbar.url.fg.success'] }}
|
||||
}
|
||||
|
||||
QLabel#UrlText[urltype="error"] {{
|
||||
{color[statusbar.url.fg.error]}
|
||||
}}
|
||||
QLabel#UrlText[urltype="error"] {
|
||||
{{ color['statusbar.url.fg.error'] }}
|
||||
}
|
||||
|
||||
QLabel#UrlText[urltype="warn"] {{
|
||||
{color[statusbar.url.fg.warn]}
|
||||
}}
|
||||
QLabel#UrlText[urltype="warn"] {
|
||||
{{ color['statusbar.url.fg.warn'] }}
|
||||
}
|
||||
|
||||
QLabel#UrlText[urltype="hover"] {{
|
||||
{color[statusbar.url.fg.hover]}
|
||||
}}
|
||||
QLabel#UrlText[urltype="hover"] {
|
||||
{{ color['statusbar.url.fg.hover'] }}
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
|
@ -98,7 +98,7 @@ setupdata = {
|
||||
'description': _get_constant('description'),
|
||||
'long_description': read_file('README.asciidoc'),
|
||||
'url': 'http://www.qutebrowser.org/',
|
||||
'requires': ['pypeg2'],
|
||||
'requires': ['pypeg2', 'jinja2'],
|
||||
'author': _get_constant('author'),
|
||||
'author_email': _get_constant('email'),
|
||||
'license': _get_constant('license'),
|
||||
|
2
setup.py
2
setup.py
@ -47,7 +47,7 @@ try:
|
||||
['qutebrowser = qutebrowser.qutebrowser:main']},
|
||||
test_suite='qutebrowser.test',
|
||||
zip_safe=True,
|
||||
install_requires=['pypeg2'],
|
||||
install_requires=['pypeg2', 'jinja2'],
|
||||
extras_require={'nice-debugging': ['colorlog', 'colorama'],
|
||||
'checks': ['flake8', 'pylint', 'check-manifest',
|
||||
'pyroma']},
|
||||
|
Loading…
Reference in New Issue
Block a user