Exemple #1
0
    def __init__(self, model_strand, virtual_helix_item, viewroot):
        """The parent should be a VirtualHelixItem."""
        super(StrandItem, self).__init__(virtual_helix_item)
        self._model_strand = model_strand
        self._virtual_helix_item = virtual_helix_item
        self._viewroot = viewroot
        self._getActiveTool = virtual_helix_item._getActiveTool

        self._controller = StrandItemController(self, model_strand)
        is_drawn_5to3 = model_strand.strandSet().isDrawn5to3()

        self._strand_filter = model_strand.strandFilter()

        self._insertion_items = {}
        # caps
        self._low_cap = EndpointItem(self, 'low', is_drawn_5to3)
        self._high_cap = EndpointItem(self, 'high', is_drawn_5to3)
        # self._high_cap = None
        self._dual_cap = EndpointItem(self, 'dual', is_drawn_5to3)

        # orientation
        self._is_drawn_5to3 = is_drawn_5to3
        # self._isOnTop = virtual_helix_item.isStrandOnTop(model_strand)
        # label
        self._seq_label = QGraphicsSimpleTextItem(self)

        self.refreshInsertionItems(model_strand)
        if not getBatch():
            self._updateSequenceText()

        # create a larger click area rect to capture mouse events
        self._click_area = c_a = QGraphicsRectItem(_DEFAULT_RECT, self)
        c_a.mousePressEvent = self.mousePressEvent
        c_a.setPen(_NO_PEN)
        self.setAcceptHoverEvents(True)
        c_a.setAcceptHoverEvents(True)
        c_a.hoverMoveEvent = self.hoverMoveEvent

        self.setZValue(styles.ZSTRANDITEM)

        # xover comming from the 3p end
        self._xover3pEnd = XoverItem(self, virtual_helix_item)
        if not getBatch():
            # initial refresh
            self._updateColor(model_strand)
            self._updateAppearance(model_strand)

        self.setZValue(styles.ZSTRANDITEM)
        self.setFlag(QGraphicsItem.ItemIsSelectable)
Exemple #2
0
    def __init__(self, model_strand, virtual_helix_item, viewroot):
        """The parent should be a VirtualHelixItem."""
        super(StrandItem, self).__init__(virtual_helix_item)
        self._model_strand = model_strand
        self._virtual_helix_item = virtual_helix_item
        self._viewroot = viewroot
        self._getActiveTool = virtual_helix_item._getActiveTool

        self._controller = StrandItemController(self, model_strand)
        is_drawn_5to3 = model_strand.strandSet().isDrawn5to3()

        self._strand_filter = model_strand.strandFilter()

        self._insertion_items = {}
        # caps
        self._low_cap = EndpointItem(self, 'low', is_drawn_5to3)
        self._high_cap = EndpointItem(self, 'high', is_drawn_5to3)
        # self._high_cap = None
        self._dual_cap = EndpointItem(self, 'dual', is_drawn_5to3)

        # orientation
        self._is_drawn_5to3 = is_drawn_5to3
        # self._isOnTop = virtual_helix_item.isStrandOnTop(model_strand)
        # label
        self._seq_label = QGraphicsSimpleTextItem(self)
        
        self.refreshInsertionItems(model_strand)
        if not getBatch():
            self._updateSequenceText()

        # create a larger click area rect to capture mouse events
        self._click_area = c_a = QGraphicsRectItem(_DEFAULT_RECT, self)
        c_a.mousePressEvent = self.mousePressEvent
        c_a.setPen(_NO_PEN)
        self.setAcceptHoverEvents(True)
        c_a.setAcceptHoverEvents(True)
        c_a.hoverMoveEvent = self.hoverMoveEvent

        self.setZValue(styles.ZSTRANDITEM)

        # xover comming from the 3p end
        self._xover3pEnd = XoverItem(self, virtual_helix_item)
        if not getBatch():
            # initial refresh
            self._updateColor(model_strand)
            self._updateAppearance(model_strand)

        self.setZValue(styles.ZSTRANDITEM)
        self.setFlag(QGraphicsItem.ItemIsSelectable)
