def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) # INITIALIZE EDGE LINE pp1 = QPointF(((kwargs['w'] - 52) / 2), kwargs['h'] / 2) pp2 = QPointF(((kwargs['w'] - 52) / 2) + 52 - 2, kwargs['h'] / 2) line = QLineF(pp1, pp2) # CALCULATE HEAD COORDINATES angle = radians(line.angle()) p1 = QPointF(line.p2().x() + 2, line.p2().y()) p2 = p1 - QPointF(sin(angle + M_PI / 3.0) * 8, cos(angle + M_PI / 3.0) * 8) p3 = p1 - QPointF(sin(angle + M_PI - M_PI / 3.0) * 8, cos(angle + M_PI - M_PI / 3.0) * 8) # INITIALIZE EDGE HEAD head = QPolygonF([p1, p2, p3]) # DRAW THE POLYGON painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.drawLine(line) # DRAW HEAD painter.setPen(QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.setBrush(QColor(0, 0, 0)) painter.drawPolygon(head) # DRAW THE TEXT ON TOP OF THE EDGE space = 2 if Platform.identify() is Platform.Darwin else 0 painter.setFont(Font('Arial', 9, Font.Light)) painter.drawText(pp1.x() + space, (kwargs['h'] / 2) - 4, 'instanceOf') return pixmap
def buildNodeFromShapeNode(self, item, element): """ Build a node using the given item type and QDomElement. :type item: Item :type element: QDomElement :rtype: AbstractNode """ data = element.firstChildElement('data') while not data.isNull(): if data.attribute('key', '') == self.keys['node_key']: shapeNode = data.firstChildElement('y:ShapeNode') geometry = shapeNode.firstChildElement('y:Geometry') kwargs = { 'id': element.attribute('id'), 'height': float(geometry.attribute('height')), 'width': float(geometry.attribute('width')), } node = self.factory.create(item, self.scene, **kwargs) # yEd uses the TOP-LEFT corner as (0,0) coordinate => we need to translate our # position (0,0), which is instead at the center of the shape, so that the TOP-LEFT # corner of the shape in yEd matches the TOP-LEFT corner of the shape in Eddy. # Additionally we force-snap the position to the grid so that items say aligned. pos = QPointF(float(geometry.attribute('x')), float(geometry.attribute('y'))) pos = pos + QPointF(node.width() / 2, node.height() / 2) pos = QPointF(snapF(pos.x(), DiagramScene.GridSize), snapF(pos.y(), DiagramScene.GridSize)) node.setPos(pos) return node data = data.nextSiblingElement('data') return None
def _get_selected_edge(self, pos: QPointF, transform: QTransform, horizontal_selection: bool): x1, x2 = self.x, self.x + self.width y1, y2 = self.y, self.y + self.height x, y = pos.x(), pos.y() spacing = 5 spacing /= transform.m11() if horizontal_selection else transform.m22() if horizontal_selection: x1a, x1b = x1 - spacing, x1 + spacing y1a, y1b = y1, y2 x2a, x2b = x2 - spacing, x2 + spacing y2a, y2b = y1, y2 else: x1a, x1b, x2a, x2b = x1, x2, x1, x2 y1a, y1b = min(y1 - spacing, y1 + spacing), max(y1 - spacing, y1 + spacing) y2a, y2b = min(y2 - spacing, y2 + spacing), max(y2 - spacing, y2 + spacing) if x1a < x < x1b and y1a < y < y1b: self.selected_edge = 0 return 0 if x2a < x < x2b and y2a < y < y2b: self.selected_edge = 1 return 1 self.selected_edge = None return None
def mouseIsOnIO(self, mousePos, click = False): #Returns the IO that the mouse is on for i in range(0, len(self.ioList)): #Adjust if IO is centered on a side if self.ioList[i][3] == 'left': yTranslation = self.yTranslationLeftIO else: yTranslation = self.yTranslationRightIO #Get point of IO IOPoint = QPointF(self.ioList[i][0], self.ioList[i][1] + yTranslation) #If mouse is over IO -> return IO if mousePos.x() > IOPoint.x() and mousePos.x() < IOPoint.x() + self.ioWidth: if mousePos.y() > IOPoint.y() and mousePos.y() < IOPoint.y() + self.ioHeight: # entry point for drawing graphs....... # if click: # print('mouse on IO: ' + str(i) + ' (' + str(self.ioList[i][3]) + ', ' + str(self.ioList[i][4]) + ')') #Update the hover paramater of the IO self.ioList.insert(i, (self.ioList[i][0], self.ioList[i][1], self.ioList[i][2], self.ioList[i][3], self.ioList[i][4], True, self.ioList[i][6])) del self.ioList[i + 1] self.setFlag(QGraphicsItem.ItemIsSelectable, False) self.setFlag(QGraphicsItem.ItemIsMovable, False) self.hover = False return i #If no IO is found under the mouse -> make sure hovering is enabled and return -1 self.hover = True self.setHoveringToFalse() return -1
def calcRowCol(self, point: QPointF): """ Calculate the network row and column that a point is int calc the row and column indexes of a point The following is the algorithm: 1. Find the distance between the point and the left (or top) 2. Divide the distance with the width of path to find the relative position 3. Multipile this relative position with the number of rows/cols 4. Convert the result to int to find the indexes 5. If the index is the number of row/col reduce the index (This is for the case the the point is on the boundary and in this case the relative position is 1 which will cause the indexes to be the number of rows/cols - out of the matrix indexes) Args: point (QPointF) : The point to resolve Returns: int : The network row that the point is in int : The network column that the point is in """ partialX = (point.x() - self.charPath.boundingRect().left()) / self.charPath.boundingRect().width() partialY = (point.y() - self.charPath.boundingRect().top()) / self.charPath.boundingRect().height() col_idx = int(partialX * self.netCols) row_idx = int(partialY * self.netRows) if row_idx == self.netRows: row_idx -= 1 if col_idx == self.netCols: col_idx -= 1 return row_idx, col_idx
def render(self, widget): painter = widget.painter if not painter: return previousRenderHint = painter.renderHints() painter.setRenderHints(previousRenderHint | QPainter.Antialiasing) painter.setPen(Qt.NoPen) painter.setBrush(QColor(253, 242, 245)) painter.drawEllipse(QRectF(self.pos - self.toPointF(self.size/2), self.size)) mouseOffset = QPointF(widget.mousePosition) \ - self.pos \ - QPointF(widget.frameGeometry().topLeft()) ox, oy = mouseOffset.x(), mouseOffset.y() distance = math.sqrt(ox**2 + oy**2) if distance > self.eyesight_radius: ox *= self.eyesight_radius / distance oy *= self.eyesight_radius / distance px = self.pos.x() + ox/self.eyesight_radius * (self.size-self.pupil_size).width() / 2 py = self.pos.y() + oy/self.eyesight_radius * (self.size-self.pupil_size).height() / 2 pos = QPointF(px, py) painter.setBrush(Qt.black) painter.drawEllipse(QRectF(pos - self.toPointF(self.pupil_size/2), self.pupil_size)) painter.setRenderHints(previousRenderHint)
def __init__(self): super().__init__() self.MAR = 50 self.points = [] self.top_left = QPointF(float('Inf'), float('Inf')) self.bottom_right = QPointF(-float('Inf'), -float('Inf')) self.rect = QRectF()
def applyLayerValue(self, id, val): layer = self.mObject layerIndex = self.mMapDocument.map().layers().indexOf(layer) command = None x = id if x==PropertyId.NameProperty: command = RenameLayer(self.mMapDocument, layerIndex, val) elif x==PropertyId.VisibleProperty: command = SetLayerVisible(self.mMapDocument, layerIndex, val) elif x==PropertyId.OpacityProperty: command = SetLayerOpacity(self.mMapDocument, layerIndex, val) elif x==PropertyId.OffsetXProperty or x==PropertyId.OffsetYProperty: offset = QPointF(layer.offset()) if id == PropertyId.OffsetXProperty: offset.setX(val) else: offset.setY(val) command = SetLayerOffset(self.mMapDocument, layerIndex, offset) else: x = layer.layerType() if x==Layer.TileLayerType: self.applyTileLayerValue(id, val) elif x==Layer.ObjectGroupType: self.applyObjectGroupValue(id, val) elif x==Layer.ImageLayerType: self.applyImageLayerValue(id, val) if (command): self.mMapDocument.undoStack().push(command)
def is_in_roi(self, pos: QPointF): x1 = self.rect().x() x2 = x1 + self.rect().width() y1 = self.rect().y() y2 = y1 + self.rect().width() if x1 < pos.x() < x2 and y1 < pos.y() < y2: return True return False
def update_bounding_rect(self, pt): """插入新顶点时,更新 bounding rect Args: pt (QPointF): 新插入的顶点 """ self.top_left = QPointF(min(self.top_left.x(), pt.x()), min(self.top_left.y(), pt.y())) self.bottom_right = QPointF(max(self.bottom_right.x(), pt.x()), max(self.bottom_right.y(), pt.y())) self.rect = QRectF(self.top_left, self.bottom_right).adjusted(-self.MAR, -self.MAR, self.MAR, self.MAR) self.prepareGeometryChange()
class Eye: pupil_size = QSizeF(5, 5) eyesight_radius = 100.0 def __init__(self, x, y, w, h): ## x, y are the coordinates of the center of the eye. ## w, h are the total width and height of the eye. self.size = QSizeF(w, h) self.pos = QPointF(x, y) def toPointF(self, size): return QPointF(size.width(), size.height()) def render(self, widget): painter = widget.painter if not painter: return previousRenderHint = painter.renderHints() painter.setRenderHints(previousRenderHint | QPainter.Antialiasing) painter.setPen(Qt.NoPen) painter.setBrush(QColor(253, 242, 245)) painter.drawEllipse(QRectF(self.pos - self.toPointF(self.size/2), self.size)) mouseOffset = QPointF(widget.mousePosition) \ - self.pos \ - QPointF(widget.frameGeometry().topLeft()) ox, oy = mouseOffset.x(), mouseOffset.y() distance = math.sqrt(ox**2 + oy**2) if distance > self.eyesight_radius: ox *= self.eyesight_radius / distance oy *= self.eyesight_radius / distance px = self.pos.x() + ox/self.eyesight_radius * (self.size-self.pupil_size).width() / 2 py = self.pos.y() + oy/self.eyesight_radius * (self.size-self.pupil_size).height() / 2 pos = QPointF(px, py) painter.setBrush(Qt.black) painter.drawEllipse(QRectF(pos - self.toPointF(self.pupil_size/2), self.pupil_size)) painter.setRenderHints(previousRenderHint)
def moveUIPoint(contour, point, delta): if point.segmentType is None: # point is an offCurve. Get its sibling onCurve and the other # offCurve. onCurve, otherPoint = _getOffCurveSiblingPoints(contour, point) # if the onCurve is selected, the offCurve will move along with it if onCurve.selected: return point.move(delta) if not onCurve.smooth: contour.dirty = True return # if the onCurve is smooth, we need to either... if otherPoint.segmentType is None and not otherPoint.selected: # keep the other offCurve inline line = QLineF(point.x, point.y, onCurve.x, onCurve.y) otherLine = QLineF( onCurve.x, onCurve.y, otherPoint.x, otherPoint.y) line.setLength(line.length() + otherLine.length()) otherPoint.x = line.x2() otherPoint.y = line.y2() else: # keep point in tangency with onCurve -> otherPoint segment, # ie. do an orthogonal projection line = QLineF(otherPoint.x, otherPoint.y, onCurve.x, onCurve.y) n = line.normalVector() n.translate(QPointF(point.x, point.y) - n.p1()) targetPoint = QPointF() n.intersect(line, targetPoint) # check that targetPoint is beyond its neighbor onCurve # we do this by calculating position of the offCurve and second # onCurve relative to the first onCurve. If there is no symmetry # in at least one of the axis, then we need to clamp onCurvePoint = line.p2() onDistance = line.p1() - onCurvePoint newDistance = targetPoint - onCurvePoint if (onDistance.x() >= 0) != (newDistance.x() <= 0) or \ (onDistance.y() >= 0) != (newDistance.y() <= 0): targetPoint = onCurvePoint # ok, now set pos point.x, point.y = targetPoint.x(), targetPoint.y() else: # point is an onCurve. Move its offCurves along with it. index = contour.index(point) point.move(delta) for d in (-1, 1): # edge-case: contour open, trailing offCurve and moving first # onCurve in contour if contour.open and index == 0 and d == -1: continue pt = contour.getPoint(index + d) if pt.segmentType is None: pt.move(delta) contour.dirty = True
def helixIndex(self, point: QPointF) -> Vec2T: """Returns the (row, col) of the base which point lies within. Returns: point (tuple) in virtual_helix_item coordinates Args: point (TYPE): Description """ x = int(int(point.x()) / _BW) y = int(int(point.y()) / _BW) return (x, y)
def getModelPos(self, pos: QPointF) -> Vec3T: """Y-axis is inverted in Qt +y === DOWN Args: pos: a position in this scene Returns: position in model coordinates """ sf = self.scale_factor x, y = pos.x()/sf, -1.0*pos.y()/sf return x, y, 0.
def __init__(self, sourceNode, destNode): super(Edge, self).__init__() self.arrowSize = 10.0 self.sourcePoint = QPointF() self.destPoint = QPointF() self.setAcceptedMouseButtons(Qt.NoButton) self.source = sourceNode self.dest = destNode self.source.addEdge(self) self.dest.addEdge(self) self.adjust()
def setPoints(self, xa, ya, xb, yb): """ Public method to set the start and end points of the line. <b>Note:</b> This method does not redraw the item. @param xa x-coordinate of the start point (float) @param ya y-coordinate of the start point (float) @param xb x-coordinate of the end point (float) @param yb y-coordinate of the end point (float) """ self._origin = QPointF(xa, ya) self._end = QPointF(xb, yb)
class PolygonBase(QGraphicsWidget): def __init__(self): super().__init__() self.MAR = 50 self.points = [] self.top_left = QPointF(float('Inf'), float('Inf')) self.bottom_right = QPointF(-float('Inf'), -float('Inf')) self.rect = QRectF() def boundingRect(self): return self.rect def update_bounding_rect(self, pt): """插入新顶点时,更新 bounding rect Args: pt (QPointF): 新插入的顶点 """ self.top_left = QPointF(min(self.top_left.x(), pt.x()), min(self.top_left.y(), pt.y())) self.bottom_right = QPointF(max(self.bottom_right.x(), pt.x()), max(self.bottom_right.y(), pt.y())) self.rect = QRectF(self.top_left, self.bottom_right).adjusted(-self.MAR, -self.MAR, self.MAR, self.MAR) self.prepareGeometryChange() def move_bounding_rect(self, offset): """移动多边形时,更新 bounding rect Args: offset (QPointF): 平移向量 """ self.top_left += offset self.bottom_right += offset self.rect.adjust(offset.x(), offset.y(), offset.x(), offset.y()) self.prepareGeometryChange() def get_points(self): """获取多边形中的顶点列表 Returns: points (list[QPointF]): 顶点列表 """ return self.points def get_vertices(self): """获取多边形中的顶点列表 Returns: vertices (list[list[float]]): 顶点列表 """ vertices = [[vertex.x(), vertex.y()] for vertex in self.points] return vertices
def intersectLineGeometry(self, lineGeo, breakShape): """ Try to break lineGeo with the given breakShape. Will return the intersection points of lineGeo with breakShape. """ # TODO geos should be abs intersections = [] line = QLineF(lineGeo.Ps.x, lineGeo.Ps.y, lineGeo.Pe.x, lineGeo.Pe.y) for breakGeo in breakShape.geos.abs_iter(): if isinstance(breakGeo, LineGeo): breakLine = QLineF(breakGeo.Ps.x, breakGeo.Ps.y, breakGeo.Pe.x, breakGeo.Pe.y) intersection = QPointF(0, 0) # values do not matter res = line.intersect(breakLine, intersection) if res == QLineF.BoundedIntersection: intersections.append(Point(intersection.x(), intersection.y())) return intersections
def __init__(self, x, y, w, h): ## x, y are the coordinates of the center of the eye. ## w, h are the total width and height of the eye. self.size = QSizeF(w, h) self.pos = QPointF(x, y)
def setGraph(self, graphData): if graphData != None: self.graphData = graphData # set widget size based on min/max positions of nodes if not self.graphData is None: minX, minY = sys.maxsize, sys.maxsize maxX, maxY = 0, 0 for n in self.graphData.nodes(): x, y = self.graphData.node[n]['pos'] minX = min(minX, x - 50) minY = min(minY, y - 50) maxX = max(maxX, x + 50) maxY = max(maxY, y + 50) # Determine the center of the graph self.centerOfGraph = QPointF((minX + maxX) / 2, (minY + maxY) / 2) self.placeGraphObjects() #Resize scene to be slightly larger than the graph self.scene.updateSceneRect() self.resetView() self.update()
def __init__(self, parent = None): super().__init__(self.tr("Select Objects"), QIcon(":images/22x22/tool-select-objects.png"), QKeySequence(self.tr("S")), parent) self.mSelectionRectangle = SelectionRectangle() self.mOriginIndicator = OriginIndicator() self.mMousePressed = False self.mHoveredObjectItem = None self.mClickedObjectItem = None self.mClickedRotateHandle = None self.mClickedResizeHandle = None self.mResizingLimitHorizontal = False self.mResizingLimitVertical = False self.mMode = Mode.Resize self.mAction = Action.NoAction self.mRotateHandles = [0, 0, 0, 0] self.mResizeHandles = [0, 0, 0, 0, 0, 0, 0, 0] self.mAlignPosition = QPointF() self.mMovingObjects = QVector() self.mScreenStart = QPoint() self.mStart = QPointF() self.mModifiers = 0 self.mOrigin = QPointF() for i in range(AnchorPosition.CornerAnchorCount): self.mRotateHandles[i] = RotateHandle(i) for i in range(AnchorPosition.AnchorCount): self.mResizeHandles[i] = ResizeHandle(i)
def __init__(self,winParent): super(TeleopWidget, self).__init__() self.winParent=winParent self.line = QPointF(0, 0); self.qimage=QtGui.QImage() self.qimage.load(':images/ball.png') self.stopSIG.connect(self.stop) self.initUI()
def __init__(self, time_sig = '4/4', num_measures = 4, quantize_val = '1/8'): QGraphicsScene.__init__(self) self.setBackgroundBrush(QColor(50, 50, 50)) self.mousePos = QPointF() self.notes = [] self.selected_notes = [] self.piano_keys = [] self.marquee_select = False self.insert_mode = False self.velocity_mode = False self.place_ghost = False self.ghost_note = None self.default_ghost_vel = 100 self.ghost_vel = self.default_ghost_vel ## dimensions self.padding = 2 ## piano dimensions self.note_height = 10 self.start_octave = -2 self.end_octave = 8 self.notes_in_octave = 12 self.total_notes = (self.end_octave - self.start_octave) \ * self.notes_in_octave + 1 self.piano_height = self.note_height * self.total_notes self.octave_height = self.notes_in_octave * self.note_height self.piano_width = 34 ## height self.header_height = 20 self.total_height = self.piano_height - self.note_height + self.header_height #not sure why note_height is subtracted ## width self.full_note_width = 250 # i.e. a 4/4 note self.snap_value = None self.quantize_val = quantize_val ### dummy vars that will be changed self.time_sig = 0 self.measure_width = 0 self.num_measures = 0 self.max_note_length = 0 self.grid_width = 0 self.value_width = 0 self.grid_div = 0 self.piano = None self.header = None self.play_head = None self.setTimeSig(time_sig) self.setMeasures(num_measures) self.setGridDiv() self.default_length = 1. / self.grid_div
def getX(self, pos: QPointF) -> float: """ Args: pos: Description Returns: ``x`` position """ return pos.x()
def animate(self): self.angle += (math.pi / 30) xs = 200 * math.sin(self.angle) - 40 + 25 ys = 200 * math.cos(self.angle) - 40 + 25 self.m_lightSource.setPos(xs, ys) for item in self.m_items: effect = item.graphicsEffect() delta = QPointF(item.x() - xs, item.y() - ys) effect.setOffset(QPointF(delta.toPoint() / 30)) dd = math.hypot(delta.x(), delta.y()) color = effect.color() color.setAlphaF(max(0.4, min(1 - dd / 200.0, 0.7))) effect.setColor(color) self.m_scene.update()
def zoom(self, step, anchor="center"): """ Zooms the view by *step* increments (with a scale factor of 1.2^*step*), anchored to *anchor*: - QPoint_: center on that point - "cursor": center on the mouse cursor position - "center": center on the viewport - None: don’t anchor, i.e. stick to the viewport’s top-left. # TODO: improve docs from QGraphicsView descriptions. The default is "center". .. _QPoint: http://doc.qt.io/qt-5/qpoint.html """ oldScale = self._scale newScale = self._scale * pow(1.2, step) scrollArea = self._scrollArea if newScale < 1e-2 or newScale > 1e3: return if scrollArea is not None: # compute new scrollbar position # http://stackoverflow.com/a/32269574/2037879 hSB = scrollArea.horizontalScrollBar() vSB = scrollArea.verticalScrollBar() viewport = scrollArea.viewport() if isinstance(anchor, QPoint): pos = anchor elif anchor == "cursor": pos = self.mapFromGlobal(QCursor.pos()) elif anchor == "center": pos = self.mapFromParent( QPoint(viewport.width() / 2, viewport.height() / 2)) else: raise ValueError("invalid anchor value: {}".format(anchor)) scrollBarPos = QPointF(hSB.value(), vSB.value()) deltaToPos = pos / oldScale delta = deltaToPos * (newScale - oldScale) self.setScale(newScale) self.update() if scrollArea is not None: hSB.setValue(scrollBarPos.x() + delta.x()) vSB.setValue(scrollBarPos.y() + delta.y())
def __init__(self, parent, index=0): QDial.__init__(self, parent) self.fMinimum = 0.0 self.fMaximum = 1.0 self.fRealValue = 0.0 self.fIsHovered = False self.fHoverStep = self.HOVER_MIN self.fIndex = index self.fPixmap = QPixmap(":/bitmaps/dial_01d.png") self.fPixmapNum = "01" if self.fPixmap.width() > self.fPixmap.height(): self.fPixmapOrientation = self.HORIZONTAL else: self.fPixmapOrientation = self.VERTICAL self.fLabel = "" self.fLabelPos = QPointF(0.0, 0.0) self.fLabelFont = QFont(self.font()) self.fLabelFont.setPointSize(6) self.fLabelWidth = 0 self.fLabelHeight = 0 if self.palette().window().color().lightness() > 100: # Light background c = self.palette().dark().color() self.fLabelGradientColor1 = c self.fLabelGradientColor2 = QColor(c.red(), c.green(), c.blue(), 0) self.fLabelGradientColorT = [self.palette().buttonText().color(), self.palette().mid().color()] else: # Dark background self.fLabelGradientColor1 = QColor(0, 0, 0, 255) self.fLabelGradientColor2 = QColor(0, 0, 0, 0) self.fLabelGradientColorT = [Qt.white, Qt.darkGray] self.fLabelGradient = QLinearGradient(0, 0, 0, 1) self.fLabelGradient.setColorAt(0.0, self.fLabelGradientColor1) self.fLabelGradient.setColorAt(0.6, self.fLabelGradientColor1) self.fLabelGradient.setColorAt(1.0, self.fLabelGradientColor2) self.fLabelGradientRect = QRectF(0.0, 0.0, 0.0, 0.0) self.fCustomPaintMode = self.CUSTOM_PAINT_MODE_NULL self.fCustomPaintColor = QColor(0xff, 0xff, 0xff) self.updateSizes() # Fake internal value, 10'000 precision QDial.setMinimum(self, 0) QDial.setMaximum(self, 10000) QDial.setValue(self, 0) self.valueChanged.connect(self.slot_valueChanged)
def mouseMovedWhileCreatingObject(self, pos, modifiers): renderer = self.mapDocument().renderer() pixelCoords = renderer.screenToPixelCoords_(pos) # Update the size of the new map object objectPos = self.mNewMapObjectItem.mapObject().position() newSize = QPointF(max(0.0, pixelCoords.x() - objectPos.x()), max(0.0, pixelCoords.y() - objectPos.y())) # Holding shift creates circle or square if (modifiers & Qt.ShiftModifier): m = max(newSize.x(), newSize.y()) newSize.setX(m) newSize.setY(m) SnapHelper(renderer, modifiers).snap(newSize) self.mNewMapObjectItem.resizeObject(QSizeF(newSize.x(), newSize.y()))
def beginDrawing(self, pos, sliceRect): """ pos -- QPointF-like """ self.sliceRect = sliceRect self.scene.clear() self.bb = QRect() self.pos = QPointF(pos.x(), pos.y()) self._hasMoved = False
def setEndPoint(self, x, y): """ Public method to set the end point. <b>Note:</b> This method does not redraw the item. @param x x-coordinate of the end point (float) @param y y-coordinate of the end point (float) """ self._end = QPointF(x, y)
def __updatePath(self): """ Updates the connections path, using the start and end points """ path = QPainterPath(self.__start) # the end point is further left than the start point, so draw two half circles and a straight line if self.__start.x() > self.__end.x(): yDiff = abs(self.__end.y() - self.__start.y()) middleY = (self.__start.y() + self.__end.y()) / 2 # calculate the control points needed for the first half circle curve1End = QPointF(self.__start.x(), middleY) curve1CP1 = QPointF(self.__start.x() + yDiff / 2, self.__start.y()) curve1CP2 = QPointF(self.__start.x() + yDiff / 2, middleY) # calculate the control points needed for the second half circle curve2Start = QPointF(self.__end.x(), middleY) curve2CP1 = QPointF(self.__end.x() - yDiff / 2, middleY) curve2CP2 = QPointF(self.__end.x() - yDiff / 2, self.__end.y()) # draw the first half circle path.cubicTo(curve1CP1.x(), curve1CP1.y(), curve1CP2.x(), curve1CP2.y(), curve1End.x(), curve1End.y()) # draw the straight line path.lineTo(curve2Start.x(), curve2Start.y()) # draw the second half circle path.cubicTo(curve2CP1.x(), curve2CP1.y(), curve2CP2.x(), curve2CP2.y(), self.__end.x(), self.__end.y()) # the start point is further left than the end point, so draw a Bezier curve between both else: path.cubicTo((self.__start.x() + self.__end.x()) / 2, self.__start.y(), (self.__start.x() + self.__end.x()) / 2, self.__end.y(), self.__end.x(), self.__end.y()) self.setPath(path) self.update()
class ConnectionItem(QGraphicsPathItem): """ A ConnectionItem represents a top-bottom connection in the graphics scene """ def __init__(self, nodeEditor, parent=None): super(ConnectionItem, self).__init__(parent) # make selectable self.setFlag(QGraphicsPathItem.ItemIsSelectable, True) # draw behind node items self.setZValue(0.5) self.__isInPlace = False self.__nodeEditor = nodeEditor self.__hidden = False self.__topConnector = None self.__bottomConnector = None self.__start = QPointF(0, 0) self.__end = QPointF(0, 0) # create a renderer to separate drawing from logic self.__renderer = ConnectionItemRenderer(self) self.__updatePath() def paint(self, painter, option, widget=None): self.__renderer.paint(painter) def shape(self): stroker = QPainterPathStroker() stroker.setWidth(Constants.connectionItemSize * 3) return stroker.createStroke(self.path()) def setStart(self, start): """ Sets the start position of the connection and recalculates the path """ self.__start = start self.__updatePath() def setEnd(self, end): """ Sets the end position of the connection and recalculates the path """ self.__end = end self.__updatePath() def getTopConnector(self): """ Returns the connections top connector """ return self.__topConnector def getBottomConnector(self): """ Returns the connections bottom connector """ return self.__bottomConnector def setConnector(self, connector): """ Sets the top/bottom connector of the connection. Whether the top or bottom connector gets set, is decided by information provided by the connector """ if connector.isTopConnector(): self.setTopConnector(connector) else: self.setBottomConnector(connector) def setTopConnector(self, connector): """ Sets the top connector and updates the connections path """ self.__topConnector = connector self.__start = connector.scenePos() self.updateData() self.__updatePath() def setBottomConnector(self, connector): """ Sets the bottom connector and updates the connections path """ self.__bottomConnector = connector self.__end = connector.scenePos() self.updateData() self.__updatePath() def setHidden(self, hidden): """ Sets the connection to be hidden/shown and updates the rendering """ self.__hidden = hidden self.update() def updateMousePosition(self, pos): """ Updates the start/end of the connection if the connection is getting dragged (created) """ if self.__topConnector is None: self.__start = pos elif self.__bottomConnector is None: self.__end = pos self.__updatePath() def updateData(self): """ Updates the connections internal in-place and phase variables to update the rendering """ # set in-place if self.__bottomConnector is not None: self.__isInPlace = self.__bottomConnector.isInPlace() else: self.__isInPlace = False # update self.update() def __updatePath(self): """ Updates the connections path, using the start and end points """ path = QPainterPath(self.__start) # the end point is further left than the start point, so draw two half circles and a straight line if self.__start.x() > self.__end.x(): yDiff = abs(self.__end.y() - self.__start.y()) middleY = (self.__start.y() + self.__end.y()) / 2 # calculate the control points needed for the first half circle curve1End = QPointF(self.__start.x(), middleY) curve1CP1 = QPointF(self.__start.x() + yDiff / 2, self.__start.y()) curve1CP2 = QPointF(self.__start.x() + yDiff / 2, middleY) # calculate the control points needed for the second half circle curve2Start = QPointF(self.__end.x(), middleY) curve2CP1 = QPointF(self.__end.x() - yDiff / 2, middleY) curve2CP2 = QPointF(self.__end.x() - yDiff / 2, self.__end.y()) # draw the first half circle path.cubicTo(curve1CP1.x(), curve1CP1.y(), curve1CP2.x(), curve1CP2.y(), curve1End.x(), curve1End.y()) # draw the straight line path.lineTo(curve2Start.x(), curve2Start.y()) # draw the second half circle path.cubicTo(curve2CP1.x(), curve2CP1.y(), curve2CP2.x(), curve2CP2.y(), self.__end.x(), self.__end.y()) # the start point is further left than the end point, so draw a Bezier curve between both else: path.cubicTo((self.__start.x() + self.__end.x()) / 2, self.__start.y(), (self.__start.x() + self.__end.x()) / 2, self.__end.y(), self.__end.x(), self.__end.y()) self.setPath(path) self.update() def checkSameConnectorTypeRestriction(self, connector): """ Check, whether the connector type (top/bottom) is already set in the connection """ if self.__topConnector is not None and connector.isTopConnector(): return False elif self.__bottomConnector is not None and not connector.isTopConnector( ): return False return True def getConnectorIfNotFullyConnected(self): """ Returns the only connected connector, if only one connector is connected """ if self.__topConnector is not None and self.__bottomConnector is None: return self.__topConnector elif self.__topConnector is None and self.__bottomConnector is not None: return self.__bottomConnector return None def getHidden(self): """ Returns whether the connection is hidden """ return self.__hidden def getNodeEditor(self): """ Returns the node prototxt_editor object (for the renderer) """ return self.__nodeEditor def getPhase(self): """ Returns the internal phase """ if self.getTopConnector() is not None: return self.getTopConnector().getPhase() else: return self.getBottomConnector().getPhase() def getIsInPlace(self): """ Returns the internal in-place value """ return self.__isInPlace def contextMenuEvent(self, event): """ Creates the context menu """ contextMenu = QMenu() menuText = "Hide" if self.__hidden: menuText = "Show" # add action to show/hide the connection toggleHideAction = contextMenu.addAction(menuText) if not self.__nodeEditor.disable: removeAction = contextMenu.addAction("Remove") # show context menu action = contextMenu.exec_(event.screenPos()) if action is not None: if action == toggleHideAction: self.__hidden = not self.__hidden if self.__hidden: self.__nodeEditor.tryToAddHiddenConnection(self) else: self.__nodeEditor.tryToRemoveHiddenConnection(self) self.update() elif not self.__nodeEditor.disable and action == removeAction: # deselect all Layers self.__nodeEditor.tryToClearSelection() # deselect all other connections for item in self.__nodeEditor.getScene().selectedItems(): item.setSelected(False) # select this connection self.setSelected(True) # remove this connection self.__nodeEditor.tryToDeleteSelection()
def createGradient(self): center = QPointF(0, 0) self.gradient = QRadialGradient(center, self._outerRadius, center) self.gradient.setColorAt(0.5, QColor(self._innerColor)) self.gradient.setColorAt(1.0, QColor(self._outerColor))
def size_points_on_scene(self, scene, rsize): rx, ry = self.size_on_scene(scene, rsize) return QPointF(rx, 0), QPointF(0, ry)
def __calculateEndingPoints_topToBottom(self): """ Private method to calculate the ending points of the association item. The ending points are calculated from the top center of the lower item to the bottom center of the upper item. """ if self.itemA is None or self.itemB is None: return self.prepareGeometryChange() rectA = self.__mapRectFromItem(self.itemA) rectB = self.__mapRectFromItem(self.itemB) midA = QPointF(rectA.x() + rectA.width() / 2.0, rectA.y() + rectA.height() / 2.0) midB = QPointF(rectB.x() + rectB.width() / 2.0, rectB.y() + rectB.height() / 2.0) if midA.y() > midB.y(): startP = QPointF(rectA.x() + rectA.width() / 2.0, rectA.y()) endP = QPointF(rectB.x() + rectB.width() / 2.0, rectB.y() + rectB.height()) else: startP = QPointF(rectA.x() + rectA.width() / 2.0, rectA.y() + rectA.height()) endP = QPointF(rectB.x() + rectB.width() / 2.0, rectB.y()) self.setPoints(startP.x(), startP.y(), endP.x(), endP.y())
def updateLinePos(self, scenePos): if self.m_ready_to_disc: if self.m_port_type == PORT_TYPE_AUDIO_JACK: pen = QPen(canvas.theme.line_audio_jack_sel, 2, Qt.DotLine) elif self.m_port_type == PORT_TYPE_MIDI_JACK: pen = QPen(canvas.theme.line_midi_jack_sel, 2, Qt.DotLine) elif self.m_port_type == PORT_TYPE_MIDI_ALSA: pen = QPen(canvas.theme.line_midi_alsa_sel, 2, Qt.DotLine) elif self.m_port_type == PORT_TYPE_PARAMETER: pen = QPen(canvas.theme.line_parameter_sel, 2, Qt.DotLine) else: pen = QPen(Qt.black) else: if self.m_port_type == PORT_TYPE_AUDIO_JACK: pen = QPen(canvas.theme.line_audio_jack_sel, 2) elif self.m_port_type == PORT_TYPE_MIDI_JACK: pen = QPen(canvas.theme.line_midi_jack_sel, 2) elif self.m_port_type == PORT_TYPE_MIDI_ALSA: pen = QPen(canvas.theme.line_midi_alsa_sel, 2) elif self.m_port_type == PORT_TYPE_PARAMETER: pen = QPen(canvas.theme.line_parameter_sel, 2) else: pen = QPen(Qt.black) pen.setCapStyle(Qt.FlatCap) pen.setWidthF(pen.widthF() + 0.00001) self.setPen(pen) phi = 0.75 if self.m_portgrp_lenght > 2 else 0.62 phito = 0.75 if self.m_portgrp_lenght_to > 2 else 0.62 if self.parentItem().type() == CanvasPortType: if self.m_portgrp_lenght > 1: first_old_y = canvas.theme.port_height * phi last_old_y = canvas.theme.port_height * ( self.m_portgrp_lenght - phi) delta = (last_old_y - first_old_y) / (self.m_portgrp_lenght - 1) old_y = first_old_y + (self.m_port_posinportgrp * delta) \ - canvas.theme.port_height * self.m_port_posinportgrp else: old_y = canvas.theme.port_height / 2 if self.m_portgrp_lenght_to == 1: new_y = 0 else: first_new_y = canvas.theme.port_height * phito last_new_y = canvas.theme.port_height * ( self.m_portgrp_lenght_to - phito) delta = (last_new_y - first_new_y) / (self.m_portgrp_lenght_to - 1) new_y1 = first_new_y + (self.m_port_posinportgrp_to * delta) new_y = new_y1 - ( (last_new_y - first_new_y) / 2 ) \ - canvas.theme.port_height * phito elif self.parentItem().type() == CanvasPortGroupType: first_old_y = canvas.theme.port_height * phi last_old_y = canvas.theme.port_height * (self.m_portgrp_lenght - phi) delta = (last_old_y - first_old_y) / (self.m_portgrp_lenght - 1) old_y = first_old_y + (self.m_port_posinportgrp * delta) if self.m_portgrp_lenght_to == 1: new_y = 0 elif (self.m_port_posinportgrp_to == self.m_port_posinportgrp and self.m_portgrp_lenght == self.m_portgrp_lenght_to): new_y = old_y - ( (last_old_y - first_old_y) / 2 ) \ - (canvas.theme.port_height * phi) else: first_new_y = canvas.theme.port_height * phito last_new_y = canvas.theme.port_height * ( self.m_portgrp_lenght_to - phito) delta = (last_new_y - first_new_y) / (self.m_portgrp_lenght_to - 1) new_y1 = first_new_y + (self.m_port_posinportgrp_to * delta) new_y = new_y1 - ( (last_new_y - first_new_y) / 2 ) \ - (canvas.theme.port_height * phito) final_x = scenePos.x() - self.p_itemX final_y = scenePos.y() - self.p_itemY + new_y if self.m_port_mode == PORT_MODE_OUTPUT: old_x = self.p_width + 12 mid_x = abs(final_x - old_x) / 2 new_x1 = old_x + mid_x new_x2 = final_x - mid_x diffxy = abs(final_y - old_y) - abs(final_x - old_x) if diffxy > 0: new_x1 += abs(diffxy) new_x2 -= abs(diffxy) elif self.m_port_mode == PORT_MODE_INPUT: old_x = 0 mid_x = abs(final_x - old_x) / 2 new_x1 = old_x - mid_x new_x2 = final_x + mid_x diffxy = abs(final_y - old_y) - abs(final_x - old_x) if diffxy > 0: new_x1 -= abs(diffxy) new_x2 += abs(diffxy) else: return path = QPainterPath(QPointF(old_x, old_y)) path.cubicTo(new_x1, old_y, new_x2, final_y, final_x, final_y) self.setPath(path)
self.setRect(rect) self.isSet = True def setP1(self, point): rect = self.rect() rect.setX(point.x()-rect.width()/2.) rect.setY(point.y()+rect.height()/2.) self.setRect(rect) if __name__ == '__main__': # ça marche --> voici deux examples de shapes test = Ellipse2D(0, 0, 100, 100) # print(test.x(), test.y(), test.width(), test.height()) print(test.contains(QPointF(50, 50))) print(test.contains(QPointF(15, 15))) print(test.contains(QPointF(-1, -1))) print(test.contains(QPointF(0, 0))) print(test.contains(QPointF(100, 100))) print(test.contains(QPointF(100, 100.1))) print(test.x()) print(test.y()) print(test.translate(QPoint(10, 10))) print(test.x()) print(test.y()) # p1 = test.p1() # print(p1.x(), p1.y()) # p2 = test.p2() # print(p2.x(), p2.y())
def add_plot( self, plot: 'ChartPlotWidget', # noqa digits: int = 0, ) -> None: # add ``pg.graphicsItems.InfiniteLine``s # vertical and horizonal lines and a y-axis label vl = plot.addLine(x=0, pen=self.lines_pen, movable=False) vl.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache) hl = plot.addLine(y=0, pen=self.lines_pen, movable=False) hl.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache) hl.hide() yl = YAxisLabel( parent=plot.getAxis('right'), digits=digits or self.digits, opacity=_ch_label_opac, bg_color='default', ) yl.hide() # on startup if mouse is off screen # TODO: checkout what ``.sigDelayed`` can be used for # (emitted once a sufficient delay occurs in mouse movement) px_moved = pg.SignalProxy( plot.scene().sigMouseMoved, rateLimit=_mouse_rate_limit, slot=self.mouseMoved, delay=_debounce_delay, ) px_enter = pg.SignalProxy( plot.sig_mouse_enter, rateLimit=_mouse_rate_limit, slot=lambda: self.mouseAction('Enter', plot), delay=_debounce_delay, ) px_leave = pg.SignalProxy( plot.sig_mouse_leave, rateLimit=_mouse_rate_limit, slot=lambda: self.mouseAction('Leave', plot), delay=_debounce_delay, ) self.graphics[plot] = { 'vl': vl, 'hl': hl, 'yl': yl, 'px': (px_moved, px_enter, px_leave), } self.plots.append(plot) # Determine where to place x-axis label. # Place below the last plot by default, ow # keep x-axis right below main chart plot_index = -1 if _xaxis_at == 'bottom' else 0 self.xaxis_label = XAxisLabel( parent=self.plots[plot_index].getAxis('bottom'), opacity=_ch_label_opac, bg_color='default', ) # place label off-screen during startup self.xaxis_label.setPos(self.plots[0].mapFromView(QPointF(0, 0)))
def initialize(self): self.init_file() self.focus_widget( QMouseEvent(QEvent.MouseButtonPress, QPointF(0, 0), Qt.LeftButton, Qt.LeftButton, Qt.NoModifier))
class MapObject(Object): ## # Enumerates the different object shapes. Rectangle is the default shape. # When a polygon is set, the shape determines whether it should be # interpreted as a filled polygon or a line. ## Rectangle, Polygon, Polyline, Ellipse = range(4) def __init__(self, *args): super().__init__(Object.MapObjectType) self.mPolygon = QPolygonF() self.mName = QString() self.mPos = QPointF() self.mCell = Cell() self.mType = QString() self.mId = 0 self.mShape = MapObject.Rectangle self.mObjectGroup = None self.mRotation = 0.0 self.mVisible = True l = len(args) if l == 0: self.mSize = QSizeF(0, 0) elif l == 4: name, _type, pos, size = args self.mName = name self.mType = _type self.mPos = pos self.mSize = QSizeF(size) ## # Returns the id of this object. Each object gets an id assigned that is # unique for the map the object is on. ## def id(self): return self.mId ## # Sets the id of this object. ## def setId(self, id): self.mId = id ## # Returns the name of this object. The name is usually just used for # identification of the object in the editor. ## def name(self): return self.mName ## # Sets the name of this object. ## def setName(self, name): self.mName = name ## # Returns the type of this object. The type usually says something about # how the object is meant to be interpreted by the engine. ## def type(self): return self.mType ## # Sets the type of this object. ## def setType(self, type): self.mType = type ## # Returns the position of this object. ## def position(self): return QPointF(self.mPos) ## # Sets the position of this object. ## def setPosition(self, pos): self.mPos = pos ## # Returns the x position of this object. ## def x(self): return self.mPos.x() ## # Sets the x position of this object. ## def setX(self, x): self.mPos.setX(x) ## # Returns the y position of this object. ## def y(self): return self.mPos.y() ## # Sets the x position of this object. ## def setY(self, y): self.mPos.setY(y) ## # Returns the size of this object. ## def size(self): return self.mSize ## # Sets the size of this object. ## def setSize(self, *args): l = len(args) if l == 1: size = args[0] self.mSize = QSizeF(size) elif l == 2: width, height = args self.setSize(QSizeF(width, height)) ## # Returns the width of this object. ## def width(self): return self.mSize.width() ## # Sets the width of this object. ## def setWidth(self, width): self.mSize.setWidth(width) ## # Returns the height of this object. ## def height(self): return self.mSize.height() ## # Sets the height of this object. ## def setHeight(self, height): self.mSize.setHeight(height) ## # Sets the polygon associated with this object. The polygon is only used # when the object shape is set to either Polygon or Polyline. # # \sa setShape() ## def setPolygon(self, polygon): self.mPolygon = polygon ## # Returns the polygon associated with this object. Returns an empty # polygon when no polygon is associated with this object. ## def polygon(self): return QPolygonF(self.mPolygon) ## # Sets the shape of the object. ## def setShape(self, shape): self.mShape = shape ## # Returns the shape of the object. ## def shape(self): return self.mShape ## # Shortcut to getting a QRectF from position() and size(). ## def bounds(self): return QRectF(self.mPos, self.mSize) ## # Shortcut to getting a QRectF from position() and size() that uses cell tile if present. ## def boundsUseTile(self): if (self.mCell.isEmpty()): # No tile so just use regular bounds return self.bounds() # Using the tile for determing boundary # Note the position given is the bottom-left corner so correct for that return QRectF( QPointF(self.mPos.x(), self.mPos.y() - self.mCell.tile.height()), self.mCell.tile.size()) ## # Sets the tile that is associated with this object. The object will # display as the tile image. # # \warning The object shape is ignored for tile objects! ## def setCell(self, cell): self.mCell = cell ## # Returns the tile associated with this object. ## def cell(self): return self.mCell ## # Returns the object group this object belongs to. ## def objectGroup(self): return self.mObjectGroup ## # Sets the object group this object belongs to. Should only be called # from the ObjectGroup class. ## def setObjectGroup(self, objectGroup): self.mObjectGroup = objectGroup ## # Returns the rotation of the object in degrees. ## def rotation(self): return self.mRotation ## # Sets the rotation of the object in degrees. ## def setRotation(self, rotation): self.mRotation = rotation ## # This is somewhat of a workaround for dealing with the ways different objects # align. # # Traditional rectangle objects have top-left alignment. # Tile objects have bottom-left alignment on orthogonal maps, but # bottom-center alignment on isometric maps. # # Eventually, the object alignment should probably be configurable. For # backwards compatibility, it will need to be configurable on a per-object # level. ## def alignment(self): if (self.mCell.isEmpty()): return Alignment.TopLeft elif (self.mObjectGroup): map = self.mObjectGroup.map() if map: if (map.orientation() == Map.Orientation.Isometric): return Alignment.Bottom return Alignment.BottomLeft def isVisible(self): return self.mVisible def setVisible(self, visible): self.mVisible = visible ## # Flip this object in the given \a direction. This doesn't change the size # of the object. ## def flip(self, direction): if (not self.mCell.isEmpty()): if (direction == FlipDirection.FlipHorizontally): self.mCell.flippedHorizontally = not self.mCell.flippedHorizontally elif (direction == FlipDirection.FlipVertically): self.mCell.flippedVertically = not self.mCell.flippedVertically if (not self.mPolygon.isEmpty()): center2 = self.mPolygon.boundingRect().center() * 2 if (direction == FlipDirection.FlipHorizontally): for i in range(self.mPolygon.size()): # oh, QPointF mPolygon returned is a copy of internal object self.mPolygon[i] = QPointF( center2.x() - self.mPolygon[i].x(), self.mPolygon[i].y()) elif (direction == FlipDirection.FlipVertically): for i in range(self.mPolygon.size()): self.mPolygon[i] = QPointF( self.mPolygon[i].x(), center2.y() - self.mPolygon[i].y()) ## # Returns a duplicate of this object. The caller is responsible for the # ownership of this newly created object. ## def clone(self): o = MapObject(self.mName, self.mType, self.mPos, self.mSize) o.setProperties(self.properties()) o.setPolygon(self.mPolygon) o.setShape(self.mShape) o.setCell(self.mCell) o.setRotation(self.mRotation) return o
def position(self): return QPointF(self.mPos)
class Label: ''' A plain ol' "scene label" using an underlying ``QGraphicsTextItem``. After hacking for many days on multiple "label" systems inside ``pyqtgraph`` yet again we're left writing our own since it seems all of those are over complicated, ad-hoc, transform-mangling, messes which can't accomplish the simplest things via their inputs (such as pinning to the left hand side of a view box). Here we do the simple thing where the label uses callables to figure out the (x, y) coordinate "pin point": nice and simple. This type is another effort (see our graphics) to start making small, re-usable label components that can actually be used to build production grade UIs... ''' def __init__( self, view: pg.ViewBox, fmt_str: str, color: str = 'default_light', x_offset: float = 0, font_size: str = 'small', opacity: float = 1, fields: dict = {}, parent: pg.GraphicsObject = None, update_on_range_change: bool = True, ) -> None: vb = self.vb = view self._fmt_str = fmt_str self._view_xy = QPointF(0, 0) self.scene_anchor: Optional[Callable[..., QPointF]] = None self._x_offset = x_offset txt = self.txt = QtWidgets.QGraphicsTextItem(parent=parent) txt.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache) vb.scene().addItem(txt) # configure font size based on DPI dpi_font = DpiAwareFont(font_size=font_size, ) dpi_font.configure_to_dpi() txt.setFont(dpi_font.font) txt.setOpacity(opacity) # register viewbox callbacks if update_on_range_change: vb.sigRangeChanged.connect(self.on_sigrange_change) self._hcolor: str = '' self.color = color self.fields = fields self.orient_v = 'bottom' self._anchor_func = self.txt.pos().x # not sure if this makes a diff self.txt.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache) # TODO: edit and selection support # https://doc.qt.io/qt-5/qt.html#TextInteractionFlag-enum # self.setTextInteractionFlags(QtGui.Qt.TextEditorInteraction) @property def color(self) -> str: return self._hcolor @color.setter def color(self, color: str) -> None: self.txt.setDefaultTextColor(pg.mkColor(hcolor(color))) self._hcolor = color def update(self) -> None: ''' Update this label either by invoking its user defined anchoring function, or by positioning to the last recorded data view coordinates. ''' # move label in scene coords to desired position anchor = self.scene_anchor if anchor: self.txt.setPos(anchor()) else: # position based on last computed view coordinate self.set_view_pos(self._view_xy.y()) def on_sigrange_change(self, vr, r) -> None: return self.update() @property def w(self) -> float: return self.txt.boundingRect().width() def scene_br(self) -> QRectF: txt = self.txt return txt.mapToScene(txt.boundingRect()).boundingRect() @property def h(self) -> float: return self.txt.boundingRect().height() def vbr(self) -> QRectF: return self.vb.boundingRect() def set_x_anchor_func( self, func: Callable, ) -> None: assert isinstance(func(), float) self._anchor_func = func def set_view_pos( self, y: float, x: Optional[float] = None, ) -> None: if x is None: scene_x = self._anchor_func() or self.txt.pos().x() x = self.vb.mapToView(QPointF(scene_x, scene_x)).x() # get new (inside the) view coordinates / position self._view_xy = QPointF(x, y) # map back to the outer UI-land "scene" coordinates s_xy = self.vb.mapFromView(self._view_xy) if self.orient_v == 'top': s_xy = QPointF(s_xy.x(), s_xy.y() - self.h) # move label in scene coords to desired position self.txt.setPos(s_xy) assert s_xy == self.txt.pos() @property def fmt_str(self) -> str: return self._fmt_str @fmt_str.setter def fmt_str(self, fmt_str: str) -> None: self._fmt_str = fmt_str def format(self, **fields: dict) -> str: out = {} # this is hacky support for single depth # calcs of field data from field data # ex. to calculate a $value = price * size for k, v in fields.items(): if isfunction(v): out[k] = v(fields) else: out[k] = v text = self._fmt_str.format(**out) # for large numbers with a thousands place text = text.replace(',', ' ') self.txt.setPlainText(text) def render(self) -> None: self.format(**self.fields) def show(self) -> None: self.txt.show() self.txt.update() def hide(self) -> None: self.txt.hide() def delete(self) -> None: self.vb.scene().removeItem(self.txt)
def setPoints(self, points): self.points = points rect = QRectF(QPointF(*points[0]), QPointF(*points[1])) self.setRect(rect)
_OFFSET1 = _BASE_WIDTH / 4 _DEFAULT_RECT = QRectF(0, 0, _BW, _BW) _B_PEN = QPen(styles.BLUE_STROKE, styles.INSERTWIDTH) _R_PEN = QPen(styles.RED_STROKE, styles.SKIPWIDTH) _NO_PEN = QPen(Qt.NoPen) def _insertGen(path, start, c1, p1, c2): path.moveTo(start) path.quadTo(c1, p1) path.quadTo(c2, start) # end def _PATH_START = QPointF(_HBW, _HBW) _PATH_MID_UP = QPointF(_HBW, -_BW) _PATH_UP_UP_CTRL_PT = QPointF(-_HBW, -_BW) _PATH_UP_DOWN_CTRL_PT = QPointF(1.5 * _BW, -_BW) _PATH_MID_DOWN = QPointF(_HBW, 2 * _BW) _PATH_DOWN_DOWN_CTRL_PT = QPointF(-_HBW, 2 * _BW) _PATH_DOWN_UP_CTRL_PT = QPointF(1.5 * _BW, 2 * _BW) _INSERT_PATH_UP = QPainterPath() _insertGen(_INSERT_PATH_UP, _PATH_START, _PATH_UP_UP_CTRL_PT,\ _PATH_MID_UP, _PATH_UP_DOWN_CTRL_PT) _INSERT_PATH_UP.translate(_OFFSET1, 0) _INSERT_PATH_UP_RECT = _INSERT_PATH_UP.boundingRect() _INSERT_PATH_DOWN = QPainterPath() _insertGen(_INSERT_PATH_DOWN, _PATH_START, _PATH_DOWN_DOWN_CTRL_PT,\ _PATH_MID_DOWN, _PATH_DOWN_UP_CTRL_PT) _INSERT_PATH_DOWN.translate(_OFFSET1, 0)
def screenToTileCoords(self, x, y): p = RenderParams(self.map()) if (p.staggerX): if p.staggerEven: _x = p.sideOffsetX else: _x = 0 x -= _x else: if p.staggerEven: _x = p.sideOffsetY else: _x = 0 y -= _x # Start with the coordinates of a grid-aligned tile referencePoint = QPoint(math.floor(x / p.tileWidth), math.floor(y / p.tileHeight)) # Relative x and y position on the base square of the grid-aligned tile rel = QPointF(x - referencePoint.x() * p.tileWidth, y - referencePoint.y() * p.tileHeight) # Adjust the reference point to the correct tile coordinates if p.staggerX: staggerAxisIndex = referencePoint.x() else: staggerAxisIndex = referencePoint.y() staggerAxisIndex *= 2 if (p.staggerEven): staggerAxisIndex += 1 y_pos = rel.x() * ( p.tileHeight / p.tileWidth) # Check whether the cursor is in any of the corners (neighboring tiles) if (p.sideOffsetY - y_pos > rel.y()): return QPointF(self.topLeft(referencePoint.x(), referencePoint.y())) if (-p.sideOffsetY + y_pos > rel.y()): return QPointF(self.topRight(referencePoint.x(), referencePoint.y())) if (p.sideOffsetY + y_pos < rel.y()): return QPointF(self.bottomLeft(referencePoint.x(), referencePoint.y())) if (p.sideOffsetY * 3 - y_pos < rel.y()): return QPointF(self.bottomRight(referencePoint.x(), referencePoint.y())) return QPointF(referencePoint)
def onMouseMove_drag(self, imageview, event): imageview._deltaPan = QPointF(event.pos() - imageview._lastPanPoint) imageview._panning() imageview._lastPanPoint = event.pos()
def _draw_animal_direction(self, painter, animal): painter.setPen(QPen(QColor(0, 0, 0), 2)) painter.drawLine( QPointF(animal.x, animal.y), QPointF(animal.x + math.cos(animal.angle) * animal.size, animal.y + math.sin(animal.angle) * animal.size))
def mouseMoveEvent(self, event): if not event.buttons() & Qt.LeftButton: super().mouseMoveEvent(event) return contour = self._targetContour if contour is None: return # we don't make any check here, mousePressEvent did it for us pos = event.pos() # selected point pt = contour[-1] if not contour.open: pt_ = contour[0] if pt_.selected: pt = pt_ if pt.segmentType and not self._shouldMoveOnCurve: # don't make a curve until enough distance is reached widget = self.parent() rect = QRectF(self._origin, event.localPos()) widgetRect = widget.mapRectFromCanvas(rect) if (widgetRect.bottomRight() - widgetRect.topLeft( )).manhattanLength() < 10: return # go onSmooth = not event.modifiers() & Qt.AltModifier pt.selected = False pt.smooth = len(contour) > 1 and onSmooth contour.holdNotifications() if pt.segmentType == "line" and onSmooth: self._coerceSegmentToCurve(contour, pt, pos) elif pt.smooth and contour.open: # if there's a curve segment behind, we need to update the # offCurve's position to inverse if len(contour) > 1: onCurveBefore = contour[-2] onCurveBefore.x = 2 * pt.x - pos.x() onCurveBefore.y = 2 * pt.y - pos.y() if contour.open: contour.addPoint((pos.x(), pos.y())) contour[-1].selected = True contour.postNotification( notification="Contour.SelectionChanged") contour.releaseHeldNotifications() else: if pt.segmentType: onCurve = pt elif contour.open: onCurve = contour[-2] else: onCurve = contour[0] if event.modifiers() & Qt.ShiftModifier: pos = self.clampToOrigin( event.localPos(), QPointF(onCurve.x, onCurve.y)).toPoint() if self._shouldMoveOnCurve: dx = pos.x() - pt.x dy = pos.y() - pt.y moveUIPoint(contour, onCurve, (dx, dy)) else: pt.x = pos.x() pt.y = pos.y() if contour.open and len(contour) >= 3 and onCurve.smooth: if onCurve.segmentType == "line": self._coerceSegmentToCurve(contour, onCurve, pos) otherSidePoint = contour[-3] otherSidePoint.x = 2 * onCurve.x - pos.x() otherSidePoint.y = 2 * onCurve.y - pos.y() contour.dirty = True
def __findIntersection(self, p1, p2, p3, p4): """ Private method to calculate the intersection point of two lines. The first line is determined by the points p1 and p2, the second line by p3 and p4. If the intersection point is not contained in the segment p1p2, then it returns (-1.0, -1.0). For the function's internal calculations remember:<br /> QT coordinates start with the point (0,0) as the topleft corner and x-values increase from left to right and y-values increase from top to bottom; it means the visible area is quadrant I in the regular XY coordinate system <pre> Quadrant II | Quadrant I -----------------|----------------- Quadrant III | Quadrant IV </pre> In order for the linear function calculations to work in this method we must switch x and y values (x values become y values and viceversa) @param p1 first point of first line (QPointF) @param p2 second point of first line (QPointF) @param p3 first point of second line (QPointF) @param p4 second point of second line (QPointF) @return the intersection point (QPointF) """ x1 = p1.y() y1 = p1.x() x2 = p2.y() y2 = p2.x() x3 = p3.y() y3 = p3.x() x4 = p4.y() y4 = p4.x() # line 1 is the line between (x1, y1) and (x2, y2) # line 2 is the line between (x3, y3) and (x4, y4) no_line1 = True # it is false, if line 1 is a linear function no_line2 = True # it is false, if line 2 is a linear function slope1 = 0.0 slope2 = 0.0 b1 = 0.0 b2 = 0.0 if x2 != x1: slope1 = (y2 - y1) / (x2 - x1) b1 = y1 - slope1 * x1 no_line1 = False if x4 != x3: slope2 = (y4 - y3) / (x4 - x3) b2 = y3 - slope2 * x3 no_line2 = False pt = QPointF() # if either line is not a function if no_line1 and no_line2: # if the lines are not the same one if x1 != x3: return QPointF(-1.0, -1.0) # if the lines are the same ones if y3 <= y4: if y3 <= y1 and y1 <= y4: return QPointF(y1, x1) else: return QPointF(y2, x2) else: if y4 <= y1 and y1 <= y3: return QPointF(y1, x1) else: return QPointF(y2, x2) elif no_line1: pt.setX(slope2 * x1 + b2) pt.setY(x1) if y1 >= y2: if not (y2 <= pt.x() and pt.x() <= y1): pt.setX(-1.0) pt.setY(-1.0) else: if not (y1 <= pt.x() and pt.x() <= y2): pt.setX(-1.0) pt.setY(-1.0) return pt elif no_line2: pt.setX(slope1 * x3 + b1) pt.setY(x3) if y3 >= y4: if not (y4 <= pt.x() and pt.x() <= y3): pt.setX(-1.0) pt.setY(-1.0) else: if not (y3 <= pt.x() and pt.x() <= y4): pt.setX(-1.0) pt.setY(-1.0) return pt if slope1 == slope2: pt.setX(-1.0) pt.setY(-1.0) return pt pt.setY((b2 - b1) / (slope1 - slope2)) pt.setX(slope1 * pt.y() + b1) # the intersection point must be inside the segment (x1, y1) (x2, y2) if x2 >= x1 and y2 >= y1: if not ((x1 <= pt.y() and pt.y() <= x2) and (y1 <= pt.x() and pt.x() <= y2)): pt.setX(-1.0) pt.setY(-1.0) elif x2 < x1 and y2 >= y1: if not ((x2 <= pt.y() and pt.y() <= x1) and (y1 <= pt.x() and pt.x() <= y2)): pt.setX(-1.0) pt.setY(-1.0) elif x2 >= x1 and y2 < y1: if not ((x1 <= pt.y() and pt.y() <= x2) and (y2 <= pt.x() and pt.x() <= y1)): pt.setX(-1.0) pt.setY(-1.0) else: if not ((x2 <= pt.y() and pt.y() <= x1) and (y2 <= pt.x() and pt.x() <= y1)): pt.setX(-1.0) pt.setY(-1.0) return pt
def drawPitchScale(self, painter, area, intrusion, drawNumbersLeft, drawNumbersRight): unused(intrusion) # The area should be quadratic but if not width is the major size. w = area.width() if w < area.height(): w = area.height() pen = QPen() pen.setWidthF(self.lineWidth) pen.setColor(Qt.white) painter.setPen(pen) savedTransform = painter.transform() # find the mark nearest center snap = qRound( self.pitch / self.PITCH_SCALE_RESOLUTION) * self.PITCH_SCALE_RESOLUTION _min = snap - self.PITCH_SCALE_HALFRANGE _max = snap + self.PITCH_SCALE_HALFRANGE degrees = _min while degrees <= _max: isMajor = degrees % (self.PITCH_SCALE_RESOLUTION * 2) == 0 linewidth = self.PITCH_SCALE_MINORWIDTH if isMajor: linewidth = self.PITCH_SCALE_MAJORWIDTH if abs(degrees) > self.PITCH_SCALE_WIDTHREDUCTION_FROM: # we want: 1 at PITCH_SCALE_WIDTHREDUCTION_FROM and PITCH_SCALE_WIDTHREDUCTION at 90. # That is PITCH_SCALE_WIDTHREDUCTION + (1-PITCH_SCALE_WIDTHREDUCTION) * f(pitch) # where f(90)=0 and f(PITCH_SCALE_WIDTHREDUCTION_FROM)=1 # f(p) = (90-p) * 1/(90-PITCH_SCALE_WIDTHREDUCTION_FROM) # or PITCH_SCALE_WIDTHREDUCTION + f(pitch) - f(pitch) * PITCH_SCALE_WIDTHREDUCTION # or PITCH_SCALE_WIDTHREDUCTION (1-f(pitch)) + f(pitch) fromVertical = -90 - self.pitch if self.pitch >= 0: fromVertical = 90 - self.pitch if fromVertical < 0: fromVertical = -fromVertical temp = fromVertical * 1 / ( 90.0 - self.PITCH_SCALE_WIDTHREDUCTION_FROM) linewidth *= (self.PITCH_SCALE_WIDTHREDUCTION * (1 - temp) + temp) shift = self.pitchAngleToTranslation(w, self.pitch - degrees) # TODO: Intrusion detection and evasion. That is, don't draw # where the compass has intruded. painter.translate(0, shift) start = QPointF(-linewidth * w, 0) end = QPointF(linewidth * w, 0) painter.drawLine(start, end) if isMajor and (drawNumbersLeft or drawNumbersRight): displayDegrees = degrees if displayDegrees > 90: displayDegrees = 180 - displayDegrees elif displayDegrees < -90: displayDegrees = -180 - displayDegrees if self.SHOW_ZERO_ON_SCALES or degrees: if drawNumbersLeft: self.drawTextRightCenter( painter, '{0}'.format(displayDegrees), self.mediumTextSize, -self.PITCH_SCALE_MAJORWIDTH * w - 10, 0) if drawNumbersRight: self.drawTextLeftCenter( painter, '{0}'.format(displayDegrees), self.mediumTextSize, self.PITCH_SCALE_MAJORWIDTH * w + 10, 0) painter.setTransform(savedTransform) degrees += self.PITCH_SCALE_RESOLUTION
def createGraphics(self): """ Create the graphical representation of the FMU's inputs and outputs """ def variableColor(variable): if variable.type == 'Real': return QColor.fromRgb(0, 0, 127) elif variable.type in ['Integer', 'Enumeration']: return QColor.fromRgb(255, 127, 0) elif variable.type == 'Boolean': return QColor.fromRgb(255, 0, 255) elif variable.type == 'String': return QColor.fromRgb(0, 128, 0) else: return QColor.fromRgb(0, 0, 0) inputVariables = [] outputVariables = [] maxInputLabelWidth = 0 maxOutputLabelWidth = 0 textItem = QGraphicsTextItem() fontMetrics = QFontMetricsF(textItem.font()) for variable in self.modelDescription.modelVariables: if variable.causality == 'input': inputVariables.append(variable) elif variable.causality == 'output': outputVariables.append(variable) for variable in inputVariables: maxInputLabelWidth = max(maxInputLabelWidth, fontMetrics.width(variable.name)) for variable in outputVariables: maxOutputLabelWidth = max(maxOutputLabelWidth, fontMetrics.width(variable.name)) from math import floor scene = QGraphicsScene() self.ui.graphicsView.setScene(scene) group = QGraphicsItemGroup() scene.addItem(group) group.setPos(200.5, -50.5) lh = 15 # line height w = max(150., maxInputLabelWidth + maxOutputLabelWidth + 20) h = max(50., 10 + lh * max(len(inputVariables), len(outputVariables))) block = QGraphicsRectItem(0, 0, w, h, group) block.setPen(QColor.fromRgb(0, 0, 255)) pen = QPen() pen.setWidthF(1) font = QFont() font.setPixelSize(10) # inputs y = floor((h - len(inputVariables) * lh) / 2 - 2) for variable in inputVariables: text = QGraphicsTextItem(variable.name, group) text.setDefaultTextColor(QColor.fromRgb(0, 0, 255)) text.setFont(font) text.setX(3) text.setY(y) polygon = QPolygonF([ QPointF(-13.5, y + 4), QPointF(1, y + 11), QPointF(-13.5, y + 18) ]) path = QPainterPath() path.addPolygon(polygon) path.closeSubpath() contour = QGraphicsPathItem(path, group) contour.setPen(QPen(Qt.NoPen)) contour.setBrush(variableColor(variable)) y += lh # outputs y = floor((h - len(outputVariables) * lh) / 2 - 2) for variable in outputVariables: text = QGraphicsTextItem(variable.name, group) text.setDefaultTextColor(QColor.fromRgb(0, 0, 255)) text.setFont(font) text.setX(w - 3 - text.boundingRect().width()) text.setY(y) polygon = QPolygonF([ QPointF(w, y + 0 + 7.5), QPointF(w + 7, y + 3.5 + 7.5), QPointF(w, y + 7 + 7.5) ]) path = QPainterPath() path.addPolygon(polygon) path.closeSubpath() contour = QGraphicsPathItem(path, group) pen = QPen() pen.setColor(variableColor(variable)) pen.setJoinStyle(Qt.MiterJoin) contour.setPen(pen) y += lh
def paint(self, painter, option, widget): painter.setPen(Qt.white) painter.drawEllipse(QPointF(0.0, 0.0), 5.0, 5.0)
def nodePos(self, idx): return QPointF(self.m_nodePositions[idx])
def update_polygon(self): angle = self.line().angle() * math.pi / 180 dx = self.selection_offset * math.sin(angle) dy = self.selection_offset * math.cos(angle) offset1 = QPointF(dx, dy) offset2 = QPointF(-dx, -dy) if self.head is None and self.tail is None: self.selection_polygon = QPolygonF([ self.line().p1() + offset1, self.line().p1() + offset2, self.line().p2() + offset2, self.line().p2() + offset1 ]) elif self.tail is None: head_angle = self.head.angle() * math.pi / 180 head_dx = self.selection_offset * math.sin(head_angle) head_dy = self.selection_offset * math.cos(head_angle) head_offset1 = QPointF(head_dx, head_dy) head_offset2 = QPointF(-head_dx, -head_dy) self.selection_polygon = QPolygonF([ self.line().p1() + offset1, self.head.p1() + head_offset1, self.head.p1() + head_offset2, self.line().p1() + offset2, self.line().p2() + offset2, self.line().p2() + offset1 ]) elif self.head is None: tail_angle = self.tail.angle() * math.pi / 180 tail_dx = self.selection_offset * math.sin(tail_angle) tail_dy = self.selection_offset * math.cos(tail_angle) tail_offset1 = QPointF(tail_dx, tail_dy) tail_offset2 = QPointF(-tail_dx, -tail_dy) self.selection_polygon = QPolygonF([ self.line().p1() + offset1, self.line().p1() + offset2, self.line().p2() + offset2, self.tail.p2() + tail_offset2, self.tail.p2() + tail_offset1, self.line().p2() + offset1 ]) else: head_angle = self.head.angle() * math.pi / 180 head_dx = self.selection_offset * math.sin(head_angle) head_dy = self.selection_offset * math.cos(head_angle) head_offset1 = QPointF(head_dx, head_dy) head_offset2 = QPointF(-head_dx, -head_dy) tail_angle = self.tail.angle() * math.pi / 180 tail_dx = self.selection_offset * math.sin(tail_angle) tail_dy = self.selection_offset * math.cos(tail_angle) tail_offset1 = QPointF(tail_dx, tail_dy) tail_offset2 = QPointF(-tail_dx, -tail_dy) self.selection_polygon = QPolygonF([ self.line().p1() + offset1, self.head.p1() + head_offset1, self.head.p1() + head_offset2, self.line().p1() + offset2, self.line().p2() + offset2, self.tail.p2() + tail_offset2, self.tail.p2() + tail_offset1, self.line().p2() + offset1 ])
def drawAIAirframeFixedFeatures(self, painter, area): ''' red line from -7/10 to -5/10 half-width red line from 7/10 to 5/10 half-width red slanted line from -2/10 half-width to 0 red slanted line from 2/10 half-width to 0 red arrow thing under roll scale prepareTransform(painter, width, height); ''' painter.resetTransform() painter.translate(area.center()) w = area.width() h = area.height() pen = QPen() pen.setWidthF(self.lineWidth * 1.5) pen.setColor(QColor(255, 0, 0)) painter.setPen(pen) length = 0.15 side = 0.5 # The 2 lines at sides. painter.drawLine(QPointF(-side * w, 0), QPointF(-(side - length) * w, 0)) painter.drawLine(QPointF(side * w, 0), QPointF((side - length) * w, 0)) pen.setColor(QColor(255, 255, 255)) painter.setPen(pen) v = abs(self.__getAdditionalParameter('voltage')) a = abs(self.__getAdditionalParameter('current')) # Power usage self.drawTextLeftCenter(painter, '{:.1f}V'.format(v), self.smallTestSize, -side * w * 0.9, side * w / 4) self.drawTextLeftCenter(painter, '{:.1f}A'.format(a), self.smallTestSize, -side * w * 0.9, side * w / 4 + self.mediumTextSize * 1.1) # GPS groundspeed / IAS spd = '' if self.isGPSSpeedPrimary: # TODO add option to hide air speed when there is no air speed sensor spd = 'IAS ---' if self.primarySpeed == self.UNKNOWN_SPEED else 'IAS {:.1f}'.format( self.primarySpeed) else: spd = 'GS ---' if self.groundspeed == self.UNKNOWN_SPEED else 'GS {:.1f}'.format( self.groundspeed) self.drawTextLeftCenter(painter, spd, self.smallTestSize, -side * w * 0.9, side * w / 4 + self.mediumTextSize * 3.3) # Number of GPS satellites s = self.__getAdditionalParameter('gps_satellite') s = 0 if s == 255 else s self.drawTextRightCenter(painter, '{} {}'.format(chr(0x1F6F0), s), self.smallTestSize, side * w * 0.9, side * w / 4) # RC receiver RSSI s = self.__getAdditionalParameter('rc_rssi') s = 0 if s == 255 else s s /= 254.0 self.drawTextRightCenter(painter, '{} {}'.format(chr(0x1F4F6), int(s * 100.0)), self.smallTestSize, side * w * 0.9, side * w / 4 + self.smallTestSize * 1.5) pen.setColor(QColor(255, 0, 0)) painter.setPen(pen) rel = length / math.sqrt(2) # The gull painter.drawLine(QPointF(rel * w, rel * w / 2), QPoint(0, 0)) painter.drawLine(QPointF(-rel * w, rel * w / 2), QPoint(0, 0)) # The roll scale marker. markerPath = QPainterPath(QPointF(0, -w * self.ROLL_SCALE_RADIUS + 1)) markerPath.lineTo( -h * self.ROLL_SCALE_MARKERWIDTH / 2, -w * (self.ROLL_SCALE_RADIUS - self.ROLL_SCALE_MARKERHEIGHT) + 1) markerPath.lineTo( h * self.ROLL_SCALE_MARKERWIDTH / 2, -w * (self.ROLL_SCALE_RADIUS - self.ROLL_SCALE_MARKERHEIGHT) + 1) markerPath.closeSubpath() painter.drawPath(markerPath)
def mapToViewport(self, pos): return QPointF(pos.x(), pos.y())
def setNodePos(self, idx, pos): self.m_nodePositions[idx] = QPointF(pos)
def drawAIGlobalFeatures(self, painter, mainArea, paintArea): painter.resetTransform() painter.translate(mainArea.center()) pitchPixels = self.pitchAngleToTranslation(mainArea.height(), self.pitch) gradientEnd = self.pitchAngleToTranslation(mainArea.height(), 60) if math.isnan(self.roll) == False: # check for NaN painter.rotate(-self.roll) painter.translate(0, pitchPixels) # Calculate the radius of area we need to paint to cover all. rtx = painter.transform().inverted()[0] topLeft = rtx.map(paintArea.topLeft()) topRight = rtx.map(paintArea.topRight()) bottomLeft = rtx.map(paintArea.bottomLeft()) bottomRight = rtx.map(paintArea.bottomRight()) # Just KISS... make a rectangluar basis. minx = self.min4(topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x()) maxx = self.max4(topLeft.x(), topRight.x(), bottomLeft.x(), bottomRight.x()) miny = self.min4(topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y()) maxy = self.max4(topLeft.y(), topRight.y(), bottomLeft.y(), bottomRight.y()) hzonLeft = QPoint(minx, 0) hzonRight = QPoint(maxx, 0) skyPath = QPainterPath(hzonLeft) skyPath.lineTo(QPointF(minx, miny)) skyPath.lineTo(QPointF(maxx, miny)) skyPath.lineTo(hzonRight) skyPath.closeSubpath() # TODO: The gradient is wrong now. skyGradient = QLinearGradient(0, -gradientEnd, 0, 0) skyGradient.setColorAt(0, QColor.fromHsvF(0.6, 1.0, 0.7)) skyGradient.setColorAt(1, QColor.fromHsvF(0.6, 0.25, 0.9)) skyBrush = QBrush(skyGradient) painter.fillPath(skyPath, skyBrush) groundPath = QPainterPath(hzonRight) groundPath.lineTo(maxx, maxy) groundPath.lineTo(minx, maxy) groundPath.lineTo(hzonLeft) groundPath.closeSubpath() groundGradient = QLinearGradient(0, gradientEnd, 0, 0) groundGradient.setColorAt(0, QColor.fromHsvF(0.25, 1, 0.5)) groundGradient.setColorAt(1, QColor.fromHsvF(0.25, 0.25, 0.5)) groundBrush = QBrush(groundGradient) painter.fillPath(groundPath, groundBrush) pen = QPen() pen.setWidthF(self.lineWidth) pen.setColor(QColor(0, 255, 0)) painter.setPen(pen) start = QPointF(-mainArea.width(), 0) end = QPoint(mainArea.width(), 0) painter.drawLine(start, end)
def mouseMoveEvent(self, event): mouseMovePos = event.scenePos() if self.mouseIsPressed: if self.mousePressArea == 'topRect': self._rect.setTop(self.rectPress.y() - (self.mousePressPos.y() - mouseMovePos.y())) elif self.mousePressArea == 'bottomRect': self._rect.setBottom(self.rectPress.bottom() - (self.mousePressPos.y() - mouseMovePos.y())) elif self.mousePressArea == 'leftRect': self._rect.setLeft(self.rectPress.left() - (self.mousePressPos.x() - mouseMovePos.x())) elif self.mousePressArea == 'rightRect': self._rect.setRight(self.rectPress.right() - (self.mousePressPos.x() - mouseMovePos.x())) elif self.mousePressArea == 'topleftRect': self._rect.setTopLeft(self.rectPress.topLeft() - (self.mousePressPos - mouseMovePos)) elif self.mousePressArea == 'toprightRect': self._rect.setTopRight(self.rectPress.topRight() - (self.mousePressPos - mouseMovePos)) elif self.mousePressArea == 'bottomleftRect': self._rect.setBottomLeft(self.rectPress.bottomLeft() - (self.mousePressPos - mouseMovePos)) elif self.mousePressArea == 'bottomrightRect': self._rect.setBottomRight(self.rectPress.bottomRight() - (self.mousePressPos - mouseMovePos)) elif self.mousePressArea == 'topRotation': rectCenter = self._rect.center() pressedPos = self.mousePressPos #mouseMovePos A = (pressedPos - rectCenter) B = (mouseMovePos - rectCenter) pro = A.x() * B.x() + A.y() * B.y() theta = np.arctan2(A.x() * B.y() - A.y() * B.x(), pro) #theta = self.rotationAngle+theta self._rect.moveCenter(QPointF(0, 0)) # self.setAngle(theta) pos = self.rectPress.center() rad = self.rotationAngle self._rect.moveCenter( QPointF(pos.x() * np.cos(rad) - np.sin(rad) * pos.y(), pos.x() * np.sin(rad) + np.cos(rad) * pos.y())) #print("RotationBegginn") else: #pos = self.mousePressPos - mouseMovePos pos = self.rectPress.center() - (self.mousePressPos - mouseMovePos) rad = -1 * self.rotationAngle deltaPos = (self.mousePressPos - mouseMovePos) self._rect.moveCenter( QPointF( self.rectPress.center().x() - (np.cos(rad) * deltaPos.x() - np.sin(rad) * deltaPos.y()), self.rectPress.center().y() - (np.sin(rad) * deltaPos.x() + np.cos(rad) * deltaPos.y()))) self.updateResizeHandles() self.prepareGeometryChange()
def main(): import sys app = QApplication(sys.argv) series0 = QLineSeries() series1 = QLineSeries() series0 << QPointF(1, 5) << QPointF(3, 7) << QPointF(7, 6) << QPointF( 9, 7) << QPointF(12, 6) << QPointF(16, 7) << QPointF(18, 5) series1 << QPointF(1, 3) << QPointF(3, 4) << QPointF(7, 3) << QPointF( 8, 2) << QPointF(12, 3) << QPointF(16, 4) << QPointF(18, 3) series = QAreaSeries(series0, series1) series.setName("Batman") pen = QPen(0x059605) pen.setWidth(3) series.setPen(pen) gradient = QLinearGradient(QPointF(0, 0), QPointF(0, 1)) gradient.setColorAt(0.0, QColor(0x3CC63C)) gradient.setColorAt(1.0, QColor(0x26F626)) gradient.setCoordinateMode(QGradient.ObjectBoundingMode) series.setBrush(gradient) chart = QChart() chart.addSeries(series) chart.setTitle("Simple areachart example") chart.createDefaultAxes() chart.axes(Qt.Horizontal)[0].setRange(0, 20) chart.axes(Qt.Vertical)[0].setRange(0, 10) chartView = QChartView(chart) chartView.setRenderHint(QPainter.Antialiasing) window = QMainWindow() window.setCentralWidget(chartView) window.resize(400, 300) window.show() sys.exit(app.exec_())