class NodeBodyItem(GraphicsPathObject): """ The central part (body) of the `NodeItem`. """ def __init__(self, parent=None): GraphicsPathObject.__init__(self, parent) assert(isinstance(parent, NodeItem)) self.__processingState = 0 self.__progress = -1 self.__animationEnabled = False self.__isSelected = False self.__hasFocus = False self.__hover = False self.__shapeRect = QRectF(-10, -10, 20, 20) self.setAcceptHoverEvents(True) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.setPen(QPen(Qt.NoPen)) self.setPalette(default_palette()) self.shadow = QGraphicsDropShadowEffect( blurRadius=3, color=QColor(SHADOW_COLOR), offset=QPointF(0, 0), ) self.setGraphicsEffect(self.shadow) self.shadow.setEnabled(True) self.__blurAnimation = QPropertyAnimation(self.shadow, "blurRadius", self) self.__blurAnimation.setDuration(100) self.__blurAnimation.finished.connect(self.__on_finished) self.__pingAnimation = QPropertyAnimation(self, "scale", self) self.__pingAnimation.setDuration(250) self.__pingAnimation.setKeyValues([(0.0, 1.0), (0.5, 1.1), (1.0, 1.0)]) # TODO: The body item should allow the setting of arbitrary painter # paths (for instance rounded rect, ...) def setShapeRect(self, rect): """ Set the item's shape `rect`. The item should be confined within this rect. """ path = QPainterPath() path.addEllipse(rect) self.setPath(path) self.__shapeRect = rect def setPalette(self, palette): """ Set the body color palette (:class:`QPalette`). """ self.palette = palette self.__updateBrush() def setAnimationEnabled(self, enabled): """ Set the node animation enabled. """ if self.__animationEnabled != enabled: self.__animationEnabled = enabled def setProcessingState(self, state): """ Set the processing state of the node. """ if self.__processingState != state: self.__processingState = state if not state and self.__animationEnabled: self.ping() def setProgress(self, progress): """ Set the progress indicator state of the node. `progress` should be a number between 0 and 100. """ self.__progress = progress self.update() def ping(self): """ Trigger a 'ping' animation. """ animation_restart(self.__pingAnimation) def hoverEnterEvent(self, event): self.__hover = True self.__updateShadowState() return GraphicsPathObject.hoverEnterEvent(self, event) def hoverLeaveEvent(self, event): self.__hover = False self.__updateShadowState() return GraphicsPathObject.hoverLeaveEvent(self, event) def paint(self, painter, option, widget): """ Paint the shape and a progress meter. """ # Let the default implementation draw the shape if option.state & QStyle.State_Selected: # Prevent the default bounding rect selection indicator. option.state = option.state ^ QStyle.State_Selected GraphicsPathObject.paint(self, painter, option, widget) if self.__progress >= 0: # Draw the progress meter over the shape. # Set the clip to shape so the meter does not overflow the shape. painter.setClipPath(self.shape(), Qt.ReplaceClip) color = self.palette.color(QPalette.ButtonText) pen = QPen(color, 5) painter.save() painter.setPen(pen) painter.setRenderHints(QPainter.Antialiasing) span = int(self.__progress * 57.60) painter.drawArc(self.__shapeRect, 90 * 16, -span) painter.restore() def __updateShadowState(self): if self.__hasFocus: color = QColor(FOCUS_OUTLINE_COLOR) self.setPen(QPen(color, 1.5)) else: self.setPen(QPen(Qt.NoPen)) radius = 3 enabled = False if self.__isSelected: enabled = True radius = 7 if self.__hover: radius = 17 enabled = True if enabled and not self.shadow.isEnabled(): self.shadow.setEnabled(enabled) if self.__animationEnabled: if self.__blurAnimation.state() == QPropertyAnimation.Running: self.__blurAnimation.pause() self.__blurAnimation.setStartValue(self.shadow.blurRadius()) self.__blurAnimation.setEndValue(radius) self.__blurAnimation.start() else: self.shadow.setBlurRadius(radius) def __updateBrush(self): palette = self.palette if self.__isSelected: cg = QPalette.Active else: cg = QPalette.Inactive palette.setCurrentColorGroup(cg) c1 = palette.color(QPalette.Light) c2 = palette.color(QPalette.Button) grad = radial_gradient(c2, c1) self.setBrush(QBrush(grad)) # TODO: The selected and focus states should be set using the # QStyle flags (State_Selected. State_HasFocus) def setSelected(self, selected): """ Set the `selected` state. .. note:: The item does not have `QGraphicsItem.ItemIsSelectable` flag. This property is instead controlled by the parent NodeItem. """ self.__isSelected = selected self.__updateBrush() def setHasFocus(self, focus): """ Set the `has focus` state. .. note:: The item does not have `QGraphicsItem.ItemIsFocusable` flag. This property is instead controlled by the parent NodeItem. """ self.__hasFocus = focus self.__updateShadowState() def __on_finished(self): if self.shadow.blurRadius() == 0: self.shadow.setEnabled(False)
class NodeBodyItem(GraphicsPathObject): """ The central part (body) of the `NodeItem`. """ def __init__(self, parent=None): GraphicsPathObject.__init__(self, parent) assert (isinstance(parent, NodeItem)) self.__processingState = 0 self.__progress = -1 self.__animationEnabled = False self.__isSelected = False self.__hasFocus = False self.__hover = False self.__shapeRect = QRectF(-10, -10, 20, 20) self.setAcceptHoverEvents(True) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.setPen(QPen(Qt.NoPen)) self.setPalette(default_palette()) self.shadow = QGraphicsDropShadowEffect( blurRadius=3, color=QColor(SHADOW_COLOR), offset=QPointF(0, 0), ) self.setGraphicsEffect(self.shadow) self.shadow.setEnabled(True) self.__blurAnimation = QPropertyAnimation(self.shadow, "blurRadius", self) self.__blurAnimation.setDuration(100) self.__blurAnimation.finished.connect(self.__on_finished) self.__pingAnimation = QPropertyAnimation(self, "scale", self) self.__pingAnimation.setDuration(250) self.__pingAnimation.setKeyValues([(0.0, 1.0), (0.5, 1.1), (1.0, 1.0)]) # TODO: The body item should allow the setting of arbitrary painter # paths (for instance rounded rect, ...) def setShapeRect(self, rect): """ Set the item's shape `rect`. The item should be confined within this rect. """ path = QPainterPath() path.addEllipse(rect) self.setPath(path) self.__shapeRect = rect def setPalette(self, palette): """ Set the body color palette (:class:`QPalette`). """ self.palette = palette self.__updateBrush() def setAnimationEnabled(self, enabled): """ Set the node animation enabled. """ if self.__animationEnabled != enabled: self.__animationEnabled = enabled def setProcessingState(self, state): """ Set the processing state of the node. """ if self.__processingState != state: self.__processingState = state if not state and self.__animationEnabled: self.ping() def setProgress(self, progress): """ Set the progress indicator state of the node. `progress` should be a number between 0 and 100. """ self.__progress = progress self.update() def ping(self): """ Trigger a 'ping' animation. """ animation_restart(self.__pingAnimation) def hoverEnterEvent(self, event): self.__hover = True self.__updateShadowState() return GraphicsPathObject.hoverEnterEvent(self, event) def hoverLeaveEvent(self, event): self.__hover = False self.__updateShadowState() return GraphicsPathObject.hoverLeaveEvent(self, event) def paint(self, painter, option, widget): """ Paint the shape and a progress meter. """ # Let the default implementation draw the shape if option.state & QStyle.State_Selected: # Prevent the default bounding rect selection indicator. option.state = option.state ^ QStyle.State_Selected GraphicsPathObject.paint(self, painter, option, widget) if self.__progress >= 0: # Draw the progress meter over the shape. # Set the clip to shape so the meter does not overflow the shape. painter.setClipPath(self.shape(), Qt.ReplaceClip) color = self.palette.color(QPalette.ButtonText) pen = QPen(color, 5) painter.save() painter.setPen(pen) painter.setRenderHints(QPainter.Antialiasing) span = max(1, int(self.__progress * 57.60)) painter.drawArc(self.__shapeRect, 90 * 16, -span) painter.restore() def __updateShadowState(self): if self.__hasFocus: color = QColor(FOCUS_OUTLINE_COLOR) self.setPen(QPen(color, 1.5)) else: self.setPen(QPen(Qt.NoPen)) radius = 3 enabled = False if self.__isSelected: enabled = True radius = 7 if self.__hover: radius = 17 enabled = True if enabled and not self.shadow.isEnabled(): self.shadow.setEnabled(enabled) if self.__animationEnabled: if self.__blurAnimation.state() == QPropertyAnimation.Running: self.__blurAnimation.pause() self.__blurAnimation.setStartValue(self.shadow.blurRadius()) self.__blurAnimation.setEndValue(radius) self.__blurAnimation.start() else: self.shadow.setBlurRadius(radius) def __updateBrush(self): palette = self.palette if self.__isSelected: cg = QPalette.Active else: cg = QPalette.Inactive palette.setCurrentColorGroup(cg) c1 = palette.color(QPalette.Light) c2 = palette.color(QPalette.Button) grad = radial_gradient(c2, c1) self.setBrush(QBrush(grad)) # TODO: The selected and focus states should be set using the # QStyle flags (State_Selected. State_HasFocus) def setSelected(self, selected): """ Set the `selected` state. .. note:: The item does not have `QGraphicsItem.ItemIsSelectable` flag. This property is instead controlled by the parent NodeItem. """ self.__isSelected = selected self.__updateBrush() def setHasFocus(self, focus): """ Set the `has focus` state. .. note:: The item does not have `QGraphicsItem.ItemIsFocusable` flag. This property is instead controlled by the parent NodeItem. """ self.__hasFocus = focus self.__updateShadowState() def __on_finished(self): if self.shadow.blurRadius() == 0: self.shadow.setEnabled(False)