Source code for optimeed.visualize.gui.widgets.graphsVisualWidget.pyqtgraphRedefine

from .pyqtgraph.graphicsItems.LegendItem import ItemSample
from .pyqtgraph.graphicsItems.ScatterPlotItem import ScatterPlotItem, drawSymbol
from .pyqtgraph.graphicsItems.PlotDataItem import PlotDataItem
from .pyqtgraph import functions as fn
from . import pyqtgraph as pg
from PyQt5 import QtCore, QtGui, QtWidgets, QtOpenGL
import os

[docs]isOnWindows = os.name == 'nt'
"""Other modified files (directly): ScatterPlotItem.py, to change point selection. Ctrl + clic: select area. Clic: only one single point: class OnClicSelector: def __init__(self): self.p_list = [] def add_point(self, newp): self.p_list.append(newp) def draw(self, painter): if len(self.p_list) > 2: pen = fn.mkPen(1) pen.setWidthF(2) painter.setPen(pen) painter.drawPolyline(QtGui.QPolygonF(self.p_list)) def reset(self): self.p_list = [] def getPath(self): return path.Path([(p.x(), p.y()) for p in self.p_list] + [(self.p_list[-1].x(), self.p_list[-1].y())]) ------------------------------ def mouseDragEvent(self, ev): if ev.modifiers() and QtCore.Qt.ControlModifier: ev.accept() self.clicSelector.add_point(ev.pos()) if ev.isFinish(): path = self.clicSelector.getPath() points = self.points() contains_points = path.contains_points([(p.pos().x(), p.pos().y()) for p in points]) indices = [i for i, cond in enumerate(contains_points) if cond] points_clicked = [points[i] for i in indices] self.ptsClicked = points_clicked self.sigClicked.emit(self, self.ptsClicked) self.clicSelector.reset() self.update() else: ev.ignore() """
[docs]class myGraphicsLayoutWidget(pg.GraphicsView): def __init__(self, parent=None, **_kwargs): pg.GraphicsView.__init__(self, parent) self.ci = myGraphicsLayout() for n in ['nextRow', 'nextCol', 'nextColumn', 'addPlot', 'addViewBox', 'addItem', 'getItem', 'addLayout', 'addLabel', 'removeItem', 'itemIndex', 'clear']: setattr(self, n, getattr(self.ci, n)) self.setCentralItem(self.ci)
[docs] def useOpenGL(self, b=True): """Overwrited to fix bad antialiasing while using openGL""" if b: if isOnWindows: v = QtOpenGL.QGLWidget() else: v = QtWidgets.QOpenGLWidget() fmt = QtGui.QSurfaceFormat() fmt.setSamples(8) fmt.setDepthBufferSize(24) fmt.setStencilBufferSize(8) # Fixes antialias zoom error v.setFormat(fmt) else: v = QtGui.QWidget() self.setViewport(v)
[docs]class myGraphicsLayout(pg.GraphicsLayout): def __init__(self): super().__init__() self.layout.setHorizontalSpacing(-5) self.layout.setVerticalSpacing(-5)
[docs] def addItem(self, item, row=None, col=None, rowspan=1, colspan=1): """ Add an item to the layout and place it in the next available cell (or in the cell specified). The item must be an instance of a QGraphicsWidget subclass. """ if row is None: row = self.currentRow if col is None: col = self.currentCol self.items[item] = [] for i in range(rowspan): for j in range(colspan): row2 = row + i col2 = col + j if row2 not in self.rows: self.rows[row2] = {} self.rows[row2][col2] = item self.items[item].append((row2, col2)) self.layout.addItem(item, row, col, rowspan, colspan) self.nextRow()
[docs] def set_graph_disposition(self, item, row=1, col=1, rowspan=1, colspan=1): """ Function to modify the position of an item in the list :param item: WidgetPlotItem to set :param row: Row :param col: Column :param rowspan: :param colspan: :return: """ # Remove from existing self.layout.removeItem(item) for r, c in self.items[item]: del self.rows[r][c] # Add new configuration for i in range(rowspan): for j in range(colspan): row2 = row + i col2 = col + j if row2 not in self.rows: self.rows[row2] = {} self.rows[row2][col2] = item self.items[item].append((row2, col2)) self.layout.addItem(item, row, col, rowspan, colspan)
[docs]class myItemSample(ItemSample): def __init__(self, item): super().__init__(item) self.offset = 13 self.widthCell = 40
[docs] def set_offset(self, offset): self.offset = offset
[docs] def set_width_cell(self, width): self.widthCell = width
# """Overwrites for text closer to the sample""" # def boundingRect(self): # return QtCore.QRectF(0, 0, 0, 0)
[docs] def paint(self, p, *args): """Overwrites to make matlab-like samples""" # p.setRenderHint(p.Antialiasing) # only if the data is antialiased. opts = self.item.opts if opts.get('fillLevel', None) is not None and opts.get('fillBrush', None) is not None: p.setBrush(fn.mkBrush(opts['fillBrush'])) p.setPen(fn.mkPen(None)) p.drawPolygon(QtGui.QPolygonF([QtCore.QPointF(2, 18), QtCore.QPointF(18, 2), QtCore.QPointF(18, 18)])) p.save() linewidth = self.widthCell v_offset = self.offset opts = self.item.opts if not isinstance(self.item, ScatterPlotItem): pen = fn.mkPen(opts['pen']) pen.setWidthF(pen.widthF()*0.9) p.setPen(pen) p.drawLine(0, v_offset, linewidth, v_offset) p.restore() symbol = opts.get('symbol', None) if symbol is not None: if isinstance(symbol, list): symbol = symbol[0] if isinstance(self.item, PlotDataItem): opts = self.item.scatter.opts pen = fn.mkPen(opts['pen']) brush = fn.mkBrush(opts['brush']) size = opts['size'] * 1.0 # Scale factor for legend size p.translate(linewidth / 2, v_offset) drawSymbol(p, symbol, size, pen, brush)
[docs]class myLegend(pg.LegendItem): """Legend that fixes bugs (flush left + space) from pyqtgraph's legend""" def __init__(self, size=None, offset=(30, 30), is_light=False): super(myLegend, self).__init__(size, offset) self.layout.setVerticalSpacing(-8) self.is_light = is_light self.width_cell_sample = 25
[docs] def set_space_sample_label(self, theSpace): """To set the gap between the sample and the label""" self.layout.setHorizontalSpacing(theSpace)
[docs] def set_offset_sample(self, offset): """To tune the offset between the sample and the text""" for sample, label in self.items: sample.set_offset(offset)
[docs] def set_width_cell_sample(self, width): """Set width of sample""" self.width_cell_sample = width self.apply_width_sample()
[docs] def updateSize(self): if self.size is not None: return height = 0 width = 0 for sample, label in self.items: height += max(sample.boundingRect().height(), label.height()) width = max(width, sample.boundingRect().width() + label.width()) # + 13 self.setGeometry(0, 0, width, height)
[docs] def addItem(self, item, name): """Overwrites to flush left""" # label = LabelItem(name) label = myLabelItem(name, justify='left') width_cell = self.width_cell_sample if isinstance(item, ItemSample): sample = item else: sample = myItemSample(item) sample.set_width_cell(width_cell*1) row = self.layout.rowCount() self.items.append((sample, label)) self.layout.addItem(sample, row, 0) self.layout.setColumnFixedWidth(0, width_cell) self.layout.setColumnFixedWidth(1, 10) self.layout.setHorizontalSpacing(0.0) # No spacing between sample and text self.layout.addItem(label, row, 1) self.layout.setRowFixedHeight(row, 15) self.updateSize()
[docs] def apply_width_sample(self): width_cell = self.width_cell_sample for sample, label in self.items: sample.set_width_cell(width_cell*1) self.layout.setColumnFixedWidth(0, width_cell) self.updateSize()
[docs] def set_font(self, font_size, font_color, fontname=None): for item in self.items: for single_item in item: if isinstance(single_item, pg.LabelItem): if fontname is not None: single_item.setText(single_item.text, **{'color': font_color, 'size': str(font_size) + 'pt', 'font-family': fontname}) else: single_item.setText(single_item.text, **{'color': font_color, 'size': str(font_size) + 'pt'})
[docs] def paint(self, p, *args): """Overwrited to select background color""" if len(self.items): if self.is_light: p.setPen(pg.mkPen(100, 100, 100, 255)) p.setBrush(pg.mkBrush(255, 255, 255, 255)) else: p.setPen(pg.mkPen(100, 100, 100, 255)) p.setBrush(pg.mkBrush(10, 10, 10, 255)) offset_x = 0 offset_y = 20 boundingRect = QtCore.QRectF(0, offset_y/2, self.width()-offset_x, self.height()-offset_y) p.drawRect(boundingRect) # self.boundingRect())
[docs] def set_position(self, position, offset): """ Set the position of the legend, in a corner. :param position: String (NW, NE, SW, SE), indicates which corner the legend is close :param offset: Tuple (xoff, yoff), x and y offset from the edge :return: """ offPosMult = 1 if position == "NW": quartil_parent = (0, 0) oxMult, oyMult = 1, 1 elif position == "NE": quartil_parent = (1, 0) oxMult, oyMult = -1, 1 elif position == "SW": quartil_parent = (0, 1) oxMult, oyMult = 1, -1 offPosMult = -1 elif position == "SE": quartil_parent = (1, 1) oxMult, oyMult = -1, -1 offPosMult = -1 else: print("Could not place legend : position unknown (please use NW, NE, SW or SE)") return self.anchor(quartil_parent, quartil_parent, offset=(offset[0] * oxMult, offPosMult * (-10) + offset[1] * oyMult)) # * -10: corresponds to offset_y defined in paint
[docs]class myLabelItem(pg.LabelItem):
[docs] def setText(self, text, **args): """ Overwrited to add font-family to options """ self.text = text opts = self.opts for k in args: opts[k] = args[k] optlist = [] color = self.opts['color'] if color is None: color = pg.getConfigOption('foreground') color = fn.mkColor(color) optlist.append('color: #' + fn.colorStr(color)[:6]) if 'size' in opts: optlist.append('font-size: ' + opts['size']) if 'bold' in opts and opts['bold'] in [True, False]: optlist.append('font-weight: ' + {True: 'bold', False: 'normal'}[opts['bold']]) if 'italic' in opts and opts['italic'] in [True, False]: optlist.append('font-style: ' + {True: 'italic', False: 'normal'}[opts['italic']]) if 'font-family' in opts: optlist.append('font-family: ' + opts['font-family']) full = "<span style='%s'>%s</span>" % ('; '.join(optlist), text) # print full self.item.setHtml(full) self.updateMin() self.resizeEvent(None) self.updateGeometry()
[docs]class myAxis(pg.AxisItem): def __init__(self, orientation): super().__init__(orientation) self.nudges = {'left': (-10, 0), 'right': (5, 0), 'bottom': (0, -5), 'top': (0, 5)} # self.style.update({'textFillLimits': [(0, 0.8), ## never fill more than 80% of the axis # (4, 0.4), ## If we already have 4 ticks with text, fill no more than 40% of the axis # (6, 0.2)]})
[docs] def get_label_pos(self): """Overwrited to place label closer to the axis""" br = self.label.boundingRect() x, y = 0, 0 if self.orientation == 'left': y, x = int(self.size().height()/2 + br.width()/2), 0 elif self.orientation == 'right': y, x = int(self.size().height()/2 + br.width()/2), int(self.size().width()-br.height()) elif self.orientation == 'top': y, x = 0, int(self.size().width()/2. - br.width()/2.) elif self.orientation == 'bottom': x, y = int(self.size().width()/2. - br.width()/2.), int(self.size().height()-br.height()) try: return int(x + self.nudges[self.orientation][0]), int(y - self.nudges[self.orientation][1]) except AttributeError: return x, y
[docs] def resizeEvent(self, ev=None): """Overwrited to place label closer to the axis""" p = QtCore.QPointF(0, 0) x_pos, y_pos = self.get_label_pos() p.setX(int(x_pos)) p.setY(int(y_pos)) self.label.setPos(p) self.picture = None
[docs] def set_label_pos(self, orientation, x_offset=0, y_offset=0): self.nudges[orientation] = (x_offset, y_offset)
[docs] def set_number_ticks(self, number): self.style.update({'textFillLimits': [(number, 0)]})