def _createPreXoverPainterPath( elements: List[List[QPointF]], end_poly: QPolygonF = None, is_fwd: bool = True) -> QPainterPath: path = QPainterPath() next_pt = None for element in elements: start_pt = element[0] path.moveTo(start_pt) for next_pt in element[1:]: path.lineTo(next_pt) if end_poly is not None: h = end_poly.boundingRect().height()/2 xoffset = -h if is_fwd else h w = end_poly.boundingRect().width() yoffset = w if is_fwd else -w angle = -90 if is_fwd else 90 T = QTransform() T.translate(next_pt.x()+xoffset, next_pt.y()+yoffset) T.rotate(angle) path.addPolygon(T.map(end_poly)) return path
def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) polygon = QPolygonF([ QPointF(+27 - 10, -17), # 0 QPointF(-27, -17), # 1 QPointF(-27, +17), # 2 QPointF(+27, +17), # 3 QPointF(+27, -17 + 10), # 4 QPointF(+27 - 10, -17), # 5 ]) fold = QPolygonF([ QPointF(polygon[cls.indexTR].x(), polygon[cls.indexTR].y()), QPointF(polygon[cls.indexTR].x(), polygon[cls.indexTR].y() + 10), QPointF(polygon[cls.indexRT].x(), polygon[cls.indexRT].y()), QPointF(polygon[cls.indexTR].x(), polygon[cls.indexTR].y()), ]) # ITEM SHAPE painter.setPen(QPen(QColor(0, 0, 0), 1.0, Qt.SolidLine)) painter.setBrush(QColor(252, 252, 252)) painter.translate(kwargs['w'] / 2, kwargs['h'] / 2) painter.drawPolygon(polygon) painter.drawPolygon(fold) # TEXT WITHIN THE SHAPE painter.setFont(Font('Arial', 10, Font.Light)) painter.drawText(polygon.boundingRect(), Qt.AlignCenter, 'value\nrestriction') return pixmap
class MapObject(Object): ## # Enumerates the different object shapes. Rectangle is the default shape. # When a polygon is set, the shape determines whether it should be # interpreted as a filled polygon or a line. ## Rectangle, Polygon, Polyline, Ellipse = range(4) def __init__(self, *args): super().__init__(Object.MapObjectType) self.mPolygon = QPolygonF() self.mName = QString() self.mPos = QPointF() self.mCell = Cell() self.mType = QString() self.mId = 0 self.mShape = MapObject.Rectangle self.mObjectGroup = None self.mRotation = 0.0 self.mVisible = True l = len(args) if l==0: self.mSize = QSizeF(0, 0) elif l==4: name, _type, pos, size = args self.mName = name self.mType = _type self.mPos = pos self.mSize = QSizeF(size) ## # Returns the id of this object. Each object gets an id assigned that is # unique for the map the object is on. ## def id(self): return self.mId ## # Sets the id of this object. ## def setId(self, id): self.mId = id ## # Returns the name of this object. The name is usually just used for # identification of the object in the editor. ## def name(self): return self.mName ## # Sets the name of this object. ## def setName(self, name): self.mName = name ## # Returns the type of this object. The type usually says something about # how the object is meant to be interpreted by the engine. ## def type(self): return self.mType ## # Sets the type of this object. ## def setType(self, type): self.mType = type ## # Returns the position of this object. ## def position(self): return QPointF(self.mPos) ## # Sets the position of this object. ## def setPosition(self, pos): self.mPos = pos ## # Returns the x position of this object. ## def x(self): return self.mPos.x() ## # Sets the x position of this object. ## def setX(self, x): self.mPos.setX(x) ## # Returns the y position of this object. ## def y(self): return self.mPos.y() ## # Sets the x position of this object. ## def setY(self, y): self.mPos.setY(y) ## # Returns the size of this object. ## def size(self): return self.mSize ## # Sets the size of this object. ## def setSize(self, *args): l = len(args) if l==1: size = args[0] self.mSize = QSizeF(size) elif l==2: width, height = args self.setSize(QSizeF(width, height)) ## # Returns the width of this object. ## def width(self): return self.mSize.width() ## # Sets the width of this object. ## def setWidth(self, width): self.mSize.setWidth(width) ## # Returns the height of this object. ## def height(self): return self.mSize.height() ## # Sets the height of this object. ## def setHeight(self, height): self.mSize.setHeight(height) ## # Sets the polygon associated with this object. The polygon is only used # when the object shape is set to either Polygon or Polyline. # # \sa setShape() ## def setPolygon(self, polygon): self.mPolygon = polygon ## # Returns the polygon associated with this object. Returns an empty # polygon when no polygon is associated with this object. ## def polygon(self): return QPolygonF(self.mPolygon) ## # Sets the shape of the object. ## def setShape(self, shape): self.mShape = shape ## # Returns the shape of the object. ## def shape(self): return self.mShape ## # Shortcut to getting a QRectF from position() and size(). ## def bounds(self): return QRectF(self.mPos, self.mSize) ## # Shortcut to getting a QRectF from position() and size() that uses cell tile if present. ## def boundsUseTile(self): if (self.mCell.isEmpty()): # No tile so just use regular bounds return self.bounds() # Using the tile for determing boundary # Note the position given is the bottom-left corner so correct for that return QRectF(QPointF(self.mPos.x(), self.mPos.y() - self.mCell.tile.height()), self.mCell.tile.size()) ## # Sets the tile that is associated with this object. The object will # display as the tile image. # # \warning The object shape is ignored for tile objects! ## def setCell(self, cell): self.mCell = cell ## # Returns the tile associated with this object. ## def cell(self): return self.mCell ## # Returns the object group this object belongs to. ## def objectGroup(self): return self.mObjectGroup ## # Sets the object group this object belongs to. Should only be called # from the ObjectGroup class. ## def setObjectGroup(self, objectGroup): self.mObjectGroup = objectGroup ## # Returns the rotation of the object in degrees. ## def rotation(self): return self.mRotation ## # Sets the rotation of the object in degrees. ## def setRotation(self, rotation): self.mRotation = rotation ## # This is somewhat of a workaround for dealing with the ways different objects # align. # # Traditional rectangle objects have top-left alignment. # Tile objects have bottom-left alignment on orthogonal maps, but # bottom-center alignment on isometric maps. # # Eventually, the object alignment should probably be configurable. For # backwards compatibility, it will need to be configurable on a per-object # level. ## def alignment(self): if (self.mCell.isEmpty()): return Alignment.TopLeft elif (self.mObjectGroup): map = self.mObjectGroup.map() if map: if (map.orientation() == Map.Orientation.Isometric): return Alignment.Bottom return Alignment.BottomLeft def isVisible(self): return self.mVisible def setVisible(self, visible): self.mVisible = visible ## # Flip this object in the given \a direction. This doesn't change the size # of the object. ## def flip(self, direction): if (not self.mCell.isEmpty()): if (direction == FlipDirection.FlipHorizontally): self.mCell.flippedHorizontally = not self.mCell.flippedHorizontally elif (direction == FlipDirection.FlipVertically): self.mCell.flippedVertically = not self.mCell.flippedVertically if (not self.mPolygon.isEmpty()): center2 = self.mPolygon.boundingRect().center() * 2 if (direction == FlipDirection.FlipHorizontally): for i in range(self.mPolygon.size()): # oh, QPointF mPolygon returned is a copy of internal object self.mPolygon[i] = QPointF(center2.x() - self.mPolygon[i].x(), self.mPolygon[i].y()) elif (direction == FlipDirection.FlipVertically): for i in range(self.mPolygon.size()): self.mPolygon[i] = QPointF(self.mPolygon[i].x(), center2.y() - self.mPolygon[i].y()) ## # Returns a duplicate of this object. The caller is responsible for the # ownership of this newly created object. ## def clone(self): o = MapObject(self.mName, self.mType, self.mPos, self.mSize) o.setProperties(self.properties()) o.setPolygon(self.mPolygon) o.setShape(self.mShape) o.setCell(self.mCell) o.setRotation(self.mRotation) return o
def setRect(self): polygon = QPolygonF(self.points) rect = polygon.boundingRect() self._rect = rect self._boundingRect = rect
class MapObject(Object): ## # Enumerates the different object shapes. Rectangle is the default shape. # When a polygon is set, the shape determines whether it should be # interpreted as a filled polygon or a line. ## Rectangle, Polygon, Polyline, Ellipse = range(4) def __init__(self, *args): super().__init__(Object.MapObjectType) self.mPolygon = QPolygonF() self.mName = QString() self.mPos = QPointF() self.mCell = Cell() self.mType = QString() self.mId = 0 self.mShape = MapObject.Rectangle self.mObjectGroup = None self.mRotation = 0.0 self.mVisible = True l = len(args) if l == 0: self.mSize = QSizeF(0, 0) elif l == 4: name, _type, pos, size = args self.mName = name self.mType = _type self.mPos = pos self.mSize = QSizeF(size) ## # Returns the id of this object. Each object gets an id assigned that is # unique for the map the object is on. ## def id(self): return self.mId ## # Sets the id of this object. ## def setId(self, id): self.mId = id ## # Returns the name of this object. The name is usually just used for # identification of the object in the editor. ## def name(self): return self.mName ## # Sets the name of this object. ## def setName(self, name): self.mName = name ## # Returns the type of this object. The type usually says something about # how the object is meant to be interpreted by the engine. ## def type(self): return self.mType ## # Sets the type of this object. ## def setType(self, type): self.mType = type ## # Returns the position of this object. ## def position(self): return QPointF(self.mPos) ## # Sets the position of this object. ## def setPosition(self, pos): self.mPos = pos ## # Returns the x position of this object. ## def x(self): return self.mPos.x() ## # Sets the x position of this object. ## def setX(self, x): self.mPos.setX(x) ## # Returns the y position of this object. ## def y(self): return self.mPos.y() ## # Sets the x position of this object. ## def setY(self, y): self.mPos.setY(y) ## # Returns the size of this object. ## def size(self): return self.mSize ## # Sets the size of this object. ## def setSize(self, *args): l = len(args) if l == 1: size = args[0] self.mSize = QSizeF(size) elif l == 2: width, height = args self.setSize(QSizeF(width, height)) ## # Returns the width of this object. ## def width(self): return self.mSize.width() ## # Sets the width of this object. ## def setWidth(self, width): self.mSize.setWidth(width) ## # Returns the height of this object. ## def height(self): return self.mSize.height() ## # Sets the height of this object. ## def setHeight(self, height): self.mSize.setHeight(height) ## # Sets the polygon associated with this object. The polygon is only used # when the object shape is set to either Polygon or Polyline. # # \sa setShape() ## def setPolygon(self, polygon): self.mPolygon = polygon ## # Returns the polygon associated with this object. Returns an empty # polygon when no polygon is associated with this object. ## def polygon(self): return QPolygonF(self.mPolygon) ## # Sets the shape of the object. ## def setShape(self, shape): self.mShape = shape ## # Returns the shape of the object. ## def shape(self): return self.mShape ## # Shortcut to getting a QRectF from position() and size(). ## def bounds(self): return QRectF(self.mPos, self.mSize) ## # Shortcut to getting a QRectF from position() and size() that uses cell tile if present. ## def boundsUseTile(self): if (self.mCell.isEmpty()): # No tile so just use regular bounds return self.bounds() # Using the tile for determing boundary # Note the position given is the bottom-left corner so correct for that return QRectF( QPointF(self.mPos.x(), self.mPos.y() - self.mCell.tile.height()), self.mCell.tile.size()) ## # Sets the tile that is associated with this object. The object will # display as the tile image. # # \warning The object shape is ignored for tile objects! ## def setCell(self, cell): self.mCell = cell ## # Returns the tile associated with this object. ## def cell(self): return self.mCell ## # Returns the object group this object belongs to. ## def objectGroup(self): return self.mObjectGroup ## # Sets the object group this object belongs to. Should only be called # from the ObjectGroup class. ## def setObjectGroup(self, objectGroup): self.mObjectGroup = objectGroup ## # Returns the rotation of the object in degrees. ## def rotation(self): return self.mRotation ## # Sets the rotation of the object in degrees. ## def setRotation(self, rotation): self.mRotation = rotation ## # This is somewhat of a workaround for dealing with the ways different objects # align. # # Traditional rectangle objects have top-left alignment. # Tile objects have bottom-left alignment on orthogonal maps, but # bottom-center alignment on isometric maps. # # Eventually, the object alignment should probably be configurable. For # backwards compatibility, it will need to be configurable on a per-object # level. ## def alignment(self): if (self.mCell.isEmpty()): return Alignment.TopLeft elif (self.mObjectGroup): map = self.mObjectGroup.map() if map: if (map.orientation() == Map.Orientation.Isometric): return Alignment.Bottom return Alignment.BottomLeft def isVisible(self): return self.mVisible def setVisible(self, visible): self.mVisible = visible ## # Flip this object in the given \a direction. This doesn't change the size # of the object. ## def flip(self, direction): if (not self.mCell.isEmpty()): if (direction == FlipDirection.FlipHorizontally): self.mCell.flippedHorizontally = not self.mCell.flippedHorizontally elif (direction == FlipDirection.FlipVertically): self.mCell.flippedVertically = not self.mCell.flippedVertically if (not self.mPolygon.isEmpty()): center2 = self.mPolygon.boundingRect().center() * 2 if (direction == FlipDirection.FlipHorizontally): for i in range(self.mPolygon.size()): # oh, QPointF mPolygon returned is a copy of internal object self.mPolygon[i] = QPointF( center2.x() - self.mPolygon[i].x(), self.mPolygon[i].y()) elif (direction == FlipDirection.FlipVertically): for i in range(self.mPolygon.size()): self.mPolygon[i] = QPointF( self.mPolygon[i].x(), center2.y() - self.mPolygon[i].y()) ## # Returns a duplicate of this object. The caller is responsible for the # ownership of this newly created object. ## def clone(self): o = MapObject(self.mName, self.mType, self.mPos, self.mSize) o.setProperties(self.properties()) o.setPolygon(self.mPolygon) o.setShape(self.mShape) o.setCell(self.mCell) o.setRotation(self.mRotation) return o
class Link(QGraphicsLineItem): """! Link between Ports """ def __init__(self, from_port, to_port): """! @brief Link between two Ports (of different Nodes) @param from_port <OutputPort>: origin port @param to_port <InputPort>: destination port """ super().__init__() self.setFlag(QGraphicsItem.ItemIsSelectable) self.head = None self.tail = None self.selection_offset = 8 self.arrow_size = 10 self.selection_polygon = None self.from_port = from_port self.to_port = to_port self.arrow_head = QPolygonF() self.update_nodes() self.pen = QPen(Qt.black, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) self.dashed_pen = QPen(Qt.black, 1, Qt.DashLine) self.brush = QBrush(Qt.black) def save(self): return '|'.join( map(str, [ self.from_port.parentItem().index(), self.from_port.index(), self.to_port.parentItem().index(), self.to_port.index() ])) def update_polygon(self): angle = self.line().angle() * math.pi / 180 dx = self.selection_offset * math.sin(angle) dy = self.selection_offset * math.cos(angle) offset1 = QPointF(dx, dy) offset2 = QPointF(-dx, -dy) if self.head is None and self.tail is None: self.selection_polygon = QPolygonF([ self.line().p1() + offset1, self.line().p1() + offset2, self.line().p2() + offset2, self.line().p2() + offset1 ]) elif self.tail is None: head_angle = self.head.angle() * math.pi / 180 head_dx = self.selection_offset * math.sin(head_angle) head_dy = self.selection_offset * math.cos(head_angle) head_offset1 = QPointF(head_dx, head_dy) head_offset2 = QPointF(-head_dx, -head_dy) self.selection_polygon = QPolygonF([ self.line().p1() + offset1, self.head.p1() + head_offset1, self.head.p1() + head_offset2, self.line().p1() + offset2, self.line().p2() + offset2, self.line().p2() + offset1 ]) elif self.head is None: tail_angle = self.tail.angle() * math.pi / 180 tail_dx = self.selection_offset * math.sin(tail_angle) tail_dy = self.selection_offset * math.cos(tail_angle) tail_offset1 = QPointF(tail_dx, tail_dy) tail_offset2 = QPointF(-tail_dx, -tail_dy) self.selection_polygon = QPolygonF([ self.line().p1() + offset1, self.line().p1() + offset2, self.line().p2() + offset2, self.tail.p2() + tail_offset2, self.tail.p2() + tail_offset1, self.line().p2() + offset1 ]) else: head_angle = self.head.angle() * math.pi / 180 head_dx = self.selection_offset * math.sin(head_angle) head_dy = self.selection_offset * math.cos(head_angle) head_offset1 = QPointF(head_dx, head_dy) head_offset2 = QPointF(-head_dx, -head_dy) tail_angle = self.tail.angle() * math.pi / 180 tail_dx = self.selection_offset * math.sin(tail_angle) tail_dy = self.selection_offset * math.cos(tail_angle) tail_offset1 = QPointF(tail_dx, tail_dy) tail_offset2 = QPointF(-tail_dx, -tail_dy) self.selection_polygon = QPolygonF([ self.line().p1() + offset1, self.head.p1() + head_offset1, self.head.p1() + head_offset2, self.line().p1() + offset2, self.line().p2() + offset2, self.tail.p2() + tail_offset2, self.tail.p2() + tail_offset1, self.line().p2() + offset1 ]) def boundingRect(self): return self.selection_polygon.boundingRect() def shape(self): path = QPainterPath() path.addPolygon(self.selection_polygon) return path def paint(self, painter, options, widget=None): painter.setPen(self.pen) if self.head is not None: painter.drawLine(self.head) if self.tail is not None: painter.drawLine(self.tail) painter.drawLine(self.line()) if self.isSelected(): painter.setPen(self.dashed_pen) painter.drawPolygon(self.selection_polygon) # draw the arrow tip if self.tail is not None: tail_line = self.tail else: tail_line = self.line() intersection_point = QPointF(0, 0) for line in self.to_port.lines(): line = QLineF(self.mapFromScene(line.p1()), self.mapFromScene(line.p2())) intersection_type = tail_line.intersect(line, intersection_point) if intersection_type == QLineF.BoundedIntersection: break angle = math.acos(tail_line.dx() / tail_line.length()) if tail_line.dy() >= 0: angle = (math.pi * 2) - angle arrow_p1 = intersection_point - QPointF( math.sin(angle + math.pi / 3) * self.arrow_size, math.cos(angle + math.pi / 3) * self.arrow_size) arrow_p2 = intersection_point - QPointF( math.sin(angle + math.pi - math.pi / 3) * self.arrow_size, math.cos(angle + math.pi - math.pi / 3) * self.arrow_size) self.arrow_head.clear() for p in [intersection_point, arrow_p1, arrow_p2]: self.arrow_head.append(p) path = QPainterPath() path.addPolygon(self.arrow_head) painter.fillPath(path, self.brush) def update_nodes(self): p1 = self.from_port.rect().center() p2 = self.to_port.rect().center() p1 = self.from_port.parentItem().mapToScene(p1) p2 = self.to_port.parentItem().mapToScene(p2) line = QLineF(p1, p2) self.setLine(line) self.update_polygon() box1 = self.from_port.parentItem().mapRectToScene( self.from_port.parentItem().box.rect()) if box1.intersects(self.boundingRect()): p3 = box1.bottomRight() + QPointF(0, Port.WIDTH / 2) self.head = QLineF(p1, p3) line = QLineF(p3, p2) else: self.head = None box2 = self.to_port.parentItem().mapRectToScene( self.to_port.parentItem().box.rect()) if box2.intersects(self.boundingRect()): p4 = box2.topLeft() + QPointF(-Port.WIDTH / 2, 0) self.tail = QLineF(p4, p2) line = QLineF(line.p1(), p4) else: self.tail = None self.setLine(line) self.update_polygon() self.update() def remove(self): self.from_port.disconnect(self.to_port) self.to_port.disconnect() self.from_port.parentItem().remove_link(self) self.to_port.parentItem().remove_link(self) self.to_port.parentItem().reconfigure()
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()
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()