Esempio n. 1
0
class RDisc(QObject):
    def __init__(self, r, x, y, color, line_color, line_width):
        self._radius = r
        self._pos = QPointF(x, y)
        super().__init__()
        # The supporting rectangle
        self.rect = QRectF(x - r, y - r, 2 * r, 2 * r)
        # The underlying QGraphicsEllipseItem
        self.disc = QGraphicsEllipseItem()
        self.disc.setRect(self.rect)
        self.disc.setBrush(QtGui.QBrush(color))
        pen = QPen()
        pen.setWidthF(line_width)
        pen.setColor(line_color)
        self.disc.setPen(pen)
        self._visible = 1

    def x(self):
        return self._pos.x()

    def y(self):
        return self._pos.y()

    # The following functions are for animation support

    @pyqtProperty(QPointF)
    def pos(self):
        return self._pos

    @pos.setter
    def pos(self, value):
        self.rect = QRectF(value.x() - self._radius,
                           value.y() - self._radius, 2 * self._radius,
                           2 * self._radius)
        self.disc.setRect(self.rect)
        self._pos = value

    @pyqtProperty(int)
    def visible(self):
        return self._visible

    @visible.setter
    def visible(self, value):
        if (value > 0):
            self.disc.show()
        else:
            self.disc.hide()
        self._visible = value
Esempio n. 2
0
class AbstractSliceTool(QGraphicsObject):
    """Summary

    Attributes:
        angles (TYPE): Description
        FILTER_NAME (str): Description
        is_started (bool): Description
        manager (TYPE): Description
        part_item (TYPE): Description
        sgv (TYPE): Description
        vectors (TYPE): Description
    """
    _RADIUS = styles.SLICE_HELIX_RADIUS
    _CENTER_OF_HELIX = QPointF(_RADIUS, _RADIUS)
    FILTER_NAME = 'virtual_helix'
    # _CENTER_OF_HELIX = QPointF(0. 0.)
    """Abstract base class to be subclassed by all other pathview tools."""
    def __init__(self, manager):
        """Summary

        Args:
            manager (TYPE): Description
        """
        super(AbstractSliceTool, self).__init__(parent=manager.viewroot)
        """ Pareting to viewroot to prevent orphan _line_item from occuring
        """
        self.sgv = None
        self.manager = manager
        self._active = False
        self._last_location = None
        self._line_item = QGraphicsLineItem(self)
        self._line_item.hide()
        self._vhi = None

        self.hide()
        self.is_started = False
        self.angles = [math.radians(x) for x in range(0, 360, 30)]
        self.vectors = self.setVectors()
        self.part_item = None

        self.vhi_hint_item = QGraphicsEllipseItem(_DEFAULT_RECT, self)
        self.vhi_hint_item.setPen(_MOD_PEN)
        self.vhi_hint_item.setZValue(styles.ZPARTITEM)
    # end def

    ######################## Drawing #######################################
    def setVectors(self):
        """Summary

        Returns:
            TYPE: Description
        """
        rad = self._RADIUS
        return [QLineF(rad, rad,
                       rad*(1. + 2.*math.cos(x)), rad*(1. + 2.*math.sin(x))
                       ) for x in self.angles]
    # end def

    def setVirtualHelixItem(self, virtual_helix_item):
        """Summary

        Args:
            virtual_helix_item (cadnano.gui.views.sliceview.virtualhelixitem.VirtualHelixItem): Description

        Returns:
            TYPE: Description
        """
        rad = self._RADIUS
        self._vhi = virtual_helix_item
        li = self._line_item
        li.setParentItem(virtual_helix_item)
        li.setLine(rad, rad, rad, rad)
        # li.setLine(0., 0., 0., 0.)
    # end def

    def setSelectionFilter(self, filter_name_list):
        if 'virtual_helix' in filter_name_list:
            self.vhi_hint_item.setPen(_MOD_PEN)
        else:
            self.vhi_hint_item.setPen(_INACTIVE_PEN)
    # end def

    def resetTool(self):
        """Summary

        Returns:
            TYPE: Description
        """
        self._line_item.setParentItem(self)

    def idNum(self):
        """Summary

        Returns:
            TYPE: Description
        """
        if self._vhi is not None:
            return self._vhi.idNum()

    def setPartItem(self, part_item):
        """Summary

        Args:
            part_item (TYPE): Description

        Returns:
            TYPE: Description
        """
        self.vhi_hint_item.setParentItem(part_item)
        self.part_item = part_item
    # end def

    def boundingRect(self):
        """Required to prevent NotImplementedError()
        """
        return QRectF()

    def eventToPosition(self, part_item, event):
        """take an event and return a position as a QPointF
        update widget as well

        Args:
            part_item (TYPE): Description
            event (TYPE): Description
        """
        if self.is_started:
            pos = self.findNearestPoint(part_item, event.scenePos())
        else:
            pos =  event.pos()
        self.vhi_hint_item.setPos(  pos -
                                    QPointF(_RADIUS - DELTA, _RADIUS - DELTA))
        return pos
    # end def

    def setHintPos(self, pos):
        self.vhi_hint_item.setPos(  pos -
                                    QPointF(_RADIUS - DELTA, _RADIUS - DELTA))
    # end def

    def findNearestPoint(self, part_item, target_scenepos):
        """
        Args:
            part_item (TYPE): Description
            target_scenepos (TYPE): Description
        """
        li = self._line_item
        pos = li.mapFromScene(target_scenepos)

        line = li.line()
        mouse_point_vec = QLineF(self._CENTER_OF_HELIX, pos)

        # Check if the click happened on the origin VH
        if mouse_point_vec.length() < self._RADIUS:
            # return part_item.mapFromScene(target_scenepos)
            return None

        angle_min = 9999
        direction_min = None
        for vector in self.vectors:
            angle_new = mouse_point_vec.angleTo(vector)
            if angle_new < angle_min:
                direction_min = vector
                angle_min = angle_new
        if direction_min is not None:
            li.setLine(direction_min)
            return part_item.mapFromItem(li, direction_min.p2())
        else:
            print("default point")
            line.setP2(pos)
            li.setLine(line)
            return part_item.mapFromItem(li, pos)
    # end def

    def findNextPoint(self, part_item, target_part_pos):
        """
        Args:
            part_item (TYPE): Description
            target_part_pos (TYPE): Description
        """
        li = self._line_item
        pos = li.mapFromItem(part_item, target_part_pos)
        for i, vector in enumerate(self.vectors):
            if vector.p2() == pos:
                return part_item.mapFromItem(li, self.vectors[i - 1].p2())
        # origin VirtualHelixItem is overlapping destination VirtualHelixItem
        return part_item.mapFromItem(li, self.vectors[0].p2())
    # end def

    def hideLineItem(self):
        """Summary

        Returns:
            TYPE: Description
        """
        self.vhi_hint_item.hide()
        li = self._line_item
        li.hide()
        li.setParentItem(self)
        line = li.line()
        line.setP2(self._CENTER_OF_HELIX)
        li.setLine(line)
        # li.hide()
        self.is_started = False
    # end def

    # def hoverEnterEvent(self, event):
    #     self.vhi_hint_item.show()
    #     #print("Slice VHI hoverEnterEvent")

    # # def hoverMoveEvent(self, event):
    #     # print("Slice VHI hoverMoveEvent")

    # def hoverLeaveEvent(self, event):
    #     # self.vhi_hint_item.hide()
    #     #print("Slice VHI hoverLeaveEvent")


    def hoverMoveEvent(self, part_item, event):
        """Summary

        Args:
            part_item (TYPE): Description
            event (TYPE): Description

        Returns:
            TYPE: Description
        """
        # self.vhi_hint_item.setPos(  event.pos()-
        #                             QPointF(_RADIUS - DELTA, _RADIUS - DELTA))
        pos = self.eventToPosition(part_item, event)
        return pos
    # end def

    def setActive(self, will_be_active, old_tool=None):
        """
        Called by SliceToolManager.setActiveTool when the tool becomes
        active. Used, for example, to show/hide tool-specific ui elements.

        Args:
            will_be_active (TYPE): Description
            old_tool (None, optional): Description
        """
        if self._active and not will_be_active:
            self.deactivate()
        self._active = will_be_active
        self.sgv = self.manager.window.slice_graphics_view
        if hasattr(self, 'getCustomContextMenu'):
            # print("connecting ccm")
            try:    # Hack to prevent multiple connections
                self.sgv.customContextMenuRequested.disconnect()
            except:
                pass
            self.sgv.customContextMenuRequested.connect(self.getCustomContextMenu)
    # end def

    def deactivate(self):
        """Summary

        Returns:
            TYPE: Description
        """
        if hasattr(self, 'getCustomContextMenu'):
            # print("disconnecting ccm")
            self.sgv.customContextMenuRequested.disconnect(self.getCustomContextMenu)
        self.sgv = None
        self.is_started = False
        self.hideLineItem()
        self._vhi = None
        self.part_item = None
        self.hide()
        self._active = False
    # end def

    def isActive(self):
        """Returns isActive
        """
        return self._active
