def setupPaint(self): """Offscreen rather than onscreen redraw (few changes).""" path = QPainterPath() if self.data.isInput: path.addEllipse( self.pinW / 2, self.pinW / 2, self.bodyW, self.bodyW) else: path.addRect( 3 * self.pinW / 2 + 1, self.pinW / 2, self.bodyW, self.bodyW) path.addPath(self.pinPath) self.setPath(path) self.name.setVisible(self.showName) self.name.setText(self.data.name) br = self.mapToScene(self.boundingRect()) w = self.boundingRect().width() h = self.boundingRect().height() realX = min([i.x() for i in br]) realY = min([i.y() for i in br]) self.name.setPos(self.mapFromScene( realX, realY + (w if self.rotation() % 180 else h) + 1)) self.value.setText( str(int(self.data.value)) if self.data.value is not None else 'E') self.value.setPos(self.mapFromScene(realX + w / 3, realY + h / 3)) self.value.setBrush(QColor('green' if self.data.value else 'red')) self.update() # Force onscreen redraw after changes.
def set_shape(self, width, height): ''' Define the bouding rectangle of the JOIN symbol ''' circ = min(width, height) path = QPainterPath() path.addEllipse(0, 0, circ, circ) self.setPath(path) super(Join, self).set_shape(width, height)
def animate(self, shapes): self.start_signal.emit() time.sleep(self.start_delay) self.running = True self.ended = False max_radius = [] original_clips = [] centers = [] animating_radius = [] inc_rate = [] for s in shapes: # Setting max of width or height as radius, ergo "circular" reveal, # not "oval" reveal target = max(s.width, s.height) max_radius.append(target) # Starting from the zero reaching the max animating_radius.append(0) # Getting the original masks; Used in case of cancelation original_clips.append(s.clip) # Center of the shape, considering margin centers.append(QPoint((s.width / 2) + s.margin_start, (s.height / 2) + s.margin_top)) # Calculating the increase rate using the good ol' formula inc_rate.append((target / self.fps) * (1000 / self.duration)) while self.running or self.paused: if self.canceled: for i, s in enumerate(shapes): s.clip = original_clips[i] self.cancel_signal.emit() return elif self.ended: self.end_signal.emit() return elif self.paused: # Handling the pause self.pause_signal.emit() while not self.paused: pass self.resume_signal.emit() else: # Setting FPS from the animator time.sleep(1 / self.fps) completed = False for i, s in enumerate(shapes): if animating_radius[i] < max_radius[i]: path = QPainterPath() path.addEllipse(centers[i], animating_radius[i], animating_radius[i]) s.clip = path s.update() QApplication.processEvents() animating_radius[i] += inc_rate[i] else: completed = True # No need to check on every iteration, duration is same so # all objects are gonna end at the same time if completed: self.end_signal.emit() self.started = False self.ended = True return
def setupPaint(self): """Offscreen rather than onscreen redraw (few changes).""" self.nIn = self.data.nb_inputs() self.nOut = self.data.nb_outputs() # 3 sections with different heights must be aligned : self.imgH = self.image.size().height() # central (png image) self.imgW = self.image.size().width() self.inH = (self.nIn - 1) * self.ioH + 2 * self.radius # inputs self.outH = (self.nOut - 1) * self.ioH + 2 * self.radius # outputs # therefore we calculate a vertical offset for each section : self.maxH = max(self.imgH, self.inH, self.outH) self.imgOff = ( 0 if self.maxH == self.imgH else (self.maxH - self.imgH) / 2.) self.inOff = ( 0 if self.maxH == self.inH else (self.maxH - self.inH) / 2.) self.outOff = ( 0 if self.maxH == self.outH else (self.maxH - self.outH) / 2.) # i/o mouseover detection. Create once, use on each mouseMoveEvent. self.inputPaths = [] self.outputPaths = [] ni = self.data.nb_inputs() no = self.data.nb_outputs() for i in range(1 - int(ni / 2), 2 + int(ni / 2)): if i != 1 or ni % 2: path = QPainterPath() path.addEllipse( -self.ioW - self.radius, i * self.ioH - self.radius / 2, self.radius, self.radius) self.inputPaths.append(path) for i in range(1 - int(no / 2), 2 + int(no / 2)): if i != 1 or no % 2: path = QPainterPath() path.addEllipse( self.imgW + self.ioW, i * self.ioH - self.radius / 2, self.radius, self.radius) self.outputPaths.append(path) self.name.setVisible(self.showName) self.category.setVisible(self.showCategory) if self.showName or self.showCategory: br = self.mapToScene(self.boundingRect()) w = self.boundingRect().width() h = self.boundingRect().height() realX = min([i.x() for i in br]) realY = min([i.y() for i in br]) firstY = realY + (w if self.rotation() % 180 else h) + 1 secondY = firstY + self.textH if self.showName: self.name.setBrush(QColor('red')) self.name.setText(self.data.name) self.name.setPos(self.mapFromScene(realX, firstY)) if self.showCategory: self.category.setBrush(QColor('green')) self.category.setText( self.data.category if self.data.category else self.data.__class__.__name__) self.category.setPos(self.mapFromScene( realX, secondY if self.showName else firstY)) self.prepareGeometryChange() # Must be called (cf Qt doc) self.update() # Force onscreen redraw after changes.
def shape(self): """ Returns the shape of this item as a QPainterPath in local coordinates. """ path = QPainterPath() path.addRect(self.rect()) if self.isSelected(): for shape in self.handles.values(): path.addEllipse(shape) return path
def fillEllipse( self, painter, x, y, size, brush, ): path = QPainterPath() path.addEllipse(x, y, size, size) painter.fillPath(path, brush)
def __init__(self, pos, edge): ''' Set the original control point - with color, shape ''' path = QPainterPath() path.addEllipse(pos.x() - 5, pos.y() - 5, 10, 10) super(Controlpoint, self).__init__(path, parent=edge) self.setPen(QColor(50, 100, 120, 200)) self.setBrush(QColor(200, 200, 210, 120)) self.setFlags(QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsMovable) self.edge = edge self.hide()
def __init__(self, parent): super(QNEOutputPort, self).__init__(parent) self.parent = parent self.setPen(self.parent.pen()) self.setBrush(self.parent.brush()) radius_ = parent.radius_ path = QPainterPath() path.addEllipse(0, -radius_, 2*radius_, 2*radius_) self.setPath(path)
def __init__(self, parent): super(QNEOutputPort, self).__init__(parent) self.parent = parent self.setPen(self.parent.pen()) self.setBrush(self.parent.brush()) radius_ = parent.radius_ path = QPainterPath() path.addEllipse(0, -radius_, 2 * radius_, 2 * radius_) self.setPath(path)
def setCanConnect(self, hasInput, hasOutput): self.hasInput_ = hasInput self.hasOutput_ = hasOutput if self.hasOutput_: self.outputPort.setVisible(True) else: self.outputPort.setVisible(False) path = QPainterPath() if self.hasInput_: path.addEllipse(0, -self.radius_, 2*self.radius_, 2*self.radius_) else: pass self.setPath(path)
def set_shape(self, width, height): ''' Define the symbol shape ''' circ = min(width, height) path = QPainterPath() path.addEllipse(0, 0, circ, circ) point1 = path.pointAtPercent(0.625) point2 = path.pointAtPercent(0.125) point3 = path.pointAtPercent(0.875) point4 = path.pointAtPercent(0.375) path.moveTo(point1) path.lineTo(point2) path.moveTo(point3) path.lineTo(point4) self.setPath(path) # call Join superclass, otherwise symbol will take Join shape super(Join, self).set_shape(circ, circ)
def setupPaint(self): """Draw the wire segments and handle.""" if not self.data['startIO'] or not self.data['endIO']: self.setPen(QPen(QBrush(QColor(QColor('black'))), 2)) elif self.data['startIO'].value: self.setPen(QPen(QBrush(QColor(QColor('green'))), 2)) else: self.setPen(QPen(QBrush(QColor(QColor('red'))), 2)) path = QPainterPath() path.moveTo(self.data['points'][0]) for p in self.data['points'][1:]: path.lineTo(p) if not self.complete: # An incomplete wire needs a handle path.addEllipse(self.data['points'][-1], self.radius, self.radius) self.setPath(path) self.update()
def setCanConnect(self, hasInput, hasOutput): self.hasInput_ = hasInput self.hasOutput_ = hasOutput if self.hasOutput_: self.outputPort.setVisible(True) else: self.outputPort.setVisible(False) path = QPainterPath() if self.hasInput_: path.addEllipse(0, -self.radius_, 2 * self.radius_, 2 * self.radius_) else: pass self.setPath(path)
def paint(self, painter, option, widget=None): """@reimp @public virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) """ #Q_UNUSED(option) #Q_UNUSED(widget) d = self.__d key = d.hash pm = QPixmap() if not QPixmapCache.find(key, pm): # Set up a convenient path path = QPainterPath() path.setFillRule(Qt.OddEvenFill) path.addEllipse(QPointF(d.actualOuterRadius, d.actualOuterRadius), d.actualOuterRadius, d.actualOuterRadius) path.addEllipse(QPointF(d.actualOuterRadius, d.actualOuterRadius), d.actualInnerRadius, d.actualInnerRadius) nActualDiameter = 2.0 * d.actualOuterRadius pm = QPixmap(nActualDiameter, nActualDiameter) pm.fill(Qt.transparent) p = QPainter(pm) # Draw the ring background p.setPen(Qt.NoPen) p.setBrush(d.backgroundColor) p.setRenderHint(QPainter.Antialiasing) p.drawPath(path) # Draw the ring foreground # TODO: Expose this gradient as Qml Property gradient = QConicalGradient(d.actualOuterRadius, d.actualOuterRadius, 0.0) gradient.setColorAt(0.0, Qt.transparent) gradient.setColorAt(0.05, d.foregroundColor) gradient.setColorAt(0.8, Qt.transparent) p.setBrush(gradient) p.drawPath(path) p.end() QPixmapCache.insert(key, pm) # Draw pixmap at center of item w, h = self.width(), self.height() sz = min(w, h) painter.drawPixmap(0.5 * (w - sz), 0.5 * (h - sz), pm)
def set_shape(self, width, height): ''' Define the shape of the LABEL symbol ''' path = QPainterPath() path.addEllipse(0, height / 2, width / 4, height / 2) path.moveTo(width / 4, height * 3 / 4) path.lineTo(width / 2, height * 3 / 4) # Add arrow head path.moveTo(width / 2 - 5, height * 3 / 4 - 5) path.lineTo(width / 2, height * 3 / 4) path.lineTo(width / 2 - 5, height * 3 / 4 + 5) # Add vertical line in the middle of the symbol path.moveTo(width / 2, 0) path.lineTo(width / 2, height) # Make sure the bounding rect is withing specifications path.moveTo(width, height) self.setPath(path) super(Label, self).set_shape(width, height)
def set_shape(self, width, height): ''' Define the shape of the LABEL symbol ''' #print traceback.print_stack() path = QPainterPath() path.addEllipse(0, height / 2, width / 4, height / 2) path.moveTo(width / 4, height * 3 / 4) path.lineTo(width / 2, height * 3 / 4) # Add arrow head path.moveTo(width / 2 - 5, height * 3 / 4 - 5) path.lineTo(width / 2, height * 3 / 4) path.lineTo(width / 2 - 5, height * 3 / 4 + 5) # Add vertical line in the middle of the symbol path.moveTo(width / 2, 0) path.lineTo(width / 2, height) # Make sure the bounding rect is withing specifications path.moveTo(width, height) self.setPath(path) super(Label, self).set_shape(width, height)
def __init__(self, parent): super(QNEPort, self).__init__(parent) self.label = QGraphicsTextItem(self) self.radius_ = 4 self.margin = 3 path = QPainterPath() path.addEllipse(-self.radius_, -self.radius_, 2*self.radius_, 2*self.radius_); self.setPath(path) self.setPen(QPen(Qt.darkRed)) self.setBrush(Qt.red) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges) self.m_portFlags = 0 self.isOutput_ = False self.m_block = None self.m_connections = []
def animate(self, shapes): self.start_signal.emit() time.sleep(self.start_delay) self.running = True self.ended = False target_radius = [] original_clips = [] centers = [] animating_radius = [] rate_of_change = [] for s in shapes: if self.target.startswith("show"): # Setting max of width or height as radius, ergo "circular" reveal, # not "oval" reveal target = max(s.width, s.height) # Starting from the zero reaching the max animating_radius.append(0) rate_of_change.append((target / self.fps) * (1000 / self.duration)) elif self.target.startswith("hide"): # You know why... target = 0 # Starting from the max reaching the 0 animating_radius.append(max(s.width, s.height)) rate_of_change.append(((target - max(s.width, s.height)) / self.fps) * (1000 / self.duration)) else: raise ValueError("Target should be either 'reveal' or 'hide'") target_radius.append(target) # Getting the original masks; Used in case of cancelation original_clips.append(s.clip) # Center of the shape, considering margin centers.append(QPoint((s.width / 2) + s.margin_left, (s.height / 2) + s.margin_top)) # Calculating the increase rate using the good ol' formula while self.running or self.paused: if self.canceled and not self.paused: for i, s in enumerate(shapes): s.clip = original_clips[i] self.cancel_signal.emit() return elif self.ended: self.end_signal.emit() return elif self.paused: # Handling the pause self.pause_signal.emit() while self.paused: # If you want the current state, pause the # animation and then cancel it print("paused") if self.canceled: print("canceled") self.ended = True self.started = False self.cancel_signal.emit() return self.resume_signal.emit() else: # Setting FPS from the animator time.sleep(1 / self.fps) completed = False for i, s in enumerate(shapes): if rate_of_change[i] > 0: if not animating_radius[i] < target_radius[i]: completed = True else: if not animating_radius[i] > target_radius[i]: completed = True if not completed: animating_radius[i] += rate_of_change[i] path = QPainterPath() if self.target.endswith("circle"): path.addEllipse( QPointF((s.width / 2) + s.margin_left, (s.height / 2) + s.margin_top), animating_radius[i] / 2, animating_radius[i] / 2 ) else: path.addEllipse( QPointF((s.width / 2) + s.margin_left, (s.height / 2) + s.margin_top), animating_radius[i], animating_radius[i] ) s.clip = path s.update() QApplication.processEvents() # No need to check on every iteration, duration is same so # all objects are gonna end at the same time if completed: self.end_signal.emit() self.started = False self.ended = True return
def handleAtPos(self, pos): """Is there an interactive handle where the mouse is?""" if not self.complete: path = QPainterPath() path.addEllipse(self.data['points'][-1], self.radius, self.radius) return path.contains(pos)
def updateLeader(self): """ TOWRITE """ arrowStyle = Closed # int # TODO: Make this customizable arrowStyleAngle = 15.0 # qreal # TODO: Make this customizable arrowStyleLength = 1.0 # qreal # TODO: Make this customizable self.lineStyleAngle = 45.0 # qreal # TODO: Make this customizable self.lineStyleLength = 1.0 # qreal # TODO: Make this customizable lyne = self.line() # QLineF angle = lyne.angle() # qreal ap0 = lyne.p1() # QPointF lp0 = lyne.p2() # QPointF # Arrow lynePerp = QLineF(lyne.pointAt(arrowStyleLength / lyne.length()), lp0) lynePerp.setAngle(angle + 90) lyne1 = QLineF(ap0, lp0) lyne2 = QLineF(ap0, lp0) lyne1.setAngle(angle + arrowStyleAngle) lyne2.setAngle(angle - arrowStyleAngle) # ap1 = QPointF() # ap2 = QPointF() _, ap1 = lynePerp.intersect(lyne1) _, ap2 = lynePerp.intersect(lyne2) # Math Diagram # .(ap1) .(lp1) # /| /| # / | / | # / | / | # / | / | # / | / | # / | / | # / | / | # / | / | # /+(aSA) | /+(lSA) | # (ap0)./__(aSL)__|__________(lp0)./__(lSL)__| # \ -(aSA) | \ -(lSA) | # \ | \ | # \ | \ | # \ | \ | # \ | \ | # \ | \ | # \ | \ | # \ | \ | # \ | \ | # \| \| # .(ap2) .(lp2) if arrowStyle == Open: arrowStylePath = QPainterPath() arrowStylePath.moveTo(ap1) arrowStylePath.lineTo(ap0) arrowStylePath.lineTo(ap2) arrowStylePath.lineTo(ap0) arrowStylePath.lineTo(ap1) elif arrowStyle == Closed: arrowStylePath = QPainterPath() arrowStylePath.moveTo(ap1) arrowStylePath.lineTo(ap0) arrowStylePath.lineTo(ap2) arrowStylePath.lineTo(ap1) elif arrowStyle == Dot: arrowStylePath = QPainterPath() arrowStylePath.addEllipse(ap0, arrowStyleLength, arrowStyleLength) elif arrowStyle == Box: arrowStylePath = QPainterPath() side = QLineF(ap1, ap2).length() # qreal ar0 = QRectF(0, 0, side, side) ar0.moveCenter(ap0) arrowStylePath.addRect(ar0) elif arrowStyle == Tick: pass #TODO/PORT# Is this supposed to be empty? lineStylePath = QPainterPath() lineStylePath.moveTo(ap0) lineStylePath.lineTo(lp0)
class PlugItem(QGraphicsPathItem): """Graphical wrapper around the engine Plug class.""" bodyW = 30 """The width of the body of plugs.""" pinW = 10 """The width of the pin part of plugs.""" def __init__(self, plug): super(PlugItem, self).__init__() self.data = plug """The real info. The class PlugItem is just a graphical container around it. data is saved / loaded to / from file. """ self.showName = False """Is the name of the item shown on screen?""" self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemIsSelectable) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) self.setAcceptsHoverEvents(True) self.setPen(QPen(QBrush(QColor(QColor('black'))), 2)) # This path is needed at each mouse over event, to check if # the mouse is over a pin. We save it as an instance field, # rather than recreate it at each event. self.pinPath = QPainterPath() if self.data.isInput: self.pinPath.addEllipse( self.bodyW + self.pinW / 2, self.bodyW / 2, self.pinW, self.pinW) else: self.pinPath.addEllipse( self.pinW / 2, self.bodyW / 2, self.pinW, self.pinW) f = QFont('Times', 12, 75) # Name and value text labels. self.name = QGraphicsSimpleTextItem(self) # that won't rotate when the PlugItem is rotated by the user. self.name.setFlag(QGraphicsItem.ItemIgnoresTransformations) self.name.setText(self.data.name) self.name.setFont(f) self.value = QGraphicsSimpleTextItem(self) self.value.setFlag(QGraphicsItem.ItemIgnoresTransformations) # Or else value would get the clicks, instead of the PlugItem. self.value.setFlag(QGraphicsItem.ItemStacksBehindParent) self.value.setFont(f) self.setupPaint() def handleAtPos(self, pos): """Is there an interactive handle where the mouse is? Also return the Plug under this handle. """ return self.data if self.pinPath.contains(pos) else None def itemChange(self, change, value): """Warning view it will soon have to correct pos.""" if change == QGraphicsItem.ItemPositionHasChanged: # Restart till we stop moving. self.scene().views()[0].timer.start() return QGraphicsItem.itemChange(self, change, value) def setAndUpdate(self): """Change the undelying plug's value, and force updates items.""" self.data.set(not self.data.value) for i in self.scene().items(): if isinstance(i, PlugItem) or isinstance(i, WireItem): i.setupPaint() def setNameVisibility(self, isVisible): """Shows/Hide the item name in the graphical view.""" self.showName = isVisible self.setupPaint() def setupPaint(self): """Offscreen rather than onscreen redraw (few changes).""" path = QPainterPath() if self.data.isInput: path.addEllipse( self.pinW / 2, self.pinW / 2, self.bodyW, self.bodyW) else: path.addRect( 3 * self.pinW / 2 + 1, self.pinW / 2, self.bodyW, self.bodyW) path.addPath(self.pinPath) self.setPath(path) self.name.setVisible(self.showName) self.name.setText(self.data.name) br = self.mapToScene(self.boundingRect()) w = self.boundingRect().width() h = self.boundingRect().height() realX = min([i.x() for i in br]) realY = min([i.y() for i in br]) self.name.setPos(self.mapFromScene( realX, realY + (w if self.rotation() % 180 else h) + 1)) self.value.setText( str(int(self.data.value)) if self.data.value is not None else 'E') self.value.setPos(self.mapFromScene(realX + w / 3, realY + h / 3)) self.value.setBrush(QColor('green' if self.data.value else 'red')) self.update() # Force onscreen redraw after changes.