def qwtDrawTriangleSymbols(painter, type, points, numPoint, symbol): size = symbol.size() pen = QPen(symbol.pen()) pen.setJoinStyle(Qt.MiterJoin) painter.setPen(pen) painter.setBrush(symbol.brush()) sw2 = 0.5 * size.width() sh2 = 0.5 * size.height() for pos in points: x = pos.x() y = pos.y() x1 = x - sw2 x2 = x1 + size.width() y1 = y - sh2 y2 = y1 + size.height() if type == QwtTriangle.Left: triangle = [QPointF(x2, y1), QPointF(x1, y), QPointF(x2, y2)] elif type == QwtTriangle.Right: triangle = [QPointF(x1, y1), QPointF(x2, y), QPointF(x1, y2)] elif type == QwtTriangle.Up: triangle = [QPointF(x1, y2), QPointF(x, y1), QPointF(x2, y2)] elif type == QwtTriangle.Down: triangle = [QPointF(x1, y1), QPointF(x, y2), QPointF(x2, y1)] else: raise TypeError("Unknown triangle type %s" % type) painter.drawPolygon(QPolygonF(triangle))
def paint(self, q_painter: 'QPainter', style_option_graphics_item: 'QStyleOptionGraphicsItem', widget: 'QWidget' = None): pen = QPen() pen.setColor(self.color) pen.setWidth(3) pen.setJoinStyle(Qt.MiterJoin) # 让箭头变尖 q_painter.setPen(pen) path = QPainterPath() point1 = self.start_port path.moveTo(self.start_port.center_pos) # for i in self.line_items: # i.setPen(QColor(255, 0, 0)) # i.update() for p in self.center_points + [self.end_port]: pen.setWidth(3) q_painter.setPen(pen) path.lineTo(p.center_pos) q_painter.drawLine(QLineF(point1.center_pos, p.center_pos)) arrow = self.draw_arrow(q_painter, point1.center_pos, p.center_pos) path.addPolygon(arrow) # 将箭头添加到连线上 point1 = p
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 drawStar(self, qp): """Draw a star in the preview pane. Parameters ---------- qp: QPainter object """ width = self.rect().width() height = self.rect().height() col = QColor(135, 206, 235) pen = QPen(col, self._value) pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin) qp.setPen(pen) path = QPainterPath() # draw pentagram star_center_x = width / 2 star_center_y = height / 2 # make sure the star equal no matter the size of the qframe if width < height: # not taking it all the way to the edge so the star has room to grow radius_outer = width * 0.35 else: radius_outer = height * 0.35 # start at the top point of the star and move counter clockwise to draw the path. # every other point is the shorter radius (1/(1+golden_ratio)) of the larger radius golden_ratio = (1 + np.sqrt(5)) / 2 radius_inner = radius_outer / (1 + golden_ratio) theta_start = np.pi / 2 theta_inc = (2 * np.pi) / 10 for n in range(11): theta = theta_start + (n * theta_inc) theta = np.mod(theta, 2 * np.pi) if np.mod(n, 2) == 0: # use radius_outer x = radius_outer * np.cos(theta) y = radius_outer * np.sin(theta) else: # use radius_inner x = radius_inner * np.cos(theta) y = radius_inner * np.sin(theta) x_adj = star_center_x - x y_adj = star_center_y - y + 3 if n == 0: path.moveTo(x_adj, y_adj) else: path.lineTo(x_adj, y_adj) qp.drawPath(path)
def qwtDrawRectSymbols(painter, points, numPoints, symbol): size = symbol.size() pen = QPen(symbol.pen()) pen.setJoinStyle(Qt.MiterJoin) painter.setPen(pen) painter.setBrush(symbol.brush()) 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: x = pos.x() y = pos.y() r = QRectF(x - sw2, y - sh2, sw, sh) painter.drawRect(r)
def qwtDrawDiamondSymbols(painter, points, numPoints, symbol): size = symbol.size() pen = QPen(symbol.pen()) pen.setJoinStyle(Qt.MiterJoin) painter.setPen(pen) painter.setBrush(symbol.brush()) for pos in points: x1 = pos.x() - 0.5 * size.width() y1 = pos.y() - 0.5 * size.height() x2 = x1 + size.width() y2 = y1 + size.height() polygon = QPolygonF() polygon += QPointF(pos.x(), y1) polygon += QPointF(x1, pos.y()) polygon += QPointF(pos.x(), y2) polygon += QPointF(x2, pos.y()) painter.drawPolygon(polygon)
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 crosshair_pixmap(): """Create a cross cursor with white/black hollow square pixmap in the middle. For use as points cursor.""" size = 25 pixmap = QPixmap(QSize(size, size)) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) # Base measures width = 1 center = 3 # Must be odd! rect_size = center + 2 * width square = rect_size + width * 4 pen = QPen(Qt.white, 1) pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin) painter.setPen(pen) # # Horizontal rectangle painter.drawRect(0, (size - rect_size) // 2, size - 1, rect_size - 1) # Vertical rectangle painter.drawRect((size - rect_size) // 2, 0, rect_size - 1, size - 1) # Square painter.drawRect( (size - square) // 2, (size - square) // 2, square - 1, square - 1 ) pen = QPen(Qt.black, 2) pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin) painter.setPen(pen) # # Square painter.drawRect( (size - square) // 2 + 2, (size - square) // 2 + 2, square - 4, square - 4, ) pen = QPen(Qt.black, 3) pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin) painter.setPen(pen) # # # Horizontal lines mid_vpoint = QPoint(2, size // 2) painter.drawLine( mid_vpoint, QPoint(((size - center) // 2) - center + 1, size // 2) ) mid_vpoint = QPoint(size - 3, size // 2) painter.drawLine( mid_vpoint, QPoint(((size - center) // 2) + center + 1, size // 2) ) # # # Vertical lines mid_hpoint = QPoint(size // 2, 2) painter.drawLine( QPoint(size // 2, ((size - center) // 2) - center + 1), mid_hpoint ) mid_hpoint = QPoint(size // 2, size - 3) painter.drawLine( QPoint(size // 2, ((size - center) // 2) + center + 1), mid_hpoint ) painter.end() return pixmap
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
# --- Construct custom symbol --- path = QPainterPath() path.moveTo(0, 8) path.lineTo(0, 5) path.lineTo(-3, 5) path.lineTo(0, 0) path.lineTo(3, 5) path.lineTo(0, 5) transform = QTransform() transform.rotate(-30.0) path = transform.map(path) pen = QPen(Qt.black, 2) pen.setJoinStyle(Qt.MiterJoin) symbol = QwtSymbol() symbol.setPen(pen) symbol.setBrush(Qt.red) symbol.setPath(path) symbol.setPinPoint(QPointF(0.0, 0.0)) symbol.setSize(10, 14) # --- Test it within a simple plot --- curve = QwtPlotCurve() curve_pen = QPen(Qt.blue) curve_pen.setStyle(Qt.DotLine) curve.setPen(curve_pen) curve.setSymbol(symbol)