added colormap sampling functions

This commit is contained in:
Nils Nemitz 2021-03-19 16:44:25 +09:00
parent 172cef1628
commit 244182d2bf
7 changed files with 824 additions and 239 deletions

View File

@ -24,6 +24,8 @@ class MainWindow(QtWidgets.QMainWindow):
self.setWindowTitle('pyqtgraph example: Palette application test')
self.resize(600,600)
test_palette = pg.palette.get('system')
pg.palette.get('relaxed-dark').apply()
main_layout = QtWidgets.QGridLayout( main_wid )
@ -63,10 +65,17 @@ class MainWindow(QtWidgets.QMainWindow):
self.data1 = +3 + np.random.normal(size=(15)) #500))
self.data2 = -3 + np.random.normal(size=(15)) #500))
self.curve1 = pg.PlotDataItem(pen='r', symbol='o', symbolSize=10, symbolPen='gr_fg', symbolBrush=('y',127))
# self.curve1 = pg.PlotDataItem(
# pen='r',
# symbol='o', symbolSize=10, symbolPen='gr_fg', symbolBrush=('y',127),
# hoverable=True, hoverPen='w', hoverBrush='w')
self.curve1 = pg.ScatterPlotItem(
symbol='o', symbolSize=12, symbolPen='gr_fg', symbolBrush=('y',127),
hoverable=True, hoverPen='gr_acc', hoverBrush='gr_reg')
# self.curve1.setHoverable(True)
self.plt.addItem(self.curve1)
self.curve2 = pg.PlotCurveItem(pen='w', brush='d')
self.curve2 = pg.PlotCurveItem(pen='l', brush='d')
self.curve2.setFillLevel(0)
self.plt.addItem(self.curve2)
self.show()

View File

