Beispiel #1
0
    def __init__(self,
                 label,
                 indices,
                 pos,
                 length,
                 height,
                 label_width,
                 frame_width,
                 color,
                 parent=None):
        super(TimeLine, self).__init__(parent)
        self._pos = pos
        self._length = length
        self._height = height
        self.label = label
        self._label_width = label_width
        self._indices = indices
        self.frame_width = frame_width

        size = QSizeF()
        size.setHeight(self._height)
        size.setWidth(self._length)
        self._bounding_rect = QRectF()
        self._bounding_rect.setX(self._pos.x())
        self._bounding_rect.setY(self._pos.y())
        self._bounding_rect.setSize(size)
        self._tpos = QPointF(pos)
        self._tpos.setY(pos.y() + self._height)
        self.color = QColor()
        self.color.setRed(color[0] * 255)
        self.color.setGreen(color[1] * 255)
        self.color.setBlue(color[2] * 255)
        self.color.setAlpha(255)
Beispiel #2
0
    def create_frame_indicator(self):
        size = QSizeF()
        height = 100
        size.setHeight(height)
        size.setWidth(self.label_height)
        self.frame_indicator = FrameIndicator(size, self.frame_width,
                                              self.label_width, 0)
        self._label_scene.addItem(self.frame_indicator)

        self.edit_start_frame_indicator = FrameIndicator(
            size, self.frame_width, self.label_width, 0, blue)
        self._label_scene.addItem(self.edit_start_frame_indicator)
Beispiel #3
0
 def getSizeFromCmnd(self, sizeinfo):
     '''
     Returns a QSizeF based on the information in the dictionary
     sizeinfo.  Recognized keys are "width" and "height", and
     correspond to those float values in the QSizeF.  Values not
     given in sizeinfo are assigned as zero in the returned QSizeF.
     '''
     myrect = QSizeF(0.0, 0.0)
     try:
         myrect.setWidth(float(sizeinfo["width"]))
     except KeyError:
         pass
     try:
         myrect.setHeight(float(sizeinfo["height"]))
     except KeyError:
         pass
     return myrect
