Ejemplo n.º 1
0
class FilterIcon(QGraphicsEllipseItem):
    """An icon to show that a Link has filters."""
    def __init__(self, x, y, w, h, parent):
        super().__init__(x, y, w, h, parent)
        self._parent = parent
        color = QColor("slateblue")
        self.setBrush(qApp.palette().window())  # pylint: disable=undefined-variable
        self._text_item = QGraphicsTextItem(self)
        font = QFont('Font Awesome 5 Free Solid')
        self._text_item.setFont(font)
        self._text_item.setPos(0, 0)
        self._text_item.setPlainText("\uf0b0")
        self._text_item.setDefaultTextColor(color)
        self._text_item.setPos(self.sceneBoundingRect().center() -
                               self._text_item.sceneBoundingRect().center())
        self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=True)
        self.setCursor(Qt.PointingHandCursor)

    def itemChange(self, change, value):
        """Selects the parent item instead of this."""
        if change == QGraphicsItem.GraphicsItemChange.ItemSelectedChange and value == 1:
            if not self._parent.isSelected():
                self._parent.setSelected(True)
            return not value
        return super().itemChange(change, value)
Ejemplo n.º 2
0
    def overlay_text(
        self,
        message: str,
        color: int,
        size: int,
        x: int,
        y: int,
        timeout: int,
        font_name: str,
        centered: bool,
        shadow: bool,
    ):
        gfx = QGraphicsTextItem(message)
        gfx.setDefaultTextColor(decode_color(color))

        font = QFont(font_name, min(50, size))
        font.setStyleHint(QFont.SansSerif)
        gfx.setFont(font)

        if shadow:
            effect = QGraphicsDropShadowEffect(gfx)
            effect.setBlurRadius(0)
            effect.setColor(Qt.GlobalColor.black)
            effect.setOffset(1, 1)
            gfx.setGraphicsEffect(effect)

        if centered:
            # The provided x, y is at the center of the text
            bound = gfx.boundingRect()
            gfx.setPos(x - (bound.width() / 2), y - (bound.height() / 2))
        else:
            gfx.setPos(x, y)

        self._finalize_gfx(gfx, timeout)
Ejemplo n.º 3
0
def draw_message_text(item: QGraphicsTextItem, font: QFont, text: str, x: int,
                      y: int, max_width: int):
    item.setDefaultTextColor(QColor.fromRgba(0xFF440400))
    item.setFont(font)
    item.setPos(x, y)

    # Calculate how many character to trim *without* making copies of the string.
    font_metrics = QFontMetrics(font)
    cur_width = font_metrics.width(text)
    cur_end = len(text) - 1
    while cur_width > max_width:
        cur_width -= font_metrics.charWidth(text, cur_end)
        cur_end -= 1
    item.setPlainText(text[:cur_end + 1])
Ejemplo n.º 4
0
class FilterIcon(QGraphicsEllipseItem):
    """An icon to show that a Link has filters."""

    def __init__(self, x, y, w, h, parent):
        super().__init__(x, y, w, h, parent)
        self._parent = parent
        color = QColor("slateblue")
        self.setBrush(qApp.palette().window())  # pylint: disable=undefined-variable
        self._text_item = QGraphicsTextItem(self)
        font = QFont('Font Awesome 5 Free Solid')
        self._text_item.setFont(font)
        self._text_item.setPos(0, 0)
        self._text_item.setPlainText("\uf0b0")
        self._text_item.setDefaultTextColor(color)
        self._text_item.setPos(self.sceneBoundingRect().center() - self._text_item.sceneBoundingRect().center())
        self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=False)
Ejemplo n.º 5
0
class _LinkIcon(QGraphicsEllipseItem):
    """An icon to show over a Link."""
    def __init__(self, x, y, w, h, parent):
        super().__init__(x, y, w, h, parent)
        self._parent = parent
        color = QColor("slateblue")
        self.setBrush(qApp.palette().window())  # pylint: disable=undefined-variable
        self._text_item = QGraphicsTextItem(self)
        font = QFont('Font Awesome 5 Free Solid')
        self._text_item.setFont(font)
        self._text_item.setDefaultTextColor(color)
        self._svg_item = QGraphicsSvgItem(self)
        self._datapkg_renderer = QSvgRenderer()
        self._datapkg_renderer.load(":/icons/datapkg.svg")
        self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=False)
        self._block_updates = False

    def update_icon(self):
        """Sets the icon (filter, datapkg, or none), depending on Connection state."""
        connection = self._parent.connection
        if connection.use_datapackage:
            self.setVisible(True)
            self._svg_item.setVisible(True)
            self._svg_item.setSharedRenderer(self._datapkg_renderer)
            scale = 0.8 * self.rect().width(
            ) / self._datapkg_renderer.defaultSize().width()
            self._svg_item.setScale(scale)
            self._svg_item.setPos(0, 0)
            self._svg_item.setPos(self.sceneBoundingRect().center() -
                                  self._svg_item.sceneBoundingRect().center())
            self._text_item.setVisible(False)
            return
        if connection.has_filters():
            self.setVisible(True)
            self._text_item.setVisible(True)
            self._text_item.setPlainText("\uf0b0")
            self._svg_item.setPos(0, 0)
            self._text_item.setPos(
                self.sceneBoundingRect().center() -
                self._text_item.sceneBoundingRect().center())
            self._svg_item.setVisible(False)
            return
        self.setVisible(False)
        self._text_item.setVisible(False)
        self._svg_item.setVisible(False)
Ejemplo n.º 6
0
    def dibujar(self):
        pen = QPen()
        brush = QBrush()
        pen.setWidth(3)

        for i in self.organizador:
            color = QColor(i.red, i.green, i.blue)
            brush.setStyle(Qt.SolidPattern)
            brush.setColor(color)
            pen.setColor(color)

            self.scene.addEllipse(i.or_x, i.or_y, 7, 7, pen, brush)
            self.scene.addEllipse(i.de_x, i.de_y, 7, 7, pen, brush)
            self.scene.addLine((i.or_x) + 3.5, (i.or_y) + 3.5, (i.de_x) + 3.5,
                               (i.de_y) + 3.5, pen)

        for keys in self.organizador.grafo_dic:
            text = QGraphicsTextItem(str(keys))
            text.setFlag(QGraphicsItem.ItemIsMovable)
            text.setFont(QFont("TimesNewRoman", 12, QFont.ExtraBold))
            self.scene.addItem(text)
            text.setPos(keys[0], keys[1])
