def paintEvent(self, e): if self.points is None: return painter = QPainter(self) pen = QPen() # creates a default pen pen.setWidth(2) pen.setCapStyle(Qt.RoundCap) pen.setJoinStyle(Qt.RoundJoin) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) # to avoid zero division m = max(max(self.points), 10) for i, v in enumerate(self.points): pen.setBrush(self.colors[min(v * len(self.colors) // m, len(self.colors) - 1)]) painter.setPen(pen) painter.drawLine( 40 + 5 * i, self.height() // 2 - min(v, self.height()) // 2, 40 + 5 * i, self.height() // 2 + min(v, self.height()) // 2, ) painter.end()
def qwtDrawXCrossSymbols(painter, points, numPoints, symbol): size = symbol.size() pen = QPen(symbol.pen()) if pen.width() > 1: pen.setCapStyle(Qt.FlatCap) painter.setPen(pen) sw = size.width() sh = size.height() sw2 = 0.5 * size.width() sh2 = 0.5 * size.height() for pos in points: x1 = pos.x() - sw2 x2 = x1 + sw y1 = pos.y() - sh2 y2 = y1 + sh painter.drawLine(x1, y1, x2, y2) painter.drawLine(x2, y1, x1, y2)
def qwtDrawLineSymbols(painter, orientations, points, numPoints, symbol): size = symbol.size() pen = QPen(symbol.pen()) if pen.width() > 1: pen.setCapStyle(Qt.FlatCap) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing, False) sw = size.width() sh = size.height() sw2 = 0.5 * size.width() sh2 = 0.5 * size.height() for pos in points: if orientations & Qt.Horizontal: x = round(pos.x()) - sw2 y = round(pos.y()) painter.drawLine(x, y, x + sw, y) if orientations & Qt.Vertical: x = round(pos.x()) y = round(pos.y()) - sh2 painter.drawLine(x, y, x, y + sh)
def qwtDrawStar2Symbols(painter, points, numPoints, symbol): pen = QPen(symbol.pen()) if pen.width() > 1: pen.setCapStyle(Qt.FlatCap) pen.setJoinStyle(Qt.MiterJoin) painter.setPen(pen) painter.setBrush(symbol.brush()) cos30 = np.cos(30 * np.pi / 180.0) dy = 0.25 * symbol.size().height() dx = 0.5 * symbol.size().width() * cos30 / 3.0 for pos in points: x = pos.x() y = pos.y() x1 = x - 3 * dx y1 = y - 2 * dy x2 = x1 + 1 * dx x3 = x1 + 2 * dx x4 = x1 + 3 * dx x5 = x1 + 4 * dx x6 = x1 + 5 * dx x7 = x1 + 6 * dx y2 = y1 + 1 * dy y3 = y1 + 2 * dy y4 = y1 + 3 * dy y5 = y1 + 4 * dy star = [ QPointF(x4, y1), QPointF(x5, y2), QPointF(x7, y2), QPointF(x6, y3), QPointF(x7, y4), QPointF(x5, y4), QPointF(x4, y5), QPointF(x3, y4), QPointF(x1, y4), QPointF(x2, y3), QPointF(x1, y2), QPointF(x3, y2), ] painter.drawPolygon(QPolygonF(star))
def drawRoundedFrame(self, painter, rect, xRadius, yRadius, palette, lineWidth, frameStyle): """ Draw a rectangular frame with rounded borders :param QPainter painter: Painter :param QRectF rect: Frame rectangle :param float xRadius: x-radius of the ellipses defining the corners :param float yRadius: y-radius of the ellipses defining the corners :param QPalette palette: `QPalette.WindowText` is used for plain borders, `QPalette.Dark` and `QPalette.Light` for raised or sunken borders :param int lineWidth: Line width :param int frameStyle: bitwise OR´ed value of `QFrame.Shape` and `QFrame.Shadow` """ painter.save() painter.setRenderHint(QPainter.Antialiasing, True) painter.setBrush(Qt.NoBrush) lw2 = lineWidth * 0.5 r = rect.adjusted(lw2, lw2, -lw2, -lw2) path = QPainterPath() path.addRoundedRect(r, xRadius, yRadius) Plain, Sunken, Raised = list(range(3)) style = Plain if (frameStyle & QFrame.Sunken) == QFrame.Sunken: style = Sunken if (frameStyle & QFrame.Raised) == QFrame.Raised: style = Raised if style != Plain and path.elementCount() == 17: pathList = [QPainterPath() for _i in range(8)] for i in range(4): j = i * 4 + 1 pathList[2 * i].moveTo( path.elementAt(j - 1).x, path.elementAt(j - 1).y) pathList[2 * i].cubicTo( path.elementAt(j + 0).x, path.elementAt(j + 0).y, path.elementAt(j + 1).x, path.elementAt(j + 1).y, path.elementAt(j + 2).x, path.elementAt(j + 2).y, ) pathList[2 * i + 1].moveTo( path.elementAt(j + 2).x, path.elementAt(j + 2).y) pathList[2 * i + 1].lineTo( path.elementAt(j + 3).x, path.elementAt(j + 3).y) c1 = QColor(palette.color(QPalette.Dark)) c2 = QColor(palette.color(QPalette.Light)) if style == Raised: c1, c2 = c2, c1 for i in range(5): r = pathList[2 * i].controlPointRect() arcPen = QPen() arcPen.setCapStyle(Qt.FlatCap) arcPen.setWidth(lineWidth) linePen = QPen() linePen.setCapStyle(Qt.FlatCap) linePen.setWidth(lineWidth) if i == 0: arcPen.setColor(c1) linePen.setColor(c1) elif i == 1: gradient = QLinearGradient() gradient.setStart(r.topLeft()) gradient.setFinalStop(r.bottomRight()) gradient.setColorAt(0.0, c1) gradient.setColorAt(1.0, c2) arcPen.setBrush(gradient) linePen.setColor(c2) elif i == 2: arcPen.setColor(c2) linePen.setColor(c2) elif i == 3: gradient = QLinearGradient() gradient.setStart(r.bottomRight()) gradient.setFinalStop(r.topLeft()) gradient.setColorAt(0.0, c2) gradient.setColorAt(1.0, c1) arcPen.setBrush(gradient) linePen.setColor(c1) painter.setPen(arcPen) painter.drawPath(pathList[2 * i]) painter.setPen(linePen) painter.drawPath(pathList[2 * i + 1]) else: pen = QPen(palette.color(QPalette.WindowText), lineWidth) painter.setPen(pen) painter.drawPath(path) painter.restore()
class PyDMDrawing(QWidget, PyDMWidget): """ Base class to be used for all PyDM Drawing Widgets. This class inherits from QWidget and PyDMWidget. Parameters ---------- parent : QWidget The parent widget for the Label init_channel : str, optional The channel to be used by the widget. """ def __init__(self, parent=None, init_channel=None): self._rotation = 0.0 self._brush = QBrush(Qt.SolidPattern) self._original_brush = None self._painter = QPainter() self._pen = QPen(Qt.NoPen) self._pen_style = Qt.NoPen self._pen_cap_style = Qt.SquareCap self._pen_join_style = Qt.MiterJoin self._pen_width = 0 self._pen_color = QColor(0, 0, 0) self._pen.setCapStyle(self._pen_cap_style) self._pen.setJoinStyle(self._pen_join_style) self._original_pen_style = self._pen_style self._original_pen_color = self._pen_color QWidget.__init__(self, parent) PyDMWidget.__init__(self, init_channel=init_channel) self.alarmSensitiveBorder = False def sizeHint(self): return QSize(100, 100) def paintEvent(self, _): """ Paint events are sent to widgets that need to update themselves, for instance when part of a widget is exposed because a covering widget was moved. At PyDMDrawing this method handles the alarm painting with parameters from the stylesheet, configures the brush, pen and calls ```draw_item``` so the specifics can be performed for each of the drawing classes. Parameters ---------- event : QPaintEvent """ painter = QPainter(self) opt = QStyleOption() opt.initFrom(self) self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self) painter.setRenderHint(QPainter.Antialiasing) painter.setBrush(self._brush) painter.setPen(self._pen) self.draw_item(painter) def draw_item(self, painter): """ The classes inheriting from PyDMDrawing must overwrite this method. This method translate the painter to the center point given by ```get_center``` and rotate the canvas by the given amount of degrees. """ xc, yc = self.get_center() painter.translate(xc, yc) painter.rotate(-self._rotation) def get_center(self): """ Simple calculation of the canvas' center point. Returns ------- x, y : float Tuple with X and Y coordinates of the center. """ return self.width() * 0.5, self.height() * 0.5 def get_bounds(self, maxsize=False, force_no_pen=False): """ Returns a tuple containing the useful area for the drawing. Parameters ---------- maxsize : bool, default is False If True, width and height information are based on the maximum inner rectangle dimensions given by ```get_inner_max```, otherwise width and height will receive the widget size. force_no_pen : bool, default is False If True the pen width will not be considered when calculating the bounds. Returns ------- x, y, w, h : tuple Tuple with X and Y coordinates followed by the maximum width and height. """ w, h = self.width(), self.height() if maxsize: w, h = self.get_inner_max() xc, yc = w * 0.5, h * 0.5 if self.has_border() and not force_no_pen: w = max(0, w - 2 * self._pen_width) h = max(0, h - 2 * self._pen_width) x = max(0, self._pen_width) y = max(0, self._pen_width) else: x = 0 y = 0 return x - xc, y - yc, w, h def has_border(self): """ Check whether or not the drawing have a border based on the Pen Style and Pen width. Returns ------- bool True if the drawing has a border, False otherwise. """ if self._pen.style() != Qt.NoPen and self._pen_width > 0: return True else: return False def is_square(self): """ Check if the widget has the same width and height values. Returns ------- bool True in case the widget has a square shape, False otherwise. """ return self.height() == self.width() def get_inner_max(self): """ Calculates the largest inner rectangle in a rotated rectangle. This implementation was based on https://stackoverflow.com/a/18402507 Returns ------- w, h : tuple The width and height of the largest rectangle. """ # Based on https://stackoverflow.com/a/18402507 w0 = 0 h0 = 0 angle = math.radians(self._rotation) origWidth = self.width() origHeight = self.height() if origWidth == 0: logger.error( "Invalid width. The value must be greater than {0}".format( origWidth)) return if origHeight == 0: logger.error( "Invalid height. The value must be greater than {0}".format( origHeight)) return if (origWidth <= origHeight): w0 = origWidth h0 = origHeight else: w0 = origHeight h0 = origWidth # Angle normalization in range [-PI..PI) ang = angle - math.floor( (angle + math.pi) / (2 * math.pi)) * 2 * math.pi ang = math.fabs(ang) if ang > math.pi / 2: ang = math.pi - ang c = w0 / (h0 * math.sin(ang) + w0 * math.cos(ang)) w = 0 h = 0 if (origWidth <= origHeight): w = w0 * c h = h0 * c else: w = h0 * c h = w0 * c return w, h @Property(QBrush) def brush(self): """ PyQT Property for the brush object to be used when coloring the drawing Returns ------- QBrush """ return self._brush @brush.setter def brush(self, new_brush): """ PyQT Property for the brush object to be used when coloring the drawing Parameters ---------- new_brush : QBrush """ if new_brush != self._brush: if self._alarm_state == PyDMWidget.ALARM_NONE: self._original_brush = new_brush self._brush = new_brush self.update() @Property(Qt.PenStyle) def penStyle(self): """ PyQT Property for the pen style to be used when drawing the border Returns ------- int Index at Qt.PenStyle enum """ return self._pen_style @penStyle.setter def penStyle(self, new_style): """ PyQT Property for the pen style to be used when drawing the border Parameters ---------- new_style : int Index at Qt.PenStyle enum """ if self._alarm_state == PyDMWidget.ALARM_NONE: self._original_pen_style = new_style if new_style != self._pen_style: self._pen_style = new_style self._pen.setStyle(new_style) self.update() @Property(Qt.PenCapStyle) def penCapStyle(self): """ PyQT Property for the pen cap to be used when drawing the border Returns ------- int Index at Qt.PenCapStyle enum """ return self._pen_cap_style @penCapStyle.setter def penCapStyle(self, new_style): """ PyQT Property for the pen cap style to be used when drawing the border Parameters ---------- new_style : int Index at Qt.PenStyle enum """ if new_style != self._pen_cap_style: self._pen_cap_style = new_style self._pen.setCapStyle(new_style) self.update() @Property(Qt.PenJoinStyle) def penJoinStyle(self): """ PyQT Property for the pen join style to be used when drawing the border Returns ------- int Index at Qt.PenJoinStyle enum """ return self._pen_join_style @penJoinStyle.setter def penJoinStyle(self, new_style): """ PyQT Property for the pen join style to be used when drawing the border Parameters ---------- new_style : int Index at Qt.PenStyle enum """ if new_style != self._pen_join_style: self._pen_join_style = new_style self._pen.setJoinStyle(new_style) self.update() @Property(QColor) def penColor(self): """ PyQT Property for the pen color to be used when drawing the border Returns ------- QColor """ return self._pen_color @penColor.setter def penColor(self, new_color): """ PyQT Property for the pen color to be used when drawing the border Parameters ---------- new_color : QColor """ if self._alarm_state == PyDMWidget.ALARM_NONE: self._original_pen_color = new_color if new_color != self._pen_color: self._pen_color = new_color self._pen.setColor(new_color) self.update() @Property(float) def penWidth(self): """ PyQT Property for the pen width to be used when drawing the border Returns ------- float """ return self._pen_width @penWidth.setter def penWidth(self, new_width): """ PyQT Property for the pen width to be used when drawing the border Parameters ---------- new_width : float """ if new_width < 0: return if new_width != self._pen_width: self._pen_width = new_width self._pen.setWidth(self._pen_width) self.update() @Property(float) def rotation(self): """ PyQT Property for the counter-clockwise rotation in degrees to be applied to the drawing. Returns ------- float """ return self._rotation @rotation.setter def rotation(self, new_angle): """ PyQT Property for the counter-clockwise rotation in degrees to be applied to the drawing. Parameters ---------- new_angle : float """ if new_angle != self._rotation: self._rotation = new_angle self.update() def alarm_severity_changed(self, new_alarm_severity): PyDMWidget.alarm_severity_changed(self, new_alarm_severity) if new_alarm_severity == PyDMWidget.ALARM_NONE: if self._original_brush is not None: self.brush = self._original_brush if self._original_pen_color is not None: self.penColor = self._original_pen_color if self._original_pen_style is not None: self.penStyle = self._original_pen_style
def draw(self, painter, xMap, yMap, canvasRect): """ Draw the grid The grid is drawn into the bounding rectangle such that grid lines begin and end at the rectangle's borders. The X and Y maps are used to map the scale divisions into the drawing region screen. :param QPainter painter: Painter :param qwt.scale_map.QwtScaleMap xMap: X axis map :param qwt.scale_map.QwtScaleMap yMap: Y axis :param QRectF canvasRect: Contents rectangle of the plot canvas """ minorPen = QPen(self.__data.minorPen) minorPen.setCapStyle(Qt.FlatCap) painter.setPen(minorPen) if self.__data.xEnabled and self.__data.xMinEnabled: self.drawLines( painter, canvasRect, Qt.Vertical, xMap, self.__data.xScaleDiv.ticks(QwtScaleDiv.MinorTick), ) self.drawLines( painter, canvasRect, Qt.Vertical, xMap, self.__data.xScaleDiv.ticks(QwtScaleDiv.MediumTick), ) if self.__data.yEnabled and self.__data.yMinEnabled: self.drawLines( painter, canvasRect, Qt.Horizontal, yMap, self.__data.yScaleDiv.ticks(QwtScaleDiv.MinorTick), ) self.drawLines( painter, canvasRect, Qt.Horizontal, yMap, self.__data.yScaleDiv.ticks(QwtScaleDiv.MediumTick), ) majorPen = QPen(self.__data.majorPen) majorPen.setCapStyle(Qt.FlatCap) painter.setPen(majorPen) if self.__data.xEnabled: self.drawLines( painter, canvasRect, Qt.Vertical, xMap, self.__data.xScaleDiv.ticks(QwtScaleDiv.MajorTick), ) if self.__data.yEnabled: self.drawLines( painter, canvasRect, Qt.Horizontal, yMap, self.__data.yScaleDiv.ticks(QwtScaleDiv.MajorTick), )