# -*- coding: utf-8 -*-
#
# Licensed under the terms of the Qwt License
# Copyright (c) 2002 Uwe Rathmann, for the original C++ code
# Copyright (c) 2015 Pierre Raybaut, for the Python translation/optimization
# (see LICENSE file for more details)
"""
Color maps
----------
QwtColorMap
~~~~~~~~~~~
.. autoclass:: QwtColorMap
:members:
QwtLinearColorMap
~~~~~~~~~~~~~~~~~
.. autoclass:: QwtLinearColorMap
:members:
QwtAlphaColorMap
~~~~~~~~~~~~~~~~
.. autoclass:: QwtAlphaColorMap
:members:
"""
from qwt.qt.QtGui import QColor, qRed, qGreen, qBlue, qRgb, qRgba, qAlpha
from qwt.qt.QtCore import Qt, qIsNaN
class ColorStop(object):
def __init__(self, pos=0., color=None):
self.pos = pos
if color is None:
self.rgb = 0
else:
self.rgb = color.rgba()
self.r = qRed(self.rgb)
self.g = qGreen(self.rgb)
self.b = qBlue(self.rgb)
self.a = qAlpha(self.rgb)
# when mapping a value to rgb we will have to calcualate:
# - const int v = int( ( s1.v0 + ratio * s1.vStep ) + 0.5 );
# Thus adding 0.5 ( for rounding ) can be done in advance
self.r0 = self.r + 0.5
self.g0 = self.g + 0.5
self.b0 = self.b + 0.5
self.a0 = self.a + 0.5
self.rStep = self.gStep = self.bStep = self.aStep = 0.
self.posStep = 0.
def updateSteps(self, nextStop):
self.rStep = nextStop.r - self.r
self.gStep = nextStop.g - self.g
self.bStep = nextStop.b - self.b
self.aStep = nextStop.a - self.a
self.posStep = nextStop.pos - self.pos
class ColorStops(object):
def __init__(self):
self.__doAlpha = False
self.__stops = []
self.stops = []
def insert(self, pos, color):
if pos < 0. or pos > 1.:
return
if len(self.__stops) == 0:
index = 0
self.__stops = [None]
else:
index = self.findUpper(pos)
if index == len(self.__stops) or\
abs(self.__stops[index].pos-pos) >= .001:
self.__stops.append(None)
for i in range(len(self.__stops)-1, index, -1):
self.__stops[i] = self.__stops[i-1]
self.__stops[index] = ColorStop(pos, color)
if color.alpha() != 255:
self.__doAlpha = True
if index > 0:
self.__stops[index-1].updateSteps(self.__stops[index])
if index < len(self.__stops)-1:
self.__stops[index].updateSteps(self.__stops[index+1])
def stops(self):
return [stop.pos for stop in self.__stops]
def findUpper(self, pos):
index = 0
n = len(self.__stops)
stops = self.__stops
while n > 0:
half = n >> 1
middle = index + half
if stops[middle].pos <= pos:
index = middle + 1
n -= half + 1
else:
n = half
return index
def rgb(self, mode, pos):
if pos <= 0.:
return self.__stops[0].rgb
if pos >= 1.0:
return self.__stops[-1].rgb
index = self.findUpper(pos)
if mode == QwtLinearColorMap.FixedColors:
return self.__stops[index-1].rgb
else:
s1 = self.__stops[index-1]
ratio = (pos-s1.pos)/s1.posStep
r = int(s1.r0 + ratio*s1.rStep)
g = int(s1.g0 + ratio*s1.gStep)
b = int(s1.b0 + ratio*s1.bStep)
if self.__doAlpha:
if s1.aStep:
a = int(s1.a0 + ratio*s1.aStep)
return qRgba(r, g, b, a)
else:
return qRgba(r, g, b, s1.a)
else:
return qRgb(r, g, b)
[docs]class QwtColorMap(object):
"""
QwtColorMap is used to map values into colors.
For displaying 3D data on a 2D plane the 3rd dimension is often
displayed using colors, like f.e in a spectrogram.
Each color map is optimized to return colors for only one of the
following image formats:
* `QImage.Format_Indexed8`
* `QImage.Format_ARGB32`
.. py:class:: QwtColorMap(format_)
:param int format_: Preferred format of the color map (:py:data:`QwtColorMap.RGB` or :py:data:`QwtColorMap.Indexed`)
.. seealso ::
:py:data:`qwt.QwtScaleWidget`
"""
# enum Format
RGB, Indexed = list(range(2))
def __init__(self, format_=None):
if format_ is None:
format_ = self.RGB
self.__format = format_
[docs] def color(self, interval, value):
"""
Map a value into a color
:param qwt.interval.QwtInterval interval: valid interval for value
:param float value: value
:return: the color corresponding to value
.. warning ::
This method is slow for Indexed color maps. If it is necessary to
map many values, its better to get the color table once and find
the color using `colorIndex()`.
"""
if self.__format == self.RGB:
return QColor.fromRgba(self.rgb(interval, value))
else:
index = self.colorIndex(interval, value)
return self.colorTable(interval)[index]
def format(self):
return self.__format
[docs] def colorTable(self, interval):
"""
Build and return a color map of 256 colors
:param qwt.interval.QwtInterval interval: range for the values
:return: a color table, that can be used for a `QImage`
The color table is needed for rendering indexed images in combination
with using `colorIndex()`.
"""
table = [0] * 256
if interval.isValid():
step = interval.width()/(len(table)-1)
for i in range(len(table)):
table[i] = self.rgb(interval, interval.minValue()+step*i)
return table
def colorIndex(self, interval, value):
raise NotImplementedError
class QwtLinearColorMap_PrivateData(object):
def __init__(self):
self.colorStops = None
self.mode = None
[docs]class QwtLinearColorMap(QwtColorMap):
"""
Build a linear color map with two stops.
.. py:class:: QwtLinearColorMap(format_)
Build a color map with two stops at 0.0 and 1.0.
The color at 0.0 is `Qt.blue`, at 1.0 it is `Qt.yellow`.
:param int format_: Preferred format of the color map (:py:data:`QwtColorMap.RGB` or :py:data:`QwtColorMap.Indexed`)
.. py:class:: QwtLinearColorMap(color1, color2, [format_=QwtColorMap.RGB]):
Build a color map with two stops at 0.0 and 1.0.
:param QColor color1: color at 0.
:param QColor color2: color at 1.
:param int format_: Preferred format of the color map (:py:data:`QwtColorMap.RGB` or :py:data:`QwtColorMap.Indexed`)
"""
# enum Mode
FixedColors, ScaledColors = list(range(2))
def __init__(self, *args):
color1, color2 = QColor(Qt.blue), QColor(Qt.yellow)
format_ = QwtColorMap.RGB
if len(args) == 1:
format_, = args
elif len(args) == 2:
color1, color2 = args
elif len(args) == 3:
color1, color2, format_ = args
elif len(args) != 0:
raise TypeError("%s() takes 0, 1, 2 or 3 argument(s) (%s given)"\
% (self.__class__.__name__, len(args)))
super(QwtLinearColorMap, self).__init__(format_)
self.__data = QwtLinearColorMap_PrivateData()
self.__data.mode = self.ScaledColors
self.setColorInterval(color1, color2)
[docs] def setMode(self, mode):
"""
Set the mode of the color map
:param int mode: :py:data:`QwtLinearColorMap.FixedColors` or :py:data:`QwtLinearColorMap.ScaledColors`
`FixedColors` means the color is calculated from the next lower color
stop. `ScaledColors` means the color is calculated by interpolating
the colors of the adjacent stops.
"""
self.__data.mode = mode
[docs] def mode(self):
"""
:return: the mode of the color map
.. seealso ::
:py:meth:`QwtLinearColorMap.setMode`
"""
return self.__data.mode
def setColorInterval(self, color1, color2):
self.__data.colorStops = ColorStops()
self.__data.colorStops.insert(0., QColor(color1))
self.__data.colorStops.insert(1., QColor(color2))
def addColorStop(self, value, color):
if value >= 0. and value <= 1.:
self.__data.colorStops.insert(value, QColor(color))
def colorStops(self):
return self.__data.colorStops.stops()
def color1(self):
return QColor(self.__data.colorStops.rgb(self.__data.mode, 0.))
def color2(self):
return QColor(self.__data.colorStops.rgb(self.__data.mode, 1.))
def rgb(self, interval, value):
if qIsNaN(value):
return 0
width = interval.width()
if width <= 0.:
return 0
ratio = (value-interval.minValue())/width
return self.__data.colorStops.rgb(self.__data.mode, ratio)
def colorIndex(self, interval, value):
width = interval.width()
if qIsNaN(value) or width <= 0. or value <= interval.minValue():
return 0
if value >= interval.maxValue():
return 255
ratio = (value-interval.minValue())/width
if self.__data.mode == self.FixedColors:
return int(ratio*255)
else:
return int(ratio*255+.5)
class QwtAlphaColorMap_PrivateData(object):
def __init__(self):
self.color = None
self.rgb = None
self.rgbMax = None
[docs]class QwtAlphaColorMap(QwtColorMap):
"""
QwtAlphaColorMap varies the alpha value of a color
.. py:class:: QwtAlphaColorMap(color)
Build a color map varying the alpha value of a color.
:param QColor color: color of the map
"""
def __init__(self, color):
super(QwtAlphaColorMap, self).__init__(QwtColorMap.RGB)
self.__data = QwtAlphaColorMap_PrivateData()
self.setColor(color)
[docs] def setColor(self, color):
"""
Set the color of the map
:param QColor color: color of the map
"""
self.__data.color = color
self.__data.rgb = color.rgb() & qRgba(255, 255, 255, 0)
self.__data.rgbMax = self.__data.rgb | ( 255 << 24 )
[docs] def color(self):
"""
:return: the color of the map
.. seealso ::
:py:meth:`QwtAlphaColorMap.setColor`
"""
return self.__data.color()
def rgb(self, interval, value):
if qIsNaN(value):
return 0
width = interval.width()
if width <= 0.:
return 0
if value <= interval.minValue():
return self.__data.rgb
if value >= interval.maxValue():
return self.__data.rgbMax
ratio = (value-interval.minValue())/width
return self.__data.rgb | (int(round(255*ratio)) << 24)
def colorIndex(self, interval, value):
return 0