Ejemplo n.º 7
0
class NodeItem(AbstractNodeItem):
    """
    Base Node item.

    Args:
        name (str): name displayed on the node.
        parent (QtWidgets.QGraphicsItem): parent item.
    """
    def __init__(self, name='node', parent=None):
        super(NodeItem, self).__init__(name, parent)
        pixmap = QtGui.QPixmap(ICON_NODE_BASE)
        if pixmap.size().height() > NODE_ICON_SIZE:
            pixmap = pixmap.scaledToHeight(NODE_ICON_SIZE,
                                           QtCore.Qt.SmoothTransformation)
        self._properties['icon'] = ICON_NODE_BASE
        self._icon_item = QGraphicsPixmapItem(pixmap, self)
        self._icon_item.setTransformationMode(QtCore.Qt.SmoothTransformation)
        self._text_item = QGraphicsTextItem(self.name, self)
        self._x_item = XDisabledItem(self, 'DISABLED')
        self._input_items = {}
        self._output_items = {}
        self._widgets = {}

    def paint(self, painter, option, widget):
        """
        Draws the node base not the ports.

        Args:
            painter (QtGui.QPainter): painter used for drawing the item.
            option (QtGui.QStyleOptionGraphicsItem):
                used to describe the parameters needed to draw.
            widget (QtWidgets.QWidget): not used.
        """
        painter.save()
        bg_border = 1.0
        rect = QtCore.QRectF(0.5 - (bg_border / 2), 0.5 - (bg_border / 2),
                             self._width + bg_border, self._height + bg_border)
        radius = 2
        border_color = QtGui.QColor(*self.border_color)

        path = QtGui.QPainterPath()
        path.addRoundedRect(rect, radius, radius)
        painter.setPen(QtGui.QPen(border_color.darker(200), 1.5))
        painter.drawPath(path)

        rect = self.boundingRect()
        bg_color = QtGui.QColor(*self.color)
        painter.setBrush(bg_color)
        painter.setPen(QtCore.Qt.NoPen)
        painter.drawRoundRect(rect, radius, radius)

        if self.selected and NODE_SEL_COLOR:
            painter.setBrush(QtGui.QColor(*NODE_SEL_COLOR))
            painter.drawRoundRect(rect, radius, radius)

        label_rect = QtCore.QRectF(rect.left() + (radius / 2),
                                   rect.top() + (radius / 2),
                                   self._width - (radius / 1.25), 28)
        path = QtGui.QPainterPath()
        path.addRoundedRect(label_rect, radius / 1.5, radius / 1.5)
        painter.setBrush(QtGui.QColor(0, 0, 0, 50))
        painter.fillPath(path, painter.brush())

        border_width = 0.8
        if self.selected and NODE_SEL_BORDER_COLOR:
            border_width = 1.2
            border_color = QtGui.QColor(*NODE_SEL_BORDER_COLOR)
        border_rect = QtCore.QRectF(rect.left() - (border_width / 2),
                                    rect.top() - (border_width / 2),
                                    rect.width() + border_width,
                                    rect.height() + border_width)

        pen = QtGui.QPen(border_color, border_width)
        pen.setCosmetic(self.viewer().get_zoom() < 0.0)
        path = QtGui.QPainterPath()
        path.addRoundedRect(border_rect, radius, radius)
        painter.setBrush(QtCore.Qt.NoBrush)
        painter.setPen(pen)
        painter.drawPath(path)

        painter.restore()

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.MouseButton.LeftButton:
            start = PortItem().boundingRect().width()
            end = self.boundingRect().width() - start
            x_pos = event.pos().x()
            if not start <= x_pos <= end:
                event.ignore()
        super(NodeItem, self).mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        if event.modifiers() == QtCore.Qt.AltModifier:
            event.ignore()
            return
        super(NodeItem, self).mouseReleaseEvent(event)

    def itemChange(self, change, value):
        if change == self.ItemSelectedChange and self.scene():
            self.reset_pipes()
            if value:
                self.hightlight_pipes()
            self.setZValue(Z_VAL_NODE)
            if not self.selected:
                self.setZValue(Z_VAL_NODE + 1)

        return super(NodeItem, self).itemChange(change, value)

    def _tooltip_disable(self, state):
        tooltip = '<b>{}</b>'.format(self._properties['name'])
        if state:
            tooltip += ' <font color="red"><b>(DISABLED)</b></font>'
        tooltip += '<br/>{}<br/>'.format(self._properties['type'])
        self.setToolTip(tooltip)

    def _set_base_size(self):
        """
        setup initial base size.
        """
        self._width = NODE_WIDTH
        self._height = NODE_HEIGHT
        width, height = self.calc_size()
        if width > self._width:
            self._width = width
        if height > self._height:
            self._height = height

    def _set_text_color(self, color):
        """
        set text color.

        Args:
            color (tuple): color value in (r, g, b, a).
        """
        text_color = QtGui.QColor(*color)
        for port, text in self._input_items.items():
            text.setDefaultTextColor(text_color)
        for port, text in self._output_items.items():
            text.setDefaultTextColor(text_color)
        self._text_item.setDefaultTextColor(text_color)

    def activate_pipes(self):
        """
        active pipe color.
        """
        ports = self.inputs + self.outputs
        for port in ports:
            for pipe in port.connected_pipes:
                pipe.activate()

    def hightlight_pipes(self):
        """
        highlight pipe color.
        """
        ports = self.inputs + self.outputs
        for port in ports:
            for pipe in port.connected_pipes:
                pipe.highlight()

    def reset_pipes(self):
        """
        reset the pipe color.
        """
        ports = self.inputs + self.outputs
        for port in ports:
            for pipe in port.connected_pipes:
                pipe.reset()

    def calc_size(self):
        """
        calculate minimum node size.
        """
        width = 0.0
        if self._widgets:
            widget_widths = [
                w.boundingRect().width() for w in self._widgets.values()
            ]
            width = max(widget_widths)
        if self._text_item.boundingRect().width() > width:
            width = self._text_item.boundingRect().width()

        port_height = 0.0
        if self._input_items:
            input_widths = []
            for port, text in self._input_items.items():
                input_width = port.boundingRect().width() * 2
                if text.isVisible():
                    input_width += text.boundingRect().width()
                input_widths.append(input_width)
            width += max(input_widths)
            port = list(self._input_items.keys())[0]
            port_height = port.boundingRect().height() * 2
        if self._output_items:
            output_widths = []
            for port, text in self._output_items.items():
                output_width = port.boundingRect().width() * 2
                if text.isVisible():
                    output_width += text.boundingRect().width()
                output_widths.append(output_width)
            width += max(output_widths)
            port = list(self._output_items.keys())[0]
            port_height = port.boundingRect().height() * 2

        in_count = len([p for p in self.inputs if p.isVisible()])
        out_count = len([p for p in self.outputs if p.isVisible()])
        height = port_height * (max([in_count, out_count]) + 2)
        if self._widgets:
            wid_height = sum(
                [w.boundingRect().height() for w in self._widgets.values()])
            if wid_height > height:
                height = wid_height + (wid_height / len(self._widgets))

        height += 10

        return width, height

    def arrange_icon(self):
        """
        Arrange node icon to the default top left of the node.
        """
        self._icon_item.setPos(2.0, 2.0)

    def arrange_label(self):
        """
        Arrange node label to the default top center of the node.
        """
        text_rect = self._text_item.boundingRect()
        text_x = (self._width / 2) - (text_rect.width() / 2)
        self._text_item.setPos(text_x, 1.0)

    def arrange_widgets(self):
        """
        Arrange node widgets to the default center of the node.
        """
        if not self._widgets:
            return
        wid_heights = sum(
            [w.boundingRect().height() for w in self._widgets.values()])
        pos_y = self._height / 2
        pos_y -= wid_heights / 2
        for name, widget in self._widgets.items():
            rect = widget.boundingRect()
            pos_x = (self._width / 2) - (rect.width() / 2)
            widget.setPos(pos_x, pos_y)
            pos_y += rect.height()

    def arrange_ports(self, padding_x=0.0, padding_y=0.0):
        """
        Arrange input, output ports in the node layout.
    
        Args:
            padding_x (float): horizontal padding.
            padding_y: (float): vertical padding.
        """
        width = self._width - padding_x
        height = self._height - padding_y

        # adjust input position
        inputs = [p for p in self.inputs if p.isVisible()]
        if inputs:
            port_width = inputs[0].boundingRect().width()
            port_height = inputs[0].boundingRect().height()
            chunk = (height / len(inputs))
            port_x = (port_width / 2) * -1
            port_y = (chunk / 2) - (port_height / 2)
            for port in inputs:
                port.setPos(port_x + padding_x, port_y + (padding_y / 2))
                port_y += chunk
        # adjust input text position
        for port, text in self._input_items.items():
            if not port.isVisible():
                continue
            txt_height = text.boundingRect().height() - 8.0
            txt_x = port.x() + port.boundingRect().width()
            txt_y = port.y() - (txt_height / 2)
            text.setPos(txt_x + 3.0, txt_y)
        # adjust output position
        outputs = [p for p in self.outputs if p.isVisible()]
        if outputs:
            port_width = outputs[0].boundingRect().width()
            port_height = outputs[0].boundingRect().height()
            chunk = height / len(outputs)
            port_x = width - (port_width / 2)
            port_y = (chunk / 2) - (port_height / 2)
            for port in outputs:
                port.setPos(port_x, port_y + (padding_y / 2))
                port_y += chunk
        # adjust output text position
        for port, text in self._output_items.items():
            if not port.isVisible():
                continue
            txt_width = text.boundingRect().width()
            txt_height = text.boundingRect().height() - 8.0
            txt_x = width - txt_width - (port.boundingRect().width() / 2)
            txt_y = port.y() - (txt_height / 2)
            text.setPos(txt_x - 1.0, txt_y)

    def offset_icon(self, x=0.0, y=0.0):
        """
        offset the icon in the node layout.

        Args:
            x (float): horizontal x offset
            y (float): vertical y offset
        """
        if self._icon_item:
            icon_x = self._icon_item.pos().x() + x
            icon_y = self._icon_item.pos().y() + y
            self._icon_item.setPos(icon_x, icon_y)

    def offset_label(self, x=0.0, y=0.0):
        """
        offset the label in the node layout.

        Args:
            x (float): horizontal x offset
            y (float): vertical y offset
        """
        icon_x = self._text_item.pos().x() + x
        icon_y = self._text_item.pos().y() + y
        self._text_item.setPos(icon_x, icon_y)

    def offset_widgets(self, x=0.0, y=0.0):
        """
        offset the node widgets in the node layout.

        Args:
            x (float): horizontal x offset
            y (float): vertical y offset
        """
        for name, widget in self._widgets.items():
            pos_x = widget.pos().x()
            pos_y = widget.pos().y()
            widget.setPos(pos_x + x, pos_y + y)

    def offset_ports(self, x=0.0, y=0.0):
        """
        offset the ports in the node layout.

        Args:
            x (float): horizontal x offset
            y (float): vertical y offset
        """
        for port, text in self._input_items.items():
            port_x, port_y = port.pos().x(), port.pos().y()
            text_x, text_y = text.pos().x(), text.pos().y()
            port.setPos(port_x + x, port_y + y)
            text.setPos(text_x + x, text_y + y)
        for port, text in self._output_items.items():
            port_x, port_y = port.pos().x(), port.pos().y()
            text_x, text_y = text.pos().x(), text.pos().y()
            port.setPos(port_x + x, port_y + y)
            text.setPos(text_x + x, text_y + y)

    def post_init(self, viewer=None, pos=None):
        """
        Called after node has been added into the scene.
        Adjust the node layout and form after the node has been added.

        Args:
            viewer (NodeGraphQt.widgets.viewer.NodeViewer): not used
            pos (tuple): cursor position.
        """
        # setup initial base size.
        self._set_base_size()
        # set text color when node is initialized.
        self._set_text_color(self.text_color)
        # set the tooltip
        self._tooltip_disable(self.disabled)

        # --- setup node layout ---

        # arrange label text
        self.arrange_label()
        self.offset_label(0.0, 5.0)

        # arrange icon
        self.arrange_icon()
        self.offset_icon(5.0, 2.0)

        # arrange node widgets
        self.arrange_widgets()
        self.offset_widgets(0.0, 10.0)

        # arrange input and output ports.
        self.arrange_ports(padding_y=35.0)
        self.offset_ports(0.0, 15.0)

        # set initial node position.
        if pos:
            self.xy_pos = pos

    @property
    def icon(self):
        return self._properties['icon']

    @icon.setter
    def icon(self, path=None):
        self._properties['icon'] = path
        path = path or ICON_NODE_BASE
        pixmap = QtGui.QPixmap(path)
        if pixmap.size().height() > NODE_ICON_SIZE:
            pixmap = pixmap.scaledToHeight(NODE_ICON_SIZE,
                                           QtCore.Qt.SmoothTransformation)
        self._icon_item.setPixmap(pixmap)
        if self.scene():
            self.post_init()

    @AbstractNodeItem.width.setter
    def width(self, width=0.0):
        w, h = self.calc_size()
        width = width if width > w else w
        AbstractNodeItem.width.fset(self, width)

    @AbstractNodeItem.height.setter
    def height(self, height=0.0):
        w, h = self.calc_size()
        h = 70 if h < 70 else h
        height = height if height > h else h
        AbstractNodeItem.height.fset(self, height)

    @AbstractNodeItem.disabled.setter
    def disabled(self, state=False):
        AbstractNodeItem.disabled.fset(self, state)
        for n, w in self._widgets.items():
            w.widget.setDisabled(state)
        self._tooltip_disable(state)
        self._x_item.setVisible(state)

    @AbstractNodeItem.selected.setter
    def selected(self, selected=False):
        AbstractNodeItem.selected.fset(self, selected)
        if selected:
            self.hightlight_pipes()

    @AbstractNodeItem.name.setter
    def name(self, name=''):
        AbstractNodeItem.name.fset(self, name)
        self._text_item.setPlainText(name)
        if self.scene():
            self.post_init()

    @property
    def inputs(self):
        """
        Returns:
            list[PortItem]: input port graphic items.
        """
        return list(self._input_items.keys())

    @property
    def outputs(self):
        """
        Returns:
            list[PortItem]: output port graphic items.
        """
        return list(self._output_items.keys())

    def add_input(self, name='input', multi_port=False, display_name=True):
        """
        Args:
            name (str): name for the port.
            multi_port (bool): allow multiple connections.
            display_name (bool): display the port name. 

        Returns:
            PortItem: input item widget
        """
        port = PortItem(self)
        port.name = name
        port.port_type = IN_PORT
        port.multi_connection = multi_port
        port.display_name = display_name
        text = QGraphicsTextItem(port.name, self)
        text.font().setPointSize(8)
        text.setFont(text.font())
        text.setVisible(display_name)
        self._input_items[port] = text
        if self.scene():
            self.post_init()
        return port

    def add_output(self, name='output', multi_port=False, display_name=True):
        """
        Args:
            name (str): name for the port.
            multi_port (bool): allow multiple connections.
            display_name (bool): display the port name.

        Returns:
            PortItem: output item widget
        """
        port = PortItem(self)
        port.name = name
        port.port_type = OUT_PORT
        port.multi_connection = multi_port
        port.display_name = display_name
        text = QGraphicsTextItem(port.name, self)
        text.font().setPointSize(8)
        text.setFont(text.font())
        text.setVisible(display_name)
        self._output_items[port] = text
        if self.scene():
            self.post_init()
        return port

    def get_input_text_item(self, port_item):
        """
        Args:
            port_item (PortItem): port item.

        Returns:
            QGraphicsTextItem: graphic item used for the port text.
        """
        return self._input_items[port_item]

    def get_output_text_item(self, port_item):
        """
        Args:
            port_item (PortItem): port item.

        Returns:
            QGraphicsTextItem: graphic item used for the port text.
        """
        return self._output_items[port_item]

    @property
    def widgets(self):
        return dict(self._widgets)

    def add_combo_menu(self, name='', label='', items=None, tooltip=''):
        items = items or []
        widget = NodeComboBox(self, name, label, items)
        widget.setToolTip(tooltip)
        self.add_widget(widget)
        return widget

    def add_text_input(self, name='', label='', text='', tooltip=''):
        widget = NodeLineEdit(self, name, label, text)
        widget.setToolTip(tooltip)
        self.add_widget(widget)
        return widget

    def add_checkbox(self,
                     name='',
                     label='',
                     text='',
                     state=False,
                     tooltip=''):
        widget = NodeCheckBox(self, name, label, text, state)
        widget.setToolTip(tooltip)
        self.add_widget(widget)
        return widget

    def add_widget(self, widget):
        if isinstance(widget, NodeBaseWidget):
            self._widgets[widget.name] = widget
        else:
            raise TypeError('{} is not an instance of a node widget.')

    def get_widget(self, name):
        widget = self._widgets.get(name)
        if widget:
            return widget
        raise KeyError('node has no widget "{}"'.format(name))

    def delete(self):
        for port, text in self._input_items.items():
            port.delete()
        for port, text in self._output_items.items():
            port.delete()
        super(NodeItem, self).delete()

    def from_dict(self, node_dict):
        super(NodeItem, self).from_dict(node_dict)
        widgets = node_dict.pop('widgets', {})
        for name, value in widgets.items():
            if self._widgets.get(name):
                self._widgets[name].value = value