Esempio n. 3
0
class AbstractSliceTool(QGraphicsObject):
    """Summary

    Attributes:
        angles (TYPE): Description
        FILTER_NAME (str): Description
        is_started (bool): Description
        manager (TYPE): Description
        part_item (TYPE): Description
        sgv (TYPE): Description
        vectors (TYPE): Description
    """
    _RADIUS = styles.SLICE_HELIX_RADIUS
    _CENTER_OF_HELIX = QPointF(_RADIUS, _RADIUS)
    FILTER_NAME = 'virtual_helix'
    # _CENTER_OF_HELIX = QPointF(0. 0.)
    """Abstract base class to be subclassed by all other pathview tools."""
    def __init__(self, manager):
        """Summary

        Args:
            manager (TYPE): Description
        """
        super(AbstractSliceTool, self).__init__(parent=manager.viewroot)
        """ Pareting to viewroot to prevent orphan _line_item from occuring
        """
        self.sgv = None
        self.manager = manager
        self._active = False
        self._last_location = None
        self._line_item = QGraphicsLineItem(self)
        self._line_item.hide()
        self._vhi = None

        self.hide()
        self.is_started = False
        self.angles = [math.radians(x) for x in range(0, 360, 30)]
        self.vectors = self.setVectors()
        self.part_item = None

        self.vhi_hint_item = QGraphicsEllipseItem(_DEFAULT_RECT, self)
        self.vhi_hint_item.setPen(_MOD_PEN)
        self.vhi_hint_item.setZValue(styles.ZPARTITEM)

    # end def

    ######################## Drawing #######################################
    def setVectors(self):
        """Summary

        Returns:
            TYPE: Description
        """
        rad = self._RADIUS
        return [
            QLineF(rad, rad, rad * (1. + 2. * math.cos(x)),
                   rad * (1. + 2. * math.sin(x))) for x in self.angles
        ]

    # end def

    def setVirtualHelixItem(self, virtual_helix_item):
        """Summary

        Args:
            virtual_helix_item (cadnano.gui.views.sliceview.virtualhelixitem.VirtualHelixItem): Description

        Returns:
            TYPE: Description
        """
        rad = self._RADIUS
        self._vhi = virtual_helix_item
        li = self._line_item
        li.setParentItem(virtual_helix_item)
        li.setLine(rad, rad, rad, rad)
        # li.setLine(0., 0., 0., 0.)

    # end def

    def setSelectionFilter(self, filter_name_list):
        if 'virtual_helix' in filter_name_list:
            self.vhi_hint_item.setPen(_MOD_PEN)
        else:
            self.vhi_hint_item.setPen(_INACTIVE_PEN)

    # end def

    def resetTool(self):
        """Summary

        Returns:
            TYPE: Description
        """
        self._line_item.setParentItem(self)

    def idNum(self):
        """Summary

        Returns:
            TYPE: Description
        """
        if self._vhi is not None:
            return self._vhi.idNum()

    def setPartItem(self, part_item):
        """Summary

        Args:
            part_item (TYPE): Description

        Returns:
            TYPE: Description
        """
        self.vhi_hint_item.setParentItem(part_item)
        self.part_item = part_item

    # end def

    def boundingRect(self):
        """Required to prevent NotImplementedError()
        """
        return QRectF()

    def eventToPosition(self, part_item, event):
        """take an event and return a position as a QPointF
        update widget as well

        Args:
            part_item (TYPE): Description
            event (TYPE): Description
        """
        if self.is_started:
            pos = self.findNearestPoint(part_item, event.scenePos())
        else:
            pos = event.pos()
        self.vhi_hint_item.setPos(pos -
                                  QPointF(_RADIUS - DELTA, _RADIUS - DELTA))
        return pos

    # end def

    def setHintPos(self, pos):
        self.vhi_hint_item.setPos(pos -
                                  QPointF(_RADIUS - DELTA, _RADIUS - DELTA))

    # end def

    def findNearestPoint(self, part_item, target_scenepos):
        """
        Args:
            part_item (TYPE): Description
            target_scenepos (TYPE): Description
        """
        li = self._line_item
        pos = li.mapFromScene(target_scenepos)

        line = li.line()
        mouse_point_vec = QLineF(self._CENTER_OF_HELIX, pos)

        # Check if the click happened on the origin VH
        if mouse_point_vec.length() < self._RADIUS:
            # return part_item.mapFromScene(target_scenepos)
            return None

        angle_min = 9999
        direction_min = None
        for vector in self.vectors:
            angle_new = mouse_point_vec.angleTo(vector)
            if angle_new < angle_min:
                direction_min = vector
                angle_min = angle_new
        if direction_min is not None:
            li.setLine(direction_min)
            return part_item.mapFromItem(li, direction_min.p2())
        else:
            print("default point")
            line.setP2(pos)
            li.setLine(line)
            return part_item.mapFromItem(li, pos)

    # end def

    def findNextPoint(self, part_item, target_part_pos):
        """
        Args:
            part_item (TYPE): Description
            target_part_pos (TYPE): Description
        """
        li = self._line_item
        pos = li.mapFromItem(part_item, target_part_pos)
        for i, vector in enumerate(self.vectors):
            if vector.p2() == pos:
                return part_item.mapFromItem(li, self.vectors[i - 1].p2())
        # origin VirtualHelixItem is overlapping destination VirtualHelixItem
        return part_item.mapFromItem(li, self.vectors[0].p2())

    # end def

    def hideLineItem(self):
        """Summary

        Returns:
            TYPE: Description
        """
        self.vhi_hint_item.hide()
        li = self._line_item
        li.hide()
        li.setParentItem(self)
        line = li.line()
        line.setP2(self._CENTER_OF_HELIX)
        li.setLine(line)
        # li.hide()
        self.is_started = False

    # end def

    # def hoverEnterEvent(self, event):
    #     self.vhi_hint_item.show()
    #     #print("Slice VHI hoverEnterEvent")

    # # def hoverMoveEvent(self, event):
    #     # print("Slice VHI hoverMoveEvent")

    # def hoverLeaveEvent(self, event):
    #     # self.vhi_hint_item.hide()
    #     #print("Slice VHI hoverLeaveEvent")

    def hoverMoveEvent(self, part_item, event):
        """Summary

        Args:
            part_item (TYPE): Description
            event (TYPE): Description

        Returns:
            TYPE: Description
        """
        # self.vhi_hint_item.setPos(  event.pos()-
        #                             QPointF(_RADIUS - DELTA, _RADIUS - DELTA))
        pos = self.eventToPosition(part_item, event)
        return pos

    # end def

    def setActive(self, will_be_active, old_tool=None):
        """
        Called by SliceToolManager.setActiveTool when the tool becomes
        active. Used, for example, to show/hide tool-specific ui elements.

        Args:
            will_be_active (TYPE): Description
            old_tool (None, optional): Description
        """
        if self._active and not will_be_active:
            self.deactivate()
        self._active = will_be_active
        self.sgv = self.manager.window.slice_graphics_view
        if hasattr(self, 'getCustomContextMenu'):
            # print("connecting ccm")
            try:  # Hack to prevent multiple connections
                self.sgv.customContextMenuRequested.disconnect()
            except:
                pass
            self.sgv.customContextMenuRequested.connect(
                self.getCustomContextMenu)

    # end def

    def deactivate(self):
        """Summary

        Returns:
            TYPE: Description
        """
        if hasattr(self, 'getCustomContextMenu'):
            # print("disconnecting ccm")
            self.sgv.customContextMenuRequested.disconnect(
                self.getCustomContextMenu)
        self.sgv = None
        self.is_started = False
        self.hideLineItem()
        self._vhi = None
        self.part_item = None
        self.hide()
        self._active = False

    # end def

    def isActive(self):
        """Returns isActive
        """
        return self._active
