Merge branch 'ninja'

This commit is contained in:
Florian Bruhin 2014-08-29 07:33:43 +02:00
commit d059c468af
26 changed files with 359 additions and 321 deletions

3
.gitignore vendored
View File

@ -13,4 +13,5 @@ __pycache__
.ropeproject .ropeproject
# We can probably remove these later # We can probably remove these later
*.asciidoc *.asciidoc
*.html /doc/*.html
/README.html

View File

@ -74,6 +74,7 @@ The following software and libraries are required to run qutebrowser:
(5.3.1 recommended) for Python 3 (5.3.1 recommended) for Python 3
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools] * https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
* http://fdik.org/pyPEG/[pyPEG2] * http://fdik.org/pyPEG/[pyPEG2]
* http://jinja.pocoo.org/[jinja2]
The following libraries are optional and provide colored logging in the The following libraries are optional and provide colored logging in the
console: 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 # 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]. * Get pip as described http://stackoverflow.com/a/12476379[on Stack Overflow].
* Run +pip install --allow-external pypeg2 --allow-unverified pypeg2 pypeg2+ to * Run +pip install --allow-external pypeg2 --allow-unverified pypeg2 pypeg2+ to
install pypeg2. install pypeg2.
* Run +pip install jinja2+ to install jinja2.
As soon as v0.1 is out, a standalone .exe (built with As soon as v0.1 is out, a standalone .exe (built with
http://cx-freeze.sourceforge.net/[cx_Freeze]) will be provided. In the http://cx-freeze.sourceforge.net/[cx_Freeze]) will be provided. In the

View File

@ -9,7 +9,7 @@ arch=(any)
url="http://www.qutebrowser.org/" url="http://www.qutebrowser.org/"
license=('GPL') license=('GPL')
depends=('python>=3.4' 'python-setuptools' 'python-pyqt5>=5.2' 'qt5-base>=5.2' 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') makedepends=('python' 'python-setuptools')
optdepends=('python-colorlog: colored logging output') optdepends=('python-colorlog: colored logging output')
options=(!emptydirs) options=(!emptydirs)

View File

@ -394,7 +394,7 @@ class Application(QApplication):
status.prompt.prompter.ask_question, Qt.DirectConnection) status.prompt.prompter.ask_question, Qt.DirectConnection)
# config # 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, for obj in (tabs, completion, self.mainwindow, self.cmd_history,
websettings, kp[utypes.KeyMode.normal], self.modeman, websettings, kp[utypes.KeyMode.normal], self.modeman,
status, status.txt): status, status.txt):

View File

@ -19,10 +19,10 @@
"""The main browser widgets.""" """The main browser widgets."""
from functools import partial import functools
import sip 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.QtNetwork import QNetworkReply
from PyQt5.QtWidgets import QFileDialog from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtPrintSupport import QPrintDialog from PyQt5.QtPrintSupport import QPrintDialog
@ -30,7 +30,7 @@ from PyQt5.QtWebKitWidgets import QWebPage
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.network import networkmanager 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): class BrowserPage(QWebPage):
@ -118,7 +118,8 @@ class BrowserPage(QWebPage):
log.webview.debug("Error domain: {}, error code: {}".format( log.webview.debug("Error domain: {}, error code: {}".format(
info.domain, info.error)) info.domain, info.error))
title = "Error loading page: {}".format(urlstr) 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='') title=title, url=urlstr, error=info.errorString, icon='')
return True return True
@ -197,8 +198,8 @@ class BrowserPage(QWebPage):
if reply.isFinished(): if reply.isFinished():
self.display_content(reply, 'image/jpeg') self.display_content(reply, 'image/jpeg')
else: else:
reply.finished.connect( reply.finished.connect(functools.partial(
partial(self.display_content, reply, 'image/jpeg')) self.display_content, reply, 'image/jpeg'))
else: else:
# Unknown mimetype, so download anyways. # Unknown mimetype, so download anyways.
self.start_download.emit(reply) self.start_download.emit(reply)
@ -240,19 +241,16 @@ class BrowserPage(QWebPage):
log.webview.warning("Extension {} not supported!".format(ext)) log.webview.warning("Extension {} not supported!".format(ext))
return super().extension(ext, opt, out) return super().extension(ext, opt, out)
return handler(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 # 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 # for some reason.
# them when we're back in the mainloop.
# http://www.riverbankcomputing.com/pipermail/pyqt/2014-August/034722.html # http://www.riverbankcomputing.com/pipermail/pyqt/2014-August/034722.html
# #
# Note we somehow can't re-raise with the correct traceback here. # We used to re-raise the exception with a single-shot QTimer here,
# Using "raise from" or ".with_traceback()" just ignores the # but that lead to a strange proble with a KeyError with some
# exception again. # random jinja template stuff as content. For now, we only log it,
exc = e # needed for the closure # so it doesn't pass 100% silently.
def raise_(): log.webview.exception("Error inside WebPage::extension")
raise exc
QTimer.singleShot(0, raise_)
return False return False
def javaScriptAlert(self, _frame, msg): def javaScriptAlert(self, _frame, msg):

View File

@ -17,40 +17,31 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Utilities related to the look&feel of qutebrowser. """Utilities related to the look&feel of qutebrowser."""
Module attributes:
_colordict: The global cached ColorDict.
_fontdict: The global cached FontDict.
"""
import functools import functools
import jinja2
from PyQt5.QtGui import QColor from PyQt5.QtGui import QColor
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.utils import log, utils from qutebrowser.utils import log, utils
_colordict = None @functools.lru_cache(maxsize=16)
_fontdict = None def get_stylesheet(template_str):
def get_stylesheet(template):
"""Format a stylesheet based on a template. """Format a stylesheet based on a template.
Args: Args:
template: The stylesheet template as string. template_str: The stylesheet template as string.
Return: Return:
The formatted template as string. The formatted template as string.
""" """
global _colordict, _fontdict colordict = ColorDict(config.section('colors'))
if _colordict is None: fontdict = FontDict(config.section('fonts'))
_colordict = ColorDict(config.section('colors')) template = jinja2.Template(template_str)
if _fontdict is None: return template.render(color=colordict, font=fontdict,
_fontdict = FontDict(config.section('fonts'))
return template.strip().format(color=_colordict, font=_fontdict,
config=config.instance()) config=config.instance())
@ -78,15 +69,6 @@ def _update_stylesheet(obj, _section, _option):
obj.setStyleSheet(get_stylesheet(obj.STYLESHEET)) 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): class ColorDict(dict):
"""A dict aimed at Qt stylesheet colors.""" """A dict aimed at Qt stylesheet colors."""
@ -123,20 +105,6 @@ class ColorDict(dict):
else: else:
return val 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): class FontDict(dict):
@ -160,17 +128,3 @@ class FontDict(dict):
return '' return ''
else: else:
return 'font: {};'.format(val) 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

View 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>

View File

@ -1,22 +1,7 @@
<!DOCTYPE html> {% extends "base.html" %}
<!-- {% block style %}
vim: ft=html fileencoding=utf-8 sts=4 sw=4 et: {{ super() }}
Based on html/error.html from dwb #errorContainer {
-->
<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;
}}
#errorContainer {{
background: #fff; background: #fff;
min-width: 35em; min-width: 35em;
max-width: 35em; max-width: 35em;
@ -26,35 +11,37 @@ Based on html/error.html from dwb
padding: 10px; padding: 10px;
border: 2px solid #eee; border: 2px solid #eee;
-webkit-border-radius: 5px; -webkit-border-radius: 5px;
}} }
#errorTitleText {{ #errorTitleText {
font-size: 118%; font-size: 118%;
font-weight: bold; font-weight: bold;
}} }
#errorMessageText {{ #errorMessageText {
font-size: 80%; font-size: 80%;
}} }
</style> {% endblock %}
<script type="text/javascript">
{% block script %}
{{ super() }}
function tryagain() function tryagain()
{{ {
location.reload(); location.reload();
}} }
function searchFor(uri) {{ function searchFor(uri) {
location.href = uri; location.href = uri;
}} }
</script> {% endblock %}
</head>
<body> {% block content %}
<div id="errorContainer"> <div id="errorContainer">
<div id="errorTitle"> <div id="errorTitle">
<p id="errorTitleText">Unable to load page</p> <p id="errorTitleText">Unable to load page</p>
</div> </div>
<div id="errorMessage"> <div id="errorMessage">
<p>Problem occurred while loading the URL {url}</p> <p>Problem occurred while loading the URL {{ url }}</p>
<p id="errorMessageText">{error}</p> <p id="errorMessageText">{{ error }}</p>
</p> </p>
</div> </div>
@ -63,5 +50,4 @@ Based on html/error.html from dwb
<!--<input type="button" value="Search" style="visibility:%s" onclick="javascript:searchFor('%s')" />--> <!--<input type="button" value="Search" style="visibility:%s" onclick="javascript:searchFor('%s')" />-->
</form> </form>
</div> </div>
</body> {% endblock %}
</html>

35
qutebrowser/html/log.html Normal file
View 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 %}

View File

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block content %}
{{ super() }}
<pre>
{{ content }}
</pre>
{% endblock %}

View 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 %}

View File

@ -16,60 +16,28 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. # 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. """Handler functions for different qute:... pages.
Module attributes: Module attributes:
_HTML_TEMPLATE: The HTML boilerplate used to convert text into html.
pyeval_output: The output of the last :pyeval command. pyeval_output: The output of the last :pyeval command.
""" """
import html as pyhtml
from PyQt5.QtNetwork import QNetworkReply from PyQt5.QtNetwork import QNetworkReply
import qutebrowser import qutebrowser
from qutebrowser.network import schemehandler 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 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" 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): class QuteSchemeHandler(schemehandler.SchemeHandler):
"""Scheme handler for qute: URLs.""" """Scheme handler for qute: URLs."""
@ -110,61 +78,39 @@ class QuteHandlers:
@classmethod @classmethod
def pyeval(cls): def pyeval(cls):
"""Handler for qute:pyeval. Return HTML content as bytes.""" """Handler for qute:pyeval. Return HTML content as bytes."""
text = pyhtml.escape(pyeval_output) html = jinja.env.get_template('pre.html').render(
return _get_html('pyeval', '<pre>{}</pre>'.format(text)) title='pyeval', content=pyeval_output)
return html.encode('UTF-8', errors='xmlcharrefreplace')
@classmethod @classmethod
def version(cls): def version(cls):
"""Handler for qute:version. Return HTML content as bytes.""" """Handler for qute:version. Return HTML content as bytes."""
text = pyhtml.escape(version.version()) html = jinja.env.get_template('version.html').render(
html = '<h1>Version info</h1>' title='Version info', version=version.version(),
html += '<p>{}</p>'.format(text.replace('\n', '<br/>')) copyright=qutebrowser.__copyright__)
html += '<h1>Copyright info</h1>' return html.encode('UTF-8', errors='xmlcharrefreplace')
html += '<p>{}</p>'.format(qutebrowser.__copyright__)
html += version.GPL_BOILERPLATE_HTML
return _get_html('Version', html)
@classmethod @classmethod
def plainlog(cls): 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: if logutils.ram_handler is None:
text = "Log output was disabled." text = "Log output was disabled."
else: else:
text = pyhtml.escape(logutils.ram_handler.dump_log()) text = logutils.ram_handler.dump_log()
return _get_html('log', '<pre>{}</pre>'.format(text)) html = jinja.env.get_template('pre.html').render(
title='log', content=text)
return html.encode('UTF-8', errors='xmlcharrefreplace')
@classmethod @classmethod
def log(cls): def log(cls):
"""Handler for qute:log. Return HTML content as bytes.""" """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: if logutils.ram_handler is None:
html = "<p>Log output was disabled.</p>" html_log = None
else: else:
html = logutils.ram_handler.dump_log(html=True) html_log = logutils.ram_handler.dump_log(html=True)
return _get_html('log', html, head=style) html = jinja.env.get_template('log.html').render(
title='log', content=html_log)
return html.encode('UTF-8', errors='xmlcharrefreplace')
@classmethod @classmethod
def gpl(cls): def gpl(cls):

View File

@ -112,6 +112,7 @@ def main():
earlyinit.check_pyqt_webkit() earlyinit.check_pyqt_webkit()
earlyinit.check_pkg_resources() earlyinit.check_pkg_resources()
earlyinit.check_pypeg2() earlyinit.check_pypeg2()
earlyinit.check_jinja2()
# We do this import late as we need to fix harfbuzz first. # We do this import late as we need to fix harfbuzz first.
from qutebrowser import app from qutebrowser import app
from qutebrowser.utils import debug from qutebrowser.utils import debug

View File

@ -29,7 +29,7 @@ from PyQt5.QtCore import pyqtRemoveInputHook, QEvent, QCoreApplication
from qutebrowser.utils import log, utils from qutebrowser.utils import log, utils
from qutebrowser.commands import cmdutils from qutebrowser.commands import cmdutils
from qutebrowser.config import config from qutebrowser.config import config, style
@cmdutils.register(debug=True, name='debug-set-trace') @cmdutils.register(debug=True, name='debug-set-trace')
@ -87,9 +87,11 @@ def debug_all_objects():
@cmdutils.register(debug=True) @cmdutils.register(debug=True)
def debug_cache_stats(): def debug_cache_stats():
"""Print config LRU cache stats.""" """Print LRU cache stats."""
info = config.instance().get.cache_info() config_info = config.instance().get.cache_info()
log.misc.debug(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): def log_events(klass):

View File

@ -248,3 +248,17 @@ def check_pypeg2():
pip="pypeg2 --allow-external pypeg2 " pip="pypeg2 --allow-external pypeg2 "
"--allow-unverified pypeg2") "--allow-unverified pypeg2")
_die(text) _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)

View 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)

View File

@ -341,13 +341,12 @@ class RAMHandler(logging.Handler):
self.data.append(record) self.data.append(record)
def dump_log(self, html=False): def dump_log(self, html=False):
"""Dump the complete formatted log data as as string.""" """Dump the complete formatted log data as as string.
if html:
fmt = self.html_formatter.format FIXME: We should do all the HTML formatter via jinja2.
lines = ['<table>'] """
else:
fmt = self.format
lines = [] lines = []
fmt = self.html_formatter.format if html else self.format
self.acquire() self.acquire()
try: try:
records = list(self.data) records = list(self.data)
@ -355,8 +354,6 @@ class RAMHandler(logging.Handler):
self.release() self.release()
for record in records: for record in records:
lines.append(fmt(record)) lines.append(fmt(record))
if html:
lines.append('</table>')
return '\n'.join(lines) return '\n'.join(lines)

View File

@ -48,25 +48,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/> or use
:open qute:gpl. :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(): def _git_str():
"""Try to find out git version. """Try to find out git version.
@ -179,6 +160,13 @@ def _module_versions():
else: else:
ver = getattr(pypeg2, '__version__', 'yes') ver = getattr(pypeg2, '__version__', 'yes')
lines.append('pypeg2: {}'.format(ver)) lines.append('pypeg2: {}'.format(ver))
try:
import jinja2
except ImportError:
pass
else:
ver = getattr(jinja2, '__version__', 'yes')
lines.append('jinja2: {}'.format(ver))
return lines return lines

View File

@ -42,7 +42,6 @@ class CompletionView(QTreeView):
Highlights completions based on marks in the Role.marks data. Highlights completions based on marks in the Role.marks data.
Class attributes: Class attributes:
STYLESHEET: The stylesheet template for the CompletionView.
COLUMN_WIDTHS: A list of column widths, in percent. COLUMN_WIDTHS: A list of column widths, in percent.
Attributes: Attributes:
@ -59,29 +58,31 @@ class CompletionView(QTreeView):
# Drawing the item foreground will be done by CompletionItemDelegate, so we # Drawing the item foreground will be done by CompletionItemDelegate, so we
# don't define that in this stylesheet. # don't define that in this stylesheet.
STYLESHEET = """ STYLESHEET = """
QTreeView {{ QTreeView {
{font[completion]} {{ font['completion'] }}
{color[completion.bg]} {{ color['completion.bg'] }}
outline: 0; outline: 0;
}} }
QTreeView::item:disabled {{ QTreeView::item:disabled {
{color[completion.category.bg]} {{ color['completion.category.bg'] }}
border-top: 1px solid {color[completion.category.border.top]}; border-top: 1px solid
{{ color['completion.category.border.top'] }};
border-bottom: 1px solid border-bottom: 1px solid
{color[completion.category.border.bottom]}; {{ color['completion.category.border.bottom'] }};
}} }
QTreeView::item:selected, QTreeView::item:selected:hover {{ QTreeView::item:selected, QTreeView::item:selected:hover {
border-top: 1px solid {color[completion.item.selected.border.top]}; border-top: 1px solid
{{ color['completion.item.selected.border.top'] }};
border-bottom: 1px solid border-bottom: 1px solid
{color[completion.item.selected.border.bottom]}; {{ color['completion.item.selected.border.bottom'] }};
{color[completion.item.selected.bg]} {{ color['completion.item.selected.bg'] }}
}} }
QTreeView:item::hover {{ QTreeView:item::hover {
border: 0px; border: 0px;
}} }
""" """
COLUMN_WIDTHS = (20, 70, 10) COLUMN_WIDTHS = (20, 70, 10)

View File

@ -189,9 +189,9 @@ class CompletionItemDelegate(QStyledItemDelegate):
self._doc.setDefaultFont(self._opt.font) self._doc.setDefaultFont(self._opt.font)
self._doc.setDefaultTextOption(text_option) self._doc.setDefaultTextOption(text_option)
self._doc.setDefaultStyleSheet(style.get_stylesheet(""" self._doc.setDefaultStyleSheet(style.get_stylesheet("""
.highlight {{ .highlight {
{color[completion.match.fg]} {{ color['completion.match.fg'] }}
}} }
""")) """))
self._doc.setDocumentMargin(2) self._doc.setDocumentMargin(2)

View File

@ -37,14 +37,14 @@ class DownloadView(QListView):
""" """
STYLESHEET = """ STYLESHEET = """
QListView {{ QListView {
{color[downloads.bg.bar]} {{ color['downloads.bg.bar'] }}
{font[downloads]} {{ font['downloads'] }}
}} }
QListView::item {{ QListView::item {
padding-right: 2px; padding-right: 2px;
}} }
""" """
def __init__(self, parent=None): def __init__(self, parent=None):

View File

@ -40,9 +40,6 @@ class StatusBar(QWidget):
"""The statusbar at the bottom of the mainwindow. """The statusbar at the bottom of the mainwindow.
Class attributes:
STYLESHEET: The stylesheet template.
Attributes: Attributes:
cmd: The Command widget in the statusbar. cmd: The Command widget in the statusbar.
txt: The Text widget in the statusbar. txt: The Text widget in the statusbar.
@ -96,26 +93,26 @@ class StatusBar(QWidget):
_insert_active = False _insert_active = False
STYLESHEET = """ STYLESHEET = """
QWidget#StatusBar {{ QWidget#StatusBar {
{color[statusbar.bg]} {{ color['statusbar.bg'] }}
}} }
QWidget#StatusBar[insert_active="true"] {{ QWidget#StatusBar[insert_active="true"] {
{color[statusbar.bg.insert]} {{ color['statusbar.bg.insert'] }}
}} }
QWidget#StatusBar[prompt_active="true"] {{ QWidget#StatusBar[prompt_active="true"] {
{color[statusbar.bg.prompt]} {{ color['statusbar.bg.prompt'] }}
}} }
QWidget#StatusBar[error="true"] {{ QWidget#StatusBar[error="true"] {
{color[statusbar.bg.error]} {{ color['statusbar.bg.error'] }}
}} }
QWidget {{ QWidget {
{color[statusbar.fg]} {{ color['statusbar.fg'] }}
{font[statusbar]} {{ font['statusbar'] }}
}} }
""" """
def __init__(self, parent=None): def __init__(self, parent=None):

View File

@ -28,24 +28,20 @@ from qutebrowser.config import style
class Progress(QProgressBar): class Progress(QProgressBar):
"""The progress bar part of the status bar. """The progress bar part of the status bar."""
Class attributes:
STYLESHEET: The stylesheet template.
"""
# FIXME for some reason, margin-left is not shown # FIXME for some reason, margin-left is not shown
STYLESHEET = """ STYLESHEET = """
QProgressBar {{ QProgressBar {
border-radius: 0px; border-radius: 0px;
border: 2px solid transparent; border: 2px solid transparent;
margin-left: 1px; margin-left: 1px;
background-color: transparent; background-color: transparent;
}} }
QProgressBar::chunk {{ QProgressBar::chunk {
{color[statusbar.progress.bg]} {{ color['statusbar.progress.bg'] }}
}} }
""" """
def __init__(self, parent=None): def __init__(self, parent=None):

View File

@ -36,9 +36,6 @@ class UrlText(textbase.TextBase):
"""URL displayed in the statusbar. """URL displayed in the statusbar.
Class attributes:
STYLESHEET: The stylesheet template.
Attributes: Attributes:
normal_url: The normal URL to be displayed as a UrlType instance. 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. normal_url_type: The type of the normal URL as a UrlType instance.
@ -56,25 +53,25 @@ class UrlText(textbase.TextBase):
_urltype = None _urltype = None
STYLESHEET = """ STYLESHEET = """
QLabel#UrlText[urltype="normal"] {{ QLabel#UrlText[urltype="normal"] {
{color[statusbar.url.fg]} {{ color['statusbar.url.fg'] }}
}} }
QLabel#UrlText[urltype="success"] {{ QLabel#UrlText[urltype="success"] {
{color[statusbar.url.fg.success]} {{ color['statusbar.url.fg.success'] }}
}} }
QLabel#UrlText[urltype="error"] {{ QLabel#UrlText[urltype="error"] {
{color[statusbar.url.fg.error]} {{ color['statusbar.url.fg.error'] }}
}} }
QLabel#UrlText[urltype="warn"] {{ QLabel#UrlText[urltype="warn"] {
{color[statusbar.url.fg.warn]} {{ color['statusbar.url.fg.warn'] }}
}} }
QLabel#UrlText[urltype="hover"] {{ QLabel#UrlText[urltype="hover"] {
{color[statusbar.url.fg.hover]} {{ color['statusbar.url.fg.hover'] }}
}} }
""" """
def __init__(self, parent=None): def __init__(self, parent=None):

View File

@ -98,7 +98,7 @@ setupdata = {
'description': _get_constant('description'), 'description': _get_constant('description'),
'long_description': read_file('README.asciidoc'), 'long_description': read_file('README.asciidoc'),
'url': 'http://www.qutebrowser.org/', 'url': 'http://www.qutebrowser.org/',
'requires': ['pypeg2'], 'requires': ['pypeg2', 'jinja2'],
'author': _get_constant('author'), 'author': _get_constant('author'),
'author_email': _get_constant('email'), 'author_email': _get_constant('email'),
'license': _get_constant('license'), 'license': _get_constant('license'),

View File

@ -47,7 +47,7 @@ try:
['qutebrowser = qutebrowser.qutebrowser:main']}, ['qutebrowser = qutebrowser.qutebrowser:main']},
test_suite='qutebrowser.test', test_suite='qutebrowser.test',
zip_safe=True, zip_safe=True,
install_requires=['pypeg2'], install_requires=['pypeg2', 'jinja2'],
extras_require={'nice-debugging': ['colorlog', 'colorama'], extras_require={'nice-debugging': ['colorlog', 'colorama'],
'checks': ['flake8', 'pylint', 'check-manifest', 'checks': ['flake8', 'pylint', 'check-manifest',
'pyroma']}, 'pyroma']},