Ejemplo n.º 8
0
class ArrowItem(QGraphicsLineItem):

    MOUSE_MARGIN = 20
    ENDS_TEXT_SHIFT_X = 20
    ENDS_TEXT_SHIFT_Y = 0
    TEXT_SHIFT = QPoint(ENDS_TEXT_SHIFT_X, ENDS_TEXT_SHIFT_Y)

    def __init__(self, line: QLineF):
        super().__init__(line)
        self.setAcceptHoverEvents(True)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        pen = QPen()
        pen.setWidth(5)
        self.setPen(pen)
        self.__p1Ends = QGraphicsTextItem('1', self)
        self.__p2Ends = QGraphicsTextItem('2', self)
        self.__updateTextLinePos()

        self.__currentEndsMove = MoveLineEnds.NONE
        self.__currentEndsHover = MoveLineEnds.NONE
        self.__endsCapture = [None, None]

        self.__tryCapture = None

        self.__blockMove = False

    def __updateTextLinePos(self):
        if self.line().p1().x() < self.line().p2().x():
            self.__p1Ends.setPos(self.line().p1()-self.TEXT_SHIFT)
            self.__p2Ends.setPos(self.line().p2()+self.TEXT_SHIFT)
        else:
            self.__p1Ends.setPos(self.line().p1()+self.TEXT_SHIFT)
            self.__p2Ends.setPos(self.line().p2()-self.TEXT_SHIFT)

    def setEndsText(self, moveLineEnds: MoveLineEnds, bold: bool=False, color: QColor=Qt.black):
        if moveLineEnds == MoveLineEnds.NONE:
            return

        if moveLineEnds == MoveLineEnds.BOTH:
            self.setEndsText(MoveLineEnds.P1, bold, color)
            self.setEndsText(MoveLineEnds.P2, bold, color)
            return

        color = 'red' if color == Qt.red else 'black'
        style = f'color="{color}"'
        index = int(moveLineEnds)
        text = f'<font {style}><b>{index}</b></font>' if bold else f'<font {style}><p>{index}</p></font>'
        if moveLineEnds == MoveLineEnds.P1:
            self.__p1Ends.setHtml(text)
            return
        if moveLineEnds == MoveLineEnds.P2:
            self.__p2Ends.setHtml(text)
            return

    @classmethod
    def isInBox(cls, mousePosition: QPoint, objectPosition: QPoint):
        x_ok = mousePosition.x() - cls.MOUSE_MARGIN <= objectPosition.x() <= mousePosition.x() + cls.MOUSE_MARGIN
        y_ok = mousePosition.y() - cls.MOUSE_MARGIN <= objectPosition.y() <= mousePosition.y() + cls.MOUSE_MARGIN
        return x_ok and y_ok

    def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
        if self.__blockMove:
            return

        if self.__currentEndsMove != MoveLineEnds.NONE:
            return

        p1_ok = self.isInBox(event.pos(), self.line().p1())
        p2_ok = self.isInBox(event.pos(), self.line().p2())

        if p1_ok:
            self.setEndsText(MoveLineEnds.P1, True)
            self.setCursor(Qt.OpenHandCursor)
            self.setSelected(False)
            self.__currentEndsHover = MoveLineEnds.P1
            return

        if p2_ok:
            self.setEndsText(MoveLineEnds.P2, True)
            self.setCursor(Qt.OpenHandCursor)
            self.setSelected(False)
            self.__currentEndsHover = MoveLineEnds.P2
            return

        if not self.isSelected():
            self.setCursor(Qt.OpenHandCursor)
            self.__currentEndsHover = MoveLineEnds.BOTH
            self.setEndsText(MoveLineEnds.BOTH, True)
            super().hoverMoveEvent(event)

    def hoverLeaveEvent(self, event: QHoverEvent):

        if self.__blockMove:
            return

        if self.__currentEndsMove != MoveLineEnds.NONE:
            return

        self.setEndsText(MoveLineEnds.BOTH)
        self.setCursor(Qt.ArrowCursor)
        self.setSelected(False)
        super().hoverLeaveEvent(event)


    def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
        if self.__blockMove:
            return

        if self.__currentEndsMove != MoveLineEnds.NONE:
            return

        if self.isInBox(event.pos(), self.line().p1()) and self.__endsCapture[0] is None:
            self.__currentEndsMove = MoveLineEnds.P1
            self.setEndsText(MoveLineEnds.P1, True, Qt.red)
            self.setCursor(Qt.ClosedHandCursor)
            return

        if self.isInBox(event.pos(), self.line().p2()) and self.__endsCapture[1] is None:
            self.__currentEndsMove = MoveLineEnds.P2
            self.setEndsText(MoveLineEnds.P2, True, Qt.red)
            self.setCursor(Qt.ClosedHandCursor)
            return

        if self.__endsCapture[0] is None and self.__endsCapture[1] is None:
            self.__currentEndsMove = MoveLineEnds.BOTH
            self.setEndsText(MoveLineEnds.BOTH, True, Qt.red)
            self.setCursor(Qt.ClosedHandCursor)
            super().mousePressEvent(event)

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent):
        if self.__blockMove:
            return

        self.setCursor(Qt.OpenHandCursor)
        self.setEndsText(self.__currentEndsMove, True)

        if self.__tryCapture is not None:
            print(type(self.__tryCapture))
            self.__tryCapture.capture(self)
            if self.__currentEndsMove == MoveLineEnds.P1:
                self.__endsCapture[0] = self.__tryCapture
            else:
                self.__endsCapture[1] = self.__tryCapture
        self.__tryCapture = None

        self.__currentEndsMove = MoveLineEnds.NONE

    def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent):

        if self.__blockMove:
            return
        if self.__currentEndsMove == MoveLineEnds.NONE:
            return

        if self.__tryCapture is not None:
            self.__tryCapture.unCapture(self)

        if self.__currentEndsMove == MoveLineEnds.P1:
            line = QLineF(event.pos(), self.line().p2())
            self.setLine(line)
            self.__updateTextLinePos()
            rect = self.__filteredCapturedObject()
            print(rect)
            if 1 == len(rect):
                rect = rect[0]
                self.__tryCapture = rect
                self.__tryCapture.tryCapture(self)
            self.update()
            return

        if self.__currentEndsMove == MoveLineEnds.P2:
            line = QLineF(self.line().p1(), event.pos())
            self.setLine(line)
            self.__updateTextLinePos()
            rect = self.__filteredCapturedObject()
            print(rect)
            if 1 == len(rect):
                rect = rect[0]
                self.__tryCapture = rect
                self.__tryCapture.tryCapture(self)
            self.update()
            return

        if self.__currentEndsMove == MoveLineEnds.BOTH:
            super().mouseMoveEvent(event)
            self.__updateTextLinePos()
            self.update()
            return

    def moveObject(self, object, pos):

        if object is self.__endsCapture[0]:
            line = QLineF(pos, self.line().p2())
            self.__blockMove = True
            self.setLine(line)
            self.__updateTextLinePos()
            self.__blockMove = False
            return
        if object is self.__endsCapture[1]:
            line = QLineF(self.line().p1(), pos)
            self.__blockMove = True
            self.setLine(line)
            self.__updateTextLinePos()
            rect = self.collidingItems()
            self.__blockMove = False
            return

    def __filteredCapturedObject(self):
        rect = self.collidingItems()
        try:
            rect.remove(self.__endsCapture[0])
            for ch in self.__endsCapture[0].children():
                try:
                    rect.remove(ch)
                except:
                    ...
        except:
            ...
        try:
            rect.remove(self.__endsCapture[1])
            for ch in self.__endsCapture[1].children():
                try:
                    rect.remove(ch)
                except:
                    ...
        except:
            ...
        return rect