Esempio n. 4
0
class StickWidget(QGraphicsObject):

    font: QFont = QFont("monospace", 32)

    delete_clicked = pyqtSignal(Stick)
    link_initiated = pyqtSignal('PyQt_PyObject') # Actually StickWidget
    link_accepted = pyqtSignal('PyQt_PyObject')
    hovered = pyqtSignal(['PyQt_PyObject', 'PyQt_PyObject'])
    stick_changed = pyqtSignal('PyQt_PyObject')
    sibling_changed = pyqtSignal(bool)
    right_clicked = pyqtSignal('PyQt_PyObject')

    handle_idle_brush = QBrush(QColor(0, 125, 125, 50))
    handle_hover_brush = QBrush(QColor(125, 125, 0, 50))
    handle_press_brush = QBrush(QColor(200, 200, 0, 0))
    handle_idle_pen = QPen(QColor(0, 0, 0, 255))
    handle_press_pen = QPen(QColor(200, 200, 0, 255))
    handle_size = 20

    normal_color = QColor(0, 200, 120)
    negative_color = QColor(200, 0, 0)
    positive_color = QColor(0, 200, 0)

    mismatched = pyqtSignal('PyQt_PyObject')
    misplaced = pyqtSignal('PyQt_PyObject')
    measurement_corrected = pyqtSignal('PyQt_PyObject')
    clearly_visible = pyqtSignal('PyQt_PyObject')
    zero_clicked = pyqtSignal('PyQt_PyObject')

    def __init__(self, stick: Stick, camera: Camera, parent: Optional[QGraphicsItem] = None):
        QGraphicsObject.__init__(self, parent)
        self.camera = camera
        self.stick = stick
        self.line = QLineF()
        self.gline = QGraphicsLineItem(self.line)

        self.stick_label_text = QGraphicsSimpleTextItem("0", self)
        self.stick_label_text.setFont(StickWidget.font)
        self.stick_label_text.setPos(self.line.p1() - QPoint(0, 24))
        self.stick_label_text.setBrush(QBrush(QColor(0, 255, 0)))
        self.stick_label_text.hide()
        self.setZValue(10)

        self.mode = StickMode.Display

        self.btn_delete = Button("delete", "x", parent=self)
        self.btn_delete.setFlag(QGraphicsItem.ItemIgnoresTransformations, True)
        self.btn_delete.set_base_color([ButtonColor.RED])
        self.btn_delete.setVisible(False)
        btn_size = max(int(np.linalg.norm(self.stick.top - self.stick.bottom) / 5.0), 15)
        self.btn_delete.set_height(12)
        self.btn_delete.clicked.connect(self.handle_btn_delete_clicked)
        self.btn_delete.setPos(self.line.p1() - QPointF(0.5 * self.btn_delete.boundingRect().width(), 1.1 * self.btn_delete.boundingRect().height()))
        self.btn_delete.set_opacity(0.7)

        self.top_handle = QGraphicsEllipseItem(0, 0, self.handle_size, self.handle_size, self)
        self.mid_handle = QGraphicsEllipseItem(0, 0, self.handle_size, self.handle_size, self)
        self.bottom_handle = QGraphicsEllipseItem(0, 0, self.handle_size, self.handle_size, self)

        self.top_handle.setAcceptedMouseButtons(Qt.NoButton)
        self.mid_handle.setAcceptedMouseButtons(Qt.NoButton)
        self.bottom_handle.setAcceptedMouseButtons(Qt.NoButton)
        self.top_handle.setBrush(self.handle_idle_brush)
        self.top_handle.setPen(self.handle_idle_pen)
        self.mid_handle.setBrush(self.handle_idle_brush)
        self.mid_handle.setPen(self.handle_idle_pen)
        self.bottom_handle.setBrush(self.handle_idle_brush)
        self.bottom_handle.setPen(self.handle_idle_pen)

        self.hovered_handle: Optional[QGraphicsRectItem] = None
        self.handles = [self.top_handle, self.mid_handle, self.bottom_handle]

        self.link_button = Button("link", "Link to...", parent=self)
        self.link_button.set_base_color([ButtonColor.GREEN])
        self.link_button.set_height(12)
        self.link_button.set_label("Link", direction="vertical")
        self.link_button.fit_to_contents()
        self.link_button.clicked.connect(lambda: self.link_initiated.emit(self))
        self.link_button.setVisible(False)
        self.link_button.setFlag(QGraphicsObject.ItemIgnoresTransformations, False)

        self.adjust_line()

        self.setAcceptHoverEvents(True)
        self.top_handle.setZValue(4)
        self.bottom_handle.setZValue(4)
        self.mid_handle.setZValue(4)

        self.top_handle.hide()
        self.mid_handle.hide()
        self.bottom_handle.hide()

        self.handle_mouse_offset = QPointF(0, 0)
        self.available_for_linking = False
        self.link_source = False
        self.current_highlight_color: QColor = StickWidget.normal_color
        self.highlighted = False
        self.frame_color: Optional[None] = self.normal_color
        self.is_linked = False

        self.is_master = True
        self.selected = False

        self.measured_height: int = -1
        self.current_color = self.normal_color

        self.show_label = False
        self.highlight_animation = QPropertyAnimation(self, b"highlight_color")
        self.highlight_animation.valueChanged.connect(self.handle_highlight_animation_value_changed)
        self.deleting = False
        self.update_tooltip()
        self.show_measurements: bool = False
        self.proposed_snow_height: int = -1

        self.zero_btn = Button("zero_btn", "0", parent=self)
        self.zero_btn.setFlag(QGraphicsItem.ItemIgnoresTransformations, True)
        self.zero_btn.setVisible(False)
        self.zero_btn.setPos(self.boundingRect().center() + QPointF(self.zero_btn.boundingRect().width() * -0.5,
                                                                    self.boundingRect().height() * 0.5))
        self.zero_btn.clicked.connect(self.handle_zero)

    @pyqtSlot()
    def handle_btn_delete_clicked(self):
        self.delete_clicked.emit(self.stick)

    def prepare_for_deleting(self):
        self.deleting = True
        self.highlight_animation.stop()
        self.btn_delete.setParentItem(None)
        self.scene().removeItem(self.btn_delete)
        self.btn_delete.deleteLater()

    def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem,
              widget: Optional[PyQt5.QtWidgets.QWidget] = ...):
        painter.setPen(QPen(self.current_color, 1.0))

        brush = QBrush(self.current_highlight_color)
        pen = QPen(brush, 4)
        painter.setPen(pen)
        if self.highlighted:
            painter.fillRect(self.boundingRect(), QBrush(self.current_highlight_color))

        if self.frame_color is not None and self.mode != StickMode.Edit and self.mode != StickMode.EditDelete:
            painter.setPen(QPen(self.frame_color, 4))
            painter.drawRect(self.boundingRect())

        pen = QPen(QColor(0, 255, 0, 255))

        pen.setWidth(1.0)
        pen.setColor(QColor(255, 0, 255, 255))
        pen.setStyle(Qt.DotLine)
        painter.setPen(pen)
        off = 10
        painter.drawLine(self.line.p1() - QPointF(0, off), self.line.p1() + QPointF(0, off))
        painter.drawLine(self.line.p1() - QPointF(off, 0), self.line.p1() + QPointF(off, 0))
        painter.drawLine(self.line.p2() - QPointF(0, off), self.line.p2() + QPointF(0, off))
        painter.drawLine(self.line.p2() - QPointF(off, 0), self.line.p2() + QPointF(off, 0))
        pen.setStyle(Qt.SolidLine)
        pen.setColor(QColor(0, 255, 0, 255))
        painter.setPen(pen)

        if self.mode != StickMode.EditDelete:
            pen.setWidth(2.0)
            br = painter.brush()
            painter.setPen(pen)
            painter.drawEllipse(self.line.p1(), 10, 10)
            painter.drawEllipse(self.line.p2(), 10, 10)
            painter.setBrush(br)

            if self.mode == StickMode.Measurement and self.proposed_snow_height >= 0:
                point = QPointF(self.boundingRect().x(), -self.proposed_snow_height + self.line.p2().y())
                pen = QPen(QColor(200, 100, 0, 255), 3.0)
                painter.setPen(pen)
                painter.drawLine(point,
                                 point + QPointF(self.boundingRect().width(), 0.0))

            if self.measured_height >= 0:
                vec = (self.stick.top - self.stick.bottom) / np.linalg.norm(self.stick.top - self.stick.bottom)
                dist_along_stick = self.measured_height / np.dot(np.array([0.0, -1.0]), vec)
                point = self.line.p2() + dist_along_stick * QPointF(vec[0], vec[1])
                point = QPointF(self.boundingRect().x(), point.y())
                pen = QPen(QColor(0, 100, 200, 255), 3.0)
                painter.setPen(pen)
                painter.drawLine(point,
                                 point + QPointF(self.boundingRect().width(), 0.0))
        else:
            painter.drawLine(self.line.p1(), self.line.p2())

        if self.selected:
            pen.setColor(QColor(255, 125, 0, 255))
            pen.setStyle(Qt.DashLine)
            painter.setPen(pen)
            painter.drawRect(self.boundingRect().marginsAdded(QMarginsF(5, 5, 5, 5)))

        if self.show_measurements:
            painter.fillRect(self.stick_label_text.boundingRect().translated(self.stick_label_text.pos()),
                             QBrush(QColor(0, 0, 0, 120)))

    def boundingRect(self) -> PyQt5.QtCore.QRectF:
        return self.gline.boundingRect().united(self.top_handle.boundingRect()).\
            united(self.mid_handle.boundingRect()).united(self.bottom_handle.boundingRect())
    
    def set_edit_mode(self, value: bool):
        if value:
            self.set_mode(StickMode.EditDelete)
        else:
            self.set_mode(StickMode.Display)
    
    def set_mode(self, mode: StickMode):
        if mode == StickMode.Display:
            self.btn_delete.setVisible(False)
            self.top_handle.setVisible(False)
            self.mid_handle.setVisible(False)
            self.bottom_handle.setVisible(False)
            self.link_button.setVisible(False)
            self.available_for_linking = False
            self.link_source = False
            self.zero_btn.setVisible(False)
            self.setVisible(self.stick.is_visible)
        elif mode == StickMode.EditDelete:
            self.set_mode(StickMode.Display)
            self.top_handle.setVisible(True)
            self.mid_handle.setVisible(True)
            self.bottom_handle.setVisible(True)
            self.available_for_linking = False
            self.link_source = False
            self.btn_delete.setVisible(True)
        elif mode == StickMode.LinkSource:
            self.set_mode(StickMode.Display)
            self.link_source = True
            self.available_for_linking = False
            self.link_button.setPos(self.boundingRect().topLeft())
            self.link_button.set_width(int(self.boundingRect().width()))
            self.link_button.set_button_height(int(self.boundingRect().height()))
            self.link_button.adjust_text_to_button()
        elif mode == StickMode.LinkTarget:
            self.set_mode(StickMode.Display)
            self.link_source = False
            self.available_for_linking = True
        elif mode == StickMode.Edit:
            self.set_mode(StickMode.EditDelete)
            self.btn_delete.setVisible(False)
        elif mode == StickMode.Measurement:
            self.zero_btn.setVisible(True)
            self.setVisible(True)

        self.mode = mode
        self.update_tooltip()
        self.update()

    def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
        if self.mode != StickMode.EditDelete:
            return
        if self.hovered_handle is None:
            return

        self.hovered_handle.setBrush(self.handle_press_brush)
        if self.hovered_handle == self.mid_handle:
            self.bottom_handle.setBrush(self.handle_press_brush)
            self.bottom_handle.setPen(self.handle_press_pen)
            self.bottom_handle.setOpacity(0.5)
            self.top_handle.setBrush(self.handle_press_brush)
            self.top_handle.setPen(self.handle_press_pen)
            self.top_handle.setOpacity(0.5)
        self.hovered_handle.setPen(self.handle_press_pen)
        self.hovered_handle.setOpacity(0.5)
        self.handle_mouse_offset = self.hovered_handle.rect().center() - event.pos()
        self.btn_delete.setVisible(False)

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent):
        if self.available_for_linking:
            self.link_accepted.emit(self)
            return

        if self.mode == StickMode.Measurement:
            old_snow = self.stick.snow_height_px
            self.measured_height = self.proposed_snow_height
            self.stick.set_snow_height_px(self.proposed_snow_height)
            if abs(old_snow - self.proposed_snow_height) > 0:
                self.measurement_corrected.emit(self)
            self.proposed_snow_height = -1

        if self.mode != StickMode.EditDelete and self.mode != StickMode.Edit:
            return

        if self.hovered_handle is not None:
            self.hovered_handle.setBrush(self.handle_hover_brush)
            self.hovered_handle.setPen(self.handle_idle_pen)
            self.hovered_handle.setOpacity(1.0)
            if self.hovered_handle == self.mid_handle:
                self.bottom_handle.setBrush(self.handle_idle_brush)
                self.bottom_handle.setPen(self.handle_idle_pen)
                self.bottom_handle.setOpacity(1.0)
                self.top_handle.setBrush(self.handle_idle_brush)
                self.top_handle.setPen(self.handle_idle_pen)
                self.top_handle.setOpacity(1.0)
            self.stick_changed.emit(self)
        self.hovered_handle = None
        if self.mode == StickMode.EditDelete:
            self.btn_delete.setVisible(True)
    
    def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent):
        if self.hovered_handle is None:
            return
        if self.hovered_handle == self.top_handle:
            self.line.setP1((event.pos() + self.handle_mouse_offset).toPoint())
        elif self.hovered_handle == self.bottom_handle:
            self.line.setP2((event.pos() + self.handle_mouse_offset).toPoint())
        else:
            displacement = event.pos() - event.lastPos()
            self.setPos(self.pos() + displacement)
        self.adjust_handles()
        self.adjust_stick()
        self.scene().update()

    def set_top(self, pos: QPoint):
        self.line.setP1(pos)
        self.adjust_handles()
        self.adjust_stick()
        self.scene().update()

    def set_bottom(self, pos: QPoint):
        self.line.setP2(pos)
        self.adjust_handles()
        self.adjust_stick()
        self.scene().update()

    def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
        if self.available_for_linking:
            self.hovered.emit(True, self)
        elif self.link_source:
            self.link_button.setVisible(True)
        self.scene().update()

    def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent):
        for h in self.handles:
            h.setBrush(self.handle_idle_brush)
        self.hovered_handle = None
        if self.available_for_linking:
            self.hovered.emit(False, self)
        self.link_button.setVisible(False)
        self.proposed_snow_height = -1
        self.scene().update()
    
    def hoverMoveEvent(self, event: QGraphicsSceneHoverEvent):
        if self.mode != StickMode.EditDelete and self.mode != StickMode.Edit and self.mode != StickMode.Measurement:
            return
        if self.mode == StickMode.Measurement:
            self.proposed_snow_height = max(self.line.p2().y() - event.pos().y(), 0)
            self.update()
            return
        hovered_handle = list(filter(lambda h: h.rect().contains(event.pos()), self.handles))
        if len(hovered_handle) == 0:
            if self.hovered_handle is not None:
                self.hovered_handle.setBrush(self.handle_idle_brush)
                self.hovered_handle = None
            return
        if self.hovered_handle is not None and self.hovered_handle != hovered_handle[0]:
            self.hovered_handle.setBrush(self.handle_idle_brush)
        self.hovered_handle = hovered_handle[0]
        if self.hovered_handle == self.top_handle:
            self.top_handle.setBrush(self.handle_hover_brush)
        elif self.hovered_handle == self.bottom_handle:
            self.bottom_handle.setBrush(self.handle_hover_brush)
        else:
            self.mid_handle.setBrush(self.handle_hover_brush)

        self.scene().update()
    
    def adjust_stick(self):
        self.stick.top[0] = self.pos().x() + self.line.p1().x()
        self.stick.top[1] = self.pos().y() + self.line.p1().y()
        self.stick.bottom[0] = self.pos().x() + self.line.p2().x()
        self.stick.bottom[1] = self.pos().y() + self.line.p2().y()

    def adjust_handles(self):
        if self.line.p1().y() > self.line.p2().y():
            p1, p2 = self.line.p1(), self.line.p2()
            self.line.setP1(p2)
            self.line.setP2(p1)
            if self.hovered_handle is not None:
                self.hovered_handle.setBrush(self.handle_idle_brush)
                self.hovered_handle.setPen(self.handle_idle_pen)
                self.hovered_handle = self.top_handle if self.hovered_handle == self.bottom_handle else self.bottom_handle
                self.hovered_handle.setBrush(self.handle_press_brush)
                self.hovered_handle.setPen(self.handle_press_pen)
        rect = self.top_handle.rect()
        rect.moveCenter(self.line.p1())
        self.top_handle.setRect(rect)
        rect = self.bottom_handle.rect()
        rect.moveCenter(self.line.p2())
        self.bottom_handle.setRect(rect)
        rect = self.mid_handle.rect()
        rect.moveCenter(self.line.center())
        self.mid_handle.setRect(rect)
        self.btn_delete.setPos(self.top_handle.rect().center() - QPointF(self.btn_delete.boundingRect().width() / 2,
                                                               self.btn_delete.boundingRect().height() + self.top_handle.boundingRect().height() / 2))

    def set_available_for_linking(self, available: bool):
        self.available_for_linking = available

    def set_is_link_source(self, is_source: bool):
        self.link_source = is_source
        self.link_button.setPos(self.boundingRect().topLeft())
        self.link_button.set_width(int(self.boundingRect().width()))
        self.link_button.set_button_height(int(self.boundingRect().height()))
        self.link_button.adjust_text_to_button()
    
    def set_frame_color(self, color: Optional[QColor]):
        self.frame_color = color if color is not None else self.normal_color
        self.update()

    def set_is_linked(self, value: bool):
        self.is_linked = value
        if not self.is_linked:
            self.set_frame_color(None)
            if self.available_for_linking:
                self.highlight(QColor(0, 255, 0, 100))
            else:
                self.highlight(None)
        self.update_tooltip()

    def adjust_line(self):
        self.setPos(QPointF(0.5 * (self.stick.top[0] + self.stick.bottom[0]), 0.5 * (self.stick.top[1] + self.stick.bottom[1])))
        vec = 0.5 * (self.stick.top - self.stick.bottom)
        self.line.setP1(QPointF(vec[0], vec[1]))
        self.line.setP2(-self.line.p1())
        self.gline.setLine(self.line)
        self.adjust_handles()
        self.stick_label_text.setPos(self.line.p1() - QPointF(0.5 * self.stick_label_text.boundingRect().width(),
                                                             1.3 * self.stick_label_text.boundingRect().height()))
        self.update()

    def set_selected(self, selected: bool):
        self.selected = selected
        self.update()

    def is_selected(self) -> bool:
        return self.selected

    def set_snow_height(self, height: int):
        self.measured_height = height
        self.update()

    def border_normal(self):
        self.current_color = self.normal_color
        self.update()

    def border_positive(self):
        self.current_color = self.positive_color
        self.update()

    def border_negative(self):
        self.current_color = self.negative_color
        self.update()

    @pyqtProperty(QColor)
    def highlight_color(self) -> QColor:
        return self.current_highlight_color

    @highlight_color.setter
    def highlight_color(self, color: QColor):
        self.current_highlight_color = color

    def highlight(self, color: Optional[QColor], animated: bool = False):
        self.highlighted = color is not None
        if not animated or color is None:
            self.highlight_animation.stop()
            self.current_highlight_color = self.normal_color if color is None else color
            self.update()
            return
        self.highlight_animation.setStartValue(color)
        self.highlight_animation.setEndValue(color)
        self.highlight_animation.setKeyValueAt(0.5, color.darker())
        self.highlight_animation.setDuration(2000)
        self.highlight_animation.setLoopCount(-1)
        self.highlight_animation.start()

    def handle_link_button_hovered(self, btn: Dict[str, Any]):
        self.link_button.setVisible(btn['hovered'])

    def handle_highlight_animation_value_changed(self, new: QColor):
        if not self.deleting:
            self.update(self.boundingRect().marginsAdded(QMarginsF(10, 10, 10, 10)))

    def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent) -> None:
        self.right_clicked.emit({'stick_widget': self})

    def set_stick_label(self, label: str):
        self.stick.label = label
        self.stick_label_text.setText(label)
        self.update_tooltip()
        self.update()

    def get_stick_label(self) -> str:
        return self.stick.label

    def get_stick_length_cm(self) -> int:
        return self.stick.length_cm

    def set_stick_length_cm(self, length: int):
        self.stick.length_cm = length
        self.update_tooltip()
        self.update()

    def update_tooltip(self):
        if self.mode != StickMode.Display or self.mode == StickMode.Measurement:
            self.setToolTip("")
            return
        snow_txt = "Snow height: "
        if self.stick.snow_height_px >= 0:
            snow_txt += str(self.stick.snow_height_cm) + " cm"
            self.stick_label_text.setText(str(self.stick.snow_height_cm))
        else:
            snow_txt = "not measured"
            self.stick_label_text.setVisible(False)
        self.stick_label_text.setText(self.stick.label)
        self.stick_label_text.setVisible(True)
        stick_view_text = ''
        role = ''
        if self.stick.alternative_view is not None:
            alt_view = self.stick.alternative_view
            role = " - primary"
            alt = "Secondary"
            if not self.stick.primary:
                role = " - secondary"
                alt = "Primary"
            stick_view_text = f'\n{alt} view: {alt_view.label} in {alt_view.camera_folder.name}\n'
        mark = '*' if self.stick.determines_quality else ''
        self.setToolTip(f'{mark}{self.stick.label}{role}{stick_view_text}\nLength: {self.stick.length_cm} cm\n{snow_txt}')

    def set_stick(self, stick: Stick):
        self.reset_d_btns()
        self.stick = stick
        self.adjust_line()
        self.adjust_handles()
        self.set_snow_height(stick.snow_height_px)
        self.update_tooltip()
        self.set_show_measurements(self.show_measurements)
        if self.mode == StickMode.Measurement:
            self.set_frame_color(QColor(200, 100, 0, 100) if not self.stick.is_visible else None)
            self.setVisible(True)
            self.clearly_visible_btn.setVisible(not self.stick.is_visible)
        else:
            self.setVisible(self.stick.is_visible)

    def set_show_measurements(self, show: bool):
        self.show_measurements = show
        if self.show_measurements:
            self.stick_label_text.setText(str(self.stick.snow_height_cm) if self.stick.snow_height_cm >= 0 else
                                          "n/a")
        else:
            self.stick_label_text.setText(self.stick.label)
        self.update()

    def handle_zero(self):
        self.measured_height = 0
        self.stick.set_snow_height_px(0)
        self.measurement_corrected.emit(self)

    def reset_d_btns(self):
        self.zero_btn.set_default_state()
