def __activeControlMoved(self, pos): # The active control point has moved, update the control # rectangle control = self.__activeControl pos = control.pos() rect = QRectF(self.__rect) margins = self.__margins # TODO: keyboard modifiers and constraints. anchor = control.anchor() if anchor & ControlPoint.Top: rect.setTop(pos.y() + margins.top()) elif anchor & ControlPoint.Bottom: rect.setBottom(pos.y() - margins.bottom()) if anchor & ControlPoint.Left: rect.setLeft(pos.x() + margins.left()) elif anchor & ControlPoint.Right: rect.setRight(pos.x() - margins.right()) changed = self.__rect != rect self.blockSignals(True) self.setRect(rect) self.blockSignals(False) if changed: self.rectEdited.emit(rect.normalized())
def boundingRect(self): extra = (SfLine.ARROW_WIDTH + SfLine.ARROW_HEAD_LENGTH)/2.0 + 5 size = QSizeF(self.line().p2().x() - self.line().p1().x(), self.line().p2().y() - self.line().p1().y()) rect = QRectF(self.line().p1(), size) rect = rect.normalized() rect.adjust(-extra, -extra, extra, extra) return rect
class RectangleSelectionAction(UserInteraction): """ Select items in the scene using a Rectangle selection """ def __init__(self, document, *args, **kwargs): UserInteraction.__init__(self, document, *args, **kwargs) # The initial selection at drag start self.initial_selection = None # Selection when last updated in a mouseMoveEvent self.last_selection = None # A selection rect (`QRectF`) self.selection_rect = None # Keyboard modifiers self.modifiers = 0 def mousePressEvent(self, event): pos = event.scenePos() any_item = self.scene.item_at(pos) if not any_item and event.button() & Qt.LeftButton: self.modifiers = event.modifiers() self.selection_rect = QRectF(pos, QSizeF(0, 0)) self.rect_item = QGraphicsRectItem( self.selection_rect.normalized() ) self.rect_item.setPen( QPen(QBrush(QColor(51, 153, 255, 192)), 0.4, Qt.SolidLine, Qt.RoundCap) ) self.rect_item.setBrush( QBrush(QColor(168, 202, 236, 192)) ) self.rect_item.setZValue(-100) # Clear the focus if necessary. if not self.scene.stickyFocus(): self.scene.clearFocus() if not self.modifiers & Qt.ControlModifier: self.scene.clearSelection() event.accept() return True else: self.cancel(self.ErrorReason) return False def mouseMoveEvent(self, event): if not self.rect_item.scene(): # Add the rect item to the scene when the mouse moves. self.scene.addItem(self.rect_item) self.update_selection(event) return True def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: if self.initial_selection is None: # A single click. self.scene.clearSelection() else: self.update_selection(event) self.end() return True def update_selection(self, event): """ Update the selection rectangle from a QGraphicsSceneMouseEvent `event` instance. """ if self.initial_selection is None: self.initial_selection = set(self.scene.selectedItems()) self.last_selection = self.initial_selection pos = event.scenePos() self.selection_rect = QRectF(self.selection_rect.topLeft(), pos) # Make sure the rect_item does not cause the scene rect to grow. rect = self._bound_selection_rect(self.selection_rect.normalized()) # Need that 0.5 constant otherwise the sceneRect will still # grow (anti-aliasing correction by QGraphicsScene?) pw = self.rect_item.pen().width() + 0.5 self.rect_item.setRect(rect.adjusted(pw, pw, -pw, -pw)) selected = self.scene.items(self.selection_rect.normalized(), Qt.IntersectsItemShape, Qt.AscendingOrder) selected = set([item for item in selected if \ item.flags() & Qt.ItemIsSelectable]) if self.modifiers & Qt.ControlModifier: for item in selected | self.last_selection | \ self.initial_selection: item.setSelected( (item in selected) ^ (item in self.initial_selection) ) else: for item in selected.union(self.last_selection): item.setSelected(item in selected) self.last_selection = set(self.scene.selectedItems()) def end(self): self.initial_selection = None self.last_selection = None self.modifiers = 0 self.rect_item.hide() if self.rect_item.scene() is not None: self.scene.removeItem(self.rect_item) UserInteraction.end(self) def viewport_rect(self): """ Return the bounding rect of the document's viewport on the scene. """ view = self.document.view() vsize = view.viewport().size() viewportrect = QRect(0, 0, vsize.width(), vsize.height()) return view.mapToScene(viewportrect).boundingRect() def _bound_selection_rect(self, rect): """ Bound the selection `rect` to a sensible size. """ srect = self.scene.sceneRect() vrect = self.viewport_rect() maxrect = srect.united(vrect) return rect.intersected(maxrect)
class RectangleSelectionAction(UserInteraction): """ Select items in the scene using a Rectangle selection """ def __init__(self, document, *args, **kwargs): UserInteraction.__init__(self, document, *args, **kwargs) # The initial selection at drag start self.initial_selection = None # Selection when last updated in a mouseMoveEvent self.last_selection = None # A selection rect (`QRectF`) self.selection_rect = None # Keyboard modifiers self.modifiers = 0 def mousePressEvent(self, event): pos = event.scenePos() any_item = self.scene.item_at(pos) if not any_item and event.button() & Qt.LeftButton: self.modifiers = event.modifiers() self.selection_rect = QRectF(pos, QSizeF(0, 0)) self.rect_item = QGraphicsRectItem( self.selection_rect.normalized()) self.rect_item.setPen( QPen(QBrush(QColor(51, 153, 255, 192)), 0.4, Qt.SolidLine, Qt.RoundCap)) self.rect_item.setBrush(QBrush(QColor(168, 202, 236, 192))) self.rect_item.setZValue(-100) # Clear the focus if necessary. if not self.scene.stickyFocus(): self.scene.clearFocus() if not self.modifiers & Qt.ControlModifier: self.scene.clearSelection() event.accept() return True else: self.cancel(self.ErrorReason) return False def mouseMoveEvent(self, event): if not self.rect_item.scene(): # Add the rect item to the scene when the mouse moves. self.scene.addItem(self.rect_item) self.update_selection(event) return True def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: if self.initial_selection is None: # A single click. self.scene.clearSelection() else: self.update_selection(event) self.end() return True def update_selection(self, event): """ Update the selection rectangle from a QGraphicsSceneMouseEvent `event` instance. """ if self.initial_selection is None: self.initial_selection = set(self.scene.selectedItems()) self.last_selection = self.initial_selection pos = event.scenePos() self.selection_rect = QRectF(self.selection_rect.topLeft(), pos) # Make sure the rect_item does not cause the scene rect to grow. rect = self._bound_selection_rect(self.selection_rect.normalized()) # Need that 0.5 constant otherwise the sceneRect will still # grow (anti-aliasing correction by QGraphicsScene?) pw = self.rect_item.pen().width() + 0.5 self.rect_item.setRect(rect.adjusted(pw, pw, -pw, -pw)) selected = self.scene.items(self.selection_rect.normalized(), Qt.IntersectsItemShape, Qt.AscendingOrder) selected = set([item for item in selected if \ item.flags() & Qt.ItemIsSelectable]) if self.modifiers & Qt.ControlModifier: for item in selected | self.last_selection | \ self.initial_selection: item.setSelected((item in selected) ^ (item in self.initial_selection)) else: for item in selected.union(self.last_selection): item.setSelected(item in selected) self.last_selection = set(self.scene.selectedItems()) def end(self): self.initial_selection = None self.last_selection = None self.modifiers = 0 self.rect_item.hide() if self.rect_item.scene() is not None: self.scene.removeItem(self.rect_item) UserInteraction.end(self) def viewport_rect(self): """ Return the bounding rect of the document's viewport on the scene. """ view = self.document.view() vsize = view.viewport().size() viewportrect = QRect(0, 0, vsize.width(), vsize.height()) return view.mapToScene(viewportrect).boundingRect() def _bound_selection_rect(self, rect): """ Bound the selection `rect` to a sensible size. """ srect = self.scene.sceneRect() vrect = self.viewport_rect() maxrect = srect.united(vrect) return rect.intersected(maxrect)
class EFrameLayout(QGraphicsPolygonItem): def __init__(self, parent=None, scene=None): super(EFrameLayout, self).__init__(parent, scene) self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setAcceptsHoverEvents(True) self.__name = QGraphicsTextItem() self.__name.setPlainText('Frame Layout') self.__handleWidth = 500 self.__handleHeight = 20 self.__separator = 5 self.__boundExtra = 3 + self.pen().width() self.__handleRect = QRectF( 0.0 , 0.0 , self.__handleWidth, self.__handleHeight ) self.__controlsBound = 0.0 self.__controls = [] self.__isDefaultPen = False self.__isCollapsed = True self.__pens = { 0: EDraw.EColor.DefaultLeaveHoverPen, 1: EDraw.EColor.DefaultEnterHoverPen } self.setPen(self.__pens[self.__isDefaultPen]) @property def Label(self): return self.__name.toPlainText() @Label.setter def Label(self, label): self.__name.setPlainText( str( label ) ) @property def Width(self): return self.__handleWidth @Width.setter def Width(self, width): self.__handleWidth = width self.updateGeometry() def toggleContentVisibility(self): if not len(self.__controls): return if self.__controls[0].isVisible(): [ control.hide() for control in self.__controls ] self.__isCollapsed = True return [ control.show() for control in self.__controls ] self.__isCollapsed = False def updateGeometry(self): self.__handleRect = QRectF( 0.0 , 0.0 , self.__handleWidth, self.__handleHeight ) if not len(self.__controls) or self.__isCollapsed: self.__controlsBound = 0.0 return self.__controlsBound = 0.0 self.__controlsBound = self.__separator self.__controlsBound += sum( [ control.boundingRect().height() + self.__separator for control in self.__controls ] ) step = self.__handleHeight + self.__separator * 2 for control in self.__controls: control.Name.setTextWidth( self.__controlNameWidth ) control.Width = self.__handleRect.normalized().adjusted( 5.0, 0.0, -5.0, 0.0 ).width() control.setPos( 5.0 , step ) step += control.boundingRect().height() + self.__separator def addControl(self, control): if not isinstance(control, EControlsGroup): raise AttributeError self.__controls.append(control) control.setParentItem(self) control.setFlag(QGraphicsItem.ItemIsMovable, False) self.__controlNameWidth = max([ control.Name.boundingRect().width() for control in self.__controls ]) + 5 self.__isCollapsed = False self.updateGeometry() def clear(self): [ control.setParentItem(None) for control in self.__controls ] self.__controls = [] self.updateGeometry() def mouseDoubleClickEvent(self, mouseEvent): QGraphicsPolygonItem.mouseDoubleClickEvent(self, mouseEvent) self.toggleContentVisibility() self.updateGeometry() def shape(self): return QGraphicsItem.shape(self) def boundingRect(self): return self.__handleRect.normalized().adjusted( 0.0, 0.0, 0.0, self.__controlsBound ) def paint(self, painter, option, widget=None): if not self.__isCollapsed: painter.setPen( QPen( QColor( 0, 0, 0, 50 ), 2 , Qt.SolidLine ) ) painter.setBrush( QColor( 0, 0, 0, 50 ) ) painter.drawRect( self.boundingRect().adjusted( self.pen().width(), self.__handleHeight , -self.pen().width(), 0.0 ) ) painter.setPen(self.pen()) painter.setBrush( EDraw.EColor.LinearGradient( self.__handleRect, Qt.darkGray ) ) #painter.drawPolygon( QPolygonF( self.__handleRect.normalized().adjusted(-self.__boundExtra, 0.0, self.__boundExtra, 0.0 ) ) ) painter.drawPolygon( QPolygonF( self.__handleRect ) ) painter.setPen(Qt.lightGray) r = QRectF( 0.0, 0.0, self.__name.boundingRect().width(), self.__handleRect.height() ) painter.drawText( r, Qt.AlignCenter, self.__name.toPlainText() )
class ENode(QGraphicsObject): onMove = pyqtSignal() onPress = pyqtSignal() kGuiPropertyId = EObject() kGuiPropertyName = EObject() kGuiAttribute = EObject() kGuiAttributeId = EObject() kGuiAttributeType = EObject() kGuiAttributePlug = EObject() kGuiAttributeParent = EObject() kGuiAttributeParentName = EObject() kGuiAttributeLongName = EObject() kGuiAttributeShortName = EObject() def __init__(self, eNodeHandle): QGraphicsObject.__init__(self) self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setAcceptsHoverEvents(True) self.__isDefaultPen = False self.__pen = None self.__pens = {0: EDraw.EColor.DefaultLeaveHoverPen, 1: EDraw.EColor.DefaultEnterHoverPen} self.setPen(self.__pens[self.__isDefaultPen]) self.__font = EDraw.DefaultNodeFont self.__nodeHandle = eNodeHandle self.__nodeHandle.Message.connect(self.__messageFilter) self.__name = self.__nodeHandle.__class__.__name__ self.__attrRect = QRectF(0, 0, 15, 15) self.__titleRect = QRectF(0, 0, 135, 20) self.__textSpace = QRectF(self.__attrRect.width() + self.pen().width(), 0.0, (self.__titleRect.width() - self.__attrRect.width() * 2 - self.pen().width() * 2) / 2, self.__attrRect.height()) self.buildAttributes() self.__height = max([self.__in_attr_step, self.__out_attr_step]) def __messageFilter(self, message): if message.matches(self.__nodeHandle.kMessageAttributeAdded): self.__buildAttribute(message.getData()) if message.matches(self.__nodeHandle.kMessageAttributeRemoved): print message.getData() self.__attributes = {} self.__properties = {} self.buildAttributes() def __getAttrShortName(self, attributeName): fm = QFontMetrics(self.__font) shortName = '' if fm.width(attributeName) > self.__textSpace.width(): for x in range(len(attributeName) + 1): if fm.width(shortName) > int(self.__textSpace.width()) - 10: return shortName shortName = attributeName[:x] + '...' return attributeName def __getAttributePosition(self, opposite=False): attr_x_pos = 0 if opposite: attr_x_pos = self.__titleRect.width() - self.__attrRect.width() rect = self.__attrRect.translated(QPointF(attr_x_pos, self.__out_attr_step)) point = QPointF((rect.topRight() + rect.bottomRight()) / 2) point.setX(point.x() + self.pen().width() * 2) self.__out_attr_step += self.__attrRect.width() + self.pen().width() return [rect, point] rect = self.__attrRect.translated(QPointF(attr_x_pos, self.__in_attr_step)) point = QPointF((rect.topLeft() + rect.bottomLeft()) / 2) point.setX(point.x() - self.pen().width() * 2) self.__in_attr_step += self.__attrRect.width() + self.pen().width() return [rect, point] def __buildAttribute(self, attribute, opposite=False): """ if not attribute.Type.matches(EAttribute.kTypeGenericInput) and not attribute.Type.matches(EAttribute.kTypeGenericOutput): self.__properties[attribute.Id] = dict({self.kGuiPropertyId: attribute.Id, self.kGuiPropertyName: attribute.Name}) return """ data = self.__getAttributePosition(opposite) self.__attributes[data[0]] = dict({self.kGuiAttributeId: attribute.Id, self.kGuiAttributeType: attribute.Type, self.kGuiAttributeParent: self, self.kGuiAttributePlug: data[1], self.kGuiAttributeLongName: attribute.Name, self.kGuiAttributeShortName: self.__getAttrShortName(attribute.Name)}) self.__height = max([self.__in_attr_step, self.__out_attr_step]) self.update() self.onMove.emit() def buildAttributes(self): self.__attributes = {} self.__properties = {} self.__out_attr_step = self.__titleRect.height() + self.pen().width() self.__in_attr_step = self.__titleRect.height() + self.pen().width() for eddAttr in self.__nodeHandle.lsInputAttributes(): self.__buildAttribute(eddAttr) for eddAttr in self.__nodeHandle.lsOutputAttributes(): self.__buildAttribute(eddAttr, True) self.update() def __toggleHighlight(self): if self.__isDefaultPen: self.__isDefaultPen = False self.setPen(self.__pens[self.__isDefaultPen]) #self.setZValue(0.0) return self.__isDefaultPen = True self.setPen(self.__pens[self.__isDefaultPen]) #self.setZValue(-2.0) def mute(self, uuid): pass @property def Id(self): return self.__nodeHandle.Id @property def Name(self): return self.__name @Name.setter def Name(self, newName): self.__name = newName def pen(self): return self.__pen def setPen(self, pen): if not isinstance(pen, QPen): raise AttributeError self.__pen = pen @property def Font(self): return self.__font @Font.setter def Font(self, QFont): self.__font = QFont @property def Handle(self): return self.__nodeHandle @property def Properties(self): return self.__properties def mapFromPoint(self, QPoint): for attrRect, attrValues in self.__attributes.iteritems(): if attrRect.contains(self.mapFromScene(QPoint)): return attrValues[self.kGuiAttributeId], QPoint return self.__nodeHandle.Id, None def mapFromId(self, attrId): for attrValue in self.__attributes.itervalues(): if attrValue[self.kGuiAttributeId] == attrId: return attrValue return None def hoverEnterEvent(self, mouseEvent): QGraphicsObject.hoverEnterEvent(self, mouseEvent) self.__toggleHighlight() def hoverMoveEvent(self, mouseEvent): QGraphicsObject.hoverMoveEvent(self, mouseEvent) def hoverLeaveEvent(self, mouseEvent): QGraphicsObject.hoverLeaveEvent(self, mouseEvent) self.__toggleHighlight() def mousePressEvent(self, mouseEvent): QGraphicsObject.mousePressEvent(self, mouseEvent) #if mouseEvent.button() == Qt.RightButton: #print self.mapFromPoint(mouseEvent.scenePos()) self.onPress.emit() def mouseDoubleClickEvent(self, mouseEvent): QGraphicsObject.mouseDoubleClickEvent(self, mouseEvent) #self.__nodeHandle.compute() def mouseMoveEvent(self, mouseEvent): QGraphicsObject.mouseMoveEvent(self, mouseEvent) self.onMove.emit() def boundingRect(self): extra = self.pen().width() return self.__titleRect.normalized().adjusted(-extra, -extra, extra, (self.__height - self.__titleRect.height()) + extra) def shape(self): return QGraphicsItem.shape(self) def paint(self, painter, option, widget=None): painter.setBrush(EDraw.EColor.DefaultNodeFillColor) painter.setPen(self.pen()) painter.drawRect(self.boundingRect()) painter.setPen(Qt.NoPen) painter.setBrush(EDraw.EColor.DefaultTitleColor) painter.drawRect(self.__titleRect) painter.setPen(EDraw.EColor.DefaultTitleTextColor) painter.drawText(self.__titleRect, Qt.AlignCenter, self.__name) painter.setPen(Qt.NoPen) painter.setBrush(EDraw.EColor.DefaultAttributeFillColor) for rect in self.__attributes.iterkeys(): painter.drawRect(rect) painter.setBrush(Qt.darkGray) for rect in self.__attributes.iterkeys(): painter.drawPolygon(EDraw.Circle(rect.height() / 3, 3).translated(rect.center())) painter.setPen(QPen(QColor(43, 43, 43), 1.0, Qt.SolidLine)) painter.setBrush(Qt.NoBrush) for attrRect, attrValues in self.__attributes.iteritems(): attrNameRect = self.__textSpace.translated(attrRect.topLeft()) align = Qt.AlignLeft if attrRect.topLeft().x() > 0: attrNameRect = self.__textSpace.translated( QPointF((self.__titleRect.width() / 2) - (attrRect.width() + self.pen().width()), attrRect.topLeft().y())) align = Qt.AlignRight painter.drawText(attrNameRect, align, attrValues[self.kGuiAttributeShortName])
class EFrameLayout(QGraphicsPolygonItem): def __init__(self, parent=None, scene=None): super(EFrameLayout, self).__init__(parent, scene) self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setAcceptsHoverEvents(True) self.__name = QGraphicsTextItem() self.__name.setPlainText('Frame Layout') self.__handleWidth = 500 self.__handleHeight = 20 self.__separator = 5 self.__boundExtra = 3 + self.pen().width() self.__handleRect = QRectF(0.0, 0.0, self.__handleWidth, self.__handleHeight) self.__controlsBound = 0.0 self.__controls = [] self.__isDefaultPen = False self.__isCollapsed = True self.__pens = { 0: EDraw.EColor.DefaultLeaveHoverPen, 1: EDraw.EColor.DefaultEnterHoverPen } self.setPen(self.__pens[self.__isDefaultPen]) @property def Label(self): return self.__name.toPlainText() @Label.setter def Label(self, label): self.__name.setPlainText(str(label)) @property def Width(self): return self.__handleWidth @Width.setter def Width(self, width): self.__handleWidth = width self.updateGeometry() def toggleContentVisibility(self): if not len(self.__controls): return if self.__controls[0].isVisible(): [control.hide() for control in self.__controls] self.__isCollapsed = True return [control.show() for control in self.__controls] self.__isCollapsed = False def updateGeometry(self): self.__handleRect = QRectF(0.0, 0.0, self.__handleWidth, self.__handleHeight) if not len(self.__controls) or self.__isCollapsed: self.__controlsBound = 0.0 return self.__controlsBound = 0.0 self.__controlsBound = self.__separator self.__controlsBound += sum([ control.boundingRect().height() + self.__separator for control in self.__controls ]) step = self.__handleHeight + self.__separator * 2 for control in self.__controls: control.Name.setTextWidth(self.__controlNameWidth) control.Width = self.__handleRect.normalized().adjusted( 5.0, 0.0, -5.0, 0.0).width() control.setPos(5.0, step) step += control.boundingRect().height() + self.__separator def addControl(self, control): if not isinstance(control, EControlsGroup): raise AttributeError self.__controls.append(control) control.setParentItem(self) control.setFlag(QGraphicsItem.ItemIsMovable, False) self.__controlNameWidth = max([ control.Name.boundingRect().width() for control in self.__controls ]) + 5 self.__isCollapsed = False self.updateGeometry() def clear(self): [control.setParentItem(None) for control in self.__controls] self.__controls = [] self.updateGeometry() def mouseDoubleClickEvent(self, mouseEvent): QGraphicsPolygonItem.mouseDoubleClickEvent(self, mouseEvent) self.toggleContentVisibility() self.updateGeometry() def shape(self): return QGraphicsItem.shape(self) def boundingRect(self): return self.__handleRect.normalized().adjusted(0.0, 0.0, 0.0, self.__controlsBound) def paint(self, painter, option, widget=None): if not self.__isCollapsed: painter.setPen(QPen(QColor(0, 0, 0, 50), 2, Qt.SolidLine)) painter.setBrush(QColor(0, 0, 0, 50)) painter.drawRect(self.boundingRect().adjusted( self.pen().width(), self.__handleHeight, -self.pen().width(), 0.0)) painter.setPen(self.pen()) painter.setBrush( EDraw.EColor.LinearGradient(self.__handleRect, Qt.darkGray)) #painter.drawPolygon( QPolygonF( self.__handleRect.normalized().adjusted(-self.__boundExtra, 0.0, self.__boundExtra, 0.0 ) ) ) painter.drawPolygon(QPolygonF(self.__handleRect)) painter.setPen(Qt.lightGray) r = QRectF(0.0, 0.0, self.__name.boundingRect().width(), self.__handleRect.height()) painter.drawText(r, Qt.AlignCenter, self.__name.toPlainText())
class ControlPointRect(QGraphicsObject): Free = 0 KeepAspectRatio = 1 KeepCenter = 2 rectChanged = Signal(QRectF) rectEdited = Signal(QRectF) def __init__(self, parent=None, rect=None, constraints=0, **kwargs): QGraphicsObject.__init__(self, parent, **kwargs) self.setFlag(QGraphicsItem.ItemHasNoContents) self.setFlag(QGraphicsItem.ItemIsFocusable) self.__rect = rect if rect is not None else QRectF() self.__margins = QMargins() points = \ [ControlPoint(self, ControlPoint.Left), ControlPoint(self, ControlPoint.Top), ControlPoint(self, ControlPoint.TopLeft), ControlPoint(self, ControlPoint.Right), ControlPoint(self, ControlPoint.TopRight), ControlPoint(self, ControlPoint.Bottom), ControlPoint(self, ControlPoint.BottomLeft), ControlPoint(self, ControlPoint.BottomRight) ] assert(points == sorted(points, key=lambda p: p.anchor())) self.__points = dict((p.anchor(), p) for p in points) if self.scene(): self.__installFilter() for p in points: p.setFlag(QGraphicsItem.ItemIsFocusable) p.setFocusProxy(self) self.controlPoint(ControlPoint.Top).setConstraint(Qt.Vertical) self.controlPoint(ControlPoint.Bottom).setConstraint(Qt.Vertical) self.controlPoint(ControlPoint.Left).setConstraint(Qt.Horizontal) self.controlPoint(ControlPoint.Right).setConstraint(Qt.Horizontal) self.__constraints = constraints self.__activeControl = None self.__pointsLayout() def controlPoint(self, anchor): """ Return the anchor point (:class:`ControlPoint`) at anchor position or `None` if an anchor point is not set. """ return self.__points.get(anchor) def setRect(self, rect): """ Set the control point rectangle (:class:`QRectF`) """ if self.__rect != rect: self.__rect = QRectF(rect) self.__pointsLayout() self.prepareGeometryChange() self.rectChanged.emit(rect.normalized()) def rect(self): """ Return the control point rectangle. """ # Return the rect normalized. During the control point move the # rect can change to an invalid size, but the layout must still # know to which point does an unnormalized rect side belong, # so __rect is left unnormalized. # NOTE: This means all signal emits (rectChanged/Edited) must # also emit normalized rects return self.__rect.normalized() rect_ = Property(QRectF, fget=rect, fset=setRect, user=True) def setControlMargins(self, *margins): """Set the controls points on the margins around `rect` """ if len(margins) > 1: margins = QMargins(*margins) else: margins = margins[0] if isinstance(margins, int): margins = QMargins(margins, margins, margins, margins) if self.__margins != margins: self.__margins = margins self.__pointsLayout() def controlMargins(self): return self.__margins def setConstraints(self, constraints): raise NotImplementedError def isControlActive(self): """Return the state of the control. True if the control is active (user is dragging one of the points) False otherwise. """ return self.__activeControl is not None def itemChange(self, change, value): if change == QGraphicsItem.ItemSceneHasChanged and self.scene(): self.__installFilter() return QGraphicsObject.itemChange(self, change, value) def sceneEventFilter(self, obj, event): try: obj = toGraphicsObjectIfPossible(obj) if isinstance(obj, ControlPoint): etype = event.type() if etype == QEvent.GraphicsSceneMousePress and \ event.button() == Qt.LeftButton: self.__setActiveControl(obj) elif etype == QEvent.GraphicsSceneMouseRelease and \ event.button() == Qt.LeftButton: self.__setActiveControl(None) except Exception: log.error("Error in 'ControlPointRect.sceneEventFilter'", exc_info=True) return QGraphicsObject.sceneEventFilter(self, obj, event) def __installFilter(self): # Install filters on the control points. try: for p in self.__points.values(): p.installSceneEventFilter(self) except Exception: log.error("Error in ControlPointRect.__installFilter", exc_info=True) def __pointsLayout(self): """Layout the control points """ rect = self.__rect margins = self.__margins rect = rect.adjusted(-margins.left(), -margins.top(), margins.right(), margins.bottom()) center = rect.center() cx, cy = center.x(), center.y() left, top, right, bottom = \ rect.left(), rect.top(), rect.right(), rect.bottom() self.controlPoint(ControlPoint.Left).setPos(left, cy) self.controlPoint(ControlPoint.Right).setPos(right, cy) self.controlPoint(ControlPoint.Top).setPos(cx, top) self.controlPoint(ControlPoint.Bottom).setPos(cx, bottom) self.controlPoint(ControlPoint.TopLeft).setPos(left, top) self.controlPoint(ControlPoint.TopRight).setPos(right, top) self.controlPoint(ControlPoint.BottomLeft).setPos(left, bottom) self.controlPoint(ControlPoint.BottomRight).setPos(right, bottom) def __setActiveControl(self, control): if self.__activeControl != control: if self.__activeControl is not None: self.__activeControl.positionChanged[QPointF].disconnect( self.__activeControlMoved ) self.__activeControl = control if control is not None: control.positionChanged[QPointF].connect( self.__activeControlMoved ) def __activeControlMoved(self, pos): # The active control point has moved, update the control # rectangle control = self.__activeControl pos = control.pos() rect = QRectF(self.__rect) margins = self.__margins # TODO: keyboard modifiers and constraints. anchor = control.anchor() if anchor & ControlPoint.Top: rect.setTop(pos.y() + margins.top()) elif anchor & ControlPoint.Bottom: rect.setBottom(pos.y() - margins.bottom()) if anchor & ControlPoint.Left: rect.setLeft(pos.x() + margins.left()) elif anchor & ControlPoint.Right: rect.setRight(pos.x() - margins.right()) changed = self.__rect != rect self.blockSignals(True) self.setRect(rect) self.blockSignals(False) if changed: self.rectEdited.emit(rect.normalized()) def boundingRect(self): return QRectF()
class ENode(QGraphicsObject): onMove = pyqtSignal() onPress = pyqtSignal() kGuiAttributeId = EObject() kGuiAttributeType = EObject() kGuiAttributePlug = EObject() kGuiAttributeParent = EObject() kGuiAttributeParentName = EObject() kGuiAttributeLongName = EObject() kGuiAttributeShortName = EObject() def __init__(self, eNodeHandle): QGraphicsObject.__init__(self) self.setFlag(QGraphicsItem.ItemIsMovable, True) self.setAcceptsHoverEvents(True) self.__isDefaultPen = False self.__pen = None self.__pens = { 0: EDraw.EColor.DefaultLeaveHoverPen, 1: EDraw.EColor.DefaultEnterHoverPen } self.setPen(self.__pens[self.__isDefaultPen]) self.Name = eNodeHandle.Name self.__font = QFont('Helvetica', 8, False) self.__nodeHandle = eNodeHandle self.__nodeHandle.Message.connect(self.__messageFilter) self.__attrRect = QRectF(0, 0, 15, 15) self.__titleRect = QRectF(0, 0, 125, 20) self.__textSpace = QRectF( self.__attrRect.width() + self.pen().width(), 0.0, (self.__titleRect.width() - self.__attrRect.width() * 2 - self.pen().width() * 2) / 2, self.__attrRect.height()) self.__attributes = {} self.__out_attr_step = self.__titleRect.height() + self.pen().width() self.__in_attr_step = self.__titleRect.height() + self.pen().width() self.__buildAttributes() self.__height = max([self.__in_attr_step, self.__out_attr_step]) def __messageFilter(self, message): if message.match(self.__nodeHandle.kMessageAttributeAdded): self.__buildAttribute(message.getData()) def __getAttrShortName(self, attributeName): fm = QFontMetrics(self.__font) shortName = '' if fm.width(attributeName) > self.__textSpace.width(): for x in range(len(attributeName) + 1): if fm.width(shortName) > int(self.__textSpace.width()) - 10: return shortName shortName = attributeName[:x] + '...' return attributeName def __getAttributePosition(self, attrType): attr_x_pos = 0 if attrType.match(EAttribute.kTypeOutput): attr_x_pos = self.__titleRect.width() - self.__attrRect.width() rect = self.__attrRect.translated( QPointF(attr_x_pos, self.__out_attr_step)) point = QPointF((rect.topRight() + rect.bottomRight()) / 2) point.setX(point.x() + self.pen().width() * 2) self.__out_attr_step += self.__attrRect.width() + self.pen().width( ) return [rect, point] rect = self.__attrRect.translated( QPointF(attr_x_pos, self.__in_attr_step)) point = QPointF((rect.topLeft() + rect.bottomLeft()) / 2) point.setX(point.x() - self.pen().width() * 2) self.__in_attr_step += self.__attrRect.width() + self.pen().width() return [rect, point] def __buildAttribute(self, attribute): data = self.__getAttributePosition(attribute.Type) self.__attributes[data[0]] = dict({ self.kGuiAttributeId: attribute.Id, self.kGuiAttributeType: attribute.Type, self.kGuiAttributeParent: self, self.kGuiAttributeParentName: self.__nodeHandle.Name, self.kGuiAttributePlug: data[1], self.kGuiAttributeLongName: attribute.Name, self.kGuiAttributeShortName: self.__getAttrShortName(attribute.Name) }) self.__height = max([self.__in_attr_step, self.__out_attr_step]) self.update() self.onMove.emit() def __buildAttributes(self): for eddAttr in self.__nodeHandle.ls(): self.__buildAttribute(eddAttr) def __toggleHighlight(self): if self.__isDefaultPen: self.__isDefaultPen = False self.setPen(self.__pens[self.__isDefaultPen]) #self.setZValue(0.0) return self.__isDefaultPen = True self.setPen(self.__pens[self.__isDefaultPen]) #self.setZValue(-2.0) @property def Id(self): return self.__nodeHandle.Id def pen(self): return self.__pen def setPen(self, pen): if not isinstance(pen, QPen): raise AttributeError self.__pen = pen @property def Handle(self): return self.__nodeHandle def mapFromPoint(self, QPoint): for attrRect, attrValues in self.__attributes.iteritems(): if attrRect.contains(self.mapFromScene(QPoint)): return attrValues[self.kGuiAttributeId] return self.__nodeHandle.Id def mapFromId(self, attrId): for attrValue in self.__attributes.itervalues(): if attrValue[self.kGuiAttributeId] == attrId: return attrValue return None def hoverEnterEvent(self, mouseEvent): QGraphicsObject.hoverEnterEvent(self, mouseEvent) self.__toggleHighlight() def hoverMoveEvent(self, mouseEvent): QGraphicsObject.hoverMoveEvent(self, mouseEvent) def hoverLeaveEvent(self, mouseEvent): QGraphicsObject.hoverLeaveEvent(self, mouseEvent) self.__toggleHighlight() def mousePressEvent(self, mouseEvent): QGraphicsObject.mousePressEvent(self, mouseEvent) #if mouseEvent.button() == Qt.RightButton: #print self.mapFromPoint(mouseEvent.scenePos()) self.onPress.emit() def mouseDoubleClickEvent(self, mouseEvent): QGraphicsObject.mouseDoubleClickEvent(self, mouseEvent) #self.__nodeHandle.compute() def mouseMoveEvent(self, mouseEvent): QGraphicsObject.mouseMoveEvent(self, mouseEvent) self.onMove.emit() def boundingRect(self): extra = self.pen().width() return self.__titleRect.normalized().adjusted( -extra, -extra, extra, (self.__height - self.__titleRect.height()) + extra) def shape(self): return QGraphicsItem.shape(self) def paint(self, painter, option, widget=None): painter.setBrush(Qt.darkGray) painter.setPen(self.pen()) painter.drawRect(self.boundingRect()) painter.setPen(Qt.NoPen) painter.setBrush(QColor(43, 43, 43)) painter.drawRect(self.__titleRect) painter.setPen(Qt.darkGray) painter.drawText(self.__titleRect, Qt.AlignCenter, self.Name) painter.setPen(Qt.NoPen) painter.setBrush(QColor(60, 63, 65)) for rect in self.__attributes.iterkeys(): painter.drawRect(rect) painter.setBrush(Qt.darkGray) for rect in self.__attributes.iterkeys(): painter.drawPolygon( EDraw.Circle(rect.height() / 3, 3).translated(rect.center())) painter.setPen(QPen(QColor(43, 43, 43), 1.0, Qt.SolidLine)) painter.setBrush(Qt.NoBrush) painter.setFont(self.__font) for attrRect, attrValues in self.__attributes.iteritems(): attrNameRect = self.__textSpace.translated(attrRect.topLeft()) align = Qt.AlignLeft if attrRect.topLeft().x() > 0: attrNameRect = self.__textSpace.translated( QPointF((self.__titleRect.width() / 2) - (attrRect.width() + self.pen().width()), attrRect.topLeft().y())) align = Qt.AlignRight painter.drawText(attrNameRect, align, attrValues[self.kGuiAttributeShortName])