def paintEvent(self, a0: QPaintEvent) -> None: super().paintEvent(a0) painter = QPainter(self) painter.save() painter.setRenderHint(QPainter.Antialiasing) rect = QRectF(self.margin, self.margin, self.width() - self.margin * 2, self.height() - 2 * self.margin) painter.setBrush(Qt.white) painter.setPen(Qt.white) painter.drawRect(rect) painter.restore() painter.save() painter.setRenderHint(QPainter.Antialiasing) pen = QPen() pen.setWidth(3) painter.setPen(pen) path = QPainterPath() height, width = rect.height() + self.margin, rect.width() + self.margin path.moveTo(self.margin, height) path.cubicTo(height * 0.5, width * 0.9, height * 0.9, width * 0.5, height, self.margin) painter.drawPath(path) painter.restore()
def paintEvent(self, event): painter = QPainter(self) painter.setRenderHints(QPainter.Antialiasing) side = min(self.width(), self.height()) painter.scale(side / 32.0, side / 32.0) painter.setPen(Qt.NoPen) if not self.is_pressed: painter.setBrush(self.back_color) else: painter.setBrush( QColor(self.back_color.red() + 30, self.back_color.green() + 30, self.back_color.blue() + 30)) painter.drawRoundedRect(QRect(0, 0, 32, 32), 8, 8) if self.is_checked: check_path = QPainterPath() check_path.moveTo(self.x1) check_path.lineTo(self.x2) check_path.lineTo(self.x3) pen = QPen(self.check_color, self.check_thick, Qt.SolidLine) painter.setPen(pen) painter.drawPath(check_path)
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, event): painter = QPainter(self) painter.setRenderHints(QPainter.Antialiasing) if self.label_width <= 0: return painter.setPen(Qt.NoPen) if self.hover: painter.setBrush( QColor(self.back_color.red() + 30, self.back_color.green() + 30, self.back_color.blue() + 30)) else: painter.setBrush(self.back_color) painter.drawRoundedRect(QRect(0, 0, self.width(), self.height()), self.border_radius, self.border_radius) x1 = QPointF(self.label_width + float(self.height() / 3), float(self.height() * 0.45)) x2 = QPointF( self.label_width + float(self.height() * (0.66 + self.rate) / 2), float(self.height() * 0.55)) x3 = QPointF(self.width() - float(self.height() / 3), float(self.height() * 0.45)) check_path = QPainterPath() check_path.moveTo(x1) check_path.lineTo(x2) check_path.lineTo(x3) pen = QPen(self.text_color, self.drop_thick, Qt.SolidLine) painter.setPen(pen) painter.drawPath(check_path)
def drawValue(self, p: QPainter, baseRect: QRectF, value: float, delta: float): if value == self.m_min: return if self.m_barStyle == self.BarStyle.EXPAND: p.setBrush(self.palette().highlight()) p.setPen(QPen(self.palette().shadow().color(), self.m_dataPenWidth)) radius = (baseRect.height() / 2) / delta p.drawEllipse(baseRect.center(), radius, radius) return if self.m_barStyle == self.BarStyle.LINE: p.setPen(QPen(self.palette().highlight().color(), self.m_dataPenWidth)) p.setBrush(Qt.NoBrush) if value == self.m_max: p.drawEllipse(baseRect.adjusted(self.m_outlinePenWidth / 2, self.m_outlinePenWidth / 2, -self.m_outlinePenWidth / 2, -self.m_outlinePenWidth / 2)) else: arcLength = 360 / delta p.drawArc(baseRect.adjusted(self.m_outlinePenWidth / 2, self.m_outlinePenWidth / 2, -self.m_outlinePenWidth / 2, -self.m_outlinePenWidth / 2), int(self.m_nullPosition * 16), int(-arcLength * 16)) return dataPath = QPainterPath() dataPath.setFillRule(Qt.WindingFill) if value == self.m_max: dataPath.addEllipse(baseRect) else: arcLength = 360 / delta dataPath.moveTo(baseRect.center()) dataPath.arcTo(baseRect, self.m_nullPosition, -arcLength) dataPath.lineTo(baseRect.center()) p.setBrush(self.palette().highlight()) p.setPen(QPen(self.palette().shadow().color(), self.m_dataPenWidth)) p.drawPath(dataPath)
def paint(self, p, opt, widget): # Enforce constraints on handles r2 = Point(np.cos(np.radians(self.thetacenter)), np.sin(np.radians(self.thetacenter))) # chi center direction vector # constrain innerhandle to be parallel to outerhandle, and shorter than outerhandle self.innerhandle.setPos(r2 * self.innerradius) # constrain widthhandle to be counter-clockwise from innerhandle widthangle = np.radians(self.thetawidth / 2 + self.thetacenter) widthv = Point(np.cos(widthangle), np.sin(widthangle)) if self.thetawidth > 0 else r2 # constrain widthhandle to half way between inner and outerhandles self.widthhandle.setPos(widthv * (self.innerradius + self.outerradius) / 2) # constrain handles to base values self.outerhandle.setPos(r2 * self.outerradius) pen = self.currentPen pen.setColor(QColor(0, 255, 255)) p.setPen(pen) r = self.boundingRect() # p.drawRect(r) p.setRenderHint(QPainter.Antialiasing) p.scale(r.width(), r.height()) # workaround for GL bug centerangle = self.innerhandle.pos().angle(Point(1, 0)) startangle = centerangle - self.thetawidth / 2 endangle = centerangle + self.thetawidth / 2 r = QCircRectF(radius=0.5) if self.innerradius < self.outerradius and self.thetawidth > 0: p.drawArc(r, -startangle * 16, -self.thetawidth * 16) radius = self.innerradius / self.outerradius / 2 r = QCircRectF() r.radius = radius if self.innerradius < self.outerradius and self.thetawidth > 0: p.drawArc(r, -startangle * 16, -self.thetawidth * 16) pen.setStyle(Qt.DashLine) p.setPen(pen) p.drawLine(QPointF(0., 0.), self.widthhandle.pos().norm() / 2) r1v = self.innerhandle.pos().norm() p.drawLine(QPointF(0., 0.), (-1. * self.widthhandle.pos() + 2 * self.widthhandle.pos().dot(r1v) * r1v).norm() / 2) pen.setStyle(Qt.SolidLine) if self.innerradius < self.outerradius and self.thetawidth > 0: path = QPainterPath() path.moveTo((-1. * self.widthhandle.pos() + 2 * self.widthhandle.pos().dot(r1v) * r1v).norm() / 2) path.arcTo(r, -startangle, -self.thetawidth) # inside path.lineTo(self.widthhandle.pos().norm() / 2) # ? side path.arcTo(QCircRectF(radius=0.5), -endangle, self.thetawidth) # outside path.lineTo((-1. * self.widthhandle.pos() + 2 * self.widthhandle.pos().dot(r1v) * r1v).norm() / 2) self.path = path p.fillPath(path, QBrush(QColor(0, 255, 255, 20)))
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 updatePosition(self, scene): path = QPainterPath() self.prepareGeometryChange() count = len(self._longitudes) if count > 0: x, y = scene.posFromLonLat(self._longitudes, self._latitudes) path.moveTo(x[0], y[0]) for i in iterRange(1, count): path.lineTo(x[i], y[i]) self.setPath(path)
def shape(self): if self._shape is None: radius = self.getState()['size'][1] p = QPainterPath() p.moveTo(Point(0, -radius)) p.lineTo(Point(0, radius)) p.moveTo(Point(-radius, 0)) p.lineTo(Point(radius, 0)) p = self.mapToDevice(p) stroker = QPainterPathStroker() stroker.setWidth(10) outline = stroker.createStroke(p) self._shape = self.mapFromDevice(outline) return self._shape
def shape(self): # (used for hitbox for menu) centerangle = self.innerhandle.pos().angle(Point(1, 0)) startangle = centerangle - self.thetawidth / 2 endangle = centerangle + self.thetawidth / 2 r1v = self.innerhandle.pos().norm() # Draw out the path in external space path = QPainterPath() path.moveTo(-1.0 * self.widthhandle.pos() + 2 * self.widthhandle.pos().dot(r1v) * r1v) path.arcTo(QCircRectF(radius=self.innerradius), -startangle, -self.thetawidth) # inside path.lineTo(self.widthhandle.pos()) # ? side path.arcTo(QCircRectF(radius=self.outerradius), -endangle, self.thetawidth) # outside path.lineTo(-1.0 * self.widthhandle.pos() + 2 * self.widthhandle.pos().dot(r1v) * r1v) return path
def paintEvent(self, event) -> None: super(ToolTip, self).paintEvent(event) x = self.rect().x() + self._anchor_width y = self.rect().y() + self._anchor_width w = self.rect().width() - self._anchor_width * 2 h = self.rect().height() - self._anchor_width * 2 # 背景 painter = QPainter(self) path = QPainterPath() path.addRoundedRect(QRectF(x, y, w, h), 4, 4) # 画锚 if self._direction == ToolTip.TOP: x1 = x + w / 2 - self._anchor_width y1 = y + h x2 = x + w / 2 + self._anchor_width y2 = y + h x3 = x + w / 2 y3 = y + h + self._anchor_width path.moveTo(x1, y1) path.lineTo(x2, y2) path.lineTo(x3, y3) elif self._direction == ToolTip.BOTTOM: x1 = x + w / 2 - self._anchor_width y1 = y x2 = x + w / 2 + self._anchor_width y2 = y x3 = x + w / 2 y3 = y - self._anchor_width path.moveTo(x1, y1) path.lineTo(x2, y2) path.lineTo(x3, y3) elif self._direction == ToolTip.RIGHT: x1 = x y1 = y + h / 2 - self._anchor_width x2 = x y2 = y + h / 2 + self._anchor_width x3 = x - self._anchor_width y3 = y + h / 2 path.moveTo(x1, y1) path.lineTo(x2, y2) path.lineTo(x3, y3) elif self._direction == ToolTip.LEFT: x1 = x + w y1 = y + h / 2 - self._anchor_width x2 = x + w y2 = y + h / 2 + self._anchor_width x3 = x + w + self._anchor_width y3 = y + h / 2 path.moveTo(x1, y1) path.lineTo(x2, y2) path.lineTo(x3, y3) painter.fillPath(path, ResourceLoader().qt_color_separator_dark)
def paint(self, painter, fill=False, canvas=None): from ..application import Application mainwindow = Application.get_main_window() line_pen, last_line_pen, fill_color = self._get_pens_and_colors( canvas, mainwindow.lineColor, mainwindow.fillColor) scale = self.get_scale(canvas) # Draw all vertices self.paint_vertices(painter, self.points, scale, self._highlightIndex, self._highlightMode) line_path1, line_path2 = QPainterPath(), QPainterPath() fill_path = line_path1 # Get path for committed and uncommitted parts # respectively and draw with different colors if self.shape_type == 'curve' and self.points: # Bezier curve needs to be fitted as a whole, # so reimplementing this part here. refined_points = BezierB(self.points).smooth() sep_idx = refined_points.index(self.points[-1]) line_path1.moveTo(refined_points[0]) for p in refined_points[:sep_idx]: line_path1.lineTo(p) line_path2.moveTo(refined_points[sep_idx]) for p in refined_points[sep_idx:]: line_path2.lineTo(p) elif self.shape_type in ['circle', 'rectangle']: painter.setPen(line_pen) line_path2 = self.get_line_path(self.points, self.shape_type) painter.setPen(last_line_pen) painter.drawPath(line_path2) fill_path = line_path2 elif len(self.points) >= 2: # Otherwise, just get 2 different line paths, # painting with different colors. # Use type == 'line' even with polygon to prevent connecting back. line_path1 = self.get_line_path(self.points[:-1], 'line') line_path2 = self.get_line_path(self.points[-2:], 'line') painter.setPen(line_pen) painter.drawPath(line_path1) painter.setPen(last_line_pen) painter.drawPath(line_path2) if fill: painter.fillPath(fill_path, fill_color)
def drawLine(self, qp, value: int): """Draw line on triangle indicating value. Parameters ---------- qp: QPainter object value: int Value of highlight thickness. """ col = QColor('white') qp.setPen(QPen(col, 2)) qp.setBrush(col) path = QPainterPath() path.moveTo(value, 15) path.lineTo(value, 0) path.closeSubpath() qp.drawPath(path) self.valueChanged.emit(self._value)
def draw_target_path(self) -> None: """Draw solving path.""" pen = QPen() pen.setWidth(self.path_width) for i, n in enumerate(sorted(self.target_path)): path = self.target_path[n] if self.monochrome: line, dot = target_path_style(0) else: line, dot = target_path_style(i + 1) pen.setColor(line) self.painter.setPen(pen) if len(path) == 1: x, y = path[0] p = QPointF(x, -y) * self.zoom self.painter.drawText(p + QPointF(6, -6), f"P{n}") pen.setColor(dot) self.painter.setPen(pen) self.draw_circle(p, self.joint_size) else: painter_path = QPainterPath() for j, (x, y) in enumerate(path): p = QPointF(x, -y) * self.zoom self.draw_circle(p, self.joint_size) if j == 0: self.painter.drawText(p + QPointF(6, -6), f"P{n}") painter_path.moveTo(p) else: x2, y2 = path[j - 1] self.__draw_arrow(x, -y, x2, -y2, zoom=True) painter_path.lineTo(p) pen.setColor(line) self.painter.setPen(pen) self.painter.drawPath(painter_path) for x, y in path: pen.setColor(dot) self.painter.setPen(pen) self.draw_circle( QPointF(x, -y) * self.zoom, self.joint_size) self.painter.setBrush(Qt.NoBrush)
def get_line_path(points, shape_type): line_path = QPainterPath() if shape_type == 'rectangle' and len(points) == 2: rectangle = Shape.getRectFromLine(*points) line_path.addRect(rectangle) elif shape_type == 'circle' and len(points) == 2: rectangle = Shape.getCircleRectFromLine(points) line_path.addEllipse(rectangle) elif shape_type == 'linestrip' and points: line_path.moveTo(points[0]) for p in points: line_path.lineTo(p) elif shape_type in ['curve', 'freeform'] and points: # Paint Bezier curve across given points. refined_points = BezierB(points).smooth() line_path.moveTo(refined_points[0]) for p in refined_points: line_path.lineTo(p) elif points: line_path.moveTo(points[0]) for p in points: line_path.lineTo(p) if shape_type == 'polygon': line_path.lineTo(points[0]) return line_path
def drawPolygon(self, *args): if len(args) == 3: points, pointCount, mode = args elif len(args) == 2: points, mode = args pointCount = len(points) else: raise TypeError("Unexpected arguments") device = self.nullDevice() if device is None: return if device.mode() == QwtNullPaintDevice.PathMode: path = QPainterPath() if pointCount > 0: path.moveTo(points[0]) for i in range(1, pointCount): path.lineTo(points[i]) if mode != QPaintEngine.PolylineMode: path.closeSubpath() device.drawPath(path) return device.drawPolygon(points, pointCount, mode)
def draw_curve(self, path: Sequence[_Coord]) -> None: """Draw path as curve.""" if len(set(path)) < 2: return painter_path = QPainterPath() error = False for i, (x, y) in enumerate(path): if isnan(x): error = True self.painter.drawPath(painter_path) painter_path = QPainterPath() else: p = QPointF(x, -y) * self.zoom if i == 0: painter_path.moveTo(p) self.draw_circle(p, 2) continue if error: painter_path.moveTo(p) error = False else: painter_path.lineTo(p) self.painter.drawPath(painter_path)
def _paintBackgroundCross(cls, painter): grey = QColor(220, 220, 220) greyAccent = QColor(180, 180, 180) painter.setBrush(QBrush(grey)) painter.setPen(QPen(greyAccent, 1)) path = QPainterPath() h0 = -86 h1 = -18 h2 = 18 h3 = 86 v0 = -86 v1 = -18 v2 = 18 v3 = 86 c = 7 path.moveTo(v0, h1) path.lineTo(v1 - c, h1) path.lineTo(v1, h1 - c) path.lineTo(v1, h0) path.lineTo(v2, h0) path.lineTo(v2, h1 - c) path.lineTo(v2 + c, h1) path.lineTo(v3, h1) path.lineTo(v3, h2) path.lineTo(v2 + c, h2) path.lineTo(v2, h2 + c) path.lineTo(v2, h3) path.lineTo(v1, h3) path.lineTo(v1, h2 + c) path.lineTo(v1 - c, h2) path.lineTo(v0, h2) path.closeSubpath() painter.drawPath(path)
def drawTriangle(self, qp): """Draw triangle. Parameters ---------- qp: QPainter object """ width = self.rect().width() height = self.rect().height() col = QColor(135, 206, 235) qp.setPen(QPen(col, 1)) qp.setBrush(col) path = QPainterPath() height = 10 path.moveTo(0, height) path.lineTo(width, height) path.lineTo(width, 0) path.closeSubpath() qp.drawPath(path)
from qtpy.QtGui import QApplication, QPen, QPainterPath, QTransform from qtpy.QtCore import Qt, QPointF from qwt import QwtPlot, QwtPlotCurve, QwtSymbol import numpy as np import os.path as osp app = QApplication([]) # --- 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)
def paintEvent(self, event): painter = QPainter(self) painter.setRenderHints(QPainter.Antialiasing) painter.setPen(Qt.NoPen) if self.hover: painter.setBrush( QColor(self.back_color.red() + 30, self.back_color.green() + 30, self.back_color.blue() + 30)) self.lineEdit.set_back_color( QColor(self.back_color.red() + 30, self.back_color.green() + 30, self.back_color.blue() + 30)) else: painter.setBrush(self.back_color) self.lineEdit.set_back_color(self.back_color) painter.drawRoundedRect(QRect(0, 0, self.width(), self.height()), self.border_radius, self.border_radius) pen = QPen(self.down_arrow_color, self.drop_thick, Qt.SolidLine) painter.setPen(pen) self.down_arrow_poly = QPolygon() x1 = QPoint(self.label_width + float(self.height() / 3), float(self.height() * 0.6)) x2 = QPoint( self.label_width + float(self.height() / 3) + float(self.height() * self.rate / 2), float(self.height() * 0.75)) x3 = QPoint(self.width() - float(self.height() / 3), float(self.height() * 0.6)) self.down_arrow_poly << x1 << x2 << x3 check_path = QPainterPath() check_path.moveTo(x1) check_path.lineTo(x2) check_path.lineTo(x3) check_path.lineTo(x1) painter.drawPath(check_path) if self.down_arrow_color == self.arrow_activate_color: painter.fillPath(check_path, QBrush(self.down_arrow_color)) else: if self.hover: painter.fillPath( check_path, QColor(self.back_color.red() + 30, self.back_color.green() + 30, self.back_color.blue() + 30)) else: painter.fillPath(check_path, QBrush(self.back_color)) pen = QPen(self.up_arrow_color, self.drop_thick, Qt.SolidLine) painter.setPen(pen) x1 = QPoint(self.label_width + float(self.height() / 3), float(self.height() * 0.4)) x2 = QPoint( self.label_width + float(self.height() / 3) + float(self.height() * self.rate / 2), float(self.height() * 0.25)) x3 = QPoint(self.width() - float(self.height() / 3), float(self.height() * 0.4)) self.up_arrow_poly = QPolygon() self.up_arrow_poly << x1 << x2 << x3 check_path = QPainterPath() check_path.moveTo(x1) check_path.lineTo(x2) check_path.lineTo(x3) check_path.lineTo(x1) painter.drawPath(check_path) if self.up_arrow_color == self.arrow_activate_color: painter.fillPath(check_path, QBrush(self.up_arrow_color)) else: if self.hover: painter.fillPath( check_path, QColor(self.back_color.red() + 30, self.back_color.green() + 30, self.back_color.blue() + 30)) else: painter.fillPath(check_path, QBrush(self.back_color))
def drawPath(self): p = QPainterPath() x, y = self.opts['x'], self.opts['y'] if x is None or y is None: self.path = p return if self.opts['xlog']: x = np.log10(x) if self.opts['ylog']: y = np.log10(y) beam = self.opts['beam'] height, top, bottom = self.opts['height'], self.opts['top'], self.opts[ 'bottom'] if height is not None or top is not None or bottom is not None: ## draw vertical error bars if height is not None: y1 = y - height / 2. y2 = y + height / 2. else: if bottom is None: y1 = y else: y1 = y - bottom if top is None: y2 = y else: y2 = y + top for i in range(len(x)): p.moveTo(x[i], y1[i]) p.lineTo(x[i], y2[i]) if beam is not None and beam > 0: x1 = x - beam / 2. x2 = x + beam / 2. if height is not None or top is not None: for i in range(len(x)): p.moveTo(x1[i], y2[i]) p.lineTo(x2[i], y2[i]) if height is not None or bottom is not None: for i in range(len(x)): p.moveTo(x1[i], y1[i]) p.lineTo(x2[i], y1[i]) width, right, left = self.opts['width'], self.opts['right'], self.opts[ 'left'] if width is not None or right is not None or left is not None: ## draw vertical error bars if width is not None: x1 = x - width / 2. x2 = x + width / 2. else: if left is None: x1 = x else: x1 = x - left if right is None: x2 = x else: x2 = x + right for i in range(len(x)): p.moveTo(x1[i], y[i]) p.lineTo(x2[i], y[i]) if beam is not None and beam > 0: y1 = y - beam / 2. y2 = y + beam / 2. if width is not None or right is not None: for i in range(len(x)): p.moveTo(x2[i], y1[i]) p.lineTo(x2[i], y2[i]) if width is not None or left is not None: for i in range(len(x)): p.moveTo(x1[i], y1[i]) p.lineTo(x1[i], y2[i]) self.path = p self.prepareGeometryChange()
class FadingTipBox(FadingDialog): """ """ def __init__(self, parent, opacity, duration, easing_curve, tour=None): super(FadingTipBox, self).__init__(parent, opacity, duration, easing_curve) self.holder = self.anim # needed for qt to work self.parent = parent self.tour = tour self.frames = None self.color_top = QColor.fromRgb(230, 230, 230) self.color_back = QColor.fromRgb(255, 255, 255) self.offset_shadow = 0 self.fixed_width = 300 self.key_pressed = None self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setModal(False) # Widgets def toolbutton(icon): bt = QToolButton() bt.setAutoRaise(True) bt.setIcon(icon) return bt self.button_close = toolbutton(ima.icon("tour.close")) self.button_home = toolbutton(ima.icon("tour.home")) self.button_previous = toolbutton(ima.icon("tour.previous")) self.button_end = toolbutton(ima.icon("tour.end")) self.button_next = toolbutton(ima.icon("tour.next")) self.button_run = QPushButton(_('Run code')) self.button_disable = None self.button_current = QToolButton() self.label_image = QLabel() self.label_title = QLabel() self.combo_title = QComboBox() self.label_current = QLabel() self.label_content = QLabel() self.label_content.setMinimumWidth(self.fixed_width) self.label_content.setMaximumWidth(self.fixed_width) self.label_current.setAlignment(Qt.AlignCenter) self.label_content.setWordWrap(True) self.widgets = [self.label_content, self.label_title, self.label_current, self.combo_title, self.button_close, self.button_run, self.button_next, self.button_previous, self.button_end, self.button_home, self.button_current] arrow = get_image_path('hide.png') self.stylesheet = '''QComboBox { padding-left: 5px; background-color: rgbs(230,230,230,100%); border-width: 0px; border-radius: 0px; min-height:20px; max-height:20px; } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top left; border-width: 0px; } QComboBox::down-arrow { image: url(''' + arrow + '''); } ''' # Windows fix, slashes should be always in unix-style self.stylesheet = self.stylesheet.replace('\\', '/') self.setFocusPolicy(Qt.StrongFocus) for widget in self.widgets: widget.setFocusPolicy(Qt.NoFocus) widget.setStyleSheet(self.stylesheet) layout_top = QHBoxLayout() layout_top.addWidget(self.combo_title) layout_top.addStretch() layout_top.addWidget(self.button_close) layout_top.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_content = QHBoxLayout() layout_content.addWidget(self.label_content) layout_content.addWidget(self.label_image) layout_content.addSpacerItem(QSpacerItem(5, 5)) layout_run = QHBoxLayout() layout_run.addStretch() layout_run.addWidget(self.button_run) layout_run.addStretch() layout_run.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_navigation = QHBoxLayout() layout_navigation.addWidget(self.button_home) layout_navigation.addWidget(self.button_previous) layout_navigation.addStretch() layout_navigation.addWidget(self.label_current) layout_navigation.addStretch() layout_navigation.addWidget(self.button_next) layout_navigation.addWidget(self.button_end) layout_navigation.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout = QVBoxLayout() layout.addLayout(layout_top) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_content) layout.addLayout(layout_run) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_navigation) layout.addSpacerItem(QSpacerItem(self.offset_shadow, self.offset_shadow)) layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(layout) self.set_funcs_before_fade_in([self._disable_widgets]) self.set_funcs_after_fade_in([self._enable_widgets, self.setFocus]) self.set_funcs_before_fade_out([self._disable_widgets]) self.setContextMenuPolicy(Qt.CustomContextMenu) # signals and slots # These are defined every time by the AnimatedTour Class def _disable_widgets(self): """ """ for widget in self.widgets: widget.setDisabled(True) def _enable_widgets(self): """ """ self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) for widget in self.widgets: widget.setDisabled(False) if self.button_disable == 'previous': self.button_previous.setDisabled(True) self.button_home.setDisabled(True) elif self.button_disable == 'next': self.button_next.setDisabled(True) self.button_end.setDisabled(True) def set_data(self, title, content, current, image, run, frames=None, step=None): """ """ self.label_title.setText(title) self.combo_title.clear() self.combo_title.addItems(frames) self.combo_title.setCurrentIndex(step) # min_content_len = max([len(f) for f in frames]) # self.combo_title.setMinimumContentsLength(min_content_len) # Fix and try to see how it looks with a combo box self.label_current.setText(current) self.button_current.setText(current) self.label_content.setText(content) self.image = image if image is None: self.label_image.setFixedHeight(1) self.label_image.setFixedWidth(1) else: extension = image.split('.')[-1] self.image = QPixmap(get_image_path(image), extension) self.label_image.setPixmap(self.image) self.label_image.setFixedSize(self.image.size()) if run is None: self.button_run.setVisible(False) else: self.button_run.setDisabled(False) self.button_run.setVisible(True) # Refresh layout self.layout().activate() def set_pos(self, x, y): """ """ self.x = x self.y = y self.move(QPoint(x, y)) def build_paths(self): """ """ geo = self.geometry() radius = 0 shadow = self.offset_shadow x0, y0 = geo.x(), geo.y() width, height = geo.width() - shadow, geo.height() - shadow left, top = 0, 0 right, bottom = width, height self.round_rect_path = QPainterPath() self.round_rect_path.moveTo(right, top + radius) self.round_rect_path.arcTo(right-radius, top, radius, radius, 0.0, 90.0) self.round_rect_path.lineTo(left+radius, top) self.round_rect_path.arcTo(left, top, radius, radius, 90.0, 90.0) self.round_rect_path.lineTo(left, bottom-radius) self.round_rect_path.arcTo(left, bottom-radius, radius, radius, 180.0, 90.0) self.round_rect_path.lineTo(right-radius, bottom) self.round_rect_path.arcTo(right-radius, bottom-radius, radius, radius, 270.0, 90.0) self.round_rect_path.closeSubpath() # Top path header = 36 offset = 2 left, top = offset, offset right = width - (offset) self.top_rect_path = QPainterPath() self.top_rect_path.lineTo(right, top + radius) self.top_rect_path.moveTo(right, top + radius) self.top_rect_path.arcTo(right-radius, top, radius, radius, 0.0, 90.0) self.top_rect_path.lineTo(left+radius, top) self.top_rect_path.arcTo(left, top, radius, radius, 90.0, 90.0) self.top_rect_path.lineTo(left, top + header) self.top_rect_path.lineTo(right, top + header) def paintEvent(self, event): """ """ self.build_paths() painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.fillPath(self.round_rect_path, self.color_back) painter.fillPath(self.top_rect_path, self.color_top) painter.strokePath(self.round_rect_path, QPen(Qt.gray, 1)) # TODO: Build the pointing arrow? def keyReleaseEvent(self, event): """ """ key = event.key() self.key_pressed = key keys = [Qt.Key_Right, Qt.Key_Left, Qt.Key_Down, Qt.Key_Up, Qt.Key_Escape, Qt.Key_PageUp, Qt.Key_PageDown, Qt.Key_Home, Qt.Key_End, Qt.Key_Menu] if key in keys: if not self.is_fade_running(): self.sig_key_pressed.emit() def mousePressEvent(self, event): """override Qt method""" # Raise the main application window on click self.parent.raise_() self.raise_() if event.button() == Qt.RightButton: pass # clicked_widget = self.childAt(event.x(), event.y()) # if clicked_widget == self.label_current: # self.context_menu_requested(event) def focusOutEvent(self, event): """Override Qt method.""" # To be used so tips do not appear outside spyder self.tour.lost_focus() def context_menu_requested(self, event): """ """ pos = QPoint(event.x(), event.y()) menu = QMenu(self) actions = [] action_title = create_action(self, _('Go to step: '), icon=QIcon()) action_title.setDisabled(True) actions.append(action_title) # actions.append(create_action(self, _(': '), icon=QIcon())) add_actions(menu, actions) menu.popup(self.mapToGlobal(pos)) def reject(self): """Qt method to handle escape key event""" if not self.is_fade_running(): key = Qt.Key_Escape self.key_pressed = key self.sig_key_pressed.emit()
class LineChartItem(ChartItem): """ Visualises the given data as line chart_tests. :param parent: Items parent """ def __init__(self, parent=None): super(LineChartItem, self).__init__(parent) self._xData = None self._yData = None self._label = None # self._color = None self._bRect = None self.markers = {} self._showticks = False self._visible_range = None self._ordinate = None self._abscissa = None # self._column = None # # @property # def column(self): # return self._column @property def ordinate(self): """ Property to set/get the items ordinate name. """ return self._ordinate @ordinate.setter def ordinate(self, value): self._ordinate = value @property def abscissa(self): """ Property to set/get the items abscissa name. """ return self._abscissa @abscissa.setter def abscissa(self, value): self._abscissa = value @property def showTicks(self): """ Property if true the chart_tests will display tickmarks if the number of displayed data points is below 400. """ return self._showticks @showTicks.setter def showTicks(self, value): self._showticks = value self.visibleRangeChanged(self._visible_range) # @property # def color(self): # return self._color @property def label(self): """ Property to get the items label. """ return self._label def plot(self, data, index=None, label=None, color=QColor(Qt.red)): """ Sets the charts data points. :param data: ordinate values :param index: abscissa values. Optional, if not set datapoints are indexed starting with 0 :param label: Label of the chart_tests. :param color: Color of the chart_tests """ self._yData = data if index is not None: self._xData = index else: self._xData = np.arange(len(data)) if label is not None: self._label = label self._color = color self._bRect = QRectF( QPointF(np.min(self._xData), np.min(self._yData)), QPointF(np.max(self._xData), np.max(self._yData)), ) # _log.debug("Plot BB: {}".format(self._bRect)) # # # TODO: Public access # self.canvas._items.append(self._bRect) self._makePath() def _makePath(self): self._path = QPainterPath() self._path.moveTo(self._xData[0], self._yData[0]) for k, idx in enumerate(self._xData): d = self._yData[k] self._path.lineTo(idx, d) def boundingRect(self): """ Returns the bounding rect of the chart_tests item :return: Bounding Rectangle :rtype: QRectF """ return self._bRect def paint(self, p=QPainter(), o=QStyleOptionGraphicsItem(), widget=None): # _log.debug("LCI: paint") pen = makePen(self.color) p.setPen(pen) if self._path: p.drawPath(self._path) # if any(self.markers): # # pen = makePen(Qt.yellow) # # p.setPen(pen) # # p.drawRects(self.markers) # # for m in self.markers: # marker = RectMarker(m) # marker.setParentItem(self) if CONFIG.debug_layout: p.setPen(makePen(Qt.yellow)) p.setBrush(Qt.transparent) p.drawRect(self._bRect) def visibleRangeChanged(self, rect=QRectF()): """ Slot that is called whenver the views visible range changed. :param rect: view rectangle. """ _log.debug("Visible range changed to: {}".format(rect)) self._visible_range = rect visible_indices = np.where( np.logical_and( np.logical_and(self._xData >= rect.left(), self._xData <= rect.right()), np.logical_and(self._yData >= rect.top(), self._yData <= rect.bottom()), ))[0] # _log.debug("Visible plot points idx: {}".format(visible_indices)) # self.markers = [] if len(visible_indices) < 400 and self.showTicks: # _log.debug("Too many indices for display") # return # self.markers = [QPointF(self._xData[idx], self._yData[idx]) for idx in visible_indices] _log.debug("Making markers") for idx in visible_indices: if idx not in self.markers: pos = QPointF(self._xData[idx], self._yData[idx]) marker = RectMarker(pos) self.markers[idx] = marker marker.setParentItem(self) else: _log.debug("removing all markers") for k in self.markers: item = self.markers[k] self.scene().removeItem(item) # del self.markers[k] self.markers = {} def __del__(self): _log.debug("Finalize Linechart {}".format(self))
def _paintAbsDropIcon(self, painter, placement): self._configureBrushAndPen(painter, Qt.white, Qt.white) r = QRectF(-14, -14, 28, 28) painter.drawRoundedRect(r, 2, 2) self._configureBrushAndPen(painter, self.Blue, self.BlueAccent) title = { Placement.LEFT: QRectF(-10, -10, 10, 4), Placement.RIGHT: QRectF(0, -10, 10, 4), Placement.TOP: QRectF(-10, -10, 20, 4), Placement.BOTTOM: QRectF(-10, -2, 20, 4), Placement.TAB: None, } if title[placement] is not None: painter.drawRect(title[placement]) if placement == self._activeAbsoluteRegion: self._configureBrushAndPen(painter, self.Yellow, self.BlueAccent) else: self._configureBrushAndPen(painter, self.BlueDim, self.BlueAccentDim) bg = { Placement.LEFT: QRectF(-10, -5, 10, 14), Placement.RIGHT: QRectF(0, -5, 10, 14), Placement.TOP: QRectF(-10, -5, 20, 7), Placement.BOTTOM: QRectF(-10, 2, 20, 7), Placement.TAB: None, } if bg[placement] is not None: painter.drawRect(bg[placement]) if placement == Placement.LEFT: painter.setBrush(QBrush(self.Blue)) painter.setPen(QPen(self.BlueAccent, 1)) path = QPainterPath() path.moveTo(6, 0) path.lineTo(10, -4) path.lineTo(10, 4) path.closeSubpath() painter.drawPath(path) elif placement == Placement.TOP: painter.setBrush(QBrush(self.Blue)) painter.setPen(QPen(self.BlueAccent, 1)) path = QPainterPath() path.moveTo(0, 6) path.lineTo(-4, 10) path.lineTo(4, 10) path.closeSubpath() painter.drawPath(path) elif placement == Placement.RIGHT: painter.setBrush(QBrush(self.Blue)) painter.setPen(QPen(self.BlueAccent, 1)) path = QPainterPath() path.moveTo(-6, 0) path.lineTo(-10, -4) path.lineTo(-10, 4) path.closeSubpath() painter.drawPath(path) elif placement == Placement.BOTTOM: painter.setBrush(QBrush(self.Blue)) painter.setPen(QPen(self.BlueAccent, 1)) path = QPainterPath() path.moveTo(0, -6) path.lineTo(-4, -10) path.lineTo(4, -10) path.closeSubpath() painter.drawPath(path)
def drawFrame( self, painter, rect, palette, foregroundRole, frameWidth, midLineWidth, frameStyle, ): """ Draw a rectangular frame :param QPainter painter: Painter :param QRectF rect: Frame rectangle :param QPalette palette: Palette :param QPalette.ColorRole foregroundRole: Palette :param int frameWidth: Frame width :param int midLineWidth: Used for `QFrame.Box` :param int frameStyle: bitwise OR´ed value of `QFrame.Shape` and `QFrame.Shadow` """ if frameWidth <= 0 or rect.isEmpty(): return shadow = frameStyle & QFrame.Shadow_Mask painter.save() if shadow == QFrame.Plain: outerRect = rect.adjusted(0.0, 0.0, -1.0, -1.0) innerRect = outerRect.adjusted(frameWidth, frameWidth, -frameWidth, -frameWidth) path = QPainterPath() path.addRect(outerRect) path.addRect(innerRect) painter.setPen(Qt.NoPen) painter.setBrush(palette.color(foregroundRole)) painter.drawPath(path) else: shape = frameStyle & QFrame.Shape_Mask if shape == QFrame.Box: outerRect = rect.adjusted(0.0, 0.0, -1.0, -1.0) midRect1 = outerRect.adjusted(frameWidth, frameWidth, -frameWidth, -frameWidth) midRect2 = midRect1.adjusted(midLineWidth, midLineWidth, -midLineWidth, -midLineWidth) innerRect = midRect2.adjusted(frameWidth, frameWidth, -frameWidth, -frameWidth) path1 = QPainterPath() path1.moveTo(outerRect.bottomLeft()) path1.lineTo(outerRect.topLeft()) path1.lineTo(outerRect.topRight()) path1.lineTo(midRect1.topRight()) path1.lineTo(midRect1.topLeft()) path1.lineTo(midRect1.bottomLeft()) path2 = QPainterPath() path2.moveTo(outerRect.bottomLeft()) path2.lineTo(outerRect.bottomRight()) path2.lineTo(outerRect.topRight()) path2.lineTo(midRect1.topRight()) path2.lineTo(midRect1.bottomRight()) path2.lineTo(midRect1.bottomLeft()) path3 = QPainterPath() path3.moveTo(midRect2.bottomLeft()) path3.lineTo(midRect2.topLeft()) path3.lineTo(midRect2.topRight()) path3.lineTo(innerRect.topRight()) path3.lineTo(innerRect.topLeft()) path3.lineTo(innerRect.bottomLeft()) path4 = QPainterPath() path4.moveTo(midRect2.bottomLeft()) path4.lineTo(midRect2.bottomRight()) path4.lineTo(midRect2.topRight()) path4.lineTo(innerRect.topRight()) path4.lineTo(innerRect.bottomRight()) path4.lineTo(innerRect.bottomLeft()) path5 = QPainterPath() path5.addRect(midRect1) path5.addRect(midRect2) painter.setPen(Qt.NoPen) brush1 = palette.dark().color() brush2 = palette.light().color() if shadow == QFrame.Raised: brush1, brush2 = brush2, brush1 painter.setBrush(brush1) painter.drawPath(path1) painter.drawPath(path4) painter.setBrush(brush2) painter.drawPath(path2) painter.drawPath(path3) painter.setBrush(palette.mid()) painter.drawPath(path5) else: outerRect = rect.adjusted(0.0, 0.0, -1.0, -1.0) innerRect = outerRect.adjusted( frameWidth - 1.0, frameWidth - 1.0, -(frameWidth - 1.0), -(frameWidth - 1.0), ) path1 = QPainterPath() path1.moveTo(outerRect.bottomLeft()) path1.lineTo(outerRect.topLeft()) path1.lineTo(outerRect.topRight()) path1.lineTo(innerRect.topRight()) path1.lineTo(innerRect.topLeft()) path1.lineTo(innerRect.bottomLeft()) path2 = QPainterPath() path2.moveTo(outerRect.bottomLeft()) path2.lineTo(outerRect.bottomRight()) path2.lineTo(outerRect.topRight()) path2.lineTo(innerRect.topRight()) path2.lineTo(innerRect.bottomRight()) path2.lineTo(innerRect.bottomLeft()) painter.setPen(Qt.NoPen) brush1 = palette.dark().color() brush2 = palette.light().color() if shadow == QFrame.Raised: brush1, brush2 = brush2, brush1 painter.setBrush(brush1) painter.drawPath(path1) painter.setBrush(brush2) painter.drawPath(path2) painter.restore()
class Connection_Item(QGraphicsPathItem): def __init__(self, connection, parent): super().__init__() self._connection = connection self._source_ui = self._find_ui(parent, connection.source.connector) self._sink_ui = self._find_ui(parent, connection.sink.connector) self.avoid_conn = None self._duplicate_is = None self._duplicate_of = None self._switch_direction_count = 1 self.path = QPainterPath() self.stroker_path = QPainterPath() self.stroker = QPainterPathStroker() self.stroker.setWidth(8) self.update_endpoints() self.setZValue(-10) self.arrow = QGraphicsPolygonItem() self._set_default_appearance() self.setFlag(self.ItemIsFocusable) self._ports_item = Connection_Ports_Item() self._ports_item.setParentItem(self) self._timer = QTimer(self.scene()) self._timer.setSingleShot(True) self._timer.setSingleShot(True) self._timer.timeout.connect(self._start_hover) self._hover_pos = 0, 0 self.set_show_connection_ports_on_hover(False) def shape(self): return self.stroker_path def setPath(self, path): super().setPath(path) self.stroker_path = self.stroker.createStroke(path) def _set_default_appearance(self): self.setPen(QPen(Qt.black, 2)) self.arrow.setPen(QPen(Qt.black)) self.arrow.setBrush(QBrush(Qt.black)) self._arrow_height = 8 self.update_from_avoid_router() # trigger arrow size change def focusInEvent(self, event): self.setPen(QPen(highlight_color, 4)) self.arrow.setPen(QPen(highlight_color)) self.arrow.setBrush(QBrush(highlight_color)) self._arrow_height = 12 self.update_from_avoid_router() # trigger arrow size change super().focusInEvent(event) def focusOutEvent(self, event): self._set_default_appearance() super().focusOutEvent(event) def keyPressEvent(self, event): key = event.key() if (key == Qt.Key_Delete): self.parentItem().remove_connection(self) return elif (key == Qt.Key_D): self._switch_direction() return super().keyPressEvent(event) def _switch_direction(self): if self._duplicate_is: self.parentItem().remove_connection(self._duplicate_is) self._switch_direction_count = 0 elif self._switch_direction_count < 2: sink = self._connection.sink self._connection.sink = self._connection.source self._connection.source = sink self._source_ui = self._find_ui(self.parentItem(), self._connection.source.connector) self._sink_ui = self._find_ui(self.parentItem(), self._connection.sink.connector) self._switch_direction_count += 1 else: conn = diagram.Connection( source=self._connection.sink, sink=self._connection.source, ) self.parentItem().add_connection(conn) self._switch_direction_count = 3 self.update_endpoints() self.parentItem()._hide_duplicate_connections() if self._switch_direction_count == 0: self._switch_direction() def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton: self._switch_direction() return # Do not call mousePressEvent, will pass on to diagram super().mouseDoubleClickEvent(event) def set_show_connection_ports_on_hover(self, show): if not show: self._ports_item.hide() self.setAcceptHoverEvents(show) def hoverEnterEvent(self, event): w = self._ports_item.rect().width() h = self._ports_item.rect().height() x = event.pos().x() - w / 2.0 y = event.pos().y() - h / 2.0 self._hover_pos = x, y self._timer.start(200) def hoverLeaveEvent(self, event): self._stop_hover() def _start_hover(self): if self.hasFocus(): return #print("start hover", [a.name for a in self._connection.source_ports]) x, y = self._hover_pos self._ports_item.setX(x) self._ports_item.setY(y) self.setZValue(10) self._ports_item.show() def _stop_hover(self): self._timer.stop() self._ports_item.hide() self.setZValue(-10) def _find_ui(self, parent, c): result = None for b_ui in parent._block_items: for c_ui in b_ui._connectors: if c_ui._connector == c: return c_ui return result def _get_endpoint(self, c_ui): x, y = c_ui.get_connection_point() if x is None: return None, None p = self.mapToParent( self.mapFromScene(c_ui.parentItem().mapToScene(x, y))) return p.x(), p.y() def update_endpoints(self): self.x1, self.y1 = self._get_endpoint(self._source_ui) self.x2, self.y2 = self._get_endpoint(self._sink_ui) if self.x1 is None or self.x2 is None: return False self.path = QPainterPath() self.path.moveTo(self.x1, self.y1) self.path.lineTo(self.x2, self.y2) self.setPath(self.path) self._update_avoid() return True def _update_avoid(self): if self.parentItem(): avoid_router = self.parentItem().avoid_router src = avoid.ConnEnd(avoid.Point(self.x1, self.y1)) if self.isVisible(): dest = avoid.ConnEnd(avoid.Point(self.x2, self.y2)) else: dest = src # Don't route duplicate connection if self.avoid_conn is None: self.avoid_conn = avoid.ConnRef(avoid_router, src, dest) else: self.avoid_conn.setEndpoints(src, dest) def update_from_avoid_router(self): if self.avoid_conn is not None and self.avoid_conn.needsRepaint(): radius = self.parentItem().route_radius route = self.avoid_conn.displayRoute() self.path = QPainterPath() last_i = route.size() - 1 for i in range(0, route.size()): point = route.at(i) if i > 0: last_point = route.at(i - 1) last_path_point = self.path.currentPosition() if point.y == last_point.y: # horizontal line if point.x > last_point.x: # right sign = 1 else: # left sign = -1 self.path.quadTo(last_point.x, last_point.y, last_point.x + sign * radius, point.y) if i == last_i: my_x = point.x else: my_x = point.x - sign * radius self.path.lineTo(my_x, point.y) elif point.x == last_point.x: # vertical line if point.y > last_point.y: # down sign = 1 else: # up sign = -1 self.path.quadTo(last_point.x, last_point.y, point.x, last_point.y + sign * radius) if i == last_i: my_y = point.y else: my_y = point.y - sign * radius self.path.lineTo(point.x, my_y) else: self.path.lineTo(point.x, point.y) else: self.path.moveTo(point.x, point.y) self.setPath(self.path) sink = self._sink_ui entry_from = "L" x = 0 if route.at(route.size() - 1).x < route.at(route.size() - 2).x: entry_from = "R" x = sink.parentItem().rect().width() if self._duplicate_of: route = self._duplicate_of.avoid_conn.displayRoute() if route.at(0).x < route.at(1).x: entry_from = "R" x = sink.parentItem().rect().width() y = sink.y() + sink.rect().height() / 2.0 p = self.mapToParent( self.mapFromScene(sink.parentItem().mapToScene(x, y))) xc, yc = p.x(), p.y() arrow_h = self._arrow_height arrow_y = arrow_h / 2.0 arrow_x = sqrt(arrow_h * arrow_h - arrow_y * arrow_y) poly = QPolygonF() if entry_from == "L": poly << QPointF(xc - arrow_x, yc) poly << QPointF(xc - arrow_x, yc + arrow_y) poly << QPointF(xc, yc) poly << QPointF(xc - arrow_x, yc - arrow_y) poly << QPointF(xc - arrow_x, yc) else: poly << QPointF(xc + arrow_x, yc) poly << QPointF(xc + arrow_x, yc + arrow_y) poly << QPointF(xc, yc) poly << QPointF(xc + arrow_x, yc - arrow_y) poly << QPointF(xc + arrow_x, yc) self.arrow.setPolygon(poly) def setParentItem(self, parent_item): super().setParentItem(parent_item) self._update_avoid()
class FadingTipBox(FadingDialog): """ """ def __init__(self, parent, opacity, duration, easing_curve, tour=None, color_top=None, color_back=None, combobox_background=None): super(FadingTipBox, self).__init__(parent, opacity, duration, easing_curve) self.holder = self.anim # needed for qt to work self.parent = parent self.tour = tour self.frames = None self.offset_shadow = 0 self.fixed_width = 300 self.key_pressed = None self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setModal(False) # Widgets def toolbutton(icon): bt = QToolButton() bt.setAutoRaise(True) bt.setIcon(icon) return bt self.button_close = toolbutton(ima.icon("tour.close")) self.button_home = toolbutton(ima.icon("tour.home")) self.button_previous = toolbutton(ima.icon("tour.previous")) self.button_end = toolbutton(ima.icon("tour.end")) self.button_next = toolbutton(ima.icon("tour.next")) self.button_run = QPushButton(_('Run code')) self.button_disable = None self.button_current = QToolButton() self.label_image = QLabel() self.label_title = QLabel() self.combo_title = QComboBox() self.label_current = QLabel() self.label_content = QLabel() self.label_content.setMinimumWidth(self.fixed_width) self.label_content.setMaximumWidth(self.fixed_width) self.label_current.setAlignment(Qt.AlignCenter) self.label_content.setWordWrap(True) self.widgets = [ self.label_content, self.label_title, self.label_current, self.combo_title, self.button_close, self.button_run, self.button_next, self.button_previous, self.button_end, self.button_home, self.button_current ] arrow = get_image_path('hide.png') self.color_top = color_top self.color_back = color_back self.combobox_background = combobox_background self.stylesheet = '''QComboBox {{ padding-left: 5px; background-color: {} border-width: 0px; border-radius: 0px; min-height:20px; max-height:20px; }} QComboBox::drop-down {{ subcontrol-origin: padding; subcontrol-position: top left; border-width: 0px; }} QComboBox::down-arrow {{ image: url({}); }} '''.format(self.combobox_background.name(), arrow) # Windows fix, slashes should be always in unix-style self.stylesheet = self.stylesheet.replace('\\', '/') self.setFocusPolicy(Qt.StrongFocus) for widget in self.widgets: widget.setFocusPolicy(Qt.NoFocus) widget.setStyleSheet(self.stylesheet) layout_top = QHBoxLayout() layout_top.addWidget(self.combo_title) layout_top.addStretch() layout_top.addWidget(self.button_close) layout_top.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_content = QHBoxLayout() layout_content.addWidget(self.label_content) layout_content.addWidget(self.label_image) layout_content.addSpacerItem(QSpacerItem(5, 5)) layout_run = QHBoxLayout() layout_run.addStretch() layout_run.addWidget(self.button_run) layout_run.addStretch() layout_run.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout_navigation = QHBoxLayout() layout_navigation.addWidget(self.button_home) layout_navigation.addWidget(self.button_previous) layout_navigation.addStretch() layout_navigation.addWidget(self.label_current) layout_navigation.addStretch() layout_navigation.addWidget(self.button_next) layout_navigation.addWidget(self.button_end) layout_navigation.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout = QVBoxLayout() layout.addLayout(layout_top) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_content) layout.addLayout(layout_run) layout.addStretch() layout.addSpacerItem(QSpacerItem(15, 15)) layout.addLayout(layout_navigation) layout.addSpacerItem( QSpacerItem(self.offset_shadow, self.offset_shadow)) layout.setSizeConstraint(QLayout.SetFixedSize) self.setLayout(layout) self.set_funcs_before_fade_in([self._disable_widgets]) self.set_funcs_after_fade_in([self._enable_widgets, self.setFocus]) self.set_funcs_before_fade_out([self._disable_widgets]) self.setContextMenuPolicy(Qt.CustomContextMenu) # signals and slots # These are defined every time by the AnimatedTour Class def _disable_widgets(self): """ """ for widget in self.widgets: widget.setDisabled(True) def _enable_widgets(self): """ """ self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) for widget in self.widgets: widget.setDisabled(False) if self.button_disable == 'previous': self.button_previous.setDisabled(True) self.button_home.setDisabled(True) elif self.button_disable == 'next': self.button_next.setDisabled(True) self.button_end.setDisabled(True) def set_data(self, title, content, current, image, run, frames=None, step=None): """ """ self.label_title.setText(title) self.combo_title.clear() self.combo_title.addItems(frames) self.combo_title.setCurrentIndex(step) # min_content_len = max([len(f) for f in frames]) # self.combo_title.setMinimumContentsLength(min_content_len) # Fix and try to see how it looks with a combo box self.label_current.setText(current) self.button_current.setText(current) self.label_content.setText(content) self.image = image if image is None: self.label_image.setFixedHeight(1) self.label_image.setFixedWidth(1) else: extension = image.split('.')[-1] self.image = QPixmap(get_image_path(image), extension) self.label_image.setPixmap(self.image) self.label_image.setFixedSize(self.image.size()) if run is None: self.button_run.setVisible(False) else: self.button_run.setDisabled(False) self.button_run.setVisible(True) # Refresh layout self.layout().activate() def set_pos(self, x, y): """ """ self.x = ceil(x) self.y = ceil(y) self.move(QPoint(self.x, self.y)) def build_paths(self): """ """ geo = self.geometry() radius = 0 shadow = self.offset_shadow x0, y0 = geo.x(), geo.y() width, height = geo.width() - shadow, geo.height() - shadow left, top = 0, 0 right, bottom = width, height self.round_rect_path = QPainterPath() self.round_rect_path.moveTo(right, top + radius) self.round_rect_path.arcTo(right - radius, top, radius, radius, 0.0, 90.0) self.round_rect_path.lineTo(left + radius, top) self.round_rect_path.arcTo(left, top, radius, radius, 90.0, 90.0) self.round_rect_path.lineTo(left, bottom - radius) self.round_rect_path.arcTo(left, bottom - radius, radius, radius, 180.0, 90.0) self.round_rect_path.lineTo(right - radius, bottom) self.round_rect_path.arcTo(right - radius, bottom - radius, radius, radius, 270.0, 90.0) self.round_rect_path.closeSubpath() # Top path header = 36 offset = 2 left, top = offset, offset right = width - (offset) self.top_rect_path = QPainterPath() self.top_rect_path.lineTo(right, top + radius) self.top_rect_path.moveTo(right, top + radius) self.top_rect_path.arcTo(right - radius, top, radius, radius, 0.0, 90.0) self.top_rect_path.lineTo(left + radius, top) self.top_rect_path.arcTo(left, top, radius, radius, 90.0, 90.0) self.top_rect_path.lineTo(left, top + header) self.top_rect_path.lineTo(right, top + header) def paintEvent(self, event): """ """ self.build_paths() painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.fillPath(self.round_rect_path, self.color_back) painter.fillPath(self.top_rect_path, self.color_top) painter.strokePath(self.round_rect_path, QPen(Qt.gray, 1)) # TODO: Build the pointing arrow? def keyReleaseEvent(self, event): """ """ key = event.key() self.key_pressed = key keys = [ Qt.Key_Right, Qt.Key_Left, Qt.Key_Down, Qt.Key_Up, Qt.Key_Escape, Qt.Key_PageUp, Qt.Key_PageDown, Qt.Key_Home, Qt.Key_End, Qt.Key_Menu ] if key in keys: if not self.is_fade_running(): self.sig_key_pressed.emit() def mousePressEvent(self, event): """override Qt method""" # Raise the main application window on click self.parent.raise_() self.raise_() if event.button() == Qt.RightButton: pass # clicked_widget = self.childAt(event.x(), event.y()) # if clicked_widget == self.label_current: # self.context_menu_requested(event) def focusOutEvent(self, event): """Override Qt method.""" # To be used so tips do not appear outside spyder self.tour.lost_focus() def context_menu_requested(self, event): """ """ pos = QPoint(event.x(), event.y()) menu = QMenu(self) actions = [] action_title = create_action(self, _('Go to step: '), icon=QIcon()) action_title.setDisabled(True) actions.append(action_title) # actions.append(create_action(self, _(': '), icon=QIcon())) add_actions(menu, actions) menu.popup(self.mapToGlobal(pos)) def reject(self): """Qt method to handle escape key event""" if not self.is_fade_running(): key = Qt.Key_Escape self.key_pressed = key self.sig_key_pressed.emit()
class RectangularRegion(ChartItem): """ Rectangular overlay, that (optionally) can be resized by handles. TODO: * Notification framework * Modifiers to allow for more flexible mouse interactions """ def __init__(self, x, y, width=1, height=1, rotation=0, parent=None): """ Constructor. :param x: Initial x position of ROIs center. :param y: Initial y position of ROIs center. :param width: Initial width :param height: Initial height :param rotation: Initial rotation in radians :param parent: Optional parent item. """ super(RectangularRegion, self).__init__(parent) self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemSendsGeometryChanges) # self.setHandlesChildEvents(False) # self.setFiltersChildEvents(True) self.__in_move = False self._path = QPainterPath() self.pen = QPen(QBrush(QColor(161, 0, 161, 255)), 1.0, Qt.SolidLine) self.pen.setCosmetic(True) self.brush = QBrush(QColor(255, 255, 255, 40)) self.state = RoiState() self.state.pos = QPointF(x, y) self.state.w0 = width self.state.w1 = width self.state.h0 = height self.state.h1 = height self.state.rotation = rotation self.setPos(self.state.pos) self.updatePath() self._handles = [] @property def handles(self): """ Returns all handles which are attached to the ROI. :return: list """ return self._handles def addHandle(self, handle): """ Adds a handle to the ROI. :param handle: Resize or rotate handle """ handle.setParentItem(self) # self.addToGroup(handle) self.handles.append(handle) handle.updatePosition() def removeHandle(self, handle): """ Removes the given handle from the ROI. :param handle: Handle :return: """ if handle not in self.handles: _log.warning("Handle not part of ROI.") return self.handles.remove(handle) # self.removeFromGroup(handle) self.scene().removeItem(handle) def boundingRect(self): return self._path.boundingRect() def updatePath(self): p0 = Vec2(self.state.w0, self.state.h0) p1 = Vec2(-self.state.w1, self.state.h0) p2 = Vec2(-self.state.w1, -self.state.h1) p3 = Vec2(self.state.w0, -self.state.h1) p0t = p0.rotate(self.state.rotation) p1t = p1.rotate(self.state.rotation) p2t = p2.rotate(self.state.rotation) p3t = p3.rotate(self.state.rotation) self._path = QPainterPath() self._path.moveTo(p0t.x, p0t.y) self._path.lineTo(p1t.x, p1t.y) self._path.lineTo(p2t.x, p2t.y) self._path.lineTo(p3t.x, p3t.y) self._path.closeSubpath() self.prepareGeometryChange() def paint(self, p=QPainter(), o=QStyleOptionGraphicsItem(), widget=None): p.setRenderHint(QPainter.Antialiasing) p.setPen(self.pen) p.setBrush(self.brush) p.drawPath(self._path) p.setBrush(Qt.transparent) p.drawLine(QLineF(-0.5, 0, 0.5, 0)) p.drawLine(QLineF(0, -0.5, 0, 0.5)) p.drawEllipse(QPointF(0, 0), 0.25, 0.25) def mousePressEvent(self, e): # !!!F*****g leason learned, call super to avoid weird jumps in position!!! super(RectangularRegion, self).mousePressEvent(e) self.__in_move = True def mouseReleaseEvent(self, e): super(RectangularRegion, self).mouseReleaseEvent(e) self.__in_move = False def itemChange(self, change, value): if change == QGraphicsItem.ItemPositionChange and self.__in_move: old_pos = self.pos() new_pos = value # .toPointF() delta = old_pos - new_pos _log.debug("Delta: {}".format(delta)) self.state.pos = new_pos value = new_pos for handle in self.handles: handle.updatePosition() return super(RectangularRegion, self).itemChange(change, value) def rotation(self): """ Override takes rotation from the state instead of items transformation. :return: rotation in degrees """ return self.state.rotation * 180 / np.pi def setRotation(self, degree): """ Override to propagate rotation to state instead of applying it to the items transform :param degree: Rotation in degrees """ self.state.rotation = (degree % 360) * np.pi / 180.0 for h in self.handles: h.updatePosition() self.updatePath()