Source code for optimeed.visualize.graphs.traceVisual

from .pyqtgraph import mkPen, mkBrush
from PyQt5 import QtCore, QtGui
from .colormap_pyqtgraph import matplotlib_colormap_to_pg_colormap


default_colormap = matplotlib_colormap_to_pg_colormap('jet')


[docs]def _normalize_colors(z): # Get colors to set min_value = min(z) max_value = max(z) colors = [(0, 0, 0)] * len(z) for k, value in enumerate(z): normalized_value = (value - min_value) / (max_value - min_value) colors[k] = default_colormap.mapToQColor(normalized_value) return colors
[docs]class TraceVisual(QtCore.QObject): """Defines a trace in a graph.""" signal_must_update = QtCore.pyqtSignal()
[docs] class _ModifiedPaintElem: """Hidden class to manage brushes or pens""" def __init__(self): self.changed = dict() # {index_plot, newPaintElem}
[docs] def add_modified_paintElem(self, index, newPaintElem): self.changed[index] = newPaintElem
[docs] def modify_paintElems(self, paintElemsIn_List): """Apply transformation to paintElemsIn_List. :param: paintElemsIn_List: list of brushes or pens to modify :return: False if nothing has been modified, True is something has been modified """ if not len(self.changed): return False for index in self.changed: try: paintElemsIn_List[index] = self.changed[index] except IndexError: pass return True
[docs] def reset_paintElem(self, index): """ Remove transformation of point index""" self.changed.pop(index, None)
[docs] def reset(self): self.__init__()
def __init__(self, theData, theWGPlot, highlight_last): """ :param theData: from class Data. Contains all the informations to plot (x, y, z, transforms, etc.) :param theWGPlot: holder of the trace. This is a plotWidget :param highlight_last: Boolean. If set to true, the last point in the data will be highlighted. """ super().__init__() # set brush self.theBrushesModifier = self._ModifiedPaintElem() # set symbolPen self.theSymbolPensModifier = self._ModifiedPaintElem() # set symbols self.theSymbolModifier = self._ModifiedPaintElem() # set data self.theData = theData # set trace item (plotItem) if theData.get_legend(): self.thePlotItem = theWGPlot.plot([float('inf')], name=theData.get_legend()) else: self.thePlotItem = theWGPlot.plot([float('inf')]) # set highlight last self.highlight_last = highlight_last self.drawPoints = True # signals self.signal_must_update.connect(self.updateTrace) # Performance issues: self._base_symbol_pen = None self._base_symbol_brush = None self._base_pen = None self.visible = True
[docs] def hide_points(self): """Hide all the points""" self.drawPoints = False self.signal_must_update.emit()
[docs] def get_color(self): """Get colour of the trace, return tuple (r,g,b)""" return self.get_data().get_color()
[docs] def set_color(self, color): """Set colour of the trace, argument as tuple (r,g,b)""" self.get_data().set_color(color) self.thePlotItem.scatter.opts['brush'] = color
[docs] def get_base_symbol_brush(self): """Get symbol brush configured for this trace, return `pg.QBrush`""" if self._base_symbol_brush is None: self._base_symbol_brush = mkBrush(self.get_data().get_color_alpha()) return self._base_symbol_brush
[docs] def get_base_pen(self): """Get pen configured for this trace, return `pg.QPen`""" if self._base_pen is None: pen = mkPen(self.get_color(), width=self.get_data().get_width()) if self.theData.isScattered(): pen.setStyle(QtCore.Qt.NoPen) else: self.set_pen_linestyle(pen, self.theData.get_linestyle()) self._base_pen = pen return self._base_pen
[docs] def get_base_symbol_pen(self): """Get symbol pen configured for this trace, return`pg.QPen`""" if self._base_symbol_pen is None: if self.get_data().symbol_isfilled(): if self.get_data().get_symbolOutline() == 1: return None init_color = QtGui.QColor(*self.get_color()) darker_color = init_color.darker(int(self.get_data().get_symbolOutline()*100)) alpha = self.get_data().get_alpha() if alpha < 255: darker_color.setAlpha(self.get_data().get_alpha()) thePen = mkPen(darker_color, isCosmetic=True) # thePen = mkPen(QtGui.QColor(*self.get_color()).darker(int(self.get_data().get_symbolOutline()*100)), isCosmetic=True) # 120 or 250 ? :) else: thePen = mkPen(self.get_color(), isCosmetic=False, width=self.get_data().get_width()) self._base_symbol_pen = thePen return self._base_symbol_pen
[docs] def get_base_symbol(self): """Get base symbol configured for this trace, return str of the symbol (e.g. 'o')""" return self.get_data().get_symbol()
[docs] def get_symbol(self, size): """Get actual symbols for the trace. If the symbols have been modified: return a list which maps each points to a symbol. Otherwise: return :meth:TraceVisual.get_base_symbol()""" symbol = self.get_base_symbol() theSymbolList = [symbol] * size hasBeenModified = self.theSymbolModifier.modify_paintElems(theSymbolList) return symbol if not hasBeenModified else theSymbolList
[docs] def updateTrace(self): """Forces the trace to refresh.""" if self.visible: try: theData = self.get_data() x, y = theData.get_plot_data() if x and y: z = theData.get_plot_meta(x, y) if z is not None: self.set_brushes(list(range(len(z))), list(map(QtGui.QBrush, _normalize_colors(z))), update=False) if self.drawPoints: theBrushes = self.get_brushes(len(x)) theSymbolPens = self.get_symbolPens(len(x)) else: theBrushes = None theSymbolPens = None theBasePen = self.get_base_pen() self.thePlotItem.setData(x, y, symbolBrush=theBrushes, symbol=self.get_symbol(len(x)), symbolPen=theSymbolPens, symbolSize=self.get_data().get_symbolsize(), pen=theBasePen) # symbolPen = None for no outline else: self.thePlotItem.setData([], [], symbolBrush=None) except ValueError: pass else: self.thePlotItem.setData([], [], symbolBrush=None)
[docs] def get_length(self): """Return number of data to plot""" x = self.get_data().get_plot_data() return len(x)
[docs] def hide(self): """Hides the trace""" self.visible = False self.thePlotItem.clear() self.signal_must_update.emit()
[docs] def show(self): """Shows the trace""" self.visible = True self.signal_must_update.emit()
[docs] def toggle(self, boolean): """Toggle the trace (hide/show)""" if boolean: self.show() else: self.hide()
[docs] def get_data(self): """Get data to plot :class:`~optimeed.visualize.graphs.Graphs.Data`""" return self.theData
[docs] def get_brushes(self, size): """Get actual brushes for the trace (=symbol filling). return a list which maps each points to a symbol brush""" symbolBrush = self.get_base_symbol_brush() if self.get_data().symbol_isfilled() else None theBrushList = [symbolBrush] * size if self.highlight_last: theBrushList[-1] = mkBrush('y') hasBeenModified = self.theBrushesModifier.modify_paintElems(theBrushList) if not hasBeenModified and not self.highlight_last: return symbolBrush return theBrushList
[docs] def set_brush(self, indexPoint, newbrush, update=True): """Set the symbol brush for a specific point: :param indexPoint: Index of the point (in the graph) to modify :param newbrush: either QBrush or tuple (r, g, b) of the new brush :param update: if True, update the trace afterwards. This is slow operation.""" if isinstance(newbrush, tuple): newbrush = mkBrush(newbrush) self.theBrushesModifier.add_modified_paintElem(indexPoint, newbrush) if update: self.signal_must_update.emit()
[docs] def set_symbol(self, indexPoint, newSymbol, update=True): """Set the symbol shape for a specific point: :param indexPoint: Index of the point (in the graph) to modify :param newSymbol: string of the new symbol (e.g.: 'o') :param update: if True, update the trace afterwards. This is slow operation.""" self.theSymbolModifier.add_modified_paintElem(indexPoint, newSymbol) if update: self.signal_must_update.emit()
[docs] def set_brushes(self, list_indexPoint, list_newbrush, update=True): """Same as :meth:`~TraceVisual.set_brush` but by taking a list as input""" if not isinstance(list_newbrush, list): list_newbrush = [list_newbrush] * len(list_indexPoint) for i in range(len(list_indexPoint)): self.set_brush(list_indexPoint[i], list_newbrush[i], update=False) if update: self.signal_must_update.emit()
[docs] def reset_brush(self, indexPoint, update=True): """Reset the brush of the point indexpoint""" self.theBrushesModifier.reset_paintElem(indexPoint) if update: self.signal_must_update.emit()
[docs] def reset_brushes(self, list_indexPoint, update=True): """Same as :meth:`~TraceVisual.reset_brush` but by taking a list as input""" [self.reset_brush(idPoint, update=False) for idPoint in list_indexPoint] if update: self.signal_must_update.emit()
[docs] def reset_all_brushes(self, update=True): """Reset all the brushes""" self.theBrushesModifier.reset() if update: self.signal_must_update.emit()
[docs] def reset_symbol(self, indexPoint, update=True): """Reset the symbol shape of the point indexpoint""" self.theSymbolModifier.reset_paintElem(indexPoint) if update: self.signal_must_update.emit()
[docs] def get_symbolPens(self, size): """Get actual symbol pens for the trace (=symbol outline). return a list which maps each points to a symbol pen""" thePenList = [self.get_base_symbol_pen()] * size hasBeenModified = self.theSymbolPensModifier.modify_paintElems(thePenList) if not hasBeenModified: return self.get_base_symbol_pen() return thePenList
[docs] def set_symbolPen(self, indexPoint, newPen, update=True): """Set the symbol shape for a specific point: :param indexPoint: Index of the point (in the graph) to modify :param newPen: QPen item or tuple of the color (r,g,b) :param update: if True, update the trace afterwards. This is slow operation.""" if isinstance(newPen, tuple): newPen = mkPen(newPen) self.theSymbolPensModifier.add_modified_paintElem(indexPoint, newPen) if update: self.signal_must_update.emit()
[docs] def set_symbolPens(self, list_indexPoint, list_newpens, update=True): """Same as :meth:`~TraceVisual.set_symbolPen` but by taking a list as input""" if not isinstance(list_newpens, list): list_newpens = [list_newpens] * len(list_indexPoint) for i in range(len(list_indexPoint)): self.set_symbolPen(list_indexPoint[i], list_newpens[i], update=False) if update: self.signal_must_update.emit()
[docs] def reset_symbolPen(self, indexPoint, update=True): """Reset the symbol pen of the point indexpoint""" self.theSymbolPensModifier.reset_paintElem(indexPoint) if update: self.signal_must_update.emit()
[docs] def reset_symbolPens(self, list_indexPoint, update=True): """Same as :meth:`~TraceVisual.reset_symbolPen` but by taking a list as input""" [self.reset_symbolPen(idPoint, update=False) for idPoint in list_indexPoint] if update: self.signal_must_update.emit()
[docs] def reset_all_symbolPens(self, update=True): """Reset all the symbol pens""" self.theSymbolPensModifier.reset() if update: self.signal_must_update.emit()
@staticmethod def set_pen_linestyle(thePen, linestyle): """Transform a pen for dashed lines: :param thePen: QPen item :param linestyle: str (e.g.: '.', '.-', '--', ...) """ if linestyle in ['.', ':', '..']: thePen.setDashPattern([0.1, 2]) thePen.setCapStyle(QtCore.Qt.RoundCap) thePen.setWidthF(thePen.width()*1.75) elif linestyle in ['.-', '-.']: thePen.setCapStyle(QtCore.Qt.RoundCap) thePen.setDashPattern([3, 2, 0.1, 2]) thePen.setWidthF(thePen.width()*1.25) elif linestyle in ['-..', '.-.', '..-']: thePen.setDashPattern([0.1, 2, 3, 2, 0.1, 2]) thePen.setWidthF(thePen.width()*1.25) elif linestyle in ['--']: thePen.setDashPattern([2.5, 2]) thePen.setWidthF(thePen.width()*1.1) else: thePen.setStyle(QtCore.Qt.SolidLine)
[docs] def get_point(self, indexPoint): """Return object pyqtgraph.SpotItem""" return self.thePlotItem.scatter.points()[indexPoint]