added procedure generation for monochrome palettes

This commit is contained in:
Nils Nemitz 2021-03-13 03:26:28 +09:00
parent f14e687df8
commit 172cef1628
5 changed files with 159 additions and 71 deletions

View File

@ -24,11 +24,11 @@ class MainWindow(QtWidgets.QMainWindow):
self.setWindowTitle('pyqtgraph example: Palette application test')
self.resize(600,600)
pg.palette.get('monogreen').apply()
pg.palette.get('relaxed-dark').apply()
main_layout = QtWidgets.QGridLayout( main_wid )
gr_wid = pg.GraphicsLayoutWidget(show=True)
main_layout.addWidget( gr_wid, 0,0, 1,4 )
main_layout.addWidget( gr_wid, 0,0, 1,5 )
btn = QtWidgets.QPushButton('continuous')
btn.clicked.connect(self.handle_button_timer_on)
@ -38,29 +38,20 @@ class MainWindow(QtWidgets.QMainWindow):
btn.clicked.connect(self.handle_button_timer_off)
main_layout.addWidget(btn, 2,0, 1,1 )
btn = QtWidgets.QPushButton('apply <legacy>')
btn.clicked.connect(self.handle_button_pal1)
main_layout.addWidget(btn, 1,2, 1,1 )
btn = QtWidgets.QPushButton('apply <mono green>')
btn.clicked.connect(self.handle_button_pal2)
main_layout.addWidget(btn, 1,3, 1,1 )
btn = QtWidgets.QPushButton('apply <relaxed - dark>')
btn.clicked.connect(self.handle_button_pal3)
main_layout.addWidget(btn, 2,2, 1,1 )
btn = QtWidgets.QPushButton('apply <relaxed - light>')
btn.clicked.connect(self.handle_button_pal4)
main_layout.addWidget(btn, 2,3, 1,1 )
btn = QtWidgets.QPushButton('legacy fg/bg override 1')
btn.clicked.connect(self.handle_button_leg1)
main_layout.addWidget(btn, 3,2, 1,1 )
btn = QtWidgets.QPushButton('legacy fg/bg override 2')
btn.clicked.connect(self.handle_button_leg2)
main_layout.addWidget(btn, 3,3, 1,1 )
palette_buttons = (
('apply <legacy>', 1,2, self.handle_button_pal1 ),
('legacy fg/bg 1', 1,3, self.handle_button_leg1 ),
('legacy fg/bg 2', 1,4, self.handle_button_leg2 ),
('apply <mono green>', 2,2, self.handle_button_mono1 ),
('apply <mono amber>', 2,3, self.handle_button_mono2 ),
('apply <mono blue>' , 2,4, self.handle_button_mono3 ),
('apply <relaxed-dark>' , 3,2, self.handle_button_pal2 ),
('apply <relaxed-light>', 3,3, self.handle_button_pal3 )
)
for text, row, col, func in palette_buttons:
btn = QtWidgets.QPushButton(text)
btn.clicked.connect(func)
main_layout.addWidget(btn, row,col, 1,1 )
self.plt = gr_wid.addPlot()
self.plt.enableAutoRange(False)
@ -75,15 +66,17 @@ class MainWindow(QtWidgets.QMainWindow):
self.curve1 = pg.PlotDataItem(pen='r', symbol='o', symbolSize=10, symbolPen='gr_fg', symbolBrush=('y',127))
self.plt.addItem(self.curve1)
self.curve2 = pg.PlotCurveItem(pen='p3', brush='p4')
self.curve2 = pg.PlotCurveItem(pen='w', brush='d')
self.curve2.setFillLevel(0)
self.plt.addItem(self.curve2)
self.show()
self.pal_1 = pg.palette.get('legacy')
self.pal_2 = pg.palette.get('monogreen')
self.pal_3 = pg.palette.get('relaxed_dark')
self.pal_4 = pg.palette.get('relaxed_light')
self.pal_2 = pg.palette.get('relaxed_dark')
self.pal_3 = pg.palette.get('relaxed_light')
self.mpal_1 = pg.palette.make_monochrome('green')
self.mpal_2 = pg.palette.make_monochrome('amber')
self.mpal_3 = pg.palette.make_monochrome('blue')
self.lastTime = time()
self.fps = None
@ -107,24 +100,28 @@ class MainWindow(QtWidgets.QMainWindow):
def handle_button_pal1(self):
""" apply palette 1 on request """
print('--> legacy')
self.pal_1.apply()
def handle_button_pal2(self):
""" apply palette 2 on request """
print('--> mono green')
self.pal_2.apply()
def handle_button_pal3(self):
""" apply palette 1 on request """
print('--> relax(light)')
self.pal_3.apply()
def handle_button_pal4(self):
""" apply palette 1 on request """
print('--> relax(light)')
self.pal_4.apply()
def handle_button_mono1(self):
""" apply monochrome palette 1 on request """
self.mpal_1.apply()
def handle_button_mono2(self):
""" apply monochrome palette 2 on request """
self.mpal_2.apply()
def handle_button_mono3(self):
""" apply monochrome palette 3 on request """
self.mpal_3.apply()
def handle_button_leg1(self):
""" test legacy background / foreground overrides """
pg.setConfigOption('background', '#ff0000')

View File