Ejemplo n.º 9
0
class ExecutionIcon(QGraphicsEllipseItem):
    """An icon to show information about the item's execution."""

    _CHECK = "\uf00c"
    _CROSS = "\uf00d"
    _CLOCK = "\uf017"

    def __init__(self, parent):
        """
        Args:
            parent (ProjectItemIcon): the parent item
        """
        super().__init__(parent)
        self._parent = parent
        self._execution_state = "not started"
        self._text_item = QGraphicsTextItem(self)
        font = QFont('Font Awesome 5 Free Solid')
        self._text_item.setFont(font)
        parent_rect = parent.rect()
        self.setRect(0, 0, 0.5 * parent_rect.width(),
                     0.5 * parent_rect.height())
        self.setPen(Qt.NoPen)
        # pylint: disable=undefined-variable
        self.normal_brush = qApp.palette().window()
        self.selected_brush = qApp.palette().highlight()
        self.setBrush(self.normal_brush)
        self.setAcceptHoverEvents(True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=False)
        self.hide()

    def item_name(self):
        return self._parent.name()

    def _repaint(self, text, color):
        self._text_item.prepareGeometryChange()
        self._text_item.setPos(0, 0)
        self._text_item.setPlainText(text)
        self._text_item.setDefaultTextColor(color)
        size = self._text_item.boundingRect().size()
        dim_max = max(size.width(), size.height())
        rect_w = self.rect().width()
        self._text_item.setScale(rect_w / dim_max)
        self._text_item.setPos(self.sceneBoundingRect().center() -
                               self._text_item.sceneBoundingRect().center())
        self.show()

    def mark_execution_wating(self):
        self._execution_state = "waiting for dependencies"
        self._repaint(self._CLOCK, QColor("orange"))

    def mark_execution_started(self):
        self._execution_state = "in progress"
        self._repaint(self._CHECK, QColor("orange"))

    def mark_execution_finished(self, success, skipped):
        if success:
            self._execution_state = "skipped" if skipped else "completed"
            colorname = "orange" if skipped else "green"
            self._repaint(self._CHECK, QColor(colorname))
        else:
            self._execution_state = "failed"
            self._repaint(self._CROSS, QColor("red"))

    def hoverEnterEvent(self, event):
        tip = f"<p><b>Execution {self._execution_state}</b>. Select this item to see Console and Log messages.</p>"
        QToolTip.showText(event.screenPos(), tip)

    def hoverLeaveEvent(self, event):
        QToolTip.hideText()