Exemple #3
0
class StrandItem(QGraphicsLineItem):
    _filter_name = "strand"
    
    def __init__(self, model_strand, virtual_helix_item, viewroot):
        """The parent should be a VirtualHelixItem."""
        super(StrandItem, self).__init__(virtual_helix_item)
        self._model_strand = model_strand
        self._virtual_helix_item = virtual_helix_item
        self._viewroot = viewroot
        self._getActiveTool = virtual_helix_item._getActiveTool

        self._controller = StrandItemController(self, model_strand)
        is_drawn_5to3 = model_strand.strandSet().isDrawn5to3()

        self._strand_filter = model_strand.strandFilter()

        self._insertion_items = {}
        # caps
        self._low_cap = EndpointItem(self, 'low', is_drawn_5to3)
        self._high_cap = EndpointItem(self, 'high', is_drawn_5to3)
        # self._high_cap = None
        self._dual_cap = EndpointItem(self, 'dual', is_drawn_5to3)

        # orientation
        self._is_drawn_5to3 = is_drawn_5to3
        # self._isOnTop = virtual_helix_item.isStrandOnTop(model_strand)
        # label
        self._seq_label = QGraphicsSimpleTextItem(self)
        
        self.refreshInsertionItems(model_strand)
        if not getBatch():
            self._updateSequenceText()

        # create a larger click area rect to capture mouse events
        self._click_area = c_a = QGraphicsRectItem(_DEFAULT_RECT, self)
        c_a.mousePressEvent = self.mousePressEvent
        c_a.setPen(_NO_PEN)
        self.setAcceptHoverEvents(True)
        c_a.setAcceptHoverEvents(True)
        c_a.hoverMoveEvent = self.hoverMoveEvent

        self.setZValue(styles.ZSTRANDITEM)

        # xover comming from the 3p end
        self._xover3pEnd = XoverItem(self, virtual_helix_item)
        if not getBatch():
            # initial refresh
            self._updateColor(model_strand)
            self._updateAppearance(model_strand)

        self.setZValue(styles.ZSTRANDITEM)
        self.setFlag(QGraphicsItem.ItemIsSelectable)
    # end def

    ### SIGNALS ###

    ### SLOTS ###
    def strandResizedSlot(self, strand, indices):
        """docstring for strandResizedSlot"""
        low_moved = self._low_cap.updatePosIfNecessary(self.idxs()[0])
        high_moved = self._high_cap.updatePosIfNecessary(self.idxs()[1])
        group = self.group()
        self.tempReparent()  
        if low_moved:
            self.updateLine(self._low_cap)
        if high_moved:
            self.updateLine(self._high_cap)
        if strand.connection3p():
            self._xover3pEnd.update(strand)
        self.refreshInsertionItems(strand)
        self._updateSequenceText()
        if group:
            group.addToGroup(self)
    # end def

    def sequenceAddedSlot(self, oligo):
        """docstring for sequenceAddedSlot"""
        pass
    # end def

    def sequenceClearedSlot(self, oligo):
        """docstring for sequenceClearedSlot"""
        pass
    # end def

    def strandRemovedSlot(self, strand):
        # self._model_strand = None
        self._controller.disconnectSignals()
        self._controller = None
        scene = self.scene()
        scene.removeItem(self._click_area)
        self._high_cap.destroy()
        self._low_cap.destroy()
        # scene.removeItem(self._high_cap)
        # scene.removeItem(self._low_cap)
        scene.removeItem(self._seq_label)
        self._xover3pEnd.remove()
        self._xover3pEnd = None
        for insertionItem in self._insertion_items.values():
            insertionItem.remove()
        self._insertion_items = None
        self._click_area = None
        self._high_cap = None
        self._low_cap = None
        self._seq_label = None
        self._model_strand = None
        self._virtual_helix_item = None
        scene.removeItem(self)
    # end def

    def strandUpdateSlot(self, strand):
        """
        Slot for just updating connectivity and color, and endpoint showing
        """
        self._updateAppearance(strand)
    # end def

    def oligoAppearanceChangedSlot(self, oligo):
        strand = self._model_strand
        self._updateColor(strand)
        if strand.connection3p():
            self._xover3pEnd._updateColor(strand)
        for insertion in self.insertionItems().values():
            insertion.updateItem()
    # end def

    def oligoSequenceAddedSlot(self, oligo):
        self._updateSequenceText()
    # end def

    def oligoSequenceClearedSlot(self, oligo):
        self._updateSequenceText()
    # end def

    def strandHasNewOligoSlot(self, strand):
        strand = self._model_strand
        self._controller.reconnectOligoSignals()
        self._updateColor(strand)
        if strand.connection3p():
            self._xover3pEnd._updateColor(strand)
    # end def

    def strandInsertionAddedSlot(self, strand, insertion):
        self.insertionItems()[insertion.idx()] = \
                    InsertionItem(self._virtual_helix_item, strand, insertion)
    # end def
    def strandInsertionChangedSlot(self, strand, insertion):
        self.insertionItems()[insertion.idx()].updateItem()
    # end def

    def strandInsertionRemovedSlot(self, strand, index):
        instItem = self.insertionItems()[index]
        instItem.remove()
        del self.insertionItems()[index]
    # end def

    def strandModsAddedSlot(self, strand, mod_id, idx):
        idx_l, idx_h = strand.idxs()
        color = strand.part().getMod(mod_id)['color']
        if idx == idx_h:
            self._high_cap.showMod(mod_id, color)
        else:
            self._low_cap.showMod(mod_id, color) 
    # end def

    def strandModsChangedSlot(self, strand, mod_id, idx):
        idx_l, idx_h = strand.idxs()
        color = strand.part().getMod(mod_id)['color']
        if idx == idx_h:
            self._high_cap.changeMod(mod_id, color)
        else:
            self._low_cap.changeMod(mod_id, color) 
    # end def

    def strandModsRemovedSlot(self, strand, mod_id, idx):
        idx_l, idx_h = strand.idxs()
        color = strand.part().getMod(mod_id)['color']
        if idx == idx_h:
            self._high_cap.destroyMod()
        else:
            self._low_cap.destroyMod() 
    # end def

    def strandModifierAddedSlot(self, strand, modifier):
        pass
    # end def
    def strandModifierChangedSlot(self, strand, modifier):
        pass
    # end def
    def strandModifierRemovedSlot(self, strand, index):
        pass
    # end def

    def selectedChangedSlot(self, strand, indices):
        self.selectIfRequired(self.partItem().document(), indices)
    # end def

    ### ACCESSORS ###
    def viewroot(self):
        return self._viewroot
    # end def

    def insertionItems(self):
        return self._insertion_items
    # end def

    def strand(self):
        return self._model_strand
    # end def

    def strandFilter(self):
        return self._strand_filter
    # end def

    def idxs(self):
        return self._model_strand.idxs()
    # end def

    def virtualHelixItem(self):
        return self._virtual_helix_item
    # end def

    def partItem(self):
        return self._virtual_helix_item.partItem()
    # end def

    def window(self):
        return self._virtual_helix_item.window()

    ### PUBLIC METHODS FOR DRAWING / LAYOUT ###
    def refreshInsertionItems(self, strand):
        i_items = self.insertionItems()
        i_model = strand.insertionsOnStrand()
        
        was_in_use = set(i_items)
        in_use = set()
        # add in the ones supposed to be there
        for insertion in i_model:
            idx = insertion.idx()
            in_use.add(idx)
            if idx in i_items:
                pass
            else:
                i_items[insertion.idx()] = \
                    InsertionItem(self._virtual_helix_item, strand, insertion)
        # end for
        
        # remove all in items
        not_in_use = was_in_use - in_use
        for index in not_in_use:
            i_items[index].remove()
            del i_items[index]
        # end for
    # end def

    def resetStrandItem(self, virtual_helix_item, is_drawn_5to3):
        self.setParentItem(virtual_helix_item)
        self._virtual_helix_item = virtual_helix_item
        self.resetEndPointItems(is_drawn_5to3)
    # end def

    def resetEndPointItems(self, is_drawn_5to3):
        self._is_drawn_5to3 = is_drawn_5to3
        self._low_cap.resetEndPoint(is_drawn_5to3)
        self._high_cap.resetEndPoint(is_drawn_5to3)
        self._dual_cap.resetEndPoint(is_drawn_5to3)
    # end def

    def updateLine(self, moved_cap):
        # setup
        bw = _BASE_WIDTH
        c_a = self._click_area
        line = self.line()
        # set new line coords
        if moved_cap == self._low_cap:
            p1 = line.p1()
            new_x = self._low_cap.pos().x() + bw
            p1.setX(new_x)
            line.setP1(p1)
            temp = c_a.rect()
            temp.setLeft(new_x)
            c_a.setRect(temp)
        else:
            p2 = line.p2()
            new_x = self._high_cap.pos().x()
            p2.setX(new_x)
            line.setP2(p2)
            temp = c_a.rect()
            temp.setRight(new_x)
            c_a.setRect(temp)
        self.setLine(line)
    # end def

    ### PRIVATE SUPPORT METHODS ###
    def _updateAppearance(self, strand):
        """
        Prepare Strand for drawing, positions are relative to the VirtualHelixItem:
        1. Show or hide caps depending on L and R connectivity.
        2. Determine line coordinates.
        3. Apply paint styles.
        """
        # 0. Setup
        vhi = self._virtual_helix_item
        bw = _BASE_WIDTH
        half_base_width = bw / 2.0
        low_idx, high_idx = strand.lowIdx(), strand.highIdx()

        l_upper_left_x, l_upper_left_y = vhi.upperLeftCornerOfBase(low_idx, strand)
        h_upper_left_x, h_upper_left_y = vhi.upperLeftCornerOfBase(high_idx, strand)
        low_cap = self._low_cap
        high_cap = self._high_cap
        dual_cap = self._dual_cap

        # 1. Cap visibilty
        lx = l_upper_left_x + bw  # draw from right edge of base
        low_cap.safeSetPos(l_upper_left_x, l_upper_left_y)
        if strand.connectionLow() is not None:  # has low xover
            # if we are hiding it, we might as well make sure it is reparented to the StrandItem
            low_cap.restoreParent()
            low_cap.hide()
        else:  # has low cap
            if not low_cap.isVisible():
                low_cap.show()

        hx = h_upper_left_x  # draw to edge of base
        high_cap.safeSetPos(h_upper_left_x, h_upper_left_y)
        if strand.connectionHigh() is not None:  # has high xover
            # if we are hiding it, we might as well make sure it is reparented to the StrandItem
            high_cap.restoreParent()
            high_cap.hide()
        else:  # has high cap
            if not high_cap.isVisible():
                high_cap.show()

        # special case: single-base strand with no L or H connections,
        # (unconnected caps were made visible in previous block of code)
        if strand.length() == 1 and \
                  (low_cap.isVisible() and high_cap.isVisible()):
            low_cap.hide()
            high_cap.hide()
            dual_cap.safeSetPos(l_upper_left_x, l_upper_left_y)
            dual_cap.show()
        else:
            dual_cap.hide()

        # 2. Xover drawing
        xo = self._xover3pEnd
        if strand.connection3p():
            xo.update(strand)
            xo.showIt()
        else:
            xo.restoreParent()
            xo.hideIt()

        # 3. Refresh insertionItems if necessary drawing
        self.refreshInsertionItems(strand)

        # 4. Line drawing
        hy = ly = l_upper_left_y + half_base_width
        self.setLine(lx, ly, hx, hy)
        rectf = QRectF(l_upper_left_x + bw, l_upper_left_y, bw*(high_idx - low_idx - 1), bw)
        self._click_area.setRect(rectf)
        # self._updateHighlight(self.pen().color())
        self._updateColor(strand)
    # end def

    def _updateColor(self, strand):
        oligo = self._model_strand.oligo()
        color = QColor(oligo.color())
        self._updateHighlight(color)
    # end def

    def _updateHighlight(self, color):
        """
        
        """
        oligo = self._model_strand.oligo()
        pen_width = styles.PATH_STRAND_STROKE_WIDTH
        if oligo.shouldHighlight():
            color.setAlpha(128)
            pen_width = styles.PATH_STRAND_HIGHLIGHT_STROKE_WIDTH
        pen = QPen(color, pen_width)
        # pen.setCosmetic(True)
        brush = QBrush(color)
        pen.setCapStyle(Qt.FlatCap)
        self.setPen(pen)
        self._low_cap.updateHighlight(brush)
        self._high_cap.updateHighlight(brush)
        self._dual_cap.updateHighlight(brush)
    # end def

    def _updateSequenceText(self):
        """
        docstring for _updateSequenceText
        """
        bw = _BASE_WIDTH
        seq_lbl = self._seq_label
        strand = self.strand()
        
        seq_txt = strand.sequence()
        isDrawn3to5 = not self._is_drawn_5to3
        textXCenteringOffset = styles.SEQUENCETEXTXCENTERINGOFFSET

        if seq_txt == '':
            seq_lbl.hide()
            for iItem in self.insertionItems().values():
                iItem.hideSequence()
            return
        # end if

        strand_seq_list = strand.getSequenceList()
        seq_list = [x[1][0] for x in strand_seq_list]
        insert_seq_list = [(x[0], x[1][1]) for x in strand_seq_list]
        
        i_items = self.insertionItems()
        for idx, seq_txt in insert_seq_list:
            if seq_txt != '':
                i_items[idx].setSequence(seq_txt)

        if isDrawn3to5:
            seq_list = seq_list[::-1]
            
        seq_txt = ''.join(seq_list)
        
        # seq_lbl.setPen(QPen( Qt.NoPen))    # leave the Pen as None for unless required
        seq_lbl.setBrush(QBrush(Qt.black))
        seq_lbl.setFont(styles.SEQUENCEFONT)

        # this will always draw from the 5 Prime end!
        seqX = 2*textXCenteringOffset + bw*strand.idx5Prime()
        seqY = styles.SEQUENCETEXTYCENTERINGOFFSET

        if isDrawn3to5:
            # offset it towards the bottom
            seqY += bw * .8
            # offset X by the reverse centering offset and the string length
            seqX += textXCenteringOffset
            # rotate the characters upside down this does not affect positioning
            # coordinate system, +Y is still Down, and +X is still Right
            seq_lbl.setRotation(180)
            # draw the text and reverse the string to draw 5 prime to 3 prime
            # seq_txt = seq_txt[::-1]
        # end if
        seq_lbl.setPos(seqX,seqY)
        seq_lbl.setText(seq_txt)
        seq_lbl.show()
    # end def

    ### EVENT HANDLERS ###
    def mousePressEvent(self, event):
        """
        Parses a mousePressEvent to extract strandSet and base index,
        forwarding them to approproate tool method as necessary.
        """
        active_tool_str = self._getActiveTool().methodPrefix()
        self.scene().views()[0].addToPressList(self)
        idx = int(floor((event.pos().x()) / _BASE_WIDTH))
        self._virtual_helix_item.setActive(idx)
        tool_method_name =  active_tool_str + "MousePress"
        if hasattr(self, tool_method_name):
            getattr(self, tool_method_name)(event, idx)
        else:
            event.setAccepted(False)
    # end def

    def mouseMoveEvent(self, event):
        """
        Parses a mouseMoveEvent to extract strandSet and base index,
        forwarding them to approproate tool method as necessary.
        """
        tool_method_name = self._getActiveTool().methodPrefix() + "MouseMove"
        if hasattr(self, tool_method_name):
            idx = int(floor((event.pos().x()) / _BASE_WIDTH))
            getattr(self, tool_method_name)(idx)
    # end def

    def hoverLeaveEvent(self, event):
        self.partItem().updateStatusBar("")
        tool_method_name = self._getActiveTool().methodPrefix() + "HoverLeave"
        if hasattr(self, tool_method_name):
            getattr(self, tool_method_name)(event)
    # end def

    def hoverMoveEvent(self, event):
        """
        Parses a mouseMoveEvent to extract strandSet and base index,
        forwarding them to approproate tool method as necessary.
        """
        vhi_num = self._virtual_helix_item.number()
        idx = int(floor((event.pos().x()) / _BASE_WIDTH))
        oligo_length = self._model_strand.oligo().length()
        self.partItem().updateStatusBar("%d[%d]\tlength: %d" % (vhi_num, idx, oligo_length))
        tool_method_name = self._getActiveTool().methodPrefix() + "HoverMove"
        if hasattr(self, tool_method_name):
            getattr(self, tool_method_name)(event, idx)
    # end def

    def customMouseRelease(self, event):
        """
        Parses a mouseReleaseEvent to extract strandSet and base index,
        forwarding them to approproate tool method as necessary.
        """
        tool_method_name = self._getActiveTool().methodPrefix() + "MouseRelease"
        if hasattr(self, tool_method_name):
            idx = int(floor((event.pos().x()) / _BASE_WIDTH))
            getattr(self, tool_method_name)(idx)
    # end def

    ### TOOL METHODS ###
    def breakToolMousePress(self, event, idx):
        """Break the strand is possible."""
        m_strand = self._model_strand
        m_strand.split(idx)
    # end def

    def breakToolHoverMove(self, event, idx):
        pass
        # m_strand = self._model_strand
        vhi = self._virtual_helix_item
        break_tool = self._getActiveTool()
        break_tool.hoverMove(vhi, event, flag=None)
        # break_tool.updateHoverRect(vhi, m_strand, idx, show=True)
    # end def

    def breakToolHoverLeave(self, event):
        self._getActiveTool().hoverLeaveEvent(event)
    # end def

    def selectToolMousePress(self, event, idx):
        event.setAccepted(False)
        current_filter_dict = self._viewroot.selectionFilterDict()
        if self.strandFilter() in current_filter_dict and self._filter_name in current_filter_dict:
            selection_group = self._viewroot.strandItemSelectionGroup()
            mod = Qt.MetaModifier
            if not (event.modifiers() & mod):
                 selection_group.clearSelection(False)
            selection_group.setSelectionLock(selection_group)
            selection_group.pendToAdd(self)
            selection_group.pendToAdd(self._low_cap)
            selection_group.pendToAdd(self._high_cap)
            selection_group.processPendingToAddList()
            event.setAccepted(True)
            return selection_group.mousePressEvent(event)
    # end def

    def eraseToolMousePress(self, event, idx):
        m_strand = self._model_strand
        m_strand.strandSet().removeStrand(m_strand)
    # end def

    def insertionToolMousePress(self, event, idx):
        """Add an insert to the strand if possible."""
        m_strand = self._model_strand
        m_strand.addInsertion(idx, 1)
    # end def

    def paintToolMousePress(self, event, idx):
        """Add an insert to the strand if possible."""
        m_strand = self._model_strand
        if m_strand.isStaple():
            color = self.window().path_color_panel.stapColorName()
        else:
            color = self.window().path_color_panel.scafColorName()
        m_strand.oligo().applyColor(color)
    # end def

    def pencilToolHoverMove(self, event, idx):
        """Pencil the strand is possible."""
        m_strand = self._model_strand
        vhi = self._virtual_helix_item
        active_tool = self._getActiveTool()

        if not active_tool.isFloatingXoverBegin():
            temp_xover = active_tool.floatingXover()
            temp_xover.updateFloatingFromStrandItem(vhi, m_strand, idx)
            # if m_strand.idx5Prime() == idx:
            #     temp_xover.hide3prime()
            # elif m_strand.idx3Prime() != idx:
            #     temp_xover.show3prime()
    # end def

    def pencilToolMousePress(self, event, idx):
        """Break the strand is possible."""
        m_strand = self._model_strand
        vhi = self._virtual_helix_item
        part_item = vhi.partItem()
        active_tool = self._getActiveTool()

        if active_tool.isFloatingXoverBegin():
            # block xovers starting at a 5 prime end
            if m_strand.idx5Prime() == idx:
                return
            else:
                temp_xover = active_tool.floatingXover()
                temp_xover.updateBase(vhi, m_strand, idx)
                active_tool.setFloatingXoverBegin(False)
        else:
            active_tool.setFloatingXoverBegin(True)
            # install Xover
            active_tool.attemptToCreateXover(vhi, m_strand, idx)
    # end def

    def skipToolMousePress(self, event, idx):
        """Add an insert to the strand if possible."""
        m_strand = self._model_strand
        m_strand.addInsertion(idx, -1)
    # end def
    
    def addSeqToolMousePress(self, event, idx):
        """
        Checks that a scaffold was clicked, and then calls apply sequence
        to the clicked strand via its oligo.
        """
        m_strand = self._model_strand
        if m_strand.isScaffold():
            olgLen, seqLen = self._getActiveTool().applySequence(m_strand.oligo())
            if olgLen:
                msg = "Populated %d of %d scaffold bases." % (min(seqLen, olgLen), olgLen)
                if olgLen > seqLen:
                    d = olgLen - seqLen
                    msg = msg + " Warning: %d bases have no sequence." % d
                elif olgLen < seqLen:
                    d = seqLen - olgLen
                    msg = msg + " Warning: %d sequence bases unused." % d
                self.partItem().updateStatusBar(msg)
    # end def
    
    def restoreParent(self, pos=None):
        """
        Required to restore parenting and positioning in the partItem
        """
        # map the position
        # print "restoring parent si"
        self.tempReparent(pos)
        self.setSelectedColor(False)
        self.setSelected(False)
    # end def
    
    def tempReparent(self, pos=None):
        vh_item = self.virtualHelixItem()
        if pos == None:
            pos = self.scenePos()
        self.setParentItem(vh_item)
        tempP = vh_item.mapFromScene(pos)
        self.setPos(tempP)
    # end def

    def setSelectedColor(self, value):
        if value == True:
            color = QColor("#ff3333")
        else:
            oligo = self._model_strand.oligo()
            color = QColor(oligo.color())
            if oligo.shouldHighlight():
                color.setAlpha(128)
        pen = self.pen()
        pen.setColor(color)
        self.setPen(pen)
    # end def

    def itemChange(self, change, value):
        # for selection changes test against QGraphicsItem.ItemSelectedChange
        # intercept the change instead of the has changed to enable features.
        if change == QGraphicsItem.ItemSelectedChange and self.scene():
            active_tool = self._getActiveTool()
            if active_tool.methodPrefix() == "selectTool":
                viewroot = self._viewroot
                current_filter_dict = viewroot.selectionFilterDict()
                selection_group = viewroot.strandItemSelectionGroup()
        
                # only add if the selection_group is not locked out
                is_normal_select = selection_group.isNormalSelect()
                if value == True and (self._filter_name in current_filter_dict or not is_normal_select):
                    if self._strand_filter in current_filter_dict:
                        if self.group() != selection_group:
                            self.setSelectedColor(True)
                            # This should always be the case, but...
                            if is_normal_select:
                                selection_group.pendToAdd(self)
                                selection_group.setSelectionLock(selection_group)
                                selection_group.pendToAdd(self._low_cap)
                                selection_group.pendToAdd(self._high_cap)
                            # this else will capture the error.  Basically, the
                            # strandItem should be member of the group before this
                            # ever gets fired
                            else:
                                selection_group.addToGroup(self)
                        return True
                    else:
                        return False
                # end if
                elif value == True:
                    # Don't select
                    return False
                # end elif
                else:
                    # Deselect
                    # print "Deselecting strand"
                    selection_group.pendToRemove(self)
                    self.setSelectedColor(False)
                    selection_group.pendToRemove(self._low_cap)
                    selection_group.pendToRemove(self._high_cap)
                    return False
                # end else
            # end if
            elif active_tool.methodPrefix() == "paintTool":
                viewroot = self._viewroot
                current_filter_dict = viewroot.selectionFilterDict()
                if self._strand_filter in current_filter_dict:
                    if not active_tool.isMacrod():
                        active_tool.setMacrod()
                    self.paintToolMousePress(None, None)
            # end elif
            return False
        # end if
        return QGraphicsItem.itemChange(self, change, value)
    # end def

    def selectIfRequired(self, document, indices):
        """
        Select self or xover item as necessary
        """
        strand5p = self._model_strand
        con3p = strand5p.connection3p()
        selection_group = self._viewroot.strandItemSelectionGroup()
        # check this strand's xover
        idx_l, idx_h = indices
        if con3p:
            # perhaps change this to a direct call, but here are seeds of an 
            # indirect way of doing selection checks    
            if document.isModelStrandSelected(con3p) and document.isModelStrandSelected(strand5p):
                val3p = document.getSelectedStrandValue(con3p)
                # print "xover idx", indices
                test3p = val3p[0] if con3p.isDrawn5to3() else val3p[1]
                test5p = idx_h if strand5p.isDrawn5to3() else idx_l
                if test3p and test5p:
                    xoi = self._xover3pEnd
                    if not xoi.isSelected() or not xoi.group():
                        selection_group.setNormalSelect(False)
                        selection_group.addToGroup(xoi)
                        xoi.modelSelect(document)
                        selection_group.setNormalSelect(True)
                # end if
            # end if
        # end if
        # Now check the endpoints
        
        low_cap = self._low_cap
        if idx_l == True:
            if not low_cap.isSelected() or not low_cap.group():
                selection_group.addToGroup(low_cap)
                low_cap.modelSelect(document)
        else:
            if low_cap.isSelected() or low_cap.group():
                low_cap.restoreParent()
        high_cap = self._high_cap
        if idx_h == True:
            if not high_cap.isSelected() or not high_cap.group():
                selection_group.addToGroup(high_cap)
                high_cap.modelSelect(document)
        else:
            if high_cap.isSelected() or high_cap.group():
                high_cap.restoreParent()
        
        # now check the strand itself
        if idx_l == True and idx_h == True:
            if not self.isSelected() or not self.group():
                selection_group.setNormalSelect(False)
                selection_group.addToGroup(self)
                self.modelSelect(document)
                selection_group.setNormalSelect(True)
        elif idx_l == False and idx_h == False:
            self.modelDeselect(document)
    # end def

    def modelDeselect(self, document):
        self.restoreParent()
        self._low_cap.modelDeselect(document)
        self._high_cap.modelDeselect(document)
    # end def

    def modelSelect(self, document):
        self.setSelected(True)
        self.setSelectedColor(True)
    # end def
    
    def paint(self, painter, option, widget):
        painter.setPen(self.pen())
        painter.drawLine(self.line())
