def drawGrid(self): black_notes = [2, 4, 6, 9, 11] scale_bar = QGraphicsRectItem(0, 0, self.grid_width, self.note_height, self.piano) scale_bar.setPos(self.piano_width, 0) scale_bar.setBrush(QColor(100, 100, 100)) clearpen = QPen(QColor(0, 0, 0, 0)) for i in range(self.end_octave - self.start_octave, self.start_octave - self.start_octave, -1): for j in range(self.notes_in_octave, 0, -1): scale_bar = QGraphicsRectItem(0, 0, self.grid_width, self.note_height, self.piano) scale_bar.setPos( self.piano_width, self.note_height * j + self.octave_height * (i - 1)) scale_bar.setPen(clearpen) if j not in black_notes: scale_bar.setBrush(QColor(120, 120, 120)) else: scale_bar.setBrush(QColor(100, 100, 100)) measure_pen = QPen(QColor(0, 0, 0, 120), 3) half_measure_pen = QPen(QColor(0, 0, 0, 40), 2) line_pen = QPen(QColor(0, 0, 0, 40)) for i in range(0, int(self.num_measures) + 1): measure = QGraphicsLineItem( 0, 0, 0, self.piano_height + self.header_height - measure_pen.width(), self.header) measure.setPos(self.measure_width * i, 0.5 * measure_pen.width()) measure.setPen(measure_pen) if i < self.num_measures: number = QGraphicsSimpleTextItem('%d' % (i + 1), self.header) number.setPos(self.measure_width * i + 5, 2) number.setBrush(Qt.white) for j in self.frange( 0, self.time_sig[0] * self.grid_div / self.time_sig[1], 1.): line = QGraphicsLineItem(0, 0, 0, self.piano_height, self.header) line.setZValue(1.0) line.setPos(self.measure_width * i + self.value_width * j, self.header_height) if j == self.time_sig[0] * self.grid_div / self.time_sig[ 1] / 2.0: line.setPen(half_measure_pen) else: line.setPen(line_pen)
def drawGrid(self): black_notes = [2,4,6,9,11] scale_bar = QGraphicsRectItem(0, 0, self.grid_width, self.note_height, self.piano) scale_bar.setPos(self.piano_width, 0) scale_bar.setBrush(QColor(100,100,100)) clearpen = QPen(QColor(0,0,0,0)) for i in range(self.end_octave - self.start_octave, self.start_octave - self.start_octave, -1): for j in range(self.notes_in_octave, 0, -1): scale_bar = QGraphicsRectItem(0, 0, self.grid_width, self.note_height, self.piano) scale_bar.setPos(self.piano_width, self.note_height * j + self.octave_height * (i - 1)) scale_bar.setPen(clearpen) if j not in black_notes: scale_bar.setBrush(QColor(120,120,120)) else: scale_bar.setBrush(QColor(100,100,100)) measure_pen = QPen(QColor(0, 0, 0, 120), 3) half_measure_pen = QPen(QColor(0, 0, 0, 40), 2) line_pen = QPen(QColor(0, 0, 0, 40)) for i in range(0, int(self.num_measures) + 1): measure = QGraphicsLineItem(0, 0, 0, self.piano_height + self.header_height - measure_pen.width(), self.header) measure.setPos(self.measure_width * i, 0.5 * measure_pen.width()) measure.setPen(measure_pen) if i < self.num_measures: number = QGraphicsSimpleTextItem('%d' % (i + 1), self.header) number.setPos(self.measure_width * i + 5, 2) number.setBrush(Qt.white) for j in self.frange(0, self.time_sig[0]*self.grid_div/self.time_sig[1], 1.): line = QGraphicsLineItem(0, 0, 0, self.piano_height, self.header) line.setZValue(1.0) line.setPos(self.measure_width * i + self.value_width * j, self.header_height) if j == self.time_sig[0]*self.grid_div/self.time_sig[1] / 2.0: line.setPen(half_measure_pen) else: line.setPen(line_pen)
class CustomGraphicsView(QGraphicsView): def __init__(self, parent): super().__init__(parent) self.pen = QPen(Qt.green, 3, join=Qt.MiterJoin) self.item_batch = [] self.item_batches = collections.deque() def wheelEvent(self, event): if self.scene() and QApplication.keyboardModifiers( ) == Qt.ControlModifier: transform = self.transform() scale = 1 + ZOOM_FACTOR if event.angleDelta().y( ) > 0 else 1 - ZOOM_FACTOR self.setTransform(transform * scale) def mousePressEvent(self, event): if not self.scene(): return position = self.mapToScene(event.x(), event.y()) item = self.scene().addRect(position.x(), position.y(), self.pen.width(), self.pen.width(), self.pen, self.pen.brush()) self.item_batch.append(item) # TODO: Calculate line between new and last-known position, and draw a rectangle for each point on the line, # TODO: addLine() seems to add a different-looking pattern def mouseMoveEvent(self, event): self.mousePressEvent(event) def mouseReleaseEvent(self, event): if self.item_batch: self.item_batches.append(self.item_batch) self.item_batch = [] # TODO: This is super ugly because we don't use addLine() def undo(self): if not self.item_batches: return item_batch = self.item_batches.pop() scene = self.scene() for item in item_batch: scene.removeItem(item)
def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) roundedRect = QRect() radius = 6 roundedRect.setX(self.rect().x() + radius / 2) roundedRect.setY(self.rect().y() + radius / 2) roundedRect.setWidth(self.rect().width() - radius) roundedRect.setHeight(self.rect().height() - radius) palette = QPalette() rectColor = palette.color(QPalette.Window) painter.setBrush(QBrush(rectColor)) roundedRectPen = QPen(Qt.black) painter.setPen(roundedRectPen) painter.drawRoundedRect(roundedRect, radius, radius) closeButtonGeometry = self.closeButton.geometry() lineColor = palette.color(QPalette.Text) pen = QPen(lineColor) pen.setWidth(1) painter.setPen(pen) # horizontal line if hasattr(self, 'detailsButton'): detailsButtonGeometry = self.detailsButton.geometry() y = (closeButtonGeometry.bottom() + detailsButtonGeometry.top()) / 2 left = QPoint( min(closeButtonGeometry.left(), detailsButtonGeometry.left()), y) right = QPoint( max(closeButtonGeometry.right(), detailsButtonGeometry.right()) - 8, y) painter.drawLine(left, right) # vertical line # close button and details button have Preferred size policy x = closeButtonGeometry.left() - pen.width() top = QPoint(x, roundedRect.top() + roundedRectPen.width()) bottom = QPoint(x, roundedRect.bottom() - roundedRectPen.width()) painter.drawLine(top, bottom)
def line(xml: QXmlStreamWriter, pen: QPen, tag: str = "line"): xml.writeStartElement(tag) xml.writeAttribute("color", pen.color().name()) xml.writeAttribute("width", str(pen.width())) if pen.style() == Qt.DashLine: xml.writeAttribute("style", "dash") elif pen.style() == Qt.DotLine: xml.writeAttribute("style", "dot") else: xml.writeAttribute("style", "solid") xml.writeEndElement() #fecha line
def paint(self, painter, option, index): background = index.data(Qt.BackgroundRole) if isinstance(background, QBrush): painter.fillRect(option.rect, background) super().paint(painter, option, index) if option.state & QStyle.State_Selected: painter.save() pen = QPen(Qt.black, 2, Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin) w = pen.width() / 2 painter.setPen(pen) painter.drawRect(option.rect.adjusted(w, w, -w, -w)) painter.restore()
def setValues(self, pen: QPen): for i in range(self.lstPenStyles.count()): item = self.lstPenStyles.item(i) if item.data(PenParametersDialog.PenStyleRole) == pen.style(): item.setSelected(True) break for i in range(self.lstPenColors.count()): item = self.lstPenColors.item(i) if QColor(item.data(PenParametersDialog.ColorRole)).name( ) == pen.color().name(): item.setSelected(True) break self.spinPenSize.setValue(pen.width())
class AnalogGaugeWidget(QWidget): gaugeValueChanged = pyqtSignal(int) def __init__(self, parent=None): super(AnalogGaugeWidget, self).__init__(parent) self.useTimerEvent = False self.initUI() def initUI(self): # Color of needle self.setNeedleColor(50, 50, 50, 255) # Color of needle hub self.setNeedleHubColor(50, 50, 50, 255) # Color of gauge values self.setGaugeValuesColor(255, 255, 255, 255) # Color of LCD value self.setDigitalValueColor(255, 255, 255, 255) # Gauge needle object self.needle = QObject self.setNeedleStyle([ QPolygon([ QPoint(4, 4), QPoint(-4, 4), QPoint(-3, -120), QPoint(0, -126), QPoint(3, -120) ]) ]) # minimum gauge value self.minGaugeValue = 0 # maximum gauge value self.maxGaugeValue = 500 # current gauge value self.gaugeValue = self.minGaugeValue # gauge value units self.gaugeValueUnits = '' # outer radius of gauge self.gaugeOuterRadius = 1 # inner radius of gauge self.gaugeInnerRadius = 0.95 # orientation of gauge self.gaugeRotation = 135 # number of degrees to draw gauge (360 is a complete circle) self.gaugeArcAngle = 270 # number of gauge values self.setGaugeValueMajorAxisCount(10) # number of ticks between gauge values self.gaugeValueMinorAxisCount = 5 self.pen = QPen(QColor(0, 0, 0)) self.font = QFont('Decorative', 20) self.gaugeColors = [] self.setGaugeColors([[.00, Qt.red], [.1, Qt.yellow], [.15, Qt.green], [1, Qt.transparent]]) # gauge value font family and font size self.setGaugeValuesEnabled(True) self.gaugeValueFont = "Decorative" self.initGaugeValueFontSize = 15 self.gaugeValueFontSize = self.initGaugeValueFontSize # digital value font family and font size self.digitalValueEnabled = True self.digitalValueFontName = "Decorative" self.initDigitalValueFontSize = 40 self.digitalValueUnitsFontSize = 15 self.digitalValueFontSize = self.initDigitalValueFontSize self.digitalValueRadius = 0.7 self.setGaugeColorBarsEnabled(True) self.setGaugeAnnulusFilledEnabled(True) self.needleHubEnabled = True self.gaugeMinorAxisMarkerEnabled = True self.gaugeMajorAxisMarkerEnabled = True self.needleSize = 0.8 self.needleEnabled = True self.update() self.resizeGauge() sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(1) sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizePolicy) self.setMinimumSize(QSize(300, 300)) self.setMaximumSize(QSize(600, 600)) self.setBaseSize(QSize(300, 300)) def resizeGauge(self): if self.width() <= self.height(): self.widgetDiameter = self.width() else: self.widgetDiameter = self.height() self.setNeedleStyle([ QPolygon([ QPoint(4, 30), QPoint(-4, 30), QPoint(-2, -self.widgetDiameter / 2 * self.needleSize), QPoint(0, -self.widgetDiameter / 2 * self.needleSize - 6), QPoint(2, -self.widgetDiameter / 2 * self.needleSize) ]) ]) self.gaugeValueFontSize = self.initGaugeValueFontSize * self.widgetDiameter / 400 self.digitalValueFontSize = self.initDigitalValueFontSize * self.widgetDiameter / 400 def setNeedleStyle(self, design): self.needle = [] for i in design: self.needle.append(i) self.update() def updateGaugeValue(self, value): if value <= self.minGaugeValue: self.gaugeValue = self.minGaugeValue elif value >= self.maxGaugeValue: self.gaugeValue = self.maxGaugeValue else: self.gaugeValue = value self.gaugeValueChanged.emit(int(value)) self.update() ############################################################################################### # Set Methods ############################################################################################### def setNeedleColor(self, R=50, G=50, B=50, Transparency=255): self.needleColor = QColor(R, G, B, Transparency) self.update() def setGaugeValuesColor(self, R=50, G=50, B=50, Transparency=255): self.gaugeValuesColor = QColor(R, G, B, Transparency) self.update() def setDigitalValueColor(self, R=50, G=50, B=50, Transparency=255): self.digitalValueColor = QColor(R, G, B, Transparency) self.update() def setNeedleHubColor(self, R=50, G=50, B=50, Transparency=255): self.needleHubColor = QColor(R, G, B, Transparency) self.update() def setNeedleEnabled(self, enable=True): self.needleEnabled = enable self.update() def setGaugeValuesEnabled(self, enable=True): self.gaugeValuesEnabled = enable self.update() def setGaugeColorBarsEnabled(self, enable=True): self.GaugeColorBarsEnabled = enable self.update() def setDigitalValueEnabled(self, enable=True): self.digitalValueEnabled = enable self.update() def setNeedleHubEnabled(self, enable=True): self.needleHubEnabled = enable self.update() def setGaugeAnnulusFilledEnabled(self, enable=True): self.gaugeAnnulusEnabled = enable self.update() def setGaugeMajorAxisEnabled(self, enable=True): self.gaugeMajorAxisMarkerEnabled = enable self.update() def setGaugeMinorAxisEnabled(self, enable=True): self.gaugeMinorAxisMarkerEnabled = enable self.update() def setGaugeValueMajorAxisCount(self, count): if count < 1: count = 1 self.gaugeValueMajorAxisCount = count self.update() def setGaugeValueUnits(self, unit): self.gaugeValueUnits = unit def setMinGaugeValue(self, min): if self.gaugeValue < min: self.gaugeValue = min if min >= self.maxGaugeValue: self.minGaugeValue = self.maxGaugeValue - 1 else: self.minGaugeValue = min self.update() def setMaxGaugeValue(self, max): if self.gaugeValue > max: self.gaugeValue = max if max <= self.minGaugeValue: self.maxGaugeValue = self.minGaugeValue + 1 else: self.maxGaugeValue = max self.update() def setGaugeRotation(self, value): self.gaugeRotation = value self.update() def setGaugeArcAngle(self, value): self.gaugeArcAngle = value self.update() def setGaugeOuterRadius(self, value): self.gaugeOuterRadius = float(value) / 1000 self.update() def setGaugeInnerRadius(self, value): self.gaugeInnerRadius = float(value) / 1000 self.update() def setGaugeColors(self, colorArray): if 'list' in str(type(colorArray)): self.gaugeColors = colorArray elif colorArray == None: self.gaugeColors = [[.0, Qt.transparent]] else: self.gaugeColors = [[.0, Qt.transparent]] self.update() ############################################################################################### # Get Methods ############################################################################################### def getMaxGaugeValue(self): return self.maxGaugeValue ############################################################################################### # Painter ############################################################################################### def drawGauge(self, outerRadius, innerRadius, start, length): gauge = QPolygonF() n = 360 # angle steps size for full circle w = 360 / n # angle per step x = 0 y = 0 if not self.GaugeColorBarsEnabled: length = int( round((length / (self.maxGaugeValue - self.minGaugeValue)) * (self.gaugeValue - self.minGaugeValue))) # add the points of polygon for i in range(length + 1): t = w * i + start x = outerRadius * math.cos(math.radians(t)) y = outerRadius * math.sin(math.radians(t)) gauge.append(QPointF(x, y)) # create inner circle line from "start + length"-angle to "start"-angle for i in range(length + 1): # add the points of polygon t = w * (length - i) + start x = innerRadius * math.cos(math.radians(t)) y = innerRadius * math.sin(math.radians(t)) gauge.append(QPointF(x, y)) # close outer line gauge.append(QPointF(x, y)) return gauge def drawGaugeAnnulus(self, outlinePenWith=0): if not self.gaugeColors == None: gaugeAnnulus = QPainter(self) gaugeAnnulus.setRenderHint(QPainter.Antialiasing) gaugeAnnulus.translate(self.width() / 2, self.height() / 2) gaugeAnnulus.setPen(Qt.NoPen) self.pen.setWidth(outlinePenWith) if outlinePenWith > 0: gaugeAnnulus.setPen(self.pen) coloredScalePolygon = self.drawGauge( ((self.widgetDiameter / 2) - (self.pen.width() / 2)) * self.gaugeOuterRadius, (((self.widgetDiameter / 2) - (self.pen.width() / 2)) * self.gaugeInnerRadius), self.gaugeRotation, self.gaugeArcAngle) gradient = QConicalGradient( QPointF(0, 0), -self.gaugeArcAngle - self.gaugeRotation + -1) for eachcolor in self.gaugeColors: gradient.setColorAt(eachcolor[0], eachcolor[1]) gaugeAnnulus.setBrush(gradient) gaugeAnnulus.drawPolygon(coloredScalePolygon) ############################################################################################### # Gauge Axis Markers ############################################################################################### def drawGaugeMajorAxisMarkers(self): myPainter = QPainter(self) myPainter.setRenderHint(QPainter.Antialiasing) myPainter.translate(self.width() / 2, self.height() / 2) self.pen = QPen(QColor(0, 0, 0, 255)) self.pen.setWidth(2) myPainter.setPen(self.pen) myPainter.rotate(self.gaugeRotation) stepsSize = (float(self.gaugeArcAngle) / float(self.gaugeValueMajorAxisCount)) scaleLineOuterStart = self.widgetDiameter / 2 scaleLineLength = (self.widgetDiameter / 2) - (self.widgetDiameter / 20) for i in range(self.gaugeValueMajorAxisCount + 1): myPainter.drawLine(scaleLineLength, 0, scaleLineOuterStart, 0) myPainter.rotate(stepsSize) def drawGaugeValues(self): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) font = QFont(self.gaugeValueFont, self.gaugeValueFontSize) fm = QFontMetrics(font) penShadow = QPen() penShadow.setBrush(self.gaugeValuesColor) painter.setPen(penShadow) gaugeValueRadiusFactor = 0.8 gaugeValueRadius = self.widgetDiameter / 2 * gaugeValueRadiusFactor scalePerDiv = int((self.maxGaugeValue - self.minGaugeValue) / self.gaugeValueMajorAxisCount) angleDistance = (float(self.gaugeArcAngle) / float(self.gaugeValueMajorAxisCount)) for i in range(self.gaugeValueMajorAxisCount + 1): text = str(int(self.minGaugeValue + scalePerDiv * i)) w = fm.width(text) + 1 h = fm.height() painter.setFont(QFont(self.gaugeValueFont, self.gaugeValueFontSize)) angle = angleDistance * i + float(self.gaugeRotation) x = gaugeValueRadius * math.cos(math.radians(angle)) y = gaugeValueRadius * math.sin(math.radians(angle)) text = [ x - int(w / 2), y - int(h / 2), int(w), int(h), Qt.AlignCenter, text ] painter.drawText(text[0], text[1], text[2], text[3], text[4], text[5]) def drawGaugeMinorAxisMarkers(self): myPainter = QPainter(self) myPainter.setRenderHint(QPainter.Antialiasing) myPainter.translate(self.width() / 2, self.height() / 2) myPainter.setPen(Qt.black) myPainter.rotate(self.gaugeRotation) stepsSize = (float(self.gaugeArcAngle) / float( self.gaugeValueMajorAxisCount * self.gaugeValueMinorAxisCount)) scaleLineOuterStart = self.widgetDiameter / 2 scaleLineLength = (self.widgetDiameter / 2) - (self.widgetDiameter / 40) for i in range((self.gaugeValueMajorAxisCount * self.gaugeValueMinorAxisCount) + 1): myPainter.drawLine(scaleLineLength, 0, scaleLineOuterStart, 0) myPainter.rotate(stepsSize) def drawDigitalValue(self): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) font = QFont(self.digitalValueFontName, self.digitalValueFontSize) fm = QFontMetrics(font) penShadow = QPen() penShadow.setBrush(self.digitalValueColor) painter.setPen(penShadow) digitalValueRadius = self.widgetDiameter / 2 * self.digitalValueRadius text = str(int(self.gaugeValue)) w = fm.width(text) + 1 h = fm.height() painter.setFont( QFont(self.digitalValueFontName, self.digitalValueFontSize)) angleEnd = float(self.gaugeRotation + self.gaugeArcAngle - 360) angle = (angleEnd - self.gaugeRotation) / 2 + self.gaugeRotation x = digitalValueRadius * math.cos(math.radians(angle)) y = digitalValueRadius * math.sin(math.radians(angle)) offset = 20 if self.gaugeValueUnits else 0 text = [ x - int(w / 2) - offset, y - int(h / 2), int(w), int(h), Qt.AlignCenter, text ] painter.drawText(text[0], text[1], text[2], text[3], text[4], text[5]) self.drawDigitalValueUnits() def drawDigitalValueUnits(self): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) font = QFont(self.digitalValueFontName, self.digitalValueUnitsFontSize) fm = QFontMetrics(font) penShadow = QPen() penShadow.setBrush(self.digitalValueColor) painter.setPen(penShadow) digitalValueRadius = self.widgetDiameter / 2 * self.digitalValueRadius text = self.gaugeValueUnits w = fm.width(text) + 1 h = fm.height() painter.setFont( QFont(self.digitalValueFontName, self.digitalValueUnitsFontSize)) angleEnd = float(self.gaugeRotation + self.gaugeArcAngle - 360) angle = (angleEnd - self.gaugeRotation) / 2 + self.gaugeRotation x = digitalValueRadius * math.cos(math.radians(angle)) y = digitalValueRadius * math.sin(math.radians(angle)) text = [ x - int(w / 2) + 20, y - int(h / 2), int(w), int(h), Qt.AlignCenter, text ] painter.drawText(text[0], text[1], text[2], text[3], text[4], text[5]) def drawNeedleHub(self, diameter=30): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) painter.setPen(Qt.NoPen) painter.setBrush(self.needleHubColor) painter.drawEllipse(int(-diameter / 2), int(-diameter / 2), int(diameter), int(diameter)) def drawNeedle(self): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) painter.setPen(Qt.NoPen) painter.setBrush(self.needleColor) painter.rotate(( (self.gaugeValue - self.minGaugeValue) * self.gaugeArcAngle / (self.maxGaugeValue - self.minGaugeValue)) + 90 + self.gaugeRotation) painter.drawConvexPolygon(self.needle[0]) ############################################################################################### # Events ############################################################################################### def resizeEvent(self, event): self.resizeGauge() def paintEvent(self, event): # Main Drawing Event: Executed on every change # draw gauge annulus if self.gaugeAnnulusEnabled: self.drawGaugeAnnulus() # draw gauge axis markers if self.gaugeMinorAxisMarkerEnabled: self.drawGaugeMinorAxisMarkers() if self.gaugeMajorAxisMarkerEnabled: self.drawGaugeMajorAxisMarkers() # draw gauge values if self.gaugeValuesEnabled: self.drawGaugeValues() # display digital value if self.digitalValueEnabled: self.drawDigitalValue() # draw needle if self.needleEnabled: self.drawNeedle() # draw needle hub if self.needleHubEnabled: self.drawNeedleHub(diameter=(self.widgetDiameter / 6))
class ArrowGraphicsItem(QGraphicsItem): Type = QGraphicsItem.UserType + 1 def __init__(self, hero, move, fromSquare, toSquare, squareWidth): super(ArrowGraphicsItem, self).__init__() self.squareWidth = squareWidth self.move = move self.fromSquare = fromSquare self.toSquare = toSquare self.sourcePoint = self.fromSquare.pos() + \ QPointF(self.squareWidth / 2, self.squareWidth / 2) self.destPoint = self.toSquare.pos() + \ QPointF(self.squareWidth / 2, self.squareWidth / 2) self.arrowSize = float(userConfig.config['BOARD']['arrowSize']) self.hero = hero if self.hero: col = QColor(userConfig.config['BOARD']['heroArrowColor']) else: col = QColor(userConfig.config['BOARD']['enemyArrowColor']) self.createPallette(col) def type(self): return self.Type def createPallette(self, col): self.brush = QBrush(col) self.pen = QPen(self.brush, float(userConfig.config['BOARD']['arrowWidth']), Qt.SolidLine, Qt.RoundCap, Qt.BevelJoin) def changeHero(self): if self.hero: col = QColor(userConfig.config['BOARD']['enemyArrowColor']) else: col = QColor(userConfig.config['BOARD']['heroArrowColor']) self.createPallette(col) self.hero = not self.hero def adjust(self): self.prepareGeometryChange() self.sourcePoint = self.fromSquare.pos() + \ QPointF(self.squareWidth / 2, self.squareWidth / 2) self.destPoint = self.toSquare.pos() + \ QPointF(self.squareWidth / 2, self.squareWidth / 2) def boundingRect(self): extra = (self.pen.width() + self.arrowSize) / 2.0 return QRectF(self.sourcePoint, QSizeF(self.destPoint.x() - self.sourcePoint.x(), self.destPoint.y() - self.sourcePoint.y())) \ .normalized().adjusted(-extra, -extra, extra, extra) def paint(self, painter, option, widget): assert self.fromSquare is not None assert self.toSquare is not None line = QLineF(self.sourcePoint, self.destPoint) assert(line.length() != 0.0) # Draw the arrows if there's enough room. angle = math.acos(line.dx() / line.length()) if line.dy() >= 0: angle = (math.pi*2.0) - angle destArrowP1 = self.destPoint + QPointF( math.sin(angle - math.pi / 3) * self.arrowSize, math.cos(angle - math.pi / 3) * self.arrowSize ) destArrowP2 = self.destPoint + QPointF( math.sin(angle - math.pi + math.pi / 3) * self.arrowSize, math.cos(angle - math.pi + math.pi / 3) * self.arrowSize ) painter.setPen(self.pen) painter.setBrush(self.brush) # arrowhead1 = QPolygonF([line.p1(), sourceArrowP1, sourceArrowP2]) arrowhead2 = QPolygonF([line.p2(), destArrowP1, destArrowP2]) painter.drawPolygon(arrowhead2) painter.setPen(self.pen) painter.drawLine(line)
class TemplateConfigurationScene(QGraphicsScene): first_point_added = pyqtSignal() second_point_added = pyqtSignal() def __init__(self, camera_width: int): super().__init__() self.upper_left_selected = False self.bottom_right_selected = False self.line_left = None self.line_top = None self.line_bot = None self.line_right = None self.upper_left = None self.bottom_right = None self.pen = QPen() self.pen.setColor(Qt.green) self.pen.setWidth(2) self.first_mouse_move = True self.scale = camera_width / 470 self.mouse_hori_line = QGraphicsLineItem(0, 0, 0, 0) self.mouse_vert_line = QGraphicsLineItem(0, 0, 0, 0) self.mouse_hori_line.setPen(self.pen) self.mouse_vert_line.setPen(self.pen) def _reset(self) -> None: self.removeItem(self.line_left) self.removeItem(self.line_right) self.removeItem(self.line_top) self.removeItem(self.line_bot) self.upper_left_selected = False self.bottom_right_selected = False self.first_mouse_move = True def _is_mouse_on_scene(self, position) -> None: return (position.x() < self.sceneRect().width() - self.pen.width()) \ and (position.y() < self.sceneRect().height() - self.pen.width()) def mouseMoveEvent(self, event) -> None: if self._is_mouse_on_scene(event.scenePos()): self.mouse_hori_line.setLine( 0, event.scenePos().y(), self.sceneRect().width() - self.pen.width(), event.scenePos().y()) self.mouse_vert_line.setLine( event.scenePos().x(), 0, event.scenePos().x(), self.sceneRect().height() - self.pen.width()) if self.first_mouse_move: self.first_mouse_move = False self.addItem(self.mouse_hori_line) self.addItem(self.mouse_vert_line) def mousePressEvent(self, event) -> None: if self._is_mouse_on_scene(event.scenePos()): if not self.upper_left_selected: self.upper_left_selected = True self.upper_left = event.scenePos() * self.scale self.line_top = QGraphicsLineItem( 0, event.scenePos().y(), self.sceneRect().width() - self.pen.width(), event.scenePos().y()) self.line_left = QGraphicsLineItem( event.scenePos().x(), 0, event.scenePos().x(), self.sceneRect().height() - self.pen.width()) self.line_top.setPen(self.pen) self.line_left.setPen(self.pen) self.addItem(self.line_top) self.addItem(self.line_left) self.first_point_added.emit() elif not self.bottom_right_selected: self.bottom_right_selected = True self.bottom_right = event.scenePos() * self.scale self.line_bot = QGraphicsLineItem( 0, event.scenePos().y(), self.sceneRect().width() - self.pen.width(), event.scenePos().y()) self.line_right = QGraphicsLineItem( event.scenePos().x(), 0, event.scenePos().x(), self.sceneRect().height() - self.pen.width()) self.line_bot.setPen(self.pen) self.line_right.setPen(self.pen) self.addItem(self.line_bot) self.addItem(self.line_right) self.removeItem(self.mouse_vert_line) self.removeItem(self.mouse_hori_line) self.second_point_added.emit()
class AnalogGaugeWidget(QWidget): """Fetches rows from a Bigtable. Args: none """ valueChanged = pyqtSignal(int) def __init__(self, parent=None): super(AnalogGaugeWidget, self).__init__(parent) self.use_timer_event = False self.black = QColor(0, 0, 0, 255) # self.valueColor = QColor(50, 50, 50, 255) # self.set_valueColor(50, 50, 50, 255) # self.NeedleColor = QColor(50, 50, 50, 255) self.set_NeedleColor(50, 50, 50, 255) self.NeedleColorReleased = self.NeedleColor # self.NeedleColorDrag = QColor(255, 0, 00, 255) self.set_NeedleColorDrag(255, 0, 00, 255) self.set_ScaleValueColor(50, 50, 50, 255) self.set_DisplayValueColor(50, 50, 50, 255) # self.CenterPointColor = QColor(50, 50, 50, 255) self.set_CenterPointColor(50, 50, 50, 255) # self.valueColor = black # self.black = QColor(0, 0, 0, 255) self.value_needle_count = 1 self.value_needle = QObject self.change_value_needle_style([ QPolygon([ QPoint(4, 4), QPoint(-4, 4), QPoint(-3, -120), QPoint(0, -126), QPoint(3, -120) ]) ]) self.value_min = 0 self.value_max = 1000 self.value = self.value_min self.value_offset = 0 self.value_needle_snapzone = 0.05 self.last_value = 0 # self.value2 = 0 # self.value2Color = QColor(0, 0, 0, 255) self.gauge_color_outer_radius_factor = 1 self.gauge_color_inner_radius_factor = 0.95 self.center_horizontal_value = 0 self.center_vertical_value = 0 self.debug1 = None self.debug2 = None self.scale_angle_start_value = 135 self.scale_angle_size = 270 self.angle_offset = 0 # self.scala_main_count = 10 self.set_scala_main_count(10) self.scala_subdiv_count = 5 self.pen = QPen(QColor(0, 0, 0)) self.font = QFont('Decorative', 20) self.scale_polygon_colors = [] self.set_scale_polygon_colors([[.00, Qt.red], [.1, Qt.yellow], [.15, Qt.green], [1, Qt.transparent]]) # initialize Scale value text # self.enable_scale_text = True self.set_enable_ScaleText(True) self.scale_fontname = "Decorative" self.initial_scale_fontsize = 15 self.scale_fontsize = self.initial_scale_fontsize # initialize Main value text self.enable_value_text = True self.value_fontname = "Decorative" self.initial_value_fontsize = 40 self.value_fontsize = self.initial_value_fontsize self.text_radius_factor = 0.7 # En/disable scale / fill # self.enable_barGraph = True self.set_enable_barGraph(True) # self.enable_filled_Polygon = True self.set_enable_filled_Polygon(True) self.enable_CenterPoint = True self.enable_fine_scaled_marker = True self.enable_big_scaled_marker = True self.needle_scale_factor = 0.8 self.enable_Needle_Polygon = True # necessary for resize self.setMouseTracking(False) # QTimer sorgt für neu Darstellung alle X ms # evtl performance hier verbessern mit self.update() und self.use_timer_event = False # todo: self.update als default ohne ueberpruefung, ob self.use_timer_event gesetzt ist oder nicht # Timer startet alle 10ms das event paintEvent if self.use_timer_event: timer = QTimer(self) timer.timeout.connect(self.update) timer.start(10) else: self.update() self.setWindowTitle("Analog Gauge") # self.connect(self, SIGNAL("resize()"), self.rescaleMethod) # self.resize(300 , 300) self.rescale_method() def rescale_method(self): # print("slotMethod") if self.width() <= self.height(): self.widget_diameter = self.width() else: self.widget_diameter = self.height() self.change_value_needle_style([ QPolygon([ QPoint(4, 30), QPoint(-4, 30), QPoint(-2, -self.widget_diameter / 2 * self.needle_scale_factor), QPoint( 0, -self.widget_diameter / 2 * self.needle_scale_factor - 6), QPoint(2, -self.widget_diameter / 2 * self.needle_scale_factor) ]) ]) # needle = [QPolygon([ # QPoint(4, 4), # QPoint(-4, 4), # QPoint(-3, -120), # QPoint(0, -126), # QPoint(3, -120)])] # print(str(type(needle)).split("'")[1]) # # needle = [2] # print(str(type(needle[0])).split("'")[1]) self.scale_fontsize = self.initial_scale_fontsize * self.widget_diameter / 400 self.value_fontsize = self.initial_value_fontsize * self.widget_diameter / 400 # print("slotMethod end") pass def change_value_needle_style(self, design): # prepared for multiple needle instrument self.value_needle = [] for i in design: self.value_needle.append(i) if not self.use_timer_event: self.update() def update_value(self, value, mouse_controlled=False): # if not mouse_controlled: # self.value = value # # if mouse_controlled: # self.valueChanged.emit(int(value)) if value <= self.value_min: self.value = self.value_min elif value >= self.value_max: self.value = self.value_max else: self.value = value # self.paintEvent("") self.valueChanged.emit(int(value)) # print(self.value) # ohne timer: aktiviere self.update() if not self.use_timer_event: self.update() def update_angle_offset(self, offset): self.angle_offset = offset if not self.use_timer_event: self.update() def center_horizontal(self, value): self.center_horizontal_value = value # print("horizontal: " + str(self.center_horizontal_value)) def center_vertical(self, value): self.center_vertical_value = value # print("vertical: " + str(self.center_vertical_value)) ############################################################################################### # Set Methods ############################################################################################### def set_NeedleColor(self, R=50, G=50, B=50, Transparency=255): # Red: R = 0 - 255 # Green: G = 0 - 255 # Blue: B = 0 - 255 # Transparency = 0 - 255 self.NeedleColor = QColor(R, G, B, Transparency) self.NeedleColorReleased = self.NeedleColor if not self.use_timer_event: self.update() def set_NeedleColorDrag(self, R=50, G=50, B=50, Transparency=255): # Red: R = 0 - 255 # Green: G = 0 - 255 # Blue: B = 0 - 255 # Transparency = 0 - 255 self.NeedleColorDrag = QColor(R, G, B, Transparency) if not self.use_timer_event: self.update() def set_ScaleValueColor(self, R=50, G=50, B=50, Transparency=255): # Red: R = 0 - 255 # Green: G = 0 - 255 # Blue: B = 0 - 255 # Transparency = 0 - 255 self.ScaleValueColor = QColor(R, G, B, Transparency) if not self.use_timer_event: self.update() def set_DisplayValueColor(self, R=50, G=50, B=50, Transparency=255): # Red: R = 0 - 255 # Green: G = 0 - 255 # Blue: B = 0 - 255 # Transparency = 0 - 255 self.DisplayValueColor = QColor(R, G, B, Transparency) if not self.use_timer_event: self.update() def set_CenterPointColor(self, R=50, G=50, B=50, Transparency=255): self.CenterPointColor = QColor(R, G, B, Transparency) if not self.use_timer_event: self.update() def set_enable_Needle_Polygon(self, enable=True): self.enable_Needle_Polygon = enable if not self.use_timer_event: self.update() def set_enable_ScaleText(self, enable=True): self.enable_scale_text = enable if not self.use_timer_event: self.update() def set_enable_barGraph(self, enable=True): self.enable_barGraph = enable if not self.use_timer_event: self.update() def set_enable_value_text(self, enable=True): self.enable_value_text = enable if not self.use_timer_event: self.update() def set_enable_CenterPoint(self, enable=True): self.enable_CenterPoint = enable if not self.use_timer_event: self.update() def set_enable_filled_Polygon(self, enable=True): self.enable_filled_Polygon = enable if not self.use_timer_event: self.update() def set_enable_big_scaled_grid(self, enable=True): self.enable_big_scaled_marker = enable if not self.use_timer_event: self.update() def set_enable_fine_scaled_marker(self, enable=True): self.enable_fine_scaled_marker = enable if not self.use_timer_event: self.update() def set_scala_main_count(self, count): if count < 1: count = 1 self.scala_main_count = count if not self.use_timer_event: self.update() def set_MinValue(self, min): if self.value < min: self.value = min if min >= self.value_max: self.value_min = self.value_max - 1 else: self.value_min = min if not self.use_timer_event: self.update() def set_MaxValue(self, max): if self.value > max: self.value = max if max <= self.value_min: self.value_max = self.value_min + 1 else: self.value_max = max if not self.use_timer_event: self.update() def set_start_scale_angle(self, value): # Value range in DEG: 0 - 360 self.scale_angle_start_value = value # print("startFill: " + str(self.scale_angle_start_value)) if not self.use_timer_event: self.update() def set_total_scale_angle_size(self, value): self.scale_angle_size = value # print("stopFill: " + str(self.scale_angle_size)) if not self.use_timer_event: self.update() def set_gauge_color_outer_radius_factor(self, value): self.gauge_color_outer_radius_factor = float(value) / 1000 # print(self.gauge_color_outer_radius_factor) if not self.use_timer_event: self.update() def set_gauge_color_inner_radius_factor(self, value): self.gauge_color_inner_radius_factor = float(value) / 1000 # print(self.gauge_color_inner_radius_factor) if not self.use_timer_event: self.update() def set_scale_polygon_colors(self, color_array): # print(type(color_array)) if 'list' in str(type(color_array)): self.scale_polygon_colors = color_array elif color_array == None: self.scale_polygon_colors = [[.0, Qt.transparent]] else: self.scale_polygon_colors = [[.0, Qt.transparent]] if not self.use_timer_event: self.update() ############################################################################################### # Get Methods ############################################################################################### def get_value_max(self): return self.value_max ############################################################################################### # Painter ############################################################################################### def create_polygon_pie(self, outer_radius, inner_raduis, start, lenght): polygon_pie = QPolygonF() # start = self.scale_angle_start_value # start = 0 # lenght = self.scale_angle_size # lenght = 180 # inner_raduis = self.width()/4 # print(start) n = 360 # angle steps size for full circle # changing n value will causes drawing issues w = 360 / n # angle per step # create outer circle line from "start"-angle to "start + lenght"-angle x = 0 y = 0 # todo enable/disable bar graf here if not self.enable_barGraph: # float_value = ((lenght / (self.value_max - self.value_min)) * (self.value - self.value_min)) lenght = int( round((lenght / (self.value_max - self.value_min)) * (self.value - self.value_min))) # print("f: %s, l: %s" %(float_value, lenght)) pass # mymax = 0 for i in range(lenght + 1): # add the points of polygon t = w * i + start - self.angle_offset x = outer_radius * math.cos(math.radians(t)) y = outer_radius * math.sin(math.radians(t)) polygon_pie.append(QPointF(x, y)) # create inner circle line from "start + lenght"-angle to "start"-angle for i in range(lenght + 1): # add the points of polygon # print("2 " + str(i)) t = w * (lenght - i) + start - self.angle_offset x = inner_raduis * math.cos(math.radians(t)) y = inner_raduis * math.sin(math.radians(t)) polygon_pie.append(QPointF(x, y)) # close outer line polygon_pie.append(QPointF(x, y)) return polygon_pie def draw_filled_polygon(self, outline_pen_with=0): if not self.scale_polygon_colors == None: painter_filled_polygon = QPainter(self) painter_filled_polygon.setRenderHint(QPainter.Antialiasing) # Koordinatenursprung in die Mitte der Flaeche legen painter_filled_polygon.translate(self.width() / 2, self.height() / 2) painter_filled_polygon.setPen(Qt.NoPen) self.pen.setWidth(outline_pen_with) if outline_pen_with > 0: painter_filled_polygon.setPen(self.pen) colored_scale_polygon = self.create_polygon_pie( ((self.widget_diameter / 2) - (self.pen.width() / 2)) * self.gauge_color_outer_radius_factor, (((self.widget_diameter / 2) - (self.pen.width() / 2)) * self.gauge_color_inner_radius_factor), self.scale_angle_start_value, self.scale_angle_size) gauge_rect = QRect( QPoint(0, 0), QSize(self.widget_diameter / 2 - 1, self.widget_diameter - 1)) grad = QConicalGradient( QPointF(0, 0), -self.scale_angle_size - self.scale_angle_start_value + self.angle_offset - 1) # todo definition scale color as array here for eachcolor in self.scale_polygon_colors: grad.setColorAt(eachcolor[0], eachcolor[1]) # grad.setColorAt(.00, Qt.red) # grad.setColorAt(.1, Qt.yellow) # grad.setColorAt(.15, Qt.green) # grad.setColorAt(1, Qt.transparent) painter_filled_polygon.setBrush(grad) # self.brush = QBrush(QColor(255, 0, 255, 255)) # painter_filled_polygon.setBrush(self.brush) painter_filled_polygon.drawPolygon(colored_scale_polygon) # return painter_filled_polygon ############################################################################################### # Scale Marker ############################################################################################### def draw_big_scaled_markter(self): my_painter = QPainter(self) my_painter.setRenderHint(QPainter.Antialiasing) # Koordinatenursprung in die Mitte der Flaeche legen my_painter.translate(self.width() / 2, self.height() / 2) # my_painter.setPen(Qt.NoPen) self.pen = QPen(QColor(0, 0, 0, 255)) self.pen.setWidth(2) # # if outline_pen_with > 0: my_painter.setPen(self.pen) my_painter.rotate(self.scale_angle_start_value - self.angle_offset) steps_size = (float(self.scale_angle_size) / float(self.scala_main_count)) scale_line_outer_start = self.widget_diameter / 2 scale_line_lenght = (self.widget_diameter / 2) - (self.widget_diameter / 20) # print(stepszize) for i in range(self.scala_main_count + 1): my_painter.drawLine(scale_line_lenght, 0, scale_line_outer_start, 0) my_painter.rotate(steps_size) def create_scale_marker_values_text(self): painter = QPainter(self) # painter.setRenderHint(QPainter.HighQualityAntialiasing) painter.setRenderHint(QPainter.Antialiasing) # Koordinatenursprung in die Mitte der Flaeche legen painter.translate(self.width() / 2, self.height() / 2) # painter.save() font = QFont(self.scale_fontname, self.scale_fontsize) fm = QFontMetrics(font) pen_shadow = QPen() pen_shadow.setBrush(self.ScaleValueColor) painter.setPen(pen_shadow) text_radius_factor = 0.8 text_radius = self.widget_diameter / 2 * text_radius_factor scale_per_div = int( (self.value_max - self.value_min) / self.scala_main_count) angle_distance = (float(self.scale_angle_size) / float(self.scala_main_count)) for i in range(self.scala_main_count + 1): # text = str(int((self.value_max - self.value_min) / self.scala_main_count * i)) text = str(int(self.value_min + scale_per_div * i)) w = fm.width(text) + 1 h = fm.height() painter.setFont(QFont(self.scale_fontname, self.scale_fontsize)) angle = angle_distance * i + float(self.scale_angle_start_value - self.angle_offset) x = text_radius * math.cos(math.radians(angle)) y = text_radius * math.sin(math.radians(angle)) # print(w, h, x, y, text) text = [ x - int(w / 2), y - int(h / 2), int(w), int(h), Qt.AlignCenter, text ] painter.drawText(text[0], text[1], text[2], text[3], text[4], text[5]) # painter.restore() def create_fine_scaled_marker(self): # Description_dict = 0 my_painter = QPainter(self) my_painter.setRenderHint(QPainter.Antialiasing) # Koordinatenursprung in die Mitte der Flaeche legen my_painter.translate(self.width() / 2, self.height() / 2) my_painter.setPen(Qt.black) my_painter.rotate(self.scale_angle_start_value - self.angle_offset) steps_size = (float(self.scale_angle_size) / float(self.scala_main_count * self.scala_subdiv_count)) scale_line_outer_start = self.widget_diameter / 2 scale_line_lenght = (self.widget_diameter / 2) - (self.widget_diameter / 40) for i in range((self.scala_main_count * self.scala_subdiv_count) + 1): my_painter.drawLine(scale_line_lenght, 0, scale_line_outer_start, 0) my_painter.rotate(steps_size) def create_values_text(self): painter = QPainter(self) # painter.setRenderHint(QPainter.HighQualityAntialiasing) painter.setRenderHint(QPainter.Antialiasing) # Koordinatenursprung in die Mitte der Flaeche legen painter.translate(self.width() / 2, self.height() / 2) # painter.save() # xShadow = 3.0 # yShadow = 3.0 font = QFont(self.value_fontname, self.value_fontsize) fm = QFontMetrics(font) pen_shadow = QPen() pen_shadow.setBrush(self.DisplayValueColor) painter.setPen(pen_shadow) text_radius = self.widget_diameter / 2 * self.text_radius_factor # angle_distance = (float(self.scale_angle_size) / float(self.scala_main_count)) # for i in range(self.scala_main_count + 1): text = str(int(self.value)) w = fm.width(text) + 1 h = fm.height() painter.setFont(QFont(self.value_fontname, self.value_fontsize)) # Mitte zwischen Skalenstart und Skalenende: # Skalenende = Skalenanfang - 360 + Skalenlaenge # Skalenmitte = (Skalenende - Skalenanfang) / 2 + Skalenanfang angle_end = float(self.scale_angle_start_value + self.scale_angle_size - 360) angle = (angle_end - self.scale_angle_start_value ) / 2 + self.scale_angle_start_value x = text_radius * math.cos(math.radians(angle)) y = text_radius * math.sin(math.radians(angle)) # print(w, h, x, y, text) text = [ x - int(w / 2), y - int(h / 2), int(w), int(h), Qt.AlignCenter, text ] painter.drawText(text[0], text[1], text[2], text[3], text[4], text[5]) # painter.restore() def draw_big_needle_center_point(self, diameter=30): painter = QPainter(self) # painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing) painter.setRenderHint(QPainter.Antialiasing) # Koordinatenursprung in die Mitte der Flaeche legen painter.translate(self.width() / 2, self.height() / 2) painter.setPen(Qt.NoPen) # painter.setPen(Qt.NoPen) painter.setBrush(self.CenterPointColor) # diameter = diameter # self.widget_diameter/6 painter.drawEllipse(int(-diameter / 2), int(-diameter / 2), int(diameter), int(diameter)) def draw_needle(self): painter = QPainter(self) # painter.setRenderHint(QtGui.QPainter.HighQualityAntialiasing) painter.setRenderHint(QPainter.Antialiasing) # Koordinatenursprung in die Mitte der Flaeche legen painter.translate(self.width() / 2, self.height() / 2) painter.setPen(Qt.NoPen) painter.setBrush(self.NeedleColor) painter.rotate(((self.value - self.value_offset - self.value_min) * self.scale_angle_size / (self.value_max - self.value_min)) + 90 + self.scale_angle_start_value) painter.drawConvexPolygon(self.value_needle[0]) ############################################################################################### # Events ############################################################################################### def resizeEvent(self, event): # self.resized.emit() # return super(self.parent, self).resizeEvent(event) # print("resized") # print(self.width()) self.rescale_method() # self.emit(QtCore.SIGNAL("resize()")) # print("resizeEvent") def paintEvent(self, event): # Main Drawing Event: # Will be executed on every change # vgl http://doc.qt.io/qt-4.8/qt-demos-affine-xform-cpp.html # print("event", event) # colored pie area if self.enable_filled_Polygon: self.draw_filled_polygon() # draw scale marker lines if self.enable_fine_scaled_marker: self.create_fine_scaled_marker() if self.enable_big_scaled_marker: self.draw_big_scaled_markter() # draw scale marker value text if self.enable_scale_text: self.create_scale_marker_values_text() # Display Value if self.enable_value_text: self.create_values_text() # draw needle 1 if self.enable_Needle_Polygon: self.draw_needle() # Draw Center Point if self.enable_CenterPoint: self.draw_big_needle_center_point(diameter=(self.widget_diameter / 6)) ############################################################################################### # MouseEvents ############################################################################################### def setMouseTracking(self, flag): def recursive_set(parent): for child in parent.findChildren(QObject): try: child.setMouseTracking(flag) except: pass recursive_set(child) QWidget.setMouseTracking(self, flag) recursive_set(self) def mouseReleaseEvent(self, QMouseEvent): # print("released") self.NeedleColor = self.NeedleColorReleased if not self.use_timer_event: self.update() pass def mouseMoveEvent(self, event): x, y = event.x() - (self.width() / 2), event.y() - (self.height() / 2) if not x == 0: angle = math.atan2(y, x) / math.pi * 180 # winkellaenge der anzeige immer positiv 0 - 360deg # min wert + umskalierter wert value = (float(math.fmod(angle - self.scale_angle_start_value + 720, 360)) / \ (float(self.scale_angle_size) / float(self.value_max - self.value_min))) + self.value_min temp = value fmod = float( math.fmod(angle - self.scale_angle_start_value + 720, 360)) state = 0 if (self.value - (self.value_max - self.value_min) * self.value_needle_snapzone) <= \ value <= \ (self.value + (self.value_max - self.value_min) * self.value_needle_snapzone): self.NeedleColor = self.NeedleColorDrag # todo: evtl ueberpruefen # state = 9 # if value >= self.value_max and self.last_value < (self.value_max - self.value_min) / 2: if value >= self.value_max and self.last_value < ( self.value_max - self.value_min) / 2: state = 1 value = self.value_max self.last_value = self.value_min self.valueChanged.emit(int(value)) elif value >= self.value_max >= self.last_value: state = 2 value = self.value_max self.last_value = self.value_max self.valueChanged.emit(int(value)) else: state = 3 self.last_value = value self.valueChanged.emit(int(value))
class GaugeWidget(QWidget): def __init__(self, _width=400, _height=400, parent=None): super(GaugeWidget, self).__init__(parent) self.setWidth(_width) self.setHeight(_height) self.pen = QPen(QColor(0, 0, 0)) self.widgetDiameter = 0 if _width > _height: self.widgetDiameter = _width else: self.widgetDiameter = _height self.outerRadiusFactor = 1 self.innerRadiusFactor = 0.9 self.scaleAngleStartValue = 165 self.scaleAngleSize = 210 self.angleOffset = 0 self.scalePolygonColors = [[.0, Qt.red], [.33, Qt.yellow], [.66, Qt.green], [1, Qt.transparent]] self.scaleMainCount = 10 # For main ticks self.scaleSubDivisionCount = 5 # for inner ticks self.scaleValueColor = QColor(50, 50, 50, 255) self.needleColor = QColor(50, 50, 50, 255) self.centerPointColor = QColor(20, 20, 20, 255) self.valueMin = 0 self.valueMax = 100 #self.valueFontName = "Decorative" self.valueFontName = "Lucida" self.valueFontSize = 40 self.textRadiusFactor = 0.7 self.displayValueColor = QColor(50, 50, 50, 255) self.value = 25 #self.valueMin self.valueOffset = 0 self.scaleFontName = "Decorative" self.scaleFontSize = 15 self.valueNeedle = [ QPolygon([ QPoint(4, 4), QPoint(-4, 4), QPoint(-3, -1 * (self.height() * self.innerRadiusFactor / 2)), QPoint(0, -6 - (self.height() * self.innerRadiusFactor / 2)), QPoint(3, -1 * (self.height() * self.innerRadiusFactor / 2)) ]) ] def setWidth(self, _width): self.resize(_width, self.height()) def setHeight(self, _height): self.resize(self.width(), _height) def paintEvent(self, event): self.drawFilledPolygon() self.createFineScaledMarker() self.drawBigScaledMarker() self.createScaleMarkerValuesText() self.createValuesText() self.drawNeedle() self.drawNeedleCenterPoint(diameter=(self.widgetDiameter / 6)) def createPloygonPie(self, outerRadius, innerRaduis, start, lenght): """ Create Polygon for given parameters""" polygonPie = QPolygonF() n = 360 # angle steps size for full circle # changing n value will causes drawing issues w = 360 / n # angle per step # create outer circle line from "start"-angle to "start + lenght"-angle x = 0 y = 0 for i in range(lenght + 1): # add the points of polygon t = w * i + start - self.angleOffset x = outerRadius * math.cos(math.radians(t)) y = outerRadius * math.sin(math.radians(t)) polygonPie.append(QPointF(x, y)) # create inner circle line from "start + lenght"-angle to "start"-angle for i in range(lenght + 1): # add the points of polygon # print("2 " + str(i)) t = w * (lenght - i) + start - self.angleOffset x = innerRaduis * math.cos(math.radians(t)) y = innerRaduis * math.sin(math.radians(t)) polygonPie.append(QPointF(x, y)) # close outer line polygonPie.append(QPointF(x, y)) return polygonPie def drawFilledPolygon(self, outlinePenWidth=0): """Fill polygon with gradiant colors, polygon created through createPolygonPie""" painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # Position in middle of the widget painter.translate(self.width() / 2, self.height() / 2) painter.setPen(Qt.NoPen) self.pen.setWidth(0) if outlinePenWidth > 0: painter.setPen(self.pen) coloredScalePolygon = self.createPloygonPie( ((self.widgetDiameter / 2) - (self.pen.width() / 2)) * self.outerRadiusFactor, (((self.widgetDiameter / 2) - (self.pen.width() / 2)) * self.innerRadiusFactor), self.scaleAngleStartValue, self.scaleAngleSize) grad = QConicalGradient( QPointF(0, 0), -self.scaleAngleSize - self.scaleAngleStartValue + self.angleOffset - 1) # set gradiant colors for eachcolor in self.scalePolygonColors: grad.setColorAt(eachcolor[0], eachcolor[1]) painter.setBrush(grad) painter.drawPolygon(coloredScalePolygon) def createFineScaledMarker(self): """Draw fine tick marsk on the color bar""" painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) painter.setPen(Qt.black) painter.rotate(self.scaleAngleStartValue - self.angleOffset) stepSize = (float(self.scaleAngleSize) / float(self.scaleMainCount * self.scaleSubDivisionCount)) scaleLineOuterStart = self.widgetDiameter / 2 #scaleLineLenght = (self.widgetDiameter / 2) - (self.widgetDiameter / 40) scaleLineLenght = (self.widgetDiameter / 2) - ( self.widgetDiameter * (self.outerRadiusFactor - self.innerRadiusFactor) / 2) for i in range((self.scaleMainCount * self.scaleSubDivisionCount) + 1): painter.drawLine(scaleLineLenght, 0, scaleLineOuterStart, 0) painter.rotate(stepSize) def drawBigScaledMarker(self): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) self.pen = QPen(QColor(0, 0, 0, 255)) self.pen.setWidth(2) painter.setPen(self.pen) painter.rotate(self.scaleAngleStartValue - self.angleOffset) stepsSize = (float(self.scaleAngleSize) / float(self.scaleMainCount)) scaleLineOuterStart = self.widgetDiameter / 2 scaleLineLenght = (self.widgetDiameter / 2) - (self.widgetDiameter / 20) for _ in range(self.scaleMainCount + 1): painter.drawLine(scaleLineLenght, 0, scaleLineOuterStart, 0) painter.rotate(stepsSize) def createScaleMarkerValuesText(self): painter = QPainter(self) # painter.setRenderHint(QPainter.HighQualityAntialiasing) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) # painter.save() font = QFont(self.scaleFontName, self.scaleFontSize) fm = QFontMetrics(font) penShadow = QPen() penShadow.setBrush(self.scaleValueColor) painter.setPen(penShadow) textRadiusFactor = 0.8 textRadius = self.widgetDiameter / 2 * textRadiusFactor scalePerDiv = int( (self.valueMax - self.valueMin) / self.scaleMainCount) angleDistance = (float(self.scaleAngleSize) / float(self.scaleMainCount)) for i in range(self.scaleMainCount + 1): # text = str(int((self.valueMax - self.valueMin) / self.scaleMainCount * i)) text = str(int(self.valueMin + scalePerDiv * i)) w = fm.width(text) + 1 h = fm.height() painter.setFont(QFont(self.scaleFontName, self.scaleFontSize)) angle = angleDistance * i + float(self.scaleAngleStartValue - self.angleOffset) x = textRadius * math.cos(math.radians(angle)) y = textRadius * math.sin(math.radians(angle)) # print(w, h, x, y, text) text = [ x - int(w / 2), y - int(h / 2), int(w), int(h), Qt.AlignCenter, text ] painter.drawText(text[0], text[1], text[2], text[3], text[4], text[5]) # painter.restore() def createValuesText(self): painter = QPainter(self) # painter.setRenderHint(QPainter.HighQualityAntialiasing) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) font = QFont(self.valueFontName, self.valueFontSize) fm = QFontMetrics(font) penShadow = QPen() penShadow.setBrush(self.displayValueColor) painter.setPen(penShadow) textRadius = self.widgetDiameter / 2 * self.textRadiusFactor # angle_distance = (float(self.scaleAngleSize) / float(self.scala_main_count)) # for i in range(self.scala_main_count + 1): text = str(int(self.value)) w = fm.width(text) + 1 h = fm.height() painter.setFont(QFont(self.valueFontName, self.valueFontSize)) angleEnd = float(self.scaleAngleStartValue + self.scaleAngleSize - 360) angle = (angleEnd - self.scaleAngleStartValue) / 2 + self.scaleAngleStartValue x = textRadius * math.cos(math.radians(angle)) y = textRadius * math.sin(math.radians(angle)) text = [ x - int(w / 2), y - int(h / 2), int(w), int(h), Qt.AlignCenter, text ] painter.drawText(text[0], text[1], text[2], text[3], text[4], text[5]) def drawNeedle(self): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) painter.setPen(Qt.NoPen) painter.setBrush(self.needleColor) painter.rotate(((self.value - self.valueOffset - self.valueMin) * self.scaleAngleSize / (self.valueMax - self.valueMin)) + 90 + self.scaleAngleStartValue) painter.drawConvexPolygon(self.valueNeedle[0]) def drawNeedleCenterPoint(self, diameter=30): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width() / 2, self.height() / 2) painter.setPen(Qt.NoPen) painter.setBrush(self.centerPointColor) painter.drawEllipse(int(-diameter / 2), int(-diameter / 2), int(diameter), int(diameter))
class RenderArea(QWidget): """ Класс области рисования """ def __init__(self, parent=None): super(RenderArea, self).__init__(parent) self.sphere = Sphere(self) self.pen = QPen(QColor(0, 0, 0), 0) self.faces_color = QColor(0, 255, 0) self.is_light = False self.is_clipping = False self.setBackgroundRole(QPalette.Base) self.setAutoFillBackground(True) def minimumSizeHint(self): return QSize(200, 200) def sizeHint(self): return QSize(400, 400) def set_pen_width(self, width): self.pen = QPen(self.pen.color(), width) self.update() def set_pen_color(self, label): color = QColorDialog.getColor() if color.isValid(): self.pen = QPen(color, self.pen.width()) label_palette = QPalette() label_palette.setColor(QPalette.WindowText, color) label.setPalette(label_palette) label.setText("Цвет линии " + color.name()) self.update() def paintEvent(self, event): painter = QPainter(self) painter.setPen(self.pen) # Пересчитываем сферу self.sphere.recalculate() # Рисуем for face in self.sphere.geom.faces: self.draw_item(face, painter) # Окантовка виджета painter.setPen(self.palette().dark().color()) painter.setBrush(Qt.NoBrush) painter.drawRect(QRect(0, 0, self.width() - 1, self.height() - 1)) def draw_item(self, face, painter): is_draw = True if self.is_clipping: is_draw = self.sphere.is_face_visible(face) if is_draw: polygon = QPolygon() for index, point_index in enumerate(face): p1_x = int(self.sphere.geom.points[face[index-1]][0]) p1_y = int(self.sphere.geom.points[face[index-1]][1]) p1_z = int(self.sphere.geom.points[face[index-1]][2]) p2_x = int(self.sphere.geom.points[point_index][0]) p2_y = int(self.sphere.geom.points[point_index][1]) p2_z = int(self.sphere.geom.points[point_index][2]) if self.sphere.projection_name == "front": # Фронтальная проекция (вид спереди) -> z = 0 real_p1 = QPoint(p1_x, p1_y) real_p2 = QPoint(p2_x, p2_y) elif self.sphere.projection_name == "horizontal": # Горизонтальная проекция (вид сверху) -> y = 0 real_p1 = QPoint(p1_x, p1_z) real_p2 = QPoint(p2_x, p2_z) elif self.sphere.projection_name == "profile": # Профильная проекция (вид сбоку) -> x = 0 real_p1 = QPoint(p1_y, p1_z) real_p2 = QPoint(p2_y, p2_z) else: real_p1 = QPoint(p1_x, p1_y) real_p2 = QPoint(p2_x, p2_y) # Точки для проволочного рисования real_p1.setX(self.width()/2 + real_p1.x()) real_p1.setY(self.height()/2 - real_p1.y()) real_p2.setX(self.width()/2 + real_p2.x()) real_p2.setY(self.height()/2 - real_p2.y()) # Полигоны для рисования с цветом polygon.append(real_p1) polygon.append(real_p2) if not self.is_light: painter.drawLine(real_p1, real_p2) if self.is_light: painter.setBrush(self.sphere.get_face_light(face, self.faces_color)) painter.drawPolygon(polygon) def set_projection(self, button): self.sphere.projection_name = button.objectName() self.update() def set_clipping(self, state): self.is_clipping = True if state == Qt.Checked else False self.update() def set_faces_color(self, label): color = QColorDialog.getColor() if color.isValid(): self.faces_color = color label_palette = QPalette() label_palette.setColor(QPalette.WindowText, color) label.setPalette(label_palette) label.setText("Цвет объекта " + color.name()) self.update() def set_light(self, is_light, clipping_checkbox): self.is_light = is_light clipping_checkbox.setChecked(self.is_light) clipping_checkbox.setDisabled(self.is_light) self.update()
class QImagePainter(QSmoothGraphicsView): # signals imageFlattened = pyqtSignal(QImage) def __init__(self): super().__init__() self.scene = QGraphicsScene(self) self.setScene(self.scene) self.setRenderHint(QPainter.Antialiasing) self.mainPixmapItem = self.scene.addPixmap(QPixmap()) self._appContext = None # policies # self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) # self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.toolbar = QToolBar() self.initToolbar() self._pen = QPen() self._pen.setWidth(50) self.setDefaultPenColor() self._drawStartPos = None self._dynamicOval = None self._drawnItems = [] self.updateDragMode() @property def appContext(self): return self._appContext @appContext.setter def appContext(self, context): self._appContext = context self.toolbar.clear() self.initToolbar() def setMainPixmapFromPath(self, imgPath): # set image image = QImage(str(imgPath)) pixmap = self.mainPixmapItem.pixmap() pixmap.convertFromImage(image) self.setMainPixmap(pixmap) def setMainPixmap(self, pixmap): self.mainPixmapItem.setPixmap(pixmap) # set scene rect boundingRect = self.mainPixmapItem.boundingRect() margin = 0 boundingRect += QMarginsF(margin, margin, margin, margin) self.scene.setSceneRect(boundingRect) def saveImage(self, fileName): image = self.flattenImage() image.save(fileName) def flattenImageIfDrawnOn(self): if not len(self._drawnItems) == 0: self.flattenImage() def flattenImage(self): # get region of scene area = self.mainPixmapItem.boundingRect() # create a QImage to render to and fix up a QPainter for it image = QImage(area.width(), area.height(), QImage.Format_ARGB32_Premultiplied) painter = QPainter(image) # render the region of interest to the QImage self.scene.render(painter, QRectF(image.rect()), area) painter.end() # set this flattened image to this view pixmap = self.mainPixmapItem.pixmap() pixmap.convertFromImage(image) self.setMainPixmap(pixmap) # clear the drawings from the view self.clearDrawnItems() # emit flattened image signal self.imageFlattened.emit(image) # return the flattened image return image def clearDrawnItems(self): for item in self._drawnItems: self.scene.removeItem(item) self._drawnItems.clear() def removeLastDrawnItem(self): try: item = self._drawnItems.pop() except IndexError: pass else: self.scene.removeItem(item) def scaleView(self, scaleFactor): # print(f'self.width: {self.width()}') # print(f'pixmap.width(): {self.scene.map.mainPixmapItem.boundingRect().width()}') self.scale(scaleFactor, scaleFactor) def centerImage(self): self.centerOn(self.mainPixmapItem) def bestFitImage(self): self.fitInView(self.mainPixmapItem, Qt.KeepAspectRatio) def keyPressEvent(self, event: QKeyEvent): key = event.key() if key == Qt.Key_Space: self.bestFitImage() else: super().keyPressEvent(event) def mousePressEvent(self, event): self._drawStartPos = None if self.ovalModeAct.isChecked(): if self.mainPixmapItem.isUnderMouse(): self._drawStartPos = self.mapToScene(event.pos()) self._dynamicOval = self.scene.addEllipse( QRectF(self._drawStartPos.x(), self._drawStartPos.y(), 1, 1), self._pen) else: super().mousePressEvent(event) def mouseMoveEvent(self, event): if self._dynamicOval: pos = self.mapToScene(event.pos()) self._dynamicOval.setRect( QRectF(self._drawStartPos.x(), self._drawStartPos.y(), pos.x() - self._drawStartPos.x(), pos.y() - self._drawStartPos.y())) else: super().mouseMoveEvent(event) def mouseReleaseEvent(self, event): if self._dynamicOval: self._drawnItems.append(self._dynamicOval) self._dynamicOval = None else: super().mouseReleaseEvent(event) def toggleSelectionMode(self): if self.selectionModeAct.isChecked(): self.ovalModeAct.setChecked(False) else: self.selectionModeAct.setChecked(True) self.updateDragMode() def toggleOvalMode(self): if self.ovalModeAct.isChecked(): self.selectionModeAct.setChecked(False) else: self.ovalModeAct.setChecked(True) self.updateDragMode() def updateDragMode(self): if self.selectionModeAct.isChecked(): self.setDragMode(QGraphicsView.ScrollHandDrag) else: self.setDragMode(QGraphicsView.NoDrag) @property def penWidth(self): return self._pen.width() @penWidth.setter def penWidth(self, value): self._pen.setWidth(value) @property def penColor(self): return self._pen.color() @penColor.setter def penColor(self, value): self._pen.setColor(QColor(value)) def setDefaultPenColor(self): self.setPenColor(COLORS['Teleric Blue']) def promptForPenWidth(self): width, okPressed = QInputDialog.getInt(self, 'Pen Width', 'Pen width (px):', self.penWidth, 1, 100, 1) if okPressed: self.penWidth = width def setResourcePaths(self): if self.appContext is None: self.selectionModeFp = './icons/selectIcon.png' self.ovalModeFp = './icons/ovalIcon.png' self.flattenFp = './icons/saveIcon.png' self.undoFp = './icons/undoIcon.png' self.penFp = './icons/pen.png' self.penWidthFp = './icons/penWidth.png' else: self.selectionModeFp = self.appContext.get_resource( 'selectIcon.png') self.ovalModeFp = self.appContext.get_resource('ovalIcon.png') self.flattenFp = self.appContext.get_resource('saveIcon.png') self.undoFp = self.appContext.get_resource('undoIcon.png') self.penFp = self.appContext.get_resource('pen.png') self.penWidthFp = self.appContext.get_resource('penWidth.png') def createActions(self): self.setResourcePaths() self.selectionModeAct = QAction(QIcon(self.selectionModeFp), 'Select (v)', self, checkable=True, checked=True, shortcut=Qt.Key_V, triggered=self.toggleSelectionMode) self.ovalModeAct = QAction(QIcon(self.ovalModeFp), 'Draw &Oval (o)', self, checkable=True, checked=False, shortcut=Qt.Key_O, triggered=self.toggleOvalMode) self.flattenAct = QAction(QIcon(self.flattenFp), 'Save', self, shortcut=QKeySequence.Save, triggered=self.flattenImage) self.undoAct = QAction(QIcon(self.undoFp), 'Undo', self, shortcut=QKeySequence.Undo, triggered=self.removeLastDrawnItem) self.setPenWidthAct = QAction(QIcon(self.penWidthFp), 'Set Pen Width', self, triggered=self.promptForPenWidth) def addPenToolMenu(self): penButton = QToolButton(self) penButton.setText('Pen') penButton.setIcon(QIcon(self.penFp)) penButton.setPopupMode(QToolButton.InstantPopup) self.penMenu = QMenu(penButton) self.penMenu.addAction(self.setPenWidthAct) self.addPaletteToMenu(self.penMenu) penButton.setMenu(self.penMenu) self.toolbar.addWidget(penButton) def setPenColor(self, color): qColor = QColor(color) for a in self.penMenu.actions(): a.setChecked(False) try: actionColor = QColor(a.color) except AttributeError: pass else: if actionColor == qColor: a.setChecked(True) self.penColor = actionColor def addPaletteToMenu(self, menu): for name, color in COLORS.items(): paletteIcon = QPaletteIcon(color) action = QAction(paletteIcon, name, self, checkable=True) action.color = color action.triggered.connect( lambda checked, color=color: self.setPenColor(color)) menu.addAction(action) def initToolbar(self): self.createActions() # self.toolbar.addAction(self.flattenAct) self.toolbar.addAction(self.undoAct) # self.toolbar.addSeparator() self.toolbar.addAction(self.selectionModeAct) self.toolbar.addAction(self.ovalModeAct) self.addPenToolMenu()
class PaintArea(QGraphicsView): def __init__(self, width=10, parent=None): QGraphicsView.__init__(self, parent) self._frame = None self._instructions = None self.setScene(QGraphicsScene(self)) self._items = self.scene().createItemGroup([]) self.setMouseTracking(True) self.pen = QPen(Qt.black, width, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self.painting = False self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) self.viewport().setCursor(self.getCursor()) self.updateScene() def updateScene(self): if self._frame: self.scene().setBackgroundBrush(Qt.gray) oldCanvas = self.canvas() self.setSceneRect(QRectF(self.contentsRect())) self.centerScene() self.scaleItems(oldCanvas, self.canvas()) def centerScene(self): self.centerFrame() self.centerInstructions() def scaleItems(self, oldCanvas, newCanvas): pass def canvas(self): if self._frame: return self._frame.rect() return QRectF(self.contentsRect()) def fitInstructions(self): textSize = self._instructions.document().size() factor = min(self.canvas().size().width() / textSize.width(), self.canvas().size().height() / textSize.height()) f = self._instructions.font() f.setPointSizeF(f.pointSizeF() * factor) self._instructions.setFont(f) def centerInstructions(self): if self._instructions: self.fitInstructions() size = self.size() textSize = self._instructions.document().size() self._instructions.setPos((size.width() - textSize.width()) / 2.0, (size.height() - textSize.height()) / 2.0) def setInstructions(self, text): if self._instructions: self._instructions.setPlainText(text) else: self._instructions = self.scene().addText(text, QFont('Arial', 10, QFont.Bold)) self._instructions.setZValue(-1) self._instructions.setDefaultTextColor(QColor(220, 220, 220)) self._text = text self.centerInstructions() def setFrame(self, width, height): if self._frame: self._frame.setRect(0, 0, width, height) else: self.addFrame(QRectF(0, 0, width, height)) self.centerScene() def addFrame(self, rect): self._frame = QGraphicsRectItem(rect) self._frame.setPen(QPen(Qt.NoPen)) self._frame.setBrush(Qt.white) self._frame.setZValue(-2) self.scene().addItem(self._frame) def centerFrame(self): if self._frame: rect = self._frame.rect() size = self.contentsRect() factor = min((size.width() + 1) / rect.width(), (size.height() + 1) / rect.height()) w, h = rect.width() * factor, rect.height() * factor self._frame.setRect(size.x() + (size.width() - w) / 2.0, size.y() + (size.height() - h) / 2.0, w, h) def resizeEvent(self, event): self.updateScene() def setBrushSize(self, size): self.pen.setWidth(size) self.viewport().setCursor(self.getCursor()) def render(self, painter): if self._instructions: self.scene().removeItem(self._instructions) self.scene().render(painter, source=self.scene().itemsBoundingRect()) if self._instructions: self.scene().addItem(self._instructions) def getLines(self): items = [item for item in self.scene().items() if item.group() == self._items] return self.canvas(), items def clear(self): for item in self.scene().items(): if item.group() == self._items: self._items.removeFromGroup(item) def getCursor(self): antialiasing_margin = 1 size = self.pen.width() pixmap = QPixmap(size + antialiasing_margin * 2, size + antialiasing_margin * 2) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform) painter.drawEllipse(QRectF(QPointF(antialiasing_margin, antialiasing_margin), QSizeF(size, size))) painter.end() return QCursor(pixmap) def addLine(self, start, end): if start == end: delta = QPointF(.0001, 0) end = start - delta line = self.scene().addLine(QLineF(start, end), self.pen) self._items.addToGroup(line) def drawPoint(self, pos): delta = QPointF(.0001, 0) line = self.scene().addLine(QLineF(pos, pos - delta), self.pen) self._items.addToGroup(line) def mousePressEvent(self, event): self.start = QPointF(self.mapToScene(event.pos())) self.painting = True self.addLine(self.start, self.start) def mouseReleaseEvent(self, event): self.painting = False def mouseMoveEvent(self, event): pos = QPointF(self.mapToScene(event.pos())) if self.painting: self.addLine(self.start, pos) self.start = pos
def paint(self, painter): # use antialiasing painter.setRenderHint(QPainter.Antialiasing) # connections, connected to layers with the include.phase parameter, # have a different opacity than normal connections opacity = Constants.itemOpacityInPhase if not self.__connection.getNodeEditor().isCurrentPhase( self.__connection.getPhase()): opacity = Constants.itemOpacityNotInPhase # connections between a layer and a in-place working layer have a different color than normal connections connectionColor = Constants.connectionItemColor if self.__connection.getIsInPlace(): connectionColor = Constants.itemInPlaceColor # set local color object alpha connectionColor.setAlpha(opacity) # draw highlight if the connection is selected if self.__connection.isSelected(): selectedColor = Constants.selectedColor selectedColor.setAlpha(opacity) painter.setPen( QPen(selectedColor, Constants.connectionItemSelectionSize)) painter.drawPath(self.__connection.path()) pen = QPen(connectionColor, Constants.connectionItemSize) # if the connection is hidden, draw a dashed line at the start and end of the connection if self.__connection.getHidden(): dashes = [] # calculate the dashes at the start of the connection for i in range(0, Constants.connectionItemHiddenDashCount - 1): dashes.append(Constants.connectionItemHiddenDashSize / pen.width()) dashes.append(Constants.connectionItemHiddenDashSpace / pen.width()) dashes.append(Constants.connectionItemHiddenDashSize / pen.width()) # calculate the size of the space between the dashed start and dashed end middleSpace = self.__connection.path().length() middleSpace -= 2 * Constants.connectionItemHiddenDashCount * Constants.connectionItemHiddenDashSize middleSpace -= 2 * (Constants.connectionItemHiddenDashCount - 1) * Constants.connectionItemHiddenDashSpace dashes.append(middleSpace / pen.width()) # calculate the dashes at the end of the connection for i in range(0, Constants.connectionItemHiddenDashCount): dashes.append(Constants.connectionItemHiddenDashSize / pen.width()) dashes.append(Constants.connectionItemHiddenDashSpace / pen.width()) pen.setDashPattern(dashes) # draw the connection painter.setPen(pen) painter.drawPath(self.__connection.path())
class Arrow(QGraphicsItemGroup): def __init__(self, start): super().__init__() self.fromItem = start self.startPoint = None self.toItem = None self.endPoint = None self.setFlag(QGraphicsItem.ItemIsSelectable) self.color = Qt.black self.pen = QPen(self.color, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self.arrowHead = QPolygonF() self.lines = [] self.qLines = [] #experimental self.setFlag(QGraphicsItem.ItemIsMovable) """ We need to reimplement this function because the arrow is larger than the bounding rectangle of the QGraphicsLineItem. The graphics scene uses the bounding rectangle to know which regions of the scene to update """ def boundingRect(self): extra = (self.pen.width() + 20) / 2.0 #rect = QRectF(self.line().p1(), QSizeF(self.line().p2().x() - self.line().p1().x(), # self.line().p2().y() - self.line().p1().y())) rect = self.calcRect() #return self.arrowHead.boundingRect() return rect.normalized().adjusted(-extra, -extra, extra, extra) """ The shape function returns a QPainterPath that is the exact shape of the item. The QGraphicsLineItem::shape() returns a path with a line drawn with the current pen, so we only need to add the arrow head. This function is used to check for collisions and selections with the mouse. """ def shape(self): #path = super().shape() path = QPainterPath() #path.addRect(self.calcRect()) path.addPolygon(self.arrowHead) return path #redraw, due to moving an object def recalcUpdate(self): if self.fromItem is None or self.toItem is None: return coordinates = QLineF( self.fromItem.getLinePoint(self.toItem.center())[1], self.toItem.getLinePoint(self.fromItem.center())[1]) #self.setLine(coordinates) """ is called, when one of the items is moved. it recalculates the lines """ def move(self): self.startPoint = GeoHelper.movePoint( self.posFromItem, self.fromItem.getPositionalRect().topLeft(), self.startPoint) self.endPoint = GeoHelper.movePoint( self.posToItem, self.toItem.getPositionalRect().topLeft(), self.endPoint) g = Geometry() self.lines = g.rectToRect(self.startPoint, self.fromItem.getPositionalRect(), self.endPoint, self.toItem.getPositionalRect()) self.posToItem = self.toItem.getPositionalRect().topLeft() self.posFromItem = self.fromItem.getPositionalRect().topLeft() self.update(self.boundingRect()) def drawLine(self, point, item): if item is not None: self.setItem2(item, point) else: self.setPoint2(point) self.update(self.boundingRect()) #trigger paint to update/ redraw #self.setLine(self.lines[-1]) #super().update(self.boundingRect()) def setPoint2(self, point): if self.startPoint is None: s1, self.startPoint = self.fromItem.getNearestPoint(point) g = Geometry() self.lines = g.rectToPoint(self.startPoint, self.fromItem.getPositionalRect(), point) """ saves second rectangle/Item and end point of a connection params ------ end: RoundedRect second rectangle/ toItem pos: QPointF position of mouse """ def setItem2(self, end, pos): #save endPoint s2, self.endPoint = end.getNearestPoint(pos) #save endItem self.toItem = end #save position (for moving) self.posToItem = self.toItem.getPositionalRect().topLeft() self.posFromItem = self.fromItem.getPositionalRect().topLeft() g = Geometry() self.lines = g.rectToRect(self.startPoint, self.fromItem.getPositionalRect(), self.endPoint, end.getPositionalRect()) print("a:", self.lines) def connect(self, end): self.toItem = end self.toItem.addInput(self) self.fromItem.addOutput(self) def calcRect(self): minX = self.lines[0].p1().x() minY = self.lines[0].p1().y() maxX = self.lines[0].p1().x() maxY = self.lines[0].p1().y() for l in self.lines: minX = min(minX, l.p1().x()) minY = min(minY, l.p1().y()) maxX = max(maxX, l.p1().x()) maxY = max(maxY, l.p1().y()) minX = min(minX, l.p2().x()) minY = min(minY, l.p2().y()) maxX = max(maxX, l.p2().x()) maxY = max(maxY, l.p2().y()) return QRectF(QPointF(minX, minY), QPointF(maxX, maxY)) def addOrAppend(self, i, line): if len(self.lines) > i: self.lines[i] = line else: self.lines.append(line) def resetLines(self, pen): for l in self.qLines: self.scene().removeItem(l) self.qLines = [] for l in self.lines: #line = QGraphicsLineItem(l, self) line = LineItem(l, self) line.setPen(pen) #line.setFlag(QGraphicsItem.ItemIsSelectable) self.qLines.append(line) def paint(self, painter, option, widget=None): arrowSize = 10 if self.isSelected(): pen = QPen(Qt.red, 1) color = Qt.red else: pen = self.pen color = self.color painter.setPen(pen) painter.setBrush(color) #calculate line #coordinates = QLineF(self.fromItem.getLinePoint(self.toItem.center()), # self.toItem.getLinePoint(self.fromItem.center())) #self.setLine(coordinates) #calculate points of arrow line = self.lines[-1] angle = math.acos(line.dx() / line.length()) if line.dy() >= 0: angle = (math.pi * 2) - angle p1 = line.p2() - QPointF( math.sin(angle + math.pi / 3) * arrowSize, math.cos(angle + math.pi / 3) * arrowSize) p2 = line.p2() - QPointF( math.sin(angle + math.pi - math.pi / 3) * arrowSize, math.cos(angle + math.pi - math.pi / 3) * arrowSize) self.arrowHead.clear() self.arrowHead.append(line.p2()) self.arrowHead.append(p1) self.arrowHead.append(p2) painter.drawPolygon(self.arrowHead) self.resetLines(pen)
class PenFormation(InstrumentFormation): ''' Specialize to Qt <QPen> Redefine: - applyTo() ''' # TODO: a QPen has a QBrush also, and it needs to be scaled also def __init__(self, parentSelector, role=""): InstrumentFormation.__init__(self, name="Pen", parentSelector=parentSelector, role=role) self.instrument = QPen() self.styleProperties=[BaseStyleProperty("Color", self.instrument.setColor, self.selector, default = self.instrument.color(), resettableValueFactory=ResettableColorValue, layoutFactory=ColorStylePropertyLayout), BaseStyleProperty("Width", self.instrument.setWidth, self.selector, default=self.instrument.width(), layoutFactory=IntStylePropertyLayout, minimum=0, maximum=10, singleStep=1), BaseStyleProperty("Style", self.instrument.setStyle, self.selector, default=self.instrument.style(), layoutFactory=ComboBoxStylePropertyLayout, domainModel = config.PenModel) ] def applyTo(self, morph): ''' Assert this formation's values have already been applied to instrument via editing (which calls Instrument.setters()) What remains is to set the instrument to the morph. Also, scale instrument correlated with scale of morph. ''' # Callback morph API: only morph knows its scale, and how to inversely scale drawing instrument morph.scaleInstrument(self.instrument, baseValue=self.styleProperties[1].resettableValue.value) morph.setPen(self.instrument) """ def scaledPropagateToInstrument(self, morph): ''' Propagate my values that are transformed, after unscaling by the local (item) transform. Where a DocumentElement has a transform that is used to size it (e.g. when the DocumentElement comprises a unit shape, scaled to size.) TODO when item transform is 2D and not uniform in x, y??? This is not undoing viewing transform, only local transform. For a Pen in Qt, width property. Note that in Qt, QPen.setWidth() also affects the QPen's QBrush. ''' unscaledWidth = self.styleProperties[1].resettableValue.value() itemScale = morph.scale() scaledWidthF = 1.0/itemScale * unscaledWidth # !!! Note float value and setWidthF is float setter self.instrument.setWidthF(scaledWidthF) #print "PenFormation.applyTo width: item scale, unscaled width, scaled width", itemScale, unscaledWidth, scaledWidthF, " on morph", morph """
class Triangle(QGraphicsItem): """ Abstract class knowing how to draw the polygon """ a = 60.0 h = a * sqrt(3) / 2.0 p_w = 5 def __init__(self, parent=None): super().__init__(parent) self.parent = parent self.setFlag(QGraphicsItem.ItemIsSelectable) self.setAcceptedMouseButtons(Qt.LeftButton) self.pen = QPen(QColor("blue"), self.p_w, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self.bg_color = QColor(230, 230, 230, 255) self.staple_a = None self.staple_b = None self.staple_c = None def set_staple_by_type(self, type, staple): if type is StapleTypeID.A: self.staple_a = staple if type is StapleTypeID.B: self.staple_b = staple if type is StapleTypeID.C: self.staple_c = staple def get_staple_by_type(self, type): if type is StapleTypeID.A: return self.staple_a if type is StapleTypeID.B: return self.staple_b if type is StapleTypeID.C: return self.staple_c def has_staple_class_type(self, staple_class_type): if type(self.staple_a) is staple_class_type: return True if type(self.staple_b) is staple_class_type: return True if type(self.staple_c) is staple_class_type: return True return False def shape(self): path = QPainterPath() path.addPolygon(self.polygon) return path def boundingRect(self): pen_w = self.pen.width() return QRectF(0 - pen_w, 0 - pen_w, self.a + pen_w, self.h + pen_w) def load_oligo(self, item, plate_name): seq = item["seq"] fabric = Staple if item["protector"]: fabric = Protector if item["stype"] == "A": self.staple_a = fabric(self, seq=seq, type_id=StapleTypeID.A, plate_name=plate_name, info=item) return self.staple_a if item["stype"] is "B": self.staple_b = fabric(self, seq=seq, type_id=StapleTypeID.B, plate_name=plate_name, info=item) return self.staple_b if item["stype"] is "C": self.staple_c = fabric(self, seq=seq, type_id=StapleTypeID.C, plate_name=plate_name, info=item) return self.staple_c def paint(self, QPainter, QStyleOptionGraphicsItem, QWidget_widget=None): if not self.isEnabled(): return QPainter.setPen(QPen(QColor("white"))) QPainter.setBrush(QBrush(self.bg_color)) QPainter.drawPolygon(self.polygon) AB = QLineF(self.vertex_A, self.vertex_B) BC = QLineF(self.vertex_B, self.vertex_C) CA = QLineF(self.vertex_C, self.vertex_A) vertices = [self.vertex_A, self.vertex_B, self.vertex_C] if self.staple_a: self.staple_a.draw(QPainter, vertices) if self.staple_b: self.staple_b.draw(QPainter, vertices) if self.staple_c: self.staple_c.draw(QPainter, vertices) def __str__(self): return "\n".join( map(str, [ "-" * 80, ("L_" if type(self) is TriangleLeft else "R_") + str(self.parent.row) + "_" + str(self.parent.col), self.staple_a, self.staple_b, self.staple_c, "-" * 80 ])) def random_fill(self, pool): for staple in (self.staple_a, self.staple_b, self.staple_c): # only works only if no seq assigned staple.random_fill(pool) def fill_by(self, neighbors, pool): mp = { StapleTypeID.A: [StapleTypeID.A, StapleTypeID.B], StapleTypeID.B: [StapleTypeID.B, StapleTypeID.C], StapleTypeID.C: [StapleTypeID.C, StapleTypeID.A], } nxt = { StapleTypeID.A: StapleTypeID.B, StapleTypeID.B: StapleTypeID.C, StapleTypeID.C: StapleTypeID.A } for stype, neighbor in zip(StapleTypeID, neighbors): strand = self.get_staple_by_type(stype) if strand: #makes sure we do have a strand strand_type_1, strand_type_2 = mp[stype] neighbor_stand1, neighbor_strand2 = neighbor.get_staple_by_type(strand_type_1), \ neighbor.get_staple_by_type(strand_type_2) self.__strand_staple_neighbor_staple(neighbor_stand1, neighbor_strand2, nxt, strand) self.__strand_protector_neighbor_staple( neighbor_stand1, neighbor_strand2, strand) self.__strand_connection_neighbor_staple( neighbor_stand1, neighbor_strand2, strand, pool) def _get_triangle_pos(self): lst = [] a = lst.append row, col = self.parent.pos a(row) a(col) if type(self) is TriangleLeft: a("L") else: a("R") return lst def __strand_protector_neighbor_staple(self, neighbor_stand1, neighbor_strand2, strand): if type(strand) is Protector and type(neighbor_stand1) is Staple: strand.short = neighbor_strand2.get_rev_c_or_none( StapleDomains.short) strand.long1 = neighbor_stand1.get_rev_c_or_none( StapleDomains.long2) strand.long2 = neighbor_stand1.get_rev_c_or_none( StapleDomains.long1) def __strand_staple_neighbor_staple(self, neighbor_stand1, neighbor_strand2, nxt, strand): if type(strand) is Staple and type(neighbor_stand1) is Staple: if not strand.long1: strand.long1 = neighbor_strand2.get_rev_c_or_none( StapleDomains.short) if not strand.long2: strand.long2 = neighbor_stand1.get_rev_c_or_none( StapleDomains.long2) # we can fill out also the next fragment of the next strand nxt_strand = self.get_staple_by_type(nxt[strand.type_id]) if not nxt_strand.short: nxt_strand.short = neighbor_stand1.get_rev_c_or_none( StapleDomains.long1) def __strand_connection_neighbor_staple(self, neighbor_strand1, neighbor_strand2, strand, pool=None): if type(strand) is ConnectionElement and type( neighbor_strand1) is Staple: # if it has an assigned sequence we need to assign the rev_c to the neighbors # for Connection elements all domains have to be filled out so we can check only 1 domain if neighbor_strand1.long1 and not strand.short: # rely only on first neighbor #if neighbor_strand1.long1: # rely only on first neighbor # the sequence based on the neighborhood (if they have a seq ) strand.info = None strand.short = neighbor_strand2.get_rev_c_or_none( StapleDomains.short) strand.long1 = neighbor_strand1.get_rev_c_or_none( StapleDomains.long2) strand.long2 = neighbor_strand1.get_rev_c_or_none( StapleDomains.long1) elif strand.info: return #do nothing if we have already generated the sequences # TODO:check this section, as inner are now generated first # if not strand.short: #makes sure that we do have a seq # # otherwise we assign a random seq and forward it to the neighbors # strand.random_fill(pool) else: neighbor_strand2.info = None neighbor_strand1.info = None neighbor_strand2.short = strand.get_rev_c_or_none( StapleDomains.short) neighbor_strand1.long2 = strand.get_rev_c_or_none( StapleDomains.long1) neighbor_strand1.long1 = strand.get_rev_c_or_none( StapleDomains.long2) neighbor_strand1.triangle.set_staple_by_type( neighbor_strand1.type_id, neighbor_strand1) neighbor_strand2.triangle.set_staple_by_type( neighbor_strand2.type_id, neighbor_strand2) # print("Connection strand looks like ") # print(strand) # print(strand.short is not None) # now we just need to change the seq for the to_protector if it does not have a seq (see #1) to_strand = strand.to_element print("*" * 80) print("to_strand", type(to_strand)) if not to_strand.short: to_strand.info = None to_strand.short = neighbor_strand1.long1 to_strand.long1 = neighbor_strand1.long2 to_strand.long2 = neighbor_strand2.short # update the triangle to_strand.triangle.set_staple_by_type(to_strand.type_id, to_strand)
class PageViewer(QGraphicsView): mouseRelaseEvent = pyqtSignal() wheelZoomEvent = pyqtSignal() @property def drawing(self): return self._drawing @property def tool_mode(self): return self._toolMode @property def empty(self): return self._page is None @property def page_layer(self): return self._pageLayer @property def drawing_layer(self): return self._drawingLayer @property def page(self): return self._page @property def pen_size(self): return self._pen.width() @property def eraser_size(self): return self._eraserSize def __init__(self, parent): super(PageViewer, self).__init__(parent) self._page = None # todo: add all these constants to preferences dialog self._zoom = 0 self._zoomMaxDistance = 10 self._zoomInFactor = 1.25 self._zoomOutFactor = 0.8 self._panDivisor = 2 self._toolMode = ToolMode.NOTHING self._previousToolModeDrag = ToolMode.NOTHING self._drawing = False self._dragStart = None self._drawPoint = None self._lastPenPos = None self._scene = QGraphicsScene(self) self._pageLayer = None self._drawingLayer = None self._predrawPixmap = None self._eraserSize = 100 self._eraserEllipse = self.getEraserEllipse() pen_brush = QBrush(QColor('red'), Qt.SolidPattern) self._pen = QPen(pen_brush, 5, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self._increaseSizeAction = QAction('Increase tool size', self) self._increaseSizeAction.setEnabled(False) self._increaseSizeAction.triggered.connect( self.onIncreaseSizeActionTriggered) self._decreaseSizeAction = QAction('Decrease tool size', self) self._decreaseSizeAction.setEnabled(False) self._decreaseSizeAction.triggered.connect( self.onDecreaseSizeActionTriggered) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self.setMouseTracking(True) def reset(self): self.setInteractive(False) self.setToolMode(ToolMode.NOTHING) self.setScene(QGraphicsScene(self)) self._page = None self._zoom = 0 def getEraserEllipse(self): brush = QBrush(QColor(0, 0, 0, alpha=64), Qt.SolidPattern) pen = QPen(brush, 2, Qt.DashDotLine, Qt.RoundCap, Qt.RoundJoin) pen.setCosmetic(True) ellipse = QGraphicsEllipseItem(0, 0, self._eraserSize, self._eraserSize) ellipse.setPen(pen) return ellipse def fitPage(self): if not self.empty: rect = QRectF(self._pageLayer.pixmap().rect()) if not rect.isNull(): self.setSceneRect(rect) unity = self.transform().mapRect(QRectF(0, 0, 1, 1)) self.scale(1 / unity.width(), 1 / unity.height()) viewrect = self.viewport().rect() scenerect = self.transform().mapRect(rect) factor = min(viewrect.width() / scenerect.width(), viewrect.height() / scenerect.height()) self.scale(factor, factor) self._zoom = 0 def setPage(self, page): self._page = page if not self._page.page_image.isNull(): self._scene.clear() self._pageLayer = QGraphicsPixmapItem(self._page.page_image) self._scene.addItem(self._pageLayer) self._drawingLayer = QGraphicsPixmapItem(self._page.drawing_image) self._scene.addItem(self._drawingLayer) self.fitPage() self._eraserEllipse = self.getEraserEllipse() self.setScene(self._scene) self.setToolMode(self._toolMode) self.setInteractive(True) def canZoomIn(self): return self._zoom < self._zoomMaxDistance def canZoomOut(self): return self._zoom > 0 def zoomIn(self): if self.canZoomIn(): self._zoom += 1 self._applyZoom(self._zoomInFactor) return True return False def zoomOut(self): if self.canZoomOut(): self._zoom -= 1 self._applyZoom(self._zoomOutFactor) return True return False def _applyZoom(self, factor): if self._zoom > 0: self.scale(factor, factor) else: self._zoom = 0 self.fitPage() def wheelEvent(self, event): if not self.empty: if event.angleDelta().y() > 0: factor = self._zoomInFactor if not self.zoomIn(): return else: factor = self._zoomOutFactor self.zoomOut() self._applyZoom(factor) self.wheelZoomEvent.emit() def _penDraw(self, pos): self._drawing = True self._lastPenPos = self._drawPoint self._drawPoint = pos self.update() def _eraseDraw(self, pos): self._drawing = True self._drawPoint = pos self.update() def mousePressEvent(self, event): if not self.empty: button = event.button() scenePos = self.mapToScene(event.pos()) if button == Qt.MiddleButton: self._previousToolModeDrag = self._toolMode self.setToolMode(ToolMode.DRAGGING) self._dragStart = scenePos elif button == Qt.LeftButton: if self._toolMode == ToolMode.NOTHING: self.setToolMode(ToolMode.DRAGGING) else: sceneRect = self._drawingLayer.mapRectToScene( QRectF(self._page.drawing_image.rect())) if sceneRect.contains(scenePos.x(), scenePos.y()): self._predrawPixmap = copyPixmap( self._page.drawing_image) if self._toolMode == ToolMode.PEN: self._penDraw(scenePos) if self._toolMode == ToolMode.ERASER: self._eraseDraw(scenePos) super(PageViewer, self).mousePressEvent(event) def mouseMoveEvent(self, event): scenePos = self.mapToScene(event.pos()) if self._toolMode == ToolMode.ERASER: r = self._eraserEllipse.rect() x = scenePos.x() - (r.width() / 2) y = scenePos.y() - (r.height() / 2) self._eraserEllipse.setPos(x, y) if not self.empty: if self._toolMode == ToolMode.DRAGGING and self._dragStart is not None: delta = self.mapToScene(event.pos()) self.translate(delta.x() - self._dragStart.x(), delta.y() - self._dragStart.y()) else: if self._drawing: if self._toolMode == ToolMode.PEN: self._penDraw(scenePos) super(PageViewer, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): if not self.empty: button = event.button() scenePos = self.mapToScene(event.pos()) if button == Qt.MiddleButton: self.setToolMode(self._previousToolModeDrag) elif button == Qt.LeftButton: if self._toolMode == ToolMode.DRAGGING: self.setToolMode(ToolMode.NOTHING) elif self._toolMode == ToolMode.PEN: self._penDraw(scenePos) self._page.pushCommand( DrawCommand(self, self._predrawPixmap)) self._lastPenPos = None elif self._toolMode == ToolMode.ERASER: self._eraseDraw(scenePos) self._page.pushCommand( DrawCommand(self, self._predrawPixmap)) self._drawPoint = None self._drawing = False self.mouseRelaseEvent.emit() def paintEvent(self, event): if self._drawing: painter = QPainter(self._page.drawing_image) painter.setRenderHint(QPainter.HighQualityAntialiasing, True) if self._toolMode == ToolMode.ERASER: painter.setPen(Qt.NoPen) painter.setBrush(painter.background()) r = int(self._eraserSize / 2) painter.drawEllipse(self._drawPoint, r, r) if self._toolMode == ToolMode.PEN: painter.setPen(self._pen) if self._lastPenPos is None: painter.drawPoint(self._drawPoint) else: painter.drawLine(self._lastPenPos, self._drawPoint) painter.end() self.updateDrawingLayer() super(PageViewer, self).paintEvent(event) def setToolMode(self, mode: ToolMode): self._toolMode = mode self._increaseSizeAction.setEnabled(False) self._decreaseSizeAction.setEnabled(False) for item in self._scene.items(): if isinstance(item, QGraphicsEllipseItem): self._scene.removeItem(item) if self._toolMode == ToolMode.DRAGGING: self.setCursor(Qt.ClosedHandCursor) else: if self._toolMode == ToolMode.ERASER: self._scene.addItem(self._eraserEllipse) self._increaseSizeAction.setText('Increase Eraser width') self._decreaseSizeAction.setText('Decrease Eraser width') self._increaseSizeAction.setEnabled(True) self._decreaseSizeAction.setEnabled(True) elif self._toolMode == ToolMode.PEN: self.setDragMode(QGraphicsView.NoDrag) self.setCursor(Qt.CrossCursor) self._increaseSizeAction.setText('Increase Pen width') self._decreaseSizeAction.setText('Decrease Pen width') self._increaseSizeAction.setEnabled(True) self._decreaseSizeAction.setEnabled(True) else: self.setDragMode(QGraphicsView.NoDrag) self.setCursor(Qt.ArrowCursor) def setEraserSize(self, v): if 500 >= v >= 10: self._eraserEllipse.setRect(0, 0, v, v) self._eraserSize = v def setPenSize(self, v): if 30 >= v > 0: self._pen.setWidth(v) def onIncreaseSizeActionTriggered(self): if self._toolMode == ToolMode.ERASER: v = self._eraserSize + 1 self.setEraserSize(v) elif self._toolMode == ToolMode.PEN: v = self._pen.width() + 1 self.setPenSize(v) def onDecreaseSizeActionTriggered(self): if self._toolMode == ToolMode.ERASER: v = self._eraserSize - 1 self.setEraserSize(v) elif self._toolMode == ToolMode.PEN: v = self._pen.width() - 1 self.setPenSize(v) def getActiveToolSizeText(self): if self._toolMode == ToolMode.ERASER: return '{:03d}px'.format(self._eraserSize) elif self._toolMode == ToolMode.PEN: return '{:02d}px'.format(self._pen.width()) else: return '' def getIncreaseSizeAction(self): return self._increaseSizeAction def getDecreaseSizeAction(self): return self._decreaseSizeAction def updateDrawingLayer(self): self._drawingLayer.setPixmap(self._page.drawing_image)
class RenderArea(QWidget): """ Класс области рисования """ def __init__(self, parent=None): super(RenderArea, self).__init__(parent) self.sphere = Sphere(self) self.pen = QPen(QColor(0, 0, 0), 0) self.faces_color = QColor(0, 255, 0) self.is_light = False self.is_clipping = False self.setBackgroundRole(QPalette.Base) self.setAutoFillBackground(True) def minimumSizeHint(self): return QSize(200, 200) def sizeHint(self): return QSize(400, 400) def set_pen_width(self, width): self.pen = QPen(self.pen.color(), width) self.update() def set_pen_color(self, label): color = QColorDialog.getColor() if color.isValid(): self.pen = QPen(color, self.pen.width()) label_palette = QPalette() label_palette.setColor(QPalette.WindowText, color) label.setPalette(label_palette) label.setText("Цвет линии " + color.name()) self.update() def paintEvent(self, event): painter = QPainter(self) painter.setPen(self.pen) # Пересчитываем сферу self.sphere.recalculate() # Рисуем for face in self.sphere.geom.faces: self.draw_item(face, painter) # Окантовка виджета painter.setPen(self.palette().dark().color()) painter.setBrush(Qt.NoBrush) painter.drawRect(QRect(0, 0, self.width() - 1, self.height() - 1)) def draw_item(self, face, painter): is_draw = True if self.is_clipping: is_draw = self.sphere.is_face_visible(face) if is_draw: polygon = QPolygon() for index, point_index in enumerate(face): p1_x = int(self.sphere.geom.points[face[index - 1]][0]) p1_y = int(self.sphere.geom.points[face[index - 1]][1]) p1_z = int(self.sphere.geom.points[face[index - 1]][2]) p2_x = int(self.sphere.geom.points[point_index][0]) p2_y = int(self.sphere.geom.points[point_index][1]) p2_z = int(self.sphere.geom.points[point_index][2]) if self.sphere.projection_name == "front": # Фронтальная проекция (вид спереди) -> z = 0 real_p1 = QPoint(p1_x, p1_y) real_p2 = QPoint(p2_x, p2_y) elif self.sphere.projection_name == "horizontal": # Горизонтальная проекция (вид сверху) -> y = 0 real_p1 = QPoint(p1_x, p1_z) real_p2 = QPoint(p2_x, p2_z) elif self.sphere.projection_name == "profile": # Профильная проекция (вид сбоку) -> x = 0 real_p1 = QPoint(p1_y, p1_z) real_p2 = QPoint(p2_y, p2_z) else: real_p1 = QPoint(p1_x, p1_y) real_p2 = QPoint(p2_x, p2_y) # Точки для проволочного рисования real_p1.setX(self.width() / 2 + real_p1.x()) real_p1.setY(self.height() / 2 - real_p1.y()) real_p2.setX(self.width() / 2 + real_p2.x()) real_p2.setY(self.height() / 2 - real_p2.y()) # Полигоны для рисования с цветом polygon.append(real_p1) polygon.append(real_p2) if not self.is_light: painter.drawLine(real_p1, real_p2) if self.is_light: painter.setBrush( self.sphere.get_face_light(face, self.faces_color)) painter.drawPolygon(polygon) def set_projection(self, button): self.sphere.projection_name = button.objectName() self.update() def set_clipping(self, state): self.is_clipping = True if state == Qt.Checked else False self.update() def set_faces_color(self, label): color = QColorDialog.getColor() if color.isValid(): self.faces_color = color label_palette = QPalette() label_palette.setColor(QPalette.WindowText, color) label.setPalette(label_palette) label.setText("Цвет объекта " + color.name()) self.update() def set_light(self, is_light, clipping_checkbox): self.is_light = is_light clipping_checkbox.setChecked(self.is_light) clipping_checkbox.setDisabled(self.is_light) self.update()
class LinkGI(QGraphicsPathItem): def __init__(self, name, srcSocket, dstSocket, thickness=2): super().__init__() self.setFlag(QGraphicsItem.ItemIsSelectable, True) self.setAcceptHoverEvents(True) self.hovering = False self.linkName = name self.srcSocket = srcSocket self.dstSocket = dstSocket self.srcSocket.link = self self.dstSocket.link = self self.updateShape() # set the pen style self.linkPen = QPen() self.linkPen.setWidth(thickness) self.linkPen.setCapStyle(Qt.RoundCap) self.linkPen.setColor(schemastyle.LINK_COLOR) self.setPen(self.linkPen) def updateShape(self): # Create the bezier curve path srcX, srcY = self.srcSocket.linkConnectionPos() dstX, dstY = self.dstSocket.linkConnectionPos() linkPath = QPainterPath() linkPath.setFillRule(Qt.WindingFill) linkPath.moveTo(srcX, srcY) linkPath.cubicTo(srcX + 100, srcY, dstX - 100, dstY, dstX, dstY) self.setPath(linkPath) def hoverEnterEvent(self, event): self.linkPen.setColor(QColor(Qt.white)) self.setPen(self.linkPen) super().hoverEnterEvent(event) def hoverLeaveEvent(self, event): self.linkPen.setColor(QColor(Qt.gray)) self.setPen(self.linkPen) super().hoverLeaveEvent(event) @property def name(self): return self.linkName @name.setter def name(self, name): self.linkName = name self.update() @property def thickness(self): return self.linkPen.width() @thickness.setter def thickness(self, thickness): self.linkPen.setWidth(thickness) self.setPen(self.linkPen) self.update()