added procedure generation for monochrome palettes
This commit is contained in:
parent
f14e687df8
commit
172cef1628
@ -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')
|
||||
|
@ -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()
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user