def redo(self): # Remove the strand strand = self._strand strandset = self._strandset # strandset._removeFromStrandList(strand) doc = strandset._doc doc.removeStrandFromSelection(strand) strandset._strand_list.pop(self._s_set_idx) strand5p = self._old_strand5p strand3p = self._old_strand3p oligo = self._oligo olg5p = self._new_oligo5p olg3p = self._new_oligo3p #oligo.incrementLength(-strand.totalLength()) oligo.removeFromPart() if strand5p is not None: strand5p.setConnection3p(None) if strand3p is not None: strand3p.setConnection5p(None) # Clear connections and update oligos if strand5p is not None: for s5p in oligo.strand5p().generator3pStrand(): Strand.setOligo(s5p, olg5p) olg5p.refreshLength() olg5p.addToPart(strandset.part()) if self._solo: part = strandset.part() vh = strandset.virtualHelix() part.partActiveVirtualHelixChangedSignal.emit(part, vh) #strand5p.strandXover5pChangedSignal.emit(strand5p, strand) strand5p.strandUpdateSignal.emit(strand5p) # end if if strand3p is not None: if not oligo.isLoop(): # apply 2nd oligo copy to all 3' downstream strands for s3p in strand3p.generator3pStrand(): Strand.setOligo(s3p, olg3p) olg3p.addToPart(strandset.part()) if self._solo: part = strandset.part() vh = strandset.virtualHelix() part.partActiveVirtualHelixChangedSignal.emit(part, vh) # strand.strandXover5pChangedSignal.emit(strand, strand3p) strand3p.strandUpdateSignal.emit(strand3p) # end if # Emit a signal to notify on completion strand.strandRemovedSignal.emit(strand) if self.mids[0] is not None: strand.part().removeModStrandInstance(strand, strand.lowIdx(), self.mids[0]) # strand.strandModsRemovedSignal.emit(strand, self.mids[0], strand.lowIdx()) if self.mids[1] is not None: strand.part().removeModStrandInstance(strand, strand.highIdx(), self.mids[1]) # strand.strandModsRemovedSignal.emit(strand, self.mids[1], strand.highIdx()) # for updating the Slice View displayed helices strandset.part().partStrandChangedSignal.emit(strandset.part(), strandset.virtualHelix())
def __init__(self, strandset, base_idx_low, base_idx_high, strandset_idx): super(CreateStrandCommand, self).__init__("create strand") self._strandset = strandset self._s_set_idx = strandset_idx doc = strandset.document() self._strand = Strand(strandset, base_idx_low, base_idx_high) colorList = prefs.STAP_COLORS if strandset.isStaple( ) else prefs.SCAF_COLORS color = random.choice(colorList).name() self._new_oligo = Oligo(None, color) # redo will set part self._new_oligo.setLength(self._strand.totalLength())
def redo(self): visited = {} doc = self._part.document() for vh in self._part.getVirtualHelices(): stap_ss = vh.stapleStrandSet() for strand in stap_ss: visited[strand] = False if self.include_scaffold: scap_ss = vh.scaffoldStrandSet() for strand in scap_ss: visited[strand] = False colors = self.colors for strand in list(visited.keys()): if visited[strand]: continue visited[strand] = True start_oligo = strand.oligo() if colors is not None: if strand.isStaple(): start_oligo.setColor(colors[1]) else: start_oligo.setColor(colors[0]) strand5gen = strand.generator5pStrand() # this gets the oligo and burns a strand in the generator strand5 = next(strand5gen) for strand5 in strand5gen: oligo5 = strand5.oligo() if oligo5 != start_oligo: oligo5.removeFromPart() Strand.setOligo(strand5, start_oligo) # emits strandHasNewOligoSignal visited[strand5] = True # end for start_oligo.setStrand5p(strand5) # is it a loop? if strand.connection3p() == strand5: start_oligo.setLoop(True) else: strand3gen = strand.generator3pStrand() strand3 = next(strand3gen) # burn one for strand3 in strand3gen: oligo3 = strand3.oligo() if oligo3 != start_oligo: oligo3.removeFromPart() Strand.setOligo(strand3, start_oligo) # emits strandHasNewOligoSignal visited[strand3] = True # end for start_oligo.refreshLength() # end for for strand in visited.keys(): strand.strandUpdateSignal.emit(strand)
def redo(self): ss = self._s_set sL = self._strand_low sH = self._strand_high oS = self._old_strand idx = self._s_set_idx olg = self._old_oligo doc = ss.document() l_olg = self._l_oligo h_olg = self._h_oligo was_not_loop = l_olg != h_olg # Remove old Strand from the s_set ss._removeFromStrandList(oS) # Add new strands to the s_set (reusing idx, so order matters) ss._addToStrandList(sH, idx) ss._addToStrandList(sL, idx) # update connectivity of strands sLcL = sL.connectionLow() if sLcL: if (oS.isDrawn5to3() and sLcL.isDrawn5to3()) or \ (not oS.isDrawn5to3() and not sLcL.isDrawn5to3()): sLcL.setConnectionHigh(sL) else: sLcL.setConnectionLow(sL) sHcH = sH.connectionHigh() if sHcH: if (oS.isDrawn5to3() and sHcH.isDrawn5to3()) or \ (not oS.isDrawn5to3() and not sHcH.isDrawn5to3()): sHcH.setConnectionLow(sH) else: sHcH.setConnectionHigh(sH) # Traverse the strands via 3'conns to assign the new oligos for strand in l_olg.strand5p().generator3pStrand(): Strand.setOligo(strand, l_olg) # emits strandHasNewOligoSignal if was_not_loop: # do the second oligo which is different for strand in h_olg.strand5p().generator3pStrand(): # emits strandHasNewOligoSignal Strand.setOligo(strand, h_olg) # Add new oligo and remove old oligos from the part olg.removeFromPart() l_olg.addToPart(sL.part()) if was_not_loop: h_olg.addToPart(sH.part()) # Emit Signals related to destruction and addition oS.strandRemovedSignal.emit(oS) ss.strandsetStrandAddedSignal.emit(ss, sH) ss.strandsetStrandAddedSignal.emit(ss, sL)
def undo(self): ss = self._s_set doc = ss._doc sL = self._strand_low sH = self._strand_high nS = self._new_strand idx = self._s_set_idx olg = self._new_oligo l_olg = self._s_low_oligo h_olg = self._s_high_oligo # Remove the new_strand from the s_set ss._removeFromStrandList(nS) # Add old strands to the s_set (reusing idx, so order matters) ss._addToStrandList(sH, idx) ss._addToStrandList(sL, idx) # update connectivity of strands sLcL = sL.connectionLow() if sLcL: if (sL.isDrawn5to3() and sLcL.isDrawn5to3()) or \ (not sL.isDrawn5to3() and not sLcL.isDrawn5to3()): sLcL.setConnectionHigh(sL) else: sLcL.setConnectionLow(sL) sHcH = sH.connectionHigh() if sHcH: if (sH.isDrawn5to3() and sHcH.isDrawn5to3()) or \ (not sH.isDrawn5to3() and not sHcH.isDrawn5to3()): sHcH.setConnectionLow(sH) else: sHcH.setConnectionHigh(sH) # Traverse the strands via 3'conns to assign the old oligo for strand in l_olg.strand5p().generator3pStrand(): Strand.setOligo(strand, l_olg) # emits strandHasNewOligoSignal for strand in h_olg.strand5p().generator3pStrand(): Strand.setOligo(strand, h_olg) # emits strandHasNewOligoSignal # Remove new oligo and add old oligos olg.removeFromPart() l_olg.addToPart(sL.part()) if h_olg != l_olg: h_olg.addToPart(sH.part()) # Emit Signals related to destruction and addition nS.strandRemovedSignal.emit(nS) ss.strandsetStrandAddedSignal.emit(ss, sL) ss.strandsetStrandAddedSignal.emit(ss, sH) # end def # end class
def undo(self): ss = self._s_set sL = self._strand_low sH = self._strand_high oS = self._old_strand idx = self._s_set_idx olg = self._old_oligo doc = ss.document() l_olg = self._l_oligo h_olg = self._h_oligo was_not_loop = l_olg != h_olg # Remove new strands from the s_set (reusing idx, so order matters) ss._removeFromStrandList(sL) ss._removeFromStrandList(sH) # Add the old strand to the s_set ss._addToStrandList(oS, idx) # update connectivity of strands oScL = oS.connectionLow() if oScL: if (oS.isDrawn5to3() and oScL.isDrawn5to3()) or \ (not oS.isDrawn5to3() and not oScL.isDrawn5to3()): oScL.setConnectionHigh(oS) else: oScL.setConnectionLow(oS) oScH = oS.connectionHigh() if oScH: if (oS.isDrawn5to3() and oScH.isDrawn5to3()) or \ (not oS.isDrawn5to3() and not oScH.isDrawn5to3()): oScH.setConnectionLow(oS) else: oScH.setConnectionHigh(oS) # Traverse the strands via 3'conns to assign the old oligo for strand in olg.strand5p().generator3pStrand(): Strand.setOligo(strand, olg) # Add old oligo and remove new oligos from the part olg.addToPart(ss.part()) l_olg.removeFromPart() if was_not_loop: h_olg.removeFromPart() # Emit Signals related to destruction and addition sL.strandRemovedSignal.emit(sL) sH.strandRemovedSignal.emit(sH) ss.strandsetStrandAddedSignal.emit(ss, oS) # end def # end class
def __init__(self, strandset, base_idx_low, base_idx_high, color, update_segments=True): """ TODO: Now that parts have a UUID this could be instantiated via a document, uuid, id_num, is_fwd, base_idx_low, ... instead of an object to be independent of parts keeping strandsets live """ super(CreateStrandCommand, self).__init__("create strand") self._strandset = strandset doc = strandset.document() self._strand = Strand(strandset, base_idx_low, base_idx_high) self._new_oligo = Oligo(None, color, length=self._strand.totalLength()) # redo will set part self.update_segments = update_segments
def resizeSelection(self, delta, use_undostack=True): """ Moves the selected idxs by delta by first iterating over all strands to calculate new idxs (method will return if snap-to behavior would create illegal state), then applying a resize command to each strand. """ resize_list = [] # calculate new idxs for strandset_dict in self._selection_dict.values(): for strand, selected in strandset_dict.items(): part = strand.virtualHelix().part() idxL, idxH = strand.idxs() newL, newH = strand.idxs() deltaL = deltaH = delta # process xovers to get revised delta if selected[0] and strand.connectionLow(): newL = part.xoverSnapTo(strand, idxL, delta) if newL == None: return deltaH = newL-idxL if selected[1] and strand.connectionHigh(): newH = part.xoverSnapTo(strand, idxH, delta) if newH == None: return deltaL = newH-idxH # process endpoints if selected[0] and not strand.connectionLow(): newL = idxL + deltaL if selected[1] and not strand.connectionHigh(): newH = idxH + deltaH if newL > newH: # check for illegal state return resize_list.append((strand, newL, newH)) # end for # end for # execute the resize commands if use_undostack: self.undoStack().beginMacro("Resize Selection") for strand, idxL, idxH in resize_list: Strand.resize(strand, (idxL, idxH), use_undostack) if use_undostack: self.undoStack().endMacro()
def resizeSelection(self, delta, use_undostack=True): """ Moves the selected idxs by delta by first iterating over all strands to calculate new idxs (method will return if snap-to behavior would create illegal state), then applying a resize command to each strand. """ resize_list = [] # calculate new idxs for strandset_dict in self._selection_dict.values(): for strand, selected in strandset_dict.items(): part = strand.virtualHelix().part() idxL, idxH = strand.idxs() newL, newH = strand.idxs() deltaL = deltaH = delta # process xovers to get revised delta if selected[0] and strand.connectionLow(): newL = part.xoverSnapTo(strand, idxL, delta) if newL == None: return deltaH = newL - idxL if selected[1] and strand.connectionHigh(): newH = part.xoverSnapTo(strand, idxH, delta) if newH == None: return deltaL = newH - idxH # process endpoints if selected[0] and not strand.connectionLow(): newL = idxL + deltaL if selected[1] and not strand.connectionHigh(): newH = idxH + deltaH if newL > newH: # check for illegal state return resize_list.append((strand, newL, newH)) # end for # end for # execute the resize commands if use_undostack: self.undoStack().beginMacro("Resize Selection") for strand, idxL, idxH in resize_list: Strand.resize(strand, (idxL, idxH), use_undostack) if use_undostack: self.undoStack().endMacro()
def sequenceExport(self, output): """ Iterative appending to argument `output` which is a dictionary of lists Args: output (dict): dictionary with keys given in `NucleicAcidPart.getSequences` Returns: dict: output with this oligo's values appended for each key """ part = self.part() vh_num5p = self.strand5p().idNum() strand5p = self.strand5p() idx5p = strand5p.idx5Prime() seq = [] a_seq = [] if self.isCircular(): # print("A loop exists") raise CircularOligoException("Cannot export circular oligo " + self.getName()) for strand in strand5p.generator3pStrand(): seq.append(Strand.sequence(strand, for_export=True)) a_seq.append(Strand.abstractSeq(strand)) if strand.connection3p() is None: # last strand in the oligo vh_num3p = strand.idNum() idx3p = strand.idx3Prime() a_seq = ','.join(a_seq) a_seq = "(%s)" % (a_seq) modseq5p, modseq5p_name = part.getStrandModSequence( strand5p, idx5p, ModType.END_5PRIME) modseq3p, modseq3p_name = part.getStrandModSequence( strand, idx3p, ModType.END_3PRIME) seq = ''.join(seq) seq = modseq5p + seq + modseq3p # output = "%d[%d]\t%d[%d]\t%s\t%s\t%s\t%s\t(%s)\n" % \ # (vh_num5p, idx5p, vh_num3p, idx3p, self.getColor(), # modseq5p_name, seq, modseq3p_name, a_seq) # these are the keys # keys = ['Start','End','Color', 'Mod5', # 'Sequence','Mod3','AbstractSequence'] output['Start'].append("%d[%d]" % (vh_num5p, idx5p)) output['End'].append("%d[%d]" % (vh_num3p, idx3p)) output['Color'].append(self.getColor()) output['Mod5'].append(modseq5p_name) output['Sequence'].append(seq) output['Mod3'].append(modseq3p_name) output['AbstractSequence'].append(a_seq) return output
def sortedSelectedStrands(self, strandset): # outList = self._selection_dict[strandset].keys() # outList.sort(key=Strand.lowIdx) out_list = [x for x in self._selection_dict[strandset].items()] getLowIdx = lambda x: Strand.lowIdx(itemgetter(0)(x)) out_list.sort(key=getLowIdx) return out_list
def redo(self): ss = self._s_set doc = ss._doc sL = self._strand_low sH = self._strand_high nS = self._new_strand idx = self._s_set_idx olg = self._new_oligo l_olg = sL.oligo() h_olg = sH.oligo() # Remove old strands from the s_set (reusing idx, so order matters) ss._removeFromStrandList(sL) ss._removeFromStrandList(sH) # Add the new_strand to the s_set ss._addToStrandList(nS, idx) # update connectivity of strands nScL = nS.connectionLow() if nScL: if (nS.isDrawn5to3() and nScL.isDrawn5to3()) or \ (not nS.isDrawn5to3() and not nScL.isDrawn5to3()): nScL.setConnectionHigh(nS) else: nScL.setConnectionLow(nS) nScH = nS.connectionHigh() if nScH: if (nS.isDrawn5to3() and nScH.isDrawn5to3()) or \ (not nS.isDrawn5to3() and not nScH.isDrawn5to3()): nScH.setConnectionLow(nS) else: nScH.setConnectionHigh(nS) # Traverse the strands via 3'conns to assign the new oligo for strand in olg.strand5p().generator3pStrand(): Strand.setOligo(strand, olg) # emits strandHasNewOligoSignal # Add new oligo and remove old oligos olg.addToPart(ss.part()) l_olg.removeFromPart() if h_olg != l_olg: # check if a loop was created h_olg.removeFromPart() # Emit Signals related to destruction and addition sL.strandRemovedSignal.emit(sL) sH.strandRemovedSignal.emit(sH) ss.strandsetStrandAddedSignal.emit(ss, nS)
def sequenceExport(self, output): """ Iterative appending to argument `output` which is a dictionary of lists Args: output (dict): dictionary with keys given in `NucleicAcidPart.getSequences` Returns: dict: """ part = self.part() vh_num5p = self.strand5p().idNum() strand5p = self.strand5p() idx5p = strand5p.idx5Prime() seq = [] a_seq = [] if self.isLoop(): # print("A loop exists") raise Exception for strand in strand5p.generator3pStrand(): seq.append(Strand.sequence(strand, for_export=True)) a_seq.append(Strand.abstractSeq(strand)) if strand.connection3p() is None: # last strand in the oligo vh_num3p = strand.idNum() idx3p = strand.idx3Prime() a_seq = ','.join(a_seq) a_seq = "(%s)" % (a_seq) modseq5p, modseq5p_name = part.getStrandModSequence(strand5p, idx5p, ModType.END_5PRIME) modseq3p, modseq3p_name = part.getStrandModSequence(strand, idx3p, ModType.END_3PRIME) seq = ''.join(seq) seq = modseq5p + seq + modseq3p # output = "%d[%d]\t%d[%d]\t%s\t%s\t%s\t%s\t(%s)\n" % \ # (vh_num5p, idx5p, vh_num3p, idx3p, self.getColor(), # modseq5p_name, seq, modseq3p_name, a_seq) # these are the keys # keys = ['Start','End','Color', 'Mod5', # 'Sequence','Mod3','AbstractSequence'] output['Start'].append("%d[%d]" % (vh_num5p, idx5p)) output['End'].append("%d[%d]" % (vh_num3p, idx3p)) output['Color'].append(self.getColor()) output['Mod5'].append(modseq5p_name) output['Sequence'].append(seq) output['Mod3'].append(modseq3p_name) output['AbstractSequence'].append(a_seq) return output
def undo(self): part = self._part strand5p = self._strand5p strand5p_idx = self._strand5p_idx strand3p = self._strand3p strand3p_idx = self._strand3p_idx olg5p = strand5p.oligo() new_olg3p = self._new_oligo3p # 0. Deselect the involved strands doc = strand5p.document() doc.removeStrandFromSelection(strand5p) doc.removeStrandFromSelection(strand3p) if self._isLoop: olg5p.setLoop(True) # No need to restore whatever the old Oligo._strand5p was else: # 1. update preserved oligo length olg5p.incrementLength(new_olg3p.length()) # 2. Remove the old oligo and apply the 5' oligo to the 3' strand new_olg3p.removeFromPart() for strand in strand3p.generator3pStrand(): # emits strandHasNewOligoSignal Strand.setOligo(strand, olg5p) # end else # 3. install the Xover strand5p.setConnection3p(strand3p) strand3p.setConnection5p(strand5p) ss5 = strand5p.strandSet() vh5p = ss5.virtualHelix() st5p = ss5.strandType() ss3 = strand3p.strandSet() vh3p = ss3.virtualHelix() st3p = ss3.strandType() part.partActiveVirtualHelixChangedSignal.emit(part, vh5p) # strand5p.strandXover5pChangedSignal.emit(strand5p, strand3p) strand5p.strandUpdateSignal.emit(strand5p) strand3p.strandUpdateSignal.emit(strand3p) # end def # end class
def _strandSplitUpdate(self, new_strand5p: Strand, new_strand3p: Strand, oligo3p: OligoT, old_merged_strand: Strand): """If the oligo is a loop, splitting the strand does nothing. If the oligo isn't a loop, a new oligo must be created and assigned to the new_strand and everything connected to it downstream. """ # if you split it can't be a loop self._is_circular = False if old_merged_strand.oligo().isCircular(): self._strand5p = new_strand3p return else: if old_merged_strand.connection5p() is None: self._strand5p = new_strand5p else: self._strand5p = old_merged_strand.oligo()._strand5p oligo3p._strand5p = new_strand3p
def redo(self): part = self._part strand5p = self._strand5p strand5p_idx = self._strand5p_idx strand3p = self._strand3p strand3p_idx = self._strand3p_idx olg5p = strand5p.oligo() old_olg3p = self._old_oligo3p # 0. Deselect the involved strands doc = strand5p.document() doc.removeStrandFromSelection(strand5p) doc.removeStrandFromSelection(strand3p) if self._update_oligo: # Test for Loopiness if olg5p == strand3p.oligo(): olg5p.setLoop(True) else: # 1. update preserved oligo length olg5p.incrementLength(old_olg3p.length()) # 2. Remove the old oligo and apply the 5' oligo to the 3' strand old_olg3p.removeFromPart() for strand in strand3p.generator3pStrand(): # emits strandHasNewOligoSignal Strand.setOligo(strand, olg5p) # 3. install the Xover strand5p.setConnection3p(strand3p) strand3p.setConnection5p(strand5p) #print('strand5p = %s, connection3p = %s'%(strand5p._name, strand3p._name)) ss5 = strand5p.strandSet() vh5p = ss5.virtualHelix() st5p = ss5.strandType() ss3 = strand3p.strandSet() vh3p = ss3.virtualHelix() st3p = ss3.strandType() part.partActiveVirtualHelixChangedSignal.emit(part, vh5p) # strand5p.strandXover5pChangedSignal.emit(strand5p, strand3p) # if self._update_oligo and not getBatch(): if self._update_oligo: strand5p.strandUpdateSignal.emit(strand5p) strand3p.strandUpdateSignal.emit(strand3p)
def _strandMergeUpdate(self, old_strand_low: Strand, old_strand_high: Strand, new_strand: Strand): """This method sets the isCircular status of the oligo and the oligo's 5' strand. """ # check loop status if old_strand_low.oligo() == old_strand_high.oligo(): self._is_circular = True self._strand5p = new_strand return # leave the _strand5p as is? # end if # Now get correct 5p end to oligo if old_strand_low.isForward(): if old_strand_low.connection5p() is not None: self._strand5p = old_strand_low.oligo()._strand5p else: self._strand5p = new_strand else: if old_strand_high.connection5p() is not None: self._strand5p = old_strand_high.oligo()._strand5p else: self._strand5p = new_strand
def isModelStrandSelected(self, strand: Strand) -> bool: ss = strand.strandSet() if ss in self._selection_dict: if strand in self._selection_dict[ss]: return True else: return False else: return False
def sequence(self): temp = self.strand5p() if not temp: return None if temp.sequence(): return ''.join([Strand.sequence(strand) \ for strand in self.strand5p().generator3pStrand()]) else: return None
def redo(self): part = self._part strand5p = self._strand5p strand5p_idx = self._strand5p_idx strand3p = self._strand3p strand3p_idx = self._strand3p_idx olg5p = strand5p.oligo() old_olg3p = self._old_oligo3p # 0. Deselect the involved strands doc = strand5p.document() doc.removeStrandFromSelection(strand5p) doc.removeStrandFromSelection(strand3p) if self._update_oligo: # Test for Loopiness if olg5p == strand3p.oligo(): olg5p.setLoop(True) else: # 1. update preserved oligo length olg5p.incrementLength(old_olg3p.length()) # 2. Remove the old oligo and apply the 5' oligo to the 3' strand old_olg3p.removeFromPart() for strand in strand3p.generator3pStrand(): # emits strandHasNewOligoSignal Strand.setOligo(strand, olg5p) # 3. install the Xover strand5p.setConnection3p(strand3p) strand3p.setConnection5p(strand5p) ss5 = strand5p.strandSet() vh5p = ss5.virtualHelix() st5p = ss5.strandType() ss3 = strand3p.strandSet() vh3p = ss3.virtualHelix() st3p = ss3.strandType() part.partActiveVirtualHelixChangedSignal.emit(part, vh5p) # strand5p.strandXover5pChangedSignal.emit(strand5p, strand3p) # if self._update_oligo and not getBatch(): if self._update_oligo: strand5p.strandUpdateSignal.emit(strand5p) strand3p.strandUpdateSignal.emit(strand3p)
def __init__(self, strand_low: Strand, strand_high: Strand, priority_strand: Strand): super(MergeCommand, self).__init__("merge strands") # Store strands self._strand_low = strand_low self._strand_high = strand_high self._s_set = s_set = priority_strand.strandSet() # Store oligos self._new_oligo = priority_strand.oligo().shallowCopy() self._s_low_oligo = s_low_olg = strand_low.oligo() self._s_high_oligo = s_high_olg = strand_high.oligo() # self._s_set_idx = low_strandset_idx # update the new oligo length if it's not a loop if s_low_olg != s_high_olg: self._new_oligo._setLength(s_low_olg.length() + s_high_olg.length(), emit_signals=True) # Create the new_strand by copying the priority strand to # preserve its properties new_idxs = strand_low.lowIdx(), strand_high.highIdx() new_strand = strand_low.shallowCopy() new_strand.setIdxs(new_idxs) new_strand.setConnectionHigh(strand_high.connectionHigh()) self._new_strand = new_strand # Update the oligo for things like its 5prime end and isCircular self._new_oligo._strandMergeUpdate(strand_low, strand_high, new_strand) # set the new sequence by concatenating the sequence properly if strand_low._sequence or strand_high._sequence: tL = strand_low.totalLength() tH = strand_high.totalLength() seqL = strand_low._sequence if strand_low._sequence else "".join( [" " for i in range(tL)]) seqH = strand_high._sequence if strand_high._sequence else "".join( [" " for i in range(tH)]) if new_strand.isForward(): new_strand._sequence = seqL + seqH else: new_strand._sequence = seqH + seqL
def __init__(self, part: NucleicAcidPartT, strand5p: Strand, strand3p: Strand): super(RemoveXoverCommand, self).__init__("remove xover") self._part = part self._strand5p = strand5p self._strand5p_idx = strand5p.idx3Prime() self._strand3p = strand3p self._strand3p_idx = strand3p.idx5Prime() n_o3p = self._new_oligo3p = strand3p.oligo().shallowCopy() color_list = pathstyles.STAP_COLORS n_o3p._setColor(random.choice(color_list)) n_o3p._setLength(0, emit_signals=True) for strand in strand3p.generator3pStrand(): n_o3p._incrementLength(strand.totalLength(), emit_signals=True) # end def n_o3p.setStrand5p(strand3p) self._isCircular = strand3p.oligo().isCircular()
def undo(self): part = self._part strand5p = self._strand5p strand5p_idx = self._strand5p_idx strand3p = self._strand3p strand3p_idx = self._strand3p_idx old_olg3p = self._old_oligo3p olg5p = strand5p.oligo() # 0. Deselect the involved strands doc = strand5p.document() doc.removeStrandFromSelection(strand5p) doc.removeStrandFromSelection(strand3p) # 1. uninstall the Xover strand5p.setConnection3p(None) strand3p.setConnection5p(None) if self._update_oligo: # Test Loopiness if old_olg3p.isLoop(): old_olg3p.setLoop(False) else: # 2. restore the modified oligo length olg5p.decrementLength(old_olg3p.length()) # 3. apply the old oligo to strand3p old_olg3p.addToPart(part) for strand in strand3p.generator3pStrand(): # emits strandHasNewOligoSignal Strand.setOligo(strand, old_olg3p) ss5 = strand5p.strandSet() vh5p = ss5.virtualHelix() st5p = ss5.strandType() ss3 = strand3p.strandSet() vh3p = ss3.virtualHelix() st3p = ss3.strandType() part.partActiveVirtualHelixChangedSignal.emit(part, vh5p) # strand5p.strandXover5pChangedSignal.emit(strand5p, strand3p) if self._update_oligo: strand5p.strandUpdateSignal.emit(strand5p) strand3p.strandUpdateSignal.emit(strand3p)
def __init__(self, strandset, base_idx_low, base_idx_high, strandset_idx): super(CreateStrandCommand, self).__init__("create strand") self._strandset = strandset self._s_set_idx = strandset_idx doc = strandset.document() self._strand = Strand(strandset, base_idx_low, base_idx_high) colorList = prefs.STAP_COLORS if strandset.isStaple() else prefs.SCAF_COLORS color = random.choice(colorList).name() self._new_oligo = Oligo(None, color) # redo will set part self._new_oligo.setLength(self._strand.totalLength())
class CreateStrandCommand(UndoCommand): """ Create a new Strand based with bounds (base_idx_low, base_idx_high), and insert it into the strandset at position strandset_idx. Also, create a new Oligo, add it to the Part, and point the new Strand at the oligo. """ def __init__(self, strandset, base_idx_low, base_idx_high, strandset_idx): super(CreateStrandCommand, self).__init__("create strand") self._strandset = strandset self._s_set_idx = strandset_idx doc = strandset.document() self._strand = Strand(strandset, base_idx_low, base_idx_high) colorList = prefs.STAP_COLORS if strandset.isStaple() else prefs.SCAF_COLORS color = random.choice(colorList).name() self._new_oligo = Oligo(None, color) # redo will set part self._new_oligo.setLength(self._strand.totalLength()) # end def def redo(self): # Add the new strand to the StrandSet strand_list strand = self._strand strandset = self._strandset strandset._strand_list.insert(self._s_set_idx, strand) # Set up the new oligo oligo = self._new_oligo oligo.setStrand5p(strand) oligo.addToPart(strandset.part()) strand.setOligo(oligo) if strandset.isStaple(): strand.reapplySequence() # Emit a signal to notify on completion strandset.strandsetStrandAddedSignal.emit(strandset, strand) # for updating the Slice View displayed helices strandset.part().partStrandChangedSignal.emit(strandset.part(), strandset.virtualHelix()) # end def def undo(self): # Remove the strand from StrandSet strand_list and selectionList strand = self._strand strandset = self._strandset strandset._doc.removeStrandFromSelection(strand) strandset._strand_list.pop(self._s_set_idx) # Get rid of the new oligo oligo = self._new_oligo oligo.setStrand5p(None) oligo.removeFromPart() # Emit a signal to notify on completion strand.strandRemovedSignal.emit(strand) strand.setOligo(None) # for updating the Slice View displayed helices strandset.part().partStrandChangedSignal.emit(strandset.part(), strandset.virtualHelix()) # end def # end class
def __init__(self, strand_low: Strand, strand_high: Strand, priority_strand: Strand): super(MergeCommand, self).__init__("merge strands") # Store strands self._strand_low = strand_low self._strand_high = strand_high self._s_set = s_set = priority_strand.strandSet() # Store oligos self._new_oligo = priority_strand.oligo().shallowCopy() self._s_low_oligo = s_low_olg = strand_low.oligo() self._s_high_oligo = s_high_olg = strand_high.oligo() # self._s_set_idx = low_strandset_idx # update the new oligo length if it's not a loop if s_low_olg != s_high_olg: self._new_oligo._setLength(s_low_olg.length() + s_high_olg.length(), emit_signals=True) # Create the new_strand by copying the priority strand to # preserve its properties new_idxs = strand_low.lowIdx(), strand_high.highIdx() new_strand = strand_low.shallowCopy() new_strand.setIdxs(new_idxs) new_strand.setConnectionHigh(strand_high.connectionHigh()) self._new_strand = new_strand # Update the oligo for things like its 5prime end and isCircular self._new_oligo._strandMergeUpdate(strand_low, strand_high, new_strand) # set the new sequence by concatenating the sequence properly if strand_low._sequence or strand_high._sequence: tL = strand_low.totalLength() tH = strand_high.totalLength() seqL = strand_low._sequence if strand_low._sequence else "".join([" " for i in range(tL)]) seqH = strand_high._sequence if strand_high._sequence else "".join([" " for i in range(tH)]) if new_strand.isForward(): new_strand._sequence = seqL + seqH else: new_strand._sequence = seqH + seqL
def getSelectedStrandValue(self, strand: Strand) -> EndsSelected: """Strand is an object to look up it is pre-vetted to be in the dictionary Args: strand: ``Strand`` object in question Returns: Tuple of the end point selection """ return self._selection_dict[strand.strandSet()][strand]
def __init__(self, part: NucleicAcidPartT, strand5p: Strand, strand5p_idx: int, strand3p: Strand, strand3p_idx: int, update_oligo: bool = True): super(CreateXoverCommand, self).__init__("create xover") self._part = part self._strand5p = strand5p self._strand5p_idx = strand5p_idx self._strand3p = strand3p self._strand3p_idx = strand3p_idx self._old_oligo3p = strand3p.oligo() self._update_oligo = update_oligo
def __init__(self, strandset: StrandSetT, strand: Strand, solo: bool = True): super(RemoveStrandCommand, self).__init__("remove strands") self._strandset = strandset self._strand = strand self._solo = solo self._old_strand5p = strand.connection5p() self._old_strand3p = strand.connection3p() self._oligo = olg = strand.oligo() part = strand.part() # idxs = strand.idxs() self.mids = (part.getModID(strand, strand.lowIdx()), part.getModID(strand, strand.highIdx())) # only create a new 5p oligo if there is a 3' connection self._new_oligo5p = olg.shallowCopy() if self._old_strand5p else None if olg.isCircular() or self._old_strand3p is None: self._new_oligo3p = olg3p = None if self._new_oligo5p: self._new_oligo5p._setLoop(False) else: self._new_oligo3p = olg3p = olg.shallowCopy() olg3p.setStrand5p(self._old_strand3p) # color_list = styles.STAP_COLORS if strandset.isStaple() else prefs.SCAF_COLORS color_list = pathstyles.STAP_COLORS color = random.choice(color_list) olg3p._setColor(color) olg3p.refreshLength(emit_signals=True)
def getOverlappingStrands(self, idxLow, idxHigh): dummy_strand = Strand(self, idxLow, idxHigh) strand_list = [s for s in self._findOverlappingRanges(dummy_strand)] dummy_strand._strandset = None dummy_strand.setParent(None) dummy_strand.deleteLater() dummy_strand = None return strand_list
def sortedSelectedStrands(self, strandset): """ Get a list sorted from low to high index of `Strands` in a `StrandSet` that are selected Args: strandset (StrandSet): Returns: list: of :obj: `Strand` """ out_list = [x for x in self._selection_dict[strandset].items()] getLowIdx = lambda x: Strand.lowIdx(itemgetter(0)(x)) out_list.sort(key=getLowIdx) return out_list
def sequence(self): """Get the sequence applied to this `Oligo` Returns: str or None """ temp = self.strand5p() if not temp: return None if temp.sequence(): return ''.join([Strand.sequence(strand) for strand in self.strand5p().generator3pStrand()]) else: return None
def getStrand(self, base_idx): """Returns the strand that overlaps with base_idx.""" dummy_strand = Strand(self, base_idx, base_idx) strand_list = [s for s in self._findOverlappingRanges(dummy_strand)] dummy_strand._strandset = None dummy_strand.setParent(None) dummy_strand.deleteLater() dummy_strand = None return strand_list[0] if len(strand_list) > 0 else None
def hasStrandAt(self, idxLow, idxHigh): """ """ dummy_strand = Strand(self, idxLow, idxHigh) strand_list = [s for s in self._findOverlappingRanges(dummy_strand)] dummy_strand._strandset = None dummy_strand.setParent(None) dummy_strand.deleteLater() dummy_strand = None return len(strand_list) > 0
def addStrandToSelection(self, strand: Strand, value: EndsSelected): """ Add `Strand` object to Document selection Args: strand: value: of the form:: (is low index selected, is high index selected) """ ss = strand.strandSet() if ss in self._selection_dict: self._selection_dict[ss][strand] = value else: self._selection_dict[ss] = {strand: value} self._strand_selected_changed_dict[strand] = value
def sequenceExport(self): part = self.part() vh_num5p = self.strand5p().virtualHelix().number() strand5p = self.strand5p() idx5p = strand5p.idx5Prime() seq = '' if self.isLoop(): # print("A loop exists") raise Exception for strand in strand5p.generator3pStrand(): seq = seq + Strand.sequence(strand, for_export=True) if strand.connection3p() == None: # last strand in the oligo vhNum3p = strand.virtualHelix().number() idx3p = strand.idx3Prime() modseq5p, modseq5p_name = part.getModSequence(strand5p, idx5p, 0) modseq3p, modseq3p_name = part.getModSequence(strand, idx3p, 1) seq = modseq5p + seq + modseq3p output = "%d[%d],%d[%d],%s,%s,%s,%s,%s\n" % \ (vhNum5p, idx5p, vhNum3p, idx3p, seq, len(seq), self._color, modseq5p_name, modseq3p_name) return output
def removeStrandFromSelection(self, strand: Strand) -> bool: """Remove ``Strand`` object from Document selection Args: strand: Returns: ``True`` if successful, ``False`` otherwise """ ss = strand.strandSet() if ss in self._selection_dict: temp = self._selection_dict[ss] if strand in temp: del temp[strand] if len(temp) == 0: del self._selection_dict[ss] self._strand_selected_changed_dict[strand] = (False, False) return True else: return False else: return False
def getLowIdx(x): return Strand.lowIdx(itemgetter(0)(x))
def resizeSelection(self, delta: int, use_undostack: bool = True): """Moves the selected idxs by delta by first iterating over all strands to calculate new idxs (method will return if snap-to behavior would create illegal state), then applying a resize command to each strand. Args: delta: use_undostack: optional, default is ``True`` """ resize_list = [] vh_set = set() # calculate new idxs part = None for strandset_dict in self._selection_dict.values(): for strand, selected in strandset_dict.items(): if part is None: part = strand.part() idx_low, idx_high = strand.idxs() new_low, new_high = strand.idxs() delta_low = delta_high = delta # process xovers to get revised delta if selected[0] and strand.connectionLow(): new_low = part.xoverSnapTo(strand, idx_low, delta) if new_low is None: return delta_high = new_low - idx_low if selected[1] and strand.connectionHigh(): new_high = part.xoverSnapTo(strand, idx_high, delta) if new_high is None: return delta_low = new_high - idx_high # process endpoints if selected[0] and not strand.connectionLow(): new_low = idx_low + delta_low if selected[1] and not strand.connectionHigh(): new_high = idx_high + delta_high if new_low > new_high: # check for illegal state return vh_set.add(strand.idNum()) resize_list.append((strand, new_low, new_high)) # end for # end for # execute the resize commands us = self.undoStack() if use_undostack: us.beginMacro("Resize Selection") for strand, idx_low, idx_high in resize_list: Strand.resize(strand, (idx_low, idx_high), use_undostack, update_segments=False) if resize_list: cmd = RefreshSegmentsCommand(part, vh_set) if use_undostack: us.push(cmd) else: cmd.redo() if use_undostack: us.endMacro()
class CreateStrandCommand(UndoCommand): """Create a new `Strand` based with bounds (base_idx_low, base_idx_high), and insert it into the strandset at position strandset_idx. Also, create a new Oligo, add it to the Part, and point the new Strand at the oligo. `Strand` explicitly do not create `Oligo` as do to the 1 to many nature of Oligos """ def __init__(self, strandset, base_idx_low, base_idx_high, color, update_segments=True): """ TODO: Now that parts have a UUID this could be instantiated via a document, uuid, id_num, is_fwd, base_idx_low, ... instead of an object to be independent of parts keeping strandsets live """ super(CreateStrandCommand, self).__init__("create strand") self._strandset = strandset strandset.document() self._strand = Strand(strandset, base_idx_low, base_idx_high) self._new_oligo = Oligo( None, color, length=self._strand.totalLength()) # redo will set part self.update_segments = update_segments # end def def strand(self): return self._strand # end def def redo(self): # Add the new strand to the StrandSet strand_list strand = self._strand strandset = self._strandset strandset._addToStrandList(strand, self.update_segments) # Set up the new oligo oligo = self._new_oligo oligo.setStrand5p(strand) strand.setOligo(oligo) oligo.addToPart(strandset.part(), emit_signals=True) # strand.reapplySequence() # Emit a signal to notify on completion strandset.strandsetStrandAddedSignal.emit(strandset, strand) # for updating the Slice View displayed helices strandset.part().partStrandChangedSignal.emit(strandset.part(), strandset.idNum()) # end def def undo(self): # Remove the strand from StrandSet strand_list and selectionList strand = self._strand strandset = self._strandset strandset._document.removeStrandFromSelection(strand) strandset._removeFromStrandList(strand, self.update_segments) # Get rid of the new oligo oligo = self._new_oligo oligo.setStrand5p(None) oligo.removeFromPart(emit_signals=True) # Emit a signal to notify on completion strand.strandRemovedSignal.emit(strand) strand.setOligo(None) # for updating the Slice View displayed helices strandset.part().partStrandChangedSignal.emit(strandset.part(), strandset.idNum())