def paintEvent(self, event): # Draw backgrounds according to css styleOpt = QStyleOption() styleOpt.initFrom(self) p = QPainter(self) p.setRenderHint(QPainter.Antialiasing) self.style().drawPrimitive(QStyle.PE_Widget, styleOpt, p, self) if self.values == None or len(self.values) == 0: return # print(len(self.values)) r = self.rect() dx = r.width() / float(self.datapoints - 1) # Build a path from the readings path = QPainterPath() path.moveTo(r.bottomRight()) i = 0 for reading in reversed(self.values): pt = QPointF(r.width() - i*dx, (1.0 - reading) * r.height()) path.lineTo(pt) i = i + 1 path.lineTo(path.currentPosition().x(), r.height()) path.closeSubpath() # Use foreground color for graph gcolor = styleOpt.palette.color(QPalette.Text) p.setBrush(gcolor) p.setPen(gcolor) p.drawPath(path)
def paintEvent(self, event): rect = QRect(10, 20, 80, 60) path = QPainterPath() path.moveTo(20, 80) path.lineTo(20, 30) path.cubicTo(80, 0, 50, 50, 80, 80) startAngle = 30 * 16 arcLength = 120 * 16 painter = QPainter(self) painter.setPen(self.pen) painter.setBrush(self.brush) if self.antialiased: painter.setRenderHint(QPainter.Antialiasing) for x in range(0, self.width(), 100): for y in range(0, self.height(), 100): painter.save() painter.translate(x, y) if self.transformed: painter.translate(50, 50) painter.rotate(60.0) painter.scale(0.6, 0.9) painter.translate(-50, -50) if self.shape == RenderArea.Line: painter.drawLine(rect.bottomLeft(), rect.topRight()) elif self.shape == RenderArea.Points: painter.drawPoints(RenderArea.points) elif self.shape == RenderArea.Polyline: painter.drawPolyline(RenderArea.points) elif self.shape == RenderArea.Polygon: painter.drawPolygon(RenderArea.points) elif self.shape == RenderArea.Rect: painter.drawRect(rect) elif self.shape == RenderArea.RoundedRect: painter.drawRoundedRect(rect, 25, 25, Qt.RelativeSize) elif self.shape == RenderArea.Ellipse: painter.drawEllipse(rect) elif self.shape == RenderArea.Arc: painter.drawArc(rect, startAngle, arcLength) elif self.shape == RenderArea.Chord: painter.drawChord(rect, startAngle, arcLength) elif self.shape == RenderArea.Pie: painter.drawPie(rect, startAngle, arcLength) elif self.shape == RenderArea.Path: painter.drawPath(path) elif self.shape == RenderArea.Text: painter.drawText(rect, Qt.AlignCenter, "PyQt by\nRiverbank Computing") elif self.shape == RenderArea.Pixmap: painter.drawPixmap(10, 10, self.pixmap) painter.restore() painter.setPen(self.palette().dark().color()) painter.setBrush(Qt.NoBrush) painter.drawRect(QRect(0, 0, self.width() - 1, self.height() - 1))
def _get_pos_widget(name, backgroundColor, foregroundColor): label = QLabel() label.setAttribute(Qt.WA_TransparentForMouseEvents, True) pixmap = QPixmap(25 * 10, 25 * 10) pixmap.fill(backgroundColor) painter = QPainter() painter.begin(pixmap) pen = QPen(foregroundColor) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) font = QFont() font.setBold(True) font.setPixelSize(25 * 10 - 30) path = QPainterPath() path.addText(QPointF(50, 25 * 10 - 50), font, name) brush = QBrush(foregroundColor) painter.setBrush(brush) painter.drawPath(path) painter.setFont(font) painter.end() pixmap = pixmap.scaled(QSize(20, 20), Qt.KeepAspectRatio, Qt.SmoothTransformation) label.setPixmap(pixmap) spinbox = DelayedSpinBox(750) spinbox.setAlignment(Qt.AlignCenter) spinbox.setToolTip("{0} Spin Box".format(name)) spinbox.setButtonSymbols(QAbstractSpinBox.NoButtons) spinbox.setMaximumHeight(20) font = spinbox.font() font.setPixelSize(14) spinbox.setFont(font) sheet = TEMPLATE.format(foregroundColor.name(), backgroundColor.name()) spinbox.setStyleSheet(sheet) return label, spinbox
def paintEvent(self, event): # Check whether this orb is enhanced if type(self.parent) == Board: enh = self.parent.enhanced[self.position] else: enh = False painter = QPainter(self) painter.drawPixmap(event.rect().adjusted(2,2,-2,-2), self.pixmap()) w = event.rect().width() if enh: path = QPainterPath() pen = QPen() pen.setWidth(1); pen.setBrush(Qt.white) brush = QBrush(Qt.yellow) font = QFont() font.setPointSize(20) font.setWeight(QFont.Black) path.addText(event.rect().x()+w-15,event.rect().y()+w-5,font,'+') painter.setPen(pen) painter.setBrush(brush) painter.setFont(font) painter.drawPath(path)
class ModCrossButton(QPushButton): def __init__(self,parent,path=None): QPushButton.__init__(self,parent) self.parent=parent #self.setAttribute(Qt.WA_TranslucentBackground, True) self.backgroundColor = QPalette().light().color() self.backgroundColor.setRgb(157,157,157) #(220,203,231) #self.backgroundColor.setAlpha(100) self.brush=QBrush(Qt.SolidPattern) def paintEvent(self,event): self.wide=self.width() self.high=self.height() self.xdis=self.wide/7 self.ydis=self.xdis self.path=QPainterPath() self.path.setFillRule(Qt.OddEvenFill) self.path.moveTo(self.wide/2, self.high/2-self.xdis) self.path.arcTo(0,0, self.wide, self.high,0,360) #self.path.closeSubpath() self.path.moveTo(self.wide/2-self.xdis/2, self.ydis) self.path.lineTo(self.wide/2-self.xdis/2, self.high/2-self.xdis/2) self.path.lineTo(self.ydis, self.high/2-self.xdis/2) self.path.lineTo(self.ydis, self.high/2+self.xdis/2) self.path.lineTo(self.wide/2-self.xdis/2, self.high/2+self.xdis/2) self.path.lineTo(self.wide/2-self.xdis/2, self.high-self.ydis) self.path.lineTo(self.wide/2+self.xdis/2, self.high-self.ydis) self.path.lineTo(self.wide/2+self.xdis/2, self.high/2+self.xdis/2) self.path.lineTo(self.wide-self.ydis, self.high/2+self.xdis/2) self.path.lineTo(self.wide-self.ydis, self.high/2-self.xdis/2) self.path.lineTo(self.wide/2+self.xdis/2, self.high/2-self.xdis/2) self.path.lineTo(self.wide/2+self.xdis/2, self.ydis) self.path.closeSubpath() self.brush.setColor(self.backgroundColor) self.painter=QPainter(self) self.painter.setRenderHint(QPainter.Antialiasing) self.painter.setPen(Qt.NoPen) self.painter.setBrush(self.brush) self.painter.drawPath(self.path) self.painter.end() #def mousePressEvent(self,ev): # self.parent.close() def enterEvent(self,ev): self.backgroundColor.setRgb(242,146,52) self.update() def leaveEvent(self,ev): self.backgroundColor.setRgb(157,157,157) self.update()
def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) for shapeItem in self.shapeItems: painter.translate(shapeItem.position()) painter.setBrush(shapeItem.color()) painter.drawPath(shapeItem.path()) painter.translate(-shapeItem.position())
class ModCloseButton(QPushButton): def __init__(self,parent,wide,high,ppath=None): QPushButton.__init__(self,parent) self.parent=parent self.wide=wide self.high=high self.resize(self.wide,self.high) self.xdis=self.wide/10 #self.setAttribute(Qt.WA_TranslucentBackground, True) self.backgroundColor = QPalette().light().color() self.backgroundColor.setRgb(157,157,157) #(220,203,231) #self.backgroundColor.setAlpha(100) self.brush=QBrush(Qt.SolidPattern) if ppath : self.path=ppath else : self.path=QPainterPath() self.path.moveTo(self.wide/2, self.high/2-self.xdis) self.path.arcTo(0,0, self.wide-2*self.xdis, self.high-2*self.xdis,45,90) self.path.closeSubpath() self.path.moveTo(self.wide/2-self.xdis, self.high/2) self.path.arcTo(0,0,self.wide-2*self.xdis,self.high-2*self.xdis,135,90) self.path.closeSubpath() self.path.moveTo(self.wide/2, self.high/2+self.xdis) self.path.arcTo(0,0,self.wide-2*self.xdis, self.high-2*self.xdis,225,90) self.path.closeSubpath() self.path.moveTo(self.wide/2+self.xdis, self.high/2) self.path.arcTo(0,0,self.wide-2*self.xdis, self.high-2*self.xdis,315,90) self.path.closeSubpath() def paintEvent(self,event): self.brush.setColor(self.backgroundColor) self.painter=QPainter(self) self.painter.setRenderHint(QPainter.Antialiasing) self.painter.setPen(Qt.NoPen) self.painter.setBrush(self.brush) self.painter.drawPath(self.path) self.painter.end() def mousePressEvent(self,ev): self.parent.close() def enterEvent(self,ev): self.backgroundColor.setRgb(234,39,13) self.update() def leaveEvent(self,ev): self.backgroundColor.setRgb(157,157,157) self.update()
def paintEvent(self, event): painter = QPainter() painter.begin(self) painter.setRenderHint(QPainter.Antialiasing) painter.setBrush(QBrush(QColor(192, 192, 255))) painter.drawRect(event.rect()) painter.translate(self.width()/2.0, self.height()/2.0) painter.rotate(self._angle) painter.setBrush(QBrush(self.gradient)) painter.drawPath(self.path) painter.end()
def paintEvent(self, event): painter = QPainter(self) painter.setBrush(self.brush) if self.opacity is None or self.parent().vanishing: painter.setOpacity(self.parent().opacity / 255) else: painter.setOpacity(self.opacity / 255) pen = QPen(QColor(100, 100, 100, 150)) pen.setWidth(10) painter.setPen(pen) painter.drawPath(self.path) painter.setPen(QColor(0, 0, 0)) painter.drawText(self.path.controlPointRect(), Qt.AlignCenter, self.name)
def _drawTextShadow(self, painter: QPainter, x: int, y: int, text: str): font = self.font() # setup outline path text_path = QPainterPath() text_path.addText(x, y, font, text) # draw outline path 1 painter.setPen(self.outline_pen) painter.setBrush(self.outline_brush) painter.drawPath(text_path) # draw text painter.setPen(self.text_color) painter.setFont(font) # Note: The y-position is used as the baseline of the font. painter.drawText(x, y, text)
def _drawOutlinedText(painter: QPainter, x: int, y: int, text: str, font: QFont, textColor: QColor, outlineColor: QColor, outlineWidth: int=1): # setup outline path text_path = QPainterPath() text_path.addText(x, y, font, text) # draw outline path outlineBrush = QBrush(outlineColor, Qt.SolidPattern) outlinePen = QPen(outlineBrush, outlineWidth, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) painter.setPen(outlinePen) painter.setBrush(outlineBrush) painter.drawPath(text_path) # draw text painter.setPen(textColor) painter.setFont(font) painter.drawText(x, y, text)
def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.scale(self.width() / 100.0, self.height() / 100.0) painter.translate(50.0, 50.0) painter.rotate(-self.rotationAngle) painter.translate(-50.0, -50.0) painter.setPen( QPen(self.penColor, self.penWidth, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) gradient = QLinearGradient(0, 0, 0, 100) gradient.setColorAt(0.0, self.fillColor1) gradient.setColorAt(1.0, self.fillColor2) painter.setBrush(QBrush(gradient)) painter.drawPath(self.path)
def createAxisLabelPixmap(self): pixmap = QPixmap(250, 250) pixmap.fill(self.backgroundColor) painter = QPainter() painter.begin(pixmap) font = QFont() font.setBold(True) font.setPixelSize(250 - 30) path = QPainterPath() path.addText(QPointF(50, 250 - 50), font, self.axis) brush = QBrush(self.foregroundColor) painter.setBrush(brush) painter.drawPath(path) painter.setFont(font) painter.end() pixmap = pixmap.scaled(QSize(self.labelsWidth, self.labelsheight), Qt.KeepAspectRatio, Qt.SmoothTransformation) return pixmap
def __init__(self, width, height, color, border=None): """ Initialize the icon. :type width: T <= int | float :type height: T <= int | float :type color: str :type border: str """ pixmap = QPixmap(width, height) painter = QPainter(pixmap) painter.setRenderHint(QPainter.Antialiasing) path = QPainterPath() path.addRect(QRectF(QPointF(0, 0), QPointF(width, height))) painter.fillPath(path, QBrush(QColor(color))) if border: painter.setPen(QPen(QColor(border), 0, Qt.SolidLine)) painter.drawPath(path) painter.end() super().__init__(pixmap)
def paint(self, painter: QPainter): painter_path = QPainterPath() painter_path.moveTo(self._startX, self._startY) x1 = (7 * self._endX + self._startX) / 8 y1 = self._startY x2 = (self._endX + 7 * self._startX) / 8 y2 = self._endY painter_path.cubicTo(x1, y1, x2, y2, self._endX, self._endY) if self._startArrow: self.add_arrow(self._startX, self._startY, 'l' if self._startX <= self._endX else 'r',painter_path) if self._endArrow: self.add_arrow(self._endX, self._endY, 'r' if self._startX <= self._endX else 'l',painter_path) pen = QPen(self._color, self._curveWidth, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) painter.setPen(pen) painter.setRenderHints(QPainter.Antialiasing, True) painter.drawPath(painter_path)
def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) self._optionsRects = {} w, h = self.width(), self.height() metrics = self.fontMetrics() hphp = 2 * _hPad painter.save() path = QPainterPath() path.addRoundedRect(.5, .5, w - 1, h - 1, 4, 4) painter.fillPath(path, QColor(250, 250, 250)) x = 0 linePath = QPainterPath() for text in self._options[:-1]: x += hphp + metrics.width(text) linePath.moveTo(x, 0) linePath.lineTo(x, h) pen = painter.pen() pen.setColor(QColor(218, 218, 218)) pen.setWidth(0) painter.setPen(pen) painter.drawPath(path) painter.setRenderHint(QPainter.Antialiasing, False) painter.drawPath(linePath) painter.restore() painter.translate(_hPad, _vPad + metrics.ascent()) left = 0 for index, text in enumerate(self._options): if index in self._selection: color = QColor(20, 146, 230) else: color = QColor(63, 63, 63) painter.setPen(color) painter.drawText(0, 0, text) textWidth = metrics.width(text) rectWidth = textWidth + hphp rect = (left, 0, rectWidth, h) self._optionsRects[index] = rect painter.translate(rectWidth, 0) left += rectWidth
def create_rounded_image(pixmap): color = QColor(0, 0, 0, 0) pix = QPixmap(QSize(pixmap.width(), pixmap.height())) pix.fill(color) rect = QRectF(0.0, 0.0, pixmap.width(), pixmap.height()) painter = QPainter() painter.begin(pix) painter.setRenderHints(QPainter.Antialiasing, True) path = QPainterPath() path.addRoundedRect(rect, pixmap.width() / 2, pixmap.height() / 2) painter.drawPath(path) brush = QtGui.QBrush() brush.setTexture(pixmap) painter.fillPath(path, brush) painter.end() return pix
def _drawTextShadow(painter: QPainter, x: int, y: int, text: str, font: QFont, text_color: QColor): # setup outline path text_path = QPainterPath() text_path.addText(x, y, font, text) # draw outline path 1 outline_color = QColor(0, 0, 0, 64) outline_brush = QBrush(outline_color, Qt.SolidPattern) outline_pen = QPen(outline_brush, 8, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) painter.setPen(outline_pen) painter.setBrush(outline_brush) painter.drawPath(text_path) # draw outline path 2 outline_color = QColor(0, 0, 0, 128) outline_brush = QBrush(outline_color, Qt.SolidPattern) outline_pen = QPen(outline_brush, 4, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) painter.setPen(outline_pen) painter.setBrush(outline_brush) painter.drawPath(text_path) # draw text painter.setPen(text_color) painter.setFont(font) painter.drawText(x, y, text)
def paintEvent(self, event): self._alignmentPaths = [] painter = QPainter(self) painter.setPen(QColor(45, 45, 45)) circleRadius = self._circleRadius padding = self._padding rect = event.rect() size = min(rect.height(), rect.width()) offset = .5 * (rect.width() - size) painter.translate(offset, 0) borderRect = rect.__class__( rect.left() + circleRadius + padding, rect.top() + circleRadius + padding, size - 2 * (circleRadius + padding), size - 2 * (circleRadius + padding)) borderPath = QPainterPath() borderPath.addRect(*borderRect.getRect()) columnCount = 3 radioPath = QPainterPath() selectedPath = QPainterPath() for row in range(columnCount): for col in range(columnCount): index = row * columnCount + col path = QPainterPath() path.addEllipse( padding + col * .5 * borderRect.width(), padding + row * .5 * borderRect.height(), 2 * circleRadius, 2 * circleRadius) if self._alignment == index: selectedPath = path self._alignmentPaths.append(path.translated(offset, 0)) radioPath.addPath(path) painter.drawPath(borderPath - radioPath) painter.setRenderHint(QPainter.Antialiasing) painter.drawPath(radioPath) painter.fillPath(selectedPath, Qt.black)
def paintEvent(self, evt): """ Protected method handling a paint event. @param evt reference to the paint event (QPaintEvent) """ painter = QPainter(self) if self.__image is not None and not self.__image.isNull(): x = (self.width() - self.__image.width()) // 2 - 1 y = (self.height() - self.__image.height()) // 2 - 1 painter.drawImage(x, y, self.__image) if self.__menu is not None: triagPath = QPainterPath() startPos = QPointF(self.width() - 5, self.height() - 3) triagPath.moveTo(startPos) triagPath.lineTo(startPos.x() + 4, startPos.y()) triagPath.lineTo(startPos.x() + 2, startPos.y() + 2) triagPath.closeSubpath() painter.setPen(Qt.black) painter.setBrush(Qt.black) painter.setRenderHint(QPainter.Antialiasing, False) painter.drawPath(triagPath)
def toPdf(self, image): printer = QPrinter() printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter) printer.setResolution(600) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(self.get_path_to_revealer_file('.pdf')) printer.setPageMargins(0,0,0,0,6) painter = QPainter() painter.begin(printer) delta_h = round(image.width()/self.abstand_v) delta_v = round(image.height()/self.abstand_h) size_h = 2028+((int(self.calibration_h)*2028/(2028-(delta_h*2)+int(self.calibration_h)))/2) size_v = 1284+((int(self.calibration_v)*1284/(1284-(delta_v*2)+int(self.calibration_v)))/2) image = image.scaled(size_h, size_v) painter.drawImage(553,533, image) wpath = QPainterPath() wpath.addRoundedRect(QRectF(553,533, size_h, size_v), 19, 19) painter.setPen(QPen(Qt.black, 1)) painter.drawPath(wpath) painter.end()
def paintEvent(self, event): qp = QPainter() qp.begin(self) qp.setPen(self.textColor) qp.setFont(self.font) qp.setRenderHint(QPainter.Antialiasing) w = 0 # Draw time scale = self.getScale() while w <= self.width(): qp.drawText(w - 50, 0, 100, 100, Qt.AlignHCenter, self.get_time_string(w * scale)) w += 100 # Draw down line qp.setPen(QPen(Qt.darkCyan, 5, Qt.SolidLine)) qp.drawLine(0, 40, self.width(), 40) # Draw dash lines point = 0 qp.setPen(QPen(self.textColor)) qp.drawLine(0, 40, self.width(), 40) while point <= self.width(): if point % 30 != 0: qp.drawLine(3 * point, 40, 3 * point, 30) else: qp.drawLine(3 * point, 40, 3 * point, 20) point += 10 if self.pos is not None and self.is_in: qp.drawLine(self.pos.x(), 0, self.pos.x(), 40) if self.pointerPos is not None: line = QLine( QPoint(self.pointerTimePos / self.getScale(), 40), QPoint(self.pointerTimePos / self.getScale(), self.height())) poly = QPolygon([ QPoint(self.pointerTimePos / self.getScale() - 10, 20), QPoint(self.pointerTimePos / self.getScale() + 10, 20), QPoint(self.pointerTimePos / self.getScale(), 40) ]) else: line = QLine(QPoint(0, 0), QPoint(0, self.height())) poly = QPolygon([QPoint(-10, 20), QPoint(10, 20), QPoint(0, 40)]) # Draw samples t = 0 for sample in self.videoSamples: # Clear clip path path = QPainterPath() path.addRoundedRect( QRectF(t / scale, 50, sample.duration / scale, 200), 10, 10) qp.setClipPath(path) # Draw sample path = QPainterPath() qp.setPen(sample.color) path.addRoundedRect( QRectF(t / scale, 50, sample.duration / scale, 50), 10, 10) sample.startPos = t / scale sample.endPos = t / scale + sample.duration / scale qp.fillPath(path, sample.color) qp.drawPath(path) # Draw preview pictures if sample.picture is not None: if sample.picture.size().width() < sample.duration / scale: path = QPainterPath() path.addRoundedRect( QRectF(t / scale, 52.5, sample.picture.size().width(), 45), 10, 10) qp.setClipPath(path) qp.drawPixmap( QRect(t / scale, 52.5, sample.picture.size().width(), 45), sample.picture) else: path = QPainterPath() path.addRoundedRect( QRectF(t / scale, 52.5, sample.duration / scale, 45), 10, 10) qp.setClipPath(path) pic = sample.picture.copy(0, 0, sample.duration / scale, 45) qp.drawPixmap( QRect(t / scale, 52.5, sample.duration / scale, 45), pic) t += sample.duration # Clear clip path path = QPainterPath() path.addRect(self.rect().x(), self.rect().y(), self.rect().width(), self.rect().height()) qp.setClipPath(path) # Draw pointer qp.setPen(Qt.darkCyan) qp.setBrush(QBrush(Qt.darkCyan)) qp.drawPolygon(poly) qp.drawLine(line) qp.end()
def paintEvent(self, event): painter = QPainter(self) painter.drawPath(self.path)
def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.TextAntialiasing) R = 100 Pi = 3.14159 deg = Pi * 72 / 180 points = [ QPoint(R, 0), QPoint(R * math.cos(deg), -R * math.sin(deg)), QPoint(R * math.cos(2 * deg), -R * math.sin(2 * deg)), QPoint(R * math.cos(3 * deg), -R * math.sin(3 * deg)), QPoint(R * math.cos(4 * deg), -R * math.sin(4 * deg)) ] font = painter.font() font.setPointSize(12) font.setBold(False) painter.setFont(font) pen = QPen() pen.setWidth(2) pen.setColor(Qt.blue) pen.setStyle(Qt.SolidLine) pen.setCapStyle(Qt.FlatCap) pen.setJoinStyle(Qt.BevelJoin) painter.setPen(pen) brush = QBrush() brush.setColor(Qt.yellow) brush.setStyle(Qt.SolidPattern) painter.setBrush(brush) starPath = QPainterPath() starPath.moveTo(points[0]) starPath.lineTo(points[2]) starPath.lineTo(points[4]) starPath.lineTo(points[1]) starPath.lineTo(points[3]) starPath.closeSubpath() starPath.addText(points[0], font, "0") starPath.addText(points[1], font, "1") starPath.addText(points[2], font, "2") starPath.addText(points[3], font, "3") starPath.addText(points[4], font, "4") painter.save() painter.translate(100, 120) painter.drawPath(starPath) painter.drawText(0, 0, "S1") painter.restore() painter.translate(300, 120) painter.scale(0.8, 0.8) painter.rotate(90) painter.drawPath(starPath) painter.drawText(0, 0, "S2") painter.resetTransform() painter.translate(500, 120) painter.rotate(-145) painter.drawPath(starPath) painter.drawText(0, 0, "S3")
def paintEvent(self, event): p = QPainter() p.begin(self) self._normalMap.render(p, event.rect()) p.setPen(Qt.black) p.drawText( self.rect(), Qt.AlignBottom | Qt.TextWordWrap, "Map data CCBYSA 2009 OpenStreetMap.org contributors", ) p.end() if self.zoomed: dim = min(self.width(), self.height()) magnifierSize = min(MAX_MAGNIFIER, dim * 2 / 3) radius = magnifierSize / 2 ring = radius - 15 box = QSize(magnifierSize, magnifierSize) # reupdate our mask if self.maskPixmap.size() != box: self.maskPixmap = QPixmap(box) self.maskPixmap.fill(Qt.transparent) g = QRadialGradient() g.setCenter(radius, radius) g.setFocalPoint(radius, radius) g.setRadius(radius) g.setColorAt(1.0, QColor(255, 255, 255, 0)) g.setColorAt(0.5, QColor(128, 128, 128, 255)) mask = QPainter(self.maskPixmap) mask.setRenderHint(QPainter.Antialiasing) mask.setCompositionMode(QPainter.CompositionMode_Source) mask.setBrush(g) mask.setPen(Qt.NoPen) mask.drawRect(self.maskPixmap.rect()) mask.setBrush(QColor(Qt.transparent)) mask.drawEllipse(g.center(), ring, ring) mask.end() center = self.dragPos - QPoint(0, radius) center += QPoint(0, radius / 2) corner = center - QPoint(radius, radius) xy = center * 2 - QPoint(radius, radius) # only set the dimension to the magnified portion if self.zoomPixmap.size() != box: self.zoomPixmap = QPixmap(box) self.zoomPixmap.fill(Qt.lightGray) if True: p = QPainter(self.zoomPixmap) p.translate(-xy) self._largeMap.render(p, QRect(xy, box)) p.end() clipPath = QPainterPath() clipPath.addEllipse(QPointF(center), ring, ring) p = QPainter(self) p.setRenderHint(QPainter.Antialiasing) p.setClipPath(clipPath) p.drawPixmap(corner, self.zoomPixmap) p.setClipping(False) p.drawPixmap(corner, self.maskPixmap) p.setPen(Qt.gray) p.drawPath(clipPath) if self.invert: p = QPainter(self) p.setCompositionMode(QPainter.CompositionMode_Difference) p.fillRect(event.rect(), Qt.white) p.end()
def paint( self, painter: QtGui.QPainter, option: QStyleOptionGraphicsItem, widget: Optional[QWidget], ) -> None: # * title titlePath = QtGui.QPainterPath() titlePath.setFillRule(QtCore.Qt.WindingFill) titlePath.addRoundedRect( 0, 0, self._nodeWidth, self._nodeTitleHeight, self._nodeEdgeSize, self._nodeEdgeSize, ) titlePath.addRect( 0, self._nodeTitleHeight - self._nodeEdgeSize, self._nodeEdgeSize, self._nodeEdgeSize, ) titlePath.addRect( self._nodeWidth - self._nodeEdgeSize, self._nodeTitleHeight - self._nodeEdgeSize, self._nodeEdgeSize, self._nodeEdgeSize, ) painter.setPen(QtCore.Qt.NoPen) painter.setBrush(self._nodeTitleBrush) painter.drawPath(titlePath.simplified()) # ? content ContentPath = QtGui.QPainterPath() ContentPath.setFillRule(QtCore.Qt.WindingFill) ContentPath.addRoundedRect( 0, self._nodeTitleHeight, self._nodeWidth, self._nodeHeight - self._nodeTitleHeight, self._nodeEdgeSize, self._nodeEdgeSize, ) ContentPath.addRect( 0, self._nodeTitleHeight, self._nodeEdgeSize, self._nodeEdgeSize ) ContentPath.addRect( self._nodeWidth - self._nodeEdgeSize, self._nodeTitleHeight, self._nodeEdgeSize, self._nodeEdgeSize, ) painter.setPen(QtCore.Qt.NoPen) painter.setBrush(self._nodeBrushBackground) painter.drawPath(ContentPath.simplified()) # ? outline outline = QtGui.QPainterPath() outline.addRoundedRect( 0, 0, self._nodeWidth, self._nodeHeight, self._nodeEdgeSize, self._nodeEdgeSize, ) painter.setPen( self._nodePenDefault if not self.isSelected() else self._nodePenSelected ) painter.setBrush(QtCore.Qt.NoBrush) painter.drawPath(outline.simplified())
def paintEvent(self, event): p = QPainter() p.begin(self) self._normalMap.render(p, event.rect()) p.setPen(Qt.black) p.drawText(self.rect(), Qt.AlignBottom | Qt.TextWordWrap, "Map data CCBYSA 2009 OpenStreetMap.org contributors") p.end() if self.zoomed: dim = min(self.width(), self.height()) magnifierSize = min(MAX_MAGNIFIER, dim * 2 / 3) radius = magnifierSize / 2 ring = radius - 15 box = QSize(magnifierSize, magnifierSize) # reupdate our mask if self.maskPixmap.size() != box: self.maskPixmap = QPixmap(box) self.maskPixmap.fill(Qt.transparent) g = QRadialGradient() g.setCenter(radius, radius) g.setFocalPoint(radius, radius) g.setRadius(radius) g.setColorAt(1.0, QColor(255, 255, 255, 0)) g.setColorAt(0.5, QColor(128, 128, 128, 255)) mask = QPainter(self.maskPixmap) mask.setRenderHint(QPainter.Antialiasing) mask.setCompositionMode(QPainter.CompositionMode_Source) mask.setBrush(g) mask.setPen(Qt.NoPen) mask.drawRect(self.maskPixmap.rect()) mask.setBrush(QColor(Qt.transparent)) mask.drawEllipse(g.center(), ring, ring) mask.end() center = self.dragPos - QPoint(0, radius) center += QPoint(0, radius / 2) corner = center - QPoint(radius, radius) xy = center * 2 - QPoint(radius, radius) # only set the dimension to the magnified portion if self.zoomPixmap.size() != box: self.zoomPixmap = QPixmap(box) self.zoomPixmap.fill(Qt.lightGray) if True: p = QPainter(self.zoomPixmap) p.translate(-xy) self._largeMap.render(p, QRect(xy, box)) p.end() clipPath = QPainterPath() clipPath.addEllipse(QPointF(center), ring, ring) p = QPainter(self) p.setRenderHint(QPainter.Antialiasing) p.setClipPath(clipPath) p.drawPixmap(corner, self.zoomPixmap) p.setClipping(False) p.drawPixmap(corner, self.maskPixmap) p.setPen(Qt.gray) p.drawPath(clipPath) if self.invert: p = QPainter(self) p.setCompositionMode(QPainter.CompositionMode_Difference) p.fillRect(event.rect(), Qt.white) p.end()
def paintEvent(self, e): qp = QPainter() qp.begin(self) qp.drawPath(self.path) qp.end()
class Renderer2D: def __init__(self, context): self.ctx = context self.qp = QPainter(self.ctx) # Initiate Properties #self.fill = QColor(255, 255, 255) #self.stroke = QColor(0, 0, 0) self.tint = QColor(0, 0, 0) self.isFill = True self.isStroke = True self.isSmooth = True # Antialiasing # Stroke Properties #self.strokeWeight = 1 #self.strokeJoin = Qt.RoundJoin #self.strokeCap = Qt.RoundCap self.pen = QPen(QColor(0, 0, 0), 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self.brush = QColor(255, 255, 255) def stroke(self, color): self.pen.setColor(color) def strokeWeight(self, width): self.pen.setWidth(width) def strokeJoin(self, join): if join == "MITER": self.pen.setJoinStyle(Qt.MiterJoin) elif join == "BEVEL": self.pen.setJoinStyle(Qt.BevelJoin) elif join == "ROUND": self.pen.setJoinStyle(Qt.RoundJoin) else: print("Invalid Stroke Join") def strokeCap(self, cap): if cap == "PROJECT": self.pen.setJoinStyle(Qt.SquareCap) elif cap == "SQUARE": self.pen.setJoinStyle(Qt.FlatCap) elif cap == "ROUND": self.pen.setJoinStyle(Qt.RoundCap) else: print("Invalid Stroke Cap") def fill(self, color): self.brush = color def background(self, color): self.qp.begin(self.ctx) self.qp.setPen(QColor(0, 0, 0, 0)) self.qp.setBrush(color) self.qp.drawRect(0, 0, self.ctx.size().width(), self.ctx.size().height()) self.qp.end() def point(self, x, y): path = QPainterPath() path.moveTo(x, y) path.lineTo(x + 0.1, y) self.renderPath(path) def rect(self, x, y, width, height): path = QPainterPath() path.moveTo(x, y) path.lineTo(x + width, y) path.lineTo(x + width, y + height) path.lineTo(x, y + height) path.lineTo(x, y) self.renderPath(path) def line(self, x1, y1, x2, y2): path = QPainterPath() path.moveTo(x1, y1) path.lineTo(x2, y2) self.renderPath(path) def square(self, x, y, s): self.rect(x, y, s, s) def circle(self, cx, cy, r): self.ellipse(cx, cy, r, r) def ellipse(self, x, y, w, h): kappa = 0.5522847498 ox = w / 2 * kappa # control point offset horizontal oy = h / 2 * kappa # control point offset vertical xe = x + w # x-end ye = y + h # y-end xm = x + w / 2 # x-middle ym = y + h / 2 # y-middle path = QPainterPath() path.moveTo(x, ym) path.cubicTo(x, ym - oy, xm - ox, y, xm, y) path.cubicTo(xm + ox, y, xe, ym - oy, xe, ym) path.cubicTo(xe, ym + oy, xm + ox, ye, xm, ye) path.cubicTo(xm - ox, ye, x, ym + oy, x, ym) self.renderPath(path) def quad(self, x1, y1, x2, y2, x3, y3, x4, y4): path = QPainterPath() path.moveTo(x1, y1) path.lineTo(x2, y2) path.lineTo(x3, y3) path.lineTo(x4, y4) path.lineTo(x1, y1) self.renderPath(path) def triangle(self, x1, y1, x2, y2, x3, y3): path = QPainterPath() path.moveTo(x1, y1) path.lineTo(x2, y2) path.lineTo(x3, y3) path.lineTo(x1, y1) self.renderPath(path) def renderPath(self, path): self.qp.begin(self.ctx) if self.isStroke: self.qp.setPen(self.pen) if self.isFill: self.qp.setBrush(self.brush) if self.isSmooth: self.qp.setRenderHint(QPainter.Antialiasing, True) self.qp.setRenderHint(QPainter.HighQualityAntialiasing, True) self.qp.setRenderHint(QPainter.SmoothPixmapTransform, True) self.qp.drawPath(path) self.qp.end()
def paintEvent(self, evt): x1 = QPoint(0, -70) x2 = QPoint(0, -90) x3 = QPoint(-90, 0) x4 = QPoint(-70, 0) extRect = QRectF(-90, -90, 180, 180) intRect = QRectF(-70, -70, 140, 140) midRect = QRectF(-44, -80, 160, 160) unitRect = QRectF(-50, 60, 110, 50) speedInt = self.speed #speedDec = (self.speed * 10.0) - (speedInt * 10) s_SpeedInt = speedInt.__str__()[0:4] powerAngle = self.power * 270.0 / 100.0 dummyPath = QPainterPath() dummyPath.moveTo(x1) dummyPath.arcMoveTo(intRect, 90 - powerAngle) powerPath = QPainterPath() powerPath.moveTo(x1) powerPath.lineTo(x2) powerPath.arcTo(extRect, 90, -1 * powerAngle) powerPath.lineTo(dummyPath.currentPosition()) powerPath.arcTo(intRect, 90 - powerAngle, powerAngle) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) side = min(self.width(), self.height()) painter.scale(side / 200.0, side / 200.0) painter.save() painter.rotate(-135) if self.displayPowerPath: externalPath = QPainterPath() externalPath.moveTo(x1) externalPath.lineTo(x2) externalPath.arcTo(extRect, 90, -270) externalPath.lineTo(x4) externalPath.arcTo(intRect, 180, 270) painter.setPen(self.powerPathColor) painter.drawPath(externalPath) painter.setBrush(self.powerGradient) painter.setPen(Qt.NoPen) painter.drawPath(powerPath) painter.restore() painter.save() painter.translate(QPointF(0, -50)) painter.setPen(self.unitTextColor) fontFamily = self.font().family() unitFont = QFont(fontFamily, 9) painter.setFont(unitFont) painter.drawText(unitRect, Qt.AlignCenter, "{}".format(self.unit)) painter.restore() painter.setPen(self.unitTextColor) fontFamily = self.font().family() unitFont = QFont(fontFamily, 12) painter.setFont(unitFont) painter.drawText(unitRect, Qt.AlignCenter, "{}".format(self.title)) speedColor = QColor(0, 0, 0) speedFont = QFont(fontFamily, 30) fm1 = QFontMetrics(speedFont) speedWidth = fm1.width(s_SpeedInt) #speedDecFont = QFont(fontFamily, 23) #fm2 = QFontMetrics(speedDecFont) #speedDecWidth = fm2.width(s_SpeedDec) leftPos = -1 * speedWidth + 40 leftDecPos = leftPos + speedWidth topPos = 10 topDecPos = 10 painter.setPen(self.speedTextColor) painter.setFont(speedFont) painter.drawText(leftPos, topPos, s_SpeedInt)
def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: QWidget): painter.setPen(self.pen()) painter.setBrush(self.brush()) painter.drawPath(self.path())
def paintEvent(self, event): painter = QPainter(self) painter.setPen(Qt.black) painter.drawLine(0, 50, 700, 50) # dividing line painter.drawLine(30, 720, 670, 720) # Ox axis painter.drawLine(30, 720, 30, 80) # Oy axis painter.drawLine(670, 720, 665, 715) # arrow Ox painter.drawLine(670, 720, 665, 725) # arrow Ox painter.drawLine(30, 80, 25, 85) # arrow Oy painter.drawLine(30, 80, 35, 85) # arrow Oy if self.points != None: for p in self.points: pen = QPen() pen.setColor(Qt.red) pen.setWidth(5) painter.setPen(pen) painter.drawPoint(30 + p[0], 720 - p[1]) if self.contour != None: pen = QPen() pen.setColor(Qt.black) painter.setPen(pen) for i in range(-1, len(self.contour) - 1): painter.drawLine(30 + self.point_dict[self.contour[i]][0], 720 - self.point_dict[self.contour[i]][1], 30 + self.point_dict[self.contour[i + 1]][0], 720 - self.point_dict[self.contour[i + 1]][1]) path = QPainterPath() path.moveTo(30 + self.point_dict[self.contour[-1]][0], 720 - self.point_dict[self.contour[-1]][1]) for i in range(len(self.contour)): path.lineTo(30 + self.point_dict[self.contour[i]][0], 720 - self.point_dict[self.contour[i]][1]) path.closeSubpath() painter.fillPath(path, QColor(0, 0, 255)) if self.figure != None: for contour in self.figure: for i in range(-1, len(contour) - 1): painter.drawLine(30 + contour[i][0], 720 - contour[i][1], 30 + contour[i + 1][0], 720 - contour[i + 1][1]) if self.triangulation != None: for triangle in self.triangulation: Ax, Ay, Bx, By, Cx, Cy = triangle painter.setPen(Qt.red) painter.drawPoint(30 + Ax, 720 - Ay) painter.drawPoint(30 + Bx, 720 - By) painter.drawPoint(30 + Cx, 720 - Cy) painter.setPen(Qt.black) painter.drawLine(30 + Ax, 720 - Ay, 30 + Bx, 720 - By) painter.drawLine(30 + Bx, 720 - By, 30 + Cx, 720 - Cy) painter.drawLine(30 + Cx, 720 - Cy, 30 + Ax, 720 - Ay) if self.cells != None: pen = QPen() max_value = max(self.cells, key=lambda x: x[-1])[-1] # print(max_value) for cell in self.cells: pen.setColor(Qt.blue) pen.setWidth(5) painter.setPen(pen) painter.drawPoint(30 + cell[0], 720 - cell[1]) value = cell[-1] cell = cell[2:-1] # pen.setColor(Qt.red) # pen.setWidth(1) # painter.setPen(pen) path = QPainterPath() path.moveTo(30 + cell[-2], 720 - cell[-1]) for i in range(0, len(cell), 2): path.lineTo(30 + cell[i], 720 - cell[i + 1]) path.closeSubpath() painter.fillPath( path, QColor(min(255, int(4 * 255 * value / max_value)), 0, 255 - min(255, int(1 * 255 * value / max_value)))) pen.setColor(Qt.black) pen.setWidth(1) painter.setPen(pen) painter.drawPath(path) # for i in range(-2, len(cell) - 2, 2): # painter.drawLine(30 + cell[i], 720 - cell[i + 1], 30 + cell[i + 2], 720 - cell[i + 3]) pen.setColor(Qt.green) pen.setWidth(5) painter.setPen(pen) for i in range(0, len(cell), 2): painter.drawPoint(30 + cell[i], 720 - cell[i + 1])
class Renderer2D: def __init__(self, context): self.ctx = context self.qp = QPainter() # Initiate Properties self.tint = QColor(0, 0, 0) self.isSmooth = True # Antialiasing # Stroke Properties self.pen = QPen(QColor(0, 0, 0), 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) # Fill Properties self.brush = QColor(255, 255, 255) self.curveTightness = 0 def stroke(self, color): self.pen.setColor(color) def strokeWeight(self, width): self.pen.setWidth(width) def strokeJoin(self, join): if join == "MITER": self.pen.setJoinStyle(Qt.MiterJoin) elif join == "BEVEL": self.pen.setJoinStyle(Qt.BevelJoin) elif join == "ROUND": self.pen.setJoinStyle(Qt.RoundJoin) else: print("Invalid Stroke Join") def strokeCap(self, cap): if cap == "PROJECT": self.pen.setJoinStyle(Qt.SquareCap) elif cap == "SQUARE": self.pen.setJoinStyle(Qt.FlatCap) elif cap == "ROUND": self.pen.setJoinStyle(Qt.RoundCap) else: print("Invalid Stroke Cap") def fill(self, color): self.brush = color def background(self, color): #self.qp.begin(self.ctx) self.qp.setPen(QColor(0, 0, 0, 0)) self.qp.setBrush(color) self.qp.drawRect(0, 0, self.ctx.size().width(), self.ctx.size().height()) #self.qp.end() def point(self, x, y): path = QPainterPath() path.moveTo(x, y) path.lineTo(x + 0.1, y) self.renderPath(path) def rect(self, x, y, width, height): path = QPainterPath() path.moveTo(x, y) path.lineTo(x + width, y) path.lineTo(x + width, y + height) path.lineTo(x, y + height) path.lineTo(x, y) self.renderPath(path) def line(self, x1, y1, x2, y2): path = QPainterPath() path.moveTo(x1, y1) path.lineTo(x2, y2) self.renderPath(path) def square(self, x, y, s): self.rect(x, y, s, s) def circle(self, cx, cy, r): self.ellipse(cx, cy, r, r) def ellipse(self, x, y, w, h): kappa = 0.5522847498 ox = w/2 * kappa # control point offset horizontal oy = h/2 * kappa # control point offset vertical xe = x + w # x-end ye = y + h # y-end xm = x + w/2 # x-middle ym = y + h/2 # y-middle path = QPainterPath() path.moveTo(x, ym) path.cubicTo(x, ym - oy, xm - ox, y, xm, y) path.cubicTo(xm + ox, y, xe, ym - oy, xe, ym) path.cubicTo(xe, ym + oy, xm + ox, ye, xm, ye) path.cubicTo(xm - ox, ye, x, ym + oy, x, ym) self.renderPath(path) def quad(self, x1, y1, x2, y2, x3, y3, x4, y4): path = QPainterPath() path.moveTo(x1, y1) path.lineTo(x2, y2) path.lineTo(x3, y3) path.lineTo(x4, y4) path.lineTo(x1, y1) self.renderPath(path) def triangle(self, x1, y1, x2, y2, x3, y3): path = QPainterPath() path.moveTo(x1, y1) path.lineTo(x2, y2) path.lineTo(x3, y3) path.lineTo(x1, y1) self.renderPath(path) def endShape(self, mode, vertices, isCurve, isBezier, isQuadratic, isContour, shapeKind): if len(vertices) == 0: return closeShape = mode == CLOSE if closeShape and not isContour: vertices.append(vertices[0]) if isCurve and (shapeKind == POLYGON or shapeKind == None): s = 1 - self.curveTightness path = QPainterPath() path.moveTo(vertices[1][0], vertices[1][1]) for i in range(1, len(vertices) - 2): v = vertices[i] b = [] b.append([v[0], v[1]]) b.append([ v[0] + (s * vertices[i + 1][0] - s * vertices[i - 1][0]) / 6, v[1] + (s * vertices[i + 1][1] - s * vertices[i - 1][1]) / 6 ]) b.append([ vertices[i + 1][0] + (s * vertices[i][0] - s * vertices[i + 2][0]) / 6, vertices[i + 1][1] + (s * vertices[i][1] - s * vertices[i + 2][1]) / 6 ]) b.append([vertices[i + 1][0], vertices[i + 1][1]]) path.cubicTo( b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1] ) if closeShape: path.lineTo(vertices[i + 1][0], vertices[i + 1][1]) self.renderPath(path) elif isBezier and (shapeKind == POLYGON or shapeKind == None): path = QPainterPath() path.moveTo(vertices[0][0], vertices[0][1]) for v in vertices: if len(v) == 2: path.lineTo(v[0], v[1]) else: path.cubicTo( v[0], v[1], v[2], v[3], v[4], v[5], ) self.renderPath(path) elif isQuadratic and (shapeKind == POLYGON or shapeKind == None): path = QPainterPath() path.moveTo(vertices[0][0], vertices[0][1]) for v in vertices: if len(v) == 2: path.lineTo(v[0], v[1]) else: path.quadTo( v[0], v[1], v[2], v[3], ) self.renderPath(path) else: if shapeKind == POINTS: for p in vertices: self.point(p[0], p[1]) elif shapeKind == LINES: for i in range(0, len(vertices) - 1, 2): self.line(vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1]) elif shapeKind == TRIANGLES: for i in range(0, len(vertices) - 2, 3): self.triangle(vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1], vertices[i + 2][0], vertices[i + 2][1]) elif shapeKind == TRIANGLE_STRIP: for i in range(len(vertices) - 2): self.triangle(vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1], vertices[i + 2][0], vertices[i + 2][1]) elif shapeKind == TRIANGLE_FAN: for i in range(1, len(vertices) - 1): self.triangle(vertices[0][0], vertices[0][1], vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1]) elif shapeKind == QUADS: for i in range(0, len(vertices) - 3, 4): self.quad(vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1], vertices[i + 2][0], vertices[i + 2][1], vertices[i + 3][0], vertices[i + 3][1]) elif shapeKind == QUAD_STRIP: for i in range(0, len(vertices) - 3, 2): self.quad(vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1], vertices[i + 2][0], vertices[i + 2][1], vertices[i + 3][0], vertices[i + 3][1]) else: path = QPainterPath() path.moveTo(vertices[0][0], vertices[0][1]) for p in vertices: path.lineTo(p[0], p[1]) self.renderPath(path) return def renderPath(self, path): self.qp.setPen(self.pen) self.qp.setBrush(self.brush) if self.isSmooth: self.qp.setRenderHint(QPainter.Antialiasing, True) self.qp.setRenderHint(QPainter.HighQualityAntialiasing, True) self.qp.setRenderHint(QPainter.SmoothPixmapTransform, True) self.qp.drawPath(path) def push(self): self.qp.save() def pop(self): self.qp.restore() def applyMatrix(self, a, b, c, d, e, f): self.qp.setWorldTransform([ a, c, e, b, d, f, 0, 0, 1 ]) def resetMatrix(self): self.qp.setWorldTransform(QMatrix([ [1, 0, 0], [0, 1, 0], [0, 0, 1] ])) def rotate(self, angle): self.qp.rotate(angle) def scale(self, *args): if len(args) == 1: self.qp.scale(args[0], args[0]) elif len(args) == 2: self.qp.scale(args[0], args[1]) def shearX(self, angle): self.qp.shear(angle, 0) def shearY(self, angle): self.qp.shear(0, angle) def translate(self, x, y): self.qp.translate(x, y)
def paint(self, painter: QPainter, styleOptionGraphicsItem, widget=None): # painter.setBrush() painter.drawPath(self.path())
def overlay_marks(self, img, is_cseed=False, calibration_sheet=False): border_color = Qt.white base_img = QImage(self.f_size.width(),self.f_size.height(), QImage.Format_ARGB32) base_img.fill(border_color) img = QImage(img) painter = QPainter() painter.begin(base_img) total_distance_h = round(base_img.width() / self.abstand_v) dist_v = round(total_distance_h) / 2 dist_h = round(total_distance_h) / 2 img = img.scaledToWidth(base_img.width() - (2 * (total_distance_h))) painter.drawImage(total_distance_h, total_distance_h, img) #frame around image pen = QPen(Qt.black, 2) painter.setPen(pen) #horz painter.drawLine(0, total_distance_h, base_img.width(), total_distance_h) painter.drawLine(0, base_img.height()-(total_distance_h), base_img.width(), base_img.height()-(total_distance_h)) #vert painter.drawLine(total_distance_h, 0, total_distance_h, base_img.height()) painter.drawLine(base_img.width()-(total_distance_h), 0, base_img.width()-(total_distance_h), base_img.height()) #border around img border_thick = 6 Rpath = QPainterPath() Rpath.addRect(QRectF((total_distance_h)+(border_thick/2), (total_distance_h)+(border_thick/2), base_img.width()-((total_distance_h)*2)-((border_thick)-1), (base_img.height()-((total_distance_h))*2)-((border_thick)-1))) pen = QPen(Qt.black, border_thick) pen.setJoinStyle (Qt.MiterJoin) painter.setPen(pen) painter.drawPath(Rpath) Bpath = QPainterPath() Bpath.addRect(QRectF((total_distance_h), (total_distance_h), base_img.width()-((total_distance_h)*2), (base_img.height()-((total_distance_h))*2))) pen = QPen(Qt.black, 1) painter.setPen(pen) painter.drawPath(Bpath) pen = QPen(Qt.black, 1) painter.setPen(pen) painter.drawLine(0, base_img.height()/2, total_distance_h, base_img.height()/2) painter.drawLine(base_img.width()/2, 0, base_img.width()/2, total_distance_h) painter.drawLine(base_img.width()-total_distance_h, base_img.height()/2, base_img.width(), base_img.height()/2) painter.drawLine(base_img.width()/2, base_img.height(), base_img.width()/2, base_img.height() - total_distance_h) #print code f_size = 37 QFontDatabase.addApplicationFont(os.path.join(os.path.dirname(__file__), 'DejaVuSansMono-Bold.ttf')) font = QFont("DejaVu Sans Mono", f_size-11, QFont.Bold) font.setPixelSize(35) painter.setFont(font) if not calibration_sheet: if is_cseed: #its a secret painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine)) painter.drawLine(0, dist_v, base_img.width(), dist_v) painter.drawLine(dist_h, 0, dist_h, base_img.height()) painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v)) painter.drawLine(base_img.width()-(dist_h), 0, base_img.width()-(dist_h), base_img.height()) painter.drawImage(((total_distance_h))+11, ((total_distance_h))+11, QImage(':icons/electrumb.png').scaledToWidth(2.1*(total_distance_h), Qt.SmoothTransformation)) painter.setPen(QPen(Qt.white, border_thick*8)) painter.drawLine(base_img.width()-((total_distance_h))-(border_thick*8)/2-(border_thick/2)-2, (base_img.height()-((total_distance_h)))-((border_thick*8)/2)-(border_thick/2)-2, base_img.width()-((total_distance_h))-(border_thick*8)/2-(border_thick/2)-2 - 77, (base_img.height()-((total_distance_h)))-((border_thick*8)/2)-(border_thick/2)-2) painter.setPen(QColor(0,0,0,255)) painter.drawText(QRect(0, base_img.height()-107, base_img.width()-total_distance_h - border_thick - 11, base_img.height()-total_distance_h - border_thick), Qt.AlignRight, self.versioned_seed.version + '_'+self.versioned_seed.checksum) painter.end() else: # revealer painter.setPen(QPen(border_color, 17)) painter.drawLine(0, dist_v, base_img.width(), dist_v) painter.drawLine(dist_h, 0, dist_h, base_img.height()) painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v)) painter.drawLine(base_img.width()-(dist_h), 0, base_img.width()-(dist_h), base_img.height()) painter.setPen(QPen(Qt.black, 2)) painter.drawLine(0, dist_v, base_img.width(), dist_v) painter.drawLine(dist_h, 0, dist_h, base_img.height()) painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v)) painter.drawLine(base_img.width()-(dist_h), 0, base_img.width()-(dist_h), base_img.height()) logo = QImage(':icons/revealer_c.png').scaledToWidth(1.3*(total_distance_h)) painter.drawImage((total_distance_h)+ (border_thick), ((total_distance_h))+ (border_thick), logo, Qt.SmoothTransformation) #frame around logo painter.setPen(QPen(Qt.black, border_thick)) painter.drawLine(total_distance_h+border_thick, total_distance_h+logo.height()+3*(border_thick/2), total_distance_h+logo.width()+border_thick, total_distance_h+logo.height()+3*(border_thick/2)) painter.drawLine(logo.width()+total_distance_h+3*(border_thick/2), total_distance_h+(border_thick), total_distance_h+logo.width()+3*(border_thick/2), total_distance_h+logo.height()+(border_thick)) #frame around code/qr qr_size = 179 painter.drawLine((base_img.width()-((total_distance_h))-(border_thick/2)-2)-qr_size, (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2, (base_img.width()/2+(total_distance_h/2)-border_thick-(border_thick*8)/2)-qr_size, (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2) painter.drawLine((base_img.width()/2+(total_distance_h/2)-border_thick-(border_thick*8)/2)-qr_size, (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2, base_img.width()/2 + (total_distance_h/2)-border_thick-(border_thick*8)/2-qr_size, ((base_img.height()-((total_distance_h)))-(border_thick/2)-2)) painter.setPen(QPen(Qt.white, border_thick * 8)) painter.drawLine( base_img.width() - ((total_distance_h)) - (border_thick * 8) / 2 - (border_thick / 2) - 2, (base_img.height() - ((total_distance_h))) - ((border_thick * 8) / 2) - (border_thick / 2) - 2, base_img.width() / 2 + (total_distance_h / 2) - border_thick - qr_size, (base_img.height() - ((total_distance_h))) - ((border_thick * 8) / 2) - (border_thick / 2) - 2) painter.setPen(QColor(0,0,0,255)) painter.drawText(QRect(((base_img.width()/2) +21)-qr_size, base_img.height()-107, base_img.width()-total_distance_h - border_thick -93, base_img.height()-total_distance_h - border_thick), Qt.AlignLeft, self.versioned_seed.get_ui_string_version_plus_seed()) painter.drawText(QRect(0, base_img.height()-107, base_img.width()-total_distance_h - border_thick -3 -qr_size, base_img.height()-total_distance_h - border_thick), Qt.AlignRight, self.versioned_seed.checksum) # draw qr code qr_qt = self.paintQR(self.versioned_seed.get_ui_string_version_plus_seed() + self.versioned_seed.checksum) target = QRectF(base_img.width()-65-qr_size, base_img.height()-65-qr_size, qr_size, qr_size ) painter.drawImage(target, qr_qt) painter.setPen(QPen(Qt.black, 4)) painter.drawLine(base_img.width()-65-qr_size, base_img.height()-65-qr_size, base_img.width() - 65 - qr_size, (base_img.height() - ((total_distance_h))) - ((border_thick * 8)) - (border_thick / 2) - 4 ) painter.drawLine(base_img.width()-65-qr_size, base_img.height()-65-qr_size, base_img.width() - 65, base_img.height()-65-qr_size ) painter.end() else: # calibration only painter.end() cal_img = QImage(self.f_size.width() + 100, self.f_size.height() + 100, QImage.Format_ARGB32) cal_img.fill(Qt.white) cal_painter = QPainter() cal_painter.begin(cal_img) cal_painter.drawImage(0,0, base_img) #black lines in the middle of border top left only cal_painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine)) cal_painter.drawLine(0, dist_v, base_img.width(), dist_v) cal_painter.drawLine(dist_h, 0, dist_h, base_img.height()) pen = QPen(Qt.black, 2, Qt.DashDotDotLine) cal_painter.setPen(pen) n=15 cal_painter.setFont(QFont("DejaVu Sans Mono", 21, QFont.Bold)) for x in range(-n,n): #lines on bottom (vertical calibration) cal_painter.drawLine((((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)-13, x+2+base_img.height()-(dist_v), (((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)+13, x+2+base_img.height()-(dist_v)) num_pos = 9 if x > 9 : num_pos = 17 if x < 0 : num_pos = 20 if x < -9: num_pos = 27 cal_painter.drawText((((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)-num_pos, 50+base_img.height()-(dist_v), str(x)) #lines on the right (horizontal calibrations) cal_painter.drawLine(x+2+(base_img.width()-(dist_h)), ((base_img.height()/(2*n)) *(x))+ (base_img.height()/n)+(base_img.height()/2)-13, x+2+(base_img.width()-(dist_h)), ((base_img.height()/(2*n)) *(x))+ (base_img.height()/n)+(base_img.height()/2)+13) cal_painter.drawText(30+(base_img.width()-(dist_h)), ((base_img.height()/(2*n)) *(x))+ (base_img.height()/2)+13, str(x)) cal_painter.end() base_img = cal_img return base_img
def redraw(self, painter: QtGui.QPainter, event) -> None: # if the button is hidden then there is nothing to draw if self._state == ICWidgetState.Hidden: return # draw the label area tmp_width = painter.device().width() tmp_height = painter.device().height() # define the rectangle to draw the button rect = QtCore.QRectF(3, 3, tmp_width-6, tmp_height-6) # path to be drawn path = QtGui.QPainterPath() path.setFillRule(Qt.WindingFill) path.addRoundedRect(rect, 10, 10) # brush to fill the area brush = QtGui.QLinearGradient(rect.topRight(), rect.bottomRight()) if self._state == ICWidgetState.Transparent: brush.setColorAt(0, self.background_color) brush.setColorAt(1, self.background_color) else: brush.setColorAt(0, self._label_color_dark) brush.setColorAt(0.5, self._label_color_light) brush.setColorAt(1, self._label_color_dark) painter.setBrush(brush) # define the border pen if self._state == ICWidgetState.Transparent: pen = QtGui.QPen(self.background_color) else: pen = QtGui.QPen(self._border_color) if self.in_focus: pen.setWidth(3) else: pen.setWidth(1) pen.setCapStyle(Qt.RoundCap) pen.setJoinStyle(Qt.RoundJoin) painter.setPen(pen) # draw the rectangle painter.drawPath(path) # draw the text only if the button is visible if self._state in (ICWidgetState.VisibleEnabled, ICWidgetState.VisibleDisabled): # draw the name fnt = painter.font() fnt.setBold(True) fnt.setPixelSize(self._name_text_size) painter.setFont(fnt) pen.setColor(self._name_color) painter.setPen(pen) rect = QtCore.QRect(10, 10, tmp_width - 20, self._name_text_size + 5) painter.drawText(rect, Qt.AlignLeft, str(self._name)) # draw the value fnt.setPixelSize(self._value_text_size) painter.setFont(fnt) pen.setColor(self._value_color) painter.setPen(pen) rect = QtCore.QRect(10, tmp_height - (self._value_text_size + 15), tmp_width - 20, self._value_text_size + 5) if self.__type == ICTextLabelType.LabelText: painter.drawText(rect, Qt.AlignRight, self._value) elif self.__type == ICTextLabelType.LabelInteger: painter.drawText(rect, Qt.AlignRight, str(self._value)) else: painter.drawText(rect, Qt.AlignRight, self._text_format.format(self._value))
def paintEvent(self, _event: QPaintEvent): if not self.crop or not self.resolution: return painter = QPainter(self) # Keep a backup of the transform and create a new one transform = painter.worldTransform() # Set scaling transform transform = transform.scale(self.width() / self.resolution.width(), self.height() / self.resolution.height()) # Compute the transform to flip the coordinate system on the x axis transform_flip = QTransform() if self.flip_x: transform_flip = transform_flip.translate(self.resolution.width(), 0.0) transform_flip = transform_flip.scale(-1.0, 1.0) # Small helper for tuple to QPoint def toqp(point): return QPoint(point[0], point[1]) # Starting from here we care about AA painter.setRenderHint(QPainter.Antialiasing) # Draw all the QR code results for res in self.results: painter.setWorldTransform(transform_flip * transform, False) # Draw lines between all of the QR code points pen = QPen(self.qr_outline_pen) if res in self.validator_results.result_colors: pen.setColor(self.validator_results.result_colors[res]) painter.setPen(pen) num_points = len(res.points) for i in range(0, num_points): i_n = i + 1 line_from = toqp(res.points[i]) line_from += self.crop.topLeft() line_to = toqp( res.points[i_n] if i_n < num_points else res.points[0]) line_to += self.crop.topLeft() painter.drawLine(line_from, line_to) # Draw the QR code data # Note that we reset the world transform to only the scaled transform # because otherwise the text could be flipped. We only use transform_flip # to map the center point of the result. painter.setWorldTransform(transform, False) font_metrics = painter.fontMetrics() data_metrics = QSize(font_metrics.horizontalAdvance(res.data), font_metrics.capHeight()) center_pos = toqp(res.center) center_pos += self.crop.topLeft() center_pos = transform_flip.map(center_pos) text_offset = QPoint(data_metrics.width(), data_metrics.height()) text_offset = text_offset / 2 text_offset.setX(-text_offset.x()) center_pos += text_offset padding = self.BG_RECT_PADDING bg_rect_pos = center_pos - QPoint(padding, data_metrics.height() + padding) bg_rect_size = data_metrics + (QSize(padding, padding) * 2) bg_rect = QRect(bg_rect_pos, bg_rect_size) bg_rect_path = QPainterPath() radius = self.BG_RECT_CORNER_RADIUS bg_rect_path.addRoundedRect(QRectF(bg_rect), radius, radius, Qt.AbsoluteSize) painter.setPen(self.bg_rect_pen) painter.fillPath(bg_rect_path, self.bg_rect_fill) painter.drawPath(bg_rect_path) painter.setPen(self.text_pen) painter.drawText(center_pos, res.data)
def slideButton(self, painter: QtGui.QPainter): if self.width() > 0 and self.height() > 0: path = QtGui.QPainterPath() path2 = QtGui.QPainterPath() widthDistance = self.width() - self.height() buttonXOffsetModifier = (widthDistance / self.width()) / 10 alphaModifier = (1 / widthDistance) * buttonXOffsetModifier if self.isChecked(): new_alpha = self.buttonForegroundColor.alphaF() + alphaModifier if new_alpha > 1: new_alpha = 1 self.buttonForegroundColor.setAlphaF(new_alpha) self.buttonXOffset += buttonXOffsetModifier else: new_alpha = self.buttonForegroundColor.alphaF() - alphaModifier if new_alpha < 0: new_alpha = 0 self.buttonForegroundColor.setAlphaF(new_alpha) self.buttonXOffset -= buttonXOffsetModifier if self.buttonXOffset >= (widthDistance + self.ButtonPadding) or self.buttonXOffset <= self.ButtonPadding: if self.buttonXOffset > (widthDistance + self.ButtonPadding): self.buttonXOffset = widthDistance + self.ButtonPadding elif self.buttonXOffset < self.ButtonPadding: self.buttonXOffset = self.ButtonPadding self.isAnimationScheduled = False else: if not self.isCircular(): painter.fillRect(self.rect(), self.buttonBackgroundColor) painter.fillRect(self.rect(), self.buttonForegroundColor) painter.fillRect(QtCore.QRect( self.buttonXOffset, self.ButtonPadding, widthDistance - self.ButtonPadding * 2, self.height() - self.ButtonPadding * 2 ), self.buttonColor) elif self.isCircular(): path.addRoundedRect(QtCore.QRectF(self.rect()), self.RoundedRadius, self.height() // 2) painter.fillPath(path, self.buttonBackgroundColor) painter.fillPath(path, self.buttonForegroundColor) path2.addEllipse(QtCore.QRectF( self.buttonXOffset, self.ButtonPadding, self.height() - self.ButtonPadding * 2, self.height() - self.ButtonPadding * 2 )) painter.fillPath(path2, self.buttonColor) painter.drawPath(path)
def paintEvent(self, event): painter = QPainter(self) width = self.width() height = self.height() if DEBUG: painter.fillRect(0, 0, width, height, Qt.blue) else: painter.fillRect(event.rect(), self.plot.canvas_color) y_min_scale = self.plot.y_scale.value_min y_max_scale = self.plot.y_scale.value_max factor_x = width / self.plot.x_diff factor_y = (height - CURVE_HEIGHT_COMPENSATION) / max(y_max_scale - y_min_scale, EPSILON) if self.plot.x_min != None and self.plot.x_max != None: x_min = self.plot.x_min x_max = self.plot.x_max if self.plot.curve_start == 'left': curve_x_offset = 0 else: curve_x_offset = round((self.plot.x_diff - (x_max - x_min)) * factor_x) transform = QTransform() transform.translate(curve_x_offset, height - CURVE_Y_OFFSET_COMPENSATION) transform.scale(factor_x, -factor_y) transform.translate(-x_min, -y_min_scale) self.plot.partial_update_width = math.ceil(transform.map(QLineF(0, 0, 1.5, 0)).length()) inverted_event_rect = transform.inverted()[0].mapRect(QRectF(event.rect())) painter.save() painter.setTransform(transform) pen = QPen() pen.setCosmetic(True) pen.setWidth(0) painter.setPen(pen) if False and self.plot.curves_visible[0]: # Currently unused support for bar graphs. # If we need this later on we should add an option to the # PlotWidget for it. # I tested this for the Sound Pressure Level Bricklet and it works, # but it didnt't look good. curve_x = self.plot.curves_x[0] curve_y = self.plot.curves_y[0] t = time.time() if self.max_points == None: self.max_points = [] for y in curve_y: self.max_points.append((t, y)) else: for i in range(len(curve_y)): if (curve_y[i] > self.max_points[i][1]) or ((t - self.max_points[i][0]) > 5): self.max_points[i] = (t, curve_y[i]) for i in range(len(self.plot.curves_x[0])): pen.setColor(self.plot.curve_configs[0].color) painter.setPen(pen) painter.drawLine(QPoint(curve_x[i], 0), QPoint(curve_x[i], curve_y[i])) pen.setColor(Qt.white) painter.setPen(pen) painter.drawLine(QPoint(curve_x[i], curve_y[i]), QPoint(curve_x[i], y_max_scale)) pen.setColor(Qt.darkGreen) painter.setPen(pen) painter.drawPoint(QPoint(curve_x[i], self.max_points[i][1])) else: for c in range(len(self.plot.curves_x)): if not self.plot.curves_visible[c]: continue curve_x = self.plot.curves_x[c] curve_y = self.plot.curves_y[c] curve_jump = self.plot.curves_jump[c] path = QPainterPath() lineTo = path.lineTo moveTo = path.moveTo start = max(min(bisect.bisect_left(curve_x, inverted_event_rect.left()), len(curve_x) - 1) - 1, 0) if start >= len(curve_x): continue moveTo(curve_x[start], curve_y[start]) for i in range(start + 1, len(curve_x)): if curve_jump[i]: curve_x_diff_half = (curve_x[i] - curve_x[i - 1]) / 2 lineTo(curve_x[i - 1] + curve_x_diff_half, curve_y[i - 1]) moveTo(curve_x[i] - curve_x_diff_half, curve_y[i]) lineTo(curve_x[i], curve_y[i]) pen.setColor(self.plot.curve_configs[c].color) painter.setPen(pen) painter.drawPath(path) painter.restore()
def createArrowBackground(self, transform): scaledRect = transform.mapRect( QRect(0, 0, self.logicalSize.width(), self.logicalSize.height())) image = QImage(scaledRect.width(), scaledRect.height(), QImage.Format_ARGB32_Premultiplied) image.fill(QColor(0, 0, 0, 0).rgba()) painter = QPainter(image) painter.setRenderHint(QPainter.SmoothPixmapTransform) painter.setRenderHint(QPainter.Antialiasing) painter.setPen(Qt.NoPen) if Colors.useEightBitPalette: painter.setPen(QColor(120, 120, 120)) if self.pressed: painter.setBrush(QColor(60, 60, 60)) elif self.highlighted: painter.setBrush(QColor(100, 100, 100)) else: painter.setBrush(QColor(80, 80, 80)) else: outlinebrush = QLinearGradient(0, 0, 0, scaledRect.height()) brush = QLinearGradient(0, 0, 0, scaledRect.height()) brush.setSpread(QLinearGradient.PadSpread) highlight = QColor(255, 255, 255, 70) shadow = QColor(0, 0, 0, 70) sunken = QColor(220, 220, 220, 30) normal1 = QColor(200, 170, 160, 50) normal2 = QColor(50, 10, 0, 50) if self.pressed: outlinebrush.setColorAt(0, shadow) outlinebrush.setColorAt(1, highlight) brush.setColorAt(0, sunken) painter.setPen(Qt.NoPen) else: outlinebrush.setColorAt(1, shadow) outlinebrush.setColorAt(0, highlight) brush.setColorAt(0, normal1) if not self.highlighted: brush.setColorAt(1, normal2) painter.setPen(QPen(outlinebrush, 1)) painter.setBrush(brush) painter.drawRect(0, 0, scaledRect.width(), scaledRect.height()) xOff = scaledRect.width() / 2 yOff = scaledRect.height() / 2 sizex = 3.0 * transform.m11() sizey = 1.5 * transform.m22() if self.type == TextButton.UP: sizey *= -1 path = QPainterPath() path.moveTo(xOff, yOff + (5 * sizey)) path.lineTo(xOff - (4 * sizex), yOff - (3 * sizey)) path.lineTo(xOff + (4 * sizex), yOff - (3 * sizey)) path.lineTo(xOff, yOff + (5 * sizey)) painter.drawPath(path) return image
def export_new_dataset(self, map, tile_size, step, output_folder): # if the dataset folder already had DL subfolder than delete them output_folder_training = os.path.join(output_folder, "training") output_folder_validation = os.path.join(output_folder, "validation") output_folder_test = os.path.join(output_folder, "test") if os.path.exists(output_folder_training): shutil.rmtree(output_folder_training, ignore_errors=True) if os.path.exists(output_folder_validation): shutil.rmtree(output_folder_validation, ignore_errors=True) if os.path.exists(output_folder_test): shutil.rmtree(output_folder_test, ignore_errors=True) # create DL folders os.mkdir(output_folder_training) output_images_training = os.path.join(output_folder_training, "images") output_labels_training = os.path.join(output_folder_training, "labels") os.mkdir(output_images_training) os.mkdir(output_labels_training) os.mkdir(output_folder_validation) output_images_validation = os.path.join(output_folder_validation, "images") output_labels_validation = os.path.join(output_folder_validation, "labels") os.mkdir(output_images_validation) os.mkdir(output_labels_validation) os.mkdir(output_folder_test) output_images_test = os.path.join(output_folder_test, "images") output_labels_test = os.path.join(output_folder_test, "labels") os.mkdir(output_images_test) os.mkdir(output_labels_test) ##### CREATE LABEL IMAGE # create a black canvas of the same size of your map w = map.width() h = map.height() labelimg = QImage(w, h, QImage.Format_RGB32) labelimg.fill(qRgb(0, 0, 0)) painter = QPainter(labelimg) for i, blob in enumerate(self.seg_blobs): if blob.qpath_gitem.isVisible(): if blob.qpath_gitem.isVisible(): if blob.class_name == "Empty": rgb = qRgb(255, 255, 255) else: class_color = self.labels_info[blob.class_name] rgb = qRgb(class_color[0], class_color[1], class_color[2]) painter.setBrush(QBrush(QColor(rgb))) painter.drawPath(blob.qpath_gitem.path()) painter.end() ##### TILING h1 = h * 0.65 h2 = h * 0.85 # tiles within the height [0..h1] are used for the training # tiles within the height [h1..h2] are used for the validation # the other tiles are used for the test tile_cols = int((w + tile_size) / step) tile_rows = int((h + tile_size) / step) deltaW = int(tile_size / 2) + 1 deltaH = int(tile_size / 2) + 1 for row in range(tile_rows): for col in range(tile_cols): top = row * step - deltaH left = col * step - deltaW cropimg = utils.cropQImage(map, [top, left, tile_size, tile_size]) croplabel = utils.cropQImage(labelimg, [top, left, tile_size, tile_size]) filenameRGB = "" if top + tile_size < h1 - step: filenameRGB = os.path.join( output_images_training, "tile_" + str.format("{0:02d}", (row)) + "_" + str.format("{0:02d}", (col)) + ".png") filenameLabel = os.path.join( output_labels_training, "tile_" + str.format("{0:02d}", (row)) + "_" + str.format("{0:02d}", (col)) + ".png") elif top > h2 + step: filenameRGB = os.path.join( output_images_test, "tile_" + str.format("{0:02d}", (row)) + "_" + str.format("{0:02d}", (col)) + ".png") filenameLabel = os.path.join( output_labels_test, "tile_" + str.format("{0:02d}", (row)) + "_" + str.format("{0:02d}", (col)) + ".png") elif top + tile_size >= h1 + step and top <= h2 - step: filenameRGB = os.path.join( output_images_validation, "tile_" + str.format("{0:02d}", (row)) + "_" + str.format("{0:02d}", (col)) + ".png") filenameLabel = os.path.join( output_labels_validation, "tile_" + str.format("{0:02d}", (row)) + "_" + str.format("{0:02d}", (col)) + ".png") print(filenameRGB) print(filenameLabel) if filenameRGB != "": cropimg.save(filenameRGB) croplabel.save(filenameLabel)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setupUi(self) points = [(0, 0), (-6.6572836, 4.361927), (-9.2177166, 7.340494), (-2.2137959, 2.5753221), (-4.66601534, 6.1499951), (-4.81185534, 9.5429151), (-0.070593, 1.642318), (1.35985124, 3.843193), (2.70403794, 4.203864), (3.9058194, 1.048006), (7.699405, -2.258071), (11.636839, -2.220768), (4.653883, 0.04409), (8.669976, 4.329893), (13.502289, 2.931417), (7.08685, -2.050941), (17.480776, -9.315182), (14.745917, -16.1672141), (-0.308121, -0.771981), (-2.309601, -0.355324), (-2.309601, -0.355324)] points = [QPointF(*p) * 10 for p in points] path = QPainterPath() ref_p = QPointF(14.1681, 0.2321) * 10 path.moveTo(ref_p) for i in range(0, len(points) - 2, 3): ep = points[i + 2] + ref_p c1 = points[i] + ref_p c2 = points[i + 1] + ref_p path.cubicTo(c1, c2, ep) ref_p = ep rect = path.boundingRect().adjusted(-5, -5, 5, 5) pen_styles = { k: getattr(Qt, k) for k in dir(Qt) if isinstance(getattr(Qt, k), Qt.PenStyle) } for name, style in sorted(pen_styles.items(), key=lambda x: x[1]): if "PenStyle" in name or "Custom" in name or "NoPen" in name: continue item = QListWidgetItem(name) item.setData(PenParametersDialog.PenStyleRole, style) pixmap = QPixmap(int(rect.width()), int(rect.height())) pixmap.fill(Qt.transparent) painter = QPainter() painter.begin(pixmap) painter.setPen(QPen(Qt.darkGreen, 10, style)) painter.drawPath(path) painter.end() item.setIcon(QIcon(pixmap)) self.lstPenStyles.addItem(item) colors = { k: getattr(Qt, k) for k in dir(Qt) if isinstance(getattr(Qt, k), Qt.GlobalColor) } for name, color in sorted(colors.items(), key=lambda x: x[1]): if "color" in name or name == "transparent": continue item = QListWidgetItem(name) item.setData(PenParametersDialog.ColorRole, color) pixmap = QPixmap(100, 25) painter = QPainter() painter.begin(pixmap) painter.setBrush(color) painter.drawRect(pixmap.rect()) painter.end() item.setIcon(QIcon(pixmap)) self.lstPenColors.addItem(item)
def overlay_marks(self, img, is_cseed=False, calibration_sheet=False): border_color = Qt.white base_img = QImage(self.f_size.width(),self.f_size.height(), QImage.Format_ARGB32) base_img.fill(border_color) img = QImage(img) painter = QPainter() painter.begin(base_img) total_distance_h = round(base_img.width() / self.abstand_v) dist_v = round(total_distance_h) / 2 dist_h = round(total_distance_h) / 2 img = img.scaledToWidth(base_img.width() - (2 * (total_distance_h))) painter.drawImage(total_distance_h, total_distance_h, img) #frame around image pen = QPen(Qt.black, 2) painter.setPen(pen) #horz painter.drawLine(0, total_distance_h, base_img.width(), total_distance_h) painter.drawLine(0, base_img.height()-(total_distance_h), base_img.width(), base_img.height()-(total_distance_h)) #vert painter.drawLine(total_distance_h, 0, total_distance_h, base_img.height()) painter.drawLine(base_img.width()-(total_distance_h), 0, base_img.width()-(total_distance_h), base_img.height()) #border around img border_thick = 6 Rpath = QPainterPath() Rpath.addRect(QRectF((total_distance_h)+(border_thick/2), (total_distance_h)+(border_thick/2), base_img.width()-((total_distance_h)*2)-((border_thick)-1), (base_img.height()-((total_distance_h))*2)-((border_thick)-1))) pen = QPen(Qt.black, border_thick) pen.setJoinStyle (Qt.MiterJoin) painter.setPen(pen) painter.drawPath(Rpath) Bpath = QPainterPath() Bpath.addRect(QRectF((total_distance_h), (total_distance_h), base_img.width()-((total_distance_h)*2), (base_img.height()-((total_distance_h))*2))) pen = QPen(Qt.black, 1) painter.setPen(pen) painter.drawPath(Bpath) pen = QPen(Qt.black, 1) painter.setPen(pen) painter.drawLine(0, base_img.height()/2, total_distance_h, base_img.height()/2) painter.drawLine(base_img.width()/2, 0, base_img.width()/2, total_distance_h) painter.drawLine(base_img.width()-total_distance_h, base_img.height()/2, base_img.width(), base_img.height()/2) painter.drawLine(base_img.width()/2, base_img.height(), base_img.width()/2, base_img.height() - total_distance_h) #print code f_size = 37 QFontDatabase.addApplicationFont(os.path.join(os.path.dirname(__file__), 'DejaVuSansMono-Bold.ttf')) font = QFont("DejaVu Sans Mono", f_size-11, QFont.Bold) font.setPixelSize(35) painter.setFont(font) if not calibration_sheet: if is_cseed: #its a secret painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine)) painter.drawLine(0, dist_v, base_img.width(), dist_v) painter.drawLine(dist_h, 0, dist_h, base_img.height()) painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v)) painter.drawLine(base_img.width()-(dist_h), 0, base_img.width()-(dist_h), base_img.height()) painter.drawImage(((total_distance_h))+11, ((total_distance_h))+11, QImage(icon_path('electrumb.png')).scaledToWidth(2.1*(total_distance_h), Qt.SmoothTransformation)) painter.setPen(QPen(Qt.white, border_thick*8)) painter.drawLine(base_img.width()-((total_distance_h))-(border_thick*8)/2-(border_thick/2)-2, (base_img.height()-((total_distance_h)))-((border_thick*8)/2)-(border_thick/2)-2, base_img.width()-((total_distance_h))-(border_thick*8)/2-(border_thick/2)-2 - 77, (base_img.height()-((total_distance_h)))-((border_thick*8)/2)-(border_thick/2)-2) painter.setPen(QColor(0,0,0,255)) painter.drawText(QRect(0, base_img.height()-107, base_img.width()-total_distance_h - border_thick - 11, base_img.height()-total_distance_h - border_thick), Qt.AlignRight, self.versioned_seed.version + '_'+self.versioned_seed.checksum) painter.end() else: # revealer painter.setPen(QPen(border_color, 17)) painter.drawLine(0, dist_v, base_img.width(), dist_v) painter.drawLine(dist_h, 0, dist_h, base_img.height()) painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v)) painter.drawLine(base_img.width()-(dist_h), 0, base_img.width()-(dist_h), base_img.height()) painter.setPen(QPen(Qt.black, 2)) painter.drawLine(0, dist_v, base_img.width(), dist_v) painter.drawLine(dist_h, 0, dist_h, base_img.height()) painter.drawLine(0, base_img.height()-dist_v, base_img.width(), base_img.height()-(dist_v)) painter.drawLine(base_img.width()-(dist_h), 0, base_img.width()-(dist_h), base_img.height()) logo = QImage(icon_path('revealer_c.png')).scaledToWidth(1.3*(total_distance_h)) painter.drawImage((total_distance_h)+ (border_thick), ((total_distance_h))+ (border_thick), logo, Qt.SmoothTransformation) #frame around logo painter.setPen(QPen(Qt.black, border_thick)) painter.drawLine(total_distance_h+border_thick, total_distance_h+logo.height()+3*(border_thick/2), total_distance_h+logo.width()+border_thick, total_distance_h+logo.height()+3*(border_thick/2)) painter.drawLine(logo.width()+total_distance_h+3*(border_thick/2), total_distance_h+(border_thick), total_distance_h+logo.width()+3*(border_thick/2), total_distance_h+logo.height()+(border_thick)) #frame around code/qr qr_size = 179 painter.drawLine((base_img.width()-((total_distance_h))-(border_thick/2)-2)-qr_size, (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2, (base_img.width()/2+(total_distance_h/2)-border_thick-(border_thick*8)/2)-qr_size, (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2) painter.drawLine((base_img.width()/2+(total_distance_h/2)-border_thick-(border_thick*8)/2)-qr_size, (base_img.height()-((total_distance_h)))-((border_thick*8))-(border_thick/2)-2, base_img.width()/2 + (total_distance_h/2)-border_thick-(border_thick*8)/2-qr_size, ((base_img.height()-((total_distance_h)))-(border_thick/2)-2)) painter.setPen(QPen(Qt.white, border_thick * 8)) painter.drawLine( base_img.width() - ((total_distance_h)) - (border_thick * 8) / 2 - (border_thick / 2) - 2, (base_img.height() - ((total_distance_h))) - ((border_thick * 8) / 2) - (border_thick / 2) - 2, base_img.width() / 2 + (total_distance_h / 2) - border_thick - qr_size, (base_img.height() - ((total_distance_h))) - ((border_thick * 8) / 2) - (border_thick / 2) - 2) painter.setPen(QColor(0,0,0,255)) painter.drawText(QRect(((base_img.width()/2) +21)-qr_size, base_img.height()-107, base_img.width()-total_distance_h - border_thick -93, base_img.height()-total_distance_h - border_thick), Qt.AlignLeft, self.versioned_seed.get_ui_string_version_plus_seed()) painter.drawText(QRect(0, base_img.height()-107, base_img.width()-total_distance_h - border_thick -3 -qr_size, base_img.height()-total_distance_h - border_thick), Qt.AlignRight, self.versioned_seed.checksum) # draw qr code qr_qt = self.paintQR(self.versioned_seed.get_ui_string_version_plus_seed() + self.versioned_seed.checksum) target = QRectF(base_img.width()-65-qr_size, base_img.height()-65-qr_size, qr_size, qr_size ) painter.drawImage(target, qr_qt) painter.setPen(QPen(Qt.black, 4)) painter.drawLine(base_img.width()-65-qr_size, base_img.height()-65-qr_size, base_img.width() - 65 - qr_size, (base_img.height() - ((total_distance_h))) - ((border_thick * 8)) - (border_thick / 2) - 4 ) painter.drawLine(base_img.width()-65-qr_size, base_img.height()-65-qr_size, base_img.width() - 65, base_img.height()-65-qr_size ) painter.end() else: # calibration only painter.end() cal_img = QImage(self.f_size.width() + 100, self.f_size.height() + 100, QImage.Format_ARGB32) cal_img.fill(Qt.white) cal_painter = QPainter() cal_painter.begin(cal_img) cal_painter.drawImage(0,0, base_img) #black lines in the middle of border top left only cal_painter.setPen(QPen(Qt.black, 1, Qt.DashDotDotLine)) cal_painter.drawLine(0, dist_v, base_img.width(), dist_v) cal_painter.drawLine(dist_h, 0, dist_h, base_img.height()) pen = QPen(Qt.black, 2, Qt.DashDotDotLine) cal_painter.setPen(pen) n=15 cal_painter.setFont(QFont("DejaVu Sans Mono", 21, QFont.Bold)) for x in range(-n,n): #lines on bottom (vertical calibration) cal_painter.drawLine((((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)-13, x+2+base_img.height()-(dist_v), (((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)+13, x+2+base_img.height()-(dist_v)) num_pos = 9 if x > 9 : num_pos = 17 if x < 0 : num_pos = 20 if x < -9: num_pos = 27 cal_painter.drawText((((base_img.width())/(n*2)) *(x))+ (base_img.width()/2)-num_pos, 50+base_img.height()-(dist_v), str(x)) #lines on the right (horizontal calibrations) cal_painter.drawLine(x+2+(base_img.width()-(dist_h)), ((base_img.height()/(2*n)) *(x))+ (base_img.height()/n)+(base_img.height()/2)-13, x+2+(base_img.width()-(dist_h)), ((base_img.height()/(2*n)) *(x))+ (base_img.height()/n)+(base_img.height()/2)+13) cal_painter.drawText(30+(base_img.width()-(dist_h)), ((base_img.height()/(2*n)) *(x))+ (base_img.height()/2)+13, str(x)) cal_painter.end() base_img = cal_img return base_img
def paintEvent(self,event): #在窗口上绘图 painter=QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.TextAntialiasing) ##生成五角星的5个顶点的,假设原点在五角星中心 R=100.0 #半径 Pi=3.14159 #常数 deg=Pi*72.0/180 points=[QPoint(R,0), QPoint(R*math.cos(deg), -R*math.sin(deg)), QPoint(R*math.cos(2*deg), -R*math.sin(2*deg)), QPoint(R*math.cos(3*deg), -R*math.sin(3*deg)), QPoint(R*math.cos(4*deg), -R*math.sin(4*deg)) ] #元组数据 font=painter.font() font.setPointSize(14) font.setBold(False) painter.setFont(font) #设置画笔 pen=QPen() pen.setWidth(2) #线宽 pen.setColor(Qt.blue) #划线颜色 pen.setStyle(Qt.SolidLine) #线的类型,实线、虚线等 pen.setCapStyle(Qt.FlatCap) #线端点样式 pen.setJoinStyle(Qt.BevelJoin) #线的连接点样式 painter.setPen(pen) ##设置画刷 brush=QBrush() brush.setColor(Qt.yellow) #画刷颜色 brush.setStyle(Qt.SolidPattern) #填充样式 painter.setBrush(brush) ##设计绘制五角星的PainterPath,以便重复使用 starPath=QPainterPath() starPath.moveTo(points[0]) starPath.lineTo(points[2]) starPath.lineTo(points[4]) starPath.lineTo(points[1]) starPath.lineTo(points[3]) starPath.closeSubpath() #闭合路径,最后一个点与第一个点相连 starPath.addText(points[0],font,"0") #显示端点编号 starPath.addText(points[1],font,"1") starPath.addText(points[2],font,"2") starPath.addText(points[3],font,"3") starPath.addText(points[4],font,"4") ##绘图 painter.save() #保存坐标状态 painter.translate(100,120) painter.drawPath(starPath) #画星星 painter.drawText(0,0,"S1") painter.restore() #恢复坐标状态 painter.translate(300,120) #平移 painter.scale(0.8,0.8) #缩放 painter.rotate(90) #顺时针旋转 painter.drawPath(starPath) #画星星 painter.drawText(0,0,"S2") painter.resetTransform() #复位所有坐标变换 painter.translate(500,120) #平移 painter.rotate(-145) #逆时针旋转 painter.drawPath(starPath) #画星星 painter.drawText(0,0,"S3")
def paintEvent(self, event): super(WaterWidget, self).paintEvent(event) if self.minimum >= self.maximum: return if not self._updateTimer.isActive(): return # 正弦曲线公式 y = A * sin(ωx + φ) + k # 当前值所占百分比 percent = 1 - (self._value - self.minimum) / \ (self.maximum - self.minimum) # w表示周期,6为人为定义 w = 6 * self.waterDensity * math.pi / self.width() # A振幅 高度百分比,1/26为人为定义 A = self.height() * self.waterHeight * 1 / 26 # k 高度百分比 k = self.height() * percent # 波浪1 waterPath1 = QPainterPath() waterPath1.moveTo(0, self.height()) # 起点在左下角 # 波浪2 waterPath2 = QPainterPath() waterPath2.moveTo(0, self.height()) # 起点在左下角 # 偏移 self._offset += 0.6 if self._offset > self.width() / 2: self._offset = 0 for i in range(self.width() + 1): # 从x轴开始计算y轴点 y = A * math.sin(w * i + self._offset) + k waterPath1.lineTo(i, y) # 相对第一条需要进行错位 y = A * math.sin(w * i + self._offset + self.width() / 2 * A) + k waterPath2.lineTo(i, y) # 封闭两条波浪,形成一个 U形 上面加波浪的封闭区间 waterPath1.lineTo(self.width(), self.height()) waterPath1.lineTo(0, self.height()) waterPath2.lineTo(self.width(), self.height()) waterPath2.lineTo(0, self.height()) # 开始画路径 painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) # 设置没有画笔 painter.setPen(Qt.NoPen) # 波浪1 painter.save() painter.setBrush(self._waterBgColor) painter.drawPath(waterPath1) painter.restore() # 波浪2 painter.save() painter.setBrush(self._waterFgColor) painter.drawPath(waterPath2) painter.restore()
def paintEvent(self, event): painter = QPainter(self) metrics = self.fontMetrics() # tabs self._closeRects = {} self._tabsRects = {} painter.save() currentTab = self.currentTab() tabFillColor = QColor(240, 240, 240) hoverFillColor = QColor(253, 253, 255) textHeight = metrics.lineSpacing() left = 0 h = textHeight + topPadding * 2 for index, name in enumerate(self.tabs()): isHeroFirstTab = not index and self._heroFirstTab isClosable = not isHeroFirstTab textWidth = self._textWidth if not index: textWidth += self._firstOffset w = sidePadding * 2 + textWidth if isClosable: w += crossMargin + crossSize self._tabsRects[index] = (left, 0, w, h) # background textColor = QColor(72, 72, 72) if index == currentTab: fillColor = tabFillColor elif index == self._hoverTab: fillColor = hoverFillColor else: fillColor = None if fillColor is not None: painter.fillRect(0, 0, w, h, fillColor) # text painter.save() painter.translate(sidePadding, metrics.ascent() + topPadding) painter.setPen(textColor) painter.setRenderHint(QPainter.Antialiasing) painter.drawText( 0, 0, metrics.elidedText(name, Qt.ElideRight, textWidth)) # cross if isClosable: # 3px padding for click rect self._closeRects[index] = (left + textWidth + 2 * sidePadding - 3, metrics.ascent() + topPadding - crossSize - 3, crossSize + 6, crossSize + 6) if index == self._hoverClose: color = QColor(254, 28, 28) else: color = QColor(78, 78, 78) painter.setPen(color) pen = painter.pen() pen.setWidthF(1.5) painter.setPen(pen) painter.translate(textWidth + sidePadding, -crossSize) painter.setClipRect(0, 0, crossSize, crossSize) painter.scale(crossSize / 10, crossSize / 10) painter.drawPath(cross) painter.restore() # shift for the next tab shift = textWidth + 2 * sidePadding + spacing if isClosable: shift += crossMargin + crossSize painter.translate(shift, 0) left += shift painter.restore()
def paintEvent(self,event): global monster_data global dmg painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.drawPixmap(event.rect(),self.pixmap) if self.card is not None and self.card.ID is not 0: card = self.card # Draw card level at the bottom centered pen = QPen() if np.floor(card.lv) == monster_data[card.ID]['max_level']: lvstr = 'Lv.Max' brush = QBrush(QColor(252,232,131)) else: lvstr = 'Lv.%d' % np.floor(card.lv) brush = QBrush(Qt.white) path = QPainterPath() pen.setWidth(0); pen.setBrush(Qt.black) font = QFont() font.setPointSize(11) font.setWeight(QFont.Black) path.addText(event.rect().x(),event.rect().y()+48,font,lvstr) rect = path.boundingRect() target = (event.rect().x()+event.rect().width())/2 # center the rect in event.rect() path.translate(target-rect.center().x(), 0) painter.setPen(pen) painter.setBrush(QBrush(Qt.black)) painter.drawPath(path.translated(.5,.5)) painter.setPen(pen) painter.setBrush(brush) painter.drawPath(path) # Draw +eggs at the top right eggs = card.plus_atk+card.plus_hp+card.plus_rcv if eggs > 0: eggstr = '+%d' % eggs pen.setBrush(Qt.yellow) brush = QBrush(Qt.yellow) path = QPainterPath() pen.setWidth(0) pen.setBrush(Qt.black) font = QFont() font.setPointSize(11) font.setWeight(QFont.Black) path.addText(event.rect().x(),event.rect().y()+12,font,eggstr) path.translate(50-path.boundingRect().right()-3,0) #painter.setFont(font) painter.setPen(pen) painter.setBrush(QBrush(Qt.black)) painter.drawPath(path.translated(.5,.5)) painter.setPen(pen) painter.setBrush(brush) painter.drawPath(path) #painter.drawText(event.rect().adjusted(0,0,0,0),Qt.AlignRight, eggstr) # Draw awakenings at the top left in a green circle if card.current_awakening > 0: path = QPainterPath() rect = QRectF(event.rect()).adjusted(4,4,-36,-36) path.addEllipse(rect) painter.setBrush(QBrush(QColor(34,139,34))) pen.setBrush(Qt.white) pen.setWidth(1) painter.setPen(pen) painter.drawPath(path) path = QPainterPath() font.setPointSize(9) awkstr = ('%d' % card.current_awakening if card.current_awakening < card.max_awakening else '★') path.addText(rect.x(),rect.bottom(),font,awkstr) br = path.boundingRect() path.translate(rect.center().x()-br.center().x(), rect.center().y()-br.center().y()) pen.setBrush(QColor(0,0,0,0)) pen.setWidth(0) painter.setPen(pen) painter.setBrush(QBrush(Qt.yellow)) painter.drawPath(path) # Draw main attack damage #print(self.main_attack) if self.main_attack > 0: matkstr = '%d' % self.main_attack painter.setBrush(QBrush(COLORS[self.card.element[0]])) path = QPainterPath() font = QFont() font.setFamily('Helvetica') font.setWeight(QFont.Black) #font.setStretch(25) font.setPointSize(13) path.addText(rect.x(),rect.bottom(),font,matkstr) rect = QRectF(event.rect()) br = path.boundingRect() path.translate(rect.center().x()-br.center().x(), rect.center().y()-br.bottom()-1) # pen.setBrush(Qt.black) pen.setWidthF(.75) painter.setPen(pen) painter.drawPath(path) # Draw sub attack damage #print(self.main_attack) if self.sub_attack > 0: satkstr = '%d' % self.sub_attack painter.setBrush(QBrush(COLORS[self.card.element[1]])) path = QPainterPath() font = QFont() font.setFamily('Helvetica') font.setWeight(QFont.Black) #font.setStretch(25) font.setPointSize(12) path.addText(rect.x(),rect.bottom(),font,satkstr) rect = QRectF(event.rect()) br = path.boundingRect() path.translate(rect.center().x()-br.center().x(), rect.center().y()-br.top()+1) # pen.setBrush(Qt.black) pen.setWidthF(.75) painter.setPen(pen) painter.drawPath(path)
def paintEvent(self, event): qp = QPainter() qp.begin(self) qp.setRenderHint(QPainter.Antialiasing) # for regular keycaps regular_pen = qp.pen() regular_pen.setColor(QApplication.palette().color(QPalette.ButtonText)) qp.setPen(regular_pen) regular_brush = QBrush() regular_brush.setColor(QApplication.palette().color(QPalette.Button)) regular_brush.setStyle(Qt.SolidPattern) qp.setBrush(regular_brush) # for currently selected keycap active_pen = qp.pen() active_pen.setColor(QApplication.palette().color(QPalette.HighlightedText)) active_brush = QBrush() active_brush.setColor(QApplication.palette().color(QPalette.Highlight)) active_brush.setStyle(Qt.SolidPattern) mask_font = qp.font() mask_font.setPointSize(mask_font.pointSize() * 0.8) for idx, key in enumerate(self.widgets): qp.save() qp.scale(self.scale, self.scale) qp.translate(key.shift_x, key.shift_y) qp.translate(key.rotation_x, key.rotation_y) qp.rotate(key.rotation_angle) qp.translate(-key.rotation_x, -key.rotation_y) active = key.active or (self.active_key == key and not self.active_mask) if active: qp.setPen(active_pen) qp.setBrush(active_brush) # draw the keycap qp.drawPath(key.draw_path) qp.strokePath(key.draw_path2, regular_pen) # if this is a mask key, draw the inner key if key.masked: qp.setFont(mask_font) qp.save() if key.color is not None and not active: qp.setPen(key.color) qp.drawText(key.nonmask_rect, Qt.AlignCenter, key.text) qp.restore() if self.active_key == key and self.active_mask: qp.setPen(active_pen) qp.setBrush(active_brush) qp.drawRect(key.mask_rect) if key.color is not None and not active: qp.setPen(key.color) qp.drawText(key.mask_rect, Qt.AlignCenter, key.mask_text) else: # draw the legend if key.color is not None and not active: qp.setPen(key.color) qp.drawText(key.rect, Qt.AlignCenter, key.text) qp.restore() qp.end()
class PixelWidget(QWidget): def __init__(self, form, bufhandler): super(PixelWidget, self).__init__() self.form = form self.set_zoom(10) self.maxPixelsPerLine = 64 self.maxPixelsTotal = 0 self.prev_mouse_y = 0 self.key = None self.buffers = None self.offs = 0 self.base = 0 self.fm = None self.filter_idx = 0 self.mouseOffs = 0 self.sync = True self.bh = bufhandler self.mouse_abs_x = 0 self.mouse_abs_y = 0 self.elemX = 0 self.elemY = 0 self.rect_x = 0 self.rect_x_width = 0 self.lock_width = False self.lock_sync = False self.link_pixel = True self.highlight_cursor = False self.render_data = True self.cur_formatter_idx = 0 self.max_formatters = 2 self.setMouseTracking(True) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.sh = SignalHandler() self.statechanged = self.sh.pw_statechanged self.next_filter = self.sh.pw_next_filter self.prev_filter = self.sh.pw_prev_filter self.qp = QPainter() self.show() def paintEvent(self, event): if not self.fm: return # set leftmost x-coordinate of graph zoom_level = self.get_zoom() self.rect_x_width = self.get_width() * zoom_level self.rect_x = (self.rect().width() / 2) - (self.rect_x_width / 2) self.qp.begin(self) # what is a good default font for OSX/Linux? self.qp.setFont(QFont(FONT_DEFAULT)) # fill background self.qp.fillRect(self.rect(), Qt.black) content_addr = content_size = None if self.fm.support_selection: selected, start, end = ida_kernwin.read_range_selection(None) if selected: content_addr = start #min(start, end) content_size = end - start #max(start, end) - content_addr # use colorfilter to render image img = self.render_image(addr=content_addr, buf_size=content_size) if img: """ if zoom_level > 6: opacity = self.qp.opacity() full_opacity_zoom = 40.0 cur_opacity = (1.0 - (full_opacity_zoom - float(min(zoom_level-1, full_opacity_zoom)))/full_opacity_zoom) self.qp.setOpacity(1.0-cur_opacity) """ # draw image self.qp.drawImage( QRect( QPoint(self.rect_x, 0), QPoint(self.rect_x + self.get_width() * zoom_level, (self.get_pixels_total() / self.get_width()) * zoom_level)), img) # TODO: pen color contrast # TODO: data export: render data # TODO: default fonts / OS? # TODO: optimization # FIXME: there's a bug with gaps/unmapped buffers if self.render_data and not self.fm.disable_data and zoom_level > 6: self.qp.setPen(QColor(Qt.white)) fontsize = self.qp.font().pointSize() font = self.qp.font() font.setPointSize(zoom_level / 3) self.qp.setFont(font) opacity = self.qp.opacity() full_opacity_zoom = 28 cur_opacity = ( 1.0 - (full_opacity_zoom - float(min(zoom_level - 1, full_opacity_zoom))) / full_opacity_zoom) self.qp.setOpacity(cur_opacity) m = self.qp.fontMetrics() x = y = 0 num_pixels_per_row = self.get_width() if self.cur_formatter_idx == 0: sample = "%c" % (ord('X')) cwidth = m.width(sample) cheight = m.height() for mapped, buf in self.buffers: for i in range(len(buf)): if mapped: b = ord(buf[i]) data = "%c" % (chr(b) if b in range( 0x20, 0x7E) else ".") self.qp.drawStaticText( self.rect_x + x * zoom_level + (zoom_level - cwidth) / 2, y * zoom_level + (zoom_level - cheight) / 2, QStaticText(data)) x = (i + 1) % num_pixels_per_row if not x: y = y + 1 elif self.cur_formatter_idx == 1: sample = "%02X" % (ord('X')) cwidth = m.width(sample) cheight = m.height() for mapped, buf in self.buffers: for i in range(len(buf)): if mapped: data = "%02X" % ord(buf[i]) self.qp.drawStaticText( self.rect_x + x * zoom_level + (zoom_level - cwidth) / 2, y * zoom_level + (zoom_level - cheight) / 2, QStaticText(data)) x = (i + 1) % num_pixels_per_row if not x: y = y + 1 self.qp.setOpacity(opacity) font.setPointSize(fontsize) self.qp.setFont(font) if self.show_address_range and self.fm.link_pixel: self.render_slider(addr=content_addr, buf_size=content_size) # get and draw annotations and pointers annotations = self.fm.on_get_annotations( content_addr if content_addr else self.get_address(), self.get_pixels_total(), self.mouseOffs) if annotations: self.render_annotations(annotations) self.qp.end() return def render_slider(self, addr=None, buf_size=None): if addr is None or buf_size is None: addr = self.base + self.offs buf_size = self.get_pixels_total() lowest_ea = ida_idaapi.get_inf_structure().get_minEA() highest_ea = ida_idaapi.get_inf_structure().get_maxEA() start_offs = addr - lowest_ea addr_space = highest_ea - lowest_ea perc_s = float(start_offs) / float(addr_space) perc_e = float(start_offs + buf_size) / float(addr_space) bar_width = 20 spaces_bar = 5 bar_x = self.rect_x - spaces_bar - bar_width bar_y = 5 bar_height = self.rect().height() - 2 * bar_y self.qp.fillRect(bar_x, bar_y, bar_width, bar_height, QColor(0x191919)) slider_offs_s = int(round(perc_s * bar_height)) slider_offs_e = int(round(perc_e * bar_height)) spaces_slider = 1 slider_x = bar_x + spaces_slider slider_y = bar_y + slider_offs_s slider_width = bar_width - 2 * spaces_slider # limit slider height to bar_height slider_height = max( min(slider_offs_e - slider_offs_s, bar_height - (slider_y - bar_y)), 4) self.qp.fillRect(slider_x, slider_y, slider_width, slider_height, QColor(0x404040)) # draw addresses top = '%X:' % self.get_address() bottom = '%X' % (self.get_address() + ((self.get_pixels_total() / self.get_width()) - 1) * self.get_width()) self.qp.setPen(QColor(0x808080)) self.qp.drawText( self.rect_x - self.qp.fontMetrics().width(top) - bar_width - 2 * spaces_bar, self.qp.fontMetrics().height(), top) self.qp.drawText( self.rect_x - self.qp.fontMetrics().width(bottom) - bar_width - 2 * spaces_bar, self.rect().height() - self.qp.fontMetrics().height() / 2, bottom) return def render_image(self, addr=None, buf_size=None, cursor=True): size = self.size() self.maxPixelsTotal = self.get_width() * (size.height() / self.pixelSize) if addr is None or buf_size is None: addr = self.base + self.offs buf_size = self.get_pixels_total() self.buffers = self.bh.get_buffers(addr, buf_size) img = QImage(self.get_width(), size.height() / self.pixelSize, QImage.Format_RGB32) pixels = self.fm.on_process_buffer(self.buffers, addr, self.get_pixels_total(), self.mouseOffs) x = y = 0 # transparency effect for unmapped bytes transparency_dark = [qRgb(0x2F, 0x4F, 0x4F), qRgb(0x00, 0x00, 0x00)] transparency_err = [qRgb(0x7F, 0x00, 0x00), qRgb(0x33, 0x00, 0x00)] for mapped, pix in pixels: if not mapped: if pix is None: pix = transparency_dark[(x & 2 != 0) ^ (y & 2 != 0)] img.setPixel(x, y, pix) x = (x + 1) % self.get_width() if not x: y = y + 1 if len(pixels) != self.get_pixels_total(): for i in xrange(self.get_pixels_total() - len(pixels)): pix = transparency_err[(x & 2 != 0) ^ (y & 2 != 0)] img.setPixel(x, y, pix) x = (x + 1) % self.get_width() if not x: y = y + 1 if ((cursor and self.fm.highlight_cursor) and self.mouse_abs_x >= self.rect_x and self.mouse_abs_x < self.rect_x + self.rect_x_width): coords = self.get_coords_by_address(self.get_cursor_address()) if coords: x, y = coords else: x = self.get_elem_x() y = self.get_elem_y() p = QPoint(x, y) img.setPixel(p, ~(img.pixelColor(p)).rgb()) return img def render_annotations(self, annotations=[]): a_offs = 20 base_x = self.rect_x + self.get_width() * self.pixelSize + a_offs + 10 base_y = self.qp.fontMetrics().height() offs_x = 5 offs_y = base_y for coords, arr_color, ann, txt_color in annotations: # draw arrow (experimental / WIP) self.qp.setPen( QColor(Qt.white if txt_color is None else txt_color)) self.qp.drawText(base_x + 10, (base_y + offs_y) / 2, ann) target_x = target_y = None if coords: if isinstance(coords, tuple): target_x, target_y = coords else: ptr = self.get_coords_by_address(coords) if ptr: target_x, target_y = ptr if target_x is not None and target_y is not None: target_x *= self.get_zoom() target_y *= self.get_zoom() self.qp.setPen( QColor(Qt.white if arr_color is None else arr_color)) path = QPainterPath() path.moveTo(base_x + offs_x, (base_y + offs_y) / 2 - base_y / 2) path.lineTo(base_x + offs_x - 4 - a_offs, (base_y + offs_y) / 2 - base_y / 2) # left path.lineTo(base_x + offs_x - 4 - a_offs, ((target_y / 10) * 9) + self.get_zoom() / 2) # down path.lineTo(self.rect_x + target_x + self.get_zoom() / 2, ((target_y / 10) * 9) + self.get_zoom() / 2) # left path.lineTo(self.rect_x + target_x + self.get_zoom() / 2, target_y + self.get_zoom() / 2) # down a_offs = max(a_offs - 2, 0) self.qp.drawPath(path) else: if not isinstance(coords, tuple): direction = self.get_target_direction(coords) if direction: self.qp.setPen( QColor(Qt.white if arr_color is None else arr_color)) m = self.qp.fontMetrics() dirhint = ['', '<<', '>>'][direction] cwidth = m.width("%s" % (dirhint)) self.qp.drawText(base_x - cwidth, (base_y + offs_y) / 2, dirhint) offs_y += 2 * base_y + 5 return # functions that can be called by filters # must no be called from within on_process_buffer() def on_filter_request_update(self, ea=None, center=True): if not ea: self.repaint() else: curea = self.get_address() if ea < curea or ea >= curea + self.get_pixels_total(): # TODO: verify that ea is valid after following operation if center: ea -= self.get_pixels_total() / 2 self.set_addr(ea) else: self.repaint() def on_filter_update_zoom(self, zoom): self.set_zoom(zoom) return def on_filter_update_zoom_delta(self, delta): self.set_zoom_delta(delta) return # end of functions that can be called by filters def show_help(self): ida_kernwin.info("%s" % PLUGIN_HELP) def keyPressEvent(self, event): if self.key is None: self.key = event.key() return def keyReleaseEvent(self, event): update = False key = event.key() modifiers = event.modifiers() shift_pressed = ((modifiers & Qt.ShiftModifier) == Qt.ShiftModifier) ctrl_pressed = ((modifiers & Qt.ControlModifier) == Qt.ControlModifier) if key == Qt.Key_F1 and ctrl_pressed: self.show_help() elif key == Qt.Key_G: addr = ask_addr(self.base + self.offs, 'Jump to address') if addr is not None: if self.sync: ida_kernwin.jumpto(addr) else: minea = ida_idaapi.get_inf_structure().get_minEA() maxea = ida_idaapi.get_inf_structure().get_maxEA() dst = min(max(addr, minea), maxea) self.set_addr(dst) elif key == Qt.Key_S: if not self.fm.lock_sync: self.set_sync_state(not self.get_sync_state()) update = True elif key == Qt.Key_T: self.render_data = not self.render_data self.repaint() elif key == Qt.Key_D: self.cur_formatter_idx = (self.cur_formatter_idx + 1) % self.max_formatters self.repaint() elif key == Qt.Key_N: self.next_filter.emit() elif key == Qt.Key_B: self.prev_filter.emit() elif key == Qt.Key_F2: hlp = self.fm.help if hlp is None: hlp = 'Help unavailable' ida_kernwin.info('%s\n\n' % hlp) elif key == Qt.Key_F12: img = self.render_image(cursor=False) img = img.scaled(img.width() * self.pixelSize, img.height() * self.pixelSize, Qt.KeepAspectRatio, Qt.FastTransformation) done = False i = 0 while not done: fname = 'IDACyber_%04d.bmp' % i if not os.path.isfile(fname): if img.save(fname): ida_kernwin.msg('File exported to %s\n' % fname) else: ida_kernwin.warning( 'Error exporting screenshot to %s.' % fname) done = True i += 1 if i > 40: ida_kernwin.warning('Aborted. Error exporting screenshot.') break elif key == Qt.Key_PageDown: self.set_offset_delta(-self.get_pixels_total()) update = True elif key == Qt.Key_PageUp: self.set_offset_delta(self.get_pixels_total()) update = True elif key == Qt.Key_Down: if shift_pressed: self.set_offset_delta(-1) else: self.set_offset_delta(-self.get_width()) update = True elif key == Qt.Key_Up: if shift_pressed: self.set_offset_delta(1) else: self.set_offset_delta(self.get_width()) update = True elif key == Qt.Key_Plus: if ctrl_pressed: self.set_zoom_delta(1) update = True elif key == Qt.Key_Minus: if ctrl_pressed: self.set_zoom_delta(-1) update = True self.key = None if update: if self.get_sync_state(): ida_kernwin.jumpto(self.base + self.offs) self.activateWindow() self.setFocus() self.statechanged.emit() self.repaint() return def mouseReleaseEvent(self, event): self.prev_mouse_y = event.pos().y() self.fm.on_mb_click(event, self.get_address(), self.get_pixels_total(), self.mouseOffs) if self.get_sync_state(): ida_kernwin.jumpto(self.base + self.offs) self.activateWindow() self.setFocus() self.statechanged.emit() return def mouseDoubleClickEvent(self, event): if self.link_pixel and event.button() == Qt.LeftButton: addr = self.base + self.offs + self._get_offs_by_pos(event.pos()) ida_kernwin.jumpto(addr) return def wheelEvent(self, event): delta = event.angleDelta().y() / 120 # zoom if self.key == Qt.Key_Control: self.set_zoom_delta(delta) # width elif self.key == Qt.Key_X: if not self.lock_width: self.set_width_delta(delta) # offset (fine) elif self.key == Qt.Key_Shift: self.set_offset_delta(delta) if self.get_sync_state(): ida_kernwin.jumpto(self.base + self.offs) self.activateWindow() self.setFocus() elif self.key == Qt.Key_H: if not self.lock_width: less = delta < 0 w = -8 if less else 8 self.set_width((self.get_width() & 0xFFFFFFF8) + w) # offset (coarse) else: self.set_offset_delta(delta * self.get_width()) if self.get_sync_state(): ida_kernwin.jumpto(self.base + self.offs) self.activateWindow() self.setFocus() self.statechanged.emit() self.repaint() return def mouseMoveEvent(self, event): x = event.pos().x() y = event.pos().y() within_graph = (x >= self.rect_x and x < self.rect_x + self.rect_x_width) if within_graph: if event.buttons() == Qt.NoButton: self._update_mouse_coords(event.pos()) self.mouseOffs = self._get_offs_by_pos(event.pos()) if self.link_pixel and self.highlight_cursor: highlight_item( ida_bytes.get_item_head(self.get_cursor_address())) elif self.highlight_cursor: unhighlight_item() self.setToolTip( self.fm.on_get_tooltip(self.get_address(), self.get_pixels_total(), self.mouseOffs)) # zoom elif self.key == Qt.Key_Control: self.set_zoom_delta(-1 if y > self.prev_mouse_y else 1) # width elif self.key == Qt.Key_X: if not self.lock_width: self.set_width_delta(-1 if y > self.prev_mouse_y else 1) elif self.key == Qt.Key_H: if not self.lock_width: less = y > self.prev_mouse_y delta = -16 if less else 16 self.set_width((self.get_width() & 0xFFFFFFF0) + delta) # scrolling (offset) elif y != self.prev_mouse_y: # offset (fine) delta = y - self.prev_mouse_y # offset (coarse) if self.key != Qt.Key_Shift: delta *= self.get_width() self.set_offset_delta(delta) self.prev_mouse_y = y self.x = x self.statechanged.emit() self.repaint() return def set_sync_state(self, sync): self.sync = sync def get_sync_state(self): return self.sync def get_filter_idx(self): return self.filter_idx def set_filter(self, fltobj, idx): if self.fm: self.fm.on_deactivate() if fltobj: self.fm = fltobj """load filter config""" self.set_sync_state(self.fm.sync) self.lock_width = self.fm.lock_width self.set_width(self.fm.width) self.lock_sync = self.fm.lock_sync self.show_address_range = self.fm.show_address_range # disabled for now # self.set_zoom(self.fm.zoom) self.link_pixel = self.fm.link_pixel self.highlight_cursor = self.fm.highlight_cursor self.statechanged.emit() """load filter config end""" self.fm.on_activate(idx) self.filter_idx = idx unhighlight_item() self.repaint() def set_addr(self, ea, new_cursor=None): base = self.bh.get_base(ea) self._set_base(base) self._set_offs(ea - base) if new_cursor: self.set_cursor_address(new_cursor) if self.highlight_cursor: highlight_item(ea) self.repaint() def get_zoom(self): return self.pixelSize def set_zoom(self, zoom): self.pixelSize = zoom def set_zoom_delta(self, dzoom): self.set_zoom(max(1, self.pixelSize + dzoom)) return def get_width(self): return self.maxPixelsPerLine def get_pixels_total(self): return self.maxPixelsTotal def get_address(self): return self.base + self.offs def get_cursor_address(self): return self.get_address() + self.mouseOffs def set_cursor_address(self, ea): self.mouseOffs = ea - self.get_address() def get_coords_by_address(self, address): base = self.get_address() # if address is visible in current window if address >= base and address < base + self.get_pixels_total(): offs = address - base x = offs % self.get_width() y = offs / (self.get_width()) return (x, y) return None def get_target_direction(self, address): base = self.get_address() # if address is visible in current window direction = None if address >= base and address < base + self.get_pixels_total(): direction = 0 elif address < base: direction = 1 else: direction = 2 return direction def set_width(self, width): self.maxPixelsPerLine = max(1, width) def set_width_delta(self, dwidth): self.maxPixelsPerLine = max(1, self.maxPixelsPerLine + dwidth) def set_offset_delta(self, doffs): newea = self.base + self.offs - doffs minea = ida_idaapi.get_inf_structure().get_minEA() maxea = ida_idaapi.get_inf_structure().get_maxEA() if doffs < 0: delta = doffs if newea < maxea else doffs - (maxea - newea) else: delta = doffs if newea >= minea else doffs - (minea - newea) self._set_offs(self.offs - delta) def _get_offs_by_pos(self, pos): elemX = self.get_elem_x() elemY = self.get_elem_y() offs = elemY * self.get_width() + elemX return offs def _update_mouse_coords(self, pos): x = pos.x() y = pos.y() self.mouse_abs_x = x self.mouse_abs_y = y self.elemX = max( 0, min((max(0, x - self.rect_x)) / self.pixelSize, self.get_width() - 1)) self.elemY = min(y / self.pixelSize, self.maxPixelsTotal / self.get_width() - 1) def get_elem_x(self): return self.elemX def get_elem_y(self): return self.elemY def _set_offs(self, offs): self.offs = offs def _set_base(self, ea): self.base = ea
def redraw(self, painter: QtGui.QPainter, vertical_offset: float = 0) -> None: # if the button is hidden then there is nothing to draw if self._state == ICWidgetState.Hidden: return # the size of the button is determined by the size of the widget tmp_width = painter.device().width() tmp_height = painter.device().height() # define the rectangle to draw the button rect = QtCore.QRectF(5, 5, tmp_width - 10, tmp_height - 10) # a linear gradient brush is used to fill the button brush = QtGui.QLinearGradient(rect.topRight(), rect.bottomRight()) # if the widget is transparent then the rect is drawn using background color if self._state == ICWidgetState.Transparent: brush.setColorAt(0, self.background_color) brush.setColorAt(1, self.background_color) else: brush.setColorAt(0, self._button_color_light) brush.setColorAt(1, self._button_color_dark) # set the brush painter.setBrush(brush) # define the pen that with rounded cap and join style if not self.in_focus: pen = QtGui.QPen() pen.setWidth(1) else: pen = QtGui.QPen(self.focus_color) pen.setWidth(3) pen.setCapStyle(Qt.RoundCap) pen.setJoinStyle(Qt.RoundJoin) # set the pen to use the brush and set the painter pen if not self.in_focus: pen.setBrush(brush) painter.setPen(pen) # define the path that needs to be drawn path = QtGui.QPainterPath() path.setFillRule(Qt.WindingFill) path.addRoundedRect(rect, 10, 10) painter.drawPath(path) # draw the text only if the button is visible if self._state in (ICWidgetState.VisibleEnabled, ICWidgetState.VisibleDisabled): # define the font for drawing fnt = painter.font() fnt.setBold(True) fnt.setPixelSize(self._text_size) painter.setFont(fnt) # select the font color based on if the button is enabled or not if self._state == ICWidgetState.VisibleEnabled: pen.setColor(self._text_color_enabled) else: pen.setColor(self._text_color_disabled) painter.setPen(pen) # draw the text rect = QtCore.QRectF(10, tmp_height / 2 - 0.5 * (self._text_size + 5) + vertical_offset, tmp_width - 20, self._text_size + 5) painter.drawText(rect, Qt.AlignCenter, str(self._name))
def paintEvent(self, event: QPaintEvent): _start = perf_counter() num = self.num_tabs selected = self.selected arrow_width = self._arrow_width height = self.height() width = self._button_width first_width = self._first_button_width button = self._button_path button_box = QRect(0, 0, width + arrow_width, height) first_box = QRect(0, 0, first_width + arrow_width, height) icon_area = QRect(arrow_width + 10, 0, max(48, width / 2), height) text_box = QRect(arrow_width, 0, width - arrow_width, height) text_flags = Qt.AlignCenter | Qt.AlignVCenter states = self.states painter = QPainter(self) region = event.region() painter.setClipRegion(region) #torender = self._tabs_within(event.region()) #print("regions:") #for rect in event.region().rects(): # print(" - ", rect) #painter.setPen(Qt.NoPen) painter.setPen( QPen(Qt.black, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) titleFont = painter.font() titleFont.setPointSizeF(14) titleFont.setBold(True) painter.setFont(titleFont) painter.translate(num * width + first_width, 0) if region.intersects(painter.transform().mapRect(button_box)): painter.setBrush(states[num].get_color(num == selected)) painter.drawPath(self._last_button_path) for i in reversed(range(num)): painter.translate(-width, 0) if not region.intersects(painter.transform().mapRect(button_box)): continue painter.setBrush(states[i].get_color(i == selected)) painter.drawPath(button) if states[i].state == State.ACTIVE: painter.save() painter.setPen(Qt.NoPen) gw = (width + self._arrow_width) * 2 gradient = QLinearGradient(0, 0, gw, 0) value = self._working_anim.value gradient.setColorAt(max(0.0, value - 0.2), QColor(255, 255, 255, 0)) gradient.setColorAt(value, QColor(255, 255, 255, 180)) gradient.setColorAt(min(1.0, value + 0.2), QColor(255, 255, 255, 0)) brush = QBrush(gradient) brush.setTransform(brush.transform().translate(-gw / 4, 0)) gradient_height = int(height * 0.2) painter.setBrush(brush) #painter.setClipRect(0, 0, width+self._arrow_width, gradient_height) #painter.drawPath(button) #painter.setClipRect(0, height-gradient_height, width+self._arrow_width, gradient_height) painter.drawPath(button) self._active_box = painter.transform().mapRect(button_box) painter.restore() #if states[i].icon: # states[i].icon.paint(painter, icon_area) text = states[i].text if text: _, _, short = text.rpartition('-') painter.drawText(text_box, text_flags, short.capitalize()) if region.intersects(first_box): painter.resetTransform() painter.setBrush(State.UNKNOWN.get_color(-1 == selected)) painter.drawPath(self._first_button_path) if self.is_running: icon = self.style().standardIcon(QStyle.SP_MediaStop) else: icon = self.style().standardIcon(QStyle.SP_MediaPlay) size = min(self._first_button_width, self.height()) * 0.8 painter.translate(5, (self.height() - size) / 2) icon.paint(painter, QRect(0, 0, size, size)) _end = perf_counter() if not self._paint_times: self._paint_times = times = [] else: times = self._paint_times times.append(_end - _start) if len(times) > 60: avg = sum(times) / len(times) * 1000000 print("Average render time %.2fns" % (avg, )) self._paint_times = None
class Renderer(QFrame): update_time_signal = pyqtSignal(int) pause_signal = pyqtSignal() def __init__(self, beatmap, replays, events, start_speed, paint_info, \ statistic_functions): super().__init__() self.setMinimumSize(GAMEPLAY_WIDTH + GAMEPLAY_PADDING_WIDTH * 2, GAMEPLAY_HEIGHT + GAMEPLAY_PADDING_HEIGHT * 2) self.beatmap = beatmap # list of timestamps to highlight the frames of in a different color self.events = events # whether to show some information about each player and their cursors self.should_paint_info = paint_info # functions to display info for in the visualizer self.statistic_functions = statistic_functions # whether we should paint the frametime graph self.paint_frametime = False self.painter = QPainter() self.scale = 1 self.x_offset = 0 self.y_offset = 0 # a map of QRect to Player, where the rectangle is the location of the # player's info on the screen. Updated every frame (even though it's # currently static except for the width, it may differ from frame to # frame in the future) self.player_info_positions = {} # players that have been disabled by the users and we don't want to # draw cursor movements for self.disabled_players = [] self.setMouseTracking(True) # hitobjs currently on screen self.hitobjs_to_draw = [] # we actually care about more than just the hitobjs on screen when # drawing error bar markers, so keep track of those hitobjs here. # this will (should) be a superset of ``hitobjs_to_draw`` self.hitobjs_to_draw_hits_for = [] # and we care about *less* than the hitobjs in hitobjs_to_draw when # drawing judgment indicators, since these disappear sooner. This will # be a subset of ``hitobjs_to_draw``. # TODO clean up this code, these lists shouldn't exist, instead hitobjs # should be marked with ``draw_judgment_indicators_for`` attributes # or something self.hitobjs_to_draw_judgment_indicators_for = [] self.use_hr = any(Mod.HR in replay.mods for replay in replays) self.use_ez = any(Mod.EZ in replay.mods for replay in replays) if beatmap: self.hit_objects = beatmap.hit_objects(hard_rock=self.use_hr, easy=self.use_ez) self.playback_end = self.get_hit_endtime(self.hit_objects[-1]) ar = beatmap.ar(hard_rock=self.use_hr, easy=self.use_ez) od = beatmap.od(hard_rock=self.use_hr, easy=self.use_ez) cs = beatmap.cs(hard_rock=self.use_hr, easy=self.use_ez) # see https://osu.ppy.sh/help/wiki/Beatmapping/Approach_rate if ar <= 5: self.preempt = 1200 + 600 * (5 - ar) / 5 self.fade_in = 800 + 400 * (5 - ar) / 5 else: self.preempt = 1200 - 750 * (ar - 5) / 5 self.fade_in = 800 - 500 * (ar - 5) / 5 (hitwindow_50, hitwindow_100, hitwindow_300) = hitwindows(od) self.hitwindow_50 = hitwindow_50 self.hitwindow_100 = hitwindow_100 self.hitwindow_300 = hitwindow_300 # how much to scale our error bar by from its 'standard' size, where # each ms of error is a pixel. self.error_bar_width_factor = ERROR_BAR_WIDTH / (hitwindow_50 * 2) self.hitcircle_radius = hitradius(cs) # loading stuff self.is_loading = True self.num_hitobjects = len(self.hit_objects) # not fully accurate, but good enough self.num_sliders = self.num_hitobjects self.sliders_current = 0 self.thread = threading.Thread(target=self.process_sliders) self.thread.start() self.has_beatmap = True else: self.playback_end = 0 self.is_loading = False self.has_beatmap = False # if this is nonnull, when we finish loading sliders we will seek to # this position. Set in ``seek_to`` if it is called when we're loading self.seek_to_when_loaded = None # whether the previous frame was a loading frame or not, used to # determine when we came out of a loading state self.previously_loading = False # replay stuff self.num_replays = len(replays) self.players = [] for i, replay in enumerate(replays): color = QColor().fromHslF(i / self.num_replays, 0.75, 0.5) player = Player(replay=replay, pen=QPen(color)) self.players.append(player) self.playback_start = 0 if self.num_replays > 0: self.playback_start = min(min(player.t) for player in self.players) self.playback_end = max(max(player.t) for player in self.players) # always start at 0, unless our playback_start is negative (meaning we # have negative frames) self.playback_start = min(self.playback_start, 0) # if our hitobjs are hard_rock versions, flip any player *without* hr # so they match other hr players. if self.use_hr: for player in self.players: if Mod.HardRock not in player.mods: for d in player.xy: d[1] = 384 - d[1] # clock stuff self.clock = Timer(start_speed, self.playback_start) self.paused = False self.play_direction = 1 # render stuff self.timer = QTimer(self) self.timer.timeout.connect(self.next_frame_from_timer) # 62 fps (1000ms / 60frames but the result can only be a integer) self.timer.start(1000/60) # black background pal = QPalette() pal.setColor(QPalette.Background, Qt.black) self.setAutoFillBackground(True) self.setPalette(pal) # Settings that are changeable from the control's setting button. # If `True`, don't draw crosses, and draw the line in grey if the user # was not pressing any keys in the start frame of that line. self.raw_view = False self.draw_hitobjects = True self.draw_approach_circles = True # how many frames for each replay to draw on screen at a time self.num_frames_on_screen = 15 self.only_color_keydowns = False self.should_draw_hit_error_bar = True # TODO expose this as a setting somewhere? it's not toggleable anywhere # currently self.should_draw_judgment_indicators = True self.next_frame() self.hitobj_to_judgments = {} cg = KeylessCircleguard() self.can_access_judgments = self.num_replays == 1 and cg.map_available(replays[0]) if self.can_access_judgments: r = replays[0] self.judgments = cg.judgments(r, beatmap=self.beatmap) # associate each hitobject with a judgment. Only hitobjs which are # spinners won't be associated in this mapping (since core doesn't # generate a judgment for them yet) for judgment in self.judgments: # use the hitobj time as our key. This will work fine for ranked # maps (no two hitobjs can be placed at the same time) but may # break for aspire, loved, or crazy graveyarded maps. Needs # testing. # A way around this is to simply store the slider hitobj in # circlecore's hitobjs, or convert core to using slider's # hitobjs again (or make them subclasses, or some such) self.hitobj_to_judgments[judgment.hitobject.t] = judgment def resizeEvent(self, event): width = event.size().width() - GAMEPLAY_PADDING_WIDTH * 2 height = event.size().height() - GAMEPLAY_PADDING_HEIGHT * 2 y_scale = height / GAMEPLAY_HEIGHT x_scale = width / GAMEPLAY_WIDTH if GAMEPLAY_WIDTH * y_scale > width: self.scale = x_scale self.y_offset = (height - GAMEPLAY_HEIGHT * x_scale) / 2 self.x_offset = 0 else: self.scale = y_scale self.y_offset = 0 self.x_offset = (width - GAMEPLAY_WIDTH * y_scale) / 2 def _x(self, position): return (self.x_offset + GAMEPLAY_PADDING_WIDTH + self.scaled_number(position)) def _y(self, position): return (self.y_offset + GAMEPLAY_PADDING_HEIGHT + self.scaled_number(position)) def scaled_point(self, x, y): return QPointF(self._x(x), self._y(y)) def scaled_number(self, n): return n * self.scale def next_frame_from_timer(self): """ Has the same effect as next_frame except if paused, where it returns. This is to allow the back/forward buttons to advance frame by frame while still paused (as they connect directly to next and previous frame), while still pausing the automatic timer advancement. """ if self.paused: # ignore our paused state if we're still loading sliders, or else if # we pause before the sliders are done loading we'll deadlock # ourselves if self.is_loading: self.update() return # if we wanted to seek to somewhere while we were loaded, and we # have just come out of a loading state, ignore paused and seek to # that position if self.previously_loading and self.seek_to_when_loaded: self.seek_to(self.seek_to_when_loaded) self.previously_loading = False return self.next_frame() def next_frame(self, stepping_backwards=False): """ Prepares the next frame. If we have just set our current time to be less than what it was the previous time next_frame was called, pass stepping_backwards=True so the correct frame can be chosen when searching the frame list. """ # just update the frame if currently loading if self.is_loading: self.previously_loading = True self.update() return current_time = self.clock.get_time() # if we're at the end of the track or are at the beginning of the track # (and thus are reversing), pause and dont update if current_time > self.playback_end or current_time < self.playback_start: self.pause_signal.emit() return # This is the solution to the issue of stepping forward/backwards # getting stuck on certain frames - we can fix it for stepping forward # by always preferring the right side when searching our array, but when # stepping backwards we need to prefer the left side instead. side = "left" if stepping_backwards else "right" for player in self.players: player.end_pos = np.searchsorted(player.t, current_time, side) # for some reason side=right and side=left differ by 1 even when # the array has no duplicates, so only account for that in the # right side case if side == "right": player.end_pos -= 1 player.start_pos = 0 if player.end_pos >= self.num_frames_on_screen: player.start_pos = player.end_pos - self.num_frames_on_screen if self.has_beatmap: self.get_hitobjects() self.update_time_signal.emit(current_time) self.update() def get_hitobjects(self): # get currently visible hitobjects current_time = self.clock.get_time() found_all = False # TODO optimize this by tracking our current hitobj index, this iterates # through half the hitobjects of the map on average (O(1) best case and # O(n) worst case) which can't be good for performance index = 0 self.hitobjs_to_draw = [] self.hitobjs_to_draw_hits_for = [] self.hitobjs_to_draw_judgment_indicators_for = [] while not found_all: current_hitobj = self.hit_objects[index] hit_t = current_hitobj.time.total_seconds() * 1000 if isinstance(current_hitobj, (Slider, Spinner)): hit_end = self.get_hit_endtime(current_hitobj) + self.fade_in else: hit_end = hit_t + self.hitwindow_50 + self.fade_in if hit_t > current_time - JUDGMENT_INDICATOR_THRESHOLD: self.hitobjs_to_draw_judgment_indicators_for.append(current_hitobj) if hit_t > current_time - ERROR_BAR_HIT_THRESHOLD: self.hitobjs_to_draw_hits_for.append(current_hitobj) if hit_t - self.preempt < current_time < hit_end: self.hitobjs_to_draw.append(current_hitobj) elif hit_t > current_time: found_all = True if index == self.num_hitobjects - 1: found_all = True index += 1 def paintEvent(self, _event): """ Called whenever self.update() is called """ self.painter.begin(self) self.painter.setRenderHint(QPainter.TextAntialiasing, True) self.painter.setRenderHint(QPainter.Antialiasing, True) self.painter.setPen(PEN_WHITE) _pen = self.painter.pen() # loading screen if self.is_loading: if self.thread.is_alive(): self.draw_loading_screen() self.painter.end() return else: self.is_loading = False self.clock.reset() self.painter.end() return # beatmap if self.has_beatmap: self.paint_beatmap() # cursors for player in self.players: self.paint_cursor(player) # other info self.painter.setPen(_pen) self.paint_border() if self.should_paint_info: self.paint_info() if self.paint_frametime: self.paint_frametime_graph() self.painter.end() def paint_border(self): PEN_WHITE.setWidth(self.scaled_number(1)) self.painter.setPen(PEN_WHITE) self.painter.setOpacity(0.25) self.painter.drawRect(QRectF(self.scaled_point(0, 0), self.scaled_point(GAMEPLAY_WIDTH, GAMEPLAY_HEIGHT))) def paint_cursor(self, player): """ Draws a cursor. Arguments: Player player: player to draw the cursor of. """ # don't draw anything if the player is disabled if player in self.disabled_players: return alpha_step = 1 / self.num_frames_on_screen pen = player.pen width = WIDTH_LINE_RAW_VIEW if self.raw_view else WIDTH_LINE pen.setWidth(self.scaled_number(width)) PEN_HIGHLIGHT.setWidth(self.scaled_number(width)) self.painter.setPen(pen) highlighted_pen = False for i in range(player.start_pos, player.end_pos): highlight = any((player.t[i + 1] in self.events, player.t[i] in self.events)) if highlight and not highlighted_pen: self.painter.setPen(PEN_HIGHLIGHT) highlighted_pen = True elif not highlight and highlighted_pen: self.painter.setPen(pen) highlighted_pen = False grey_out = False # only grey out lines if we're in raw view (crosses are greyed out # instead in the normal view) if self.raw_view: # grey out if we don't have a keypress at the start if not bool(player.k[i]): grey_out = True # grey out if we're only coloring keydowns and this is not a # keydown if self.only_color_keydowns and not bool(player.keydowns[i]): grey_out = True self.draw_line((i - player.start_pos) * alpha_step, player.xy[i], player.xy[i + 1], grey_out=grey_out) pen.setWidth(self.scaled_number(WIDTH_CROSS)) self.painter.setPen(pen) for i in range(player.start_pos, player.end_pos+1): alpha = (i - player.start_pos) * alpha_step xy = player.xy[i] k = player.k[i] t = player.t[i] highlight = t in self.events # grey out only if no keys are held by default grey_out = not bool(k) # but override if we're only coloring keydowns and this is not a # keydown if self.only_color_keydowns and not bool(player.keydowns[i]): grey_out = True self.draw_cross(alpha, xy, grey_out=grey_out, highlight=highlight) # reset alpha self.painter.setOpacity(1) def paint_beatmap(self): # draw playfield judgment indicators (yellow/green/blue circles under # hitobjs) before drawing hitobjs so they don't cover hitobjs # (though to be honest it doesn't make much of a difference either way) if self.should_draw_judgment_indicators and self.can_access_judgments: for hitobj in self.hitobjs_to_draw_hits_for: if isinstance(hitobj, Spinner): continue judgment = self.hitobj_to_judgments[self.get_hit_time(hitobj)] if judgment.type is JudgmentType.Miss: # misses don't have an intrinsic event time, so just use # their hitobj's time t = judgment.hitobject.time else: t = judgment.time # don't draw hits that haven't happened yet if t <= self.clock.get_time(): self.draw_judgment_indicator(hitobj, judgment) self.painter.setBrush(BRUSH_BLANK) for hitobj in self.hitobjs_to_draw[::-1]: self.draw_hitobject(hitobj) # only draw hit error bars if there's only one replay if self.should_draw_hit_error_bar and self.can_access_judgments: self.draw_hit_error_bar() for hitobj in self.hitobjs_to_draw_hits_for: # core doesn't calculate judgmnets for spinners yet, TODO # implement this when core does if isinstance(hitobj, Spinner): continue judgment = self.hitobj_to_judgments[self.get_hit_time(hitobj)] # don't draw any judgment bars for misses if judgment.type is JudgmentType.Miss: continue # don't draw hits that haven't happened yet if judgment.t <= self.clock.get_time(): self.draw_hit(hitobj, judgment) def paint_info(self): """ Draws various info about the replays in the upper left corner. """ # our current y coordinate for drawing info. Modified throughout this # function y = 15 PEN_WHITE.setWidth(1) self.painter.setPen(PEN_WHITE) self.painter.setOpacity(1) ms = round(self.clock.get_time()) text = f"{ms}" self.painter.drawText(5, y, text) # we don't use a monospaced font, so our ms text may vary by as much as # 10 pixels in width (possibly more, haven't checked rigorously). If # we just drew our minute:seconds text directly after it, the position # of that text would constantly flicker left and right, since the ms # text (and thus its width) changes every single frame. To fix this, # only increment widths in multiples of 10. (This will not fix the issue # if the text width happens to hover exactly around a multiple of 10, # but there's not much to be done in that case). text_width = self.painter.boundingRect(5, y, 0, 0, 0, text).width() if text_width < 50: x = 50 elif text_width < 60: x = 60 elif text_width < 70: x = 70 elif text_width < 80: x = 80 else: # something crazy is going on, give up and just use text_width x = text_width # now some dirty code to deal with negattive times minutes = int(ms / (1000 * 60)) seconds = ms // 1000 seconds_negative = seconds < 0 # pytohn modulo returns positive even when ``seconds_total`` is negative seconds = seconds % 60 if seconds_negative: # ``seconds`` can be 0 and 59 but not 60, so use 59 instead of 60 seconds = 59 - seconds sign = "" if minutes < 0 or seconds_negative: sign = "-" minutes = abs(minutes) seconds = abs(seconds) self.painter.drawText(5 + 4 + x, y, f"ms ({sign}{minutes:01}:{seconds:02})") self.player_info_positions = {} if self.num_replays > 0: for player in self.players: def _set_opacity(opacity): if player in self.disabled_players: opacity /= 2.4 self.painter.setOpacity(opacity) y += 13 pen = player.pen self.painter.setPen(PEN_BLANK) self.painter.setBrush(QBrush(pen.color())) keys = Key(int(player.k[player.end_pos])) _set_opacity(1 if Key.M1 in keys else 0.3) self.painter.drawRect(5, y - 9, 10, 10) _set_opacity(1 if Key.M2 in keys else 0.3) self.painter.drawRect(18, y - 9, 10, 10) _set_opacity(1) self.painter.setPen(pen) info_text = (f"{player.username} {player.mods.short_name()}: " f"{player.xy[player.end_pos][0]:.2f}, " f"{player.xy[player.end_pos][1]:.2f}") self.painter.drawText(31, y, info_text) # not sure why we need to do ``y - 9`` instead of 9 here, # our ``drawText`` call is perfectly happy to accept ``y`` but # we need to pass ``y - 9`` to our ``drawRect`` calls...maybe 9 # was a manually determined number that causes the text to align # with the drawn boxes? info_pos = self.painter.boundingRect(5, y - 9, 0, 0, 0, info_text) info_pos = Rect(info_pos.x(), info_pos.y(), info_pos.width(), info_pos.height()) # unfortunately the rects overlap if we don't make this manual # adjustment; would like to figure out why but this works for # now. info_pos.height -= 3 # our bounding rect starts at 5 but the text starts at 31, so # we need to increase the width by the difference to account info_pos.width += 31 - 5 self.player_info_positions[info_pos] = player self.painter.setOpacity(1) self.painter.setPen(PEN_WHITE) if self.num_replays == 2: try: y += 13 p1 = self.players[0] p2 = self.players[1] distance = math.sqrt(((p1.xy[p1.end_pos][0] - p2.xy[p2.end_pos][0]) ** 2) + ((p1.xy[p1.end_pos][1] - p2.xy[p2.end_pos][1]) ** 2)) self.painter.drawText(5, y, f"{int(distance)}px apart") except IndexError: # we may only have data from one cursor at the moment pass if self.num_replays == 1 and self.has_beatmap: y += 13 player = self.players[0] current_t = timedelta(milliseconds=int(self.clock.get_time())) closest_hitobj = self.beatmap.closest_hitobject(current_t) if self.use_hr: closest_hitobj = closest_hitobj.hard_rock distance = self.distance_between(player.xy[player.end_pos], closest_hitobj) # show "x px inside hitobj" instead of a negative distance inside = False if distance < 0: inside = True distance = abs(distance) inside_from = "inside" if inside else "from" text = f"{distance:0.2f}px {inside_from} closest hitobj" self.painter.drawText(5, y, text) for function in self.statistic_functions: y += 13 xys = [player.xy for player in self.players] indices = [player.end_pos for player in self.players] result = function(xys, indices) self.painter.drawText(5, y, f"{function.__name__}: {result}") def draw_line(self, alpha, start, end, grey_out=False): """ Draws a line at the given alpha level from the start point to the end point. Arguments: Float alpha: The alpha level (from 0.0 to 1.0) to set the line to. List start: The X&Y position of the start of the line. List end: The X&Y position of the end of the line. Boolean grey_out: Whether to grey out the line or not. """ if grey_out: prev_pen = self.painter.pen() PEN_GREY_INACTIVE.setWidth(self.scaled_number(WIDTH_LINE_RAW_VIEW)) self.painter.setPen(PEN_GREY_INACTIVE) self.painter.setOpacity(alpha) self.painter.drawLine(self.scaled_point(start[0], start[1]), self.scaled_point(end[0], end[1])) if self.raw_view and grey_out: self.painter.setPen(prev_pen) def draw_cross(self, alpha, point, grey_out, highlight): """ Draws a cross. Args: Float alpha: The alpha level from 0.0-1.0 to set the cross to. List point: The X and Y position of the cross. Boolean grey_out: Whether to grey out the cross or not. Boolean highlight: Whether to highlight the cross or not. This takes precedence over ``grey_out`` if both are set. """ # crosses can clutter the screen sometimes, don't draw them if raw view # is on if self.raw_view: return prev_pen = None if highlight: prev_pen = self.painter.pen() PEN_HIGHLIGHT.setWidth(self.scaled_number(WIDTH_CROSS)) self.painter.setPen(PEN_HIGHLIGHT) elif grey_out: prev_pen = self.painter.pen() PEN_GREY_INACTIVE.setWidth(self.scaled_number(WIDTH_CROSS)) self.painter.setPen(PEN_GREY_INACTIVE) half_width = LENGTH_CROSS/2 x = point[0] y = point[1] x1 = x + half_width x2 = x - half_width y1 = y + half_width y2 = y - half_width self.draw_line(alpha, [x1, y1], [x2, y2]) self.draw_line(alpha, [x2, y1], [x1, y2]) if grey_out or highlight: self.painter.setPen(prev_pen) def draw_hitobject(self, hitobj): """ Calls the corresponding function to draw ``hitobj``. """ if not self.draw_hitobjects: return if isinstance(hitobj, Circle): self.draw_hitcircle(hitobj) self.draw_approachcircle(hitobj) if isinstance(hitobj, Slider): self.draw_slider(hitobj) if isinstance(hitobj, Spinner): self.draw_spinner(hitobj) def draw_hitcircle(self, hitobj): """ Draws a circle hitobject. """ current_time = self.clock.get_time() fade_out = max(0, ((current_time - self.get_hit_time(hitobj)) / self.hitwindow_50)) opacity = min(1, ((current_time - (self.get_hit_time(hitobj) - self.preempt)) / self.fade_in)) opacity = max(0, min(1, opacity-fade_out)) p = hitobj.position # the pen width grows outwards and inwards equally (preferring outwards # if the width is odd I think), so we need to tell it to start drawing # half of the pen's width away from the radius for the final circle to # have radius `self.hitcircle_radius`. r = self.scaled_number(self.hitcircle_radius - WIDTH_CIRCLE_BORDER / 2) # normal white hitobj pen = PEN_WHITE brush = BRUSH_GRAY if self.can_access_judgments: judgment = self.hitobj_to_judgments[self.get_hit_time(hitobj)] if judgment.type is JudgmentType.Miss: # hitobj was missed, tint red pen = PEN_RED_TINT brush = BRUSH_GRAY_RED_TINT pen.setWidth(self.scaled_number(WIDTH_CIRCLE_BORDER)) self.painter.setPen(pen) self.painter.setOpacity(opacity) self.painter.setBrush(brush) self.painter.drawEllipse(self.scaled_point(p.x, p.y), r, r) self.painter.setBrush(BRUSH_BLANK) def draw_spinner(self, hitobj): """ Draws a spinner hitobject. """ current_time = self.clock.get_time() if self.get_hit_endtime(hitobj) - current_time < 0: return radius = GAMEPLAY_HEIGHT / 2 fade_out = max(0, ((current_time - self.get_hit_endtime(hitobj)) / self.hitwindow_50)) opacity = min(1, ((current_time - (self.get_hit_time(hitobj) - self.preempt)) / self.fade_in)) opacity = max(0, min(1, opacity-fade_out)) scale = min(1, (self.get_hit_endtime(hitobj) - current_time) / (self.get_hit_endtime(hitobj) - self.get_hit_time(hitobj))) radius = radius * scale PEN_WHITE.setWidth(self.scaled_number(WIDTH_CIRCLE_BORDER / 2)) self.painter.setPen(PEN_WHITE) self.painter.setOpacity(opacity) self.painter.drawEllipse(self.scaled_point(GAMEPLAY_WIDTH / 2, GAMEPLAY_HEIGHT / 2), self.scaled_number(radius), self.scaled_number(radius)) def draw_approachcircle(self, hitobj): """ Draws the approach circle of a circle hitobject. """ if not self.draw_approach_circles: return current_time = self.clock.get_time() if self.get_hit_time(hitobj) - current_time < 0: return opacity = min(1, ((current_time - (self.get_hit_time(hitobj) - self.preempt)) / self.fade_in)) opacity = max(0, min(1, opacity)) scale = max(1, ((self.get_hit_time(hitobj) - current_time) / self.preempt) * 3 + 1) p = hitobj.position r = self.scaled_number(self.hitcircle_radius * scale) pen = PEN_WHITE if self.can_access_judgments: judgment = self.hitobj_to_judgments[self.get_hit_time(hitobj)] if judgment.type is JudgmentType.Miss: # hitobj was missed, tint red pen = PEN_RED_TINT pen.setWidth(self.scaled_number(WIDTH_CIRCLE_BORDER / 2)) self.painter.setPen(pen) self.painter.setOpacity(opacity) self.painter.drawEllipse(self.scaled_point(p.x, p.y), r, r) def draw_slider(self, hitobj): """ Draws sliderbody, hitcircle, approachcircle if needed """ self.draw_sliderbody(hitobj) self.draw_hitcircle(hitobj) self.draw_approachcircle(hitobj) def draw_sliderbody(self, hitobj): """ Draws the sliderbody of a slider using a QpainterPath. """ current_time = self.clock.get_time() fade_out = max(0, ((current_time - self.get_hit_endtime(hitobj)) / self.hitwindow_50)) opacity = min(1, ((current_time - (self.get_hit_time(hitobj) - self.preempt)) / self.fade_in)) opacity = max(0, min(1, opacity-fade_out)) * 0.75 p = hitobj.position PEN_GRAY.setWidth(self.scaled_number(self.hitcircle_radius * 2)) PEN_GRAY.setCapStyle(Qt.RoundCap) PEN_GRAY.setJoinStyle(Qt.RoundJoin) self.painter.setPen(PEN_GRAY) self.painter.setOpacity(opacity) sliderbody = QPainterPath() sliderbody.moveTo(self.scaled_point(p.x, p.y)) for i in hitobj.slider_body: sliderbody.lineTo(self.scaled_point(i.x, i.y)) self.painter.drawPath(sliderbody) def draw_hit_error_bar(self): mid_x = GAMEPLAY_WIDTH / 2 y = GAMEPLAY_HEIGHT - ERROR_BAR_HIT_HEIGHT # draw the center white bar self.painter.setPen(PEN_WHITE) pen = self.painter.pen() pen.setWidth(ERROR_BAR_HIT_WIDTH) self.painter.setPen(pen) self.draw_line(1, [mid_x, y - ERROR_BAR_HIT_HEIGHT], [mid_x, y + ERROR_BAR_HIT_HEIGHT]) # draw the three error zones as slightly transparent self.painter.setOpacity(0.65) self.painter.setPen(PEN_BLANK) hw300 = self.hitwindow_300 * self.error_bar_width_factor hw100 = self.hitwindow_100 * self.error_bar_width_factor hw50 = self.hitwindow_50 * self.error_bar_width_factor self.painter.setBrush(BRUSH_BLUE) p1 = self.scaled_point(mid_x - hw300, y - ERROR_BAR_HEIGHT) p2 = self.scaled_point(mid_x + hw300, y + ERROR_BAR_HEIGHT) self.painter.drawRect(QRectF(p1, p2)) # draw two rects to avoid overlapping with hitwindow_300 in the center self.painter.setBrush(BRUSH_GREEN) p1 = self.scaled_point(mid_x - hw100, y - ERROR_BAR_HEIGHT) p2 = self.scaled_point(mid_x - hw300, y + ERROR_BAR_HEIGHT) self.painter.drawRect(QRectF(p1, p2)) p1 = self.scaled_point(mid_x + hw300, y - ERROR_BAR_HEIGHT) p2 = self.scaled_point(mid_x + hw100, y + ERROR_BAR_HEIGHT) self.painter.drawRect(QRectF(p1, p2)) self.painter.setBrush(BRUSH_YELLOW) p1 = self.scaled_point(mid_x - hw50, y - ERROR_BAR_HEIGHT) p2 = self.scaled_point(mid_x - hw100, y + ERROR_BAR_HEIGHT) self.painter.drawRect(QRectF(p1, p2)) p1 = self.scaled_point(mid_x + hw100, y - ERROR_BAR_HEIGHT) p2 = self.scaled_point(mid_x + hw50, y + ERROR_BAR_HEIGHT) self.painter.drawRect(QRectF(p1, p2)) self.painter.setBrush(BRUSH_BLANK) self.painter.setOpacity(1) def draw_hit(self, hitobj, hit): # TODO: avoid duplication in these constants between this and # `draw_hit_error_bar` - maybe just extract to globals? mid_x = GAMEPLAY_WIDTH / 2 y = GAMEPLAY_HEIGHT - ERROR_BAR_HIT_HEIGHT if hit.type is JudgmentType.Hit300: pen = PEN_BLUE elif hit.type is JudgmentType.Hit100: pen = PEN_GREEN elif hit.type is JudgmentType.Hit50: pen = PEN_YELLOW self.painter.setPen(pen) pen = self.painter.pen() pen.setWidth(ERROR_BAR_HIT_WIDTH) self.painter.setPen(pen) current_time = self.clock.get_time() # positive is a late hit, negative is an early hit error = (hit.t - self.get_hit_time(hitobj)) * self.error_bar_width_factor start = [mid_x + error, y - ERROR_BAR_HIT_HEIGHT] end = [mid_x + error, y + ERROR_BAR_HIT_HEIGHT] time_passed = current_time - hit.t # how long in ms to display hits for before they disappear time_passed = min(time_passed, ERROR_BAR_HIT_THRESHOLD) # draw most recent hits as more visible (higher alpha) and old hits # as less visible (lower alpha) x_interp = [0, ERROR_BAR_HIT_THRESHOLD] y_interp = [1, 0] f = interpolate.interp1d(x_interp, y_interp) alpha = f(time_passed) self.draw_line(alpha, start, end) def draw_judgment_indicator(self, hitobj, judgment): if judgment.type is JudgmentType.Hit300: # don't draw anything for 300s return if judgment.type is JudgmentType.Miss: brush = BRUSH_JUDGMENT_MISS judgment_t = judgment.hitobject.time else: judgment_t = judgment.time if judgment.type is JudgmentType.Hit50: brush = BRUSH_JUDGMENT_50 if judgment.type is JudgmentType.Hit100: brush = BRUSH_JUDGMENT_100 self.painter.setPen(PEN_BLANK) self.painter.setBrush(brush) current_time = self.clock.get_time() time_passed = current_time - judgment_t time_passed = min(time_passed, JUDGMENT_INDICATOR_THRESHOLD) x_interp = [0, JUDGMENT_INDICATOR_THRESHOLD] y_interp = [1, 0] f = interpolate.interp1d(x_interp, y_interp) alpha = f(time_passed) self.painter.setOpacity(alpha) p = hitobj.position r = self.scaled_number(JUDGMENT_INDICATOR_RADIUS) self.painter.drawEllipse(self.scaled_point(p.x, p.y), r, r) def draw_progressbar(self, percentage): loading_bg = QPainterPath() loading_bar = QPainterPath() c = self.painter.pen().color() _pen = self.painter.pen() _pen.setWidth(5) _pen.setCapStyle(Qt.RoundCap) _pen.setJoinStyle(Qt.RoundJoin) _pen.setColor(QColor(c.red(), c.green(), c.blue(), 25)) self.painter.setPen(_pen) loading_bg.moveTo(self.width()/2 - 75, self.height() / 2) loading_bg.lineTo(self.width()/2 - 75 + 150, self.height() / 2) loading_bar.moveTo(self.width() / 2 - 75, self.height() / 2) loading_bar.lineTo(self.width() / 2 - 75 + percentage * 1.5, self.height() / 2) self.painter.drawPath(loading_bg) _pen.setColor(QColor(c.red(), c.green(), c.blue(), 255)) self.painter.setPen(_pen) self.painter.drawPath(loading_bar) def draw_loading_screen(self): x = self.width() / 2 - 75 y = self.height() / 2 - 10 self.painter.drawText(x, y, "Calculating Sliders, please wait...") progress = int((self.sliders_current / self.num_sliders) * 100) self.draw_progressbar(progress) def process_sliders(self): for i, hitobj in enumerate(self.hit_objects): self.sliders_current = i if isinstance(hitobj, Slider): steps = max(2, int((self.get_hit_endtime(hitobj) - self.get_hit_time(hitobj)) / SLIDER_TICKRATE)) hitobj.slider_body = [hitobj.curve(i / steps) for i in range(steps + 1)] def search_nearest_frame(self, reverse=False): """ Args Boolean reverse: whether to search backwards or forwards through time """ if not reverse: next_frames = [] for player in self.players: pos = player.end_pos + 1 # stay at the end of the replay, avoid index error if pos == len(player.xy): pos -= 1 next_frames.append(player.t[pos]) # if we're only visualizing a beatmap and there's no replays, and # someone tries to advance or retreat frames, min() / max() will # crash because next_frames is empty, so avoid this. if not next_frames: return self.seek_to(min(next_frames)) else: prev_frames = [] for player in self.players: pos = player.end_pos - 1 # stay at the beginning of the replay, don't wrap around to end if pos == -1: pos += 1 prev_frames.append(player.t[pos]) if not prev_frames: return self.seek_to(max(prev_frames), seeking_backwards=True) def seek_to(self, position, seeking_backwards=False): """ Seeks to position if the change is bigger than ± 10. Also calls next_frame() so the correct frame is displayed. Args: Integer position: position to seek to in ms Boolean seeking_backwards: Whether we're seeking to a time before our current time. """ self.clock.time_counter = position # if we want to seek somewhere while we're loading sliders, we store # that position so we can seek to it when loaded if self.is_loading: self.seek_to_when_loaded = position if self.paused: self.next_frame(stepping_backwards=seeking_backwards) def wheelEvent(self, event): # from the qt docs on pixelDelta: "This value is provided on platforms # that support high-resolution pixel-based delta values, such as macOS". # Since not every OS provides pixelDelta, we should use it if possible # but fall back to angleDelta. From my testing (sample size 1) # pixelDelta will have both x and y as zero if it's unsupported. if event.pixelDelta().x() == 0 and event.pixelDelta().y() == 0: # check both x and y to support users scrolling either vertically or # horizontally to move the timeline, just respect whichever is # greatest for that event. # this /5 is an arbitrary value to slow down scrolling to what # feels reasonable. TODO expose as a setting to the user ("scrolling # sensitivity") delta = max(event.angleDelta().x(), event.angleDelta().y(), key=abs) / 5 else: delta = max(event.angleDelta().x(), event.angleDelta().y(), key=abs) self.seek_to(self.clock.time_counter + delta) def mouseMoveEvent(self, event): any_inside = False for rect in self.player_info_positions: qrect = rect.toQRect() if qrect.contains(event.pos()): any_inside = True self.setCursor(QCursor(Qt.PointingHandCursor)) if not any_inside: self.setCursor(QCursor(Qt.ArrowCursor)) return super().mouseMoveEvent(event) def mousePressEvent(self, event): for rect in self.player_info_positions: qrect = rect.toQRect() if qrect.contains(event.pos()): player = self.player_info_positions[rect] # toggle its membership in disabled_players, so users can click # a second time to re-enable a player if player in self.disabled_players: self.disabled_players.remove(player) else: self.disabled_players.append(player) self.update() return super().mousePressEvent(event) def get_hit_endtime(self, hitobj): if isinstance(hitobj, Circle): return self.get_hit_time(hitobj) t = hitobj.end_time.total_seconds() * 1000 return int(round(t)) def get_hit_time(self, hitobj): t = hitobj.time.total_seconds() * 1000 # Due to floating point errors, ``t`` could actually be something # like ``129824.99999999999`` or ``128705.00000000001``, so round to the # nearest int. return int(round(t)) def pause(self): """ Set paused flag and pauses the clock. """ self.paused = True self.clock.pause() def resume(self): """ Removes paused flag and resumes the clock. """ self.paused = False self.clock.resume() def toggle_frametime(self): self.paint_frametime = not self.paint_frametime def distance_between(self, point, hitobject): """ The shortest distance between the given point and hitobject. """ # TODO use numpy for these calculations x1 = point[0] y1 = point[1] x2 = hitobject.position.x y2 = hitobject.position.y r = self.hitcircle_radius return math.sqrt((((x2 - x1) ** 2) + (y2 - y1) ** 2)) - r def raw_view_changed(self, new_state): self.raw_view = new_state # redraw everything for the new raw view self.update() def only_color_keydowns_changed(self, new_state): self.only_color_keydowns = new_state self.update() def hitobjects_changed(self, new_state): self.draw_hitobjects = new_state self.update() def approach_circles_changed(self, new_state): self.draw_approach_circles = new_state self.update() def num_frames_changed(self, new_value): self.num_frames_on_screen = new_value self.update() def draw_hit_error_bar_changed(self, new_value): self.should_draw_hit_error_bar = new_value self.update() def circle_size_mod_changed(self, new_value): if not self.has_beatmap: # cs doesn't matter to us if we don't have a beatmap (and we don't # have the attributes necessary to compute it anyway) return use_hr = new_value == "HR" use_ez = new_value == "EZ" cs = self.beatmap.cs(hard_rock=use_hr, easy=use_ez) self.hitcircle_radius = hitradius(cs) self.update()
def paint(self, painter: QtGui.QPainter, option: 'QStyleOptionGraphicsItem', widget: typing.Optional[QWidget] = ...) -> None: painter.setPen(self.current_color) painter.drawPath(self.path())
def paintEvent(self, event): if self.minimum() == self.maximum() == 0: return # 正弦曲线公式 y = A * sin(ωx + φ) + k # 当前值所占百分比 percent = 1 - (self.value() - self.minimum()) / \ (self.maximum() - self.minimum()) # w表示周期,6为人为定义 w = 6 * self.waterDensity * math.pi / self.width() # A振幅 高度百分比,1/26为人为定义 A = self.height() * self.waterHeight * 1 / 26 # k 高度百分比 k = self.height() * percent # 波浪1 waterPath1 = QPainterPath() waterPath1.moveTo(0, self.height()) # 起点在左下角 # 波浪2 waterPath2 = QPainterPath() waterPath2.moveTo(0, self.height()) # 起点在左下角 # 偏移 self._offset += 0.6 if self._offset > self.width() / 2: self._offset = 0 for i in range(self.width() + 1): # 从x轴开始计算y轴点 y = A * math.sin(w * i + self._offset) + k waterPath1.lineTo(i, y) # 相对第一条需要进行错位 y = A * math.sin(w * i + self._offset + self.width() / 2 * A) + k waterPath2.lineTo(i, y) # 封闭两条波浪,形成一个 U形 上面加波浪的封闭区间 waterPath1.lineTo(self.width(), self.height()) waterPath1.lineTo(0, self.height()) waterPath2.lineTo(self.width(), self.height()) waterPath2.lineTo(0, self.height()) # 整体形状(矩形或者圆形) bgPath = QPainterPath() if self.styleType: bgPath.addRect(QRectF(self.rect())) else: radius = min(self.width(), self.height()) bgPath.addRoundedRect(QRectF(self.rect()), radius, radius) # 开始画路径 painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) # 设置没有画笔 painter.setPen(Qt.NoPen) if not self.styleType: # 圆形 painter.setClipPath(bgPath) # 先整体绘制背景,然后再在背景上方绘制两条波浪 painter.save() painter.setBrush(self.backgroundColor) painter.drawPath(bgPath) painter.restore() # 波浪1 painter.save() painter.setBrush(self.waterColor1) painter.drawPath(waterPath1) painter.restore() # 波浪2 painter.save() painter.setBrush(self.waterColor2) painter.drawPath(waterPath2) painter.restore() # 绘制文字 if not self.isTextVisible(): return painter.setPen(self.textColor) font = self.font() or QFont() font.setPixelSize(int(min(self.width(), self.height()) / 2)) painter.setFont(font) painter.drawText(self.rect(), Qt.AlignCenter, '%d%%' % (self.value() / self.maximum() * 100))
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 = .5 y = .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 = .5 y = y + space + kw if ext_return: rx = rx * 2 x1 = remaining_x[1] y1 = .5 + kw * 1 + space * 1 w1 = remaining_widths[1] x2 = remaining_x[2] y2 = .5 + 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)
class BaseChartPicGenerator(ABC): LANE_DISTANCE = LANE_DISTANCE SKILL_PAINT_WIDTH = SKILL_PAINT_WIDTH unit = None def __init__(self, song_id, difficulty, main_window, grand, reset_main=True, mirrored=False): self.song_id = song_id self.difficulty = difficulty self.main = main_window self.grand = grand if grand: self.lane_count = 15 else: self.lane_count = 5 self.notes = fetch_chart(None, song_id, difficulty, event=False, skip_load_notes=False)[0] if self.notes is None: self.notes = fetch_chart(None, song_id, difficulty, event=True, skip_load_notes=False)[0] self.notes['finishPos'] -= 1 self.mirrored = mirrored if mirrored: if not grand: self.notes['finishPos'] = 4 - self.notes['finishPos'] else: self.notes['finishPos'] = 15 - (self.notes['finishPos'] + self.notes['status']) self.notes_into_group() self.generate_note_objects() self.initialize_ui() if reset_main: self.main.setGeometry(200, 200, self.x_max, self.y_max) self.p = QPainter(self.label.pixmap()) self.p.setRenderHint(QPainter.Antialiasing) self.draw() self.label.repaint() def mirror_generator(self, mirrored): if self.mirrored == mirrored: return self return BaseChartPicGenerator.get_generator(self.song_id, self.difficulty, self.main, reset_main=False, mirrored=mirrored) @classmethod def get_generator(cls, song_id, difficulty, main_window, reset_main=True, mirrored=False): if isinstance(difficulty, int): difficulty = Difficulty(difficulty) if difficulty == Difficulty.PIANO or difficulty == Difficulty.FORTE: return GrandChartPicGenerator(song_id, difficulty, main_window, True, reset_main, mirrored) else: return BasicChartPicGenerator(song_id, difficulty, main_window, False, reset_main, mirrored) def notes_into_group(self): long_groups = list() long_stack = defaultdict(lambda: list()) for _, note in self.notes.iterrows(): # Handle long differently lane = note['finishPos'] if note['note_type'] == NoteType.LONG and lane not in long_stack: long_stack[lane].append((_, note)) elif lane in long_stack: long_stack[lane].append((_, note)) long_groups.append(long_stack.pop(lane)) long_dummy_group = 2000 for pair in long_groups: group_id = max(pair[0][1]['groupId'], pair[1][1]['groupId']) if group_id == 0: group_id = long_dummy_group long_dummy_group += 1 self.notes.loc[pair[0][0], 'groupId'] = group_id self.notes.loc[pair[1][0], 'groupId'] = group_id def initialize_ui(self): self.y_total = MAX_SECS_PER_GROUP * SEC_HEIGHT + 2 * Y_MARGIN self.x_total = (2 * X_MARGIN + (self.lane_count - 1) * self.LANE_DISTANCE) * self.n_groups + RIGHT_MARGIN self.label = QLabel() self.label.setAlignment(Qt.AlignBottom) self.label.setFixedSize(self.x_total, self.y_total) canvas = QPixmap(self.x_total, self.y_total) self.label.setPixmap(canvas) scroll = DraggableQScrollArea() scroll.setWidget(self.label) # Scroll to bottom vbar = scroll.verticalScrollBar() vbar.setValue(vbar.maximum()) self.main.setCentralWidget(scroll) self.y_max = WINDOW_HEIGHT self.x_max = min(MAX_WINDOW_WIDTH, self.x_total + 20) def get_x(self, lane, group): return X_MARGIN + lane * self.LANE_DISTANCE + ( 2 * X_MARGIN + (self.lane_count - 1) * self.LANE_DISTANCE) * group def get_y(self, sec, group=None, offset_group=0): if group is not None: return self.y_total - Y_MARGIN - ( sec - group * MAX_SECS_PER_GROUP) * SEC_HEIGHT else: return self.y_total - Y_MARGIN - ( sec - (sec // MAX_SECS_PER_GROUP + offset_group) * MAX_SECS_PER_GROUP) * SEC_HEIGHT # Lanes start from 0 def generate_note_objects(self, abuse_df=None): # Number of groups = ceil(last sec // MAX_SECS_PER_GROUP) self.last_sec = int(self.notes.sec.iloc[-1]) + 1 self.n_groups = ceil(self.last_sec / MAX_SECS_PER_GROUP) self.note_groups = list() for n in range(self.n_groups): group = list() df_slice = self.notes[ (n * MAX_SECS_PER_GROUP - Y_MARGIN / SEC_HEIGHT <= self.notes['sec']) & (self.notes['sec'] <= (n + 1) * MAX_SECS_PER_GROUP + Y_MARGIN / SEC_HEIGHT)] for _, row in df_slice.iterrows(): right_flick = row['note_type'] == NoteType.FLICK and ( row['status'] == 2 and not self.grand) or (row['type'] == 7 and self.grand) if self.mirrored: right_flick = not right_flick if abuse_df is not None and _ in abuse_df.index: delta = abuse_df.loc[_, 'delta'] early = abuse_df.loc[_, 'abuse_range_l'] late = abuse_df.loc[_, 'abuse_range_r'] great = abuse_df.loc[_, 'is_great'] else: delta = 0 early = 0 late = 0 great = False note_object = ChartPicNote(sec=row['sec'], note_type=row['note_type'], lane=row['finishPos'], sync=row['sync'], qgroup=n, group_id=row['groupId'], delta=delta, early=early, late=late, right_flick=right_flick, grand=self.grand, span=row['status'] - 1 if self.grand else 0, great=great) group.append(note_object) self.note_groups.append(group) def draw(self): self.draw_grid_and_secs() self.draw_sync_lines() self.draw_group_lines() self.draw_notes() def hook_cards(self, all_cards, redraw=True): try: if len(all_cards) == 15: unit = GrandUnit.from_list(all_cards) else: unit = Unit.from_list(cards=all_cards[:5]) except InvalidUnit: return # Skip drawing if same unit else reset drawing if not self.grand and isinstance(unit, GrandUnit): unit = unit.ua if unit == self.unit: return self.p.fillRect(0, 0, self.x_total, self.y_total, Qt.black) self.unit = unit self.paint_skill() self.draw() if redraw: self.label.repaint() def paint_skill(self): for card_idx, card in enumerate(self.unit.all_cards()): skill = card.sk interval = skill.interval duration = skill.duration skill_times = int((self.last_sec - 3) // interval) skill_time = 1 group = 0 while group < self.n_groups: left = skill_time * interval right = skill_time * interval + duration # Do not paint if skill entirely outside group if left > (group + 1) * MAX_SECS_PER_GROUP + Y_MARGIN / SEC_HEIGHT: group += 1 skill_time -= 1 continue if self.grand and (skill_time - 1) % 3 != skill.offset: skill_time += 1 continue if skill_time > skill_times: break skill_brush = QBrush( QColor(*SKILL_BASE[skill.skill_type]['color'], 100)) self.p.setPen(QPen()) self.p.setBrush(skill_brush) # Need to convert grand lane draw_card_idx = card_idx if self.grand: if card_idx < 5: draw_card_idx += 5 elif 5 <= card_idx < 10: draw_card_idx -= 5 x = self.get_x(draw_card_idx, group) y = self.get_y(right, group) self.p.drawRect(x - self.SKILL_PAINT_WIDTH // 2, y, self.SKILL_PAINT_WIDTH, duration * SEC_HEIGHT) skill_time += 1 def draw_grid_and_secs(self): font = QFont() font.setPixelSize(36) self.p.setFont(font) vertical_grid_pen = QPen(QColor(80, 80, 80)) vertical_grid_pen.setWidth(5) self.p.setPen(vertical_grid_pen) for group in range(self.n_groups): for lane in range(self.lane_count): x = self.get_x(lane, group) self.p.drawLine(x, 0, x, self.y_total) horizontal_grid_bold_pen = QPen(QColor(120, 120, 120)) horizontal_grid_bold_pen.setWidth(5) horizontal_grid_light_pen = QPen(QColor(80, 80, 80)) horizontal_grid_light_pen.setWidth(3) for group in range(self.n_groups): for sec in range(MAX_SECS_PER_GROUP + 1): if (sec + group * MAX_SECS_PER_GROUP) % 5 == 0: self.p.setPen(horizontal_grid_bold_pen) else: self.p.setPen(horizontal_grid_light_pen) y = self.get_y(sec, group=0) self.p.drawLine(self.get_x(0, group), y, self.get_x(self.lane_count - 1, group), y) self.p.drawText( QRect(self.get_x(0, group) - 111, y - 25, 70, 50), Qt.AlignRight, str(sec + MAX_SECS_PER_GROUP * group)) @abstractmethod def draw_notes(self): pass def _is_double_drawn_note(self, note: ChartPicNote): for _ in range(self.n_groups): if MAX_SECS_PER_GROUP * _ - Y_MARGIN / SEC_HEIGHT <= note.sec <= MAX_SECS_PER_GROUP * _ + Y_MARGIN / SEC_HEIGHT: return True return False def draw_sync_lines(self): sync_line_pen = QPen(QColor(250, 250, 240)) sync_line_pen.setWidth(3) self.p.setPen(sync_line_pen) for group_idx, qt_group in enumerate(self.note_groups): sync_pairs = defaultdict(lambda: list()) for note in qt_group: if note.sync == 0: continue sync_pairs[note.sec].append(note) for values in sync_pairs.values(): l = min(values[0].lane, values[1].lane) r = max(values[0].lane, values[1].lane) sec = values[0].sec y = self.get_y(sec, group_idx) self.p.drawLine(self.get_x(l, group_idx), y, self.get_x(r, group_idx), y) @abstractmethod def _draw_group_line(self, note1, note2, group): pass def draw_group_lines(self): for group_idx, qt_group in enumerate(self.note_groups): group_ids = set() for note in qt_group: if note.group_id == 0: continue group_ids.add(note.group_id) grouped_notes_df = self.notes[self.notes['groupId'].isin( group_ids)] for group_id, grouped_notes in grouped_notes_df.groupby("groupId"): for l, r in zip(grouped_notes.iloc[1:].T.to_dict().values(), grouped_notes.iloc[:-1].T.to_dict().values()): self._draw_group_line(l, r, group_idx) def hook_abuse(self, all_cards, abuse_df): self.hook_cards(all_cards, False) self.generate_note_objects(abuse_df) for group_idx, qt_group in enumerate(self.note_groups): for note in qt_group: self.draw_abuse(note, group_idx) self.label.repaint() def draw_abuse(self, note: ChartPicNote, group): if note.delta == 0: return x_note = self.get_x(note.lane + note.span / 2, group) - note.note_pic_smol.width() // 2 y_early = self.get_y(note.sec + note.early / 1000, group) shifted_y_early = y_early - note.note_pic_smol.height() // 2 y_late = self.get_y(note.sec + note.late / 1000, group) shifted_y_late = y_late - note.note_pic_smol.height() // 2 self.p.drawImage(QPoint(x_note, shifted_y_early), note.note_pic_smol) self.p.drawImage(QPoint(x_note, shifted_y_late), note.note_pic_smol) lane_l = self.get_x(0, group) lane_r = self.get_x(self.lane_count - 1, group) self.p.setPen(QPen(Qt.green)) self.p.drawLine(lane_l, y_early, lane_r, y_early) self.p.setPen(QPen(Qt.red)) self.p.drawLine(lane_l, y_late, lane_r, y_late) x = self.get_x(note.lane + note.span / 2, group) - note.note_pic.width() // 2 y = self.get_y(note.sec, group) + note.note_pic.height() font = QFont() font.setBold(True) font.setPixelSize(30) pen = QPen() pen.setWidth(1) pen.setColor(Qt.white) if note.great: brush = QBrush(QColor(66, 13, 110)) else: brush = QBrush(Qt.black) path = QPainterPath() path.addText(x, y, font, str(note.delta)) self.p.setFont(font) self.p.setPen(pen) self.p.setBrush(brush) self.p.drawPath(path) font.setPixelSize(24) path = QPainterPath() path.addText(x, y + 40, font, "{} {}".format(note.early, note.late)) self.p.drawPath(path) def save_image(self): self.label.pixmap().save("{}-{}.png".format(self.song_id, self.difficulty))
def createArrowBackground(self, transform): scaledRect = transform.mapRect(QRect(0, 0, self.logicalSize.width(), self.logicalSize.height())) image = QImage(scaledRect.width(), scaledRect.height(), QImage.Format_ARGB32_Premultiplied) image.fill(QColor(0, 0, 0, 0).rgba()) painter = QPainter(image) painter.setRenderHint(QPainter.SmoothPixmapTransform) painter.setRenderHint(QPainter.Antialiasing) painter.setPen(Qt.NoPen) if Colors.useEightBitPalette: painter.setPen(QColor(120, 120, 120)) if self.pressed: painter.setBrush(QColor(60, 60, 60)) elif self.highlighted: painter.setBrush(QColor(100, 100, 100)) else: painter.setBrush(QColor(80, 80, 80)) else: outlinebrush = QLinearGradient(0, 0, 0, scaledRect.height()) brush = QLinearGradient(0, 0, 0, scaledRect.height()) brush.setSpread(QLinearGradient.PadSpread) highlight = QColor(255, 255, 255, 70) shadow = QColor(0, 0, 0, 70) sunken = QColor(220, 220, 220, 30) normal1 = QColor(200, 170, 160, 50) normal2 = QColor(50, 10, 0, 50) if self.pressed: outlinebrush.setColorAt(0, shadow) outlinebrush.setColorAt(1, highlight) brush.setColorAt(0, sunken) painter.setPen(Qt.NoPen) else: outlinebrush.setColorAt(1, shadow) outlinebrush.setColorAt(0, highlight) brush.setColorAt(0, normal1) if not self.highlighted: brush.setColorAt(1, normal2) painter.setPen(QPen(outlinebrush, 1)) painter.setBrush(brush); painter.drawRect(0, 0, scaledRect.width(), scaledRect.height()) xOff = scaledRect.width() / 2 yOff = scaledRect.height() / 2 sizex = 3.0 * transform.m11() sizey = 1.5 * transform.m22() if self.type == TextButton.UP: sizey *= -1 path = QPainterPath() path.moveTo(xOff, yOff + (5 * sizey)) path.lineTo(xOff - (4 * sizex), yOff - (3 * sizey)) path.lineTo(xOff + (4 * sizex), yOff - (3 * sizey)) path.lineTo(xOff, yOff + (5 * sizey)) painter.drawPath(path) return image
def paintEvent(self, event): painter = QPainter(self) visibleRect = event.rect() columnCount = self._columnCount extra = self._cellWidthExtra cellWidth, cellHeight = self._cellWidth + 2 * extra, self._cellHeight glyphCount = len(self._glyphs) if columnCount: paintWidth = min(glyphCount, columnCount) * cellWidth else: paintWidth = 0 left = 0 top = cellHeight painter.fillRect(visibleRect, Qt.white) for index, glyph in enumerate(self._glyphs): t = top - cellHeight rect = (left, t, cellWidth, cellHeight) if visibleRect.intersects(visibleRect.__class__(*rect)): if index in self._selection: palette = self.palette() active = palette.currentColorGroup() != QPalette.Inactive opacityMultiplier = platformSpecific.colorOpacityMultiplier() selectionColor = palette.color(QPalette.Highlight) # TODO: alpha values somewhat arbitrary (here and in # glyphLineView) selectionColor.setAlphaF( .2 * opacityMultiplier if active else .7) painter.fillRect(QRectF( left, t, cellWidth, cellHeight), selectionColor) pixmap = self._getCurrentRepresentation(glyph) painter.drawPixmap(left, t, pixmap) # XXX: this hacks around the repr internals if index in self._selection and \ cellHeight >= GlyphCellMinHeightForHeader: painter.fillRect(QRectF( left, t + cellHeight - GlyphCellHeaderHeight, cellWidth, GlyphCellHeaderHeight), selectionColor) left += cellWidth if left + cellWidth > paintWidth: left = 0 top += cellHeight # drop insertion position dropIndex = self._currentDropIndex if dropIndex is not None: if columnCount: x = (dropIndex % columnCount) * cellWidth y = (dropIndex // columnCount) * cellHeight # special-case the end-column if dropIndex == glyphCount and \ glyphCount < self.width() // self._cellWidth or \ self.mapFromGlobal(QCursor.pos()).y() < y: x = columnCount * cellWidth y -= cellHeight else: x = y = 0 path = QPainterPath() path.addRect(x - 2, y, 3, cellHeight) path.addEllipse(x - 5, y - 5, 9, 9) path.addEllipse(x - 5, y + cellHeight - 5, 9, 9) path.setFillRule(Qt.WindingFill) pen = painter.pen() pen.setColor(Qt.white) pen.setWidth(2) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) painter.drawPath(path) painter.fillPath(path, insertionPositionColor)