Esempio n. 5
0
class Minion(QGraphicsPixmapItem):
    def __init__(self, equipo, tamaño="pequeño"):
        self.equipo = equipo
        self.tamaño = tamaño
        super().__init__()
        if tamaño == "pequeño":
            self.setPixmap(QPixmap("Minions/minion0.png").scaled(30, 30))
            self.setTransformOriginPoint(15, 15)
        else:
            self.setPixmap(QPixmap("Minions/minion0.png").scaled(40, 40))
            self.setTransformOriginPoint(20, 20)
        paths = ["minion{}".format(i) for i in range(0, 9)]
        paths = sorted([p for i in range(3) for p in paths])
        self.img = self.gen_path(paths)

        # Area de ataque
        if tamaño == "pequeño":
            self.area = QGraphicsEllipseItem(-5, -5, 40, 40)
            self.area.setTransformOriginPoint(20, 20)
        else:
            self.area = QGraphicsEllipseItem(-20, -20, 80, 80)
            self.area.setTransformOriginPoint(40, 40)
        self.area.setParentItem(self)
        self.area.hide()

        # La barra de vida
        if tamaño == "pequeño":
            self.barra_vida = BarraVida(30, 45)
        else:
            self.barra_vida = BarraVida(40, 60)
        self.barra_vida.setParentItem(self)

        # Para cuando ataquen
        self.target = None

        # Stats
        if self.tamaño == "pequeño":
            self.vida = 45
            self.daño = 2
            self.pix = 30
        else:
            self.vida = 60
            self.daño = 4
            self.pix = 40

        self.mover = QtCore.QTimer()
        self.mover.timeout.connect(self.move)
        # Se mueve una vez cada 50 milisegundos
        self.mover.start(50)

        # Timer de ataque
        self.ataque = QtCore.QTimer()
        self.ataque.timeout.connect(self.atacar)

        self.hover = False

    def move(self):
        img = next(self.img)
        if self.hover is False:
            self.setPixmap(QPixmap(img).scaled(self.pix, self.pix))
        else:
            self.setPixmap(QPixmap(img + "_hover").scaled(self.pix, self.pix))
        meta = self.mas_cercano(self.enemigos)

        x = self.x() + 10 - meta[0]
        y = self.y() + 10 - meta[1]

        self.angulo = -math.degrees(math.atan2(x, y))
        if self.angulo < 0:
            self.angulo += 360
        self.rotacion(self.angulo + 180)

        self.setPos(self.x() + 0.4 * math.sin(math.radians(180 - self.angulo)),
                    self.y() + 0.4 * math.cos(math.radians(180 - self.angulo)))

        # Si hay enemigos en el area empieza a atacar
        if len([
                i for i in self.area.collidingItems()
                if "equipo" in i.__dict__ and i.equipo != self.equipo
        ]) > 0:
            self.mover.stop()
            self.setPixmap(
                QPixmap("Minions/minion0.png").scaled(self.pix, self.pix))
            self.ataque.start(1000)

    def rotacion(self, angulo):
        self.setRotation(angulo)
        self.barra_vida.setRotation(-angulo)

    def mas_cercano(self, lista):
        items = [(i.x(), i.y()) for i in lista]
        distancias = [self.dist(i) for i in items]

        return items[distancias.index(min(distancias))]

    def dist(self, punto):
        distancia = math.sqrt((punto[0] - self.x())**2 +
                              (punto[1] - self.y())**2)

        return distancia

    def gen_path(self, lista):
        while True:
            for path in lista:
                yield "Minions/{}".format(path)

    def atacar(self):
        if self.target is None:
            enemigos = [
                i for i in self.area.collidingItems()
                if "equipo" in i.__dict__ and i.equipo != self.equipo
            ]
            if len(enemigos) > 0:
                self.target = enemigos[0]
                self.target.recibir_daño(self.daño)

        # Esto para eliminar un bug que dejaba como target a objetos que ya no estaban en la scene (porque habian muerto)
        elif self.target not in self.scene().items():
            self.target = None

        else:
            self.target.recibir_daño(self.daño)

        if self.scene() and len([
                i for i in self.area.collidingItems()
                if "equipo" in i.__dict__ and i.equipo != self.equipo
        ]) == 0:
            self.target = None
            self.ataque.stop()
            self.move()
            self.mover.start(50)

    def recibir_daño(self, daño):
        self.vida -= daño
        self.barra_vida.update(self.vida)

        if self.vida <= 0 and self.scene():
            self.scene().removeItem(self)
            self.ataque.stop()
            self.mover.stop()
            self.target = None

    def hoverEntra(self, evento):
        # Solo los enemigos son atacables
        if self.equipo == 2:
            self.hover = True
            if self.ataque.isActive():
                self.setPixmap(
                    QPixmap("Minions/minion0_hover.png").scaled(
                        self.pix, self.pix))

    def hoverSale(self, evento):
        self.hover = False
        if self.ataque.isActive():
            self.setPixmap(
                QPixmap("Minions/minion0.png").scaled(self.pix, self.pix))

    @property
    def enemigos(self):
        if self.scene():
            return [
                i for i in self.scene().items()
                if "equipo" in i.__dict__ and i.equipo != self.equipo
            ]