Ejemplo n.º 10
0
class ScaleLine:
    def __init__(self,
                 parent,
                 color,
                 text_color,
                 text_start="",
                 margins=(0, 0, 0, 0),
                 dp=1,
                 is_upload=False):
        self._parent = parent
        self._color = color
        self._text_color = text_color
        self._text_start = text_start
        self._left, self._top, self._right, self._bottom = margins
        self._dp = dp
        self._is_upload = is_upload

        self._line = QGraphicsLineItem(self._parent)
        self._line.setZValue(12)
        pen = QPen(self._color)
        pen.setWidth(1)
        pen.setStyle(Qt.DashLine)
        self._line.setPen(pen)

        if not self._is_upload:
            self._text_item = QGraphicsTextItem(self._parent)
            self._text_item.setZValue(11)
            self._text_item.setDefaultTextColor(self._text_color)
            font = self._text_item.font()
            font_size = 10 * self._dp
            if font_size > 0:
                self._text_item.setFont(QFont(font.family(), font_size))

    def set_line(self, max_value=0, resize=False):
        height = self._parent.size().height() + self._top + self._bottom
        shift = int(height - height / 1.1)
        y = -self._top + shift

        if not self._is_upload:
            value = 0
            max_value = int(max_value / 1.1)
            megabyte = 1024 * 1024
            if max_value > megabyte:
                value = "{} MB".format(round(max_value / megabyte, 1))
            elif max_value > 1024:
                max_value //= 1024
                if max_value >= 10:
                    max_value = max_value // 10 * 10
                value = "{} KB".format(max_value)
            elif max_value > 0:
                if max_value >= 10:
                    max_value = max_value // 10 * 10
                value = "{} B".format(max_value)
            scale_text =  self._text_start if not value \
                else "{}{}/s".format(self._text_start, value)

            font_height = QFontMetrics(self._text_item.font())\
                .boundingRect(scale_text).height()
            x = 10
            self._text_item.setPos(x, y - font_height - 10)
            self._text_item.setPlainText(scale_text)

        if not resize:
            return

        self._line.setLine(QLineF(0, y, self._parent.size().width() + 30, y))
Ejemplo n.º 11
0
    def draw_message(self,
                     text: str,
                     name: str,
                     window_type="standard",
                     mode=0,
                     left=True) -> QGraphicsItemGroup:
        # Create the message box
        talk_window: QPixmap = self.view.talk_windows[window_type][mode]
        message_box = self.scene.addPixmap(talk_window)
        talk_window_x = _TALK_WINDOW_MODE_0_X if not mode else -3
        talk_window_y = _TALK_WINDOW_Y if not mode else _TALK_WINDOW_Y - 5
        message_box.setPos(talk_window_x, talk_window_y)

        # Create the name plate
        name_plate_texture: QPixmap = self.view.talk_windows["name_plate"][
            "plate"]
        name_plate = self.scene.addPixmap(name_plate_texture)
        name_plate_x = _NAME_PLATE_LEFT_X if left else _NAME_PLATE_RIGHT_X
        name_plate.setPos(name_plate_x, _NAME_PLATE_Y)

        # Create the name plate text
        name_plate_text = QGraphicsTextItem()
        name_plate_text.setPlainText(name)
        name_plate_text.setDefaultTextColor(QColor.fromRgba(0xFFFFFFB3))
        name_plate_text.setFont(self.view.name_plate_font)
        name_plate_text.setTextWidth(name_plate_texture.width())
        name_plate_text.setPos(name_plate_x, _NAME_PLATE_Y)

        # Center the name plate text
        block_format = QTextBlockFormat()
        block_format.setAlignment(QtGui.Qt.AlignCenter)
        cursor = name_plate_text.textCursor()
        cursor.select(QTextCursor.Document)
        cursor.mergeBlockFormat(block_format)
        cursor.clearSelection()
        name_plate_text.setTextCursor(cursor)
        self.scene.addItem(name_plate_text)

        # Create the message box text. Draw two lines if required.
        # Truncate lines with width > 312
        split_text = text.split("\n")
        message_box_text = QGraphicsTextItem()
        message_box_text_2 = QGraphicsTextItem()
        if split_text and split_text[0]:
            text_utils.draw_message_text(message_box_text,
                                         self.view.name_plate_font,
                                         split_text[0],
                                         _TALK_WINDOW_MODE_0_X + 20,
                                         _TALK_WINDOW_Y + 5, 312)
        if len(split_text) > 1 and split_text[1]:
            text_utils.draw_message_text(message_box_text_2,
                                         self.view.name_plate_font,
                                         split_text[1],
                                         _TALK_WINDOW_MODE_0_X + 20,
                                         _TALK_WINDOW_Y + 21, 312)

        group = self.scene.createItemGroup([
            message_box, message_box_text, message_box_text_2, name_plate,
            name_plate_text
        ])
        group.setZValue(2.0)
        return group
