def __init__(self, port_mode, port_type, parent): QGraphicsPathItem.__init__(self) self.setParentItem(parent) self.m_port_mode = port_mode self.m_port_type = port_type # Port position doesn't change while moving around line self.p_itemX = self.scenePos().x() self.p_itemY = self.scenePos().y() self.p_width = parent.getPortWidth() if port_type == PORT_TYPE_AUDIO_JACK: pen = QPen(canvas.theme.line_audio_jack, 2) elif port_type == PORT_TYPE_MIDI_JACK: pen = QPen(canvas.theme.line_midi_jack, 2) elif port_type == PORT_TYPE_MIDI_ALSA: pen = QPen(canvas.theme.line_midi_alsa, 2) elif port_type == PORT_TYPE_PARAMETER: pen = QPen(canvas.theme.line_parameter, 2) else: qWarning( "PatchCanvas::CanvasBezierLineMov({}, {}, {}) - invalid port type" .format(port_mode2str(port_mode), port_type2str(port_type), parent)) pen = QPen(Qt.black) pen.setCapStyle(Qt.FlatCap) pen.setWidthF(pen.widthF() + 0.00001) self.setPen(pen)
def shape(self): if self.__shape is None: path = self.path() pen = QPen(self.pen()) pen.setWidthF(max(pen.widthF(), 7.0)) pen.setStyle(Qt.SolidLine) self.__shape = stroke_path(path, pen) return self.__shape
class PenWidthSettable(object): def __init__(self): self._pen = QPen() @property def penWidth(self) -> float: return self._pen.widthF() @penWidth.setter def penWidth(self, newWidth: number) -> None: self._pen.setWidthF(newWidth)
def paint(self, painter, option, widget): painter.save() painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL)) selected = self.isSelected() theme = canvas.theme if self.m_port_type == PORT_TYPE_AUDIO_JACK: poly_color = theme.port_audio_jack_bg_sel if selected else theme.port_audio_jack_bg poly_pen = theme.port_audio_jack_pen_sel if selected else theme.port_audio_jack_pen text_pen = theme.port_audio_jack_text_sel if selected else theme.port_audio_jack_text conn_pen = QPen(theme.port_audio_jack_pen_sel) elif self.m_port_type == PORT_TYPE_MIDI_JACK: poly_color = theme.port_midi_jack_bg_sel if selected else theme.port_midi_jack_bg poly_pen = theme.port_midi_jack_pen_sel if selected else theme.port_midi_jack_pen text_pen = theme.port_midi_jack_text_sel if selected else theme.port_midi_jack_text conn_pen = QPen(theme.port_midi_jack_pen_sel) elif self.m_port_type == PORT_TYPE_MIDI_ALSA: poly_color = theme.port_midi_alsa_bg_sel if selected else theme.port_midi_alsa_bg poly_pen = theme.port_midi_alsa_pen_sel if selected else theme.port_midi_alsa_pen text_pen = theme.port_midi_alsa_text_sel if selected else theme.port_midi_alsa_text conn_pen = QPen(theme.port_midi_alsa_pen_sel) elif self.m_port_type == PORT_TYPE_PARAMETER: poly_color = theme.port_parameter_bg_sel if selected else theme.port_parameter_bg poly_pen = theme.port_parameter_pen_sel if selected else theme.port_parameter_pen text_pen = theme.port_parameter_text_sel if selected else theme.port_parameter_text conn_pen = QPen(theme.port_parameter_pen_sel) else: qCritical( "PatchCanvas::CanvasPort.paint() - invalid port type '%s'" % port_type2str(self.m_port_type)) return # To prevent quality worsening poly_pen = QPen(poly_pen) poly_pen.setWidthF(poly_pen.widthF() + 0.00001) if self.m_is_alternate: poly_color = poly_color.darker(180) #poly_pen.setColor(poly_pen.color().darker(110)) #text_pen.setColor(text_pen.color()) #.darker(150)) #conn_pen.setColor(conn_pen.color()) #.darker(150)) lineHinting = poly_pen.widthF() / 2 poly_locx = [0, 0, 0, 0, 0] poly_corner_xhinting = (float(canvas.theme.port_height) / 2) % floor( float(canvas.theme.port_height) / 2) if poly_corner_xhinting == 0: poly_corner_xhinting = 0.5 * ( 1 - 7 / (float(canvas.theme.port_height) / 2)) if self.m_port_mode == PORT_MODE_INPUT: text_pos = QPointF(3, canvas.theme.port_text_ypos) if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON: poly_locx[0] = lineHinting poly_locx[1] = self.m_port_width + 5 - lineHinting poly_locx[2] = self.m_port_width + 12 - poly_corner_xhinting poly_locx[3] = self.m_port_width + 5 - lineHinting poly_locx[4] = lineHinting elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: poly_locx[0] = lineHinting poly_locx[1] = self.m_port_width + 5 - lineHinting poly_locx[2] = self.m_port_width + 5 - lineHinting poly_locx[3] = self.m_port_width + 5 - lineHinting poly_locx[4] = lineHinting else: qCritical( "PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode) return elif self.m_port_mode == PORT_MODE_OUTPUT: text_pos = QPointF(9, canvas.theme.port_text_ypos) if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON: poly_locx[0] = self.m_port_width + 12 - lineHinting poly_locx[1] = 7 + lineHinting poly_locx[2] = 0 + poly_corner_xhinting poly_locx[3] = 7 + lineHinting poly_locx[4] = self.m_port_width + 12 - lineHinting elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE: poly_locx[0] = self.m_port_width + 12 - lineHinting poly_locx[1] = 5 + lineHinting poly_locx[2] = 5 + lineHinting poly_locx[3] = 5 + lineHinting poly_locx[4] = self.m_port_width + 12 - lineHinting else: qCritical( "PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode) return else: qCritical( "PatchCanvas::CanvasPort.paint() - invalid port mode '%s'" % port_mode2str(self.m_port_mode)) return polygon = QPolygonF() polygon += QPointF(poly_locx[0], lineHinting) polygon += QPointF(poly_locx[1], lineHinting) polygon += QPointF(poly_locx[2], float(canvas.theme.port_height) / 2) polygon += QPointF(poly_locx[3], canvas.theme.port_height - lineHinting) polygon += QPointF(poly_locx[4], canvas.theme.port_height - lineHinting) polygon += QPointF(poly_locx[0], lineHinting) if canvas.theme.port_bg_pixmap: portRect = polygon.boundingRect().adjusted(-lineHinting + 1, -lineHinting + 1, lineHinting - 1, lineHinting - 1) portPos = portRect.topLeft() painter.drawTiledPixmap(portRect, canvas.theme.port_bg_pixmap, portPos) else: painter.setBrush(poly_color) #.lighter(200)) painter.setPen(poly_pen) painter.drawPolygon(polygon) painter.setPen(text_pen) painter.setFont(self.m_port_font) painter.drawText(text_pos, self.m_port_name) if canvas.theme.idx == Theme.THEME_OOSTUDIO and canvas.theme.port_bg_pixmap: conn_pen.setCosmetic(True) conn_pen.setWidthF(0.4) painter.setPen(conn_pen) if self.m_port_mode == PORT_MODE_INPUT: connLineX = portRect.left() + 1 else: connLineX = portRect.right() - 1 conn_path = QPainterPath() conn_path.addRect( QRectF(connLineX - 1, portRect.top(), 2, portRect.height())) painter.fillPath(conn_path, conn_pen.brush()) painter.drawLine( QLineF(connLineX, portRect.top(), connLineX, portRect.bottom())) painter.restore()
class Connection(QGraphicsItem): _startSocket = None _endSocket = None def __init__(self, start_socket, end_socket=None, id_=None, style="solid", curve=True): QGraphicsItem.__init__(self, None) if id_ is None: id_ = id(self) self.id = id_ self._rect = QRectF(0, 0, 0, 0) self._isTempConnection = False self._path = QPainterPath() self._activeStyle = style self._curve = curve self._isActive = False self._color = start_socket.colorRef() self._pen = QPen(self._color) self._penWidthInactive = 2 self._penWidthActive = 4 self._penWidthBlink = 8 self._penWidth = self._penWidthInactive self.setStartSocket(start_socket) self._keyPoints = [] self._centerWidget = None if end_socket is None: # creating a dummy endHook for temporary connection dragging, # the "input" and shape/style parameters have no effect end_mode = "output" if start_socket.isInput() else "input" end_socket = QtSocket(start_socket.parentSocketRow(), end_mode, start_socket._shape, parent_item=self) end_socket.boundingRect().setSize(QSizeF(2.0, 2.0)) self._isTempConnection = True self.setEndConnection(end_socket) self.updateStartPos() self.setZValue(-1.0) self.setActiveState(False) self._blink_depth = 0 def startSocket(self): if self._startSocket is None: return None return self._startSocket() def endSocket(self): if self._endSocket is None: return None return self._endSocket() @property def index(self): index, total = self.startSocket().getIndexInfo(self) return index @property def is_active(self): return self._isActive def boundingRect(self): return self._rect def setKeyPoints(self, interpoints): s = self.scenePos() sx, sy = s.x(), s.y() self._keyPoints = [QPointF(x - sx, -y - sy) for x, y in interpoints] def insertKeyPoint(self, index, coordinate): if index is None: self._keyPoints.append(coordinate) else: self._keyPoints.insert(index, coordinate) def removeKeyPoint(self, index): self._keyPoints.pop(self._keyPoints) def findNearestKeyPoints(self, coordinate): points = [] start_socket = self._startSocket() start_pos = start_socket.scenePos() + start_socket.boundingRect( ).center() points.append(start_pos) points += self._keyPoints distances = [(i, sub(point, coordinate)) for i, point in enumerate(points)] if self.endSocket(): end_socket = self.endSocket() end_position = end_socket.scenePos() + end_socket.boundingRect( ).center() distances.append((-1, self._difDistance(end_position, coordinate))) if len(distances) == 1: return distances[0][0], None distances.sort(key=lambda item: item[1]) return distances[0][0], distances[1][0] def intersectsCircle(self, position, rect, size): size_sqr = size**2 scene_translation = self.startSocket().sceneTransform() connection_rect = scene_translation.mapRect(self._rect) # Line circle intersection test http://i.stack.imgur.com/P556i.png if connection_rect.contains(rect) or ( connection_rect.width() <= size or connection_rect.height() <= size): connection_path = scene_translation.map(self._path) simplified_path = connection_path.simplified() element_count = simplified_path.elementCount() - 1 # In case path is linear if element_count == -1: simplified_path = connection_path element_count = simplified_path.elementCount() previous_point = None for i in range(element_count): element = simplified_path.elementAt(i) point = QPointF(element.x, element.y) previous_point, _previous_point = point, previous_point if _previous_point is None: continue to_position = QVector2D(position - _previous_point) to_end = QVector2D(point - _previous_point) to_end_length = to_end.length() if not to_end_length: continue projection = QVector2D.dotProduct(to_position, to_end) / to_end_length # Projected point lies within this segment if 0 <= projection <= to_end_length: dist_path_sqr = to_position.lengthSquared() - projection**2 if dist_path_sqr < size_sqr: return self def intersectsLine(self, line, path_rect): scene_translation = self.startSocket().sceneTransform() connection_rect = scene_translation.mapRect(self._rect) if connection_rect.contains(path_rect) or path_rect.contains(connection_rect) \ or path_rect.intersects(connection_rect): connection_path = scene_translation.map(self._path) simplified_path = connection_path.simplified() element_count = simplified_path.elementCount() - 1 # In case path is linear if element_count == -1: simplified_path = connection_path element_count = simplified_path.elementCount() previous_point = None for i in range(element_count): element = simplified_path.elementAt(i) point = QPointF(element.x, element.y) if previous_point is not None: segment = QLineF(previous_point, point) intersect_point = QPointF() intersect_type = segment.intersect(line, intersect_point) if intersect_type == QLineF.BoundedIntersection: return True previous_point = point def blink(self, time): self._pen.setWidth(self._penWidthBlink) self.update() timer = QTimer() def on_finished(): self._blink_depth -= 1 # Allow multiple blinks without flashing if not self._blink_depth: self._pen.setWidth(self._penWidth) self.update() self._blink_depth += 1 timer.singleShot(1000 * time, on_finished) def setActiveState(self, active): assert active in (True, False), active if active: self._penWidth = self._penWidthActive else: self._penWidth = self._penWidthInactive self._pen.setWidth(self._penWidth) value = self._activeStyle if value == "dashed": self._pen.setStyle(Qt.DashLine) elif value == "solid": self._pen.setStyle(Qt.SolidLine) elif value == "dot": self._pen.setStyle(Qt.DotLine) else: raise ValueError("Unknown pen style '%s'" % value) self._isActive = active self.update() def onDeleted(self): self.setEndConnection(None) self.setStartSocket(None) def updateStartPos(self): start_socket = self.startSocket() self.setPos(start_socket.scenePos() + start_socket.boundingRect().center()) self.updatePath() def updateEndPos(self): self.updatePath() def updateVisibility(self): visible = self.startSocket().isVisible() and self.endSocket( ).isVisible() self.setVisible(visible) def setColor(self, color): if isinstance(color, tuple): color = QColor(*color) else: color = QColor(color) self._color = color def setStartSocket(self, socket): if self.startSocket(): self.startSocket().removeConnection(self) self._startSocket = None if socket is not None: self._startSocket = weakref.ref(socket) socket.addConnection(self) def setEndConnection(self, socket): if self.endSocket(): self.endSocket().removeConnection(self) self._endSocket = None if socket is not None: self._endSocket = weakref.ref(socket) socket.addConnection(self) def setCenterWidget(self, widget): self._centerWidget = widget def centerWidget(self): return self._centerWidget def findClosestSocket(self): closest_socket = None colliding_items = self.endSocket().collidingItems( Qt.IntersectsItemBoundingRect) for colliding_item in colliding_items: if isinstance(colliding_item, QtSocket): if colliding_item.isInput() and colliding_item.isVisible(): closest_socket = colliding_item break return closest_socket def updatePath(self): end_hook = self.endSocket() if end_hook is None: return self.prepareGeometryChange() end_pos = self.mapFromItem(end_hook, 0.0, 0.0) + end_hook.boundingRect().center() if self._curve: if not self._keyPoints: tangent_length = (abs(end_pos.x()) / 2.0) + (abs(end_pos.y()) / 4.0) tangent_length2 = tangent_length else: first_pos = self._keyPoints[0] tangent_length = (abs(first_pos.x()) / 2.0) + (abs(first_pos.y()) / 4.0) last_pos = self._keyPoints[-1] last_segment = end_pos - last_pos tangent_length2 = (abs(last_segment.x()) / 2.0) + (abs(last_segment.y()) / 4.0) spread = 60.0 / 180.0 * pi # Find connection index index, number_connections = self.startSocket().getIndexInfo(self) dev = (index - number_connections / 2.0 + 0.5) * min( spread, pi / (number_connections + 2)) tx = tangent_length * cos(dev) ty = tangent_length * sin(dev) start_tangent = QPointF(tx, ty) end_tangent = QPointF(end_pos.x() - tangent_length2, end_pos.y()) path = QPainterPath() path.cubicTo(start_tangent, end_tangent, end_pos) # Dot styles are used for relationships else: path = QPainterPath() path.lineTo(end_pos) stroke_width = self._pen.widthF() rect = path.boundingRect().adjusted(-stroke_width, -stroke_width, stroke_width, stroke_width) # draw widget center_widget = self._centerWidget if center_widget is not None: center = path.pointAtPercent(0.5) center_widget.onUpdated(center, text=str(self.index)) self._path = path self._rect = rect def onSocketHoverEnter(self): center_widget = self._centerWidget if center_widget is not None: center_widget.setVisible(True) def onSocketHoverExit(self): center_widget = self._centerWidget if center_widget is not None: center_widget.setVisible(False) def paint(self, painter, option, widget): self._pen.setColor(self._color) painter.setPen(self._pen) painter.drawPath(self._path)
def drawMapObject(self, painter, object, color): painter.save() bounds = object.bounds() rect = QRectF(bounds) painter.translate(rect.topLeft()) rect.moveTopLeft(QPointF(0, 0)) cell = object.cell() if (not cell.isEmpty()): CellRenderer(painter).render(cell, QPointF(), object.size(), CellRenderer.BottomLeft) if (self.testFlag(RenderFlag.ShowTileObjectOutlines)): tile = cell.tile imgSize = tile.size() tileOffset = tile.offset() rect = QRectF(QPointF(tileOffset.x(), tileOffset.y() - imgSize.height()), QSizeF(imgSize)) pen = QPen(Qt.SolidLine) pen.setCosmetic(True) painter.setPen(pen) painter.drawRect(rect) pen.setStyle(Qt.DotLine) pen.setColor(color) painter.setPen(pen) painter.drawRect(rect) else: lineWidth = self.objectLineWidth() scale = self.painterScale() if lineWidth == 0: x = 1 else: x = lineWidth shadowDist = x / scale shadowOffset = QPointF(shadowDist * 0.5, shadowDist * 0.5) linePen = QPen(color, lineWidth, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) #linePen.setCosmetic(True) shadowPen = QPen(linePen) shadowPen.setColor(Qt.black) brushColor = QColor(color) fillBrush = QBrush(brushColor) painter.setRenderHint(QPainter.Antialiasing) # Trying to draw an ellipse with 0-width is causing a hang in # CoreGraphics when drawing the path requested by the # QCoreGraphicsPaintEngine. Draw them as rectangle instead. shape = object.shape() if (shape == MapObject.Ellipse and ((rect.width() == 0.0) ^ (rect.height() == 0.0))): shape = MapObject.Rectangle x = shape if x==MapObject.Rectangle: if (rect.isNull()): rect = QRectF(QPointF(-10, -10), QSizeF(20, 20)) # Draw the shadow painter.setPen(shadowPen) painter.drawRect(rect.translated(shadowOffset)) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawRect(rect) elif x==MapObject.Polyline: screenPolygon = self.pixelToScreenCoords_(object.polygon()) thickShadowPen = QPen(shadowPen) thickLinePen = QPen(linePen) thickShadowPen.setWidthF(thickShadowPen.widthF() * 4) thickLinePen.setWidthF(thickLinePen.widthF() * 4) painter.setPen(shadowPen) painter.drawPolyline(screenPolygon.translated(shadowOffset)) painter.setPen(thickShadowPen) painter.drawPoint(screenPolygon.first() + shadowOffset) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawPolyline(screenPolygon) painter.setPen(thickLinePen) painter.drawPoint(screenPolygon.first()) elif x==MapObject.Polygon: screenPolygon = self.pixelToScreenCoords_(object.polygon()) thickShadowPen = QPen(shadowPen) thickLinePen = QPen(linePen) thickShadowPen.setWidthF(thickShadowPen.widthF() * 4) thickLinePen.setWidthF(thickLinePen.widthF() * 4) painter.setPen(shadowPen) painter.drawPolygon(screenPolygon.translated(shadowOffset)) painter.setPen(thickShadowPen) painter.drawPoint(screenPolygon.first() + shadowOffset) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawPolygon(screenPolygon) painter.setPen(thickLinePen) painter.drawPoint(screenPolygon.first()) elif x==MapObject.Ellipse: if (rect.isNull()): rect = QRectF(QPointF(-10, -10), QSizeF(20, 20)) # Draw the shadow painter.setPen(shadowPen) painter.drawEllipse(rect.translated(shadowOffset)) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawEllipse(rect) painter.restore()
def _map_physical_pen_to_scene(pen, scale_factor): scaled_pen = QPen(pen) scaled_pen.setWidthF(scale_factor * scaled_pen.widthF()) return scaled_pen
class PrimaryFlightDisplay(QWidget): ROLL_SCALE_RANGE = 60 ROLL_SCALE_TICKMARKLENGTH = 0.04 ROLL_SCALE_RADIUS = 0.42 ROLL_SCALE_MARKERWIDTH = 0.06 ROLL_SCALE_MARKERHEIGHT = 0.04 LINEWIDTH = 0.0036 SMALL_TEXT_SIZE = 0.03 MEDIUM_TEXT_SIZE = SMALL_TEXT_SIZE * 1.2 LARGE_TEXT_SIZE = MEDIUM_TEXT_SIZE * 1.2 PITCH_SCALE_RESOLUTION = 5 PITCH_SCALE_HALFRANGE = 15 PITCH_SCALE_MAJORWIDTH = 0.1 PITCH_SCALE_MINORWIDTH = 0.066 PITCH_SCALE_WIDTHREDUCTION_FROM = 30 PITCH_SCALE_WIDTHREDUCTION = 0.3 SHOW_ZERO_ON_SCALES = True CROSSTRACK_MAX = 1000 CROSSTRACK_RADIUS = 0.6 COMPASS_DISK_MAJORTICK = 10 COMPASS_DISK_ARROWTICK = 45 COMPASS_DISK_MAJORLINEWIDTH = 0.006 COMPASS_DISK_MINORLINEWIDTH = 0.004 COMPASS_DISK_RESOLUTION = 10 COMPASS_SEPARATE_DISK_RESOLUTION = 5 COMPASS_DISK_MARKERWIDTH = 0.2 COMPASS_DISK_MARKERHEIGHT = 0.133 UNKNOWN_BATTERY = -1 UNKNOWN_ATTITUDE = 0 UNKNOWN_ALTITUDE = -1000 UNKNOWN_SPEED = -1 UNKNOWN_COUNT = -1 UNKNOWN_GPSFIXTYPE = -1 TAPE_GAUGES_TICKWIDTH_MAJOR = 0.25 TAPE_GAUGES_TICKWIDTH_MINOR = 0.15 # The altitude difference between top and bottom of scale ALTIMETER_LINEAR_SPAN = 50 # every 5 meters there is a tick mark ALTIMETER_LINEAR_RESOLUTION = 5 # every 10 meters there is a number ALTIMETER_LINEAR_MAJOR_RESOLUTION = 10 ALTIMETER_VVI_SPAN = 5 ALTIMETER_VVI_WIDTH = 0.2 AIRSPEED_LINEAR_SPAN = 15 AIRSPEED_LINEAR_RESOLUTION = 1 AIRSPEED_LINEAR_MAJOR_RESOLUTION = 5 tickValues = [10, 20, 30, 45, 60] compassWindNames = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'] visibilityChanged = pyqtSignal(bool) def __init__(self, parent): super().__init__(parent) self.instrumentOpagueBackground = QBrush( QColor.fromHsvF(0, 0, 0.3, 1.0)) self.instrumentBackground = QBrush(QColor.fromHsvF(0, 0, 0.3, 0.3)) self.instrumentEdgePen = QPen(QColor.fromHsvF(0, 0, 0.65, 0.5)) self.font = QFont() self.lineWidth = 2 self.fineLineWidth = 1 self.navigationTargetBearing = PrimaryFlightDisplay.UNKNOWN_ATTITUDE self.navigationCrosstrackError = 0 self.primaryAltitude = PrimaryFlightDisplay.UNKNOWN_ALTITUDE self.GPSAltitude = PrimaryFlightDisplay.UNKNOWN_ALTITUDE self.verticalVelocity = PrimaryFlightDisplay.UNKNOWN_ALTITUDE self.primarySpeed = PrimaryFlightDisplay.UNKNOWN_SPEED self.groundspeed = PrimaryFlightDisplay.UNKNOWN_SPEED self.roll = 0.0 self.pitch = 0.0 self.yaw = 0.0 self.rollspeed = 0.0 self.pitchspeed = 0.0 self.yawspeed = 0.0 self.latitude = 0.0 self.longitude = 0.0 self.additionalParameters = {} self.param = UserData.getInstance().getUserDataEntry(UD_PFD_KEY, {}) self.isGPSSpeedPrimary = UserData.getParameterValue( self.param, UD_PFD_PRIMARY_SPEED_SOURCE_KEY) == 'GPS' self.isGPSAltitudePrimary = UserData.getParameterValue( self.param, UD_PFD_PRIMARY_ALTITUDE_SOURCE_KEY) == 'GPS' self.setMinimumSize(480, 320) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.smallTestSize = self.SMALL_TEXT_SIZE self.mediumTextSize = self.MEDIUM_TEXT_SIZE self.largeTextSize = self.LARGE_TEXT_SIZE self.uiTimer = QTimer(self) self.uiTimer.setInterval(40) self.uiTimer.timeout.connect(self.update) self.uas = None def setActiveUAS(self, uas): uas.updateAttitudeSignal.connect(self.updateAttitude) uas.updateBatterySignal.connect(self.updateBatteryStatus) uas.updateGlobalPositionSignal.connect(self.updateGlobalPosition) uas.updateAirSpeedSignal.connect(self.updatePrimarySpeed) uas.updateGroundSpeedSignal.connect(self.updateGPSSpeed) uas.updateGPSStatusSignal.connect(self.updateGPSReception) uas.updateRCStatusSignal.connect(self.updateRCStatus) self.uas = uas def updateRCStatus(self, sourceUAS, rcType, rssi, noise, errors): unused(sourceUAS, rcType) self.additionalParameters['rc_rssi'] = rssi self.additionalParameters['rc_noise'] = noise self.additionalParameters['rc_errors'] = errors def updateAttitude(self, sourceUAS, timestamp, roll, pitch, yaw): scale = 180 / math.pi self.pitch = self.pitch if math.isnan(pitch) else pitch * scale self.roll = self.roll if math.isnan(roll) else roll * scale self.yaw = self.yaw if math.isnan(yaw) else yaw * scale unused(sourceUAS, timestamp) def updateAttitudeSpeed(self, sourceUAS, timestamp, rollspeed, pitchspeed, yawspeed): scale = 180 / math.pi self.rollspeed = self.rollspeed if math.isnan( rollspeed) else rollspeed * scale self.pitchspeed = self.pitchspeed if math.isnan( pitchspeed) else pitchspeed * scale self.yawspeed = self.yawspeed if math.isnan( yawspeed) else yawspeed * scale unused(sourceUAS, timestamp) def updateGlobalPosition(self, sourceUAS, timestamp, latitude, longitude, altitude): self.latitude = self.latitude if math.isnan(latitude) else latitude self.longitude = self.longitude if math.isnan(longitude) else longitude self.GPSAltitude = self.GPSAltitude if math.isnan( altitude) else altitude unused(sourceUAS, timestamp) def updatePrimaryAltitude(self, sourceUAS, timestamp, altitude): self.primaryAltitude = self.primaryAltitude if math.isnan( altitude) else altitude unused(sourceUAS, timestamp) def updateGPSAltitude(self, sourceUAS, timestamp, altitude): self.GPSAltitude = self.GPSAltitude if math.isnan( altitude) else altitude unused(sourceUAS, timestamp) def updatePrimarySpeed(self, sourceUAS, timestamp, speed): self.primarySpeed = self.primarySpeed if math.isnan(speed) else speed unused(sourceUAS, timestamp) def updateBatteryStatus(self, sourceUAS, timestamp, voltage, current, remaining): self.additionalParameters['voltage'] = voltage self.additionalParameters['current'] = current self.additionalParameters['remaining'] = remaining unused(sourceUAS, timestamp) def updateGPSReception(self, sourceUAS, timestamp, fixType, hdop, vdop, satelliteCount, hacc, vacc, velacc, hdgacc): self.additionalParameters['gps_fix'] = fixType self.additionalParameters['gps_satellite'] = satelliteCount unused(sourceUAS, timestamp, hdop, vdop, hacc, vacc, velacc, hdgacc) def updateGPSSpeed(self, sourceUAS, timestamp, speed): self.groundspeed = self.groundspeed if math.isnan(speed) else speed unused(sourceUAS, timestamp) def paintEvent(self, event): unused(event) compassAIIntrusion = 0 compassHalfSpan = 180 painter = QPainter() painter.begin(self) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.HighQualityAntialiasing, True) tapeGaugeWidth = self.tapesGaugeWidthFor(self.width(), self.width()) aiheight = self.height() aiwidth = self.width() - tapeGaugeWidth * 2 if (aiheight > aiwidth): aiheight = aiwidth AIMainArea = QRectF(tapeGaugeWidth, 0, aiwidth, aiheight) AIPaintArea = QRectF(0, 0, self.width(), self.height()) velocityMeterArea = QRectF(0, 0, tapeGaugeWidth, aiheight) altimeterArea = QRectF(AIMainArea.right(), 0, tapeGaugeWidth, aiheight) # calc starts compassRelativeWidth = 0.75 compassBottomMargin = 0.78 compassSize = compassRelativeWidth * AIMainArea.width( ) # Diameter is this times the width. compassCenterY = AIMainArea.bottom() + compassSize / 4 if self.height( ) - compassCenterY > AIMainArea.width() / 2 * compassBottomMargin: compassCenterY = self.height( ) - AIMainArea.width() / 2 * compassBottomMargin compassCenterY = (compassCenterY * 2 + AIMainArea.bottom() + compassSize / 4) / 3 compassArea = QRectF( AIMainArea.x() + (1 - compassRelativeWidth) / 2 * AIMainArea.width(), compassCenterY - compassSize / 2, compassSize, compassSize) if self.height() - compassCenterY < compassSize / 2: compassHalfSpan = math.acos( (compassCenterY - self.height()) * 2 / compassSize) * 180 / math.pi + self.COMPASS_DISK_RESOLUTION if compassHalfSpan > 180: compassHalfSpan = 180 compassAIIntrusion = compassSize / 2 + AIMainArea.bottom( ) - compassCenterY if compassAIIntrusion < 0: compassAIIntrusion = 0 #calc ends hadClip = painter.hasClipping() painter.setClipping(True) painter.setClipRect(AIPaintArea) self.drawAIGlobalFeatures(painter, AIMainArea, AIPaintArea) self.drawAIAttitudeScales(painter, AIMainArea, compassAIIntrusion) self.drawAIAirframeFixedFeatures(painter, AIMainArea) self.drawAICompassDisk(painter, compassArea, compassHalfSpan) painter.setClipping(hadClip) if self.isGPSAltitudePrimary: self.drawAltimeter(painter, altimeterArea, self.GPSAltitude, self.primaryAltitude, self.verticalVelocity) else: self.drawAltimeter(painter, altimeterArea, self.primaryAltitude, self.GPSAltitude, self.verticalVelocity) if self.isGPSSpeedPrimary: self.drawVelocityMeter(painter, velocityMeterArea, self.groundspeed, self.primarySpeed) else: self.drawVelocityMeter(painter, velocityMeterArea, self.primarySpeed, self.groundspeed) painter.end() def showEvent(self, event): super().showEvent(event) self.uiTimer.start() self.visibilityChanged.emit(True) def hideEvent(self, event): self.uiTimer.stop() super().hideEvent(event) self.visibilityChanged.emit(False) def resizeEvent(self, e): super().resizeEvent(e) size = e.size().width() self.lineWidth = self.constrain(size * self.LINEWIDTH, 1, 6) self.fineLineWidth = self.constrain(size * self.LINEWIDTH * 2 / 3, 1, 2) self.instrumentEdgePen.setWidthF(self.fineLineWidth) self.smallTestSize = size * self.SMALL_TEXT_SIZE self.mediumTextSize = size * self.MEDIUM_TEXT_SIZE self.largeTextSize = size * self.LARGE_TEXT_SIZE def drawTextCenter(self, painter, text, pixelSize, x, y): self.font.setPixelSize(pixelSize) painter.setFont(self.font) metrics = QFontMetrics(self.font) bounds = metrics.boundingRect(text) painter.drawText(x - bounds.width() / 2, y - bounds.height() / 2, bounds.width(), bounds.height(), Qt.AlignCenter | Qt.TextDontClip, text) def drawTextLeftCenter(self, painter, text, pixelSize, x, y): self.font.setPixelSize(pixelSize) painter.setFont(self.font) metrics = QFontMetrics(self.font) bounds = metrics.boundingRect(text) painter.drawText(x, y - bounds.height() / 2, bounds.width(), bounds.height(), Qt.AlignLeft | Qt.TextDontClip, text) def drawTextRightCenter(self, painter, text, pixelSize, x, y): self.font.setPixelSize(pixelSize) painter.setFont(self.font) metrics = QFontMetrics(self.font) bounds = metrics.boundingRect(text) painter.drawText(x - bounds.width(), y - bounds.height() / 2, bounds.width(), bounds.height(), Qt.AlignRight | Qt.TextDontClip, text) def drawTextCenterTop(self, painter, text, pixelSize, x, y): self.font.setPixelSize(pixelSize) painter.setFont(self.font) metrics = QFontMetrics(self.font) bounds = metrics.boundingRect(text) painter.drawText(x - bounds.width() / 2, y + bounds.height(), bounds.width(), bounds.height(), Qt.AlignCenter | Qt.TextDontClip, text) def drawTextCenterBottom(self, painter, text, pixelSize, x, y): self.font.setPixelSize(pixelSize) painter.setFont(self.font) metrics = QFontMetrics(self.font) bounds = metrics.boundingRect(text) painter.drawText(x - bounds.width() / 2, y, bounds.width(), bounds.height(), Qt.AlignCenter, text) def drawInstrumentBackground(self, painter, edge): painter.setPen(self.instrumentEdgePen) painter.drawRect(edge) def fillInstrumentBackground(self, painter, edge): painter.setPen(self.instrumentEdgePen) painter.setBrush(self.instrumentBackground) painter.drawRect(edge) painter.setBrush(Qt.NoBrush) def fillInstrumentOpagueBackground(self, painter, edge): painter.setPen(self.instrumentEdgePen) painter.setBrush(self.instrumentOpagueBackground) painter.drawRect(edge) painter.setBrush(Qt.NoBrush) def constrain(self, value, mn, mx): if value < mn: value = mn elif value > mx: value = mx return value def pitchAngleToTranslation(self, viewHeight, pitch): return pitch * viewHeight / 65.0 #PITCHTRANSLATION def tapesGaugeWidthFor(self, containerWidth, preferredAIWidth): result = (containerWidth - preferredAIWidth) / 2.0 minimum = containerWidth / 5.5 if result < minimum: result = minimum return result def min4(self, a, b, c, d): if b < a: a = b if c < a: a = c if d < a: a = d return a def max4(self, a, b, c, d): if b > a: a = b if c > a: a = c if d > a: a = d return a def drawAIAttitudeScales(self, painter, area, intrusion): # To save computations, we do these transformations once for both scales: painter.resetTransform() painter.translate(area.center()) painter.rotate(-self.roll) saved = painter.transform() self.drawRollScale(painter, area, True, True) painter.setTransform(saved) self.drawPitchScale(painter, area, intrusion, True, True) 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 drawRollScale(self, painter, area, drawTicks, drawNumbers): w = area.width() if w < area.height(): w = area.height() pen = QPen() pen.setWidthF(self.lineWidth) pen.setColor(Qt.white) painter.setPen(pen) # We should really do these transforms but they are assumed done by caller. # painter.resetTransform() # painter.translate(area.center()) # painter.rotate(roll) _size = w * self.ROLL_SCALE_RADIUS * 2 arcArea = QRectF(-_size / 2, -_size / 2, _size, _size) painter.drawArc(arcArea, (90 - self.ROLL_SCALE_RANGE) * 16, self.ROLL_SCALE_RANGE * 2 * 16) # painter.drawEllipse(QPoint(0,0),200,200) if drawTicks: length = len(self.tickValues) previousRotation = 0 i = 0 while i < length * 2 + 1: degrees = 0 if i > length: degrees = -self.tickValues[i - length - 1] elif i < length: degrees = self.tickValues[i] #degrees = 180 - degrees painter.rotate(degrees - previousRotation) previousRotation = degrees start = QPointF(0, -_size / 2) end = QPointF( 0, -(1.0 + self.ROLL_SCALE_TICKMARKLENGTH) * _size / 2) painter.drawLine(start, end) #QString s_number # = QString("%d").arg(degrees); #if (SHOW_ZERO_ON_SCALES || degrees) # s_number.sprintf("%d", abs(degrees)); if drawNumbers: self.drawTextCenterBottom( painter, '{0}'.format(abs(degrees)), self.mediumTextSize, 0, -(self.ROLL_SCALE_RADIUS + self.ROLL_SCALE_TICKMARKLENGTH * 1.7) * w) i = i + 1 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 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 drawAICompassDisk(self, painter, area, halfspan): start = self.yaw - halfspan end = self.yaw + halfspan firstTick = math.ceil( start / self.COMPASS_DISK_RESOLUTION) * self.COMPASS_DISK_RESOLUTION lastTick = math.floor( end / self.COMPASS_DISK_RESOLUTION) * self.COMPASS_DISK_RESOLUTION radius = area.width() / 2 innerRadius = radius * 0.96 painter.resetTransform() painter.setBrush(self.instrumentBackground) painter.setPen(self.instrumentEdgePen) painter.drawEllipse(area) painter.setBrush(Qt.NoBrush) scalePen = QPen(Qt.black) scalePen.setWidthF(self.fineLineWidth) tickYaw = firstTick while tickYaw <= lastTick: displayTick = tickYaw if displayTick < 0: displayTick += 360 elif displayTick >= 360: displayTick -= 360 # yaw is in center. off = tickYaw - self.yaw # wrap that to ]-180..180] if off <= -180: off += 360 elif off > 180: off -= 360 painter.translate(area.center()) painter.rotate(off) drewArrow = False isMajor = displayTick % self.COMPASS_DISK_MAJORTICK == 0 if displayTick == 30 or displayTick == 60 or \ displayTick ==120 or displayTick ==150 or \ displayTick ==210 or displayTick ==240 or \ displayTick ==300 or displayTick ==330: # draw a number painter.setPen(scalePen) self.drawTextCenter(painter, '{0}'.format(int(displayTick / 10)), self.smallTestSize, 0, -innerRadius * 0.75) else: if displayTick % self.COMPASS_DISK_ARROWTICK == 0: if displayTick != 0: markerPath = QPainterPath( QPointF( 0, -innerRadius * (1 - self.COMPASS_DISK_MARKERHEIGHT / 2))) markerPath.lineTo( innerRadius * self.COMPASS_DISK_MARKERWIDTH / 4, -innerRadius) markerPath.lineTo( -innerRadius * self.COMPASS_DISK_MARKERWIDTH / 4, -innerRadius) markerPath.closeSubpath() painter.setPen(scalePen) painter.setBrush(Qt.SolidPattern) painter.drawPath(markerPath) painter.setBrush(Qt.NoBrush) drewArrow = True if displayTick % 90 == 0: # Also draw a label name = self.compassWindNames[qRound(displayTick / 45)] painter.setPen(scalePen) self.drawTextCenter(painter, name, self.mediumTextSize, 0, -innerRadius * 0.75) # draw the scale lines. If an arrow was drawn, stay off from it. if drewArrow: p_start = QPoint(0, -innerRadius * 0.94) else: p_start = QPoint(0, -innerRadius) if isMajor: p_end = QPoint(0, -innerRadius * 0.86) else: p_end = QPoint(0, -innerRadius * 0.90) painter.setPen(scalePen) painter.drawLine(p_start, p_end) painter.resetTransform() tickYaw += self.COMPASS_DISK_RESOLUTION painter.setPen(scalePen) painter.translate(area.center()) markerPath = QPainterPath(QPointF(0, -radius - 2)) markerPath.lineTo( radius * self.COMPASS_DISK_MARKERWIDTH / 2, -radius - radius * self.COMPASS_DISK_MARKERHEIGHT - 2) markerPath.lineTo( -radius * self.COMPASS_DISK_MARKERWIDTH / 2, -radius - radius * self.COMPASS_DISK_MARKERHEIGHT - 2) markerPath.closeSubpath() painter.drawPath(markerPath) digitalCompassYCenter = -radius * 0.52 digitalCompassHeight = radius * 0.28 digitalCompassBottom = QPointF( 0, digitalCompassYCenter + digitalCompassHeight) digitalCompassAbsoluteBottom = painter.transform().map( digitalCompassBottom) digitalCompassUpshift = 0 if digitalCompassAbsoluteBottom.y() > self.height(): digitalCompassUpshift = digitalCompassAbsoluteBottom.y( ) - self.height() digitalCompassRect = QRectF(-radius / 3, -radius * 0.52 - digitalCompassUpshift, radius * 2 / 3, radius * 0.28) painter.setPen(self.instrumentEdgePen) painter.drawRoundedRect(digitalCompassRect, self.instrumentEdgePen.widthF() * 2 / 3, self.instrumentEdgePen.widthF() * 2 / 3) # final safeguard for really stupid systems digitalCompassValue = qRound(self.yaw) % 360 pen = QPen() pen.setWidthF(self.lineWidth) pen.setColor(Qt.white) painter.setPen(pen) self.drawTextCenter(painter, '%03d' % digitalCompassValue, self.largeTextSize, 0, -radius * 0.38 - digitalCompassUpshift) # The CDI if self.shouldDisplayNavigationData( ) and self.navigationTargetBearing != self.UNKNOWN_ATTITUDE and not math.isinf( self.navigationCrosstrackError): painter.resetTransform() painter.translate(area.center()) # TODO : Sign might be wrong? # TODO : The case where error exceeds max. Truncate to max. and make that visible somehow. # bool errorBeyondRadius = false if abs(self.navigationCrosstrackError) > self.CROSSTRACK_MAX: #errorBeyondRadius = true if self.navigationCrosstrackError > 0: self.navigationCrosstrackError = self.CROSSTRACK_MAX else: self.navigationCrosstrackError = -self.CROSSTRACK_MAX r = radius * self.CROSSTRACK_RADIUS x = self.navigationCrosstrackError / self.CROSSTRACK_MAX * r y = math.sqrt(r * r - x * x) # the positive y, there is also a negative. sillyHeading = 0 angle = sillyHeading - self.navigationTargetBearing # TODO: sign. painter.rotate(-angle) pen = QPen() pen.setWidthF(self.lineWidth) pen.setColor(Qt.black) painter.setPen(pen) painter.drawLine(QPointF(x, y), QPointF(x, -y)) def drawAltimeter(self, painter, area, primaryAltitude, secondaryAltitude, vv): unused(secondaryAltitude) painter.resetTransform() self.fillInstrumentBackground(painter, area) pen = QPen() pen.setWidthF(self.lineWidth) pen.setColor(Qt.white) painter.setPen(pen) h = area.height() w = area.width() #float secondaryAltitudeBoxHeight = mediumTextSize * 2; # The height where we being with new tickmarks. effectiveHalfHeight = h * 0.45 # not yet implemented: Display of secondary altitude. # if (isAirplane()) # effectiveHalfHeight-= secondaryAltitudeBoxHeight; markerHalfHeight = self.mediumTextSize * 0.8 leftEdge = self.instrumentEdgePen.widthF() * 2 rightEdge = w - leftEdge tickmarkLeft = leftEdge tickmarkRightMajor = tickmarkLeft + self.TAPE_GAUGES_TICKWIDTH_MAJOR * w tickmarkRightMinor = tickmarkLeft + self.TAPE_GAUGES_TICKWIDTH_MINOR * w numbersLeft = 0.42 * w markerTip = (tickmarkLeft * 2 + tickmarkRightMajor) / 3 scaleCenterAltitude = 0 if primaryAltitude == self.UNKNOWN_ALTITUDE else primaryAltitude # altitude scale start = scaleCenterAltitude - self.ALTIMETER_LINEAR_SPAN / 2 end = scaleCenterAltitude + self.ALTIMETER_LINEAR_SPAN / 2 firstTick = math.ceil(start / self.ALTIMETER_LINEAR_RESOLUTION ) * self.ALTIMETER_LINEAR_RESOLUTION lastTick = math.floor(end / self.ALTIMETER_LINEAR_RESOLUTION ) * self.ALTIMETER_LINEAR_RESOLUTION tickAlt = firstTick while tickAlt <= lastTick: y = (tickAlt - scaleCenterAltitude) * effectiveHalfHeight / ( self.ALTIMETER_LINEAR_SPAN / 2) isMajor = tickAlt % self.ALTIMETER_LINEAR_MAJOR_RESOLUTION == 0 painter.resetTransform() painter.translate(area.left(), area.center().y() - y) pen.setColor(Qt.red if tickAlt < 0 else Qt.white) painter.setPen(pen) if isMajor: painter.drawLine(tickmarkLeft, 0, tickmarkRightMajor, 0) self.drawTextLeftCenter(painter, '{0}'.format(abs(tickAlt)), self.mediumTextSize, numbersLeft, 0) else: painter.drawLine(tickmarkLeft, 0, tickmarkRightMinor, 0) tickAlt += self.ALTIMETER_LINEAR_RESOLUTION markerPath = QPainterPath(QPoint(markerTip, 0)) markerPath.lineTo(markerTip + markerHalfHeight, markerHalfHeight) markerPath.lineTo(rightEdge, markerHalfHeight) markerPath.lineTo(rightEdge, -markerHalfHeight) markerPath.lineTo(markerTip + markerHalfHeight, -markerHalfHeight) markerPath.closeSubpath() painter.resetTransform() painter.translate(area.left(), area.center().y()) pen.setWidthF(self.lineWidth) pen.setColor(Qt.white) painter.setPen(pen) painter.setBrush(Qt.SolidPattern) painter.drawPath(markerPath) painter.setBrush(Qt.NoBrush) pen.setColor(Qt.white) painter.setPen(pen) xCenter = (markerTip + rightEdge) / 2 alttxt = '---' if primaryAltitude == self.UNKNOWN_ALTITUDE else '%3.0f' % primaryAltitude self.drawTextCenter(painter, alttxt, self.mediumTextSize, xCenter, 0) if vv == self.UNKNOWN_ALTITUDE: return vvPixHeight = -vv / self.ALTIMETER_VVI_SPAN * effectiveHalfHeight if abs(vvPixHeight) < markerHalfHeight: return # hidden behind marker. vvSign = -1 if vvPixHeight > 0: vvSign = 1 # QRectF vvRect(rightEdge - w*ALTIMETER_VVI_WIDTH, markerHalfHeight*vvSign, w*ALTIMETER_VVI_WIDTH, abs(vvPixHeight)*vvSign); vvArrowBegin = QPointF(rightEdge - w * self.ALTIMETER_VVI_WIDTH / 2, markerHalfHeight * vvSign) vvArrowEnd = QPointF(rightEdge - w * self.ALTIMETER_VVI_WIDTH / 2, vvPixHeight) painter.drawLine(vvArrowBegin, vvArrowEnd) # Yeah this is a repitition of above code but we are goigd to trash it all anyway, so no fix. vvArowHeadSize = abs(vvPixHeight - markerHalfHeight * vvSign) if vvArowHeadSize > w * self.ALTIMETER_VVI_WIDTH / 3: vvArowHeadSize = w * self.ALTIMETER_VVI_WIDTH / 3 xcenter = rightEdge - w * self.ALTIMETER_VVI_WIDTH / 2 vvArrowHead = QPointF(xcenter + vvArowHeadSize, vvPixHeight - vvSign * vvArowHeadSize) painter.drawLine(vvArrowHead, vvArrowEnd) vvArrowHead = QPointF(xcenter - vvArowHeadSize, vvPixHeight - vvSign * vvArowHeadSize) painter.drawLine(vvArrowHead, vvArrowEnd) def drawVelocityMeter(self, painter, area, speed, secondarySpeed): unused(secondarySpeed) painter.resetTransform() self.fillInstrumentBackground(painter, area) pen = QPen() pen.setWidthF(self.lineWidth) h = area.height() w = area.width() effectiveHalfHeight = h * 0.45 markerHalfHeight = self.mediumTextSize leftEdge = self.instrumentEdgePen.widthF() * 2 tickmarkRight = w - leftEdge tickmarkLeftMajor = tickmarkRight - w * self.TAPE_GAUGES_TICKWIDTH_MAJOR tickmarkLeftMinor = tickmarkRight - w * self.TAPE_GAUGES_TICKWIDTH_MINOR numbersRight = 0.42 * w markerTip = (tickmarkLeftMajor + tickmarkRight * 2) / 3 # Select between air and ground speed: centerScaleSpeed = 0 if speed == self.UNKNOWN_SPEED else speed start = centerScaleSpeed - self.AIRSPEED_LINEAR_SPAN / 2 end = centerScaleSpeed + self.AIRSPEED_LINEAR_SPAN / 2 firstTick = math.ceil( start / self.AIRSPEED_LINEAR_RESOLUTION) * self.AIRSPEED_LINEAR_RESOLUTION lastTick = math.floor( end / self.AIRSPEED_LINEAR_RESOLUTION) * self.AIRSPEED_LINEAR_RESOLUTION tickSpeed = firstTick while tickSpeed <= lastTick: if tickSpeed < 0: pen.setColor(Qt.red) else: pen.setColor(Qt.white) painter.setPen(pen) y = (tickSpeed - centerScaleSpeed) * effectiveHalfHeight / ( self.AIRSPEED_LINEAR_SPAN / 2) hasText = tickSpeed % self.AIRSPEED_LINEAR_MAJOR_RESOLUTION == 0 painter.resetTransform() painter.translate(area.left(), area.center().y() - y) if hasText: painter.drawLine(tickmarkLeftMajor, 0, tickmarkRight, 0) self.drawTextRightCenter(painter, '{0}'.format(abs(tickSpeed)), self.mediumTextSize, numbersRight, 0) else: painter.drawLine(tickmarkLeftMinor, 0, tickmarkRight, 0) tickSpeed += self.AIRSPEED_LINEAR_RESOLUTION markerPath = QPainterPath(QPoint(markerTip, 0)) markerPath.lineTo(markerTip - markerHalfHeight, markerHalfHeight) markerPath.lineTo(leftEdge, markerHalfHeight) markerPath.lineTo(leftEdge, -markerHalfHeight) markerPath.lineTo(markerTip - markerHalfHeight, -markerHalfHeight) markerPath.closeSubpath() painter.resetTransform() painter.translate(area.left(), area.center().y()) pen.setWidthF(self.lineWidth) pen.setColor(Qt.white) painter.setPen(pen) painter.setBrush(Qt.SolidPattern) painter.drawPath(markerPath) painter.setBrush(Qt.NoBrush) pen.setColor(Qt.white) painter.setPen(pen) xCenter = (markerTip + leftEdge) / 2 spdtxt = '---' if speed == self.UNKNOWN_SPEED else '%3.1f' % speed self.drawTextCenter(painter, spdtxt, self.mediumTextSize, xCenter, 0) def shouldDisplayNavigationData(self): return True def __getAdditionalParameter(self, param, defaultValue=0): if param in self.additionalParameters: return self.additionalParameters[param] return defaultValue
class DrawingBox(QWidget): """ Allows to draw an image of a letter. After drawing, sends signal containing image rescaled to the input size of the neural network. """ # signal emitted when user releases mouse after drawing newImage = pyqtSignal(QImage) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.imageSize = GUI_IMAGE_SIZE self.setFixedSize(self.imageSize) self.inverted = False self.pen = QPen(Qt.white if self.inverted else Qt.black, int(np.mean(PEN_WIDTH_RANGE)), Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self.image = QImage(self.imageSize, QImage.Format_Grayscale8) self.clearImage() self.last_pos = None self.drawing = False @pyqtSlot(int) def setPenWidth(self, pen_width): self.pen.setWidth(pen_width) @pyqtSlot() def invert(self): self.inverted = not self.inverted self.pen.setColor(Qt.white if self.inverted else Qt.black) self.image.invertPixels() self.update() self.pushImage() def pushImage(self): image = self.image.scaled(NET_IMAGE_SIZE, Qt.IgnoreAspectRatio, Qt.SmoothTransformation) self.newImage.emit(image) @pyqtSlot() def clearImage(self): self.image.fill(0 if self.inverted else 255) self.update() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.last_pos = event.pos() self.drawing = True def mouseMoveEvent(self, event): dist = (event.pos() - self.last_pos).manhattanLength() if (event.buttons() & Qt.LeftButton) and self.drawing and dist > 3: self.drawLineTo(event.pos()) def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton and self.drawing: self.drawLineTo(event.pos()) self.drawing = False self.pushImage() def paintEvent(self, event): "Paints changes in the image on the widget." painter = QPainter(self) dirty_rect = event.rect() painter.drawImage(dirty_rect, self.image, dirty_rect) def drawLineTo(self, end_pos): "Draws line on the image and signals which part is dirty." painter = QPainter(self.image) painter.setPen(self.pen) painter.drawLine(self.last_pos, end_pos) radius = int((self.pen.widthF() / 2) + 2) self.update(QRect(self.last_pos, end_pos).normalized().adjusted( -radius, -radius, radius, radius)) self.last_pos = end_pos
class DesignItem(QGraphicsItem): positionChanged = pyqtSignal(int, int) def __init__(self, scene, is_scene_rect=False): QGraphicsItem.__init__(self) self.scene = scene self.is_scene_rect = is_scene_rect self.id = "" self.xscale = 1 self.yscale = 1 self.scaleX = 1.0 self.scaleY = 1.0 self.pen = QPen() self.brush = QBrush() self.hasHandles = False self.handles = [None, None, None, None, None, None, None, None] self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) def setId(self, id): self.id = id def setBrush(self, brush): self.brush = brush def setPen(self, pen): self.pen = pen def setRect(self, x, y, w, h): self.rect = QRectF(x, y, w, h) def setWidth(self, value): self.rect.setWidth(value) def setHeight(self, value): self.rect.setHeight(value) def width(self): return self.rect.width() def height(self): return self.rect.height() def boundingRect(self): return self.rect def isSceneRect(self): return self.is_scene_rect def drawHighlightSelected(self, painter, option): itemPenWidth = self.pen.widthF() pad = itemPenWidth / 2 penWidth = 0 fgcolor = option.palette.windowText().color() if fgcolor.red() > 127: r = 0 else: r = 255 if fgcolor.green() > 127: g = 0 else: g = 255 if fgcolor.blue() > 127: b = 0 else: b = 255 bgcolor = QColor(r, g, b) painter.setOpacity(1.0) painter.setPen(QPen(bgcolor, penWidth, Qt.SolidLine)) painter.setBrush(Qt.NoBrush) painter.drawRect(self.boundingRect().adjusted(pad, pad, -pad, -pad)) painter.setPen(QPen(option.palette.windowText(), 0, Qt.DashLine)) painter.setBrush(Qt.NoBrush) painter.drawRect(self.boundingRect().adjusted(pad, pad, -pad, -pad)) def scaleObjects(self): pass def setHandlePositions(self): if not self.hasHandles: return halfwidth = self.handles[0].width / 2.0 self.handles[0].setPos(-halfwidth, -halfwidth) self.handles[1].setPos(self.rect.width() - halfwidth, -halfwidth) self.handles[2].setPos(self.rect.width() - halfwidth, self.rect.height() - halfwidth) self.handles[3].setPos(-halfwidth, self.rect.height() - halfwidth) self.handles[4].setPos(self.rect.width() / 2 - halfwidth, -halfwidth) self.handles[5].setPos(self.rect.width() - halfwidth, self.rect.height() / 2 - halfwidth) self.handles[6].setPos(self.rect.width() / 2 - halfwidth, self.rect.height() - halfwidth) self.handles[7].setPos(-halfwidth, self.rect.height() / 2 - halfwidth) self.scene.update(self.x() - halfwidth - 5, self.y() - halfwidth - 5, self.x() + self.rect.width() + halfwidth * 2 + 5, self.y() + self.rect.height() + halfwidth * 2 + 5) def sceneEventFilter(self, watched, event): if isinstance(watched, ItemHandle): handle = watched else: return False if isinstance(event, QGraphicsSceneMouseEvent): mevent = event else: return False if mevent.type() == QEvent.GraphicsSceneMousePress: self.oldx = self.pos().x() self.oldy = self.pos().y() self.oldwidth = self.rect.width() self.oldheight = self.rect.height() handle.setMouseState(ItemHandle.MOUSE_DOWN) handle.mouseDownX = mevent.pos().x() handle.mouseDownY = mevent.pos().y() elif mevent.type() == QEvent.GraphicsSceneMouseRelease: if self.oldx != self.pos().x() and self.oldy != self.pos().y( ) and self.oldwidth != self.rect.width( ) and self.oldheight != self.rect.height(): undostack = self.scene.undostack cmd = ScaleItemCommand(self.pos().x(), self.pos().y(), self.rect.width(), self.rect.height(), self.oldx, self.oldy, self.oldwidth, self.oldheight, self.scene, self) undoStack.push(cmd) handle.setMouseState(ItemHandle.MOUSE_RELEASED) elif mevent.type() == QEvent.GraphicsSceneMouseMove: handle.setMouseState(ItemHandle.MOUSE_MOVING) else: return False if handle.getMouseState() == ItemHandle.MOUSE_MOVING: x = mevent.pos().x() y = mevent.pos().y() XaxisSign = 0 YaxisSign = 0 if handle.getCorner() == 0: XaxisSign = +1 YaxisSign = +1 elif handle.getCorner() == 1: XaxisSign = -1 YaxisSign = +1 elif handle.getCorner() == 2: XaxisSign = -1 YaxisSign = -1 elif handle.getCorner() == 3: XaxisSign = +1 YaxisSign = -1 elif handle.getCorner() == 4: YaxisSign = +1 elif handle.getCorner() == 5: XaxisSign = -1 elif handle.getCorner() == 6: YaxisSign = -1 elif handle.getCorner() == 7: XaxisSign = +1 xMoved = handle.mouseDownX - x yMoved = handle.mouseDownY - y newWidth = self.rect.width() + (XaxisSign * xMoved) if newWidth < 20: newWidth = 20 newHeight = self.rect.height() + (YaxisSign * yMoved) if newHeight < 20: newHeight = 20 deltaWidth = newWidth - self.rect.width() deltaHeight = newHeight - self.rect.height() shiftPressed = False controlPressed = False modifiers = QGuiApplication.keyboardModifiers() if modifiers == Qt.ShiftModifier: shiftPressed = True elif modifiers == Qt.ControlModifier: controlPressed = True elif modifiers == (Qt.ControlModifier | Qt.ShiftModifier): shiftPressed = True controlPressed = True if controlPressed: # keep ratio ratio = self.rect.width() / self.rect.height() if handle.getCorner() < 4: # corners if newWidth > newHeight: deltaWidth = int(deltaHeight * ratio) else: deltaHeight = int(deltaWidth / ratio) else: if handle.getCorner() == 4 or handle.getCorner( ) == 6: # top | bottom deltaWidth = deltaHeight * ratio else: # left | right deltaHeight = deltaWidth / ratio self.setRect(0, 0, self.rect.width() + deltaWidth, self.rect.height() + deltaHeight) self.scaleObjects() deltaWidth *= (-1) deltaHeight *= (-1) newXpos = self.pos().x() newYpos = self.pos().y() if handle.getCorner() == 0: # top left if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 else: newXpos = self.pos().x() + deltaWidth newYpos = self.pos().y() + deltaHeight elif handle.getCorner() == 1: # top right if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 else: newYpos = self.pos().y() + deltaHeight elif handle.getCorner() == 2: # bottom right if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 elif handle.getCorner() == 3: # bottom left if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 else: newXpos = self.pos().x() + deltaWidth elif handle.getCorner() == 4: # top if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 elif controlPressed: newYpos = self.pos().y() + deltaHeight newXpos = self.pos().x() + deltaWidth / 2 else: newYpos = self.pos().y() + deltaHeight elif handle.getCorner() == 5: # right if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 elif controlPressed: newYpos = self.pos().y() + deltaHeight / 2 elif handle.getCorner() == 6: # bottom if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 elif controlPressed: newXpos = self.pos().x() + deltaWidth / 2 elif handle.getCorner() == 7: # left if shiftPressed: newXpos = self.pos().x() + deltaWidth / 2 newYpos = self.pos().y() + deltaHeight / 2 elif controlPressed: newXpos = self.pos().x() + deltaWidth newYpos = self.pos().y() + deltaHeight / 2 else: newXpos = self.pos().x() + deltaWidth if newXpos != self.pos().x() or newYpos != self.pos().y(): self.setPos(newXpos, newYpos) self.posChanged(newXpos, newYpos) self.setHandlePositions() self.update() return True def itemChange(self, change, value): if change == QGraphicsItem.ItemSelectedChange: if value: if not self.hasHandles: for i in range(8): self.handles[i] = ItemHandle(self, i, self.scene.scaling) self.handles[i].installSceneEventFilter(self) self.hasHandles = True self.setHandlePositions() else: for i in range(8): self.scene.removeItem(self.handles[i]) self.handles[i] = None self.hasHandles = False elif change == QGraphicsItem.ItemPositionHasChanged: if self.isSelected(): newPos = value self.posChanged(newPos.x(), newPos.y()) self.setHandlePositions() return super().itemChange(change, value) def posChanged(self, x, y): pass #self.positionChanged.emit(x, y) def contextMenuEvent(self, event): if not self.is_scene_rect: self.scene.clearSelection() self.setSelected(True) #self.contextMenu.exec(event.screenPos()) delAct = QAction("Delete") delAct.setShortcut("Delete") delAct.triggered.connect(self.deleteItemAction) bringToFrontAct = QAction("Bring to front") bringToFrontAct.triggered.connect(self.bringToFrontAction) sendToBackAct = QAction("Send to back") sendToBackAct.triggered.connect(self.sendToBackAction) raiseAct = QAction("Raise") raiseAct.triggered.connect(self.raiseAction) lowerAct = QAction("Lower") lowerAct.triggered.connect(self.lowerAction) contextMenu = QMenu() contextMenu.addAction(delAct) contextMenu.addSeparator() contextMenu.addAction(bringToFrontAct) contextMenu.addAction(raiseAct) contextMenu.addAction(lowerAct) contextMenu.addAction(sendToBackAct) contextMenu.exec(event.screenPos()) def deleteItemAction(self): self.scene.deleteItem(self) def lowerAction(self): cmd = LowerItemCommand(self) self.scene.undostack.push(cmd) def raiseAction(self): cmd = RaiseItemCommand(self) self.scene.undostack.push(cmd) def sendToBackAction(self): cmd = SendItemToBackCommand(self) self.scene.undostack.push(cmd) def bringToFrontAction(self): cmd = BringItemToFrontCommand(self) self.scene.undostack.push(cmd) def lowerItem(self): pos = self.scene.items().index(self) for i in range(pos + 1, len(self.scene.items())): item = self.scene.items()[i] if isinstance(item, DesignItem) and not item.is_scene_rect: self.stackBefore(item) break # trick to repaint item self.setSelected(False) self.setSelected(True) def raiseItem(self): pos = self.scene.items().index(self) for i in range(pos - 1, -1, -1): item = self.scene.items()[i] if isinstance(item, DesignItem): item.stackBefore(self) break # trick to repaint item self.setSelected(False) self.setSelected(True) def bringToFront(self): pos = self.scene.items().index(self) for i in range(pos - 1, -1, -1): item = self.scene.items()[i] if isinstance(item, DesignItem): item.stackBefore(self) # trick to repaint item self.setSelected(False) self.setSelected(True) def sendToBack(self): pos = self.scene.items().index(self) for i in range(pos + 1, len(self.scene.items())): item = self.scene.items()[i] if isinstance(item, DesignItem) and not item.is_scene_rect: self.stackBefore(item) # trick to repaint item self.setSelected(False) self.setSelected(True)
class GraphicsPathObject(QGraphicsObject): """A QGraphicsObject subclass implementing an interface similar to QGraphicsPathItem, and also adding a positionChanged() signal """ positionChanged = Signal([], ["QPointF"]) def __init__(self, parent=None, **kwargs): QGraphicsObject.__init__(self, parent, **kwargs) self.setFlag(QGraphicsObject.ItemSendsGeometryChanges) self.__path = QPainterPath() self.__brush = QBrush(Qt.NoBrush) self.__pen = QPen() self.__boundingRect = None def setPath(self, path): """Set the items `path` (:class:`QPainterPath`). """ if not isinstance(path, QPainterPath): raise TypeError("%r, 'QPainterPath' expected" % type(path)) if self.__path != path: self.prepareGeometryChange() # Need to store a copy of object so the shape can't be mutated # without properly updating the geometry. self.__path = QPainterPath(path) self.__boundingRect = None self.update() def path(self): """Return the items path. """ return QPainterPath(self.__path) def setBrush(self, brush): """Set the items `brush` (:class:`QBrush`) """ if not isinstance(brush, QBrush): brush = QBrush(brush) if self.__brush != brush: self.__brush = QBrush(brush) self.update() def brush(self): """Return the items brush. """ return QBrush(self.__brush) def setPen(self, pen): """Set the items outline `pen` (:class:`QPen`). """ if not isinstance(pen, QPen): pen = QPen(pen) if self.__pen != pen: self.prepareGeometryChange() self.__pen = QPen(pen) self.__boundingRect = None self.update() def pen(self): """Return the items pen. """ return QPen(self.__pen) def paint(self, painter, option, widget=None): if self.__path.isEmpty(): return painter.save() painter.setPen(self.__pen) painter.setBrush(self.__brush) painter.drawPath(self.__path) painter.restore() def boundingRect(self): if self.__boundingRect is None: br = self.__path.controlPointRect() pen_w = self.__pen.widthF() self.__boundingRect = br.adjusted(-pen_w, -pen_w, pen_w, pen_w) return self.__boundingRect def shape(self): return shapeFromPath(self.__path, self.__pen) def itemChange(self, change, value): if change == QGraphicsObject.ItemPositionHasChanged: pos = qunwrap(value) self.positionChanged.emit() self.positionChanged[QPointF].emit(pos) return QGraphicsObject.itemChange(self, change, value)
def paint(self, painter, option, widget): if canvas.scene.loading_items: return painter.save() painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL)) selected = self.isSelected() theme = canvas.theme if self.m_port_type == PORT_TYPE_AUDIO_JACK: if self.m_is_alternate: poly_color = theme.port_cv_jack_bg_sel if selected else theme.port_cv_jack_bg poly_pen = theme.port_cv_jack_pen_sel if selected else theme.port_cv_jack_pen else: poly_color = theme.port_audio_jack_bg_sel if selected else theme.port_audio_jack_bg poly_pen = theme.port_audio_jack_pen_sel if selected else theme.port_audio_jack_pen text_pen = theme.port_audio_jack_text_sel if selected else theme.port_audio_jack_text conn_pen = QPen(theme.port_audio_jack_pen_sel) elif self.m_port_type == PORT_TYPE_MIDI_JACK: poly_color = theme.port_midi_jack_bg_sel if selected else theme.port_midi_jack_bg poly_pen = theme.port_midi_jack_pen_sel if selected else theme.port_midi_jack_pen text_pen = theme.port_midi_jack_text_sel if selected else theme.port_midi_jack_text conn_pen = QPen(theme.port_midi_jack_pen_sel) elif self.m_port_type == PORT_TYPE_MIDI_ALSA: poly_color = theme.port_midi_alsa_bg_sel if selected else theme.port_midi_alsa_bg poly_pen = theme.port_midi_alsa_pen_sel if selected else theme.port_midi_alsa_pen text_pen = theme.port_midi_alsa_text_sel if selected else theme.port_midi_alsa_text conn_pen = QPen(theme.port_midi_alsa_pen_sel) elif self.m_port_type == PORT_TYPE_PARAMETER: poly_color = theme.port_parameter_bg_sel if selected else theme.port_parameter_bg poly_pen = theme.port_parameter_pen_sel if selected else theme.port_parameter_pen text_pen = theme.port_parameter_text_sel if selected else theme.port_parameter_text conn_pen = QPen(theme.port_parameter_pen_sel) else: qCritical( "PatchCanvas::CanvasPort.paint() - invalid port type '%s'" % port_type2str(self.m_port_type)) return # To prevent quality worsening poly_pen = QPen(poly_pen) poly_pen.setWidthF(poly_pen.widthF() + 0.00001) lineHinting = poly_pen.widthF() / 2 poly_locx = [0, 0, 0, 0, 0, 0] poly_corner_xhinting = ((float(canvas.theme.port_height) / 2) % floor(float(canvas.theme.port_height) / 2)) if poly_corner_xhinting == 0: poly_corner_xhinting = 0.5 * ( 1 - 7 / (float(canvas.theme.port_height) / 2)) is_cv_port = bool(self.m_port_type == PORT_TYPE_AUDIO_JACK and self.m_is_alternate) if self.m_port_mode == PORT_MODE_INPUT: text_pos = QPointF(3, canvas.theme.port_text_ypos) if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON and not is_cv_port: poly_locx[0] = lineHinting poly_locx[1] = self.m_port_width + 5 - lineHinting poly_locx[2] = self.m_port_width + 12 - poly_corner_xhinting poly_locx[3] = self.m_port_width + 5 - lineHinting poly_locx[4] = lineHinting poly_locx[5] = self.m_port_width elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE or is_cv_port: poly_locx[0] = lineHinting poly_locx[1] = self.m_port_width + 5 - lineHinting poly_locx[2] = self.m_port_width + 5 - lineHinting poly_locx[3] = self.m_port_width + 5 - lineHinting poly_locx[4] = lineHinting poly_locx[5] = self.m_port_width else: qCritical( "PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode) return elif self.m_port_mode == PORT_MODE_OUTPUT: text_pos = QPointF(9, canvas.theme.port_text_ypos) if canvas.theme.port_mode == Theme.THEME_PORT_POLYGON and not is_cv_port: poly_locx[0] = self.m_port_width + 12 - lineHinting poly_locx[1] = 7 + lineHinting poly_locx[2] = 0 + poly_corner_xhinting poly_locx[3] = 7 + lineHinting poly_locx[4] = self.m_port_width + 12 - lineHinting poly_locx[5] = 12 - lineHinting elif canvas.theme.port_mode == Theme.THEME_PORT_SQUARE or is_cv_port: poly_locx[0] = self.m_port_width + 12 - lineHinting poly_locx[1] = 5 + lineHinting poly_locx[2] = 5 + lineHinting poly_locx[3] = 5 + lineHinting poly_locx[4] = self.m_port_width + 12 - lineHinting poly_locx[5] = 12 - lineHinting else: qCritical( "PatchCanvas::CanvasPort.paint() - invalid theme port mode '%s'" % canvas.theme.port_mode) return else: qCritical( "PatchCanvas::CanvasPort.paint() - invalid port mode '%s'" % port_mode2str(self.m_port_mode)) return polygon = QPolygonF() if self.m_portgrp_id: first_of_portgrp = False last_of_portgrp = False # look in portgroup if port is the first, # the last, or not. for portgrp in canvas.portgrp_list: if portgrp.portgrp_id == self.m_portgrp_id: if self.m_port_id == portgrp.port_id_list[0]: first_of_portgrp = True if self.m_port_id == portgrp.port_id_list[-1]: last_of_portgrp = True break if first_of_portgrp: polygon += QPointF(poly_locx[0], lineHinting) polygon += QPointF(poly_locx[5], lineHinting) else: polygon += QPointF(poly_locx[0], 0) polygon += QPointF(poly_locx[5], 0) if last_of_portgrp: polygon += QPointF(poly_locx[5], canvas.theme.port_height - lineHinting) polygon += QPointF(poly_locx[0], canvas.theme.port_height - lineHinting) else: polygon += QPointF(poly_locx[5], canvas.theme.port_height) polygon += QPointF(poly_locx[0], canvas.theme.port_height) else: polygon += QPointF(poly_locx[0], lineHinting) polygon += QPointF(poly_locx[1], lineHinting) polygon += QPointF(poly_locx[2], float(canvas.theme.port_height) / 2) polygon += QPointF(poly_locx[3], canvas.theme.port_height - lineHinting) polygon += QPointF(poly_locx[4], canvas.theme.port_height - lineHinting) polygon += QPointF(poly_locx[0], lineHinting) if canvas.theme.port_bg_pixmap: portRect = polygon.boundingRect().adjusted(-lineHinting + 1, -lineHinting + 1, lineHinting - 1, lineHinting - 1) portPos = portRect.topLeft() painter.drawTiledPixmap(portRect, canvas.theme.port_bg_pixmap, portPos) else: port_gradient = QLinearGradient(0, 0, 0, self.m_port_height) dark_color = poly_color.darker(112) light_color = poly_color.lighter(111) if poly_color.lightness() > 127: port_gradient.setColorAt(0, dark_color) port_gradient.setColorAt(0.5, light_color) port_gradient.setColorAt(1, dark_color) else: port_gradient.setColorAt(0, light_color) port_gradient.setColorAt(0.5, dark_color) port_gradient.setColorAt(1, light_color) painter.setBrush(port_gradient) painter.setPen(poly_pen) painter.drawPolygon(polygon) if self.m_is_alternate and not self.m_portgrp_id: if is_cv_port: poly_pen.setWidthF(2.000001) painter.setPen(poly_pen) y_line = canvas.theme.port_height / 2.0 if self.m_port_mode == PORT_MODE_OUTPUT: painter.drawLine(QPointF(0.0, y_line), QPointF(float(poly_locx[1]), y_line)) elif self.m_port_mode == PORT_MODE_INPUT: painter.drawLine(QPointF(self.m_port_width + 5.0, y_line), QPointF(self.m_port_width + 12.0, y_line)) else: # draw the little circle for a2j (or MidiBridge) port poly_pen.setWidthF(1.000001) painter.setBrush(canvas.theme.box_bg_1) ellipse_x = poly_locx[1] if self.m_port_mode == PORT_MODE_OUTPUT: ellipse_x -= 2 elif self.m_port_mode == PORT_MODE_INPUT: ellipse_x += 2 painter.drawEllipse( QPointF(ellipse_x, canvas.theme.port_height / 2.0), 2, 2) painter.setPen(text_pen) painter.setFont(self.m_port_font) sizer = QFontMetrics(self.m_port_font) sep_width = sizer.width(self.m_trunck_sep) if self.m_portgrp_id: print_name_size = self.get_text_width() if self.m_port_mode == PORT_MODE_OUTPUT: text_pos = QPointF(self.m_port_width + 9 - print_name_size, canvas.theme.port_text_ypos) if print_name_size > (self.m_port_width - 4): painter.setPen(QPen(port_gradient, 3)) painter.drawLine( QPointF(float(poly_locx[5]), 3.0), QPointF(float(poly_locx[5]), canvas.theme.port_height - 3.0)) painter.setPen(text_pen) painter.setFont(self.m_port_font) painter.drawText(text_pos, self.m_print_name) if self.m_name_truncked: sep_x = text_pos.x() + sizer.width(self.m_print_name) painter.drawText(QPointF(sep_x + sep_width, text_pos.y()), self.m_print_name_right) painter.setPen(poly_pen) painter.drawText(QPointF(sep_x, text_pos.y() + 1), self.m_trunck_sep) if canvas.theme.idx == Theme.THEME_OOSTUDIO and canvas.theme.port_bg_pixmap: painter.setPen(Qt.NoPen) painter.setBrush(conn_pen.brush()) if self.m_port_mode == PORT_MODE_INPUT: connRect = QRectF(portRect.topLeft(), QSizeF(2, portRect.height())) else: connRect = QRectF( QPointF(portRect.right() - 2, portRect.top()), QSizeF(2, portRect.height())) painter.drawRect(connRect) painter.restore()
class CanvasPreviewFrame(QFrame): miniCanvasMoved = pyqtSignal(float, float) # x = 2 # y = 2 # w = width - 4 # h = height - 3 # bounds -1 px _kRectX = 0 _kRectY = 1 _kRectWidth = 2 _kRectHeight = 3 _kCursorName = 0 _kCursorZoom = 1 _kCursorZoomIn = 2 _kCursorZoomOut = 3 _kCursorZoomCount = 4 _MOUSE_MODE_NONE = 0 _MOUSE_MODE_MOVE = 1 _MOUSE_MODE_SCALE = 2 _RUBBERBAND_BLENDING_DEFAULT = 0 _RUBBERBAND_BLENDING_PLUS = 1 _RUBBERBAND_BLENDING_DIFF = 2 # ----------------------------------------------------------------------------------------------------------------- def __init__(self, parent): QFrame.__init__(self, parent) #self.setMinimumWidth(210) #self.setMinimumHeight(162) #self.setMaximumHeight(162) self.fRealParent = None self.fInternalWidth = 210 - 6 # -4 for width + -1px*2 bounds self.fInternalHeight = 162 - 5 # -3 for height + -1px*2 bounds self.fInternalRatio = self.fInternalWidth / self.fInternalHeight self.fScene = None self.fRenderSource = QRectF(0.0, 0.0, 0.0, 0.0) self.fRenderTarget = QRectF(0.0, 0.0, 0.0, 0.0) self.fUseCustomPaint = False self.fFrameWidth = 0.0 self.fInitialX = 0.0 self.fInitialY = 0.0 self.fScale = 1.0 self.fViewBg = QColor(0, 0, 0) self.fViewBrush = QBrush(QColor(75, 75, 255, 30)) self.fViewPen = QPen(Qt.blue, 1) self.fViewRect = [3.0, 3.0, 10.0, 10.0] self.fMouseMode = self._MOUSE_MODE_NONE self.fMouseLeftDown = False self.fMouseRightDown = False self.fMouseInitialZoomPos = None self.fRubberBandBlending = self._RUBBERBAND_BLENDING_DEFAULT self.fZoomCursors = [None for _ in range(self._kCursorZoomCount)] self.fZoomCursors[self._kCursorName] = "black" self.fZoomCursors[self._kCursorZoom] = QCursor( QPixmap(":/cursors/zoom_black.png"), 8, 7) self.fZoomCursors[self._kCursorZoomIn] = QCursor( QPixmap(":/cursors/zoom-in_black.png"), 8, 7) self.fZoomCursors[self._kCursorZoomOut] = QCursor( QPixmap(":/cursors/zoom-out_black.png"), 8, 7) def init(self, scene: QGraphicsScene, realWidth: float, realHeight: float, useCustomPaint: bool = False): self.fScene = scene self.fRenderSource = QRectF(0.0, 0.0, realWidth, realHeight) self.fInternalRatio = realWidth / realHeight self._updateStyle() if self.fUseCustomPaint != useCustomPaint: self.fUseCustomPaint = useCustomPaint self.repaint() def setRealParent(self, parent): self.fRealParent = parent # ----------------------------------------------------------------------------------------------------------------- def setViewPosX(self, xp: float): self.fViewRect[self._kRectX] = xp * ( self.fInternalWidth - self.fViewRect[self._kRectWidth] / self.fScale) self.update() def setViewPosY(self, yp: float): self.fViewRect[self._kRectY] = yp * ( self.fInternalHeight - self.fViewRect[self._kRectHeight] / self.fScale) self.update() def setViewScale(self, scale: float): self.fScale = scale if self.fRealParent is not None: QTimer.singleShot(0, self.fRealParent.slot_miniCanvasCheckAll) def setViewSize(self, width: float, height: float): self.fViewRect[self._kRectWidth] = width * self.fInternalWidth self.fViewRect[self._kRectHeight] = height * self.fInternalHeight self.update() def setViewTheme(self, bgColor, brushColor, penColor): bg_black = bgColor.blackF() brush_black = brushColor.blackF() r0, g0, b0, _ = bgColor.getRgb() r1, g1, b1, _ = brushColor.getRgb() if brush_black < bg_black: self.fRubberBandBlending = self._RUBBERBAND_BLENDING_PLUS brushColor = QColor(r1 - r0, g1 - g0, b1 - b0, 40) elif bg_black < brush_black: self.fRubberBandBlending = self._RUBBERBAND_BLENDING_DIFF brushColor = QColor(r0 - r1, g0 - g1, b0 - b1, 40) else: bgColor.setAlpha(40) self.fRubberBandBlending = self._RUBBERBAND_BLENDING_DEFAULT penColor.setAlpha(100) self.fViewBg = bgColor self.fViewBrush = QBrush(brushColor) self.fViewPen = QPen(penColor, 1) cursorName = "black" if bg_black < 0.5 else "white" if self.fZoomCursors[self._kCursorName] != cursorName: prefix = ":/cursors/zoom" self.fZoomCursors[self._kCursorName] = cursorName self.fZoomCursors[self._kCursorZoom] = QCursor( QPixmap("{}_{}.png".format(prefix, cursorName)), 8, 7) self.fZoomCursors[self._kCursorZoomIn] = QCursor( QPixmap("{}-in_{}.png".format(prefix, cursorName)), 8, 7) self.fZoomCursors[self._kCursorZoomOut] = QCursor( QPixmap("{}-out_{}.png".format(prefix, cursorName)), 8, 7) # ----------------------------------------------------------------------------------------------------------------- def changeEvent(self, event): if event.type() in (QEvent.StyleChange, QEvent.PaletteChange): self._updateStyle() QFrame.changeEvent(self, event) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: event.accept() self.fMouseLeftDown = True self._updateMouseMode(event) return if event.button() == Qt.RightButton: event.accept() self.fMouseRightDown = True self._updateMouseMode(event) return if event.button() == Qt.MidButton: event.accept() self.fMouseLeftDown = True self.fMouseRightDown = True self._updateMouseMode(event) return QFrame.mouseMoveEvent(self, event) def mouseMoveEvent(self, event): if self.fMouseMode == self._MOUSE_MODE_MOVE: event.accept() self._moveViewRect(event.x(), event.y()) return if self.fMouseMode == self._MOUSE_MODE_SCALE: event.accept() self._scaleViewRect(event.globalY()) return QFrame.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: event.accept() self.fMouseLeftDown = False self._updateMouseMode() return if event.button() == Qt.RightButton: event.accept() self.fMouseRightDown = False self._updateMouseMode(event) return if event.button() == Qt.MidButton: event.accept() self.fMouseLeftDown = event.buttons() & Qt.LeftButton self.fMouseRightDown = event.buttons() & Qt.RightButton self._updateMouseMode(event) return QFrame.mouseReleaseEvent(self, event) def wheelEvent(self, event): if self.fScene is None: QFrame.wheelEvent(self, event) return event.accept() self.fScene.zoom_wheel(event.angleDelta().y()) def paintEvent(self, event): if self.fScene is None: QFrame.paintEvent(self, event) return painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL)) # Brightness-aware out-of-canvas shading bg_color = self.fViewBg bg_black = bg_color.black() bg_shade = -12 if bg_black < 127 else 12 r, g, b, _ = bg_color.getRgb() bg_color = QColor(r + bg_shade, g + bg_shade, b + bg_shade) frameWidth = self.fFrameWidth if self.fUseCustomPaint: # Inner shadow color = QColor.fromHsv(40, 0, 255 - max(210, bg_color.black(), bg_black)) painter.setBrush(Qt.transparent) painter.setPen(color) painter.drawRect( QRectF(0.5, 0.5, self.width() - 1, self.height() - 1)) # Background painter.setBrush(bg_color) painter.setPen(bg_color) painter.drawRect( QRectF(1.5, 1.5, self.width() - 3, self.height() - 3)) else: use_rounding = int(frameWidth > 1) rounding = 0.5 * use_rounding painter.setBrush(bg_color) painter.setPen(bg_color) painter.drawRoundedRect( QRectF(0.5 + frameWidth, 0.5 + frameWidth, self.width() - 1 - frameWidth * 2, self.height() - 1 - frameWidth * 2), rounding, rounding) clipPath = QPainterPath() rounding = 1.0 * use_rounding clipPath.addRoundedRect( QRectF(frameWidth, frameWidth, self.width() - frameWidth * 2, self.height() - frameWidth * 2), rounding, rounding) painter.setClipPath(clipPath) self.fScene.render(painter, self.fRenderTarget, self.fRenderSource, Qt.KeepAspectRatio) # Allow cursor frame to look joined with minicanvas frame painter.setClipping(False) width = self.fViewRect[self._kRectWidth] / self.fScale height = self.fViewRect[self._kRectHeight] / self.fScale # cursor lineHinting = self.fViewPen.widthF() / 2 x = self.fViewRect[self._kRectX] + self.fInitialX y = self.fViewRect[self._kRectY] + self.fInitialY scr_x = floor(x) scr_y = floor(y) rect = QRectF(scr_x + lineHinting, scr_y + lineHinting, ceil(width + x - scr_x) - lineHinting * 2, ceil(height + y - scr_y) - lineHinting * 2) if self.fRubberBandBlending == self._RUBBERBAND_BLENDING_PLUS: painter.setCompositionMode(QPainter.CompositionMode_Plus) elif self.fRubberBandBlending == self._RUBBERBAND_BLENDING_DIFF: painter.setCompositionMode(QPainter.CompositionMode_Difference) painter.setBrush(self.fViewBrush) painter.setPen(Qt.NoPen) painter.drawRect(rect) painter.setCompositionMode(QPainter.CompositionMode_SourceOver) painter.setBrush(Qt.NoBrush) painter.setPen(self.fViewPen) painter.drawRect(rect) if self.fUseCustomPaint: event.accept() else: QFrame.paintEvent(self, event) def resizeEvent(self, event): size = event.size() width = size.width() height = size.height() extRatio = (width - self.fFrameWidth * 2) / (height - self.fFrameWidth * 2) if extRatio >= self.fInternalRatio: self.fInternalHeight = floor(height - self.fFrameWidth * 2) self.fInternalWidth = floor(self.fInternalHeight * self.fInternalRatio) self.fInitialX = floor(float(width - self.fInternalWidth) / 2.0) self.fInitialY = self.fFrameWidth else: self.fInternalWidth = floor(width - self.fFrameWidth * 2) self.fInternalHeight = floor(self.fInternalWidth / self.fInternalRatio) self.fInitialX = self.fFrameWidth self.fInitialY = floor(float(height - self.fInternalHeight) / 2.0) self.fRenderTarget = QRectF(self.fInitialX, self.fInitialY, float(self.fInternalWidth), float(self.fInternalHeight)) if self.fRealParent is not None: QTimer.singleShot(0, self.fRealParent.slot_miniCanvasCheckAll) QFrame.resizeEvent(self, event) # ----------------------------------------------------------------------------------------------------------------- def _moveViewRect(self, x: float, y: float): if self.fScene is None: return x -= self.fInitialX y -= self.fInitialY fixPos = False rCentX = self.fViewRect[self._kRectWidth] / self.fScale / 2 rCentY = self.fViewRect[self._kRectHeight] / self.fScale / 2 if x < rCentX: x = rCentX fixPos = True elif x > self.fInternalWidth - rCentX: x = self.fInternalWidth - rCentX fixPos = True if y < rCentY: y = rCentY fixPos = True elif y > self.fInternalHeight - rCentY: y = self.fInternalHeight - rCentY fixPos = True if fixPos: globalPos = self.mapToGlobal( QPoint(self.fInitialX + x, self.fInitialY + y)) self.cursor().setPos(globalPos) x = self.fRenderSource.width() * x / self.fInternalWidth y = self.fRenderSource.height() * y / self.fInternalHeight self.fScene.m_view.centerOn(x, y) def _scaleViewRect(self, globalY: int): if self.fScene is None: return dy = self.fMouseInitialZoomPos.y() - globalY if dy != 0: self.setCursor( self.fZoomCursors[self._kCursorZoomIn if dy > 0 else self. _kCursorZoomOut]) self.fScene.zoom_wheel(dy) self.cursor().setPos(self.fMouseInitialZoomPos) def _updateMouseMode(self, event=None): if self.fMouseLeftDown and self.fMouseRightDown: self.fMouseInitialZoomPos = event.globalPos() self.setCursor(self.fZoomCursors[self._kCursorZoom]) self.fMouseMode = self._MOUSE_MODE_SCALE elif self.fMouseLeftDown: self.setCursor(QCursor(Qt.SizeAllCursor)) if self.fMouseMode == self._MOUSE_MODE_NONE: self._moveViewRect(event.x(), event.y()) self.fMouseMode = self._MOUSE_MODE_MOVE else: self.unsetCursor() self.fMouseMode = self._MOUSE_MODE_NONE def _updateStyle(self): self.fFrameWidth = 1 if self.fUseCustomPaint else self.frameWidth()
def drawMapObject(self, painter, object, color): painter.save() pen = QPen(Qt.black) pen.setCosmetic(True) cell = object.cell() if (not cell.isEmpty()): tile = cell.tile imgSize = tile.size() pos = self.pixelToScreenCoords_(object.position()) tileOffset = tile.offset() CellRenderer(painter).render(cell, pos, object.size(), CellRenderer.BottomCenter) if (self.testFlag(RenderFlag.ShowTileObjectOutlines)): rect = QRectF(QPointF(pos.x() - imgSize.width() / 2 + tileOffset.x(), pos.y() - imgSize.height() + tileOffset.y()), QSizeF(imgSize)) pen.setStyle(Qt.SolidLine) painter.setPen(pen) painter.drawRect(rect) pen.setStyle(Qt.DotLine) pen.setColor(color) painter.setPen(pen) painter.drawRect(rect) else: lineWidth = self.objectLineWidth() scale = self.painterScale() x = 1 if lineWidth != 0: x = lineWidth shadowOffset = x / scale brushColor = QColor(color) brushColor.setAlpha(50) brush = QBrush(brushColor) pen.setJoinStyle(Qt.RoundJoin) pen.setCapStyle(Qt.RoundCap) pen.setWidth(lineWidth) colorPen = QPen(pen) colorPen.setColor(color) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) # TODO: Do something sensible to make null-sized objects usable x = object.shape() if x==MapObject.Ellipse: polygon = self.pixelRectToScreenPolygon(object.bounds()) tw = self.map().tileWidth() th = self.map().tileHeight() transformScale = QPointF(1, 1) if (tw > th): transformScale = QPointF(1, th/tw) else: transformScale = QPointF(tw/th, 1) l1 = polygon.at(1) - polygon.at(0) l2 = polygon.at(3) - polygon.at(0) trans = QTransform() trans.scale(transformScale.x(), transformScale.y()) trans.rotate(45) iTrans, ok = trans.inverted() l1x = iTrans.map(l1) l2x = iTrans.map(l2) ellipseSize = QSizeF(l1x.manhattanLength(), l2x.manhattanLength()) if (ellipseSize.width() > 0 and ellipseSize.height() > 0): painter.save() painter.setPen(pen) painter.translate(polygon.at(0)) painter.scale(transformScale.x(), transformScale.y()) painter.rotate(45) painter.drawEllipse(QRectF(QPointF(0, 0), ellipseSize)) painter.restore() painter.setBrush(Qt.NoBrush) painter.drawPolygon(polygon) painter.setPen(colorPen) painter.setBrush(Qt.NoBrush) painter.translate(QPointF(0, -shadowOffset)) painter.drawPolygon(polygon) painter.setBrush(brush) if (ellipseSize.width() > 0 and ellipseSize.height() > 0): painter.save() painter.translate(polygon.at(0)) painter.scale(transformScale.x(), transformScale.y()) painter.rotate(45) painter.drawEllipse(QRectF(QPointF(0, 0), ellipseSize)) painter.restore() elif x==MapObject.Rectangle: polygon = self.pixelRectToScreenPolygon(object.bounds()) painter.drawPolygon(polygon) painter.setPen(colorPen) painter.setBrush(brush) polygon.translate(0, -shadowOffset) painter.drawPolygon(polygon) elif x==MapObject.Polygon: pos = object.position() polygon = object.polygon().translated(pos) screenPolygon = self.pixelToScreenCoords_(polygon) thickPen = QPen(pen) thickColorPen = QPen(colorPen) thickPen.setWidthF(thickPen.widthF() * 4) thickColorPen.setWidthF(thickColorPen.widthF() * 4) painter.drawPolygon(screenPolygon) painter.setPen(thickPen) painter.drawPoint(screenPolygon.first()) painter.setPen(colorPen) painter.setBrush(brush) screenPolygon.translate(0, -shadowOffset) painter.drawPolygon(screenPolygon) painter.setPen(thickColorPen) painter.drawPoint(screenPolygon.first()) elif x==MapObject.Polyline: pos = object.position() polygon = object.polygon().translated(pos) screenPolygon = self.pixelToScreenCoords_(polygon) thickPen = QPen(pen) thickColorPen = QPen(colorPen) thickPen.setWidthF(thickPen.widthF() * 4) thickColorPen.setWidthF(thickColorPen.widthF() * 4) painter.drawPolyline(screenPolygon) painter.setPen(thickPen) painter.drawPoint(screenPolygon.first()) painter.setPen(colorPen) screenPolygon.translate(0, -shadowOffset) painter.drawPolyline(screenPolygon) painter.setPen(thickColorPen) painter.drawPoint(screenPolygon.first()) painter.restore()
def drawMapObject(self, painter, object, color): painter.save() pen = QPen(Qt.black) pen.setCosmetic(True) cell = object.cell() if (not cell.isEmpty()): tile = cell.tile imgSize = tile.size() pos = self.pixelToScreenCoords_(object.position()) tileOffset = tile.offset() CellRenderer(painter).render(cell, pos, object.size(), CellRenderer.BottomCenter) if (self.testFlag(RenderFlag.ShowTileObjectOutlines)): rect = QRectF( QPointF(pos.x() - imgSize.width() / 2 + tileOffset.x(), pos.y() - imgSize.height() + tileOffset.y()), QSizeF(imgSize)) pen.setStyle(Qt.SolidLine) painter.setPen(pen) painter.drawRect(rect) pen.setStyle(Qt.DotLine) pen.setColor(color) painter.setPen(pen) painter.drawRect(rect) else: lineWidth = self.objectLineWidth() scale = self.painterScale() x = 1 if lineWidth != 0: x = lineWidth shadowOffset = x / scale brushColor = QColor(color) brushColor.setAlpha(50) brush = QBrush(brushColor) pen.setJoinStyle(Qt.RoundJoin) pen.setCapStyle(Qt.RoundCap) pen.setWidth(lineWidth) colorPen = QPen(pen) colorPen.setColor(color) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) # TODO: Do something sensible to make null-sized objects usable x = object.shape() if x == MapObject.Ellipse: polygon = self.pixelRectToScreenPolygon(object.bounds()) tw = self.map().tileWidth() th = self.map().tileHeight() transformScale = QPointF(1, 1) if (tw > th): transformScale = QPointF(1, th / tw) else: transformScale = QPointF(tw / th, 1) l1 = polygon.at(1) - polygon.at(0) l2 = polygon.at(3) - polygon.at(0) trans = QTransform() trans.scale(transformScale.x(), transformScale.y()) trans.rotate(45) iTrans, ok = trans.inverted() l1x = iTrans.map(l1) l2x = iTrans.map(l2) ellipseSize = QSizeF(l1x.manhattanLength(), l2x.manhattanLength()) if (ellipseSize.width() > 0 and ellipseSize.height() > 0): painter.save() painter.setPen(pen) painter.translate(polygon.at(0)) painter.scale(transformScale.x(), transformScale.y()) painter.rotate(45) painter.drawEllipse(QRectF(QPointF(0, 0), ellipseSize)) painter.restore() painter.setBrush(Qt.NoBrush) painter.drawPolygon(polygon) painter.setPen(colorPen) painter.setBrush(Qt.NoBrush) painter.translate(QPointF(0, -shadowOffset)) painter.drawPolygon(polygon) painter.setBrush(brush) if (ellipseSize.width() > 0 and ellipseSize.height() > 0): painter.save() painter.translate(polygon.at(0)) painter.scale(transformScale.x(), transformScale.y()) painter.rotate(45) painter.drawEllipse(QRectF(QPointF(0, 0), ellipseSize)) painter.restore() elif x == MapObject.Rectangle: polygon = self.pixelRectToScreenPolygon(object.bounds()) painter.drawPolygon(polygon) painter.setPen(colorPen) painter.setBrush(brush) polygon.translate(0, -shadowOffset) painter.drawPolygon(polygon) elif x == MapObject.Polygon: pos = object.position() polygon = object.polygon().translated(pos) screenPolygon = self.pixelToScreenCoords_(polygon) thickPen = QPen(pen) thickColorPen = QPen(colorPen) thickPen.setWidthF(thickPen.widthF() * 4) thickColorPen.setWidthF(thickColorPen.widthF() * 4) painter.drawPolygon(screenPolygon) painter.setPen(thickPen) painter.drawPoint(screenPolygon.first()) painter.setPen(colorPen) painter.setBrush(brush) screenPolygon.translate(0, -shadowOffset) painter.drawPolygon(screenPolygon) painter.setPen(thickColorPen) painter.drawPoint(screenPolygon.first()) elif x == MapObject.Polyline: pos = object.position() polygon = object.polygon().translated(pos) screenPolygon = self.pixelToScreenCoords_(polygon) thickPen = QPen(pen) thickColorPen = QPen(colorPen) thickPen.setWidthF(thickPen.widthF() * 4) thickColorPen.setWidthF(thickColorPen.widthF() * 4) painter.drawPolyline(screenPolygon) painter.setPen(thickPen) painter.drawPoint(screenPolygon.first()) painter.setPen(colorPen) screenPolygon.translate(0, -shadowOffset) painter.drawPolyline(screenPolygon) painter.setPen(thickColorPen) painter.drawPoint(screenPolygon.first()) painter.restore()
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)
def paint(self, painter, option, widget): painter.save() painter.setRenderHint(QPainter.Antialiasing, bool(options.antialiasing == ANTIALIASING_FULL)) rect = QRectF(0, 0, self.p_width, self.p_height) # Draw rectangle pen = QPen(canvas.theme.box_pen_sel if self.isSelected() else canvas. theme.box_pen) pen.setWidthF(pen.widthF() + 0.00001) painter.setPen(pen) lineHinting = pen.widthF() / 2 if canvas.theme.box_bg_type == Theme.THEME_BG_GRADIENT: box_gradient = QLinearGradient(0, 0, 0, self.p_height) box_gradient.setColorAt(0, canvas.theme.box_bg_1) box_gradient.setColorAt(1, canvas.theme.box_bg_2) painter.setBrush(box_gradient) else: painter.setBrush(canvas.theme.box_bg_1) rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting) painter.drawRect(rect) # Draw plugin inline display if supported self.paintInlineDisplay(painter) # Draw pixmap header rect.setHeight(canvas.theme.box_header_height) if canvas.theme.box_header_pixmap: painter.setPen(Qt.NoPen) painter.setBrush(canvas.theme.box_bg_2) # outline rect.adjust(lineHinting, lineHinting, -lineHinting, -lineHinting) painter.drawRect(rect) rect.adjust(1, 1, -1, 0) painter.drawTiledPixmap(rect, canvas.theme.box_header_pixmap, rect.topLeft()) # Draw text painter.setFont(self.m_font_name) if self.isSelected(): painter.setPen(canvas.theme.box_text_sel) else: painter.setPen(canvas.theme.box_text) if canvas.theme.box_use_icon: textPos = QPointF(25, canvas.theme.box_text_ypos) else: appNameSize = fontHorizontalAdvance(self.m_font_name, self.m_group_name) rem = self.p_width - appNameSize textPos = QPointF(rem / 2, canvas.theme.box_text_ypos) painter.drawText(textPos, self.m_group_name) self.repaintLines() painter.restore()
def drawMapObject(self, painter, object, color): painter.save() bounds = object.bounds() rect = QRectF(bounds) painter.translate(rect.topLeft()) rect.moveTopLeft(QPointF(0, 0)) cell = object.cell() if (not cell.isEmpty()): CellRenderer(painter).render(cell, QPointF(), object.size(), CellRenderer.BottomLeft) if (self.testFlag(RenderFlag.ShowTileObjectOutlines)): tile = cell.tile imgSize = tile.size() tileOffset = tile.offset() rect = QRectF( QPointF(tileOffset.x(), tileOffset.y() - imgSize.height()), QSizeF(imgSize)) pen = QPen(Qt.SolidLine) pen.setCosmetic(True) painter.setPen(pen) painter.drawRect(rect) pen.setStyle(Qt.DotLine) pen.setColor(color) painter.setPen(pen) painter.drawRect(rect) else: lineWidth = self.objectLineWidth() scale = self.painterScale() if lineWidth == 0: x = 1 else: x = lineWidth shadowDist = x / scale shadowOffset = QPointF(shadowDist * 0.5, shadowDist * 0.5) linePen = QPen(color, lineWidth, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) #linePen.setCosmetic(True) shadowPen = QPen(linePen) shadowPen.setColor(Qt.black) brushColor = QColor(color) fillBrush = QBrush(brushColor) painter.setRenderHint(QPainter.Antialiasing) # Trying to draw an ellipse with 0-width is causing a hang in # CoreGraphics when drawing the path requested by the # QCoreGraphicsPaintEngine. Draw them as rectangle instead. shape = object.shape() if (shape == MapObject.Ellipse and ((rect.width() == 0.0) ^ (rect.height() == 0.0))): shape = MapObject.Rectangle x = shape if x == MapObject.Rectangle: if (rect.isNull()): rect = QRectF(QPointF(-10, -10), QSizeF(20, 20)) # Draw the shadow painter.setPen(shadowPen) painter.drawRect(rect.translated(shadowOffset)) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawRect(rect) elif x == MapObject.Polyline: screenPolygon = self.pixelToScreenCoords_(object.polygon()) thickShadowPen = QPen(shadowPen) thickLinePen = QPen(linePen) thickShadowPen.setWidthF(thickShadowPen.widthF() * 4) thickLinePen.setWidthF(thickLinePen.widthF() * 4) painter.setPen(shadowPen) painter.drawPolyline(screenPolygon.translated(shadowOffset)) painter.setPen(thickShadowPen) painter.drawPoint(screenPolygon.first() + shadowOffset) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawPolyline(screenPolygon) painter.setPen(thickLinePen) painter.drawPoint(screenPolygon.first()) elif x == MapObject.Polygon: screenPolygon = self.pixelToScreenCoords_(object.polygon()) thickShadowPen = QPen(shadowPen) thickLinePen = QPen(linePen) thickShadowPen.setWidthF(thickShadowPen.widthF() * 4) thickLinePen.setWidthF(thickLinePen.widthF() * 4) painter.setPen(shadowPen) painter.drawPolygon(screenPolygon.translated(shadowOffset)) painter.setPen(thickShadowPen) painter.drawPoint(screenPolygon.first() + shadowOffset) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawPolygon(screenPolygon) painter.setPen(thickLinePen) painter.drawPoint(screenPolygon.first()) elif x == MapObject.Ellipse: if (rect.isNull()): rect = QRectF(QPointF(-10, -10), QSizeF(20, 20)) # Draw the shadow painter.setPen(shadowPen) painter.drawEllipse(rect.translated(shadowOffset)) painter.setPen(linePen) painter.setBrush(fillBrush) painter.drawEllipse(rect) painter.restore()