Esempio n. 6
0
class Player(QGraphicsPixmapItem):
    def __init__(self, champ):
        super().__init__()
        self.setPos(200, 250)

        # Valores Utiles despues
        self.angulo = 0
        self.mouse = (0, 0)
        self.equipo = 1
        self.nombre = champ
        self.target = None
        self.muerto = False
        self.muertes = 0

        # Stats
        if champ == "Chau":
            self.setPixmap(QPixmap("Player/Chau/player_0.png").scaled(50, 50))

            paths = ["player_{}".format(i) for i in range(8)]
            paths = sorted([p for i in range(3) for p in paths])

            self.vida = 500
            self.rango = 40
            self.velocidad = 30
            self.v_atk = 10
            self.daño = 5

        elif champ == "Hernan":
            self.setPixmap(QPixmap("Player/Hernan/peludo0.png").scaled(50, 50))
            paths = ["peludo{}".format(i) for i in range(16)]
            paths = sorted([p for i in range(3) for p in paths])

            self.vida = 666
            self.rango = 5
            self.velocidad = 10
            self.v_atk = 10
            self.daño = 20

        elif champ == "Roberto":
            self.setPixmap(
                QPixmap("Player/Roberto/player0.png").scaled(50, 50))
            paths = [
                "player1", "player2", "player3", "player4", "player5",
                "player0"
            ]
            paths = sorted([p for i in range(5) for p in paths])

            self.vida = 200
            self.rango = 70
            self.velocidad = 15
            self.v_atk = 20
            self.daño = 10

        # El generador de paths
        self.img = self.gen_path(paths)

        self.setTransformOriginPoint(25, 25)

        # Area de ataque
        self.area = QGraphicsEllipseItem(-self.rango, -self.rango,
                                         50 + self.rango * 2,
                                         50 + self.rango * 2)
        self.area.setParentItem(self)
        self.area.hide()

        # La barra de vida
        self.barra_vida = BarraVida(50, self.vida)
        self.barra_vida.setParentItem(self)

        # Se crean los timers de movimiento y ataque
        self.avanzar = QtCore.QTimer()
        self.avanzar.timeout.connect(self.acercarse)
        self.atacar = QtCore.QTimer()
        self.atacar.timeout.connect(self.ataque)

        #self.time = time.time()

    def gen_path(self, lista):
        while True:
            for path in lista:
                yield "Player/{}/{}.png".format(self.nombre, path)

    def mover(self, evento):
        # Apretar una tecla de movimiento cancela el movimiento automático
        self.avanzar.stop()
        self.atacar.stop()
        self.target = None
        #print(abs(self.time - time.time()))
        #self.time = time.time()

        text = evento.text()  # para no tener que escribirlo mil veces
        # se calcula un angulo en base a la ultima posicion del mouse
        x = self.x() + 25 - self.mouse[0]
        y = self.y() + 25 - self.mouse[1]
        self.angulo = -math.degrees(math.atan2(x, y))
        if self.angulo < 0:
            self.angulo += 360
        self.rotacion(self.angulo)

        # Cambia la imagen de movimiento
        self.setPixmap(QPixmap(next(self.img)).scaled(50, 50))

        if text == "w":
            self.setPos(
                self.x() + self.velocidad / 33.3 *
                math.sin(math.radians(180 - self.angulo)),
                self.y() + self.velocidad / 33.3 *
                math.cos(math.radians(180 - self.angulo)))

        elif text == "s":
            self.setPos(
                self.x() - self.velocidad / 33.3 *
                math.sin(math.radians(180 - self.angulo)),
                self.y() - self.velocidad / 33.3 *
                math.cos(math.radians(180 - self.angulo)))

        elif text == "d":
            self.setPos(
                self.x() + self.velocidad / 33.3 *
                math.sin(math.radians(90 - self.angulo)),
                self.y() + self.velocidad / 33.3 *
                math.cos(math.radians(90 - self.angulo)))

        elif text == "a":
            self.setPos(
                self.x() - self.velocidad / 33.3 *
                math.sin(math.radians(90 - self.angulo)),
                self.y() - self.velocidad / 33.3 *
                math.cos(math.radians(90 - self.angulo)))

    def stop(self):
        if self.nombre == "Roberto":
            self.setPixmap(
                QPixmap("Player/Roberto/player0.png").scaled(50, 50))

        elif self.nombre == "Chau":
            self.setPixmap(QPixmap("Player/Chau/player_0.png").scaled(50, 50))

        else:
            self.setPixmap(QPixmap("Player/Hernan/peludo0.png").scaled(50, 50))

    def rotacion(self, angulo):
        self.setRotation(angulo)
        self.barra_vida.setRotation(-angulo)

    def atacar_enemigo(self, target):
        self.target = target

        if self.target not in self.area.collidingItems():
            self.avanzar.start(50)

        else:
            self.atacar.start(1000 / self.v_atk)

    def acercarse(self):
        x = self.x() + 25 - self.target.x()
        y = self.y() + 25 - self.target.y()
        self.angulo = -math.degrees(math.atan2(x, y))
        if self.angulo < 0:
            self.angulo += 360
        self.rotacion(self.angulo)

        # Cambia la imagen de movimiento
        self.setPixmap(QPixmap(next(self.img)).scaled(50, 50))

        # Avanza
        self.setPos(
            self.x() +
            0.05 * self.velocidad * math.sin(math.radians(180 - self.angulo)),
            self.y() +
            0.05 * self.velocidad * math.cos(math.radians(180 - self.angulo)))

        # Si llega el timer se termina
        if self.target in self.area.collidingItems():
            self.avanzar.stop()
            # Y ataca
            self.atacar.start(1000 / self.v_atk)

    def ataque(self):
        if self.scene() and self.target in self.scene().items():
            self.target.recibir_daño(self.daño)

        else:
            self.target = None
            self.atacar.stop()

    def recibir_daño(self, daño):
        self.vida -= daño
        self.barra_vida.update(self.vida)

        if self.vida <= 0:
            self.__scene = self.scene()
            self.scene().removeItem(self)
            self.muerto = True
            self.muertes += 1

            self.revivir = QtCore.QTimer()
            self.revivir.setSingleShot(True)
            self.revivir.timeout.connect(self.respawn)
            self.revivir.start(((1.1)**self.muertes) * 10000)

    def respawn(self):
        self.vida = self.barra_vida.vida
        self.barra_vida.update(self.vida)
        self.__scene.addItem(self)
        self.setPos(200, 250)

    def mouseMoveEvent(self, evento):
        if self.target == None:
            x = self.x() + 25 - evento.x()
            y = self.y() + 25 - evento.y()
            self.angulo = -math.degrees(math.atan2(x, y))
            if self.angulo < 0:
                self.angulo += 360
            self.rotacion(self.angulo)

            self.mouse = (evento.x(), evento.y())
class RDiscRobot(QObject):
    def __init__(self, r: float, x: float, y: float, color, line_width: float, text=""):
        self._visible = 1
        self._radius = r
        self._pos = QPointF(x, y)
        super().__init__()
        # The supporting rectangle
        self.rect = QRectF(x - r, y - r, 2 * r, 2 * r)
        # The underlying QGraphicsEllipseItem
        self.disc = QGraphicsEllipseItem()
        self.disc.setRect(self.rect)
        self.disc.setBrush(QtGui.QBrush(color))
        # The underlying QGraphicsTextItem
        self._text: QGraphicsTextItem = QGraphicsTextItem(text)
        transform = QTransform.fromScale(0.3, -0.3)
        self._text.setTransformOriginPoint(self._pos)
        self._text.setTransform(transform)
        self._text.setPos(QPointF(x - 1.8, y + 1.8))
        font = QFont("Times", 2)
        self._text.setFont(font)
        pen = QPen()
        pen.setWidthF(line_width)
        self.disc.setPen(pen)
        self._visible = 1

    def x(self) -> float:
        return self._pos.x()

    def y(self) -> float:
        return self._pos.y()

    def set_text(self, text: str):
        self._text.setPlainText(text)

    # The following functions are for animation support

    @pyqtProperty(QPointF)
    def pos(self):
        return self._pos

    @pos.setter
    def pos(self, value):
        self.rect = QRectF(value.x() - self._radius, value.y() - self._radius, 2 * self._radius, 2 * self._radius)
        self.disc.setRect(self.rect)
        self._text.setPos(QPointF(value.x() - 1.8, value.y() + 1.8))
        self._pos = value

    @pyqtProperty(int)
    def visible(self):
        return self._visible

    @visible.setter
    def visible(self, value):
        if value > 0:
            self.disc.show()
            self._text.show()
        else:
            self.disc.hide()
            self._text.hide()
        self._visible = value

    @pyqtProperty(int)
    def text(self):
        if self._text.toPlainText() == '':
            return 0
        return int(self._text.toPlainText())

    @text.setter
    def text(self, val: int):
        if val == 0:
            self._text.setPlainText('')
        else:
            self._text.setPlainText(str(val))