@ -0,0 +1,393 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Update a simple plot as rapidly as possible to measure speed.
"""
## Add path to library (just for examples; you do not need this)
import initExample
import qdarkstyle
import numpy as np
from pyqtgraph.Qt import mkQApp, QtCore, QtGui, QtWidgets
from pyqtgraph.ptime import time
import pyqtgraph as pg
class MainWindow(QtWidgets.QMainWindow):
""" example application main window """
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
force_dark = True # start in dark mode on Windows
self.setWindowTitle('pyqtgraph example: Palette application test')
self.resize(600,600)
self.palette_options = (
('system', 'system', []),
('legacy', 'legacy', []),
('relaxed (dark)', 'relaxed_dark', []),
('mono green', 'monochrome', ['green']),
('mono amber', 'monochrome', ['amber']),
('mono blue' , 'monochrome', ['blue' ]),
('synthwave' , 'synthwave', []),
)
self.colormap_options = (
'CET-C1', 'CET-C2','CET-C6','CET-C7', 'CET-R2', 'CET-R4',
'CET-L8', 'CET-L16', 'none'
# , 'none', 'CET-C1', 'CET-C2', 'CET-C3', 'CET-C4', 'CET-C5', 'CET-C6', 'CET-C7', 'CET-CBC1', 'CET-CBC2'
)
app = QtWidgets.QApplication.instance()
self.q_palette = {
'system' : app.palette(),
'dark' : self.make_dark_QPalette()
}
app.setStyle("Fusion")
self.ui = self.prepare_ui() # relocate long-winded window layout
# dictionary self.ui contains references to:
# 'sample_start' QLineEdit for start of colormap sampling
# 'sample_step' QLineEdit for step of colormap sampling
# 'dark' QPushButton for toggling dark / standard GUI
if force_dark:
self.ui['dark'].setChecked(True)
self.handle_dark_button(True)
self.open_palette = pg.palette.get('system')
self.open_palette.apply()
self.update_color_fields( self.open_palette )
self.num_points = 30
# configure overview plot with four colors:
plt = self.ui['plot1']
plt.enableAutoRange(False)
plt.setYRange( 0, 4, padding=0 )
plt.setXRange( 0, self.num_points, padding=0 )
for key in ('left','right','top','bottom'):
ax = plt.getAxis(key)
ax.show()
ax.setZValue(0.1)
self.curves = []
curve = pg.PlotCurveItem(pen='p0', brush='p0') # ('p0',127))
curve.setFillLevel(0)
self.curves.append( (1, 1, curve) ) # dataset 1, vertical offset 3
plt.addItem(curve)
curve = pg.ScatterPlotItem(
symbol='o', size=5, pen='p0', brush='p0', # ('p0',127),
hoverable=True, hoverPen='gr_hlt', hoverBrush='gr_fg')
self.curves.append( (1, 1, curve) ) # dataset 1, vertical offset 2
plt.addItem(curve)
pen_list = ['p2', 'p4', 'p6'] # add three more plots
for idx, pen in enumerate( pen_list ):
curve = pg.PlotCurveItem()
curve.setPen(pen, width=5)
self.curves.append( (3+2*idx, 1.5+0.8*idx, curve) ) # datasets 2+, vertical offset 3+
plt.addItem(curve)
# configure tall plot with eight colors and region overlay:
plt = self.ui['plot2']
plt.enableAutoRange(False)
plt.setYRange( -0.6, 7.6, padding=0 )
plt.setXRange( 0, self.num_points, padding=0 )
plt.getAxis('bottom').hide()
plt.getAxis('left').setLabel('plot color')
plt.getAxis('left').setGrid(0.5) # 63)
pen_list = [('p0',255),'p1','p2','p3','p4','p5','p6','p7'] # add right-side plots for each main color
for idx, pen in enumerate( pen_list ):
curve = pg.PlotCurveItem(pen=pen)
self.curves.append( (1+idx, idx, curve) ) # datasets 2+, vertical offset by index
plt.addItem(curve)
item = pg.LinearRegionItem( values=(4, 8), orientation='vertical' )
plt.addItem(item)
self.show()
# prepare for continuous updates and frame rate measurement
self.last_time = time()
self.fps = None
self.timer = QtCore.QTimer(singleShot=False)
self.timer.timeout.connect( self.timed_update )
# prepare initial data and display in plots
self.data = np.zeros((9, self.num_points ))
self.data[0,:] = np.arange( self.data.shape[1] ) # used as x data
self.phases = np.zeros(9)
self.timed_update()
### handle GUI interaction ###############################################
def update_color_fields(self, pal):
""" update line edit fields for selected palette """
if pal is None:
print('palette is None!')
return
for key in self.ui['widget_from_color_key']:
wid = self.ui['widget_from_color_key'][key]
qcol = pal[key]
if wid is not None:
wid.setText( qcol.name() )
def handle_palette_select(self, idx):
""" user selected a palette in dropdown menu """
text, identifier, args = self.palette_options[idx]
del text # not needed here
self.open_palette = pg.palette.get(identifier, *args)
print('loaded palette:', identifier, args)
if identifier in pg.palette.PALETTE_DEFINITIONS:
info = pg.palette.PALETTE_DEFINITIONS[identifier]
colormap_sampling = info['colormap_sampling']
if colormap_sampling is None:
identifier, start, step = 'none', 0.000, 0.125
else:
identifier, start, step = colormap_sampling
self.ui['sample_start'].setText('{:+.3f}'.format(start))
self.ui['sample_step' ].setText('{:+.3f}'.format(step) )
for idx, map_id in enumerate( self.colormap_options ):
if map_id == identifier:
# print('found colormap at idx',idx)
self.ui['colormaps'].setCurrentIndex(idx)
self.update_color_fields(self.open_palette)
self.open_palette.apply()
def handle_colormap_select(self, param=None):
""" user selected a colormap in dropdown menu or changed start / step vales """
del param # drop index sent by QComboBox
identifier = self.ui['colormaps'].currentText()
if identifier == 'none':
return
start = self.ui['sample_start'].text()
step = self.ui['sample_step' ].text()
try:
start = float(start)
except ValueError:
start = 0.0
self.ui['sample_start'].setText('{:+.3f}'.format(start) )
try:
step = float(step)
except ValueError:
step = 0.125
self.ui['sample_step'].setText('{:+.3f}'.format(step) )
self.open_palette.sampleColorMap( cmap=identifier, start=start, step=step )
# print('applied color map {:s} starting at {:.3f} with step {:3f}'.format(identifier, start, step) )
self.update_color_fields(self.open_palette)
self.open_palette.apply()
def handle_color_update(self):
""" figure out what color field was updated """
source = self.sender()
print('color update requested by field',source)
def handle_update_button(self, active):
""" start/stop timer """
if active:
self.timer.start(1)
else:
self.timer.stop()
def handle_dark_button(self, active):
""" manually switch to dark palette to test on windows """
app = QtWidgets.QApplication.instance()
if active:
app.setPalette( self.q_palette['dark'] ) # apply dark QPalette
else:
app.setPalette( self.q_palette['system'] ) # reapply QPalette stored at start-up
def timed_update(self):
""" update loop, called by timer """
self.speed = np.linspace(0.01, 0.06, 9)
self.phases += self.speed * np.random.normal(1, 1, size=9)
for idx in range(1, self.data.shape[0]):
self.data[idx, :-1] = self.data[idx, 1:] # roll
# self.data[idx, -1] = np.random.normal()
self.data[1:, -1] = 0.5 * np.sin( self.phases[1:] )
xdata = self.data[0,:]
for idx, offset, curve in self.curves:
curve.setData( x=xdata, y=( offset + self.data[idx,:] ) )
now = time()
dt = now - self.last_time
self.last_time = now
if self.fps is None:
self.fps = 1.0/dt
else:
s = np.clip(dt*3., 0, 1)
self.fps = self.fps * (1-s) + (1.0/dt) * s
self.ui['plot2'].setTitle('%0.2f fps' % self.fps)
QtWidgets.QApplication.processEvents() ## force complete redraw for every plot
### Qt color definitions for dark palette on Windows #####################
def make_dark_QPalette(self):
# color definitions match QDarkstyle
BLACK = QtGui.QColor('#000000')
BG_LIGHT = QtGui.QColor('#505F69')
BG_NORMAL = QtGui.QColor('#32414B')
BG_DARK = QtGui.QColor('#19232D')
FG_LIGHT = QtGui.QColor('#F0F0F0')
FG_NORMAL = QtGui.QColor('#AAAAAA')
FG_DARK = QtGui.QColor('#787878')
SEL_LIGHT = QtGui.QColor('#148CD2')
SEL_NORMAL = QtGui.QColor('#1464A0')
SEL_DARK = QtGui.QColor('#14506E')
qpal = QtGui.QPalette( QtGui.QColor(BG_DARK) )
for ptype in ( QtGui.QPalette.Active, QtGui.QPalette.Inactive ):
qpal.setColor( ptype, QtGui.QPalette.Window, BG_NORMAL )
qpal.setColor( ptype, QtGui.QPalette.WindowText, FG_LIGHT ) # or white?
qpal.setColor( ptype, QtGui.QPalette.Base, BG_DARK )
qpal.setColor( ptype, QtGui.QPalette.Text, FG_LIGHT )
qpal.setColor( ptype, QtGui.QPalette.AlternateBase, BG_DARK )
qpal.setColor( ptype, QtGui.QPalette.ToolTipBase, BG_LIGHT )
qpal.setColor( ptype, QtGui.QPalette.ToolTipText, FG_LIGHT )
qpal.setColor( ptype, QtGui.QPalette.Button, BG_DARK )
qpal.setColor( ptype, QtGui.QPalette.ButtonText, FG_LIGHT )
qpal.setColor( ptype, QtGui.QPalette.Link, SEL_NORMAL )
qpal.setColor( ptype, QtGui.QPalette.LinkVisited, FG_NORMAL )
qpal.setColor( ptype, QtGui.QPalette.Highlight, SEL_LIGHT )
qpal.setColor( ptype, QtGui.QPalette.HighlightedText, BLACK )
qpal.setColor( QtGui.QPalette.Disabled, QtGui.QPalette.Button, BG_NORMAL )
qpal.setColor( QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, FG_DARK )
qpal.setColor( QtGui.QPalette.Disabled, QtGui.QPalette.WindowText, FG_DARK )
return qpal
##########################################################################
def prepare_ui(self):
""" Boring Qt window layout code is implemented here """
ui = {}
main_wid = QtWidgets.QWidget()
self.setCentralWidget(main_wid)
color_fields = (
# key, description, reference to line edit field)
['gr_bg' , (0,0), 'graph background' ],
['gr_fg' , (1,0), 'graph foreground' ],
['gr_txt', (2,0), 'graph text' ],
['gr_reg', (3,0), 'graph region' ],
['gr_acc', (4,0), 'graphical accent' ],
['gr_hlt', (5,0), 'graphical highlight'],
['p0', (0,1), ' plot 0'], ['p1', (1,1), ' plot 1'],
['p2', (2,1), ' plot 2'], ['p3', (3,1), ' plot 3'],
['p4', (4,1), ' plot 4'], ['p5', (5,1), ' plot 5'],
['p6', (6,1), ' plot 6'], ['p7', (7,1), ' plot 7']
)
gr_wid1 = pg.GraphicsLayoutWidget(show=True)
ui['plot1'] = gr_wid1.addPlot()
gr_wid2 = pg.GraphicsLayoutWidget(show=True)
ui['plot2'] = gr_wid2.addPlot()
main_layout = QtWidgets.QHBoxLayout( main_wid )
l_wid = QtWidgets.QWidget()
main_layout.addWidget(l_wid)
main_layout.addWidget(gr_wid2)
l_layout = QtWidgets.QGridLayout( l_wid )
l_layout.setContentsMargins(0,0,0,0)
l_layout.setSpacing(1)
row_idx = 0
label = QtWidgets.QLabel('Select a palette:')
l_layout.addWidget( label, row_idx,0, 1,2 )
row_idx += 1
box = QtWidgets.QComboBox()
for text, identifier, args in self.palette_options:
del identifier, args # not needed here
box.addItem(text)
box.activated.connect(self.handle_palette_select)
btn = QtWidgets.QPushButton('dark GUI')
btn.setCheckable(True)
btn.setChecked(False)
btn.clicked.connect(self.handle_dark_button)
l_layout.addWidget( box, row_idx,0, 1,2 )
l_layout.addWidget( btn, row_idx,3, 1,1 )
ui['dark'] = btn
row_idx += 1
label = QtWidgets.QLabel('Sampled color map:')
l_layout.addWidget( label, row_idx,0, 1,2 )
label = QtWidgets.QLabel('start')
l_layout.addWidget( label, row_idx,2, 1,1 )
label = QtWidgets.QLabel('step')
l_layout.addWidget( label, row_idx,3, 1,1 )
row_idx += 1
box = QtWidgets.QComboBox()
for identifier in self.colormap_options:
box.addItem(identifier)
ui['colormaps'] = box
ui['colormaps'].activated.connect(self.handle_colormap_select)
ui['sample_start'] = QtWidgets.QLineEdit(' 0.000')
ui['sample_start'].editingFinished.connect(self.handle_colormap_select)
ui['sample_step' ] = QtWidgets.QLineEdit('+0.125')
ui['sample_step' ].editingFinished.connect(self.handle_colormap_select)
l_layout.addWidget( box, row_idx,0, 1,2 )
l_layout.addWidget( ui['sample_start'], row_idx,2, 1,1 )
l_layout.addWidget( ui['sample_step' ], row_idx,3, 1,1 )
row_idx += 1
spacer = QtWidgets.QWidget()
spacer.setFixedHeight(10)
l_layout.addWidget( spacer, row_idx,0, 1,2 )
row_idx += 1
label = QtWidgets.QLabel('Functional colors:')
l_layout.addWidget( label, row_idx,0, 1,2 )
row_idx += 1
row = 0
ui['widget_from_color_key'] = {} # look-up for color editing fields
ui['color_key_from_widget'] = {} # reverse look-up for color editing fields
for field_list in color_fields:
key, pos, text = field_list
lab = QtWidgets.QLabel(text)
lab.setAlignment(QtCore.Qt.AlignCenter)
edt = QtWidgets.QLineEdit()
row = row_idx + pos[0]
col = 2 * pos[1] # 0 or 2
l_layout.addWidget( lab, row,col+0, 1,1 )
l_layout.addWidget( edt, row,col+1, 1,1 )
ui['color_key_from_widget'][edt] = key
ui['widget_from_color_key'][key] = edt
row_idx = row
btn = QtWidgets.QPushButton('generate continuous data')
btn.setCheckable(True)
btn.setChecked(False)
btn.clicked.connect(self.handle_update_button)
l_layout.addWidget( btn, row_idx,0, 1,2 )
row_idx += 1
spacer = QtWidgets.QWidget()
spacer.setFixedHeight(10)
l_layout.addWidget( spacer, row_idx,0, 1,2 )
row_idx += 1
label = QtWidgets.QLabel('Overview:')
l_layout.addWidget( label, row_idx,0, 1,4 )
row_idx += 1
l_layout.addWidget( gr_wid1, row_idx,0, 1,4 )
row_idx += 1
return ui
mkQApp("Palette test application")
main_window = MainWindow()
## Start Qt event loop
if __name__ == '__main__':
QtWidgets.QApplication.instance().exec_()

View File

@ -54,7 +54,7 @@ monochrome_colors = ('blue', 'green', 'amber', 'red', 'pink', 'lavender', (0.5,
for mono_val in monochrome_colors:
num_bars += 1
lw.addLabel(str(mono_val))
cmap = pg.colormap.make_monochrome(mono_val)
cmap = pg.colormap.makeMonochrome(mono_val)
imi = pg.ImageItem()
imi.setImage(img)
imi.setLookupTable( cmap.getLookupTable(alpha=True) )

View File

@ -21,6 +21,7 @@ examples = OrderedDict([
('Remote Plotting', 'RemoteSpeedTest.py'),
('Scrolling plots', 'scrollingPlots.py'),
('Color Maps', 'colorMaps.py'),
('Palette tester','PaletteTestAndEdit.py'),
('Palette adjustment','PaletteApplicationExample.py'),
('HDF5 big data', 'hdf5.py'),
('Demos', OrderedDict([

View File

@ -86,7 +86,6 @@ def _get_from_file(name):
else:
csv_mode = False
for line in fh:
name = None
line = line.strip()
if len(line) == 0: continue # empty line
if line[0] == ';': continue # comment
@ -110,11 +109,11 @@ def _get_from_file(name):
idx += 1
# end of line reading loop
# end of open
cm = ColorMap(
cmap = ColorMap( name=name,
pos=np.linspace(0.0, 1.0, len(color_list)),
color=color_list) #, names=color_names)
_mapCache[name] = cm
return cm
_mapCache[name] = cmap
return cmap
def _get_from_matplotlib(name):
""" import colormap from matplotlib definition """
@ -124,7 +123,7 @@ def _get_from_matplotlib(name):
import matplotlib.pyplot as mpl_plt
except ModuleNotFoundError:
return None
cm = None
cmap = None
col_map = mpl_plt.get_cmap(name)
if hasattr(col_map, '_segmentdata'): # handle LinearSegmentedColormap
data = col_map._segmentdata
@ -142,20 +141,21 @@ def _get_from_matplotlib(name):
positions[idx2] = tup[0]
comp_vals[idx2] = tup[1] # these are sorted in the raw data
col_data[:,idx] = np.interp(col_data[:,3], positions, comp_vals)
cm = ColorMap(pos=col_data[:,-1], color=255*col_data[:,:3]+0.5)
cmap = ColorMap(pos=col_data[:,-1], color=255*col_data[:,:3]+0.5)
# some color maps (gnuplot in particular) are defined by RGB component functions:
elif ('red' in data) and isinstance(data['red'], collections.Callable):
col_data = np.zeros((64, 4))
col_data[:,-1] = np.linspace(0., 1., 64)
for idx, key in enumerate(['red','green','blue']):
col_data[:,idx] = np.clip( data[key](col_data[:,-1]), 0, 1)
cm = ColorMap(pos=col_data[:,-1], color=255*col_data[:,:3]+0.5)
cmap = ColorMap(pos=col_data[:,-1], color=255*col_data[:,:3]+0.5)
elif hasattr(col_map, 'colors'): # handle ListedColormap
col_data = np.array(col_map.colors)
cm = ColorMap(pos=np.linspace(0.0, 1.0, col_data.shape[0]), color=255*col_data[:,:3]+0.5 )
if cm is not None:
_mapCache[name] = cm
return cm
cmap = ColorMap( name=name,
pos = np.linspace(0.0, 1.0, col_data.shape[0]), color=255*col_data[:,:3]+0.5 )
if cmap is not None:
_mapCache[name] = cmap
return cmap
def _get_from_colorcet(name):
""" import colormap from colorcet definition """
@ -173,22 +173,23 @@ def _get_from_colorcet(name):
color_list.append( color_tuple )
if len(color_list) == 0:
return None
cm = ColorMap(
cmap = ColorMap( name=name,
pos=np.linspace(0.0, 1.0, len(color_list)),
color=color_list) #, names=color_names)
_mapCache[name] = cm
return cm
_mapCache[name] = cmap
return cmap
def make_monochrome(color='green'):
def makeMonochrome(color='green'):
"""
Returns a ColorMap object imitating a monochrome computer screen
=============== =================================================================
**Arguments:**
color Primary color description. Can be one of predefined identifiers
'green' or 'amber'
'green', 'amber', 'blue', 'red', 'lavender', 'pink'
or a tuple of relative R,G,B contributions in range 0.0 to 1.0
=============== =================================================================
"""
name = 'monochrome-'+str(color)
stops = np.array([0.000, 0.167, 0.247, 0.320, 0.411, 0.539, 0.747, 1.000])
active = np.array([ 16, 72, 113, 147, 177, 205, 231, 255])
leakage = np.array([ 0, 1, 7, 21, 45, 80, 127, 191])
@ -210,8 +211,8 @@ def make_monochrome(color='green'):
g * delta + leak,
b * delta + leak )
color_list.append(color_tuple)
cm = ColorMap(pos=stops, color=color_list )
return cm
cmap = ColorMap(name=name, pos=stops, color=color_list )
return cmap
class ColorMap(object):
"""
@ -260,7 +261,7 @@ class ColorMap(object):
'qcolor': QCOLOR,
}
def __init__(self, pos, color, mode=None, mapping=None): #, names=None):
def __init__(self, pos, color, name=None, mode=None, mapping=None): #, names=None):
"""
=============== =================================================================
**Arguments:**
@ -280,6 +281,7 @@ class ColorMap(object):
DIVERGING maps colors to [-1.0;+1.0]
=============== =================================================================
"""
self.name = name # storing a name helps identify ColorMaps sampled by Palette
self.pos = np.array(pos)
order = np.argsort(self.pos)
self.pos = self.pos[order]
@ -304,6 +306,12 @@ class ColorMap(object):
self.mapping_mode = self.CLIP
self.stopsCache = {}
def __str__(self):
""" provide human-readable identifier """
if self.name is None:
return 'unnamed ColorMap({:d})'.format(len(self.pos))
return "ColorMap({:d}):'{:s}'".format(len(self.pos),self.name)
def __getitem__(self, key):
""" Convenient shorthand access to palette colors """

