Add colors to DownloadView
This commit is contained in:
parent
3c2c08f73a
commit
2ffc9bb00a
@ -26,6 +26,7 @@ import qutebrowser.config.config as config
|
|||||||
import qutebrowser.utils.message as message
|
import qutebrowser.utils.message as message
|
||||||
from qutebrowser.utils.log import downloads as logger
|
from qutebrowser.utils.log import downloads as logger
|
||||||
from qutebrowser.utils.usertypes import PromptMode
|
from qutebrowser.utils.usertypes import PromptMode
|
||||||
|
from qutebrowser.utils.misc import interpolate_color
|
||||||
|
|
||||||
|
|
||||||
class DownloadItem(QObject):
|
class DownloadItem(QObject):
|
||||||
@ -111,6 +112,16 @@ class DownloadItem(QObject):
|
|||||||
else:
|
else:
|
||||||
return 100 * self.bytes_done / self.bytes_total
|
return 100 * self.bytes_done / self.bytes_total
|
||||||
|
|
||||||
|
def bg_color(self):
|
||||||
|
"""Background color to be shown."""
|
||||||
|
start = config.get('colors', 'download.bg.start')
|
||||||
|
stop = config.get('colors', 'download.bg.stop')
|
||||||
|
system = config.get('colors', 'download.bg.system')
|
||||||
|
if self.percentage is None:
|
||||||
|
return start
|
||||||
|
else:
|
||||||
|
return interpolate_color(start, stop, self.percentage, system)
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
"""Cancel the download."""
|
"""Cancel the download."""
|
||||||
logger.debug("cancelled")
|
logger.debug("cancelled")
|
||||||
|
@ -894,6 +894,22 @@ DATA = OrderedDict([
|
|||||||
'left bottom, color-stop(0%,#FFF785), '
|
'left bottom, color-stop(0%,#FFF785), '
|
||||||
'color-stop(100%,#FFC542))'),
|
'color-stop(100%,#FFC542))'),
|
||||||
"Background color for hints."),
|
"Background color for hints."),
|
||||||
|
|
||||||
|
('download.fg',
|
||||||
|
SettingValue(types.QtColor(), '#ffffff'),
|
||||||
|
"Foreground color for downloads."),
|
||||||
|
|
||||||
|
('download.bg.start',
|
||||||
|
SettingValue(types.QtColor(), '#0000aa'),
|
||||||
|
"Color gradient start for downloads."),
|
||||||
|
|
||||||
|
('download.bg.stop',
|
||||||
|
SettingValue(types.QtColor(), '#00aa00'),
|
||||||
|
"Color gradient end for downloads."),
|
||||||
|
|
||||||
|
('download.bg.system',
|
||||||
|
SettingValue(types.ColorSystem(), 'rgb'),
|
||||||
|
"Color gradient interpolation system for downloads."),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
('fonts', sect.KeyValue(
|
('fonts', sect.KeyValue(
|
||||||
|
@ -434,6 +434,42 @@ class Command(BaseType):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
class ColorSystem(BaseType):
|
||||||
|
|
||||||
|
"""Color systems for interpolation."""
|
||||||
|
|
||||||
|
valid_values = ValidValues(('rgb', "Interpolate in the RGB color system."),
|
||||||
|
('hsv', "Interpolate in the HSV color system."),
|
||||||
|
('hsl', "Interpolate in the HSV color system."))
|
||||||
|
|
||||||
|
def validate(self, value):
|
||||||
|
super().validate(value.lower())
|
||||||
|
|
||||||
|
def transform(self, value):
|
||||||
|
mapping = {
|
||||||
|
'rgb': QColor.Rgb,
|
||||||
|
'hsv': QColor.Hsv,
|
||||||
|
'hsl': QColor.Hsl,
|
||||||
|
}
|
||||||
|
return mapping[value.lower()]
|
||||||
|
|
||||||
|
|
||||||
|
class QtColor(BaseType):
|
||||||
|
|
||||||
|
"""Base class for QColor."""
|
||||||
|
|
||||||
|
typestr = 'qcolor'
|
||||||
|
|
||||||
|
def validate(self, value):
|
||||||
|
if QColor.isValidColor(value):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise ValidationError(value, "must be a valid color")
|
||||||
|
|
||||||
|
def transform(self, value):
|
||||||
|
return QColor(value)
|
||||||
|
|
||||||
|
|
||||||
class CssColor(BaseType):
|
class CssColor(BaseType):
|
||||||
|
|
||||||
"""Base class for a CSS color value."""
|
"""Base class for a CSS color value."""
|
||||||
|
@ -21,6 +21,8 @@ from PyQt5.QtCore import (pyqtSlot, Qt, QVariant, QAbstractListModel,
|
|||||||
QModelIndex)
|
QModelIndex)
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
|
||||||
|
import qutebrowser.config.config as config
|
||||||
|
|
||||||
|
|
||||||
class DownloadModel(QAbstractListModel):
|
class DownloadModel(QAbstractListModel):
|
||||||
|
|
||||||
@ -59,19 +61,26 @@ class DownloadModel(QAbstractListModel):
|
|||||||
"""Download data from DownloadManager."""
|
"""Download data from DownloadManager."""
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return QVariant()
|
return QVariant()
|
||||||
elif role != Qt.DisplayRole:
|
|
||||||
return QVariant()
|
|
||||||
elif index.parent().isValid() or index.column() != 0:
|
elif index.parent().isValid() or index.column() != 0:
|
||||||
return QVariant()
|
return QVariant()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item = self.downloadmanager.downloads[index.row()]
|
item = self.downloadmanager.downloads[index.row()]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return QVariant()
|
return QVariant()
|
||||||
perc = item.percentage
|
if role == Qt.DisplayRole:
|
||||||
if perc is None:
|
perc = item.percentage
|
||||||
return QVariant()
|
if perc is None:
|
||||||
|
data = QVariant()
|
||||||
|
else:
|
||||||
|
data = str(round(perc)) # FIXME
|
||||||
|
elif role == Qt.ForegroundRole:
|
||||||
|
data = config.get('colors', 'download.fg')
|
||||||
|
elif role == Qt.BackgroundRole:
|
||||||
|
data = item.bg_color()
|
||||||
else:
|
else:
|
||||||
return str(round(perc)) # FIXME
|
data = QVariant()
|
||||||
|
return data
|
||||||
|
|
||||||
def rowCount(self, parent):
|
def rowCount(self, parent):
|
||||||
"""Get count of active downloads."""
|
"""Get count of active downloads."""
|
||||||
|
@ -30,6 +30,7 @@ from tempfile import mkdtemp
|
|||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from PyQt5.QtCore import QStandardPaths, QCoreApplication
|
from PyQt5.QtCore import QStandardPaths, QCoreApplication
|
||||||
|
from PyQt5.QtGui import QColor
|
||||||
|
|
||||||
import qutebrowser.utils.misc as utils
|
import qutebrowser.utils.misc as utils
|
||||||
from qutebrowser.test.helpers import environ_set_temp
|
from qutebrowser.test.helpers import environ_set_temp
|
||||||
@ -436,5 +437,93 @@ class GetQtArgsTests(TestCase):
|
|||||||
self.assertEqual(utils.get_qt_args(ns), [sys.argv[0]])
|
self.assertEqual(utils.get_qt_args(ns), [sys.argv[0]])
|
||||||
|
|
||||||
|
|
||||||
|
class InterpolateColorTests(TestCase):
|
||||||
|
|
||||||
|
"""Tests for interpolate_color.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
white: The QColor white as a valid QColor for tests.
|
||||||
|
white: The QColor black as a valid QColor for tests.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.white = QColor('white')
|
||||||
|
self.black = QColor('black')
|
||||||
|
|
||||||
|
def test_invalid_start(self):
|
||||||
|
"""Test an invalid start color."""
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
utils.interpolate_color(QColor(), self.white, 0)
|
||||||
|
|
||||||
|
def test_invalid_end(self):
|
||||||
|
"""Test an invalid end color."""
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
utils.interpolate_color(self.white, QColor(), 0)
|
||||||
|
|
||||||
|
def test_invalid_percentage(self):
|
||||||
|
"""Test an invalid percentage."""
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
utils.interpolate_color(self.white, self.white, -1)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
utils.interpolate_color(self.white, self.white, 101)
|
||||||
|
|
||||||
|
def test_invalid_colorspace(self):
|
||||||
|
"""Test an invalid colorspace."""
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
utils.interpolate_color(self.white, self.black, 10, QColor.Cmyk)
|
||||||
|
|
||||||
|
def test_valid_percentages_rgb(self):
|
||||||
|
"""Test 0% and 100% in the RGB colorspace."""
|
||||||
|
white = utils.interpolate_color(self.white, self.black, 0, QColor.Rgb)
|
||||||
|
black = utils.interpolate_color(self.white, self.black, 100,
|
||||||
|
QColor.Rgb)
|
||||||
|
self.assertEqual(white, self.white)
|
||||||
|
self.assertEqual(black, self.black)
|
||||||
|
|
||||||
|
def test_valid_percentages_hsv(self):
|
||||||
|
"""Test 0% and 100% in the HSV colorspace."""
|
||||||
|
white = utils.interpolate_color(self.white, self.black, 0, QColor.Hsv)
|
||||||
|
black = utils.interpolate_color(self.white, self.black, 100,
|
||||||
|
QColor.Hsv)
|
||||||
|
self.assertEqual(white, self.white)
|
||||||
|
self.assertEqual(black, self.black)
|
||||||
|
|
||||||
|
def test_valid_percentages_hsl(self):
|
||||||
|
"""Test 0% and 100% in the HSL colorspace."""
|
||||||
|
white = utils.interpolate_color(self.white, self.black, 0, QColor.Hsl)
|
||||||
|
black = utils.interpolate_color(self.white, self.black, 100,
|
||||||
|
QColor.Hsl)
|
||||||
|
self.assertEqual(white, self.white)
|
||||||
|
self.assertEqual(black, self.black)
|
||||||
|
|
||||||
|
def test_interpolation_rgb(self):
|
||||||
|
"""Test an interpolation in the RGB colorspace."""
|
||||||
|
color = utils.interpolate_color(QColor(0, 40, 100), QColor(0, 20, 200),
|
||||||
|
50, QColor.Rgb)
|
||||||
|
self.assertEqual(color, QColor(0, 30, 150))
|
||||||
|
|
||||||
|
def test_interpolation_hsv(self):
|
||||||
|
"""Test an interpolation in the HSV colorspace."""
|
||||||
|
start = QColor()
|
||||||
|
stop = QColor()
|
||||||
|
start.setHsv(0, 40, 100)
|
||||||
|
stop.setHsv(0, 20, 200)
|
||||||
|
color = utils.interpolate_color(start, stop, 50, QColor.Hsv)
|
||||||
|
expected = QColor()
|
||||||
|
expected.setHsv(0, 30, 150)
|
||||||
|
self.assertEqual(color, expected)
|
||||||
|
|
||||||
|
def test_interpolation_hsl(self):
|
||||||
|
"""Test an interpolation in the HSL colorspace."""
|
||||||
|
start = QColor()
|
||||||
|
stop = QColor()
|
||||||
|
start.setHsl(0, 40, 100)
|
||||||
|
stop.setHsl(0, 20, 200)
|
||||||
|
color = utils.interpolate_color(start, stop, 50, QColor.Hsl)
|
||||||
|
expected = QColor()
|
||||||
|
expected.setHsl(0, 30, 150)
|
||||||
|
self.assertEqual(color, expected)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -33,6 +33,7 @@ from functools import reduce
|
|||||||
from distutils.version import StrictVersion as Version
|
from distutils.version import StrictVersion as Version
|
||||||
|
|
||||||
from PyQt5.QtCore import QCoreApplication, QStandardPaths, qVersion
|
from PyQt5.QtCore import QCoreApplication, QStandardPaths, qVersion
|
||||||
|
from PyQt5.QtGui import QColor
|
||||||
from pkg_resources import resource_string
|
from pkg_resources import resource_string
|
||||||
|
|
||||||
import qutebrowser
|
import qutebrowser
|
||||||
@ -285,3 +286,71 @@ def get_qt_args(namespace):
|
|||||||
argv.append('-' + argname)
|
argv.append('-' + argname)
|
||||||
argv.append(val[0])
|
argv.append(val[0])
|
||||||
return argv
|
return argv
|
||||||
|
|
||||||
|
|
||||||
|
def _get_color_percentage(a_c1, a_c2, a_c3, b_c1, b_c2, b_c3, percent):
|
||||||
|
"""Get a color which is percent% interpolated between start and end.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
a_c1, a_c2, a_c3: Start color components (R, G, B / H, S, V / H, S, L)
|
||||||
|
b_c1, b_c2, b_c3: End color components (R, G, B / H, S, V / H, S, L)
|
||||||
|
percent: Percentage to interpolate, 0-100.
|
||||||
|
0: Start color will be returned.
|
||||||
|
100: End color will be returned.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
A (c1, c2, c3) tuple with the interpolated color components.
|
||||||
|
|
||||||
|
Raise:
|
||||||
|
ValueError if the percentage was invalid.
|
||||||
|
"""
|
||||||
|
if not 0 <= percent <= 100:
|
||||||
|
raise ValueError("percent needs to be between 0 and 100!")
|
||||||
|
out_c1 = round(a_c1 + (b_c1 - a_c1) * percent / 100)
|
||||||
|
out_c2 = round(a_c2 + (b_c2 - a_c2) * percent / 100)
|
||||||
|
out_c3 = round(a_c3 + (b_c3 - a_c3) * percent / 100)
|
||||||
|
return (out_c1, out_c2, out_c3)
|
||||||
|
|
||||||
|
|
||||||
|
def interpolate_color(start, end, percent, colorspace=QColor.Rgb):
|
||||||
|
"""Get an interpolated color value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
start: The start color.
|
||||||
|
end: The end color.
|
||||||
|
percent: Which value to get (0 - 100)
|
||||||
|
colorspace: The desired interpolation colorsystem,
|
||||||
|
QColor::{Rgb,Hsv,Hsl} (from QColor::Spec enum)
|
||||||
|
|
||||||
|
Return:
|
||||||
|
The interpolated QColor, with the same spec as the given start color.
|
||||||
|
|
||||||
|
Raise:
|
||||||
|
ValueError if invalid parameters are passed.
|
||||||
|
"""
|
||||||
|
if not start.isValid():
|
||||||
|
raise ValueError("Invalid start color")
|
||||||
|
if not end.isValid():
|
||||||
|
raise ValueError("Invalid end color")
|
||||||
|
out = QColor()
|
||||||
|
if colorspace == QColor.Rgb:
|
||||||
|
a_c1, a_c2, a_c3, _alpha = start.getRgb()
|
||||||
|
b_c1, b_c2, b_c3, _alpha = end.getRgb()
|
||||||
|
components = _get_color_percentage(a_c1, a_c2, a_c3, b_c1, b_c2, b_c3,
|
||||||
|
percent)
|
||||||
|
out.setRgb(*components)
|
||||||
|
elif colorspace == QColor.Hsv:
|
||||||
|
a_c1, a_c2, a_c3, _alpha = start.getHsv()
|
||||||
|
b_c1, b_c2, b_c3, _alpha = end.getHsv()
|
||||||
|
components = _get_color_percentage(a_c1, a_c2, a_c3, b_c1, b_c2, b_c3,
|
||||||
|
percent)
|
||||||
|
out.setHsv(*components)
|
||||||
|
elif colorspace == QColor.Hsl:
|
||||||
|
a_c1, a_c2, a_c3, _alpha = start.getHsl()
|
||||||
|
b_c1, b_c2, b_c3, _alpha = end.getHsl()
|
||||||
|
components = _get_color_percentage(a_c1, a_c2, a_c3, b_c1, b_c2, b_c3,
|
||||||
|
percent)
|
||||||
|
out.setHsl(*components)
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid colorspace!")
|
||||||
|
return out.convertTo(start.spec())
|
||||||
|
Loading…
Reference in New Issue
Block a user