@ -47,6 +47,21 @@ for idx in range(height):
num_bars = 0
lw.addLabel('=== monochrome generator ===')
num_bars += 1
lw.nextRow()
monochrome_colors = ('blue', 'green', 'amber', 'red', 'pink', 'lavender', (0.5, 0.5, 0.0) )
for mono_val in monochrome_colors:
num_bars += 1
lw.addLabel(str(mono_val))
cmap = pg.colormap.make_monochrome(mono_val)
imi = pg.ImageItem()
imi.setImage(img)
imi.setLookupTable( cmap.getLookupTable(alpha=True) )
vb = lw.addViewBox(lockAspect=True, enableMouse=False)
vb.addItem(imi)
lw.nextRow()
lw.addLabel('=== local color maps ===')
num_bars += 1
lw.nextRow()

View File

@ -178,7 +178,40 @@ def _get_from_colorcet(name):
color=color_list) #, names=color_names)
_mapCache[name] = cm
return cm
def make_monochrome(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'
or a tuple of relative R,G,B contributions in range 0.0 to 1.0
=============== =================================================================
"""
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])
delta_arr = active - leakage
predefined = {
'green': (0.00, 1.00, 0.33), 'amber' : (1.00, 0.50, 0.00),
'blue' : (0.00, 0.50, 1.00), 'red' : (1.00, 0.10, 0.00),
'pink' : (1.00, 0.10, 0.50), 'lavender': (0.67, 0.33, 1.00)
}
if color in predefined: color = predefined[color]
if not isinstance(color, tuple):
definitions = ["'"+key+"'" for key in predefined]
raise ValueError("'color' needs to be an (R,G,B) tuple of floats or one of "+definitions.join(', ') )
r, g, b = color
color_list = []
for leak, delta in zip(leakage, delta_arr):
color_tuple = (
r * delta + leak,
g * delta + leak,
b * delta + leak )
color_list.append(color_tuple)
cm = ColorMap(pos=stops, color=color_list )
return cm
class ColorMap(object):
"""

View File

@ -30,7 +30,7 @@ for key, col in [ # add functional colors
DEFAULT_COLORS[key] = DEFAULT_COLORS[col]
for idx, col in enumerate( ( # twelve predefined plot colors
'l','y','r','m','b','c','g','d','d','d','d','d'
'l','y','r','m','b','c','g','d'
) ):
key = 'p{:X}'.format(idx)
DEFAULT_COLORS[key] = DEFAULT_COLORS[col]

View File

@ -1,6 +1,7 @@
from .Qt import QtGui
from . import functions as fn # namedColorManager
from . import colormap
__all__ = ['Palette']
@ -26,29 +27,7 @@ LEGACY_FUNC = { # functional colors:
'gr_reg' : ( 0, 0,255, 50)
}
LEGACY_PLOT = [ # plot / accent colors:
'l','y','r','m','b','c','g','d','d','d','d','d'
]
MONOGREEN_RAW = {
'col_g0':'#001000', 'col_g1':'#014801', 'col_g2':'#077110', 'col_g3':'#159326',
'col_g4':'#2DB143', 'col_g5':'#50CD65', 'col_g6':'#7FE7A0', 'col_g7':'#BFFFD4'
}
MONOGREEN_FUNC = {
'gr_fg' : 'col_g5',
'gr_bg' : 'col_g0', # for distinction in testing, should be col_g0
'gr_txt' : 'col_g5',
'gr_acc' : 'col_g5',
'gr_hov' : 'col_g7',
'gr_reg' : ('col_g6', 30),
# legacy colors:
'b': 'col_g7', 'c': 'col_g6', 'g': 'col_g5',
'y': 'col_g4', 'r': 'col_g3', 'm': 'col_g2',
'k': 'col_g1', 'w': 'col_g7',
'd': 'col_g1', 'l': 'col_g4', 's': 'col_g7'
}
MONOGREEN_PLOT = [ # plot / accent colors:
'col_g6', 'col_g4', 'col_g2', 'col_g7', 'col_g5', 'col_g3',
'col_g1', 'col_g3', 'col_g3', 'col_g3', 'col_g3', 'col_g3'
'l','y','r','m','b','c','g','d'
]
RELAXED_RAW = { # "fresh" raw colors:
@ -133,6 +112,8 @@ def block_to_QColor( block, dic=None ):
# 'name'
# ('name', alpha)
# (R,G,B) / (R,G,B,alpha)
if isinstance(block, QtGui.QColor):
return block # this is already a QColor
alpha = None
if isinstance(block, str): # return known QColor
name = block
@ -157,11 +138,12 @@ def block_to_QColor( block, dic=None ):
qcol = QtGui.QColor( *block )
if alpha is not None and qcol is not None:
qcol = QtGui.QColor(qcol) # make a copy before changing alpha
qcol.setAlpha( alpha )
return qcol
def assemble_palette( raw_col, func_col, plot_col ):
def assemble_palette( raw_col, func_col, plot_col=None ):
"""
assemble palette color dictionary from parts:
raw_col should contain color information in (R,G,B,(A)) or hex format
@ -173,9 +155,10 @@ def assemble_palette( raw_col, func_col, plot_col ):
for key in part:
col = part[key]
pal[key] = block_to_QColor( col, pal )
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 )
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 )
@ -185,19 +168,79 @@ def get(name):
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 )
elif name == 'monogreen':
pal = assemble_palette( MONOGREEN_RAW, MONOGREEN_FUNC, MONOGREEN_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
=============== =================================================================
"""
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 )
class Palette(object):
# minimum colors to be defined:
def __init__(self, colors=None):
def __init__(self, colors=None, cmap=None, n_colors=8):
super().__init__()
self.palette = colors
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())
# needs: addColors
# needs to be aware of number of plot colors
# needs to be indexable by key and numerical plot color