Ejemplo n.º 12
0
class GraphicsPortPainter:
    """The GraphicsPortPainter class provides a set of functions and configurations for
        painting a GraphicsPort object.
    """
    class DrawingState(Enum):
        Normal = 0
        Target = 1

    class PortNamePosition(Enum):
        Left = 0
        Right = 1

    drawing_state = DrawingState.Normal
    target_port_type = None

    def __init__(self, graphics_port: "GraphicsPort",
                 port_name_position: "PortNamePosition"):
        self.__graphics_port = graphics_port
        self.__port_name = QGraphicsTextItem(parent=graphics_port)
        self.__port_name.setPlainText(graphics_port._port.name)
        self.__port_name.setDefaultTextColor("#FFFFFF")
        self.__port_name.setFlag(QGraphicsItem.ItemStacksBehindParent)

        self.port_name_position = port_name_position

        # Colors/Pens/Brushes

        self.__color = TypeColor.get_color_for(graphics_port._port.port_type)

        self.__outline_pen = QPen(self.__color.darker())
        self.__outline_pen.setWidthF(2)
        self.__background_brush = QBrush(self.__color)

        self.__dashed_outline_pen = QPen(self.__color)
        self.__dashed_outline_pen.setStyle(Qt.DashLine)
        self.__dashed_outline_pen.setWidth(2)

    @property
    def port_name_position(self) -> "PortNamePosition":
        return self.__port_name_position

    @port_name_position.setter
    def port_name_position(self, position: "PortNamePosition"):
        if position == self.PortNamePosition.Left:
            self.__port_name.setPos(
                -self.__port_name.boundingRect().width() - 3, 1)
        elif position == self.PortNamePosition.Right:
            self.__port_name.setPos(3, 1)

        self.__port_name_position = position

    @property
    def color(self):
        return self.__color

    def paint_area(self):
        return QRectF(
            -self.__graphics_port.radius,
            -self.__graphics_port.radius,
            2 * self.__graphics_port.radius,
            2 * self.__graphics_port.radius,
        )

    def paint(
        self,
        painter: "QPainter",
        option: "QStyleOptionGraphicsItem",
        widget: "QWidget" = None,
    ):
        if (GraphicsPortPainter.drawing_state
                == GraphicsPortPainter.DrawingState.Target
                and self.__graphics_port.port_type
                == GraphicsPortPainter.target_port_type):
            painter.setPen(self.__dashed_outline_pen)
            painter.setBrush(Qt.NoBrush)
            painter.drawEllipse(self.__graphics_port.boundingRect())

        painter.setPen(self.__outline_pen)
        painter.setBrush(self.__background_brush)
        painter.drawEllipse(self.paint_area())
Ejemplo n.º 13
0
class PortGraphics(QGraphicsItem):
    """
	This class defines the graphics for displaying a port in the APIM View.

	A port is shaped somewhat like an inverted, elongated pentagon.
	"""

    REQUIRED_PEN_WIDTH = 5.0
    OPTIONAL_PEN_WIDTH = 1.0
    WIDTH = 50
    SIDE_HEIGHT = 1.5 * WIDTH
    TAPER_HEIGHT = 0.5 * SIDE_HEIGHT
    TOTAL_HEIGHT = SIDE_HEIGHT + TAPER_HEIGHT
    # Center the shape about the origin of its coordinate system.
    X_POS = -0.5 * WIDTH
    Y_POS = -0.5 * TOTAL_HEIGHT

    PEN_COLOR = QColor(Qt.black)

    INNER_COLOR = QColor(100, 200, 0)
    OUTER_COLOR = QColor(252, 140, 3)

    NAME_FONT = QFont("Times", 15)
    TYPE_FONT = QFont("Times", 15)

    def __init__(self,
                 port: 'Port',
                 parent: QGraphicsItem = None,
                 menuEnabled: bool = True):
        """
		Constructs a portGraphics object for the given Port object.
		
		:param port: The port for which this graphics item represents.
		:type port: Port
		:param parent: A QGraphicsItem (probably an actionGraphics object)
		:type parent: QGraphicsItem
		:param menuEnabled: If true, a context menu will be shown on right-click.
		:type menuEnabled: bool
		"""
        QGraphicsItem.__init__(self, parent)
        self.setAcceptDrops(True)
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self._port = port
        self._menuEnabled = menuEnabled
        self.menu = PortMenu()

        # If port is required and it's an input, make the border thicker
        if not self._port.isOptional() and self._port in self._port.getAction(
        ).getInputPorts():
            self.borderWidth = PortGraphics.REQUIRED_PEN_WIDTH
        else:
            self.borderWidth = PortGraphics.OPTIONAL_PEN_WIDTH

        # show port name and type
        if self._menuEnabled:
            fm = QFontMetricsF(PortGraphics.NAME_FONT)
            name = fm.elidedText(self._port.getName(), Qt.ElideRight,
                                 PortGraphics.SIDE_HEIGHT)
            self.nameItem = QGraphicsTextItem(name, self)
            self.nameItem.setFont(PortGraphics.NAME_FONT)
            self.nameItem.setRotation(90)
            self.nameItem.setPos(PortGraphics.WIDTH / 2 + 5,
                                 -PortGraphics.TOTAL_HEIGHT / 2)

            fm = QFontMetricsF(PortGraphics.TYPE_FONT)
            t = fm.elidedText(self._port.getDataType().__name__, Qt.ElideRight,
                              PortGraphics.SIDE_HEIGHT)
            self.typeItem = QGraphicsTextItem(t, self)
            self.typeItem.setFont(PortGraphics.TYPE_FONT)
            self.typeItem.setRotation(90)
            self.typeItem.setPos(5, -PortGraphics.TOTAL_HEIGHT / 2)

    def getPort(self):
        """
		Returns the PortGraphics' Port.
		:return:
		"""
        return self._port

    def boundingRect(self) -> QRectF:
        """
		This pure virtual function defines the outer bounds of the item as a rectangle.
		:return: create the bounding of the item
		:rtype: QRectF
		"""
        halfPenWidth = self.borderWidth / 2
        x = PortGraphics.X_POS - halfPenWidth
        y = PortGraphics.Y_POS - halfPenWidth

        actualWidth = PortGraphics.WIDTH + self.borderWidth
        actualHeight = PortGraphics.TOTAL_HEIGHT + self.borderWidth

        return QRectF(x, y, actualWidth, actualHeight)

    def shape(self) -> QPainterPath:
        """
		Returns the shape of the Port as a QPainterPath. Ports are shaped like an inverted, elongated pentagon.

		:return: The shape of the Port as a QPainterPath.
		:rtype: QPainterPath
		"""
        # Create the polygon (pentagon-like shape)
        poly = QPolygon()
        poly << QPoint(PortGraphics.X_POS, PortGraphics.Y_POS)
        poly << QPoint(PortGraphics.X_POS + PortGraphics.WIDTH,
                       PortGraphics.Y_POS)
        poly << QPoint(PortGraphics.X_POS + PortGraphics.WIDTH,
                       PortGraphics.Y_POS + PortGraphics.SIDE_HEIGHT)
        poly << QPoint(0, PortGraphics.Y_POS + PortGraphics.TOTAL_HEIGHT)
        poly << QPoint(PortGraphics.X_POS,
                       PortGraphics.Y_POS + PortGraphics.SIDE_HEIGHT)
        poly << QPoint(PortGraphics.X_POS, PortGraphics.Y_POS)

        path = QPainterPath()
        path.addPolygon(poly)
        return path

    def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem,
              widget: QWidget) -> None:
        """
		Paints a port. This function is used implicitly by the QGraphicsView to render a port.
		
		:param painter: The painter to paint with.
		:type painter: QPainter
		:param option: provides style options for the item.
		:type option: QStyleOptionGraphicsItem
		:param widget: QWidget
		:type widget: It points to the widget that is being painted on; or make it = None.
		:return: None
		:rtype: NoneType
		"""

        pen = QPen(PortGraphics.PEN_COLOR)
        pen.setWidth(self.borderWidth)
        painter.setPen(pen)

        if type(self._port.getAction()) == ActionWrapper:
            painter.setBrush(PortGraphics.INNER_COLOR)
        else:
            painter.setBrush(PortGraphics.OUTER_COLOR)
        painter.drawPath(self.shape())

    def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent) -> None:
        """
		Opens a context menu (right click menu) for the component.

		:param event: The event that was generated when the user right-clicked on this item.
		:type event: QGraphicsSceneContextMenuEvent
		:return: None
		:rtype: NoneType
		"""
        return QGraphicsItem.contextMenuEvent(event)

        # if not self._menuEnabled:
        # 	return QGraphicsItem.contextMenuEvent(self, event)
        #
        # self.setSelected(True)
        # self.menu.exec_(event.screenPos())

    def mousePressEvent(self, event):
        event.ignore()