Esempio n. 8
0
class SliceNucleicAcidPartItem(QAbstractPartItem):
    """Parent should be either a SliceRootItem, or an AssemblyItem.

    Invariant: keys in _empty_helix_hash = range(_nrows) x range(_ncols)
    where x is the cartesian product.

    Attributes:
        active_virtual_helix_item (cadnano.gui.views.sliceview.virtualhelixitem.SliceVirtualHelixItem): Description
        grab_cornerBR (TYPE): Description
        grab_cornerTL (TYPE): Description
        griditem (TYPE): Description
        outline (TYPE): Description
        prexover_manager (TYPE): Description
        scale_factor (TYPE): Description
    """
    _RADIUS = styles.SLICE_HELIX_RADIUS

    def __init__(self, model_part_instance, viewroot, parent=None):
        """Summary

        Args:
            model_part_instance (TYPE): Description
            viewroot (TYPE): Description
            parent (None, optional): Description
        """
        super(SliceNucleicAcidPartItem, self).__init__(model_part_instance, viewroot, parent)

        self._getActiveTool = viewroot.manager.activeToolGetter
        m_p = self._model_part
        self._controller = NucleicAcidPartItemController(self, m_p)
        self.scale_factor = self._RADIUS / m_p.radius()

        self.active_virtual_helix_item = None

        self.prexover_manager = PreXoverManager(self)

        self.hide()  # hide while until after attemptResize() to avoid flicker

        self._rect = QRectF(0., 0., 1000., 1000.)   # set this to a token value
        self.boundRectToModel()

        self.setPen(getNoPen())

        self.setRect(self._rect)
        self.setAcceptHoverEvents(True)

        # Cache of VHs that were active as of last call to activeSliceChanged
        # If None, all slices will be redrawn and the cache will be filled.
        # Connect destructor. This is for removing a part from scenes.

        # initialize the NucleicAcidPartItem with an empty set of old coords
        self.setZValue(styles.ZPARTITEM)

        self.outline = outline = QGraphicsRectItem(self)
        o_rect = self.configureOutline(outline)
        outline.setFlag(QGraphicsItem.ItemStacksBehindParent)
        outline.setZValue(styles.ZDESELECTOR)
        model_color = m_p.getColor()
        self.outline.setPen(getPenObj(model_color, _DEFAULT_WIDTH))

        GC_SIZE = 20
        self.grab_cornerTL = GrabCornerItem(GC_SIZE, model_color, True, self)
        self.grab_cornerTL.setTopLeft(o_rect.topLeft())
        self.grab_cornerBR = GrabCornerItem(GC_SIZE, model_color, True, self)
        self.grab_cornerBR.setBottomRight(o_rect.bottomRight())

        self.griditem = GridItem(self, self._model_props['grid_type'])

        self.griditem.setZValue(1)
        self.grab_cornerTL.setZValue(2)
        self.grab_cornerBR.setZValue(2)

        self.vhi_hint_item = QGraphicsEllipseItem(_DEFAULT_RECT, self)
        self.vhi_hint_item.setPen(_MOD_PEN)
        self.vhi_hint_item.setZValue(styles.ZPARTITEM)

        # select upon creation
        for part in m_p.document().children():
            if part is m_p:
                part.setSelected(True)
            else:
                part.setSelected(False)
        self.show()
    # end def

    ### SIGNALS ###

    ### SLOTS ###
    def partActiveVirtualHelixChangedSlot(self, part, id_num):
        """Summary

        Args:
            part (TYPE): Description
            id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods.

        Args:
            TYPE: Description
        """
        vhi = self._virtual_helix_item_hash.get(id_num, None)
        self.setActiveVirtualHelixItem(vhi)
        self.setPreXoverItemsVisible(vhi)
    # end def

    def partActiveBaseInfoSlot(self, part, info):
        """Summary

        Args:
            part (TYPE): Description
            info (TYPE): Description

        Args:
            TYPE: Description
        """
        pxom = self.prexover_manager
        pxom.deactivateNeighbors()
        if info and info is not None:
            id_num, is_fwd, idx, _ = info
            pxom.activateNeighbors(id_num, is_fwd, idx)
    # end def

    def partPropertyChangedSlot(self, model_part, property_key, new_value):
        """Summary

        Args:
            model_part (Part): The model part
            property_key (TYPE): Description
            new_value (TYPE): Description

        Args:
            TYPE: Description
        """
        if self._model_part == model_part:
            self._model_props[property_key] = new_value
            if property_key == 'color':
                self.outline.setPen(getPenObj(new_value, _DEFAULT_WIDTH))
                for vhi in self._virtual_helix_item_hash.values():
                    vhi.updateAppearance()
                self.grab_cornerTL.setBrush(getBrushObj(new_value))
                self.grab_cornerBR.setBrush(getBrushObj(new_value))
            elif property_key == 'is_visible':
                if new_value:
                    self.show()
                else:
                    self.hide()
            elif property_key == 'grid_type':
                self.griditem.setGridType(new_value)
    # end def

    def partRemovedSlot(self, sender):
        """docstring for partRemovedSlot

        Args:
            sender (obj): Model object that emitted the signal.
        """
        self.parentItem().removePartItem(self)

        scene = self.scene()

        scene.removeItem(self)

        self._model_part = None
        self._mod_circ = None

        self._controller.disconnectSignals()
        self._controller = None
        self.grab_cornerTL = None
        self.grab_cornerBR = None
        self.griditem = None
    # end def

    def partVirtualHelicesTranslatedSlot(self, sender,
                                         vh_set, left_overs,
                                         do_deselect):
        """
        left_overs are neighbors that need updating due to changes

        Args:
            sender (obj): Model object that emitted the signal.
            vh_set (TYPE): Description
            left_overs (TYPE): Description
            do_deselect (TYPE): Description
        """
        if do_deselect:
            tool = self._getActiveTool()
            if tool.methodPrefix() == "selectTool":
                if tool.isSelectionActive():
                    # tool.deselectItems()
                    tool.modelClear()

        # 1. move everything that moved
        for id_num in vh_set:
            vhi = self._virtual_helix_item_hash[id_num]
            vhi.updatePosition()
        # 2. now redraw what makes sense to be redrawn
        for id_num in vh_set:
            vhi = self._virtual_helix_item_hash[id_num]
            self._refreshVirtualHelixItemGizmos(id_num, vhi)
        for id_num in left_overs:
            vhi = self._virtual_helix_item_hash[id_num]
            self._refreshVirtualHelixItemGizmos(id_num, vhi)

        # 0. clear PreXovers:
        # self.prexover_manager.hideGroups()
        # if self.active_virtual_helix_item is not None:
        #     self.active_virtual_helix_item.deactivate()
        #     self.active_virtual_helix_item = None
        avhi = self.active_virtual_helix_item
        self.setPreXoverItemsVisible(avhi)
        self.enlargeRectToFit()
    # end def

    def _refreshVirtualHelixItemGizmos(self, id_num, vhi):
        """Update props and appearance of self & recent neighbors. Ultimately
        triggered by a partVirtualHelicesTranslatedSignal.

        Args:
            id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods.
            vhi (cadnano.gui.views.sliceview.virtualhelixitem.SliceVirtualHelixItem): the item associated with id_num
        """
        neighbors = vhi.cnModel().getProperty('neighbors')
        neighbors = literal_eval(neighbors)
        vhi.beginAddWedgeGizmos()
        for nvh in neighbors:
            nvhi = self._virtual_helix_item_hash.get(nvh, False)
            if nvhi:
                vhi.setWedgeGizmo(nvh, nvhi)
        # end for
        vhi.endAddWedgeGizmos()
    # end def

    def partVirtualHelixPropertyChangedSlot(self, sender, id_num, virtual_helix, keys, values):
        """Summary

        Args:
            sender (obj): Model object that emitted the signal.
            id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods.
            keys (tuple): keys that changed
            values (tuple): new values for each key that changed

        Args:
            TYPE: Description
        """
        if self._model_part == sender:
            vh_i = self._virtual_helix_item_hash[id_num]
            vh_i.virtualHelixPropertyChangedSlot(keys, values)
    # end def

    def partVirtualHelixAddedSlot(self, sender, id_num, virtual_helix, neighbors):
        """Summary

        Args:
            sender (obj): Model object that emitted the signal.
            id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods.
            neighbors (TYPE): Description

        Args:
            TYPE: Description
        """
        vhi = SliceVirtualHelixItem(virtual_helix, self)
        self._virtual_helix_item_hash[id_num] = vhi
        self._refreshVirtualHelixItemGizmos(id_num, vhi)
        for neighbor_id in neighbors:
            nvhi = self._virtual_helix_item_hash.get(neighbor_id, False)
            if nvhi:
                self._refreshVirtualHelixItemGizmos(neighbor_id, nvhi)
        self.enlargeRectToFit()
    # end def

    def partVirtualHelixRemovingSlot(self, sender, id_num, virtual_helix, neighbors):
        """Summary

        Args:
            sender (obj): Model object that emitted the signal.
            id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods.
            neighbors (TYPE): Description

        Args:
            TYPE: Description
        """
        tm = self._viewroot.manager
        tm.resetTools()
        self.removeVirtualHelixItem(id_num)
        for neighbor_id in neighbors:
            nvhi = self._virtual_helix_item_hash[neighbor_id]
            self._refreshVirtualHelixItemGizmos(neighbor_id, nvhi)
    # end def

    def partSelectedChangedSlot(self, model_part, is_selected):
        """Set this Z to front, and return other Zs to default.

        Args:
            model_part (Part): The model part
            is_selected (TYPE): Description
        """
        if is_selected:
            # self._drag_handle.resetAppearance(_SELECTED_COLOR, _SELECTED_WIDTH, _SELECTED_ALPHA)
            self.setZValue(styles.ZPARTITEM + 1)
        else:
            # self._drag_handle.resetAppearance(self.modelColor(), _DEFAULT_WIDTH, _DEFAULT_ALPHA)
            self.setZValue(styles.ZPARTITEM)
    # end def

    def partVirtualHelicesSelectedSlot(self, sender, vh_set, is_adding):
        """is_adding (bool): adding (True) virtual helices to a selection
        or removing (False)

        Args:
            sender (obj): Model object that emitted the signal.
            vh_set (TYPE): Description
            is_adding (TYPE): Description
        """
        select_tool = self._viewroot.select_tool
        if is_adding:
            # print("got the adding slot in path")
            select_tool.selection_set.update(vh_set)
            select_tool.setPartItem(self)
            select_tool.getSelectionBoundingRect()
        else:
            select_tool.deselectSet(vh_set)
    # end def

    def partDocumentSettingChangedSlot(self, part, key, value):
        """Summary

        Args:
            part (TYPE): Description
            key (TYPE): Description
            value (TYPE): Description

        Args:
            TYPE: Description

        Raises:
            ValueError: Description
        """
        if key == 'grid':
            print("grid change", value)
            if value == 'lines and points':
                self.griditem.setDrawlines(True)
            elif value == 'points':
                self.griditem.setDrawlines(False)
            else:
                raise ValueError("unknown grid styling")
    # end def

    ### ACCESSORS ###
    def boundingRect(self):
        """Summary

        Args:
            TYPE: Description
        """
        return self._rect
    # end def

    def modelColor(self):
        """Summary

        Args:
            TYPE: Description
        """
        return self._model_props['color']
    # end def

    def window(self):
        """Summary

        Args:
            TYPE: Description
        """
        return self.parentItem().window()
    # end def

    def setActiveVirtualHelixItem(self, new_active_vhi):
        """Summary

        Args:
            new_active_vhi (TYPE): Description

        """
        current_vhi = self.active_virtual_helix_item
        # print(current_vhi, new_active_vhi)
        if new_active_vhi != current_vhi:
            if current_vhi is not None:
                current_vhi.deactivate()
            if new_active_vhi is not None:
                new_active_vhi.activate()
            self.active_virtual_helix_item = new_active_vhi
    # end def

    def setPreXoverItemsVisible(self, virtual_helix_item):
        """
        self._pre_xover_items list references prexovers parented to other
        PathHelices such that only the activeHelix maintains the list of
        visible prexovers

        Args:
            virtual_helix_item (cadnano.gui.views.sliceview.virtualhelixitem.SliceVirtualHelixItem): Description
        """
        vhi = virtual_helix_item
        pxom = self.prexover_manager
        if vhi is None:
            pxom.hideGroups()
            return

        # print("slice.setPreXoverItemsVisible", virtual_helix_item.idNum())
        part = self.part()
        info = part.active_base_info
        if info:
            id_num, is_fwd, idx, to_vh_id_num = info
            per_neighbor_hits, pairs = part.potentialCrossoverMap(id_num, idx)
            pxom.activateVirtualHelix(virtual_helix_item, idx,
                                      per_neighbor_hits, pairs)
    # end def

    def setSelectionFilter(self, filter_name_list):
        if 'virtual_helix' in filter_name_list:
            self.vhi_hint_item.setPen(_MOD_PEN)
        else:
            self.vhi_hint_item.setPen(_INACTIVE_PEN)

    def removeVirtualHelixItem(self, id_num):
        """Summary

        Args:
            id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods.

        Args:
            TYPE: Description
        """
        vhi = self._virtual_helix_item_hash[id_num]
        if vhi == self.active_virtual_helix_item:
            self.active_virtual_helix_item = None
        vhi.virtualHelixRemovedSlot()
        del self._virtual_helix_item_hash[id_num]
    # end def

    def reconfigureRect(self, top_left, bottom_right, padding=_RADIUS):
        """Summary
        NOTE: we skip padding Bottom right Y dimension

        Args:
            top_left (TYPE): Description
            bottom_right (TYPE): Description

        Returns:
            tuple: tuple of point tuples representing the top_left and
                bottom_right as reconfigured with padding
        """
        rect = self._rect
        ptTL = QPointF(*self.padTL(padding, *top_left)) if top_left else rect.topLeft()
        ptBR = QPointF(*self.padBR(padding, *bottom_right)) if bottom_right else rect.bottomRight()
        self._rect = new_rect = QRectF(ptTL, ptBR)
        self.setRect(new_rect)
        self.configureOutline(self.outline)
        self.griditem.updateGrid()
        return (ptTL.x(), ptTL.y()), (ptBR.x(), ptBR.y())
    # end def

    def padTL(self, padding, xTL, yTL):
        return xTL - padding, yTL - padding
    # end def

    def padBR(self, padding, xBR, yBR):
        return xBR + padding, yBR
    # end def

    def enlargeRectToFit(self):
        """Enlarges Part Rectangle to fit the model bounds.  Call this
        when adding a SliceVirtualHelixItem.  Pad
        """
        xTL, yTL, xBR, yBR = self.getModelBounds()
        tl, br = self.reconfigureRect((xTL, yTL), (xBR, yBR))
        self.grab_cornerTL.alignPos(*tl)
        self.grab_cornerBR.alignPos(*br)
    # end def

    ### PRIVATE SUPPORT METHODS ###
    def configureOutline(self, outline):
        """Adjusts `outline` size with default padding.

        Args:
            outline (TYPE): Description

        Returns:
            o_rect (QRect): `outline` rect adjusted by _BOUNDING_RECT_PADDING
        """
        _p = _BOUNDING_RECT_PADDING
        o_rect = self.rect().adjusted(-_p, -_p, _p, _p)
        outline.setRect(o_rect)
        return o_rect
    # end def

    def boundRectToModel(self):
        """update the boundaries to what's in the model with a minimum
        size
        """
        xTL, yTL, xBR, yBR = self.getModelBounds()
        self._rect = QRectF(QPointF(xTL, yTL), QPointF(xBR, yBR))
    # end def

    def getModelBounds(self):
        """Bounds in form of Qt scaled from model

        Args:
            Tuple (top_left, bottom_right)
        """
        xLL, yLL, xUR, yUR = self.part().boundDimensions(self.scale_factor)
        return xLL, -yUR, xUR, -yLL
    # end def

    def bounds(self):
        """x_low, x_high, y_low, y_high
        """
        rect = self._rect
        return (rect.left(), rect.right(), rect.bottom(), rect.top())

    ### PUBLIC SUPPORT METHODS ###
    def setModifyState(self, bool_val):
        """Hides the mod_rect when modify state disabled.

        Args:
            bool_val (TYPE): what the modifystate should be set to.
        """
        self._can_show_mod_circ = bool_val
        if bool_val == False:
            self._mod_circ.hide()
    # end def

    def updateStatusBar(self, status_str):
        """Shows status_str in the MainWindow's status bar.

        Args:
            status_str (TYPE): Description
        """
        pass  # disabled for now.
        # self.window().statusBar().showMessage(status_str, timeout)
    # end def

    def zoomToFit(self):
        """Summary

        Args:
            TYPE: Description
        """
        thescene = self.scene()
        theview = thescene.views()[0]
        theview.zoomToFit()
    # end def

    ### EVENT HANDLERS ###
    def mousePressEvent(self, event):
        """Handler for user mouse press.

        Args:
            event (QGraphicsSceneMouseEvent): Contains item, scene, and screen
            coordinates of the the event, and previous event.

        Args:
            TYPE: Description
        """
        if event.button() == Qt.RightButton:
            return
        part = self._model_part
        part.setSelected(True)
        if self.isMovable():
            return QGraphicsItem.mousePressEvent(self, event)
        tool = self._getActiveTool()
        if tool.FILTER_NAME not in part.document().filter_set:
            return
        tool_method_name = tool.methodPrefix() + "MousePress"
        if hasattr(self, tool_method_name):
            getattr(self, tool_method_name)(tool, event)
        else:
            event.setAccepted(False)
            QGraphicsItem.mousePressEvent(self, event)
    # end def

    def hoverEnterEvent(self, event):
        self.vhi_hint_item.show()
        #print("Slice VHI hoverEnterEvent")

    # def hoverMoveEvent(self, event):
        # print("Slice VHI hoverMoveEvent")

    def hoverLeaveEvent(self, event):
        self.vhi_hint_item.hide()
        #print("Slice VHI hoverLeaveEvent")

    def hoverMoveEvent(self, event):
        """Summary

        Args:
            event (TYPE): Description

        Args:
            TYPE: Description
        """
        # print("Slice VHI hoverMoveEvent")
        self.vhi_hint_item.setPos(event.pos()-QPointF(_RADIUS-DELTA, _RADIUS-DELTA))

        tool = self._getActiveTool()
        tool_method_name = tool.methodPrefix() + "HoverMove"
        if hasattr(self, tool_method_name):
            getattr(self, tool_method_name)(tool, event)
        else:
            event.setAccepted(False)
            QGraphicsItem.hoverMoveEvent(self, event)
    # end def

    def getModelPos(self, pos):
        """Y-axis is inverted in Qt +y === DOWN

        Args:
            pos (TYPE): Description
        """
        sf = self.scale_factor
        x, y = pos.x()/sf, -1.0*pos.y()/sf
        return x, y
    # end def

    def getVirtualHelixItem(self, id_num):
        """Summary

        Args:
            id_num (int): VirtualHelix ID number. See `NucleicAcidPart` for description and related methods.

        Returns:
            TYPE: Description
        """
        return self._virtual_helix_item_hash.get(id_num)
    # end def

    def createToolMousePress(self, tool, event, alt_event=None):
        """Summary

        Args:
            tool (TYPE): Description
            event (TYPE): Description
            alt_event (None, optional): Description

        Returns:
            TYPE: Description
        """
        # 1. get point in model coordinates:
        part = self._model_part
        if alt_event is None:
            # print()
            pt = tool.eventToPosition(self, event)
            # print("reg_event", pt)
        else:
            # pt = alt_event.scenePos()
            # pt = self.mapFromScene(pt)
            pt = alt_event.pos()
            # print("alt_event", pt)

        if pt is None:
            tool.deactivate()
            return QGraphicsItem.mousePressEvent(self, event)

        part_pt_tuple = self.getModelPos(pt)

        mod = Qt.MetaModifier
        if not (event.modifiers() & mod):
            pass

        # don't create a new VirtualHelix if the click overlaps with existing
        # VirtualHelix
        current_id_num = tool.idNum()
        check = part.isVirtualHelixNearPoint(part_pt_tuple, current_id_num)
        # print("current_id_num", current_id_num, check)
        # print(part_pt_tuple)
        tool.setPartItem(self)
        if check:
            id_num = part.getVirtualHelixAtPoint(part_pt_tuple)
            # print("got a check", id_num)
            if id_num is not None:
                # print("restart", id_num)
                vhi = self._virtual_helix_item_hash[id_num]
                tool.setVirtualHelixItem(vhi)
                tool.startCreation()
        else:
            # print("creating", part_pt_tuple)
            part.createVirtualHelix(*part_pt_tuple)
            id_num = part.getVirtualHelixAtPoint(part_pt_tuple)
            vhi = self._virtual_helix_item_hash[id_num]
            tool.setVirtualHelixItem(vhi)
            tool.startCreation()
    # end def

    def createToolHoverMove(self, tool, event):
        """Summary

        Args:
            tool (TYPE): Description
            event (TYPE): Description

        Returns:
            TYPE: Description
        """
        tool.hoverMoveEvent(self, event)
        return QGraphicsItem.hoverMoveEvent(self, event)
    # end def

    def selectToolMousePress(self, tool, event):
        """
        Args:
            tool (TYPE): Description
            event (TYPE): Description
        """
        tool.setPartItem(self)
        pt = tool.eventToPosition(self, event)
        part_pt_tuple = self.getModelPos(pt)
        part = self._model_part
        if part.isVirtualHelixNearPoint(part_pt_tuple):
            id_num = part.getVirtualHelixAtPoint(part_pt_tuple)
            if id_num is not None:
                print(id_num)
                loc = part.getCoordinate(id_num, 0)
                print("VirtualHelix #{} at ({:.3f}, {:.3f})".format(id_num, loc[0], loc[1]))
            else:
                # tool.deselectItems()
                tool.modelClear()
        else:
            # tool.deselectItems()
            tool.modelClear()
        return QGraphicsItem.mousePressEvent(self, event)
