def __init__(self, part, row, col, idnum=0): self._doc = part.document() super(VirtualHelix, self).__init__(part) self._coord = (row, col) # col, row self._part = part self._scaf_strandset = StrandSet(StrandType.SCAFFOLD, self) self._stap_strandset = StrandSet(StrandType.STAPLE, self) # If self._part exists, it owns self._number # in that only it may modify it through the # private interface. The public interface for # setNumber just routes the call to the parent # dnapart if one is present. If self._part == None # the virtualhelix owns self._number and may modify it. self._number = None self.setNumber(idnum)
class VirtualHelix(ProxyObject): """ VirtualHelix is a container class for two StrandSet objects (one scaffold and one staple). The Strands all share the same helix axis. It is called "virtual" because many different Strands (i.e. sub-oligos) combine to form the "helix", just as many fibers may be braided together to form a length of rope. """ def __init__(self, part, row, col, idnum=0): self._doc = part.document() super(VirtualHelix, self).__init__(part) self._coord = (row, col) # col, row self._part = part self._scaf_strandset = StrandSet(StrandType.SCAFFOLD, self) self._stap_strandset = StrandSet(StrandType.STAPLE, self) # If self._part exists, it owns self._number # in that only it may modify it through the # private interface. The public interface for # setNumber just routes the call to the parent # dnapart if one is present. If self._part == None # the virtualhelix owns self._number and may modify it. self._number = None self.setNumber(idnum) # end def def __repr__(self): return "<%s(%d)>" % (self.__class__.__name__, self._number) ### SIGNALS ### virtualHelixRemovedSignal = ProxySignal(ProxyObject, name='virtualHelixRemovedSignal') #pyqtSignal(QObject) # self virtualHelixNumberChangedSignal = ProxySignal(ProxyObject, int, name='virtualHelixNumberChangedSignal') #pyqtSignal(QObject, int) # self, num ### SLOTS ### ### ACCESSORS ### def scaf(self, idx): """ Returns the strand at idx in self's scaffold, if any """ return self._scaf_strandset.getStrand(idx) def stap(self, idx): """ Returns the strand at idx in self's scaffold, if any """ return self._stap_strandset.getStrand(idx) def coord(self): return self._coord # end def def number(self): return self._number # end def def part(self): return self._part # end def def document(self): return self._doc # end def def setNumber(self, number): if self._number != number: num_to_vh_dict = self._part._number_to_virtual_helix # if self._number is not None: num_to_vh_dict[self._number] = None self._number = number self.virtualHelixNumberChangedSignal.emit(self, number) num_to_vh_dict[number] = self # end def def setPart(self, new_part): self._part = new_part self.setParent(new_part) # end def def scaffoldStrandSet(self): return self._scaf_strandset # end def def stapleStrandSet(self): return self._stap_strandset # end def def undoStack(self): return self._part.undoStack() # end def ### METHODS FOR QUERYING THE MODEL ### def scaffoldIsOnTop(self): return self.isEvenParity() def getStrandSetByIdx(self, idx): """ This is a path-view-specific accessor idx == 0 means top strand idx == 1 means bottom strand """ if idx == 0: if self.isEvenParity(): return self._scaf_strandset else: return self._stap_strandset else: if self.isEvenParity(): return self._stap_strandset else: return self._scaf_strandset # end def def getStrandSetByType(self, strand_type): if strand_type == StrandType.SCAFFOLD: return self._scaf_strandset else: return self._stap_strandset # end def def getStrandSets(self): """Return a tuple of the scaffold and staple StrandSets.""" return self._scaf_strandset, self._stap_strandset # end def def hasStrandAtIdx(self, idx): """Return a tuple for (Scaffold, Staple). True if a strand is present at idx, False otherwise.""" return (self._scaf_strandset.hasStrandAt(idx, idx),\ self._stap_strandset.hasStrandAt(idx, idx)) # end def def indexOfRightmostNonemptyBase(self): """Returns the rightmost nonempty base in either scaf of stap.""" return max(self._scaf_strandset.indexOfRightmostNonemptyBase(),\ self._stap_strandset.indexOfRightmostNonemptyBase()) # end def def isDrawn5to3(self, strandset): is_scaf = strandset == self._scaf_strandset is_even = self.isEvenParity() return is_even == is_scaf # end def def isEvenParity(self): return self._part.isEvenParity(*self._coord) # end def def strandSetBounds(self, idx_helix, idx_type): """ forwards the query to the strandset """ return self.strandSet(idx_helix, idx_type).bounds() # end def ### METHODS FOR EDITING THE MODEL ### def destroy(self): # QObject also emits a destroyed() Signal self.setParent(None) self.deleteLater() # end def def remove(self, use_undostack=True): """ Removes a VirtualHelix from the model. Accepts a reference to the VirtualHelix, or a (row,col) lattice coordinate to perform a lookup. """ if use_undostack: self.undoStack().beginMacro("Delete VirtualHelix") self._scaf_strandset.remove(use_undostack) self._stap_strandset.remove(use_undostack) c = RemoveVirtualHelixCommand(self.part(), self) if use_undostack: self.undoStack().push(c) self.undoStack().endMacro() else: c.redo() # end def ### PUBLIC SUPPORT METHODS ### def deepCopy(self, part): """ This only copies as deep as the VirtualHelix strands get copied at the oligo and added to the Virtual Helix """ vh = VirtualHelix(part, self._number) vh._coords = (self._coord[0], self._coord[1]) # If self._part exists, it owns self._number # in that only it may modify it through the # private interface. The public interface for # setNumber just routes the call to the parent # dnapart if one is present. If self._part == None # the virtualhelix owns self._number and may modify it. self._number = idnum # end def def getLegacyStrandSetArray(self, strand_type): """Called by legacyencoder.""" if strand_type == StrandType.SCAFFOLD: return self._scaf_strandset.getLegacyArray() else: return self._stap_strandset.getLegacyArray() def shallowCopy(self): pass # end def # def translateCoords(self, deltaCoords): # """ # for expanding a helix # """ # deltaRow, deltaCol = deltaCoords # row, col = self._coord # self._coord = row + deltaRow, col + deltaCol # # end def # end class
class VirtualHelix(ProxyObject): """ VirtualHelix is a container class for two StrandSet objects (one scaffold and one staple). The Strands all share the same helix axis. It is called "virtual" because many different Strands (i.e. sub-oligos) combine to form the "helix", just as many fibers may be braided together to form a length of rope. """ def __init__(self, part, row, col, idnum=0): self._doc = part.document() super(VirtualHelix, self).__init__(part) self._coord = (row, col) # col, row self._part = part self._scaf_strandset = StrandSet(StrandType.SCAFFOLD, self) self._stap_strandset = StrandSet(StrandType.STAPLE, self) # If self._part exists, it owns self._number # in that only it may modify it through the # private interface. The public interface for # setNumber just routes the call to the parent # dnapart if one is present. If self._part == None # the virtualhelix owns self._number and may modify it. self._number = None self.setNumber(idnum) # end def def __repr__(self): return "<%s(%d)>" % (self.__class__.__name__, self._number) ### SIGNALS ### virtualHelixRemovedSignal = ProxySignal( ProxyObject, name='virtualHelixRemovedSignal') #pyqtSignal(QObject) # self virtualHelixNumberChangedSignal = ProxySignal( ProxyObject, int, name='virtualHelixNumberChangedSignal' ) #pyqtSignal(QObject, int) # self, num ### SLOTS ### ### ACCESSORS ### def scaf(self, idx): """ Returns the strand at idx in self's scaffold, if any """ return self._scaf_strandset.getStrand(idx) def stap(self, idx): """ Returns the strand at idx in self's scaffold, if any """ return self._stap_strandset.getStrand(idx) def coord(self): return self._coord # end def def number(self): return self._number # end def def part(self): return self._part # end def def document(self): return self._doc # end def def setNumber(self, number): if self._number != number: num_to_vh_dict = self._part._number_to_virtual_helix # if self._number is not None: num_to_vh_dict[self._number] = None self._number = number self.virtualHelixNumberChangedSignal.emit(self, number) num_to_vh_dict[number] = self # end def def setPart(self, new_part): self._part = new_part self.setParent(new_part) # end def def scaffoldStrandSet(self): return self._scaf_strandset # end def def stapleStrandSet(self): return self._stap_strandset # end def def undoStack(self): return self._part.undoStack() # end def ### METHODS FOR QUERYING THE MODEL ### def scaffoldIsOnTop(self): return self.isEvenParity() def getStrandSetByIdx(self, idx): """ This is a path-view-specific accessor idx == 0 means top strand idx == 1 means bottom strand """ if idx == 0: if self.isEvenParity(): return self._scaf_strandset else: return self._stap_strandset else: if self.isEvenParity(): return self._stap_strandset else: return self._scaf_strandset # end def def getStrandSetByType(self, strand_type): if strand_type == StrandType.SCAFFOLD: return self._scaf_strandset else: return self._stap_strandset # end def def getStrandSets(self): """Return a tuple of the scaffold and staple StrandSets.""" return self._scaf_strandset, self._stap_strandset # end def def hasStrandAtIdx(self, idx): """Return a tuple for (Scaffold, Staple). True if a strand is present at idx, False otherwise.""" return (self._scaf_strandset.hasStrandAt(idx, idx),\ self._stap_strandset.hasStrandAt(idx, idx)) # end def def indexOfRightmostNonemptyBase(self): """Returns the rightmost nonempty base in either scaf of stap.""" return max(self._scaf_strandset.indexOfRightmostNonemptyBase(),\ self._stap_strandset.indexOfRightmostNonemptyBase()) # end def def isDrawn5to3(self, strandset): is_scaf = strandset == self._scaf_strandset is_even = self.isEvenParity() return is_even == is_scaf # end def def isEvenParity(self): return self._part.isEvenParity(*self._coord) # end def def strandSetBounds(self, idx_helix, idx_type): """ forwards the query to the strandset """ return self.strandSet(idx_helix, idx_type).bounds() # end def ### METHODS FOR EDITING THE MODEL ### def destroy(self): # QObject also emits a destroyed() Signal self.setParent(None) self.deleteLater() # end def def remove(self, use_undostack=True): """ Removes a VirtualHelix from the model. Accepts a reference to the VirtualHelix, or a (row,col) lattice coordinate to perform a lookup. """ if use_undostack: self.undoStack().beginMacro("Delete VirtualHelix") self._scaf_strandset.remove(use_undostack) self._stap_strandset.remove(use_undostack) c = RemoveVirtualHelixCommand(self.part(), self) if use_undostack: self.undoStack().push(c) self.undoStack().endMacro() else: c.redo() # end def ### PUBLIC SUPPORT METHODS ### def deepCopy(self, part): """ This only copies as deep as the VirtualHelix strands get copied at the oligo and added to the Virtual Helix """ vh = VirtualHelix(part, self._number) vh._coords = (self._coord[0], self._coord[1]) # If self._part exists, it owns self._number # in that only it may modify it through the # private interface. The public interface for # setNumber just routes the call to the parent # dnapart if one is present. If self._part == None # the virtualhelix owns self._number and may modify it. self._number = idnum # end def def getLegacyStrandSetArray(self, strand_type): """Called by legacyencoder.""" if strand_type == StrandType.SCAFFOLD: return self._scaf_strandset.getLegacyArray() else: return self._stap_strandset.getLegacyArray() def shallowCopy(self): pass # end def # def translateCoords(self, deltaCoords): # """ # for expanding a helix # """ # deltaRow, deltaCol = deltaCoords # row, col = self._coord # self._coord = row + deltaRow, col + deltaCol # # end def # end class
def determineStrandSetBounds(self, selected_strand_list: List[Tuple[ Strand, EndsSelected]], strandset: StrandSet) -> Tuple[int, int]: """Determine the bounds of a :class:`StrandSet` ``strandset`` among a a list of selected strands in that same ``strandset`` Args: selected_strand_list: list of ``( Strands, (is_low, is_high) )`` items strandset: of interest Returns: tuple: min low bound and min high bound index """ length = strandset.length() min_high_delta = min_low_delta = max_ss_idx = length - 1 # init the return values ss_dict = self._selection_dict[strandset] for strand, value in selected_strand_list: idx_low, idx_high = strand.idxs() low_neighbor, high_neighbor = strandset.getNeighbors(strand) # print(low_neighbor, high_neighbor) if value[0]: # the end is selected if low_neighbor is None: temp = idx_low - 0 else: if low_neighbor in ss_dict: value_N = ss_dict[low_neighbor] # we only care if the low neighbor is not selected temp = min_low_delta if value_N[ 1] else idx_low - low_neighbor.highIdx() - 1 # end if else: # not selected temp = idx_low - low_neighbor.highIdx() - 1 # end else if temp < min_low_delta: min_low_delta = temp # end if # check the other end of the strand if not value[1]: temp = idx_high - idx_low - 1 if temp < min_high_delta: min_high_delta = temp # end if if value[1]: if high_neighbor is None: temp = max_ss_idx - idx_high else: if high_neighbor in ss_dict: value_N = ss_dict[high_neighbor] # we only care if the low neighbor is not selected temp = min_high_delta if value_N[ 0] else high_neighbor.lowIdx() - idx_high - 1 # end if else: # not selected temp = high_neighbor.lowIdx() - idx_high - 1 # end else # end else if temp < min_high_delta: min_high_delta = temp # end if # check the other end of the strand if not value[0]: temp = idx_high - idx_low - 1 if temp < min_low_delta: min_low_delta = temp # end if # end for return (min_low_delta, min_high_delta)
#Determine position for oligo strands (first oligo: from 1 --> length/2 second oligo: next (length/2)+1 --> length ) oligo_pos.append([1,int(int(i[2])/2),int(int(i[2])/2)+1,int(i[2])]) # Max length of the Strand Set MAX_LENGTH = 64 offset_idx = 0 # Make the container for the StrandSets ss_fwd = [None]*vhCounter ss_rev = [None]*vhCounter # Create the StrandsSets and fill them with Strands # The assumption here is that Scaffold is forward strand and that the reverse strands is Staple Strands try: for i in range(vhCounter): ss_fwd[i] = StrandSet(is_fwd=True, id_num=i, part=part, initial_size=MAX_LENGTH) ss_rev[i] = StrandSet(is_fwd=False, id_num=i, part=part, initial_size=MAX_LENGTH) ss_fwd[i].createStrand(base_idx_low=offset_idx, base_idx_high=offset_idx+oligo_pos[i][3], color='#0066cc') ss_rev[i].createStrand(base_idx_low=offset_idx, base_idx_high=offset_idx+oligo_pos[i][1], color='#f44242') ss_rev[i].createStrand(base_idx_low=offset_idx+oligo_pos[i][2], base_idx_high=offset_idx+oligo_pos[i][3], color='#007200') part.fwd_strandsets[i] = ss_fwd[i] part.rev_strandsets[i] = ss_rev[i] except Exception: print('Failed to create StrandSet/Strands') """ print('\t', fwd_ss, '\t', [s.idxs() for s in fwd_ss.strands()], '\n\t\t\t\t', [s.getColor() for s in fwd_ss.strands()]) print('\t', rev_ss, '\t', [s.idxs() for s in rev_ss.strands()], '\n\t\t\t\t', [s.getColor() for s in rev_ss.strands()])
def determineStrandSetBounds(self, selected_strand_list: List[Tuple[Strand, EndsSelected]], strandset: StrandSet) -> Tuple[int, int]: """Determine the bounds of a :class:`StrandSet` ``strandset`` among a a list of selected strands in that same ``strandset`` Args: selected_strand_list: list of ``( Strands, (is_low, is_high) )`` items strandset: of interest Returns: tuple: min low bound and min high bound index """ length = strandset.length() min_high_delta = min_low_delta = max_ss_idx = length - 1 # init the return values ss_dict = self._selection_dict[strandset] for strand, value in selected_strand_list: idx_low, idx_high = strand.idxs() low_neighbor, high_neighbor = strandset.getNeighbors(strand) # print(low_neighbor, high_neighbor) if value[0]: # the end is selected if low_neighbor is None: temp = idx_low - 0 else: if low_neighbor in ss_dict: value_N = ss_dict[low_neighbor] # we only care if the low neighbor is not selected temp = min_low_delta if value_N[1] else idx_low - low_neighbor.highIdx() - 1 # end if else: # not selected temp = idx_low - low_neighbor.highIdx() - 1 # end else if temp < min_low_delta: min_low_delta = temp # end if # check the other end of the strand if not value[1]: temp = idx_high - idx_low - 1 if temp < min_high_delta: min_high_delta = temp # end if if value[1]: if high_neighbor is None: temp = max_ss_idx - idx_high else: if high_neighbor in ss_dict: value_N = ss_dict[high_neighbor] # we only care if the low neighbor is not selected temp = min_high_delta if value_N[0] else high_neighbor.lowIdx() - idx_high - 1 # end if else: # not selected temp = high_neighbor.lowIdx() - idx_high - 1 # end else # end else if temp < min_high_delta: min_high_delta = temp # end if # check the other end of the strand if not value[0]: temp = idx_high - idx_low - 1 if temp < min_low_delta: min_low_delta = temp # end if # end for return (min_low_delta, min_high_delta)