Ejemplo n.º 14
0
class AbstractNode(AbstractGeneralGraphicsItemClass):
    __metaclass__ = AbstractGeneralGraphicsItem_Meta

    def __init__(self, graphWidget, state):
        super(AbstractNode, self).__init__()

        self.graph = weakref.ref(graphWidget)
        self.isAcceptable = state.isAcceptable
        self.isCurrent = state.isCurrent

        self.newPos = QPointF()
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
        self.setCacheMode(self.DeviceCoordinateCache)
        self.setZValue(-1)
        self.setAcceptHoverEvents(True)

        self._edges = []
        self.currentFill = self.fillQColor
        self.border = QGraphicsEllipseItem(-self.radius, -self.radius,
                                           self.radius * 2, self.radius * 2)
        self.finalNodeBorder = QGraphicsEllipseItem(-self.radius + 5,
                                                    -self.radius + 5,
                                                    self.radius * 2 - 10,
                                                    self.radius * 2 - 10)
        self.finalNodeBorder.mapToParent(self.border.pos())
        if self.isAcceptable == False:
            pen = self.finalNodeBorder.pen()
            pen.setColor(QColor(25, 255, 5))
            self.finalNodeBorder.update()

        if self.isCurrent == True:
            self.resolveFillColor()

        self.labelBox = QGraphicsTextItem(self)
        self.labelBox.setPos(-self.radius / 2, -self.radius / 2)
        self.labelBox.setDefaultTextColor(QColor(0, 0, 0))

    @property
    @abstractmethod
    def borderQColor(self):
        pass

    @property
    @abstractmethod
    def fillQColor(self):
        pass

    @property
    @abstractmethod
    def currentStateFillQColor(self):
        pass

    @property
    @abstractmethod
    def radius(self):
        pass

    @abstractmethod
    def setLabel(self, value):
        pass

    def toggleCurrentState(self):
        self.isCurrent = not self.isCurrent
        self.resolveFillColor()

    def resolveFillColor(self):
        brush = QBrush()
        if self.isCurrent == True:
            brush.setColor(self.currentStateFillQColor)
            brush.setStyle(Qt.SolidPattern)
            self.currentFill = self.currentStateFillQColor
        else:
            brush.setColor(self.fillQColor)
            brush.setStyle(Qt.SolidPattern)
            self.currentFill = self.fillQColor
        self.border.setBrush(brush)
        self.update()

    def toggleFinalNodeBorder(self):
        self.isAcceptable = not self.isAcceptable
        self.resolveFinalNodeBorder()

    def resolveFinalNodeBorder(self):
        pen = QPen()
        if self.isAcceptable == True:
            pen.setColor(self.borderQColor)
            pen.setWidth(2)
        else:
            pen.setColor(self.currentFill)
            pen.setWidth(1)
        self.finalNodeBorder.setPen(pen)
        self.update()

    def addEdge(self, edge):
        self._edges.append(weakref.ref(edge))
        edge.adjust()

    def getEdges(self):
        return self._edges

    def getEdgesForInput(self, input):
        edges = []
        for edge in self._edges:
            if edge.onInput == input:
                edges.append(edge)
        return edges

    def getEdgesFromSelf(self, input):
        edges = []
        for edge in self._edges:
            if edge.onInput == input and edge.fromNode is self:
                edges.append(edge)
        return edges

    def getEdgeToNode(self, toNode):
        toReturn = None
        for edge in self._edges:
            if edge.toNode() is toNode:
                toReturn = edge
        return toReturn

    def boundingRect(self):
        adjust = 2.0
        return QRectF(-self.radius - adjust, -self.radius - adjust,
                      (self.radius + adjust) * 2, (self.radius + adjust) * 2)

    def shape(self):
        path = QtGui.QPainterPath()
        path.addEllipse(-self.radius, -self.radius, self.radius * 2,
                        self.radius * 2)
        return path

    def paint(self, painter, option, widget):
        pen = QPen()
        pen.setWidth(2)
        pen.setColor(self.borderQColor)
        brush = QBrush()
        self.border.setPen(pen)
        self.border.setBrush(brush)
        self.resolveFillColor()
        self.resolveFinalNodeBorder()
        self.border.paint(painter, option, widget)
        self.finalNodeBorder.paint(painter, option, widget)
        self.labelBox.setTextWidth(35)
        self.labelBox.setDefaultTextColor(QColor(0, 0, 0))

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionChange:
            for edge in self._edges:
                edge().adjust()

        return QGraphicsItem.itemChange(self, change, value)

    def mousePressEvent(self, event):
        self.update()
        QGraphicsItem.mousePressEvent(self, event)

    def mouseReleaseEvent(self, event):
        QGraphicsItem.mouseReleaseEvent(self, event)
        self.update()