Esempio n. 9
0
class QComposer(object):
    def __init__(self, tab, tabWd, collectionSelector, actor, proxyActor):
        self.proxyActor = proxyActor
        self.actorsItemsChanged = False

        self.scene = QGraphicsScene()
        self.scene.selectionChanged.connect(self.gItemSelectionChanged)
        self.selectionIndicator = QGraphicsEllipseItem(-100, -100,
                                                       INDICATOR_SIZE,
                                                       INDICATOR_SIZE)
        self.scene.addItem(self.selectionIndicator)
        TreeItem.scene = self.scene
        TreeItem.changeAmountCallback = self.changeAmount
        TreeItem.uiFactory = ComposerItemUIFactory(self.scene)

        self.collectionSelector = collectionSelector
        collectionSelector.selectedChanged.connect(self.aItemSelectionChanged)

        self.targetActor = actor
        actor.ownedItems.aItemChanged.connect(self.targetActorsItemChanged)

        self.graphicsViewWd = tabWd.findChild(QGraphicsView, 'composer_view')
        self.graphicsViewWd.setScene(self.scene)

        self.tab = tab
        tab.tabBecameVisible.connect(self.tabBecameVisible)

        tabWd.findChild(QPushButton,
                        "make_bt").clicked.connect(self.makeClicked)
        tabWd.findChild(QPushButton,
                        "autofill_bt").clicked.connect(self.autofillClicked)
        tabWd.findChild(QPushButton,
                        "clear_bt").clicked.connect(self.clearClicked)

        self.gItemSelectionChanged()
        self.rootGItem = TreeItem()
        self.rootGItem.drawCached()

    def tabBecameVisible(self):
        if self.actorsItemsChanged:
            self.updateProxyActorsItems()

    def targetActorsItemChanged(self, batch):
        if not self.tab.isActiveTab:
            self.actorsItemsChanged = True
        else:
            self.updateProxyActorsItems()

    def updateProxyActorsItems(self):
        # copy inplace
        copyInPlace(self.targetActor.ownedItems, self.proxyActor.ownedItems)
        self.proxyActor.ownedItems.startBatch()
        self.applyItemUpdate(self.rootGItem)
        self.rootGItem.drawCached()
        self.proxyActor.ownedItems.clearBatch()

    def applyItemUpdate(self, gItem):
        if gItem.aItem == None:
            return
        i = self.proxyActor.ownedItems.getAItemById(gItem.aItem.item.id)
        if i == None:
            gItem.clear(None, TreeItem.CLEAR_IGNORE)
            return
        gItem.takePreferredAmountOfItem(self.proxyActor, i)
        for c in gItem.children:
            self.applyItemUpdate(c)

    def gItemSelectionChanged(self):
        gItems = self.scene.selectedItems()
        if len(gItems) == 0:
            self.collectionSelector.setConstantPreFilter(ConstFilter(False))
            self.selectionIndicator.hide()
        else:
            gItem = gItems[0]
            self.selectionIndicator.show()
            self.selectionIndicator.setPos(gItem.x + INDICATOR_OFFSET,
                                           gItem.y + INDICATOR_OFFSET)
            if gItem.aItem != None:
                m = self.collectionSelector.tableModel
                for i in range(len(m.filteredAItemList)):
                    if gItem.aItem == m.filteredAItemList[i]:
                        # self.collectionSelector.tableView.setCurrentIndex(self.collectionSelector.proxyModel.mapFromSource(m.index(i, 0)))
                        self.collectionSelector.tableView.selectionModel(
                        ).select(
                            self.collectionSelector.proxyModel.mapFromSource(
                                m.index(i, 0)),
                            QItemSelectionModel.SelectCurrent)
                        break

            self.collectionSelector.setConstantPreFilter(
                self.getFilterForGItem(gItem))

    def aItemSelectionChanged(self, aItem):
        if aItem == None or not aItem.isValid():
            return
        gItems = self.scene.selectedItems()
        if len(gItems) == 0:
            return
        else:
            gItem = gItems[0]
            gItem.setAItem(aItem, self.proxyActor)

    def getFilterForGItem(self, gItem):
        if gItem.parent == None:
            return RootFilter(gItem.aItem)
        else:
            return ItemFuncFilter(gItem)

    def changeAmount(self, gItem, amount):
        if amount < 1:
            return

        amount -= gItem.aItem.amount
        if amount == 0:
            return

        if amount < 0:
            if gItem.aItem.item.amount + amount < 0:
                amount = -(gItem.aItem.item.amount - 1)
            self.proxyActor.ownedItems.append(
                AmountItem(gItem.aItem.item, amount * -1))
            gItem.aItem.amount += amount
        else:
            i = self.proxyActor.ownedItems.getAItemById(gItem.aItem.item.id)
            if amount > i.amount:
                amount = i.amount

            self.proxyActor.ownedItems.remove(
                AmountItem(gItem.aItem.item, amount))
            gItem.aItem.amount += amount

    def makeClicked(self):
        if not self.rootGItem.isValidComposition():
            return
        self.targetActor.ownedItems.startBatch()
        result = AmountList()
        self.rootGItem.createComposition(result)
        self.targetActor.ownedItems.addAmountList(result)
        aItem = self.rootGItem.aItem
        self.rootGItem.clear(self.targetActor, TreeItem.CLEAR_REMOVE)
        self.targetActor.ownedItems.endBatch()
        i = self.proxyActor.ownedItems.getAItemById(aItem.item.id)
        self.rootGItem.setAItem(i, self.proxyActor)
        self.rootGItem.drawCached()

    def clearClicked(self):
        self.targetActor.ownedItems.startBatch()
        self.rootGItem.clear(self.proxyActor, TreeItem.CLEAR_RECYCLE)
        self.targetActor.ownedItems.endBatch()
        self.rootGItem.drawCached()

    def autofillClicked(self):
        if self.rootGItem.aItem is None:
            return
        self.autofill(self.rootGItem)
        self.rootGItem.drawCached()

    def autofill(self, gItem):
        if gItem.aItem == None and (gItem.parent == None
                                    or gItem.parent.aItem.item.allowAutoFill):
            filter = self.getFilterForGItem(gItem)
            vI = next(
                (x
                 for x in self.proxyActor.ownedItems if filter.isValidItem(x)),
                None)
            if vI == None:
                return
            gItem.setAItem(vI, self.proxyActor)

        for c in gItem.children:
            self.autofill(c)