Exemple #4
0
class StrandItem(QGraphicsLineItem):
    FILTER_NAME = "strand"

    __slots__ = ('_model_strand', '_virtual_helix_item', '_viewroot',
                 '_getActiveTool', '_controller', 'is_forward',
                 '_strand_filter', '_insertion_items', '_low_cap', '_high_cap',
                 '_dual_cap', '_seq_label', '_click_area', 'xover_3p_end')

    def __init__(self, model_strand, virtual_helix_item, viewroot):
        """The parent should be a VirtualHelixItem."""
        super(StrandItem, self).__init__(virtual_helix_item)
        self._model_strand = model_strand
        self._virtual_helix_item = virtual_helix_item
        self._viewroot = viewroot
        self._getActiveTool = viewroot.manager.activeToolGetter

        self._controller = StrandItemController(self, model_strand)
        is_forward = model_strand.strandSet().isForward()

        self._strand_filter = model_strand.strandFilter()

        self._insertion_items = {}
        # caps
        self._low_cap = EndpointItem(self, 'low', is_forward)
        self._high_cap = EndpointItem(self, 'high', is_forward)
        self._dual_cap = EndpointItem(self, 'dual', is_forward)

        # orientation
        self.is_forward = is_forward
        self._seq_label = QGraphicsSimpleTextItem(self)

        self.refreshInsertionItems(model_strand)
        if not getBatch():
            self._updateSequenceText()

        # create a larger click area rect to capture mouse events
        self._click_area = c_a = QGraphicsRectItem(_DEFAULT_RECT, self)
        c_a.mousePressEvent = self.mousePressEvent
        c_a.setPen(_NO_PEN)
        self.setAcceptHoverEvents(True)
        c_a.setAcceptHoverEvents(True)
        c_a.hoverMoveEvent = self.hoverMoveEvent

        self.setZValue(styles.ZSTRANDITEM)

        # xover comming from the 3p end
        self.xover_3p_end = XoverItem(self, virtual_helix_item)
        if not getBatch():
            # initial refresh
            self._updateColor(model_strand)
            self._updateAppearance(model_strand)

        self.setZValue(styles.ZSTRANDITEM)
        self.setFlag(QGraphicsItem.ItemIsSelectable)

    # end def

    ### SIGNALS ###

    ### SLOTS ###
    def strandResizedSlot(self, strand, indices):
        """docstring for strandResizedSlot"""
        low_moved, group_low = self._low_cap.updatePosIfNecessary(
            self.idxs()[0])
        high_moved, group_high = self._high_cap.updatePosIfNecessary(
            self.idxs()[1])
        group = self.group()
        self.tempReparent()
        if low_moved:
            self.updateLine(self._low_cap)
        if high_moved:
            self.updateLine(self._high_cap)
        if strand.connection3p():
            self.xover_3p_end.update(strand)
        self.refreshInsertionItems(strand)
        self._updateSequenceText()
        if group:
            group.addToGroup(self)
        if group_low is not None:
            group_low.addToGroup(self._low_cap)
        if group_high is not None:
            group_high.addToGroup(self._high_cap)

    # end def

    def strandRemovedSlot(self, strand):
        # self._model_strand = None
        self._controller.disconnectSignals()
        self._controller = None
        scene = self.scene()
        scene.removeItem(self._click_area)
        self._high_cap.destroy()
        self._low_cap.destroy()
        # scene.removeItem(self._high_cap)
        # scene.removeItem(self._low_cap)
        scene.removeItem(self._seq_label)
        self.xover_3p_end.remove()
        self.xover_3p_end = None
        for insertionItem in self._insertion_items.values():
            insertionItem.remove()
        self._insertion_items = None
        self._click_area = None
        self._high_cap = None
        self._low_cap = None
        self._seq_label = None
        self._model_strand = None
        self._virtual_helix_item = None
        scene.removeItem(self)

    # end def

    def strandUpdateSlot(self, strand):
        """
        Slot for just updating connectivity and color, and endpoint showing
        """
        self._updateAppearance(strand)

    # end def

    def oligoPropertyChangedSlot(self, model_oligo, key, new_value):
        xover_3p_end = self.xover_3p_end
        strand = self._model_strand
        if key == 'is_visible':
            document = self._viewroot.document()
            # print("isselect", self.isSelected(), new_value)
            if new_value:
                self.show()
                xover_3p_end.show()
                xover_3p_end.modelSelect(document)
                self.selectIfRequired(document, (True, True))
            else:
                self._low_cap.hide()
                self._high_cap.hide()
                self._dual_cap.hide()
                self.hide()
                xover_3p_end.hide()
                return
        try:
            self._updateAppearance(strand)
        except:
            print(model_oligo, key, new_value)
            raise
        for insertion in self.insertionItems().values():
            insertion.updateItem()

    # end def

    def oligoSequenceAddedSlot(self, oligo):
        self._updateSequenceText()

    # end def

    def oligoSequenceClearedSlot(self, oligo):
        self._updateSequenceText()

    # end def

    def strandHasNewOligoSlot(self, strand):
        strand = self._model_strand
        self._controller.reconnectOligoSignals()
        self._updateColor(strand)
        if strand.connection3p():
            self.xover_3p_end._updateColor(strand)
        for insertion in self.insertionItems().values():
            insertion.updateItem()

    # end def

    def strandInsertionAddedSlot(self, strand, insertion):
        self.insertionItems()[insertion.idx()] = InsertionItem(
            self._virtual_helix_item, strand, insertion)

    # end def

    def strandInsertionChangedSlot(self, strand, insertion):
        self.insertionItems()[insertion.idx()].updateItem()

    # end def

    def strandInsertionRemovedSlot(self, strand, index):
        instItem = self.insertionItems()[index]
        instItem.remove()
        del self.insertionItems()[index]

    # end def

    def strandModsAddedSlot(self, strand, document, mod_id, idx):
        idx_l, idx_h = strand.idxs()
        color = document.getModProperties(mod_id)['color']
        if idx == idx_h:
            self._high_cap.showMod(mod_id, color)
        else:
            self._low_cap.showMod(mod_id, color)

    # end def

    def strandModsChangedSlot(self, strand, document, mod_id, idx):
        idx_l, idx_h = strand.idxs()
        color = document.getModProperties(mod_id)['color']
        if idx == idx_h:
            self._high_cap.changeMod(mod_id, color)
        else:
            self._low_cap.changeMod(mod_id, color)

    # end def

    def strandModsRemovedSlot(self, strand, document, mod_id, idx):
        idx_l, idx_h = strand.idxs()
        # color = document.getModProperties(mod_id)['color']
        if idx == idx_h:
            self._high_cap.destroyMod()
        else:
            self._low_cap.destroyMod()

    # end def

    def strandSelectedChangedSlot(self, strand, indices):
        self.selectIfRequired(self._viewroot.document(), indices)

    # end def

    ### ACCESSORS ###
    def viewroot(self):
        return self._viewroot

    # end def

    def insertionItems(self):
        return self._insertion_items

    # end def

    def strand(self):
        return self._model_strand

    # end def

    def strandFilter(self):
        return self._strand_filter

    # end def

    def idxs(self):
        return self._model_strand.idxs()

    # end def

    def virtualHelixItem(self):
        return self._virtual_helix_item

    # end def

    def partItem(self):
        return self._virtual_helix_item.partItem()

    # end def

    def idNum(self):
        return self._virtual_helix_item.idNum()

    def window(self):
        return self._viewroot.window()

    ### PUBLIC METHODS FOR DRAWING / LAYOUT ###
    def refreshInsertionItems(self, strand):
        i_items = self.insertionItems()
        i_model = strand.insertionsOnStrand()

        was_in_use = set(i_items)
        in_use = set()
        # add in the ones supposed to be there
        for insertion in i_model:
            idx = insertion.idx()
            in_use.add(idx)
            if idx in i_items:
                pass
            else:
                i_items[insertion.idx()] = \
                    InsertionItem(self._virtual_helix_item, strand, insertion)
        # end for

        # remove all in items
        not_in_use = was_in_use - in_use
        for index in not_in_use:
            i_items[index].remove()
            del i_items[index]
        # end for

    # end def

    def resetStrandItem(self, virtual_helix_item, is_forward):
        self.setParentItem(virtual_helix_item)
        self._virtual_helix_item = virtual_helix_item
        self.resetEndPointItems(is_forward)

    # end def

    def resetEndPointItems(self, is_forward):
        self.is_forward = is_forward
        self._low_cap.resetEndPoint(is_forward)
        self._high_cap.resetEndPoint(is_forward)
        self._dual_cap.resetEndPoint(is_forward)

    # end def

    def updateLine(self, moved_cap):
        """ must be called when parented to a VirtualHelixItem
        """
        # setup
        bw = _BASE_WIDTH
        c_a = self._click_area
        line = self.line()
        # set new line coords
        if moved_cap == self._low_cap:
            p1 = line.p1()
            new_x = self._low_cap.pos().x() + bw
            # new_x = self.mapFromScene(self._low_cap.pos()).x() + bw
            p1.setX(new_x)
            line.setP1(p1)
            temp = c_a.rect()
            temp.setLeft(new_x)
            c_a.setRect(temp)
        else:
            p2 = line.p2()
            new_x = self._high_cap.pos().x()
            # new_x = self.mapFromScene(self._high_cap.pos()).x() + bw
            p2.setX(new_x)
            line.setP2(p2)
            temp = c_a.rect()
            temp.setRight(new_x)
            c_a.setRect(temp)
        self.setLine(line)

    # end def

    ### PRIVATE SUPPORT METHODS ###
    def _updateAppearance(self, strand):
        """
        Prepare Strand for drawing, positions are relative to the VirtualHelixItem:
        1. Show or hide caps depending on L and R connectivity.
        2. Determine line coordinates.
        3. Apply paint styles.
        """
        # 0. Setup
        vhi = self._virtual_helix_item
        bw = _BASE_WIDTH
        half_base_width = bw / 2.0
        low_idx, high_idx = strand.lowIdx(), strand.highIdx()

        l_upper_left_x, l_upper_left_y = vhi.upperLeftCornerOfBase(
            low_idx, strand)
        h_upper_left_x, h_upper_left_y = vhi.upperLeftCornerOfBase(
            high_idx, strand)
        low_cap = self._low_cap
        high_cap = self._high_cap
        dual_cap = self._dual_cap

        # 1. Cap visibilty
        lx = l_upper_left_x + bw  # draw from right edge of base
        low_cap.safeSetPos(l_upper_left_x, l_upper_left_y)
        if strand.connectionLow() is not None:  # has low xover
            # if we are hiding it, we might as well make sure it is reparented to the StrandItem
            low_cap.restoreParent()
            low_cap.hide()
        else:  # has low cap
            if not low_cap.isVisible():
                low_cap.show()

        hx = h_upper_left_x  # draw to edge of base
        high_cap.safeSetPos(h_upper_left_x, h_upper_left_y)
        if strand.connectionHigh() is not None:  # has high xover
            # if we are hiding it, we might as well make sure it is reparented to the StrandItem
            high_cap.restoreParent()
            high_cap.hide()
        else:  # has high cap
            if not high_cap.isVisible():
                high_cap.show()

        # special case: single-base strand with no L or H connections,
        # (unconnected caps were made visible in previous block of code)
        if strand.length() == 1 and (low_cap.isVisible()
                                     and high_cap.isVisible()):
            low_cap.hide()
            high_cap.hide()
            dual_cap.safeSetPos(l_upper_left_x, l_upper_left_y)
            dual_cap.show()
        else:
            dual_cap.hide()

        # 2. Xover drawing
        xo = self.xover_3p_end
        if strand.connection3p():
            xo.update(strand)
            xo.showIt()
        else:
            xo.restoreParent()
            xo.hideIt()

        # 3. Refresh insertionItems if necessary drawing
        self.refreshInsertionItems(strand)

        # 4. Line drawing
        hy = ly = l_upper_left_y + half_base_width
        self.setLine(lx, ly, hx, hy)
        rectf = QRectF(l_upper_left_x + bw, l_upper_left_y,
                       bw * (high_idx - low_idx - 1), bw)
        self._click_area.setRect(rectf)
        self._updateColor(strand)

    # end def

    def _updateColor(self, strand):
        oligo = strand.oligo()
        color = SELECT_COLOR if self.isSelected() else oligo.getColor()
        if oligo.shouldHighlight():
            alpha = 128
            pen_width = styles.PATH_STRAND_HIGHLIGHT_STROKE_WIDTH
        else:
            pen_width = styles.PATH_STRAND_STROKE_WIDTH
            alpha = None
        self.xover_3p_end._updateColor(strand)
        pen = getPenObj(color, pen_width, alpha=alpha, capstyle=Qt.FlatCap)
        brush = getBrushObj(color, alpha=alpha)
        self.setPen(pen)
        self._low_cap.updateHighlight(brush)
        self._high_cap.updateHighlight(brush)
        self._dual_cap.updateHighlight(brush)

    # end def

    def _updateSequenceText(self):
        """
        docstring for _updateSequenceText
        """
        # print("updating sequence")
        bw = _BASE_WIDTH
        seq_lbl = self._seq_label
        strand = self.strand()

        seq_txt = strand.sequence()
        isDrawn3to5 = not self.is_forward
        text_X_centering_offset = styles.SEQUENCETEXTXCENTERINGOFFSET

        if seq_txt == '':
            seq_lbl.hide()
            for i_item in self.insertionItems().values():
                i_item.hideSequence()
            return
        # end if

        strand_seq_list = strand.getSequenceList()
        seq_list = [x[1][0] for x in strand_seq_list]
        insert_seq_list = [(x[0], x[1][1]) for x in strand_seq_list]

        i_items = self.insertionItems()
        for idx, seq_txt in insert_seq_list:
            if seq_txt != '':
                i_items[idx].setSequence(seq_txt)

        if isDrawn3to5:
            seq_list = seq_list[::-1]

        seq_txt = ''.join(seq_list)

        # seq_lbl.setPen(QPen( Qt.NoPen))    # leave the Pen as None for unless required
        seq_lbl.setBrush(getBrushObj(styles.SEQUENCEFONTCOLOR))
        seq_lbl.setFont(styles.SEQUENCEFONT)

        # this will always draw from the 5 Prime end!
        seqX = 2 * text_X_centering_offset + bw * strand.idx5Prime()
        seqY = styles.SEQUENCETEXTYCENTERINGOFFSET

        if isDrawn3to5:
            # offset it towards the bottom
            seqY += bw * .8
            # offset X by the reverse centering offset and the string length
            seqX += text_X_centering_offset * .75
            # rotate the characters upside down this does not affect positioning
            # coordinate system, +Y is still Down, and +X is still Right
            seq_lbl.setRotation(180)
            # draw the text and reverse the string to draw 5 prime to 3 prime
            # seq_txt = seq_txt[::-1]
        # end if
        seq_lbl.setPos(seqX, seqY)
        seq_lbl.setText(seq_txt)
        seq_lbl.show()

    # end def

    ### EVENT HANDLERS ###
    def mousePressEvent(self, event):
        """
        Parses a mousePressEvent to extract strandSet and base index,
        forwarding them to approproate tool method as necessary.
        """
        active_tool_str = self._getActiveTool().methodPrefix()
        self.scene().views()[0].addToPressList(self)
        idx = int(floor((event.pos().x()) / _BASE_WIDTH))
        self._virtual_helix_item.setActive(self.is_forward, idx)
        tool_method_name = active_tool_str + "MousePress"
        if hasattr(self, tool_method_name):
            getattr(self, tool_method_name)(event, idx)
        else:
            event.setAccepted(False)

    # end def

    def mouseMoveEvent(self, event):
        """
        Parses a mouseMoveEvent to extract strandSet and base index,
        forwarding them to approproate tool method as necessary.
        """
        tool_method_name = self._getActiveTool().methodPrefix() + "MouseMove"
        if hasattr(self, tool_method_name):
            idx = int(floor((event.pos().x()) / _BASE_WIDTH))
            getattr(self, tool_method_name)(idx)

    # end def

    def hoverLeaveEvent(self, event):
        self.partItem().updateStatusBar("")
        tool_method_name = self._getActiveTool().methodPrefix() + "HoverLeave"
        if hasattr(self, tool_method_name):
            getattr(self, tool_method_name)(event)

    # end def

    def hoverMoveEvent(self, event):
        """
        Parses a mouseMoveEvent to extract strandSet and base index,
        forwarding them to approproate tool method as necessary.
        """
        vhi_num = self._virtual_helix_item.idNum()
        idx = int(floor((event.pos().x()) / _BASE_WIDTH))
        oligo_length = self._model_strand.oligo().length()
        self.partItem().updateStatusBar("%d[%d]\tlength: %d" %
                                        (vhi_num, idx, oligo_length))
        tool_method_name = self._getActiveTool().methodPrefix() + "HoverMove"
        if hasattr(self, tool_method_name):
            getattr(self, tool_method_name)(event, idx)

    # end def

    def customMouseRelease(self, event):
        """
        Parses a mouseReleaseEvent to extract strandSet and base index,
        forwarding them to approproate tool method as necessary.
        """
        tool_method_name = self._getActiveTool().methodPrefix(
        ) + "MouseRelease"
        if hasattr(self, tool_method_name):
            idx = int(floor((event.pos().x()) / _BASE_WIDTH))
            getattr(self, tool_method_name)(idx)

    # end def

    ### TOOL METHODS ###
    def nickToolMousePress(self, event, idx):
        """Break the strand is possible."""
        m_strand = self._model_strand
        m_strand.split(idx)

    # end def

    def nickToolHoverMove(self, event, idx):
        pass
        # m_strand = self._model_strand
        vhi = self._virtual_helix_item
        nick_tool = self._getActiveTool()
        nick_tool.hoverMove(vhi, event, flag=None)
        # nick_tool.updateHoverRect(vhi, m_strand, idx, show=True)

    # end def

    def nickToolHoverLeave(self, event):
        self._getActiveTool().hoverLeaveEvent(event)

    # end def

    def selectToolMousePress(self, event, idx):
        event.setAccepted(False)
        current_filter_set = self._viewroot.selectionFilterSet()
        if self.strandFilter(
        ) in current_filter_set and self.FILTER_NAME in current_filter_set:
            selection_group = self._viewroot.strandItemSelectionGroup()
            mod = Qt.MetaModifier
            if not (event.modifiers() & mod):
                selection_group.clearSelection(False)
            selection_group.setSelectionLock(selection_group)
            selection_group.pendToAdd(self)
            selection_group.pendToAdd(self._low_cap)
            selection_group.pendToAdd(self._high_cap)
            selection_group.processPendingToAddList()
            event.setAccepted(True)
            return selection_group.mousePressEvent(event)

    # end def

    def eraseToolMousePress(self, event, idx):
        m_strand = self._model_strand
        m_strand.strandSet().removeStrand(m_strand)

    # end def

    def insertionToolMousePress(self, event, idx):
        """Add an insert to the strand if possible."""
        m_strand = self._model_strand
        m_strand.addInsertion(idx, 1)

    # end def

    def paintToolMousePress(self, event, idx):
        """Add an insert to the strand if possible.
        Called by XoverItem.paintToolMousePress with event as None
        """
        m_strand = self._model_strand
        if event is None:
            color = m_strand.color()
        elif event.modifiers() & Qt.ShiftModifier:
            color = self.window().path_color_panel.shiftColorName()
        else:
            color = self.window().path_color_panel.colorName()
        m_strand.oligo().applyColor(color)

    # end def

    def pencilToolHoverMove(self, event, idx):
        """Pencil the strand is possible."""
        m_strand = self._model_strand
        vhi = self._virtual_helix_item
        active_tool = self._getActiveTool()

        if not active_tool.isFloatingXoverBegin():
            temp_xover = active_tool.floatingXover()
            temp_xover.updateFloatingFromStrandItem(vhi, m_strand, idx)

    # end def

    def pencilToolMousePress(self, event, idx):
        """Break the strand is possible."""
        m_strand = self._model_strand
        vhi = self._virtual_helix_item
        # part_item = vhi.partItem()
        active_tool = self._getActiveTool()

        if active_tool.isFloatingXoverBegin():
            # block xovers starting at a 5 prime end
            if m_strand.idx5Prime() == idx:
                return
            else:
                temp_xover = active_tool.floatingXover()
                temp_xover.updateBase(vhi, m_strand, idx)
                active_tool.setFloatingXoverBegin(False)
        else:
            active_tool.setFloatingXoverBegin(True)
            # install Xover
            active_tool.attemptToCreateXover(vhi, m_strand, idx)

    # end def

    def skipToolMousePress(self, event, idx):
        """Add an insert to the strand if possible."""
        m_strand = self._model_strand
        m_strand.addInsertion(idx, -1)

    # end def

    def addSeqToolMousePress(self, event, idx):
        """
        Checks that a scaffold was clicked, and then calls apply sequence
        to the clicked strand via its oligo.
        """
        m_strand = self._model_strand
        current_filter_set = self._viewroot.selectionFilterSet()
        if self.strandFilter(
        ) in current_filter_set and self.FILTER_NAME in current_filter_set:
            olg_len, seq_len = self._getActiveTool().applySequence(
                m_strand.oligo())
            if olg_len:
                msg = "Populated %d of %d scaffold bases." % (min(
                    seq_len, olg_len), olg_len)
                if olg_len > seq_len:
                    d = olg_len - seq_len
                    msg = msg + " Warning: %d bases have no sequence." % d
                elif olg_len < seq_len:
                    d = seq_len - olg_len
                    msg = msg + " Warning: %d sequence bases unused." % d
                self.partItem().updateStatusBar(msg)
        else:
            pass
            # logger.info("The clicked strand %s does not match current selection filter %s. "\
            #             "strandFilter()=%s, FILTER_NAME=%s", m_strand, current_filter_set,
            #             self.strandFilter(), self.FILTER_NAME)

    # end def

    def restoreParent(self, pos=None):
        """
        Required to restore parenting and positioning in the partItem
        """
        # map the position
        # print "restoring parent si"
        self.tempReparent(pos)
        self.setSelectedColor(False)
        self.setSelected(False)

    # end def

    def tempReparent(self, pos=None):
        vh_item = self.virtualHelixItem()
        if pos is None:
            pos = self.scenePos()
        self.setParentItem(vh_item)
        tempP = vh_item.mapFromScene(pos)
        self.setPos(tempP)

    # end def

    def setSelectedColor(self, value):
        if value == True:
            color = getColorObj(SELECT_COLOR)
        else:
            oligo = self._model_strand.oligo()
            color = getColorObj(oligo.getColor())
            if oligo.shouldHighlight():
                color.setAlpha(128)
        pen = self.pen()
        pen.setColor(color)
        self.setPen(pen)

    # end def

    def itemChange(self, change, value):
        # for selection changes test against QGraphicsItem.ItemSelectedChange
        # intercept the change instead of the has changed to enable features.
        if change == QGraphicsItem.ItemSelectedChange and self.scene():
            active_tool = self._getActiveTool()
            if active_tool.methodPrefix() == "selectTool":
                viewroot = self._viewroot
                current_filter_set = viewroot.selectionFilterSet()
                selection_group = viewroot.strandItemSelectionGroup()

                # only add if the selection_group is not locked out
                is_normal_select = selection_group.isNormalSelect()
                if value == True and (self.FILTER_NAME in current_filter_set
                                      or not is_normal_select):
                    if self._strand_filter in current_filter_set:
                        if self.group() != selection_group:
                            self.setSelectedColor(True)
                            # This should always be the case, but...
                            if is_normal_select:
                                selection_group.pendToAdd(self)
                                selection_group.setSelectionLock(
                                    selection_group)
                                selection_group.pendToAdd(self._low_cap)
                                selection_group.pendToAdd(self._high_cap)
                            # this else will capture the error.  Basically, the
                            # strandItem should be member of the group before this
                            # ever gets fired
                            else:
                                selection_group.addToGroup(self)
                        return True
                    else:
                        return False
                # end if
                elif value == True:
                    # Don't select
                    return False
                # end elif
                else:
                    # Deselect
                    # print("Deselecting strand")
                    selection_group.pendToRemove(self)
                    self.setSelectedColor(False)
                    selection_group.pendToRemove(self._low_cap)
                    selection_group.pendToRemove(self._high_cap)
                    return False
                # end else
            # end if
            elif active_tool.methodPrefix() == "paintTool":
                viewroot = self._viewroot
                current_filter_set = viewroot.selectionFilterSet()
                if self._strand_filter in current_filter_set:
                    if not active_tool.isMacrod():
                        active_tool.setMacrod()
                    self.paintToolMousePress(None, None)
            # end elif
            return False
        # end if
        return QGraphicsItem.itemChange(self, change, value)

    # end def

    def selectIfRequired(self, document, indices):
        """
        Select self or xover item as necessary
        """
        strand5p = self._model_strand
        con3p = strand5p.connection3p()
        selection_group = self._viewroot.strandItemSelectionGroup()
        # check this strand's xover
        idx_l, idx_h = indices
        if con3p:
            # perhaps change this to a direct call, but here are seeds of an
            # indirect way of doing selection checks
            if (document.isModelStrandSelected(con3p)
                    and document.isModelStrandSelected(strand5p)):
                val3p = document.getSelectedStrandValue(con3p)
                # print("xover idx", indices)
                test3p = val3p[0] if con3p.isForward() else val3p[1]
                test5p = idx_h if strand5p.isForward() else idx_l
                if test3p and test5p:
                    xoi = self.xover_3p_end
                    # if not xoi.isSelected() or not xoi.group():
                    if not xoi.isSelected() or xoi.group() is None:
                        selection_group.setNormalSelect(False)
                        selection_group.addToGroup(xoi)
                        xoi.modelSelect(document)
                        selection_group.setNormalSelect(True)
            else:
                xoi = self.xover_3p_end
                xoi.restoreParent()
                # # print("should deselect xover")
                # if xoi.isSelected() or xoi.group() is not None:
                #     selection_group.setNormalSelect(False)
                #     selection_group.addToGroup(xoi)
                #     xoi.modelDeselect(document)
                #     selection_group.setNormalSelect(True)
            # end if
        # end if
        # Now check the endpoints

        low_cap = self._low_cap
        if idx_l == True:
            if not low_cap.isSelected() or not low_cap.group():
                selection_group.addToGroup(low_cap)
                low_cap.modelSelect(document)
        else:
            if low_cap.isSelected() or low_cap.group():
                low_cap.restoreParent()
        high_cap = self._high_cap
        if idx_h == True:
            if not high_cap.isSelected() or not high_cap.group():
                selection_group.addToGroup(high_cap)
                high_cap.modelSelect(document)
        else:
            if high_cap.isSelected() or high_cap.group():
                high_cap.restoreParent()

        # now check the strand itself
        if idx_l == True and idx_h == True:
            if not self.isSelected() or not self.group():
                selection_group.setNormalSelect(False)
                selection_group.addToGroup(self)
                self.modelSelect(document)
                selection_group.setNormalSelect(True)
        elif idx_l == False and idx_h == False:
            self.modelDeselect(document)

    # end def

    def modelDeselect(self, document):
        self.restoreParent()
        self._low_cap.modelDeselect(document)
        self._high_cap.modelDeselect(document)

    # end def

    def modelSelect(self, document):
        self.setSelected(True)
        self.setSelectedColor(True)

    # end def

    def paint(self, painter, option, widget):
        painter.setPen(self.pen())
        painter.drawLine(self.line())

    # end def

    def setActiveEndpoint(self, cap_type):
        """ Set the active index in the VirtualHelix
        Args:
            cap_type (str): 'low', 'hi'

        Returns:
            index of cap as a convenience
        """
        idx_l, idx_h = self._model_strand.idxs()
        idx = idx_l if cap_type == 'low' else idx_h
        self._virtual_helix_item.setActive(self.is_forward, idx)
        return idx