Ejemplo n.º 15
0
class GraphicsNodePainter:
    def __init__(self, graphics_node: "GraphicsNode"):
        self.__graphics_node = graphics_node

        # Dimensions
        self.padding = 12
        self.clickable_margin = 15
        self.round_edge_size = 10
        self.window_outline_width = 4

        # Colors/Pens/Brushes
        self.__title_color = Qt.white
        self.__outline_selected_color = QColor("#FFA637")
        self.__outline_default_color = QColor("#000000")

        self.__outline_pen = QPen(self.__outline_default_color)
        self.__title_background_brush = QBrush(QColor("#FF313131"))
        self.__background_brush = QBrush(QColor("#E3212121"))

        self.__window_markers_pen = QPen(self.__outline_default_color)
        self.__window_markers_brush = QBrush(self.__outline_default_color)

        # Graphics Components
        self.__graphics_title = QGraphicsTextItem(parent=graphics_node)
        self.__graphics_title.setDefaultTextColor(self.__title_color)
        self.__graphics_title.setPlainText(self.__graphics_node.title)
        self.__graphics_title.setPos(self.padding, 0)

        # Position ProxyWidget
        self.repositionWidget()

        # position GraphicsPort objects
        def position_graphics_ports(x_offset, graphics_ports_dict):
            for i, graphics_port in enumerate(graphics_ports_dict.values()):
                graphics_port.setPos(
                    x_offset,
                    self.title_height() + (graphics_port.radius * 4) * (i + 1))

        position_graphics_ports(0, self.__graphics_node.inputs)
        position_graphics_ports(self.boundingRect().width(),
                                self.__graphics_node.outputs)

    @property
    def outline_selected_color(self) -> "QColor":
        return self.__outline_selected_color

    @outline_selected_color.setter
    def outline_selected_color(self, color: "QColor"):
        self.__outline_selected_color = color

    @property
    def outline_default_color(self) -> "QColor":
        return self.__outline_default_color

    @outline_default_color.setter
    def outline_default_color(self, color: "QColor"):
        self.__outline_default_color = color

    def boundingRect(self) -> "QRectF":
        return self.background_paint_area()

    def background_paint_area(self) -> "QRectF":
        proxy_rect = self.__graphics_node._proxy_widget.boundingRect()

        return proxy_rect.adjusted(
            0,
            0,
            self.padding * 2,
            self.title_height() + self.padding * 2,
        ).normalized()

    def repositionWidget(self):
        self.__graphics_node._proxy_widget.setPos(
            self.padding,
            self.title_height() + self.padding)

    def recalculateGeometry(self):
        for graphics_port in self.__graphics_node.outputs.values():
            graphics_port.setX(self.boundingRect().width())

    def itemChange(self, change: "QGraphicsItem.GraphicsItemChange",
                   value: Any) -> Any:
        if change == QGraphicsItem.ItemSelectedChange:
            self.__outline_pen.setColor(self.__outline_selected_color if value
                                        else self.__outline_default_color)

        return value

    def title_height(self) -> int:
        """Returns the height of the title graphics item."""
        return self.__graphics_title.boundingRect().height()

    def paint(self, painter: "QPainter", option: "QStyleOptionGraphicsItem",
              widget: "QWidget"):
        """Paints the GraphicsNode item."""

        self.__paint_background(painter)
        self.__paint_title_background(painter)
        self.__paint_outline(painter)
        self.__paint_window_markers(painter)

    def __paint_background(self, painter: "QPainter"):
        """Paints the background of the node. Plain color, no lines."""
        path_background = QPainterPath()
        path_background.addRoundedRect(
            self.background_paint_area(),
            self.round_edge_size,
            self.round_edge_size,
        )

        painter.setPen(Qt.NoPen)
        painter.setBrush(self.__background_brush)

        painter.drawPath(path_background.simplified())

    def __paint_title_background(self, painter: "QPainter"):
        """Paints a little background behind the title text, at the top of the node."""
        title_rect = self.__graphics_title.boundingRect()

        path_title_background = QPainterPath()
        path_title_background.setFillRule(Qt.WindingFill)
        path_title_background.addRoundedRect(
            0,
            0,
            self.background_paint_area().width(),
            title_rect.height(),
            self.round_edge_size,
            self.round_edge_size,
        )

        # (Drawing rects to hide the two botton round edges)
        path_title_background.addRect(
            0,
            title_rect.height() - self.round_edge_size,
            self.round_edge_size,
            self.round_edge_size,
        )

        path_title_background.addRect(
            self.background_paint_area().width() - self.round_edge_size,
            title_rect.height() - self.round_edge_size,
            self.round_edge_size,
            self.round_edge_size,
        )

        painter.setPen(Qt.NoPen)
        painter.setBrush(self.__title_background_brush)
        painter.drawPath(path_title_background.simplified())

    def __paint_window_markers(self, painter: "QPainter"):
        title_width = self.__graphics_title.boundingRect().width()

        for i, parent_window in reversed(
                list(enumerate(self.__graphics_node.parent_node_windows))):
            self.__window_markers_brush.setColor(
                parent_window.color_identifier)
            self.__window_markers_pen.setColor(
                parent_window.color_identifier.darker())

            painter.setPen(self.__window_markers_pen)
            painter.setBrush(self.__window_markers_brush)
            painter.drawRect(title_width + 20 + i * 25, 8, 15, 15)

    def __paint_outline(self, painter: "QPainter"):
        """Paints the outline of the node. Depending on if its selected or not, the
        color of the outline changes."""
        path_outline = QPainterPath()
        path_outline.addRoundedRect(
            self.boundingRect(),
            self.round_edge_size,
            self.round_edge_size,
        )

        painter.setPen(self.__outline_pen)
        painter.setBrush(Qt.NoBrush)
        painter.drawPath(path_outline.simplified())
Ejemplo n.º 16
0
    def eventFilter(self, obj, event):
        if event.type() == QEvent.GraphicsSceneMousePress:
            if event.button() == Qt.MouseButton.LeftButton:
                self.mouse_pressed = True

                self.mouse_pressed_x, self.mouse_pressed_y = event.pos().x(
                ), event.pos().y()

                if self.draw_ellipse:
                    ellipsis = QGraphicsEllipseItem(self.chart)

                    ellipsis.setZValue(12)
                    ellipsis.setBrush(QBrush(QColor(244, 67, 54, 50)))
                    ellipsis.setPen(QPen(Qt.transparent))

                    self.ellipses.append(ellipsis)
                elif self.write_text:
                    for t in self.texts:
                        r = QRectF()
                        r.setTopLeft(t.pos())
                        r.setWidth(t.boundingRect().width())
                        r.setHeight(t.boundingRect().height())

                        if r.contains(self.mouse_pressed_x,
                                      self.mouse_pressed_y):
                            return True
                    """
                        The user clicked over an area where there is no text. So we create one.
                    """

                    text = QGraphicsTextItem(self.chart)

                    text.setZValue(12)
                    text.setPos(
                        QPointF(self.mouse_pressed_x, self.mouse_pressed_y))
                    text.setPlainText("label")
                    text.setAcceptHoverEvents(True)
                    text.setTabChangesFocus(True)
                    text.setFlags(QGraphicsTextItem.ItemIsMovable)
                    text.installEventFilter(self.text_event_filter)

                    self.texts.append(text)

                return True
            elif event.button() == Qt.MouseButton.RightButton:
                x, y = event.pos().x(), event.pos().y()

                for e in self.ellipses:
                    if e.rect().contains(x, y):
                        e.hide()

                        self.ellipses.remove(e)

                for t in self.texts:
                    r = QRectF()
                    r.setTopLeft(t.pos())
                    r.setWidth(t.boundingRect().width())
                    r.setHeight(t.boundingRect().height())

                    if r.contains(x, y):
                        t.hide()

                        self.texts.remove(t)

                return True

            return QObject.eventFilter(self, obj, event)

        elif event.type() == QEvent.GraphicsSceneMouseRelease:
            self.mouse_pressed = False

            return True

        elif event.type() == QEvent.GraphicsSceneMouseMove:
            if self.mouse_pressed:
                if self.draw_ellipse:
                    x, y = event.pos().x(), event.pos().y()

                    width = x - self.mouse_pressed_x
                    height = y - self.mouse_pressed_y

                    self.ellipses[-1].setRect(self.mouse_pressed_x,
                                              self.mouse_pressed_y, width,
                                              height)

                    return True

            return QObject.eventFilter(self, obj, event)

        return QObject.eventFilter(self, obj, event)