View File

@ -25,7 +25,7 @@ for key, col in [ # add functional colors
('gr_fg','d'), # graphical foreground
('gr_bg','k'), # graphical background
('gr_txt','d'), # graphical text color
('gr_hov','r') # graphical hover color
('gr_hlt','r') # graphical hover color
]:
DEFAULT_COLORS[key] = DEFAULT_COLORS[col]

View File

@ -1,254 +1,428 @@
from .Qt import QtGui
from . import Qt
from .Qt import QtCore, QtGui, QtWidgets
from . import functions as fn # namedColorManager
from . import colormap
__all__ = ['Palette']
LEGACY_RAW = { # legacy raw colors:
'b': ( 0, 0,255,255),
'g': ( 0,255, 0,255),
'r': (255, 0, 0,255),
'c': ( 0,255,255,255),
'm': (255, 0,255,255),
'y': (255,255, 0,255),
'k': ( 0, 0, 0,255),
'w': (255,255,255,255),
'd': (150,150,150,255),
'l': (200,200,200,255),
's': (100,100,150,255)
}
LEGACY_FUNC = { # functional colors:
'gr_fg' : 'd',
'gr_bg' : 'k',
'gr_txt' : 'd',
'gr_acc' : (200,200,100,255),
'gr_hov' : 'r',
'gr_reg' : ( 0, 0,255, 50)
}
LEGACY_PLOT = [ # plot / accent colors:
'l','y','r','m','b','c','g','d'
]
#### todo list ####
# define legacy colors for relaxed-dark
# find color definitions for relaxed-light
# define color names for relaxed palettes
# enable color adjustment in PaletteTestandEdit.py!
RELAXED_RAW = { # "fresh" raw colors:
'col_orange':'#A64D21', 'col_l_orange':'#D98A62', 'col_d_orange':'#732E0B',
'col_red' :'#B32424', 'col_l_red' :'#E66767', 'col_d_red' :'#800D0D',
'col_purple':'#991F66', 'col_l_purple':'#D956A3', 'col_d_purple':'#660A31',
'col_violet':'#7922A6', 'col_l_violet':'#BC67E6', 'col_d_violet':'#5A0C80',
'col_indigo':'#5F29CC', 'col_l_indigo':'#9673FF', 'col_d_indigo':'#380E8C',
'col_blue' :'#2447B3', 'col_l_blue' :'#6787E6', 'col_d_blue' :'#0D2980',
'col_sky' :'#216AA6', 'col_l_sky' :'#77ADD9', 'col_d_sky' :'#0B4473',
'col_cyan' :'#1C8C8C', 'col_l_cyan' :'#73BFBF', 'col_d_cyan' :'#095959',
'col_green' :'#1F9952', 'col_l_green' :'#7ACC9C', 'col_d_green' :'#0A6630',
'col_grass' :'#7AA621', 'col_l_grass' :'#BCD982', 'col_d_grass' :'#50730B',
'col_yellow':'#BFB226', 'col_l_yellow':'#F2E985', 'col_d_yellow':'#80760D',
'col_gold' :'#A67A21', 'col_l_gold' :'#D9B46C', 'col_d_gold' :'#73500B',
# 'col_black' :'#000000', 'col_gr1' :'#242429', 'col_gr2' :'#44444D',
'col_black' :'#000000', 'col_gr1' :'#161619', 'col_gr2' :'#43434D',
'col_gr3' :'#70707F', 'col_gr4' :'#9D9DB2', 'col_gr5' :'#C9C9E5',
'col_white' :'#FFFFFF'
}
RELAXED_DARK_FUNC= { # functional colors:
'gr_fg' : 'col_gr5',
'gr_bg' : 'col_gr1',
'gr_txt' : 'col_gr5',
'gr_acc' : 'col_cyan',
'gr_hov' : 'col_white',
'gr_reg' : ('col_cyan', 30),
# legacy colors:
'b': 'col_l_blue' , 'c': 'col_l_cyan', 'g': 'col_l_green',
'y': 'col_l_yellow', 'r': 'col_l_red' , 'm': 'col_l_violet',
'k': 'col_black' , 'w': 'col_white',
'd': 'col_gr2' , 'l': 'col_gr4' , 's': 'col_l_sky'
}
RELAXED_DARK_PLOT = [ # plot / accent colors:
'col_l_sky' ,
'col_l_indigo',
'col_l_purple',
'col_l_red' ,
'col_l_gold' ,
'col_l_grass' ,
'col_l_cyan' ,
'col_l_blue' ,
'col_l_violet',
'col_l_orange',
'col_l_yellow',
'col_l_green'
]
PALETTE_DEFINITIONS = {
'legacy': {
'colormap_sampling' : None,
'b': ( 0, 0,255,255), 'g': ( 0,255, 0,255), 'r': (255, 0, 0,255),
'c': ( 0,255,255,255), 'm': (255, 0,255,255), 'y': (255,255, 0,255),
'k': ( 0, 0, 0,255), 'w': (255,255,255,255),
'd': (150,150,150,255), 'l': (200,200,200,255), 's': (100,100,150,255),
# --- functional colors ---
'gr_fg' : 'd', 'gr_bg' : 'k',
'gr_txt': 'd', 'gr_acc': (200,200,100,255),
'gr_hlt': 'r', 'gr_reg': ( 0, 0,255,100),
# --- manually assigned plot colors ---
'p0':'l', 'p1':'y', 'p2':'r', 'p3':'m',
'p4':'b', 'p5':'c', 'p6':'g', 'p7':'d'
},
'relaxed_dark':{
'colormap_sampling': ('CET-C6', 0.430, -0.125),
'col_black' :'#000000', 'col_white' :'#FFFFFF',
'col_gr1':'#19232D', 'col_gr2':'#32414B', 'col_gr3':'#505F69', # match QDarkStyle background colors
'col_gr4':'#787878', 'col_gr5':'#AAAAAA', 'col_gr6':'#F0F0F0', # match QDarkstyle foreground colors
# --- functional colors ---
'gr_fg' : 'col_gr4', 'gr_bg' : 'col_gr1',
'gr_txt': 'col_gr5', 'gr_acc': '#1464A0', #col_cyan',
'gr_hlt': 'col_white', 'gr_reg': ('#1464A0',100)
# legacy colors:
# 'b': 'col_l_blue' , 'c': 'col_l_cyan', 'g': 'col_l_green',
# 'y': 'col_l_yellow', 'r': 'col_l_red' , 'm': 'col_l_violet',
# 'k': 'col_black' , 'w': 'col_white',
# 'd': 'col_gr2' , 'l': 'col_gr4' , 's': 'col_l_sky'
},
'synthwave':{
'colormap_sampling': ('CET-L8', 0.275, 0.100),
'col_black' :'#000000', 'col_white' :'#FFFFFF',
'col_gr1':'#19232D', 'col_gr2':'#32414B', 'col_gr3':'#505F69', # match QDarkStyle background colors
'col_gr4':'#787878', 'col_gr5':'#AAAAAA', 'col_gr6':'#F0F0F0', # match QDarkstyle foreground colors
# --- functional colors ---
'gr_fg' : 'col_gr4', 'gr_bg' : 'col_gr1',
'gr_txt': 'col_gr5', 'gr_acc': '#1464A0', #col_cyan',
'gr_hlt': 'col_white', 'gr_reg': ('#1464A0',100)
# legacy colors:
# 'b': 'col_l_blue' , 'c': 'col_l_cyan', 'g': 'col_l_green',
# 'y': 'col_l_yellow', 'r': 'col_l_red' , 'm': 'col_l_violet',
# 'k': 'col_black' , 'w': 'col_white',
# 'd': 'col_gr2' , 'l': 'col_gr4' , 's': 'col_l_sky'
}
RELAXED_LIGHT_FUNC= { # functional colors:
'gr_fg' : 'col_gr1',
'gr_bg' : 'col_gr5',
'gr_txt' : 'col_black',
'gr_acc' : 'col_orange',
'gr_hov' : 'col_black',
'gr_reg' : ('col_blue', 30),
# legacy colors:
'b': 'col_blue' , 'c': 'col_cyan', 'g': 'col_green',
'y': 'col_yellow', 'r': 'col_red' , 'm': 'col_violet',
'k': 'col_black' , 'w': 'col_white',
'd': 'col_gr2' , 'l': 'col_gr4' , 's': 'col_sky'
}
RELAXED_LIGHT_PLOT = [ # plot / accent colors:
'col_sky' ,
'col_indigo',
'col_purple',
'col_red' ,
'col_gold' ,
'col_grass' ,
'col_cyan' ,
'col_blue' ,
'col_violet',
'col_orange',
'col_yellow',
'col_green'
]
# RELAXED_RAW = { # "fresh" raw colors:
# 'col_orange':'#A64D21', 'col_l_orange':'#D98A62', 'col_d_orange':'#732E0B',
# 'col_red' :'#B32424', 'col_l_red' :'#E66767', 'col_d_red' :'#800D0D',
# 'col_purple':'#991F66', 'col_l_purple':'#D956A3', 'col_d_purple':'#660A31',
# 'col_violet':'#7922A6', 'col_l_violet':'#BC67E6', 'col_d_violet':'#5A0C80',
# 'col_indigo':'#5F29CC', 'col_l_indigo':'#9673FF', 'col_d_indigo':'#380E8C',
# 'col_blue' :'#2447B3', 'col_l_blue' :'#6787E6', 'col_d_blue' :'#0D2980',
# 'col_sky' :'#216AA6', 'col_l_sky' :'#77ADD9', 'col_d_sky' :'#0B4473',
# 'col_cyan' :'#1C8C8C', 'col_l_cyan' :'#73BFBF', 'col_d_cyan' :'#095959',
# 'col_green' :'#1F9952', 'col_l_green' :'#7ACC9C', 'col_d_green' :'#0A6630',
# 'col_grass' :'#7AA621', 'col_l_grass' :'#BCD982', 'col_d_grass' :'#50730B',
# 'col_yellow':'#BFB226', 'col_l_yellow':'#F2E985', 'col_d_yellow':'#80760D',
# 'col_gold' :'#A67A21', 'col_l_gold' :'#D9B46C', 'col_d_gold' :'#73500B',
# # 'col_black' :'#000000', 'col_gr1' :'#242429', 'col_gr2' :'#44444D',
# 'col_black' :'#000000', 'col_gr1' :'#161619', 'col_gr2' :'#43434D',
# 'col_gr3' :'#70707F', 'col_gr4' :'#9D9DB2', 'col_gr5' :'#C9C9E5',
# 'col_white' :'#FFFFFF'
# }
# RELAXED_DARK_FUNC= { # functional colors:
# 'gr_fg' : 'col_gr5',
# 'gr_bg' : 'col_gr1',
# 'gr_txt' : 'col_gr5',
# 'gr_acc' : 'col_cyan',
# 'gr_hlt' : 'col_white',
# 'gr_reg' : ('col_cyan',100),
# # legacy colors:
# 'b': 'col_l_blue' , 'c': 'col_l_cyan', 'g': 'col_l_green',
# 'y': 'col_l_yellow', 'r': 'col_l_red' , 'm': 'col_l_violet',
# 'k': 'col_black' , 'w': 'col_white',
# 'd': 'col_gr2' , 'l': 'col_gr4' , 's': 'col_l_sky'
# }
# RELAXED_DARK_PLOT = [ # plot / accent colors:
# 'col_l_sky' ,
# 'col_l_indigo',
# 'col_l_purple',
# 'col_l_red' ,
# 'col_l_gold' ,
# 'col_l_grass' ,
# 'col_l_cyan' ,
# 'col_l_blue' ,
# 'col_l_violet',
# 'col_l_orange',
# 'col_l_yellow',
# 'col_l_green'
# ]
# RELAXED_LIGHT_FUNC= { # functional colors:
# 'gr_fg' : 'col_gr1',
# 'gr_bg' : 'col_gr5',
# 'gr_txt' : 'col_black',
# 'gr_acc' : 'col_orange',
# 'gr_reg' : ('col_blue',100),
# # legacy colors:
# 'b': 'col_blue' , 'c': 'col_cyan', 'g': 'col_green',
# 'y': 'col_yellow', 'r': 'col_red' , 'm': 'col_violet',
# 'k': 'col_black' , 'w': 'col_white',
# 'd': 'col_gr2' , 'l': 'col_gr4' , 's': 'col_sky'
# }
# RELAXED_LIGHT_PLOT = [ # plot / accent colors:
# 'col_sky' ,
# 'col_indigo',
# 'col_purple',
# 'col_red' ,
# 'col_gold' ,
# 'col_grass' ,
# 'col_cyan' ,
# 'col_blue' ,
# 'col_violet',
# 'col_orange',
# 'col_yellow',
# 'col_green'
# ]
def block_to_QColor( block, dic=None ):
""" convert color information to a QColor """
# allowed formats:
# 'name'
# ('name', alpha)
# (R,G,B) / (R,G,B,alpha)
if isinstance(block, QtGui.QColor):
return block # this is already a QColor
def identifier_to_QColor( identifier, color_dict=None ):
"""
Convert color information to a QColor
=================== =============================================================
**allowed formats**
'name' name must be a hex value or a key in 'color_dict'
('name', alpha) new copy will be assigned the specified alpha value
QColor will be copied to avoid interaction if changing alpha values
Qt.GlobalColor will result in a matching QColor
(R,G,B) a new Qcolor will be created
(R,G,B, alpha) a new Qcolor with specified opacity will be created
=================== =============================================================
"""
if isinstance(identifier, (QtGui.QColor, QtCore.Qt.GlobalColor)):
return QtGui.QColor(identifier)
alpha = None
if isinstance(block, str): # return known QColor
name = block
if dic is None or name not in dic:
if isinstance(identifier, str): # return known QColor
name = identifier
if color_dict is None or name not in color_dict:
if name[0] != '#':
raise ValueError('Undefined color name '+str(block))
raise ValueError('Undefined color name '+str(identifier))
return QtGui.QColor( name )
else:
return dic[name]
if not hasattr(block, '__len__'):
raise ValueError('Invalid color definition '+str(block))
return color_dict[name]
if not hasattr(identifier, '__len__'):
raise ValueError('Invalid color definition '+str(identifier))
qcol = None
if len(block) == 2:
name, alpha = block
if dic is None or name not in dic:
if len(identifier) == 2:
name, alpha = identifier
if color_dict is None or name not in color_dict:
if name[0] != '#':
raise ValueError('Undefined color name '+str(block))
raise ValueError('Undefined color identifier '+str(identifier))
qcol = QtGui.QColor( name )
else:
qcol = dic[ name ]
elif len(block) in (3,4):
qcol = QtGui.QColor( *block )
qcol = color_dict[ name ]
elif len(identifier) in (3,4):
qcol = QtGui.QColor( *identifier )
if alpha is not None and qcol is not None:
qcol = QtGui.QColor(qcol) # make a copy before changing alpha
# distinct QColors are now created for each color
# qcol = QtGui.QColor(qcol) # make a copy before changing alpha
qcol.setAlpha( alpha )
return qcol
def assemble_palette( raw_col, func_col, plot_col=None ):
def get(identifier, *args):
"""
assemble palette color dictionary from parts:
raw_col should contain color information in (R,G,B,(A)) or hex format
func_col typically contains keys of colors defined before
plot_col is a list of plotting colors to be included as 'c0' to 'cX' (in hex)
"""
pal = {}
for part in [raw_col, func_col]:
for key in part:
col = part[key]
pal[key] = block_to_QColor( col, pal )
if plot_col is not None:
for idx, col in enumerate( plot_col ):
key = 'p{:X}'.format(idx) # plot color 'pX' does not overlap hexadecimal codes.
pal[key] = block_to_QColor( col, pal )
return pal
DEFAULT_PALETTE = assemble_palette( LEGACY_RAW, LEGACY_FUNC, LEGACY_PLOT )
Returns a Palette object that can be applied to update the PyQtGraph color scheme
=============== ====================================================================
**Arguments:**
identifier 'system' (default): Colors are based on current Qt QPalette
'legacy': The color scheme of previous versions of PyQtGraph
'monochrome' ['color identifier']: Creates a palette that imitates
a monochrome computer monitor.
'color identifier' can be one of
'green', 'amber', 'blue', 'red', 'lavender', 'pink'
or a tuple of relative R,G,B contributions in range 0.0 to 1.0
def get(name):
if name == 'relaxed_dark':
pal = assemble_palette( RELAXED_RAW, RELAXED_DARK_FUNC, RELAXED_DARK_PLOT )
elif name == 'relaxed_light':
pal = assemble_palette( RELAXED_RAW, RELAXED_LIGHT_FUNC, RELAXED_LIGHT_PLOT )
else:
pal = DEFAULT_PALETTE
return Palette( colors=pal )
def make_monochrome(color='green', n_colors=8):
"""
Returns a Palette object imitating a monochrome computer screen
=============== =================================================================
**Arguments:**
color Primary color description. Can be one of predefined identifiers
'green' or 'amber'
or a tuple of relative R,G,B contributions in range 0.0 to 1.0
=============== =================================================================
{dictionary}: full palette specification, see palette.py for details
=============== ====================================================================
"""
cm = colormap.make_monochrome(color)
if cm is None: return None
raw = {}
for idx in range(8):
key = 'col_m{:d}'.format(idx)
raw[key] = cm[ idx/9 ]
# print('added color',key,'as',raw[key].name(),raw[key].getRgb())
func = {
'gr_bg' : 'col_m0',
'gr_fg' : 'col_m4',
'gr_txt': 'col_m6',
'gr_acc': 'col_m5',
'gr_hov': 'col_m7',
'gr_reg': ('col_m1', 30),
'k': 'col_m0', 'd': 'col_m1', 's': 'col_m3', 'l': 'col_m6', 'w': 'col_m7'
}
avail = raw.copy() # generate a disctionary of available colors
del avail['col_m0'] # already taken by black
del avail['col_m7'] # already taken by white
needed = {
'b': ( 0, 0,255), 'c': ( 0,255,255), 'g': ( 0,255, 0),
'y': (255,255, 0), 'r': (255, 0, 0), 'm': (255, 0,255)
}
for nd_key in needed:
nd_tup = needed[nd_key] # this is the int RGB tuple we are looking to represent
best_dist = 1e10
best_key = None
for av_key in avail:
av_tup = avail[av_key].getRgb() # returns (R,G,B,A) tuple
dist = (nd_tup[0]-av_tup[0])**2 + (nd_tup[1]-av_tup[1])**2 + (nd_tup[2]-av_tup[2])**2
if dist < best_dist:
best_dist = dist
best_key = av_key
# print('assigning',nd_key,'as',best_key,':',avail[best_key].getRgb() )
func[nd_key] = avail[best_key]
del avail[best_key] # remove from available list
pal = assemble_palette( raw, func )
return Palette( colors=pal, cmap=cm, n_colors=8 )
if identifier == 'system':
pal = Palette()
return pal # default QPalette based settings
if identifier == 'monochrome':
pal = Palette()
pal.setMonochrome( *args )
return pal
if identifier in PALETTE_DEFINITIONS:
info = PALETTE_DEFINITIONS[identifier].copy()
sampling_info = info.pop('colormap_sampling', None)
pal = Palette( cmap=sampling_info, colors=info )
return pal
raise KeyError("Unknown palette identifier '"+str(identifier)+"'")
class Palette(object):
# minimum colors to be defined:
def __init__(self, colors=None, cmap=None, n_colors=8):
"""
A Palette object provides a set of colors that can conveniently applied
to the PyQtGraph color scheme.
It specifies at least the following colors, but additional one can be added:
Primary colors:
'b', 'c', 'g', 'y', 'r', 'm'
Gray scale:
'k', 'd', 'l', 'w' ranging from black to white
's' slate gray
System colors:
'gr_bg', 'gr_fg', 'gr_txt' graph background, foreground and text colors
'gr_wdw' window background color
'gr_reg' partially transparent region shading color
'gr_acc' accent for UI elements
'gr_hlt' highlight for selected elements
Plot colors:
'p0' to 'p7' typically sampled from a ColorMap
"""
def __init__(self, cmap=None, colors=None ):
super().__init__()
self.palette = colors
self.cmap = cmap
self.n_colors = int(n_colors)
if self.n_colors < 8: self.n_colors = 8 # enforce minimum number of plot colors
if self.cmap is not None:
sep = 1/n_colors
for idx in range(self.n_colors):
key = 'p{:x}'.format( idx )
if key in self.palette:
continue # do not overwrite user-provided plot colors
val = sep * (0.5 + idx)
col = self.cmap[val]
self.palette[key] = col
# print('assigning',key,'as',col,':',col.name())
self.palette = { # populate dictionary of QColors with legacy defaults
'b': QtGui.QColor( 0, 0,255,255), 'g': QtGui.QColor( 0,255, 0,255),
'r': QtGui.QColor(255, 0, 0,255), 'c': QtGui.QColor( 0,255,255,255),
'm': QtGui.QColor(255, 0,255,255), 'y': QtGui.QColor(255,255, 0,255),
'k': QtGui.QColor( 0, 0, 0,255), 'w': QtGui.QColor(255,255,255,255),
'd': QtGui.QColor(150,150,150,255), 'l': QtGui.QColor(200,200,200,255),
's': QtGui.QColor(100,100,150,255)
}
self.dark = None # is initially set when assigning system palette
self.emulateSystem()
self.cmap = None
if cmap is not None: # prepare plot colors from provided colormap
if isinstance(cmap, (str, colormap.ColorMap) ): # sampleColorMap will convert if needed
self.sampleColorMap( cmap=cmap)
if isinstance(cmap, (tuple,list)): # ('identifier', start, step)
cmap, start, step = cmap
self.sampleColorMap(cmap=cmap, start=start, step=step)
if colors is not None:
# print('color dictionary:', colors)
self.add(colors) # override specified colors
def __getitem__(self, key):
""" Convenient shorthand access to palette colors """
if isinstance(key, str): # access by color name
return self.palette.get(key,None)
if isinstance(key, int): # access by plot color index
idx = key % 8 # map to 0 to 8
key = 'p'+str(idx)
return self.palette.get(key,None)
return None
def colorMap(self):
"""
Return the ColorMap object used to create plot colors or 'None' if not assigned.
"""
return self.cmap
# needs: addColors
# needs to be aware of number of plot colors
# needs to be indexable by key and numerical plot color
# indexed plot colors need to wrap around to work for any index.
# needs: clearColors
def sampleColorMap(self, cmap=None, n_colors=8, prefix='p', start=0., step=None ):
"""
Sample a ColorMap to update defined plot colors
============= ===============================================================================
**Arguments**
cmap a ColorMap object to be sampled. If not given, a default color map is used
n_colors default '8': Number of assigned colors.
The default set needs to include 'p0' to 'p7'
prefix default 'p' assigns colors as 'p0' to 'pXX', at least 'p7' is required.
Additional sets can be defined with a different prefix, e.g. 'col_0' to 'col_7'
All prefixes need to start with 'p' or 'col' to avoid namespace overlap with
functional colors and hexadecimal numbers
start first sampled value (default is 0.000)
step step between samples. Default 'None' equally samples n colors from a
linear colormap, including values 0.0 and 1.0.
Color values > 1. and < 0. wrap around!
============= ===============================================================================
"""
valid = prefix[0]=='p' or ( len(prefix)>=3 and prefix[:3]=='col')
if not valid:
raise ValueError("'prefix' of plot color needs to start with 'p'.")
if cmap is None:
cmap = self.cmap
if isinstance(cmap, str):
cmap = colormap.get(cmap) # obtain ColorMap if identifier is given
if cmap is None:
raise ValueError("Please specify 'cmap' parameter when no default colormap is available.")
if not isinstance( cmap, colormap.ColorMap ):
raise ValueError("Failed to obtain ColorMap object for 'cmap' = '+str(cmap).")
if prefix == 'p':
self.cmap = cmap # replace default color map
n_colors = 8 # always define 8 primary plot colors
if step is None:
step = 1 / (n_colors - 1) # sample 0. to 1. (inclusive) by default
for cnt in range(n_colors):
val = start + cnt * step
# print( val )
if val > 1.0 or val < 0.0: # don't touch 1.0 value
val = val % 1. # but otherwise map to 0 to 1 range
qcol = cmap[val]
key = prefix + str(cnt)
self.palette[key] = qcol
def add(self, colors):
"""
Add colors given in dictionary 'colors' to the palette
All colors will be converted to QColor.
Setting 'gr_bg' with a mean color value < 127 will set the palette's 'dark' property
"""
for key in colors:
col = identifier_to_QColor( colors[key], color_dict=self.palette )
if key == 'gr_bg':
bg_tuple = col.getRgb()
self.dark = bool( sum( bg_tuple[:3] ) < 3 * 127 ) # dark mode?
self.palette[key] = col
def emulateSystem(self):
"""
Retrieves the current Qt 'active' palette and extracts the following colors:
===================================== ============================================================================
'gr_fg','gr_txt' from QPalette.Text (foreground color used with Base)
'gr_bg' from QPalette.Base (background color for e.g. text entry widgets)
'gr_wdw' from QPalette.Window (a general background color)
'gr_reg' from QPalette.AlternateBase (alternating row background color)
'gr_acc' from QPalette.Link (color used for unvisited hyperlinks)
'gr_hlt' from QPalette.Highlight (color to indicate a selected item)
===================================== ============================================================================
"""
app = QtWidgets.QApplication.instance()
if app is None: return None
qPalette = app.palette()
col_grp = QtGui.QPalette.Active
colors = {}
for key, alpha, col_role in (
('gr_bg' , None, QtGui.QPalette.Base), # background color for e.g. text entry
('gr_fg' , None, QtGui.QPalette.WindowText), # overall foreground text color
('gr_txt', None, QtGui.QPalette.Text), # foreground color used with Base
('gr_reg', 100, QtGui.QPalette.AlternateBase), # alternating row background color
('gr_acc', None, QtGui.QPalette.Link), # color of unvisited hyperlink
('gr_hlt', None, QtGui.QPalette.Highlight), # color to indicate a selected item
('ui_wind', None, QtGui.QPalette.Window), # a general background color
('ui_text', None, QtGui.QPalette.WindowText) # overall foreground text color
):
qcol = qPalette.color(col_grp, col_role)
if alpha is not None: qcol.setAlpha(alpha)
colors[key] = qcol
self.add(colors)
colors = {
'b': QtCore.Qt.blue , 'c': QtCore.Qt.cyan, 'g': QtCore.Qt.green ,
'y': QtCore.Qt.yellow, 'r': QtCore.Qt.red , 'm': QtCore.Qt.magenta,
'w': QtCore.Qt.white , 's': QtCore.Qt.gray, 'k': QtCore.Qt.black ,
'l': QtCore.Qt.lightGray, 'd': QtCore.Qt.darkGray
}
if not self.dark: # darken some colors for light mode
colors['c'] = QtCore.Qt.darkCyan
colors['g'] = QtCore.Qt.darkGreen
colors['y'] = QtCore.Qt.darkYellow
colors['m'] = QtCore.Qt.darkMagenta
self.add(colors)
colors = {
'p'+str(idx) : name
for idx, name in enumerate( ('gr_fg', 'y','r','m','b','c','g','s') )
}
self.add(colors)
def setMonochrome(self, color='green'):
"""
Updates graph colors with a set based on 'monochrome' color map,
imitating a monochrome computer screen
============== =================================================================
**Arguments:**
color Primary color description. Can be one of predefined identifiers
'green', 'amber', 'blue'
or a tuple of relative R,G,B contributions in range 0.0 to 1.0
============== =================================================================
"""
cmap = colormap.makeMonochrome(color)
if cmap is None:
raise ValueError("Failed to generate color for '"+str(color)+"'")
self.sampleColorMap( cmap=cmap, start=1.0, step=-1/8 ) # assign bright to dark, don't go all the way to background.
# define colors 'm0' (near-black) to 'm8' (near-white):
self.sampleColorMap( n_colors=9, cmap=cmap, step=1/8, prefix='col_m' )
colors = {
'gr_bg' : 'col_m0', 'gr_fg' : 'col_m4',
'gr_txt': 'col_m5', 'gr_acc': 'col_m6',
'gr_hlt': 'col_m7', 'gr_reg': ('col_m1', 30),
'k': 'col_m0', 'd': 'col_m1', 's': 'col_m3', 'l': 'col_m6', 'w': 'col_m7'
}
self.add( colors )
# make a dictionary of plot colors (except darkest and lightest) to emulate primary colors:
avail = { key: self.palette[key] for key in ('p0','p1','p2','p3','p4','p5','p6') }
needed = { # int RGB colors that we are looking to emulate:
'b': ( 0, 0,255), 'c': ( 0,255,255), 'g': ( 0,255, 0),
'y': (255,255, 0), 'r': (255, 0, 0), 'm': (255, 0,255)
}
colors = {}
for nd_key in needed:
nd_tup = needed[nd_key] # int RGB tuple to be represented
best_dist = 1e10
best_key = None
for av_key in avail:
av_tup = avail[av_key].getRgb() # returns (R,G,B,A) tuple
sq_dist = (nd_tup[0]-av_tup[0])**2 + (nd_tup[1]-av_tup[1])**2 + (nd_tup[2]-av_tup[2])**2
if sq_dist < best_dist:
best_dist = sq_dist
best_key = av_key
# print('assigning',nd_key,'as',best_key,':',avail[best_key].getRgb() )
colors[nd_key] = avail[best_key]
del avail[best_key] # remove from available list
self.add( colors )
def apply(self):
"""
provides palette to NamedColorManager, which triggers a global refresh of named colors
Applies this palette to the overall PyQtGraph color scheme.
This provides the palette to NamedColorManager, which triggers a global refresh of named colors
"""
fn.NAMED_COLOR_MANAGER.redefinePalette( colors=self.palette )