Exemple #5
0
    def __init__(self, model_strand, virtual_helix_item, viewroot):
        """The parent should be a VirtualHelixItem."""
        super(StrandItem, self).__init__(virtual_helix_item)
        self._model_strand = model_strand
        self._virtual_helix_item = virtual_helix_item
        self._viewroot = viewroot
        self._selected = model_strand.oligo()._selected
        if self._selected:
            self.strandSelectedSlot()
        else:
            self.strandRemovedFromSelectionSlot()

        self._controller = StrandItemController(self, model_strand)
        is_drawn_5to3 = model_strand.strandSet().isDrawn5to3()

        self._strand_filter = model_strand.strandFilter()

        self._insertion_items = {}
        # end point squares and arrows
        self._low_cap = EndpointItem(self, 'low', is_drawn_5to3)
        self._high_cap = EndpointItem(self, 'high', is_drawn_5to3)
        # tick mark for connection btw two adjacent domains on the same helix
        self._tick = EndpointItem(self,'tick',is_drawn_5to3)
        # self._high_cap = None
        self._dual_cap = EndpointItem(self, 'dual', is_drawn_5to3)

        # orientation
        self._is_drawn_5to3 = is_drawn_5to3
        # self._isOnTop = virtual_helix_item.isStrandOnTop(model_strand)
        # label
        self._is_on_top = self._virtual_helix_item.isStrandOnTop(self._model_strand)
        self._seq_label = QGraphicsSimpleTextItem(self)
        
        self.refreshInsertionItems(model_strand)
        if not getBatch():
            self._updateSequenceText()

        # create a larger click area rect to capture mouse events
        self._click_area = c_a = QGraphicsRectItem(_DEFAULT_RECT, self)
        c_a.mousePressEvent = self.mousePressEvent
        c_a.setPen(_NO_PEN)
        self.setAcceptHoverEvents(True)
        c_a.setAcceptHoverEvents(True)
        c_a.hoverMoveEvent = self.hoverMoveEvent

        self.setZValue(styles.ZSTRANDITEM)

        self._toehold_item_3p = None
        self._toehold_item_5p = None
        self._toehold_cap_3p = None
        self._toehold_cap_5p = None

        if self._is_drawn_5to3:
            self._toehold_cap_3p = EndpointItem(self,'low',not self._is_drawn_5to3)
            self._toehold_cap_5p = EndpointItem(self,'low',self._is_drawn_5to3)
            self.toeholdCapHigh = self.toeholdCap3p
            self.toeholdCapLow = self.toeholdCap5p
        else:
            self._toehold_cap_3p =  EndpointItem(self,'high',not self._is_drawn_5to3)
            self._toehold_cap_5p =  EndpointItem(self,'high',self._is_drawn_5to3)
            self.toeholdCapHigh = self.toeholdCap5p
            self.toeholdCapLow = self.toeholdCap3p

        # xover comming from the 3p end
        self._xover3pEnd = XoverItem(self, virtual_helix_item)
        if not getBatch():
            # initial refresh
            self._updateColor(model_strand)
            self._updateAppearance(model_strand)

        self.setZValue(styles.ZSTRANDITEM)
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.shown_attributes=[]
        self.refresh_toehold()