class Node(SchemeItem):
    """The main component of the graph scene.

    A node represents a single vertex of the graph, with its associated information and inputs/outputs.
    """
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setFlag(self.ItemIsMovable)
        self.setFlag(self.ItemIsSelectable)

        # Default size and text
        self._size = QSizeF(0, 115)

        self._body_item = QGraphicsPathItem(self)
        self._title_item = TextLineItem("Node", self)
        self._divider = QGraphicsLineItem(self)
        self._description_item = TextRectItem("No description", self)

        # Inputs and outputs
        self.inputs = []
        self.outputs = []
        self.messages = SortedList(key=lambda item: item.severity())

        self._config_widget = None

        # Set proper style and color for the item
        self.styleChange()
        self.paletteChange()

    # Operations =======================================================================================================
    def showConnectionText(self):
        for input in self.inputs:
            input.showText()

        for output in self.outputs:
            output.showText()

    def hideConnectionText(self):
        for input in self.inputs:
            input.hideText()

        for output in self.outputs:
            output.hideText()

    # Input/output management ==========================================================================================
    def addInput(self, obj: Input):
        self.insertInput(len(self.inputs), obj)

    def insertInput(self, index, obj: Input):
        index = clamp(index, 0, len(self.inputs))
        self.inputs.insert(index, obj)

        obj.setParentItem(self)

        self._updateInputPositions()

    def removeInput(self, index):
        removed = self.inputs.pop(index)
        removed.setParentItem(None)

        self._updateInputPositions()

        return removed

    def addOutput(self, obj: Output):
        self.insertOutput(len(self.outputs), obj)

    def insertOutput(self, index, obj: Output):
        index = clamp(index, 0, len(self.outputs))
        self.outputs.insert(index, obj)

        obj.setParentItem(self)

        self._updateOutputPositions()

    def removeOutput(self, index):
        removed = self.outputs.pop(index)
        removed.setParentItem(None)

        self._updateOutputPositions()

        return removed

    # Message management ===============================================================================================
    def addMessage(self, obj: Message):
        self.messages.add(obj)
        obj.setParentItem(self)

        self._updateMessagePositions()

    def removeMessage(self, index):
        removed = self.messages.pop(index)
        removed.setParentItem(None)

        self._updateMessagePositions()

        return removed

    # Member variables =================================================================================================
    def setTitle(self, title):
        self._title_item.setText(title)

    def setDescription(self, description):
        self._description_item.setText(description)

    def setConfigWidget(self, w):
        self._config_widget = w

    def size(self):
        return self._size

    def title(self):
        return self._title_item.text()

    def description(self):
        return self._description_item.text()

    def configWidget(self):
        """Return a widget for configuring this node, or None if it does not exist."""
        return self._config_widget

    # Updating functions ===============================================================================================
    def _updateInputPositions(self):
        padding = self.style().pixelMetric(Style.NodeConnectionPadding)

        i = 0
        for item in self.inputs:
            item.setPos(0, padding * (2 * i + 1))
            i += 1

    def _updateOutputPositions(self):
        padding = self.style().pixelMetric(Style.NodeConnectionPadding)

        i = 0
        for item in self.outputs:
            item.setPos(self.size().width(), padding * (2 * i + 1))
            i += 1

    def _updateMessagePositions(self):
        padding = self.style().pixelMetric(Style.NodeMessagePadding)
        icon_size = self.style().pixelMetric(Style.MessageIconSize)

        i = 0
        for msg in self.messages:
            msg.setPos(
                0,
                self.size().height() + padding * (i + 1) + icon_size * i)
            i += 1

    # Geometry and drawing =============================================================================================
    def boundingRect(self) -> QRectF:
        frame_width = self.style().pixelMetric(Style.NodeFrameWidth)

        return QRectF(QPointF(-frame_width / 2, -frame_width / 2),
                      self.size() + QSizeF(frame_width, frame_width))

    def shape(self):
        frame_corner_radius = self.style().pixelMetric(
            Style.NodeFrameCornerRadius)

        path = QPainterPath()
        path.addRoundedRect(QRectF(QPointF(0, 0), self.size()),
                            frame_corner_radius, frame_corner_radius)

        return path

    def paint(self, painter: QPainter, option, widget=...) -> None:
        pass

    # Event handlers ===================================================================================================
    def styleChange(self):
        self.prepareGeometryChange()
        style = self.style()

        # Size and Frame
        frame_width = style.pixelMetric(Style.NodeWidth)
        self._size.setWidth(frame_width)
        self._body_item.setPen(style.framePen(self.palette()))

        path = QPainterPath()
        path.addRoundedRect(QRectF(QPointF(), self.size()),
                            style.pixelMetric(Style.NodeFrameCornerRadius),
                            style.pixelMetric(Style.NodeFrameCornerRadius))
        self._body_item.setPath(path)

        # Title item
        title_font = style.font(Style.NodeTitleFont)
        title_metrics = QFontMetricsF(title_font)

        padding = style.pixelMetric(Style.NodeFrameTextPadding)

        self._title_item.setFont(title_font)
        self._title_item.setPos(padding, padding)
        self._title_item.moveBy(0, title_metrics.capHeight())
        self._title_item.setMaximumWidth(frame_width - padding * 2)

        # Divider item
        div_margin = style.pixelMetric(Style.NodeDividerTextMargin)
        div_voffset = padding + title_metrics.capHeight(
        ) + title_metrics.descent() + div_margin

        self._divider.setLine(0, div_voffset, frame_width, div_voffset)
        self._divider.setPen(style.framePen(self.palette()))

        # Description item
        self._description_item.setFont(style.font(Style.NodeDescriptionFont))
        self._description_item.setPos(padding, div_voffset + div_margin)
        self._description_item.setFrame(
            QRectF(
                QPointF(0, 0),
                QSizeF(
                    px(frame_width) - padding * 2,
                    (px(self.size().height()) - padding) -
                    (div_voffset + padding))))

    def paletteChange(self):
        self._body_item.setPen(self.style().framePen(self.palette()))
        self._body_item.setBrush(self.palette().base())
        self._title_item.setBrush(self.palette().text())
        self._description_item.setBrush(self.palette().text())
        self._divider.setPen(self.style().framePen(self.palette()))

        self.update(self.boundingRect())

    def itemChange(self, change, value):
        if change == self.ItemSelectedHasChanged:
            selection = self.scene().selection()
            if len(selection.nodes) == 1:
                for edge in selection.edges:
                    edge.autoSelect()

            # Update selection status for all connections
            for input in self.inputs:
                input.autoSelect()

            for output in self.outputs:
                output.autoSelect()

        return super().itemChange(change, value)

    def mouseDoubleClickEvent(self, event):
        if self.configWidget() is not None:
            view = event.widget().parent()
            view.configRequested.emit(self)

    # Serialization ====================================================================================================
    def serialize(self) -> dict:
        return {
            "title": self.title(),
            "description": self.description(),
            "inputs": self.inputs,
            "outputs": self.outputs,
            "messages": list(self.messages),
            "position": inch(self.pos())
        }

    @classmethod
    def deserialize(cls, data: dict):
        obj = cls()
        obj.setTitle(data["title"])
        obj.setDescription(data["description"])
        obj.setPos(px(data["position"]))

        for i in range(len(obj.inputs)):
            obj.removeInput(0)

        for i in range(len(obj.outputs)):
            obj.removeOutput(0)

        for i in range(len(obj.messages)):
            obj.removeMessage(0)

        for input in data["inputs"]:
            obj.addInput(input)

        for output in data["outputs"]:
            obj.addOutput(output)

        for message in data["messages"]:
            obj.addMessage(message)

        return obj