Esempio n. 10
0
class topoGraphScene(QGraphicsScene):
    ''' docstring: 场景模型类 '''
    def __init__(self, parent=None):
        super().__init__(parent)
        self.tmpnode = None
        self.tmpnode_transparent = None
        self.tmpedge = None
        self.signal_to_mainwindow = None

        # 添加特殊节点用于收包动画显示
        # TODO: 建立新的点类
        self.node_file = QGraphicsEllipseItem(-12, -12, 24, 24)
        self.node_file_img = QGraphicsPixmapItem(
            QPixmap(':/file/document').scaled(80, 72), self.node_file)
        self.node_file_img.setOffset(-40, -90)
        self.node_file.setPen(QPen(QColor('#ffff80'), 2))
        self.node_file.setBrush(Qt.red)
        self.node_file.setZValue(20)
        self.addItem(self.node_file)
        self.node_file.hide()

        # 设置特殊文本类显示信息
        self.baseinfo = Text("信息显示框",
                             font=QFont("SimHei", 12, QFont.Normal),
                             color=QColor("#ff8000"))
        self.addItem(self.baseinfo)
        self.baseinfo.setFlag(self.baseinfo.ItemIgnoresTransformations)
        self.baseinfo.hide()

        # 观察节点特殊记录
        self.node_me = None
        self.nid_me = None
        self.waitlist = []  # 用于暂存添加到topo图中的新节点修改属性

        # 绘制背景线
        self.backgroundLines = []
        for i in range(-250, 250):
            j = i + 250
            n1 = Node(nodetype=1)
            n2 = Node(nodetype=1)
            n1.setPos(i * 20, -5000)
            n2.setPos(i * 20, 5000)
            e = Edge(n1, n2, 2)
            e.setFlag(e.ItemIsSelectable, False)
            if j % 8 == 0:
                e.setPen(QPen(QColor("#111111"), 2, Qt.DotLine))
            self.addItem(e)
            self.backgroundLines.append(e)
            n3 = Node(nodetype=1)
            n4 = Node(nodetype=1)
            n3.setPos(-5000, i * 20)
            n4.setPos(5000, i * 20)
            e = Edge(n3, n4, 2)
            e.setFlag(e.ItemIsSelectable, False)
            if j % 8 == 0:
                e.setPen(QPen(QColor("#111111"), 2, Qt.DotLine))
            self.addItem(e)
            self.backgroundLines.append(e)

        # 拓扑图主要参数
        self.ASinfo = {}  # AS所含节点信息,格式:id:[node,...]
        self.belongAS = {}  # 节点所属AS,格式:id:ASitem
        self.nextedges = {}  # 边表,邻接表存储,格式:id:[(nextnode, edge),...]
        self.R = 0  # 布局中大圆半径

        # json格式转换时临时保存参数
        self.topo = {}
        self.data = {}

    def addEdge(self, n1, n2, edge):
        ''' docstring: 向nextedges字典中添加点-边映射(无向边) '''
        tmplist = self.nextedges.get(n1.id, [])
        tmplist.append((n2, edge))
        self.nextedges[n1.id] = tmplist
        tmplist = self.nextedges.get(n2.id, [])
        tmplist.append((n1, edge))
        self.nextedges[n2.id] = tmplist
        # 绑定对应点,便于边操作
        edge.node1 = n1
        edge.node2 = n2

    def delEdge(self, n1, n2, edge):
        ''' docstring: 与上面操作相反 '''
        self.nextedges[n1.id].remove((n2, edge))
        self.nextedges[n2.id].remove((n1, edge))

    def findPath(self, dest):
        ''' docstring: 选择最短路径,以观察节点为起点,返回到目标节点的路径(nid序列) '''
        if self.node_me == None:
            return None
        # print('init')
        for item in self.items():
            if isinstance(item, Node):
                item.setSelected(False)
                item.isvisited = False
                item.prenode = None
        # print('start')
        tmpqueue = Queue()
        tmpqueue.put(self.node_me)
        self.node_me.isvisited = True
        while not tmpqueue.empty():
            topnode = tmpqueue.get()
            for nextnode, nextedge in self.nextedges[topnode.id]:
                if not nextnode.isvisited:
                    tmpqueue.put(nextnode)
                    nextnode.isvisited = True
                    nextnode.prenode = topnode
        # print('get ans')
        if not dest.isvisited:
            return None
        else:
            ret = []
            now = dest
            while now:
                ret.append(now.id)
                now.setSelected(True)
                now = now.prenode
            ret.reverse()
            return ret

    def resetPos(self):
        # 设置图元位置
        num1 = len(self.ASinfo)
        self.R = 32 * num1 + 256
        now = 0
        for nodelist in self.ASinfo.values():
            alpha = pi * 2 / num1 * now
            now += 1
            X, Y = (-sin(alpha) * self.R, -cos(alpha) * self.R)
            # print(i, ':', alpha, '(', X, Y, ')', item.nid)
            asitem = nodelist.pop()
            asitem.setPos(X, Y)
            num2 = len(nodelist)
            r = num2 * 16 + 100
            for i, node in enumerate(nodelist):
                beta = pi * 2 / num2 * i + alpha
                x = X - sin(beta) * r
                y = Y - cos(beta) * r
                node.setPos(x, y)
            nodelist.append(asitem)

    def initTopo(self, path):
        ''' docstring: 初始化拓扑为配置文件信息 '''
        with open(path, 'r') as f:
            self.data = load(f)
            self.topo = self.data.get('topo map', {})
        if len(self.topo) == 0:
            return

        # 添加节点
        tmpass = []
        tmpnodes = []
        for i in range(len(self.topo['nodes'])):
            node = self.topo['nodes'][i]
            ntp = node['type']
            nnm = node.get('name', None)
            nsz = node.get('size', 0)
            nid = node.get('nid', None)
            font = node.get('font', None)
            if font:
                nft = QFont()
                nft.fromString(font)
            else:
                nft = None
            ncr = node.get('color', None)
            tfont = node.get('tfont', None)
            if tfont:
                tmp = QFont()
                tmp.fromString(tfont)
                tfont = tmp
            tcolor = node.get('tcolor', None)
            item = Node(ntp, nnm, nsz, nid, nft, ncr, tfont, tcolor)
            self.addItem(item)
            pos = node['pos']
            item.setPos(pos[0], pos[1])
            if ntp == 0:
                tmpass.append((i, item))
            elif ntp == 5:
                self.node_me = item
            tmpnodes.append(item)
        # 添加AS信息
        self.ASinfo = {}
        self.belongAS = {}
        for (i, asitem) in tmpass:
            nodelist = [tmpnodes[x] for x in self.topo['ASinfo'][str(i)]]
            nodelist.append(asitem)
            self.ASinfo[asitem.id] = nodelist
            self.belongAS[asitem.id] = asitem
            for x in self.topo['ASinfo'][str(i)]:
                self.belongAS[tmpnodes[x].id] = asitem
        # 添加边
        for item in self.topo['edges']:
            x = item[0]
            y = item[1]
            if len(item) > 2:
                font = None
                if len(item) > 3:
                    font = QFont()
                    font.fromString(item[3])
                color = None
                if len(item) > 4:
                    color = item[4]
                edgeitem = Edge(tmpnodes[x], tmpnodes[y], 0, item[2], font,
                                color)
            else:
                edgeitem = Edge(tmpnodes[x], tmpnodes[y], linetype=1)
            self.addItem(edgeitem)
            self.addEdge(tmpnodes[x], tmpnodes[y], edgeitem)

        # 根据标志位显示标签
        if self.parent().labelenable:
            for item in self.items():
                if isinstance(item, Node) or isinstance(item, Edge):
                    item.label.show()
        if self.parent().throughputenable:
            for item in self.items():
                if isinstance(item, Node) and not item.myType:
                    if item.id == self.belongAS[self.node_me.id].id:
                        continue
                    item.throughputlabel.show()

    def saveTopo(self, path):
        ''' docstring: 存储拓扑图 '''
        with open(path, 'r') as f:
            self.data = load(f)
        self.topo = {}

        # 删除所有还在waitlist中的点,视为添加失败
        for item in self.waitlist:
            self.removeItem(item)
        # 删除所有背景线
        for item in self.backgroundLines:
            self.removeItem(item)
        # 添加点
        tmpnodes = []
        tmpnodemap = {}
        num = 0
        for item in self.items():
            if isinstance(item, Node):
                node = {}
                node['type'] = item.myType
                if item.myType == 0:
                    node['size'] = item.size
                    node['tfont'] = item.throughputlabel.currentfont.toString()
                    node['tcolor'] = item.throughputlabel.currentcolor.name()
                elif item.myType == 1 or item.myType == 2 or item.myType == 5:
                    node['nid'] = item.nid
                node['pos'] = [item.scenePos().x(), item.scenePos().y()]
                node['name'] = item.name
                node['font'] = item.label.currentfont.toString()
                node['color'] = item.label.currentcolor.name()
                tmpnodes.append(node)
                tmpnodemap[item.id] = num
                num += 1
        self.topo['nodes'] = tmpnodes
        # 添加边
        tmpedges = []
        for item in self.items():
            if isinstance(item, Edge):
                x = tmpnodemap[item.node1.id]
                y = tmpnodemap[item.node2.id]
                if item.PX:
                    font = item.label.currentfont.toString()
                    color = item.label.currentcolor.name()
                    tmpedges.append([x, y, item.PX, font, color])
                else:
                    tmpedges.append([x, y])
        self.topo['edges'] = tmpedges
        # 添加AS信息
        tmpasinfo = {}
        for asid, nodes in self.ASinfo.items():
            x = tmpnodemap[asid]
            y = []
            for node in nodes:
                z = tmpnodemap[node.id]
                if x != z:
                    y.append(z)
            tmpasinfo[str(x)] = y
        self.topo['ASinfo'] = tmpasinfo

        with open(path, 'w') as f:
            self.data['topo map'] = self.topo
            dump(self.data, f)