implemented some suggested improvements
This commit is contained in:
parent
9cb8986d90
commit
1f8c82ff30
@ -317,7 +317,7 @@ if QT_LIB in [PYQT5, PYQT6]:
|
||||
loadUiType = uic.loadUiType
|
||||
|
||||
QtCore.Signal = QtCore.pyqtSignal
|
||||
QtCore.Slot = QtCore.pyqtSlot
|
||||
# QtCore.Slot = QtCore.pyqtSlot # The current policy is to avoid slot decorators.
|
||||
|
||||
if QT_LIB == PYSIDE6:
|
||||
# PySide6 6.0 has a missing binding
|
||||
|
@ -78,7 +78,7 @@ class ColorRegistry(QtCore.QObject):
|
||||
Typically instantiated by functions.py as COLOR_REGISTRY
|
||||
Instantiated by 'functions.py' and retrievable as functions.COLOR_REGISTRY
|
||||
"""
|
||||
paletteHasChangedSignal = QtCore.Signal() # equated to pyqtSignal in qt.py for PyQt
|
||||
graphStyleChanged = QtCore.Signal() # equated to pyqtSignal in qt.py for PyQt
|
||||
_registrationGenerator = itertools.count()
|
||||
|
||||
def __init__(self, color_dic):
|
||||
@ -168,7 +168,7 @@ class ColorRegistry(QtCore.QObject):
|
||||
skipCache = True
|
||||
register = False
|
||||
elif name not in self.color_dic:
|
||||
warnings.warn('Unknown color identifier '+str(name)+' enocuntered.')
|
||||
warnings.warn(f"Unknown color identifier '{name}' encountered.")
|
||||
return None # unknown color identifier
|
||||
if not skipCache and desc in self.color_cache:
|
||||
return self.color_cache[desc]
|
||||
@ -202,7 +202,7 @@ class ColorRegistry(QtCore.QObject):
|
||||
skipCache = True
|
||||
register = False
|
||||
elif name not in self.color_dic:
|
||||
warnings.warn('Unknown color identifier '+str(name)+' enocuntered in pen descriptor.')
|
||||
warnings.warn(f"Unknown color identifier '{name}' encountered in pen descriptor.")
|
||||
return None # unknown color identifier
|
||||
if not skipCache and desc in self.pen_cache:
|
||||
return self.pen_cache[desc]
|
||||
@ -235,7 +235,7 @@ class ColorRegistry(QtCore.QObject):
|
||||
skipCache = True
|
||||
register = False
|
||||
elif name not in self.color_dic:
|
||||
warnings.warn('Unknown color identifier '+str(name)+' enocuntered in brush descriptor.')
|
||||
warnings.warn(f"Unknown color identifier '{name}' encountered in brush descriptor.")
|
||||
return None # unknown color identifier
|
||||
if not skipCache and desc in self.brush_cache:
|
||||
return self.brush_cache[desc]
|
||||
@ -311,10 +311,10 @@ class ColorRegistry(QtCore.QObject):
|
||||
"""
|
||||
Removes obj (QColor, QPen or QBrush) from the registry, usually called by finalize on deletion
|
||||
"""
|
||||
obj, desc = self.registered_objects[registration]
|
||||
# obj, desc = self.registered_objects[registration]
|
||||
# print('unregistering', registration, '(',str(obj),'):',str(desc))
|
||||
# del obj, desc
|
||||
del self.registered_objects[registration]
|
||||
del obj, desc
|
||||
|
||||
def colors(self):
|
||||
""" return current list of colors """
|
||||
@ -326,24 +326,23 @@ class ColorRegistry(QtCore.QObject):
|
||||
|
||||
def redefinePalette(self, colors=None):
|
||||
"""
|
||||
Update list of named colors if 'colors' dictionary is given
|
||||
Update list of registered colors if 'colors' dictionary is given
|
||||
Emits paletteHasChanged signals to color objects and widgets, even if color_dict is None
|
||||
"""
|
||||
if colors is not None:
|
||||
for key in DEFAULT_COLORS:
|
||||
if key not in colors:
|
||||
raise ValueError("Palette definition is missing '"+str(key)+"'")
|
||||
raise ValueError(f"Palette definition is missing '{key}'")
|
||||
self.color_dic.clear()
|
||||
self.color_dic.update(colors)
|
||||
|
||||
# notifies named color objects of new assignments:
|
||||
# for key in self.registered_objects:
|
||||
# notifies registerd color objects of new assignments:
|
||||
for ref, desc in self.registered_objects.values():
|
||||
# ref, desc = self.registered_objects[key]
|
||||
obj = ref()
|
||||
# print('updating', obj)
|
||||
if obj is None:
|
||||
warnings.warn('Expired object with descriptor '+str(desc)+' remains in color registry.', RuntimeWarning)
|
||||
warnings.warn(f"Expired object with descriptor '{desc})' remains in color registry.", RuntimeWarning)
|
||||
elif isinstance(obj, QtGui.QColor):
|
||||
self._update_QColor(obj, desc)
|
||||
elif isinstance(obj, QtGui.QPen):
|
||||
@ -351,4 +350,4 @@ class ColorRegistry(QtCore.QObject):
|
||||
elif isinstance(obj, QtGui.QBrush):
|
||||
self._update_QBrush(obj, desc)
|
||||
# notify all graphics widgets that redraw is required:
|
||||
self.paletteHasChangedSignal.emit()
|
||||
self.graphStyleChanged.emit()
|
||||
|
@ -189,7 +189,7 @@ def getFromColorcet(name):
|
||||
for hex_str in color_strings:
|
||||
if hex_str[0] != '#': continue
|
||||
if len(hex_str) != 7:
|
||||
raise ValueError('Invalid color string '+str(hex_str)+' in colorcet import.')
|
||||
raise ValueError(f"Invalid color string '{hex_str}' in colorcet import.")
|
||||
color_tuple = tuple( bytes.fromhex( hex_str[1:] ) )
|
||||
color_list.append( color_tuple )
|
||||
if len(color_list) == 0:
|
||||
@ -202,36 +202,45 @@ def getFromColorcet(name):
|
||||
|
||||
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', 'amber', 'blue', 'red', 'lavender', 'pink'
|
||||
or a tuple of relative R,G,B contributions in range 0.0 to 1.0
|
||||
=============== =================================================================
|
||||
Returns a ColorMap object imitating a monochrome computer screen.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
color: str of tuple of floats
|
||||
Primary color description. Can be one of predefined identifiers
|
||||
'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)
|
||||
name = f'monochrome-{color}' # if needed, this automatically stringifies numerical tuples
|
||||
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),
|
||||
'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]
|
||||
definitions = [f"'{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 = (
|
||||
|
||||
color_list = [
|
||||
(
|
||||
r * delta + leak,
|
||||
g * delta + leak,
|
||||
b * delta + leak )
|
||||
color_list.append(color_tuple)
|
||||
b * delta + leak
|
||||
) for leak, delta in zip(leakage, delta_arr)
|
||||
]
|
||||
# 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)
|
||||
cmap = ColorMap(name=name, pos=stops, color=color_list )
|
||||
return cmap
|
||||
|
||||
@ -330,7 +339,7 @@ class ColorMap(object):
|
||||
if mapping in [self.CLIP, self.REPEAT, self.DIVERGING, self.MIRROR]:
|
||||
self.mapping_mode = mapping # only allow defined values
|
||||
else:
|
||||
raise ValueError("Undefined mapping type '{:s}'".format(str(mapping)) )
|
||||
raise ValueError(f"Undefined mapping type '{mapping}'")
|
||||
self.stopsCache = {}
|
||||
|
||||
def __str__(self):
|
||||
|
@ -32,8 +32,6 @@ from .python2_3 import asUnicode
|
||||
# legacy color definitions:
|
||||
# ColorRegistry now maintains the primary list of palette colors,
|
||||
# accessible through functions.COLOR_REGISTRY.colors().
|
||||
# # NamedColorManager now maintains the primary list of palette colors,
|
||||
# # accessible through functions.NAMED_COLOR_MANAGER.colors().
|
||||
# For backwards compatibility, this dictionary is updated to contain the same information.
|
||||
#
|
||||
# For the user, colors and color palettes are most conveniently accessed through a Palette object.
|
||||
@ -229,7 +227,7 @@ def mkColor(*args):
|
||||
types. Accepted arguments are:
|
||||
|
||||
================ ===========================================================
|
||||
'name' any color name specifed in palette
|
||||
'name' any color name specifed in active palette
|
||||
('name', alpha) color name from palette with specified opacity 0-255
|
||||
R, G, B, [A] integers 0-255
|
||||
(R, G, B, [A]) tuple of integers 0-255
|
||||
@ -258,9 +256,10 @@ def mkColor(*args):
|
||||
return args # pass through registered pen directly
|
||||
return QtGui.QColor(args) ## return a copy of this color
|
||||
|
||||
err = 'Could not create a color from {:s}(type: {:s})'.format(str(args), str(type(args)))
|
||||
# no short-circuit, continue parsing to construct a QPen and register it if appropriate
|
||||
err = f'Could not create a color from {args} (type: {type(args)})'
|
||||
result = COLOR_REGISTRY.getRegisteredColor(args)
|
||||
if result is not None: # make a NamedPen
|
||||
if result is not None: # return this color if we got one.
|
||||
return result
|
||||
|
||||
# print('trying extra methods on',args)
|
||||
@ -359,7 +358,7 @@ def mkBrush(*args, **kargs):
|
||||
# if args is None:
|
||||
# return QtGui.QBrush( QtCore.Qt.NoBrush ) # explicit None means "no brush"
|
||||
|
||||
# no short-circuit, continue parsing to construct QPen or NamedPen
|
||||
# no short-circuit, continue parsing to construct a QBrush and register it if appropriate
|
||||
if 'hsv' in kargs: # hsv argument takes precedence
|
||||
qcol = hsvColor( *kargs['hsv'] )
|
||||
return QtGui.QBrush(qcol)
|
||||
@ -373,9 +372,7 @@ def mkBrush(*args, **kargs):
|
||||
if args == () or args == []:
|
||||
# print(' functions: returning default color registered brush')
|
||||
return COLOR_REGISTRY.getRegisteredBrush('gr_fg')
|
||||
# return NamedBrush( 'gr_fg', manager=NAMED_COLOR_MANAGER ) # default foreground color
|
||||
|
||||
# result = parseNamedColorSpecification(args)
|
||||
# Do the the arguments make a suitable brush descriptor?
|
||||
result = COLOR_REGISTRY.getRegisteredBrush(args)
|
||||
if result is not None:
|
||||
@ -421,7 +418,7 @@ def mkPen(*args, **kargs):
|
||||
# if args is None:
|
||||
# return QtGui.QPen( QtCore.Qt.NoPen ) # explicit None means "no pen"
|
||||
|
||||
# no short-circuit, continue parsing to construct QPen or NamedPen
|
||||
# no short-circuit, continue parsing to construct a QPen and register it if appropriate
|
||||
width = kargs.get('width', 1) # width 1 unless specified otherwise
|
||||
if 'hsv' in kargs: # hsv argument takes precedence
|
||||
qcol = hsvColor( *kargs['hsv'] )
|
||||
@ -436,9 +433,9 @@ def mkPen(*args, **kargs):
|
||||
qpen = COLOR_REGISTRY.getRegisteredPen( ('gr_fg', width) ) # default foreground color
|
||||
else:
|
||||
result = COLOR_REGISTRY.getRegisteredPen(args)
|
||||
if result is not None: # make a NamedPen
|
||||
if result is not None: # return this pen if we got one
|
||||
qpen = result
|
||||
else: # make a QPen
|
||||
else: # make a regular QPen
|
||||
qcol = mkColor(args)
|
||||
qpen = QtGui.QPen(QtGui.QBrush(qcol), width)
|
||||
# now apply styles according to kw arguments:
|
||||
@ -446,7 +443,7 @@ def mkPen(*args, **kargs):
|
||||
dash = kargs.get('dash', None)
|
||||
cosmetic = kargs.get('cosmetic', True)
|
||||
if qpen is None:
|
||||
raise ValueError('Failed to construct QPen from arguments '+str(args)+','+str(kargs) )
|
||||
raise ValueError("Failed to construct QPen from arguments '{args}','{kargs}'." )
|
||||
qpen.setCosmetic(cosmetic)
|
||||
if style is not None:
|
||||
qpen.setStyle(style)
|
||||
|
@ -1212,9 +1212,9 @@ class AxisItem(GraphicsWidget):
|
||||
return
|
||||
return lv.mouseClickEvent(event)
|
||||
|
||||
def styleHasChanged(self):
|
||||
def updateGraphStyle(self):
|
||||
""" self.picture needs to be invalidated to initiate full redraw """
|
||||
self.picture = None
|
||||
self.labelStyle['color'] = self._textPen.color().name()
|
||||
self._updateLabel()
|
||||
super().styleHasChanged()
|
||||
super().updateGraphStyle()
|
||||
|
@ -19,7 +19,7 @@ class GraphicsObject(GraphicsItem, QtGui.QGraphicsObject):
|
||||
QtGui.QGraphicsObject.__init__(self, *args)
|
||||
self.setFlag(self.ItemSendsGeometryChanges)
|
||||
GraphicsItem.__init__(self)
|
||||
fn.COLOR_REGISTRY.paletteHasChangedSignal.connect(self.styleHasChanged)
|
||||
fn.COLOR_REGISTRY.graphStyleChanged.connect(self.updateGraphStyle)
|
||||
|
||||
def itemChange(self, change, value):
|
||||
ret = super().itemChange(change, value)
|
||||
@ -42,9 +42,9 @@ class GraphicsObject(GraphicsItem, QtGui.QGraphicsObject):
|
||||
|
||||
return ret
|
||||
|
||||
@QtCore.Slot() # qt.py equates this to pyqtSlot for PyQt
|
||||
def styleHasChanged(self):
|
||||
""" called to trigger redraw after all named colors have been updated """
|
||||
# Slot for graphStyleChanged signal emitted by ColorRegistry, omitted decorator: @QtCore.Slot()
|
||||
def updateGraphStyle(self):
|
||||
""" called to trigger redraw after all registered colors have been updated """
|
||||
# self._boundingRect = None
|
||||
self.update()
|
||||
if DEBUG_REDRAW: print(' GraphicsObject: redraw after style change:', self)
|
@ -17,7 +17,7 @@ class GraphicsWidget(GraphicsItem, QtGui.QGraphicsWidget):
|
||||
"""
|
||||
QtGui.QGraphicsWidget.__init__(self, *args, **kargs)
|
||||
GraphicsItem.__init__(self)
|
||||
fn.COLOR_REGISTRY.paletteHasChangedSignal.connect(self.styleHasChanged)
|
||||
fn.COLOR_REGISTRY.graphStyleChanged.connect(self.updateGraphStyle)
|
||||
|
||||
## done by GraphicsItem init
|
||||
#GraphicsScene.registerObject(self) ## workaround for pyqt bug in graphicsscene.items()
|
||||
@ -58,9 +58,9 @@ class GraphicsWidget(GraphicsItem, QtGui.QGraphicsWidget):
|
||||
#print "shape:", p.boundingRect()
|
||||
return p
|
||||
|
||||
@QtCore.Slot() # qt.py equates this to pyqtSlot for PyQt
|
||||
def styleHasChanged(self):
|
||||
""" called to trigger redraw after all named colors have been updated """
|
||||
# Slot for graphStyleChanged signal emitted by ColorRegistry, omitted decorator: @QtCore.Slot()
|
||||
def updateGraphStyle(self):
|
||||
""" called to trigger redraw after all registered colors have been updated """
|
||||
# self._boundingRect = None
|
||||
self.update()
|
||||
if DEBUG_REDRAW: print(' GraphicsWidget: redraw after style change:', self)
|
||||
|
@ -143,14 +143,14 @@ class LabelItem(GraphicsWidget, GraphicsWidgetAnchor):
|
||||
def itemRect(self):
|
||||
return self.item.mapRectToParent(self.item.boundingRect())
|
||||
|
||||
def styleHasChanged(self):
|
||||
def updateGraphStyle(self):
|
||||
""" overridden to update color without changing the text """
|
||||
if self._hex_color_override is not None:
|
||||
return # nothing to do, overridden text color will not change.
|
||||
color_opt = self._brush.color().name() # get updated color
|
||||
full = "<span style='color: {:s}; {:s}'>{:s}</span>".format(color_opt, '; '.join(self.optlist), self.text)
|
||||
self.item.setHtml(full)
|
||||
super().styleHasChanged()
|
||||
super().updateGraphStyle()
|
||||
|
||||
#def paint(self, p, *args):
|
||||
#p.setPen(fn.mkPen('r'))
|
||||
|
@ -1269,12 +1269,12 @@ class ScatterPlotItem(GraphicsObject):
|
||||
return any(self.opts['hover' + opt.title()] != _DEFAULT_STYLE[opt]
|
||||
for opt in ['symbol', 'size', 'pen', 'brush'])
|
||||
|
||||
def styleHasChanged(self):
|
||||
def updateGraphStyle(self):
|
||||
""" overridden to trigger symbol atlas refresh """
|
||||
self.fragmentAtlas.clear()
|
||||
self.data['sourceRect'] = (0, 0, 0, 0)
|
||||
self.updateSpots(self.data)
|
||||
super().styleHasChanged()
|
||||
super().updateGraphStyle()
|
||||
|
||||
|
||||
|
||||
|
@ -221,8 +221,8 @@ class TextItem(GraphicsObject):
|
||||
self._lastTransform = pt
|
||||
self.updateTextPos()
|
||||
|
||||
def styleHasChanged(self):
|
||||
def updateGraphStyle(self):
|
||||
""" overridden to mnanually refresh color """
|
||||
if self._color is not None:
|
||||
self.textItem.setDefaultTextColor(self._color)
|
||||
super().styleHasChanged()
|
||||
super().updateGraphStyle()
|
||||
|
@ -184,7 +184,8 @@ class Palette(object):
|
||||
Primary colors:
|
||||
'b', 'c', 'g', 'y', 'r', 'm'
|
||||
Gray scale:
|
||||
'k', 'd', 'l', 'w' ranging from black to white
|
||||
'm0' to 'm9' ranging from black to white
|
||||
'k', 'd', 'l', 'w' black, dark gray, light gray and white, typically parts of the 'm0' to 'm9' range
|
||||
's' slate gray
|
||||
System colors:
|
||||
'gr_bg', 'gr_fg', 'gr_txt' graph background, foreground and text colors
|
||||
@ -219,6 +220,7 @@ class Palette(object):
|
||||
if colors is not None:
|
||||
# print('color dictionary:', colors)
|
||||
self.add(colors) # override specified colors
|
||||
# todo: add a monochrome ramp and sample 'mono' colors from that
|
||||
|
||||
def __getitem__(self, key):
|
||||
""" Convenient shorthand access to palette colors """
|
||||
@ -273,7 +275,7 @@ class Palette(object):
|
||||
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).")
|
||||
raise ValueError(f"Failed to obtain ColorMap object for 'cmap' = '{cmap}'.")
|
||||
|
||||
if prefix == 'p':
|
||||
self.cmap = cmap # replace default color map
|
||||
@ -307,7 +309,9 @@ class Palette(object):
|
||||
Adds a default ramp of grays m0 to m9,
|
||||
linearized according to CIElab lightness value
|
||||
"""
|
||||
|
||||
pass
|
||||
# todo: define based on start, intermediate, end colors
|
||||
# to give grays with different warmth and range of contrast
|
||||
|
||||
def emulateSystem(self):
|
||||
"""
|
||||
@ -394,16 +398,17 @@ class Palette(object):
|
||||
|
||||
# project legacy colors onto monochrome reference to assigne them somewhat logically
|
||||
ref_color = np.array( self.palette['m5'].getRgb()[:3] )
|
||||
brightness = [
|
||||
brightness = [ # estimate brightness of wanted colors "projected" into monochrome color space
|
||||
(np.sum( ref_color * needed[key] ), key)
|
||||
for key in needed
|
||||
]
|
||||
brightness = sorted(brightness, key=lambda brightness: brightness[0])
|
||||
# print( brightness )
|
||||
avail = ('m3','m4','m5','m6','m7','m8')
|
||||
colors = {}
|
||||
for idx, (value, key) in enumerate(brightness):
|
||||
colors[key] = avail[idx]
|
||||
choice = ('m3','m4','m5','m6','m7','m8') # 6 candidates for 6 needed colors
|
||||
colors = {
|
||||
key: choice[idx] # assign in order of dark to bright
|
||||
for idx, (_, key) in enumerate(brightness)
|
||||
}
|
||||
self.add( colors )
|
||||
|
||||
def apply(self):
|
||||
|
@ -120,7 +120,7 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
self.clickAccepted = False
|
||||
|
||||
# connect to style update signals from ColorRegistry:
|
||||
fn.COLOR_REGISTRY.paletteHasChangedSignal.connect(self.styleHasChanged)
|
||||
fn.COLOR_REGISTRY.graphStyleChanged.connect(self.updateGraphStyle)
|
||||
|
||||
|
||||
def setAntialiasing(self, aa):
|
||||
@ -400,8 +400,8 @@ class GraphicsView(QtGui.QGraphicsView):
|
||||
def dragEnterEvent(self, ev):
|
||||
ev.ignore() ## not sure why, but for some reason this class likes to consume drag events
|
||||
|
||||
@QtCore.Slot() # qt.py equates this to pyqtSlot for PyQt
|
||||
def styleHasChanged(self):
|
||||
""" called to trigger redraw after all named colors have been updated """
|
||||
# Slot for graphStyleChanged signal emitted by ColorRegistry, omitted decorator: @QtCore.Slot()
|
||||
def updateGraphStyle(self):
|
||||
""" called to trigger redraw after all registered colors have been updated """
|
||||
self.setBackgroundBrush( self._bgBrush )
|
||||
# self.update()
|
||||
|
Loading…
x
Reference in New Issue
Block a user