def toQPainterPath(self): """Return a QPainterPath containing all segments of this path. """ if not self.isValid(): raise Path2dException('invalid path') p = QPainterPath() sx, sy = self._elements[0] if len(self._elements[1]) == 2: # Only add a start point if the QPainterPath will start with a # line, not an arc. p.moveTo(sx, sy) for e in self._elements[1:]: if len(e) == 2: p.lineTo(*e) sx, sy = e else: (ex, ey), (cx, cy), arcDir = e r = hypot(ex-cx, ey-cy) d = r*2 sa = degrees(atan2(sy-cy, sx-cx)) % 360.0 ea = degrees(atan2(ey-cy, ex-cx)) % 360.0 # NOTE: machtool uses a right-handed cartesian coordinate # system with the Y+ up. Because of this, the QRectF # used to define the arc has a negative height. This # makes a positive arc angle sweep cclw as it should. rect = QRectF(cx - r, cy + r, d, -d) if arcDir == 'cclw': span = (ea + 360.0 if ea < sa else ea) - sa else: span = -((sa + 360.0 if sa < ea else sa) - ea) p.arcMoveTo(rect, sa) p.arcTo(rect, sa, span) sx, sy = ex, ey return p
def itemRegion(self, index): if not index.isValid() or not self.model(): return QRegion() return QRegion() # TODO: what to do with this code? if index.column != 1: return self.itemRect(index) if self.model().data(index).toDouble()[0] <= 0.0: return QRegion() startAngle = 0.0 for row in xrange(self.model().rowCount(self.rootIndex())): sliceIndex = self.model().index(row, 1, self.rootIndex()) value = self.model().data(sliceIndex).toDouble()[0] if value > 0.0: angle = 360*value/self.totalValue if sliceIndex == index: slicePath = QPainterPath() slicePath.moveTo(self.totalSize/2, self.totalSize/2) slicePath.arcTo(self.margin, self.margin, self.margin+self.histogramSize, self.margin+self.histogramSize, startAngle, angle) slicePath.closeSubpath() return QRegion(slicePath.toFillPolygon().toPolygon()) startAngle += angle return QRegion()
def toQPainterPath(self): """Return a QPainterPath containing all segments of this path. """ if not self.isValid(): raise Path2dException('invalid path') p = QPainterPath() sx, sy = self._elements[0] if len(self._elements[1]) == 2: # Only add a start point if the QPainterPath will start with a # line, not an arc. p.moveTo(sx, sy) for e in self._elements[1:]: if len(e) == 2: p.lineTo(*e) sx, sy = e else: (ex, ey), (cx, cy), arcDir = e r = hypot(ex - cx, ey - cy) d = r * 2 sa = degrees(atan2(sy - cy, sx - cx)) % 360.0 ea = degrees(atan2(ey - cy, ex - cx)) % 360.0 # NOTE: machtool uses a right-handed cartesian coordinate # system with the Y+ up. Because of this, the QRectF # used to define the arc has a negative height. This # makes a positive arc angle sweep cclw as it should. rect = QRectF(cx - r, cy + r, d, -d) if arcDir == 'cclw': span = (ea + 360.0 if ea < sa else ea) - sa else: span = -((sa + 360.0 if sa < ea else sa) - ea) p.arcMoveTo(rect, sa) p.arcTo(rect, sa, span) sx, sy = ex, ey return p
class RobotView(QGraphicsItem): def __init__(self, robot): super(RobotView, self).__init__() self.robot = robot self.outline = QPainterPath() self.cut_angle = 0.0 self.setFlags(QGraphicsItem.ItemIsSelectable) @property def uuid(self): return self.robot.uuid @property def color(self): if self.isSelected(): return GREEN elif self.robot.is_blue: return BLUE elif self.robot.is_yellow: return YELLOW else: return BLACK def position(self): x, y, width, height = s(self.robot.x, self.robot.y, self.robot.world.length, self.robot.world.width) radius = s(self.robot.radius) self.cut_angle = acos(self.robot.front_cut / self.robot.radius) self.outline = QPainterPath() self.outline.moveTo(radius, 0) self.outline.arcTo(-radius, -radius, 2 * radius, 2 * radius, 0, 360 - 2 * self.cut_angle) self.outline.closeSubpath() self.setPos(x, -y) def boundingRect(self): radius = s(self.robot.radius) return QRectF(-radius, -radius, 2 * radius, 2 * radius) def paint(self, painter, option, widget=None): # Save transformation: painter.save() color = self.color # Change position painter.setBrush(color) painter.setPen(color) robot_rotation = self.robot.angle or 0.0 # Draw robot shape painter.rotate(-self.cut_angle - robot_rotation) painter.drawPath(self.outline) painter.rotate(self.cut_angle + robot_rotation) # Reset transformation painter.restore()
def draw_arc(x, y, radius_in, radius_out, angle_init, angle_end, painter): path = QPainterPath() path.moveTo(x + radius_in * cos(angle_init), y + radius_in * sin(angle_init)) path.arcTo(x - radius_out, y - radius_out, 2 * radius_out, 2 * radius_out, angle_init, angle_end - angle_init) path.arcTo(x - radius_in, y - radius_in, 2 * radius_in, 2 * radius_in, angle_end, angle_init - angle_end) path.closeSubpath() painter.drawPath(path)
def __init__(self, parent, pos, angle, length, color, radius, pit=False): PhysicalSector.__init__(self, parent, pos, angle, length, color, pit) self.angle = 180.0 * self.length() / (math.pi * radius) startAngle = 180.0 if radius > 0 else 0 offset = _pitDistance if pit else 0 radius -= offset rect = QRectF(offset + (2 * radius if radius < 0 else 0), -abs(radius), 2 * abs(radius), 2 * abs(radius)) path = QPainterPath(QPointF(offset, 0)) path.arcTo(rect, startAngle, self.angle) self.setPath(path)
def drawValue(self, p, baseRect, value, delta): if value == self.m_min: 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 setupGraphics(self): """ Set up the graphics. """ shape_rect = QRectF(-24, -24, 48, 48) self.shapeItem = NodeBodyItem(self) self.shapeItem.setShapeRect(shape_rect) self.shapeItem.setAnimationEnabled(self.__animationEnabled) # Rect for widget's 'ears'. anchor_rect = QRectF(-31, -31, 62, 62) self.inputAnchorItem = SinkAnchorItem(self) input_path = QPainterPath() start_angle = 180 - self.ANCHOR_SPAN_ANGLE / 2 input_path.arcMoveTo(anchor_rect, start_angle) input_path.arcTo(anchor_rect, start_angle, self.ANCHOR_SPAN_ANGLE) self.inputAnchorItem.setAnchorPath(input_path) self.outputAnchorItem = SourceAnchorItem(self) output_path = QPainterPath() start_angle = self.ANCHOR_SPAN_ANGLE / 2 output_path.arcMoveTo(anchor_rect, start_angle) output_path.arcTo(anchor_rect, start_angle, -self.ANCHOR_SPAN_ANGLE) self.outputAnchorItem.setAnchorPath(output_path) self.inputAnchorItem.hide() self.outputAnchorItem.hide() # Title caption item self.captionTextItem = NameTextItem(self) self.captionTextItem.setPlainText("") self.captionTextItem.setPos(0, 33) def iconItem(standard_pixmap): item = GraphicsIconItem(self, icon=standard_icon(standard_pixmap), iconSize=QSize(16, 16)) item.hide() return item self.errorItem = iconItem(QStyle.SP_MessageBoxCritical) self.warningItem = iconItem(QStyle.SP_MessageBoxWarning) self.infoItem = iconItem(QStyle.SP_MessageBoxInformation) self.prepareGeometryChange() self.__boundingRect = None
def setupGraphics(self): """ Set up the graphics. """ shape_rect = QRectF(-24, -24, 48, 48) self.shapeItem = NodeBodyItem(self) self.shapeItem.setShapeRect(shape_rect) self.shapeItem.setAnimationEnabled(self.__animationEnabled) # Rect for widget's 'ears'. anchor_rect = QRectF(-31, -31, 62, 62) self.inputAnchorItem = SinkAnchorItem(self) input_path = QPainterPath() start_angle = 180 - self.ANCHOR_SPAN_ANGLE / 2 input_path.arcMoveTo(anchor_rect, start_angle) input_path.arcTo(anchor_rect, start_angle, self.ANCHOR_SPAN_ANGLE) self.inputAnchorItem.setAnchorPath(input_path) self.outputAnchorItem = SourceAnchorItem(self) output_path = QPainterPath() start_angle = self.ANCHOR_SPAN_ANGLE / 2 output_path.arcMoveTo(anchor_rect, start_angle) output_path.arcTo(anchor_rect, start_angle, - self.ANCHOR_SPAN_ANGLE) self.outputAnchorItem.setAnchorPath(output_path) self.inputAnchorItem.hide() self.outputAnchorItem.hide() # Title caption item self.captionTextItem = NameTextItem(self) self.captionTextItem.setPlainText("") self.captionTextItem.setPos(0, 33) def iconItem(standard_pixmap): item = GraphicsIconItem(self, icon=standard_icon(standard_pixmap), iconSize=QSize(16, 16)) item.hide() return item self.errorItem = iconItem(QStyle.SP_MessageBoxCritical) self.warningItem = iconItem(QStyle.SP_MessageBoxWarning) self.infoItem = iconItem(QStyle.SP_MessageBoxInformation) self.prepareGeometryChange() self.__boundingRect = None
def __init__(self, radiusOut, raiusIn, angle, arcLen, parent=None): QGraphicsPathItem.__init__(self, parent=parent) self._radiusOut = radiusOut self._raiusIn = raiusIn self._angle = angle self._arcLen = arcLen self._pen = QPen(QColor('#000000')) self._pen.setWidth(1) self.setPen(self._pen) self._hoverPen = QPen(QColor('#000000')) self._hoverPen.setWidth(2) brush = QBrush(QColor('#FF9966')) self.setBrush(brush) rectOut = QRectF(-radiusOut, -radiusOut, radiusOut*2.0, radiusOut*2.0) rectIn = QRectF(-raiusIn, -raiusIn, raiusIn*2.0, raiusIn*2.0) startAngle = angle - arcLen/2.0 endAngle = angle + arcLen/2.0 path = QPainterPath() path.arcMoveTo(rectIn, startAngle) path.arcTo(rectOut, startAngle, arcLen) path.arcTo(rectIn, endAngle, 0) path.arcTo(rectIn, endAngle, -arcLen) self.setPath(path) self._isHover = False self._ioDragFirstPos = None
def paintEvent(self, e): self._plotter.begin(self) if self._reaction.GetCatalyst().GetUsed(): change = self._reaction.GetCatalyst().GetEfficacy() else: change = 1 eqmpoint = 150 / change if change > 1: eqmpoint -= 0.1 * self._reaction.GetCatalyst().GetInitialMoles() elif change < 1: eqmpoint += 0.1 * self._reaction.GetCatalyst().GetInitialMoles() pen = QPen(Qt.black, 2, Qt.SolidLine) self._plotter.setPen(pen) self._plotter.drawLine(80, 20, 80, 220) self._plotter.drawLine(80, 220, 400, 220) self._plotter.setPen(QPen(self._reaction.GetReactantColour())) reactionpath = QPainterPath() reactionpath.moveTo(QPointF(80, 20)) reactionpath.arcTo(80, -80 + self._plotter.GetFinalY(), eqmpoint * 2, 200 - (2 * self._plotter.GetFinalY()), 180, 90) reactionpath.lineTo(400, 120 - self._plotter.GetFinalY()) self._plotter.drawPath(reactionpath) self._plotter.setPen(QPen(self._reaction.GetProductColour())) productpath = QPainterPath() productpath.moveTo(QPointF(80, 220)) productpath.arcTo(80, 120 + self._plotter.GetFinalY(), eqmpoint * 2, 200 - (2 * self._plotter.GetFinalY()), 180, -90) productpath.lineTo(400, 120 + self._plotter.GetFinalY()) self._plotter.drawPath(productpath) if not self._forprinting: white = QColor(240, 240, 240) self._plotter.setPen(QPen(white)) self._plotter.setBrush(white) self._plotter.drawRect(82 + self.parent().GetAnimUpdates() * 0.23, 20, 320, 198) self._plotter.setPen(QColor(0, 0, 0)) self._plotter.drawText(180, 235, "Time") self._plotter.drawText(0, 120, self._graphof) self._plotter.end()
def drawGraph(self, plotter, change): pen = QPen(Qt.black, 2, Qt.SolidLine) plotter.setPen(pen) plotter.drawLine(20, 20, 20, 220) plotter.drawLine(20, 220, 220, 220) reactants = self._reaction.GetReactants() AverageReactantConc = 0 for x in range(self._reaction.REACTING_SPECIES_LIMIT): AverageReactantConc += reactants[x].GetInitialMoles() AverageReactantConc /= (len(reactants) * self._reaction.GetVolume()) plotter.setPen(QPen(plotter.GetReactantColour(), 2, Qt.SolidLine)) reactionpath = QPainterPath() reactionpath.moveTo(QPointF(20, 20)) reactionpath.arcTo(20, -70, 300 * change, 180, 180, 90) reactionpath.lineTo(220, 110) plotter.drawPath(reactionpath) plotter.setPen(QPen(plotter.GetProductColour(), 2, Qt.SolidLine)) productpath = QPainterPath() productpath.moveTo(QPointF(20, 220)) productpath.arcTo(20, 130, 300 * change, 180, 180, -90) productpath.lineTo(220, 130) plotter.drawPath(productpath)
def paintEvent(self, e): self._plotter.begin(self) if self._reaction.GetCatalyst().GetUsed(): change = self._reaction.GetCatalyst().GetEfficacy() else: change = 1 eqmpoint = 150/change if change > 1: eqmpoint -= 0.1 * self._reaction.GetCatalyst().GetInitialMoles() elif change < 1: eqmpoint += 0.1 * self._reaction.GetCatalyst().GetInitialMoles() pen = QPen(Qt.black, 2, Qt.SolidLine) self._plotter.setPen(pen) self._plotter.drawLine(80, 20, 80, 220) self._plotter.drawLine(80, 220, 400, 220) self._plotter.setPen(QPen(self._reaction.GetReactantColour())) reactionpath = QPainterPath() reactionpath.moveTo(QPointF(80, 20)) reactionpath.arcTo(80, -80 + self._plotter.GetFinalY(), eqmpoint * 2, 200 - (2 * self._plotter.GetFinalY()), 180, 90) reactionpath.lineTo(400, 120 - self._plotter.GetFinalY()) self._plotter.drawPath(reactionpath) self._plotter.setPen(QPen(self._reaction.GetProductColour())) productpath = QPainterPath() productpath.moveTo(QPointF(80, 220)) productpath.arcTo(80, 120 + self._plotter.GetFinalY(), eqmpoint * 2, 200 - (2 * self._plotter.GetFinalY()), 180, -90) productpath.lineTo(400, 120 + self._plotter.GetFinalY()) self._plotter.drawPath(productpath) if not self._forprinting: white = QColor(240, 240, 240) self._plotter.setPen(QPen(white)) self._plotter.setBrush(white) self._plotter.drawRect(82 + self.parent().GetAnimUpdates() * 0.23, 20, 320, 198) self._plotter.setPen(QColor(0, 0, 0)) self._plotter.drawText(180, 235, "Time") self._plotter.drawText(0, 120, self._graphof) self._plotter.end()
def _generate_popup_path(rect, xRadius, yRadius, arrowSize, anchor): """ Generate the QPainterPath used to draw the outline of the popup. Parameters ---------- rect : QRect Bounding rect for the popup. xRadius, yRadius : int x and y radius of the popup. arrowSize : QSize Width and height of the popup anchor arrow. anchor : int Positioning of the popup relative to the parent. Determines the position of the arrow. Returns ------- result : QPainterPath Path that can be passed to QPainter.drawPath to render popup. """ awidth, aheight = arrowSize.width(), arrowSize.height() draw_arrow = (awidth > 0 and aheight > 0) if anchor == QBubbleView.AnchorRight: rect.adjust(aheight, 0, 0, 0) elif anchor == QBubbleView.AnchorLeft: rect.adjust(0, 0, -aheight, 0) elif anchor == QBubbleView.AnchorBottom: rect.adjust(0, aheight, 0, 0) else: rect.adjust(0, 0, 0, -aheight) r = rect.normalized() if r.isNull(): return hw = r.width() / 2 hh = r.height() / 2 xRadius = 100 * min(xRadius, hw) / hw yRadius = 100 * min(yRadius, hh) / hh # The starting point of the path is the top left corner x = r.x() y = r.y() w = r.width() h = r.height() rxx2 = w * xRadius / 100 ryy2 = h * yRadius / 100 center = r.center() path = QPainterPath() path.arcMoveTo(x, y, rxx2, ryy2, 180) path.arcTo(x, y, rxx2, ryy2, 180, -90) if anchor == QBubbleView.AnchorBottom and draw_arrow: path.lineTo(center.x() - awidth, y) path.lineTo(center.x(), y - aheight) path.lineTo(center.x() + awidth, y) path.arcTo(x + w - rxx2, y, rxx2, ryy2, 90, -90) if anchor == QBubbleView.AnchorLeft and draw_arrow: path.lineTo(x + w, center.y() - awidth) path.lineTo(x + w + aheight, center.y()) path.lineTo(x + w, center.y() + awidth) path.arcTo(x + w - rxx2, y + h - ryy2, rxx2, ryy2, 0, -90) if anchor == QBubbleView.AnchorTop and draw_arrow: path.lineTo(center.x() + awidth, y + h) path.lineTo(center.x(), y + h + aheight) path.lineTo(center.x() - awidth, y + h) path.arcTo(x, y + h - ryy2, rxx2, ryy2, 270, -90) if anchor == QBubbleView.AnchorRight and draw_arrow: path.lineTo(x, center.y() + awidth) path.lineTo(x - aheight, center.y()) path.lineTo(x, center.y() - awidth) path.closeSubpath() return path
class DMCircularGauge(QWidget): sci_notation = False _value = 0.0 percentage = 0.0 lolo_arc = QPainterPath() low_arc = QPainterPath() high_arc = QPainterPath() hihi_arc = QPainterPath() def __init__(self, channel=None, range_low=None, range_high=None, parent=None): super(DMCircularGauge, self).__init__(parent) self.channel = channel self._channel = self.channel.value if range_low is None: range_low = self.channel.range()[0] if range_high is None: range_high = self.channel.range()[1] if range_low > range_high: temp = range_low range_low = range_high range_high = temp self.range = [range_low, range_high] self.connect(self.channel, SIGNAL('new_value(float)'), self.update_value) self.connect(self.channel, SIGNAL('new_limits(float, float, float, float)'), self.update_limits) self.width_ref = 400.0 self.height_ref = 240.0 self.resize(self.width_ref, self.height_ref) self.ref_aspect_ratio = self.width_ref / self.height_ref self.dial_v_offset = self.height() * 0.1 self.dial_height = self.width() / 2.0 self.dial_width = self.width() self.dial = QPainterPath(QPointF(0.0, self.dial_height)) self.dial.arcTo(0.0, 1.0, self.dial_width, self.dial_height * 2, 180, -180) self.dial.lineTo(self.dial_width, self.height_ref) self.dial.lineTo(0.0, self.height_ref) self.dial.lineTo(0.0, self.dial_height) # self.dial_bg = QRadialGradient(QPointF(self.width()/2.0, self.height()-self.dial_v_offset), self.dial_height) # self.dial_bg.setColorAt(0, Qt.lightGray) # self.dial_bg.setColorAt(0.98, QColor(50, 50, 50, 255)) # self.dial_bg.setColorAt(1, Qt.black) needle_base_width = 18 self.needle = QPolygonF([QPoint(0, -needle_base_width/2.0), QPoint(self.dial_height * 0.9, 0.0), QPoint(0, needle_base_width / 2.0)]) self.needle_color = Qt.gray # self.needle_left = QPolygonF([QPoint(0, 0), # QPoint(0, -needle_base_width/2.0), # QPoint(self.dial_height*0.9, 0.0)]) # self.needle_right = QPolygonF([QPoint(0, 0), # QPoint(0, needle_base_width / 2.0), # QPoint(self.dial_height * 0.9, 0.0)]) pin_diameter = 22 self.pin_rect = QRectF(-pin_diameter / 2.0, -pin_diameter / 2.0, pin_diameter, pin_diameter) # self.pin_bg = QRadialGradient(QPointF(0.0, -pin_diameter / 5.0), pin_diameter * 0.75) # self.pin_bg.setColorAt(0, Qt.lightGray) # self.pin_bg.setColorAt(1, Qt.black) # self.shadow_rect = QRectF(-self.dial_width / 2, -self.dial_height / 2, # self.dial_width, self.dial_height * 1.1) # self.gloss_rect = QRectF(-self.dial_width / 5, -self.dial_height / 2, # self.dial_width / 2.5, self.dial_height / 2) # self.gloss_gradient = QLinearGradient(QPointF(0.0, -self.dial_height / 2), QPointF(0.0, 0.0)) # self.gloss_gradient.setColorAt(0.0, QColor(255, 255, 255, 120)) # self.gloss_gradient.setColorAt(0.95, QColor(255, 255, 255, 0)) limits = self.channel.limits() self.update_limits(limits[0], limits[1], limits[2], limits[3]) self.pv_label_font = QFont() self.pv_label_font.setPixelSize(22) self.pv_label_font.setWeight(QFont.Bold) # font_metric = QFontMetrics(self.pv_label_font) # pv_label = self.channel.egu # self.channel.name + ' (' + self.channel.egu + ')' # text_path = QPainterPath() # text_path.addText(QPointF(0.0 - font_metric.width(pv_label) / 2.0, # (self.dial_height / 2.0) + (font_metric.height() * 1.5)), # self.pv_label_font, # pv_label) # stroke_path = QPainterPathStroker() # stroke_path.setWidth(1) # self.pv_label_path = QPainterPath(stroke_path.createStroke(text_path) + text_path).simplified() def paintEvent(self, event): # Initialize QPainter properties painter = QPainter() painter.begin(self) painter.setRenderHint(QPainter.Antialiasing) if self.height() <= self.width() / self.ref_aspect_ratio: v_scale = self.height() h_scale = v_scale * self.ref_aspect_ratio else: h_scale = self.width() v_scale = h_scale / self.ref_aspect_ratio # Scale all objects proportionate to window size painter.scale(h_scale / self.width_ref, v_scale / self.height_ref) painter.setClipPath(self.dial) # Don't allow objects or text to extend outside of main dial shape painter.save() # First draw main gauge background pen = QPen(painter.pen()) pen.setWidth(1) pen.setColor(Qt.black) painter.setPen(pen) painter.setBrush(QColor(100, 100, 100, 255)) # self.dial_bg) painter.drawPath(self.dial) # Add Minor and Major Alarm limit bars pen.setWidth(16) pen.setCapStyle(Qt.FlatCap) pen.setJoinStyle(Qt.BevelJoin) pen.setColor(Qt.yellow) painter.setPen(pen) painter.setBrush(Qt.NoBrush) painter.drawPath(self.low_arc) painter.drawPath(self.high_arc) pen.setColor(Qt.red) painter.setPen(pen) painter.drawPath(self.lolo_arc) painter.drawPath(self.hihi_arc) painter.restore() # Display PV current value painter.save() font = QFont() font.setPixelSize(45) painter.setFont(font) sevr = self.channel.sevr.lower() if sevr == 'major': color = Qt.red elif sevr == 'minor': color = Qt.yellow elif sevr == 'invalid': color = Qt.magenta else: color = Qt.green pen.setColor(color) painter.setPen(pen) font_metric = QFontMetrics(font) painter.translate(self.dial_width / 2, self.dial_height / 2) label = self.format_label(self.channel_value) painter.drawText(QPointF(0.0 - font_metric.width(label) / 2.0, font_metric.height() / 2.0), label) # Display PV name painter.setFont(self.pv_label_font) pen.setColor(Qt.black) # Qt.darkCyan) pen.setWidth(1) painter.setPen(pen) # brush = QBrush(Qt.darkCyan) # painter.setBrush(brush) font_metric = QFontMetrics(self.pv_label_font) pv_label = self.channel.egu # self.channel.name + ' (' + self.channel.egu + ')' painter.drawText(QPointF(0.0 - font_metric.width(pv_label) / 2.0, (self.dial_height / 2.0) + (font_metric.height() * 1.5)), pv_label) # painter.drawPath(self.pv_label_path) painter.restore() # Next add division markers painter.save() painter.translate(self.dial_width / 2, self.dial_height * 0.98) pen.setColor(Qt.black) # Qt.cyan) pen.setWidth(2) painter.setPen(pen) for i in range(0, 31): if (i % 5) != 0: painter.drawLine(-self.dial_width / 2.1, 0.0, -self.dial_width / 2.2, 0.0) else: painter.drawLine(-self.dial_width / 2.1, 0.0, -self.dial_width / 2.3, 0.0) painter.rotate(6.0) painter.restore() # Layout division text labels painter.save() painter.translate(self.dial_width / 2, self.dial_height * 0.98) pen.setColor(Qt.black) # Qt.cyan) painter.setPen(pen) font = QFont() font.setPixelSize(18) painter.setFont(font) font_metric = QFontMetrics(font) labels = linspace(self.lim_low, self.lim_hi, 7) painter.rotate(-90) for i in range(0, 7): label = self.format_label(labels[i]) painter.drawText(QPointF(0.0 - font_metric.width(label) / 2.0, -self.dial_height * 0.75), label) painter.rotate(30) painter.restore() # Draw needle at appropriate angle for data painter.save() painter.translate(self.dial_width / 2, self.dial_height * 0.98) painter.rotate(-180 * (1.0 - self.percentage)) pen.setColor(QColor(self.needle_color).darker(200)) pen.setWidth(1) painter.setPen(pen) painter.setBrush(self.needle_color) painter.drawPolygon(self.needle) painter.restore() # if self.percentage <= 0.5: # shadow = max(490 * self.percentage, 127) # needle_left_color = QColor(0, shadow, shadow) # Qt.darkCyan # QColor(80,80,80,255) # needle_right_color = Qt.cyan # QColor(230,230,230,255) # else: # shadow = max(125 / self.percentage, 127) # needle_left_color = Qt.cyan # QColor(230,230,230,255) # needle_right_color = QColor(0, shadow, shadow) # Qt.darkCyan # QColor(80,80,80,255) # # # Draw Highlight side of needle # pen.setWidth(1) # pen.setColor(Qt.gray) # needle_left_color) # painter.setPen(pen) # painter.setBrush(Qt.gray) # needle_left_color) # painter.drawPolygon(self.needle_left) # # # Draw shadow side of needle # pen.setColor(Qt.gray) # needle_right_color) # painter.setPen(pen) # painter.setBrush(Qt.gray) # needle_right_color) # painter.drawPolygon(self.needle_right) # painter.restore() # Draw needle axel pin painter.save() pen.setWidth(1) pen.setColor(Qt.black) painter.setPen(pen) painter.setBrush(QColor(50, 50, 50, 255)) # self.pin_bg) painter.translate(self.dial_width / 2, self.dial_height * 0.98) painter.drawEllipse(self.pin_rect) painter.restore() # Draw glass reflection and shadow effects # painter.save() # painter.translate(self.dial_width / 2.0, self.dial_height / 2.0) # painter.setPen(Qt.NoPen) # painter.setBrush(QColor(0, 0, 0, 20)) # painter.drawEllipse(self.shadow_rect) # painter.setBrush(self.gloss_gradient) # painter.drawEllipse(self.gloss_rect) # painter.restore() painter.end() def format_label(self, label): # Automatically switch to scientific notation for very large or very small numbers if self.sci_notation or abs(label) >= 10000 or abs(label) < 0.01: return QString().setNum(label, 'g', 2) else: return QString().setNum(label, 'f', 2) @property def lim_hi(self): return self.range[1] @lim_hi.setter def lim_hi(self, lim): self.range[1] = lim @property def lim_low(self): return self.range[0] @lim_low.setter def lim_low(self, lim): self.range[0] = lim def update_value(self, value=0.0): self.channel_value = value self.update() @property def channel_value(self): return self._value @channel_value.setter def channel_value(self, value): self._value = value self.update_percentage() def update_limits(self, lolo, low, high, hihi): full_range = self.lim_hi - self.lim_low left_x = self.dial_width * 0.025 right_x = self.dial_width * 0.975 angle = -180 * (1 - (self.lim_hi - lolo) / full_range) self.lolo_arc = self.make_arc(left_x, self.dial_height, 180.0, angle) angle = -180 * (1 - (self.lim_hi - low) / full_range) self.low_arc = self.make_arc(left_x, self.dial_height, 180.0, angle) angle = 180 * (self.lim_hi - high) / full_range self.high_arc = self.make_arc(right_x, self.dial_height, 0.0, angle) angle = 180 * (self.lim_hi - hihi) / full_range self.hihi_arc = self.make_arc(right_x, self.dial_height, 0.0, angle) def make_arc(self, start_x, start_y, start_angle, end_angle): arc = QPainterPath(QPointF(start_x, start_y)) arc.arcTo(self.dial_width * 0.025, self.dial_width * 0.025, # self.dial_width * 0.15, self.dial_width * 0.95, self.dial_width * 0.95, start_angle, end_angle) return arc def update_percentage(self): value = min(max(self.channel_value, self.lim_low), self.lim_hi) self.percentage = (value - self.lim_low) / (self.lim_hi - self.lim_low) def channels(self): pass
def make_arc(self, start_x, start_y, start_angle, end_angle): arc = QPainterPath(QPointF(start_x, start_y)) arc.arcTo(self.dial_width * 0.025, self.dial_width * 0.025, # self.dial_width * 0.15, self.dial_width * 0.95, self.dial_width * 0.95, start_angle, end_angle) return arc
def drawCorner(self, painter, position, cornerType, maxRadius=None): #logging.debug(self.__class__.__name__ +": drawCorner() "+ self.cornerTypeString(cornerType)) thickness = self.CONNECTION_THICKNESS * self.zoomFactor() halfthick = thickness / 2 cornerRoundness = halfthick ** 0.5 cornerOffset = halfthick * (cornerRoundness) innerCorner = halfthick * (cornerRoundness - 1) outerCorner = halfthick * (cornerRoundness + 1) innerWidth = halfthick * (cornerRoundness - 1) radius = halfthick * (cornerRoundness + 1) if maxRadius: maxRadius = max(maxRadius, thickness) radius = min(radius, maxRadius) if cornerType == self.CornerType.TOP_RIGHT: startAngle = 0 outerCorner = QPointF(position.x() + halfthick - 2 * radius, position.y() - halfthick) innerCorner = QPointF(outerCorner.x(), outerCorner.y() + (thickness)) center = QPointF(outerCorner.x() + radius, outerCorner.y() + radius) outerRect = QRectF(outerCorner, QSizeF(2 * radius, 2 * radius)) innerRect = QRectF(innerCorner, QSizeF((2 * radius - thickness), (2 * radius - thickness))) outerStart = QPointF(outerCorner.x() + 2 * radius, outerCorner.y() + (radius + halfthick)) innerStart = QPointF(outerCorner.x() + (radius - halfthick), outerCorner.y()) elif cornerType == self.CornerType.TOP_LEFT: startAngle = 90 outerCorner = QPointF(position.x() - halfthick, position.y() - halfthick) innerCorner = QPointF(outerCorner.x() + (thickness), outerCorner.y() + (thickness)) center = QPointF(outerCorner.x() + radius, outerCorner.y() + radius) outerRect = QRectF(outerCorner, QSizeF(2 * radius, 2 * radius)) innerRect = QRectF(innerCorner, QSizeF((2 * radius - thickness), (2 * radius - thickness))) outerStart = QPointF(outerCorner.x() + (radius + halfthick), outerCorner.y()) innerStart = QPointF(outerCorner.x(), outerCorner.y() + (radius + halfthick)) elif cornerType == self.CornerType.BOTTOM_LEFT: startAngle = 180 outerCorner = QPointF(position.x() - halfthick, position.y() + halfthick - 2 * radius) innerCorner = QPointF(outerCorner.x() + (thickness), outerCorner.y()) center = QPointF(outerCorner.x() + radius, outerCorner.y() + radius) outerRect = QRectF(outerCorner, QSizeF(2 * radius, 2 * radius)) innerRect = QRectF(innerCorner, QSizeF((2 * radius - thickness), (2 * radius - thickness))) outerStart = QPointF(outerCorner.x(), outerCorner.y() + (radius - halfthick)) innerStart = QPointF(outerCorner.x() + (radius + halfthick), outerCorner.y() + (2 * radius)) elif cornerType == self.CornerType.BOTTOM_RIGHT: startAngle = 270 outerCorner = QPointF(position.x() + halfthick - 2 * radius, position.y() + halfthick - 2 * radius) innerCorner = QPointF(outerCorner.x(), outerCorner.y()) center = QPointF(outerCorner.x() + radius, outerCorner.y() + radius) outerRect = QRectF(outerCorner, QSizeF(2 * radius, 2 * radius)) innerRect = QRectF(innerCorner, QSizeF((2 * radius - thickness), (2 * radius - thickness))) outerStart = QPointF(outerCorner.x() + (radius - halfthick), outerCorner.y() + 2 * radius) innerStart = QPointF(outerCorner.x() + 2 * radius, outerCorner.y() + (radius - halfthick)) else: # No defined corner, so nothing to draw. #print "PointToPointConnection.drawCorner() - No valid corner, aborting..." return if painter.redirected(painter.device()): # e.q. QPixmap.grabWidget() painter.setBrush(self.FILL_COLOR1) else: brush = QRadialGradient(center, radius) if radius >= thickness: brush.setColorAt((radius - thickness) / radius, self.FILL_COLOR1) # inner border brush.setColorAt((radius - halfthick + 1) / radius, self.FILL_COLOR2) # center of line else: # If zoom is too small use single color brush.setColorAt(0, self.FILL_COLOR1) brush.setColorAt(1, self.FILL_COLOR1) # outer border painter.setBrush(brush) path = QPainterPath() path.moveTo(outerStart) path.arcTo(outerRect, startAngle, 90) path.lineTo(innerStart) path.arcTo(innerRect, startAngle + 90, - 90) path.closeSubpath() #painter.setPen(Qt.NoPen) painter.drawPath(path)
def drawCyclePath(self, v1, sepInput, v2, sepOutput, delta, cl): """ Tracé d'un arc reliant le noeud node1 à lui même dans le plan. Attention, puisque dans le plan de l'interface, l'axe des ordonnées est inveré, mais pas l'axe des abscisses la rotation dans le sens trigonométrique et le sens horaire sont inversés. """ # Il est conseillé de prendre une feuille est un stylo pour dessiner en lisant les commentaires # de cette méthode # m1 et o sont des points définis plus loin, mais on peut calculer et on a besoin de calculer leur distance # dès maintenant m1omag = float(cl ** 2 - NodeItem.NodeWidth ** 2) / (2 * cl) # demi angle entre les tangentes de l'arc à ses deux extrêmités (entre l'arrivée et le départ). gamma = atan(m1omag / NodeItem.NodeWidth) # delta est l'angle qui existe entre l'horizontale est la droite coupant orthogonalement l'arc en son milieu u = Vector2(1, 0).rotate(delta) # vecteur normé orienté du centre du noeud vers le milieu de l'arc # m1 est le point du cercle node1 au milieu du départ de l'arc v1m1norm = (u.rotate(-gamma)) # m2 est la pointe de la flêche de l'arc v2m2norm = (u.rotate(gamma)) v1m1 = NodeItem.NodeWidth * v1m1norm # Point sur le cercle node1 à une distance angulaire -alpha de m1 m1m = v1 + v1m1.rotate(-ArcItem.endingsAlpha) # Point sur le cercle node1 à une distance angulaire alpha de m1 m1p = v1 + v1m1.rotate(ArcItem.endingsAlpha) v2m2 = NodeItem.NodeWidth * v2m2norm m2 = v2 + v2m2 # Point m2 a2 = v2 + 2 * v2m2 # a2 est le milieu du segment central de la pointe de la flêche de l'arc # a2m est l'extrêmité droite de la pointe de la flêche a2m = v2 + v2m2 + (NodeItem.NodeWidth / cos(ArcItem.arrowBeta)) * v2m2norm.rotate(-ArcItem.arrowBeta) # a2p est l'extrêmité gauche de la pointe de la flêche a2p = v2 + v2m2 + (NodeItem.NodeWidth / cos(ArcItem.arrowBeta)) * v2m2norm.rotate(ArcItem.arrowBeta) # m2m est le point sur le cercle node2 à une distance angulaire -alpha de m2 v2m2m = v2m2.rotate(-ArcItem.endingsAlpha) # Vecteur v2 m2m # m2p est le point sur le cercle node2 à une distance angulaire alpha de m2 v2m2p = v2m2.rotate(ArcItem.endingsAlpha) # Vecteur v2 m2p # m2mp est le point sur le segment central de la pointe de la flêche qui appartient à la droite passant # par m2m parallèle au vecteur v2m2, définit ici à l'aide d'un projeté orthogonal m2mp = a2 + v2m2m.project(a2m - a2) # m2pp est le point sur le segment central de la pointe de la flêche qui appartient à la droite passant # par m2p parallèle au vecteur v2m2, définit ici à l'aide d'un projeté orthogonal m2pp = a2 + v2m2p.project(a2p - a2) # o est le centre du cercle passant par les 3 points suivants : m1, m2 et c # où c est le point situé à une distance self.cl de v1, en suivant le vecteur u, cad le milieu de l'arc o = v1 + v1m1 + m1omag * v1m1norm.rotate(pi / 2) c1 = 0.5 * (m2pp + m1m) # c1 est le milieu de m2pp et m1m # o1 est le centre du cercle passant par m1m et m2pp le plus proche de o o1 = c1 + (o - c1).project((m1m - m2pp).rotate(pi / 2)) r1 = (o1 - m1m).magnitude() # le rayon du cercle en question sinstang1 = Vector2(0, 1).dot(m1m - o1) # sinus de l'angle entre l'horizontale et (o1,m1m) stang1 = -Vector2(1, 0).angle(m1m - o1) # opposé de la valeur absolue de cet angle if sinstang1 < 0: stang1 *= -1 sinendang1 = Vector2(0, 1).dot(m2pp - o1) # sinus de l'angle entre l'horizontale et (o1,m2pp) endang1 = -Vector2(1, 0).angle(m2pp - o1) # opposé de la valeur absolue de cet angle if sinendang1 < 0: endang1 *= -1 pathang1 = endang1 - stang1 while pathang1 > 0: # correctifs pour afficher un bel arc de cercle dans le bon sens pathang1 -= 2 * pi while pathang1 < -2 * pi: pathang1 += 2 * pi c2 = 0.5 * (m2mp + m1p) # c2 est le milieu de m2mp et m1p # o2 est le centre du cercle passant par m1p et m2mp le plus proche de o o2 = c2 + (o - c2).project((m1p - m2mp).rotate(pi / 2)) r2 = (o2 - m1p).magnitude() # le rayon du cercle en question sinstang2 = Vector2(0, 1).dot(m2mp - o2) # sinus de l'angle entre l'horizontale et (o2,m2mp) stang2 = -Vector2(1, 0).angle(m2mp - o2) # opposé de la valeur absolue de cet angle if sinstang2 < 0: stang2 *= -1 sinendang2 = Vector2(0, 1).dot(m1p - o2) # sinus de l'angle entre l'horizontale et (o2,m1p) endang2 = -Vector2(1, 0).angle(m1p - o2) # opposé de la valeur absolue de cet angle if sinendang2 < 0: endang2 *= -1 pathang2 = endang2 - stang2 while pathang2 > 2 * pi: # correctifs pour afficher un bel arc de cercle dans le bon sens pathang2 -= 2 * pi while pathang2 < 0: pathang2 += 2 * pi # Tracé du chemin path = QPainterPath() path.moveTo(m1m.x, m1m.y) path.arcTo(o1.x - r1, o1.y - r1, 2 * r1, 2 * r1, degrees(stang1), degrees(pathang1)) path.lineTo(m2pp.x, m2pp.y) path.lineTo(m2mp.x, m2mp.y) path.arcTo(o2.x - r2, o2.y - r2, 2 * r2, 2 * r2, degrees(stang2), degrees(pathang2)) path.lineTo(m1p.x, m1p.y) path.closeSubpath() path.moveTo(m2.x, m2.y) path.lineTo(a2p.x, a2p.y) path.lineTo(a2m.x, a2m.y) path.closeSubpath() self.setPath(path) textCenter = o # v1 + cl * u labelItem = self.getLabelItem() labelItem.setArcVectorCenterAndOffset(u, textCenter, Vector2(0, 0))
def drawClassicPath(self, v1, sepInput, v2, sepOutput, cl): """ Trace d'un arc reliant les noeuds node1 et node2 dans le plan. Attention, puisque dans le plan de l'interface, l'axe des ordonnees est invere, mais pas l'axe des abscisses la rotation dans le sens trigonometrique et le sens horaire sont inverses. """ # Il est conseille de prendre une feuille est un stylo pour dessiner en lisant les commentaires # de cette methode u = v2 - v1 # Vecteur reliant v1 à v2 n = (u.rotate(pi / 2)).normalize() # Vecteur unitaire perpendiculaire à u # Point sur la mediatrice de [v1,v2] situe a une distance cl # Il servira (presque) de point de controle pour les courbes de Beziers traçant les deux bords de l'arc. c = v1 + u / 2 + cl * n v1m1norm = (c - v1).normalize() # Vecteur unitaire de la droite (v1,c), de v1 vers c v2m2norm = (c - v2).normalize() # Vecteur unitaire de la droite (v2,c), de v2 vers c # m1 est le point du cercle node1 situé sur la droite (v1,c) entre ces deux points. # c'est également le milieu du départ de l'arc sur node1 v1m1 = NodeItem.NodeWidth * v1m1norm # Vecteur v1m1 # Point sur le cercle node1 à une distance angulaire -alpha de m1 m1m = v1 + v1m1.rotate(-ArcItem.endingsAlpha) # Point sur le cercle node1 à une distance angulaire alpha de m1 m1p = v1 + v1m1.rotate(ArcItem.endingsAlpha) # m2 est le point du cercle node2 situé sur la droite (v2,c) entre ces deux points. # c'est également la pointe de la flêche de l'arc v2m2 = NodeItem.NodeWidth * v2m2norm # Vecteur v2m2 m2 = v2 + v2m2 # Point m2 a2 = v2 + 2 * v2m2 # a2 est le milieu du segment central de la pointe de la flêche de l'arc # a2m est l'extrêmité droite de la pointe de la flêche a2m = v2 + v2m2 + (NodeItem.NodeWidth / cos(ArcItem.arrowBeta)) * v2m2norm.rotate(-ArcItem.arrowBeta) # a2p est l'extrêmité gauche de la pointe de la flêche a2p = v2 + v2m2 + (NodeItem.NodeWidth / cos(ArcItem.arrowBeta)) * v2m2norm.rotate(ArcItem.arrowBeta) # m2m est le point sur le cercle node2 à une distance angulaire -alpha de m2 v2m2m = v2m2.rotate(-ArcItem.endingsAlpha) # Vecteur v2 m2m # m2p est le point sur le cercle node2 à une distance angulaire alpha de m2 v2m2p = v2m2.rotate(ArcItem.endingsAlpha) # Vecteur v2 m2p # m2mp est le point sur le segment central de la pointe de la flêche qui appartient à la droite passant # par m2m parallèle au vecteur v2m2, définit ici à l'aide d'un projeté orthogonal m2mp = a2 + v2m2m.project(a2m - a2) # m2pp est le point sur le segment central de la pointe de la flêche qui appartient à la droite passant # par m2p parallèle au vecteur v2m2, définit ici à l'aide d'un projeté orthogonal m2pp = a2 + v2m2p.project(a2p - a2) w = (m1p - m1m).magnitude() / 2 # eviron la demi largeur de l'arc c1 = c - w * n # point de contrôle de la courbe de bézier du bord gauche de l'arc c2 = c + w * n # point de contrôle de la courbe de bézier du bord droit de l'arc # Tracé de l'arc path = QPainterPath() if sepInput: path.moveTo(v1.x + NodeItem.NodeWidth, v1.y) path.arcTo(v1.x - NodeItem.NodeWidth, v1.y - NodeItem.NodeWidth, 2 * NodeItem.NodeWidth, 2 * NodeItem.NodeWidth, 0, 360) path.moveTo(m1m.x, m1m.y) path.quadTo(c1.x, c1.y, m2pp.x, m2pp.y) path.lineTo(m2mp.x, m2mp.y) path.quadTo(c2.x, c2.y, m1p.x, m1p.y) path.closeSubpath() path.moveTo(m2.x, m2.y) path.lineTo(a2p.x, a2p.y) path.lineTo(a2m.x, a2m.y) path.closeSubpath() if sepOutput: path.moveTo(v2.x + NodeItem.NodeWidth, v2.y) path.arcTo(v2.x - NodeItem.NodeWidth, v2.y - NodeItem.NodeWidth, 2 * NodeItem.NodeWidth, 2 * NodeItem.NodeWidth, 0, 360) self.setPath(path) textCenter = v1 + u / 2 # position normale labelItem = self.getLabelItem() offset = 0.5 * cl * n labelItem.setArcVectorCenterAndOffset(u, textCenter, offset)
def calculateDatasets(self, scene, axes, datasets): """ Builds the datasets for this renderer. Each renderer will need to subclass and implemenent this method, otherwise, no data will be shown in the chart. :param scene | <XChartScene> axes | [< datasets | [<XChartDataset>, ..] """ items = self.calculateDatasetItems(scene, datasets) if not items: scene.clear() return xaxis = scene.chart().horizontalAxis() yaxis = scene.chart().verticalAxis() data_axis = None all_values = [] # determine if we're mapping data aginst the sets themselves, in # which case, create a pie chart of the dataset vs. its if isinstance(xaxis, XDatasetAxis): per_dataset = False data_axis = yaxis total = 1 elif isinstance(yaxis, XDatasetAxis): per_dataset = False total = 1 data_axis = xaxis else: per_dataset = True total = len(items) if not per_dataset: all_values = [dataset.sum(data_axis) \ for dataset in datasets] # generate the build information rect = self.buildData('grid_rect') rect.setX(rect.x() + 10) rect.setY(rect.y() + 10) rect.setWidth(rect.width() - 20) rect.setHeight(rect.height() - 20) if rect.width() > rect.height(): radius = min(rect.width() / 2.0, rect.height() / 2.0) x = rect.left() y = rect.top() + radius deltax = min(radius * 2, rect.width() / float(total + 1)) deltay = 0 else: radius = min(rect.height() / 2.0, rect.width() / 2.0) x = rect.left() + radius y = rect.top() deltax = 0 deltay = min(radius * 2, rect.height() / float(total + 1)) # initialize the first pie chart angle = 0 cx = x + deltax cy = y + deltay x += deltax y += deltay for dataset in datasets: item = items.get(dataset) if not item: continue item.setBuildData('center', QPointF(cx, cy)) item.setBuildData('radius', radius) subpaths = [] bound = QRectF(cx-radius, cy-radius, radius * 2, radius * 2) path = QPainterPath() if per_dataset: data_values = dataset.values(yaxis.name()) andle = 0 for value in dataset.values(): perc = yaxis.percentOfTotal(value.get(yaxis.name()), data_values) # calculate the angle as the perc item_angle = perc * 360 subpath = QPainterPath() subpath.moveTo(cx, cy) subpath.arcTo(bound, angle, item_angle) subpath.lineTo(cx, cy) path.addPath(subpath) subpaths.append((value.get(xaxis.name()), subpath)) angle += item_angle cx = x + deltax cy = y + deltay x += deltax y += deltay else: value = dataset.sum(data_axis) perc = data_axis.percentOfTotal(value, all_values) # calculate the angle as the perc item_angle = perc * 360 subpath = QPainterPath() subpath.moveTo(cx, cy) subpath.arcTo(bound, angle, item_angle) subpath.lineTo(cx, cy) path.addPath(subpath) subpaths.append((value, subpath)) angle += item_angle item.setPath(path) item.setBuildData('subpaths', subpaths)
def rebuild(self): """ Rebuilds the item based on the current points. """ scene = self.scene() if not scene: return self._subpaths = [] grid = scene.gridRect() typ = self.chartType() hruler = scene.horizontalRuler() vruler = scene.verticalRuler() path = QPainterPath() area = QPainterPath() self._buildData.clear() self._buildData['path_area'] = area self.setPos(0, 0) # draw a line item if typ == XChartScene.Type.Line: first = True pos = None home = None self._ellipses = [] points = self.points() if (self.orientation() == Qt.Horizontal): points.sort(hruler.compareValues, key=lambda x: x[0]) else: points.sort(vruler.compareValues, key=lambda y: y[1]) points.reverse() for x, y in self.points(): pos = scene.mapFromChart(x, y) if first: home = QPointF(pos.x(), grid.bottom()) area.moveTo(home) area.lineTo(pos) path.moveTo(pos) self._ellipses.append(pos) first = False else: path.lineTo(pos) area.lineTo(pos) self._ellipses.append(pos) if pos and home: area.lineTo(pos.x(), grid.bottom()) area.lineTo(home) # draw a bar item elif typ == XChartScene.Type.Bar: barsize = self.barSize() horiz = self.orientation() == Qt.Horizontal for x, y in self.points(): pos = scene.mapFromChart(x, y) subpath = QPainterPath() if horiz: r = min(grid.bottom() - pos.y(), 8) subpath.moveTo(pos.x() - barsize / 2.0, grid.bottom()) subpath.lineTo(pos.x() - barsize / 2.0, pos.y() + r) subpath.quadTo(pos.x() - barsize / 2.0, pos.y(), pos.x() - barsize / 2.0 + r, pos.y()) subpath.lineTo(pos.x() + barsize / 2.0 - r, pos.y()) subpath.quadTo(pos.x() + barsize / 2.0, pos.y(), pos.x() + barsize / 2.0, pos.y() + r) subpath.lineTo(pos.x() + barsize / 2.0, grid.bottom()) subpath.lineTo(pos.x() - barsize / 2.0, grid.bottom()) else: subpath.moveTo(grid.left(), pos.y() - barsize / 2.0) subpath.lineTo(pos.x(), pos.y() - barsize / 2.0) subpath.lineTo(pos.x(), pos.y() + barsize / 2.0) subpath.lineTo(grid.left(), pos.y() + barsize / 2.0) subpath.lineTo(grid.left(), pos.y() - barsize / 2.0) path.addPath(subpath) self._subpaths.append((x, y, subpath)) # draw a pie chart elif typ == XChartScene.Type.Pie: if self.orientation() == Qt.Horizontal: key_index = 0 value_index = 1 value_ruler = self.verticalRuler() else: key_index = 1 value_index = 0 value_ruler = self.horizontalRuler() pie_values = {} for point in self.points(): key = point[key_index] value = point[value_index] pie_values.setdefault(key, []) pie_values[key].append(value) for key, values in pie_values.items(): pie_values[key] = value_ruler.calcTotal(values) total = max(1, value_ruler.calcTotal(pie_values.values())) # calculate drawing parameters center = self.pieCenter() radius = self.radius() diameter = radius * 2 angle = 0 bound = QRectF(-radius, -radius, diameter, diameter) for key, value in sorted(pie_values.items(), key=lambda x: x[1]): # calculate the percentage perc = float(value) / total # calculate the angle as the perc * 360 item_angle = perc * 360 self.setPos(center) sub_path = QPainterPath() sub_path.arcTo(bound, angle, item_angle) sub_path.lineTo(0, 0) path.addPath(sub_path) self._subpaths.append((key, value, sub_path)) angle += item_angle self.setPath(path) self._dirty = False
def _updatePP(self): """Create the neck geometry updating this item's QPainterPath. Return None. """ self.prepareGeometryChange() pp = QPainterPath() stringSpan = (self.nStrings - 1) * self.stringSpacing nutWidth = stringSpan + self.stringEdgeOffset * 2 # frets scaleLen = 25.5 offset = 0.0 # previous fret x coordinate self.fretXs = [0.0] for n in range(self.nFrets): pos = offset + (scaleLen - offset) / 17.817 self.fretXs.append(pos) pp.moveTo(pos, nutWidth) pp.lineTo(pos, 0.0) offset = pos # marker dots y = nutWidth / 2.0 for n in [3, 5, 7, 9, 12, 15, 17, 19, 21, 24]: if n > self.nFrets: break fretX1 = self.fretXs[n-1] fretX2 = self.fretXs[n] x = fretX1 + (fretX2 - fretX1) / 2.0 d = self.markerDia r = d / 2.0 dy = nutWidth / 4.0 if n % 12 == 0: pp.addEllipse(x-r, y-r-dy, d, d) pp.addEllipse(x-r, y-r+dy, d, d) else: pp.addEllipse(x-r, y-r, d, d) # strings self.stringYs = [] endX = self.fretXs[-1] for n in range(self.nStrings): y = self.stringEdgeOffset + self.stringSpacing * n self.stringYs.append(y) pp.moveTo(-self.nutThickness - self.fretXs[1] / 2.0, y) pp.lineTo(endX, y) # outline pp.addRect(0, 0, self.fretXs[-1], nutWidth) # nut pp.addRect(-self.nutThickness, 0, self.nutThickness, nutWidth) # partial headstock, to allow room for open note display # upper curve r = 2.0 d = self.fretXs[1] / 2.0 rectL = -self.nutThickness - r rect = QRectF(rectL, -r*2.0, r*2.0, r*2.0) ra = asin(d / r) da = degrees(ra) pp.arcMoveTo(rect, 270.0) pp.arcTo(rect, 270.0, -da) # lower curve rect = QRectF(rectL, nutWidth, r*2.0, r*2.0) pp.arcMoveTo(rect, 90.0) pp.arcTo(rect, 90.0, da) # x coordinate of open string note markers self.openX = (-self.nutThickness - sin(ra) * r) / 2.0 self.setPath(pp)
def paintEvent(self, pe): p = QPainter(self) p.setRenderHint(QPainter.Antialiasing) pen = QPen() pen.setWidth(1) pen.setColor(QColor(0x8C, 0xA3, 0xB0)) p.setPen(pen) p.setBrush(QColor(0xE4, 0xEC, 0xF4)) rx = 6 space = self.space w = self.usable_width kw = self.key_w def drawRow(row, sx, sy, last_end=False): x = sx y = sy keys = row rw = w - sx i = 0 for k in keys: rect = QRectF(x, y, kw, kw) if i == len(keys) - 1 and last_end: rect.setWidth(rw) p.drawRoundedRect(rect, rx, rx) p.setPen(Qt.black) rect.adjust(5, 1, 0, 0) p.setFont(self.lowerFont) p.drawText(rect, Qt.AlignLeft | Qt.AlignBottom, self.regular_text(k)) p.setFont(self.upperFont) p.drawText(rect, Qt.AlignLeft | Qt.AlignTop, self.shift_text(k)) rw = rw - space - kw x = x + space + kw i = i + 1 p.setPen(pen) return (x, rw) x = 0.5 y = 0.5 keys = self.kb["keys"] ext_return = self.kb["extended_return"] first_key_w = 0 rows = 4 remaining_x = [0, 0, 0, 0] remaining_widths = [0, 0, 0, 0] for i in range(0, rows): if first_key_w > 0: first_key_w = first_key_w * 1.375 if self.kb == self.kb_105 and i == 3: first_key_w = kw * 1.275 rect = QRectF(x, y, first_key_w, kw) p.drawRoundedRect(rect, rx, rx) x = x + first_key_w + space else: first_key_w = kw x, rw = drawRow(keys[i], x, y, i == 1 and not ext_return) remaining_x[i] = x remaining_widths[i] = rw if i != 1 and i != 2: rect = QRectF(x, y, rw, kw) p.drawRoundedRect(rect, rx, rx) x = 0.5 y = y + space + kw if ext_return: rx = rx * 2 x1 = remaining_x[1] y1 = 0.5 + kw * 1 + space * 1 w1 = remaining_widths[1] x2 = remaining_x[2] y2 = 0.5 + kw * 2 + space * 2 w2 = remaining_widths[2] # this is some serious crap... but it has to be so # maybe one day keyboards won't look like this... # one can only hope pp = QPainterPath() pp.moveTo(x1, y1 + rx) pp.arcTo(x1, y1, rx, rx, 180, -90) pp.lineTo(x1 + w1 - rx, y1) pp.arcTo(x1 + w1 - rx, y1, rx, rx, 90, -90) pp.lineTo(x1 + w1, y2 + kw - rx) pp.arcTo(x1 + w1 - rx, y2 + kw - rx, rx, rx, 0, -90) pp.lineTo(x2 + rx, y2 + kw) pp.arcTo(x2, y2 + kw - rx, rx, rx, -90, -90) pp.lineTo(x2, y1 + kw) pp.lineTo(x1 + rx, y1 + kw) pp.arcTo(x1, y1 + kw - rx, rx, rx, -90, -90) pp.closeSubpath() p.drawPath(pp) else: x = remaining_x[2] y = 0.5 + kw * 2 + space * 2 rect = QRectF(x, y, remaining_widths[2], kw) p.drawRoundedRect(rect, rx, rx) QWidget.paintEvent(self, pe)
def drawCorner(self, painter, position, cornerType, maxRadius=None): #logging.debug(self.__class__.__name__ +": drawCorner() "+ self.cornerTypeString(cornerType)) thickness = self.CONNECTION_THICKNESS * self.zoomFactor() halfthick = thickness / 2 cornerRoundness = halfthick**0.5 cornerOffset = halfthick * (cornerRoundness) innerCorner = halfthick * (cornerRoundness - 1) outerCorner = halfthick * (cornerRoundness + 1) innerWidth = halfthick * (cornerRoundness - 1) radius = halfthick * (cornerRoundness + 1) if maxRadius: maxRadius = max(maxRadius, thickness) radius = min(radius, maxRadius) if cornerType == self.CornerType.TOP_RIGHT: startAngle = 0 outerCorner = QPointF(position.x() + halfthick - 2 * radius, position.y() - halfthick) innerCorner = QPointF(outerCorner.x(), outerCorner.y() + (thickness)) center = QPointF(outerCorner.x() + radius, outerCorner.y() + radius) outerRect = QRectF(outerCorner, QSizeF(2 * radius, 2 * radius)) innerRect = QRectF( innerCorner, QSizeF((2 * radius - thickness), (2 * radius - thickness))) outerStart = QPointF(outerCorner.x() + 2 * radius, outerCorner.y() + (radius + halfthick)) innerStart = QPointF(outerCorner.x() + (radius - halfthick), outerCorner.y()) elif cornerType == self.CornerType.TOP_LEFT: startAngle = 90 outerCorner = QPointF(position.x() - halfthick, position.y() - halfthick) innerCorner = QPointF(outerCorner.x() + (thickness), outerCorner.y() + (thickness)) center = QPointF(outerCorner.x() + radius, outerCorner.y() + radius) outerRect = QRectF(outerCorner, QSizeF(2 * radius, 2 * radius)) innerRect = QRectF( innerCorner, QSizeF((2 * radius - thickness), (2 * radius - thickness))) outerStart = QPointF(outerCorner.x() + (radius + halfthick), outerCorner.y()) innerStart = QPointF(outerCorner.x(), outerCorner.y() + (radius + halfthick)) elif cornerType == self.CornerType.BOTTOM_LEFT: startAngle = 180 outerCorner = QPointF(position.x() - halfthick, position.y() + halfthick - 2 * radius) innerCorner = QPointF(outerCorner.x() + (thickness), outerCorner.y()) center = QPointF(outerCorner.x() + radius, outerCorner.y() + radius) outerRect = QRectF(outerCorner, QSizeF(2 * radius, 2 * radius)) innerRect = QRectF( innerCorner, QSizeF((2 * radius - thickness), (2 * radius - thickness))) outerStart = QPointF(outerCorner.x(), outerCorner.y() + (radius - halfthick)) innerStart = QPointF(outerCorner.x() + (radius + halfthick), outerCorner.y() + (2 * radius)) elif cornerType == self.CornerType.BOTTOM_RIGHT: startAngle = 270 outerCorner = QPointF(position.x() + halfthick - 2 * radius, position.y() + halfthick - 2 * radius) innerCorner = QPointF(outerCorner.x(), outerCorner.y()) center = QPointF(outerCorner.x() + radius, outerCorner.y() + radius) outerRect = QRectF(outerCorner, QSizeF(2 * radius, 2 * radius)) innerRect = QRectF( innerCorner, QSizeF((2 * radius - thickness), (2 * radius - thickness))) outerStart = QPointF(outerCorner.x() + (radius - halfthick), outerCorner.y() + 2 * radius) innerStart = QPointF(outerCorner.x() + 2 * radius, outerCorner.y() + (radius - halfthick)) else: # No defined corner, so nothing to draw. #print "PointToPointConnection.drawCorner() - No valid corner, aborting..." return if painter.redirected(painter.device()): # e.q. QPixmap.grabWidget() painter.setBrush(self.FILL_COLOR1) else: brush = QRadialGradient(center, radius) if radius >= thickness: brush.setColorAt((radius - thickness) / radius, self.FILL_COLOR1) # inner border brush.setColorAt((radius - halfthick + 1) / radius, self.FILL_COLOR2) # center of line else: # If zoom is too small use single color brush.setColorAt(0, self.FILL_COLOR1) brush.setColorAt(1, self.FILL_COLOR1) # outer border painter.setBrush(brush) path = QPainterPath() path.moveTo(outerStart) path.arcTo(outerRect, startAngle, 90) path.lineTo(innerStart) path.arcTo(innerRect, startAngle + 90, -90) path.closeSubpath() #painter.setPen(Qt.NoPen) painter.drawPath(path)
def paintEvent(self, pe): p = QPainter(self) p.setRenderHint(QPainter.Antialiasing) # p.setBrush(QColor(0xf0, 0xf0, 0xf0)) # color of the border # p.drawRect(-1, -1, 800, 800) pen = QPen() pen.setWidth(1) pen.setColor(QColor(0x58, 0x58, 0x58)) # color of the borders of the keys p.setPen(pen) p.setBrush(QColor(0x58, 0x58, 0x58)) # color of the keys p.setBackgroundMode(Qt.TransparentMode) rx = 3 space = self.space w = self.usable_width kw = self.key_w def drawRow(row, sx, sy, last_end=False): x = sx y = sy keys = row rw = w - sx i = 0 for k in keys: rect = QRectF(x, y, kw, kw) if i == len(keys) - 1 and last_end: rect.setWidth(rw) p.drawRoundedRect(rect, rx, rx) rect.adjust(5, 1, 0, 0) p.setPen(QColor(0xff, 0xff, 0xff)) p.setFont(self.lowerFont) p.drawText(rect, Qt.AlignLeft | Qt.AlignBottom, self.regular_text(k)) p.setPen(QColor(0x9e, 0xde, 0x00)) p.setFont(self.upperFont) p.drawText(rect, Qt.AlignLeft | Qt.AlignTop, self.shift_text(k)) rw = rw - space - kw x = x + space + kw i = i + 1 p.setPen(pen) return (x, rw) x = 6 y = 6 keys = self.kb["keys"] ext_return = self.kb["extended_return"] first_key_w = 0 rows = 4 remaining_x = [0, 0, 0, 0] remaining_widths = [0, 0, 0, 0] for i in range(0, rows): if first_key_w > 0: first_key_w = first_key_w * 1.375 if self.kb == self.kb_105 and i == 3: first_key_w = kw * 1.275 rect = QRectF(6, y, first_key_w, kw) p.drawRoundedRect(rect, rx, rx) x = 6 + first_key_w + space else: first_key_w = kw x, rw = drawRow(keys[i], x, y, i == 1 and not ext_return) remaining_x[i] = x remaining_widths[i] = rw if i != 1 and i != 2: rect = QRectF(x, y, rw, kw) p.drawRoundedRect(rect, rx, rx) x = .5 y = y + space + kw if ext_return: rx = rx * 2 x1 = remaining_x[1] y1 = 6 + kw * 1 + space * 1 w1 = remaining_widths[1] x2 = remaining_x[2] y2 = 6 + kw * 2 + space * 2 # this is some serious crap... but it has to be so # maybe one day keyboards won't look like this... # one can only hope pp = QPainterPath() pp.moveTo(x1, y1 + rx) pp.arcTo(x1, y1, rx, rx, 180, -90) pp.lineTo(x1 + w1 - rx, y1) pp.arcTo(x1 + w1 - rx, y1, rx, rx, 90, -90) pp.lineTo(x1 + w1, y2 + kw - rx) pp.arcTo(x1 + w1 - rx, y2 + kw - rx, rx, rx, 0, -90) pp.lineTo(x2 + rx, y2 + kw) pp.arcTo(x2, y2 + kw - rx, rx, rx, -90, -90) pp.lineTo(x2, y1 + kw) pp.lineTo(x1 + rx, y1 + kw) pp.arcTo(x1, y1 + kw - rx, rx, rx, -90, -90) pp.closeSubpath() p.drawPath(pp) else: x = remaining_x[2] y = .5 + kw * 2 + space * 2 rect = QRectF(x, y, remaining_widths[2], kw) p.drawRoundedRect(rect, rx, rx) QWidget.paintEvent(self, pe)
def calculateDatasets(self, scene, axes, datasets): """ Builds the datasets for this renderer. Each renderer will need to subclass and implemenent this method, otherwise, no data will be shown in the chart. :param scene | <XChartScene> axes | [< datasets | [<XChartDataset>, ..] """ items = self.calculateDatasetItems(scene, datasets) if not items: scene.clear() return xaxis = scene.chart().horizontalAxis() yaxis = scene.chart().verticalAxis() data_axis = None all_values = [] # determine if we're mapping data aginst the sets themselves, in # which case, create a pie chart of the dataset vs. its if isinstance(xaxis, XDatasetAxis): per_dataset = False data_axis = yaxis total = 1 elif isinstance(yaxis, XDatasetAxis): per_dataset = False total = 1 data_axis = xaxis else: per_dataset = True total = len(items) if not per_dataset: all_values = [dataset.sum(data_axis) \ for dataset in datasets] # generate the build information rect = self.buildData('grid_rect') rect.setX(rect.x() + 10) rect.setY(rect.y() + 10) rect.setWidth(rect.width() - 20) rect.setHeight(rect.height() - 20) if rect.width() > rect.height(): radius = min(rect.width() / 2.0, rect.height() / 2.0) x = rect.left() y = rect.top() + radius deltax = min(radius * 2, rect.width() / float(total + 1)) deltay = 0 else: radius = min(rect.height() / 2.0, rect.width() / 2.0) x = rect.left() + radius y = rect.top() deltax = 0 deltay = min(radius * 2, rect.height() / float(total + 1)) # initialize the first pie chart angle = 0 cx = x + deltax cy = y + deltay x += deltax y += deltay for dataset in datasets: item = items.get(dataset) if not item: continue item.setBuildData('center', QPointF(cx, cy)) item.setBuildData('radius', radius) subpaths = [] bound = QRectF(cx - radius, cy - radius, radius * 2, radius * 2) path = QPainterPath() if per_dataset: data_values = dataset.values(yaxis.name()) andle = 0 for value in dataset.values(): perc = yaxis.percentOfTotal(value.get(yaxis.name()), data_values) # calculate the angle as the perc item_angle = perc * 360 subpath = QPainterPath() subpath.moveTo(cx, cy) subpath.arcTo(bound, angle, item_angle) subpath.lineTo(cx, cy) path.addPath(subpath) subpaths.append((value.get(xaxis.name()), subpath)) angle += item_angle cx = x + deltax cy = y + deltay x += deltax y += deltay else: value = dataset.sum(data_axis) perc = data_axis.percentOfTotal(value, all_values) # calculate the angle as the perc item_angle = perc * 360 subpath = QPainterPath() subpath.moveTo(cx, cy) subpath.arcTo(bound, angle, item_angle) subpath.lineTo(cx, cy) path.addPath(subpath) subpaths.append((value, subpath)) angle += item_angle item.setPath(path) item.setBuildData('subpaths', subpaths)