def newDocument(self, fname=None): """Creates a new Document, reusing the DocumentController. Tells all of the views to reset and removes all items from them """ if fname is not None and self.fileName() == fname: setReopen(True) self._document.makeNew() self._has_no_associated_file = fname is None self.win.setWindowTitle(self.documentTitle() + '[*]')
def newDocument(self, doc=None, fname=None): """Creates a new Document, reusing the DocumentController.""" if fname is not None and self._filename == fname: setReopen(True) self._document.resetViews() setBatch(True) self._document.removeAllParts() # clear out old parts setBatch(False) self._document.undoStack().clear() # reset undostack self._filename = fname if fname else "untitled.json" self._has_no_associated_file = fname == None self._active_part = None self.win.setWindowTitle(self.documentTitle() + '[*]')
def decode(document: DocT, obj: dict, emit_signals: bool = True): """Parses a dictionary (obj) created from reading a json file and uses it to populate the given document with model data. Args: document: obj: emit_signals: whether to signal views Raises: IOError, AssertionError, TypeError """ num_bases = len(obj['vstrands'][0]['scaf']) if num_bases % 32 == 0: lattice_type = LatticeEnum.SQUARE grid_type = GridEnum.SQUARE elif num_bases % 21 == 0: lattice_type = LatticeEnum.HONEYCOMB grid_type = GridEnum.HONEYCOMB else: raise IOError("error decoding number of bases") part = None # DETERMINE MAX ROW,COL max_row_json = max_col_json = 0 for helix in obj['vstrands']: max_row_json = max(max_row_json, int(helix['row'])+1) max_col_json = max(max_col_json, int(helix['col'])+1) # CREATE PART ACCORDING TO LATTICE TYPE if lattice_type == LatticeEnum.HONEYCOMB: doLattice = HoneycombDnaPart.legacyLatticeCoordToPositionXY isEven = HoneycombDnaPart.isEvenParity elif lattice_type == LatticeEnum.SQUARE: doLattice = SquareDnaPart.legacyLatticeCoordToPositionXY isEven = SquareDnaPart.isEvenParity else: raise TypeError("Lattice type not recognized") part = document.createNucleicAcidPart(grid_type=grid_type, use_undostack=False) part.setActive(True) document.setGridType(grid_type) document.setSliceOrGridViewVisible(OrthoViewEnum.SLICE) setBatch(True) # POPULATE VIRTUAL HELICES ordered_id_list = [] vh_num_to_coord = {} min_row, max_row = 10000, -10000 min_col, max_col = 10000, -10000 # find row, column limits for helix in obj['vstrands']: row = helix['row'] if row < min_row: min_row = row if row > max_row: max_row = row col = helix['col'] if col < min_col: min_col = col if col > max_col: max_col = col # end for delta_row = (max_row + min_row) // 2 # # 2 LINES COMMENTED OUT BY NC, doesn't appear to be necessary for honeycomb # if delta_row & 0: if delta_row & 1: delta_row += 1 delta_column = (max_col + min_col) // 2 # if delta_column & 0: if delta_column & 1: delta_column += 1 print("Found cadnano version 2 file") # print("\trows(%d, %d): avg: %d" % (min_row, max_row, delta_row)) # print("\tcolumns(%d, %d): avg: %d" % (min_col, max_col, delta_column)) for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf = helix['scaf'] # align row and columns to the center 0, 0 if HoneycombDnaPart.isEvenParity(row, col): coord = (row - delta_row, col - delta_column) else: coord = (row - delta_row, col - delta_column) vh_num_to_coord[vh_num] = coord ordered_id_list.append(vh_num) # end for # make sure we retain the original order radius = DEFAULT_RADIUS for vh_num in sorted(vh_num_to_coord.keys()): row, col = vh_num_to_coord[vh_num] x, y = doLattice(radius, row, col) part.createVirtualHelix(x, y, 0., num_bases, id_num=vh_num, use_undostack=False) # zoom to fit if emit_signals: part.partZDimensionsChangedSignal.emit(part, *part.zBoundsIds(), True) if not getReopen(): setBatch(False) part.setImportedVHelixOrder(ordered_id_list) setReopen(False) setBatch(False) # INSTALL STRANDS AND COLLECT XOVER LOCATIONS scaf_seg = defaultdict(list) scaf_xo = defaultdict(list) stap_seg = defaultdict(list) stap_xo = defaultdict(list) try: for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] if isEven(row, col): scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num) else: stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num) # validate file serialization of lists assert(len(scaf) == len(stap) and len(scaf) == len(insertions) and len(insertions) == len(skips)) # read scaffold segments and xovers for i in range(len(scaf)): five_vh, five_idx, three_vh, three_idx = scaf[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandEnum.SCAFFOLD, vh_num, i, five_vh, five_idx, three_vh, three_idx): scaf_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case scaf_seg[vh_num].append(i) # end segment on a double crossover if is3primeXover(StrandEnum.SCAFFOLD, vh_num, i, three_vh, three_idx): scaf_xo[vh_num].append((i, three_vh, three_idx)) assert (len(scaf_seg[vh_num]) % 2 == 0) # install scaffold segments for i in range(0, len(scaf_seg[vh_num]), 2): low_idx = scaf_seg[vh_num][i] high_idx = scaf_seg[vh_num][i + 1] scaf_strand_set.createStrand(low_idx, high_idx, use_undostack=False) # read staple segments and xovers for i in range(len(stap)): five_vh, five_idx, three_vh, three_idx = stap[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandEnum.STAPLE, vh_num, i, five_vh, five_idx, three_vh, three_idx): stap_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case stap_seg[vh_num].append(i) # end segment on a double crossover if is3primeXover(StrandEnum.STAPLE, vh_num, i, three_vh, three_idx): stap_xo[vh_num].append((i, three_vh, three_idx)) assert (len(stap_seg[vh_num]) % 2 == 0) # install staple segments for i in range(0, len(stap_seg[vh_num]), 2): low_idx = stap_seg[vh_num][i] high_idx = stap_seg[vh_num][i + 1] stap_strand_set.createStrand(low_idx, high_idx, use_undostack=False) part.refreshSegments(vh_num) # end for except AssertionError: print("Unrecognized file format.") raise # INSTALL XOVERS for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] if isEven(row, col): scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num) else: stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num) # install scaffold xovers for (idx5p, to_vh_num, idx3p) in scaf_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p try: strand5p = scaf_strand_set.getStrand(idx5p) except Exception: print(vh_num, idx5p) print(scaf_strand_set.strand_heap) print(stap_strand_set.strand_heap) raise coord = vh_num_to_coord[to_vh_num] if isEven(*coord): to_scaf_strand_set, to_stap_strand_set = part.getStrandSets(to_vh_num) else: to_stap_strand_set, to_scaf_strand_set = part.getStrandSets(to_vh_num) strand3p = to_scaf_strand_set.getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, allow_reordering=True, update_oligo=False, use_undostack=False) # install staple xovers for (idx5p, to_vh_num, idx3p) in stap_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p strand5p = stap_strand_set.getStrand(idx5p) coord = vh_num_to_coord[to_vh_num] if isEven(*coord): to_scaf_strand_set, to_stap_strand_set = part.getStrandSets(to_vh_num) else: to_stap_strand_set, to_scaf_strand_set = part.getStrandSets(to_vh_num) strand3p = to_stap_strand_set.getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, allow_reordering=True, update_oligo=False, use_undostack=False) # need to heal all oligo connections into a continuous # oligo for the next steps RefreshOligosCommand(part).redo() # COLORS, INSERTIONS, SKIPS for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] if isEven(row, col): scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num) else: stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num) # install insertions and skips for base_idx in range(len(stap)): sum_of_insert_skip = insertions[base_idx] + skips[base_idx] if sum_of_insert_skip != 0: strand = scaf_strand_set.getStrand(base_idx) strand.addInsertion(base_idx, sum_of_insert_skip, use_undostack=False) # end for # populate colors for base_idx, color_number in helix['stap_colors']: color = intToColorHex(color_number) strand = stap_strand_set.getStrand(base_idx) strand.oligo().applyColor(color, use_undostack=False) if 'oligos' in obj: for oligo in obj['oligos']: vh_num = oligo['vh_num'] idx = oligo['idx'] seq = str(oligo['seq']) if oligo['seq'] is not None else '' if seq != '': coord = vh_num_to_coord[vh_num] if isEven(*coord): scaf_ss, stap_ss = part.getStrandSets(vh_num) else: stap_ss, scaf_ss = part.getStrandSets(vh_num) strand = scaf_ss.getStrand(idx) # print "sequence", seq, vh, idx, strand.oligo()._strand5p strand.oligo().applySequence(seq, use_undostack=False) if 'modifications' in obj: for mod_id, item in obj['modifications'].items(): if mod_id != 'int_instances' and mod_id != 'ext_instances': part.createMod(item, mod_id) for key, mid in obj['modifications']['ext_instances'].items(): # strand, idx, coord, isstaple = part.getModStrandIdx(key) strand, idx = part.getModStrandIdx(key) try: strand.addMods(document, mid, idx, use_undostack=False) except Exception: print(strand, idx) raise for key, mid in obj['modifications']['int_instances'].items(): # strand, idx, coord, isstaple = part.getModStrandIdx(key) strand, idx = part.getModStrandIdx(key) try: strand.addMods(document, mid, idx, use_undostack=False) except Exception: print(strand, idx) raise
def decode(document: DocT, obj: dict, emit_signals: bool = True): """Parses a dictionary (obj) created from reading a json file and uses it to populate the given document with model data. Args: document: obj: emit_signals: whether to signal views Raises: AssertionError, TypeError """ num_bases = len(obj['vstrands'][0]['fwd_ss']) lattice_type = LatticeEnum.HONEYCOMB part = None # DETERMINE MAX ROW,COL max_row_json = max_col_json = 0 for helix in obj['vstrands']: max_row_json = max(max_row_json, int(helix['row']) + 1) max_col_json = max(max_col_json, int(helix['col']) + 1) # CREATE PART ACCORDING TO LATTICE TYPE if lattice_type == LatticeEnum.HONEYCOMB: doLattice = HoneycombDnaPart.latticeCoordToModelXY isEven = HoneycombDnaPart.isEvenParity grid_type = GridEnum.HONEYCOMB elif lattice_type == LatticeEnum.SQUARE: doLattice = SquareDnaPart.latticeCoordToModelXY isEven = SquareDnaPart.isEvenParity grid_type = GridEnum.SQUARE else: raise TypeError("Lattice type not recognized") part = document.createNucleicAcidPart( use_undostack=False, is_lattice=True, grid_type=grid_type) part.setActive(True) setBatch(True) # POPULATE VIRTUAL HELICES ordered_id_list = [] property_dict = {} vh_num_to_coord = {} min_row, max_row = 10000, -10000 min_col, max_col = 10000, -10000 # find row, column limits for helix in obj['vstrands']: row = helix['row'] if row < min_row: min_row = row if row > max_row: max_row = row col = helix['col'] if col < min_col: min_col = col if col > max_col: max_col = col # end for delta_row = (max_row + min_row) // 2 if delta_row & 1: delta_row += 1 delta_column = (max_col + min_col) // 2 if delta_column & 1: delta_column += 1 # print("Found cadnano version 2.5 file") # print("\trows(%d, %d): avg: %d" % (min_row, max_row, delta_row)) # print("\tcolumns(%d, %d): avg: %d" % (min_col, max_col, delta_column)) encoded_keys = ['eulerZ', 'repeats', 'bases_per_repeat', 'turns_per_repeat', 'z'] model_keys = ['eulerZ', 'repeat_hint', 'bases_per_repeat', 'turns_per_repeat', 'z'] for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] # align row and columns to the center 0, 0 coord = (row - delta_row, col - delta_column) vh_num_to_coord[vh_num] = coord ordered_id_list.append(vh_num) property_dict[vh_num] = [helix[key] for key in encoded_keys] # end for radius = DEFAULT_RADIUS for vh_num in sorted(vh_num_to_coord.keys()): row, col = vh_num_to_coord[vh_num] x, y = doLattice(radius, row, col) props = property_dict[vh_num] z = convertToModelZ(props[-1]) props[-1] = z # print("%d:" % vh_num, x, y, z) # print({k:v for k, v in zip(encoded_keys, props)}) part.createVirtualHelix(x, y, z, num_bases, id_num=vh_num, properties=(model_keys, props), use_undostack=False) if not getReopen(): setBatch(False) part.setImportedVHelixOrder(ordered_id_list) # zoom to fit if emit_signals: part.partZDimensionsChangedSignal.emit(part, *part.zBoundsIds(), True) setReopen(False) setBatch(False) # INSTALL STRANDS AND COLLECT XOVER LOCATIONS fwd_ss_seg = defaultdict(list) fwd_ss_xo = defaultdict(list) rev_ss_seg = defaultdict(list) rev_ss_xo = defaultdict(list) try: for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] insertions = helix['insertions'] deletions = helix['deletions'] if isEven(row, col): # parity matters still despite the fwd and rev naming of # this format fwd_strandset, rev_strandset = part.getStrandSets(vh_num) fwd_ss = helix['fwd_ss'] rev_ss = helix['rev_ss'] else: rev_strandset, fwd_strandset = part.getStrandSets(vh_num) rev_ss = helix['fwd_ss'] fwd_ss = helix['rev_ss'] # validate file serialization of lists assert(len(fwd_ss) == len(rev_ss) and len(fwd_ss) == len(insertions) and len(insertions) == len(deletions)) # read fwd_strandset segments and xovers for i in range(len(fwd_ss)): five_vh, five_strand, five_idx, three_vh, three_strand, three_idx = fwd_ss[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandEnum.SCAFFOLD, vh_num, i, five_vh, five_idx, three_vh, three_idx): fwd_ss_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case fwd_ss_seg[vh_num].append(i) # end segment on a double crossover if is3primeXover(StrandEnum.SCAFFOLD, vh_num, i, three_vh, three_idx): fwd_ss_xo[vh_num].append((i, three_vh, three_strand, three_idx)) assert (len(fwd_ss_seg[vh_num]) % 2 == 0) # install fwd_strandset segments for i in range(0, len(fwd_ss_seg[vh_num]), 2): low_idx = fwd_ss_seg[vh_num][i] high_idx = fwd_ss_seg[vh_num][i + 1] fwd_strandset.createStrand(low_idx, high_idx, use_undostack=False) # read rev_strandset segments and xovers for i in range(len(rev_ss)): five_vh, five_strand, five_idx, three_vh, three_strand, three_idx = rev_ss[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandEnum.STAPLE, vh_num, i, five_vh, five_idx, three_vh, three_idx): rev_ss_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case rev_ss_seg[vh_num].append(i) # end segment on a double crossover if is3primeXover(StrandEnum.STAPLE, vh_num, i, three_vh, three_idx): rev_ss_xo[vh_num].append((i, three_vh, three_strand, three_idx)) assert (len(rev_ss_seg[vh_num]) % 2 == 0) # install rev_strandset segments for i in range(0, len(rev_ss_seg[vh_num]), 2): low_idx = rev_ss_seg[vh_num][i] high_idx = rev_ss_seg[vh_num][i + 1] rev_strandset.createStrand(low_idx, high_idx, use_undostack=False) part.refreshSegments(vh_num) # end for except AssertionError: print("Unrecognized file format.") raise """ INSTALL XOVERS parity matters for the from idx but is already encoded in the `to_strand3p` parameter of the tuple in `fwd_ss_xo` and `rev_ss_xo` """ for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] if isEven(row, col): fwd_strandset, rev_strandset = part.getStrandSets(vh_num) else: rev_strandset, fwd_strandset = part.getStrandSets(vh_num) # install fwd_strandset xovers for (idx5p, to_vh_num, to_strand3p, idx3p) in fwd_ss_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p strand5p = fwd_strandset.getStrand(idx5p) to_fwd_strandset, to_rev_strandset = part.getStrandSets(to_vh_num) if to_strand3p == 0: strand3p = to_fwd_strandset.getStrand(idx3p) else: strand3p = to_rev_strandset.getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, allow_reordering=True, update_oligo=False, use_undostack=False) # install rev_strandset xovers for (idx5p, to_vh_num, to_strand3p, idx3p) in rev_ss_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p strand5p = rev_strandset.getStrand(idx5p) to_fwd_strandset, to_rev_strandset = part.getStrandSets(to_vh_num) coord = vh_num_to_coord[to_vh_num] if to_strand3p == 0: strand3p = to_fwd_strandset.getStrand(idx3p) else: strand3p = to_rev_strandset.getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, allow_reordering=True, update_oligo=False, use_undostack=False) # need to heal all oligo connections into a continuous # oligo for the next steps RefreshOligosCommand(part).redo() # COLORS, INSERTIONS, deletions for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] fwd_ss = helix['fwd_ss'] rev_ss = helix['rev_ss'] insertions = helix['insertions'] deletions = helix['deletions'] fwd_strandset, rev_strandset = part.getStrandSets(vh_num) # install insertions and deletions for base_idx in range(len(rev_ss)): sum_of_insert_deletion = insertions[base_idx] + deletions[base_idx] if sum_of_insert_deletion != 0: strand = fwd_strandset.getStrand(base_idx) strand.addInsertion(base_idx, sum_of_insert_deletion, use_undostack=False) # end for # populate colors for strand_type, base_idx, color in helix['colors']: strandset = fwd_strandset if strand_type == 0 else rev_strandset strand = strandset.getStrand(base_idx) strand.oligo().applyColor(color, use_undostack=False) if 'oligos' in obj: for oligo in obj['oligos']: vh_num = oligo['vh_num'] idx = oligo['idx'] seq = str(oligo['seq']) if oligo['seq'] is not None else '' if seq != '': coord = vh_num_to_coord[vh_num] fwd_strandset, rev_strandset = part.getStrandSets(vh_num) strand = fwd_strandset.getStrand(idx) strand.oligo().applySequence(seq, use_undostack=False) if 'modifications' in obj: for mod_id, item in obj['modifications'].items(): if mod_id != 'int_instances' and mod_id != 'ext_instances': part.createMod(item, mod_id) for key, mid in obj['modifications']['ext_instances'].items(): # strand, idx, coord, isstaple = part.getModStrandIdx(key) strand, idx = part.getModStrandIdx(key) try: strand.addMods(document, mid, idx, use_undostack=False) except Exception: print(strand, idx) raise for key in obj['modifications']['int_instances'].items(): # strand, idx, coord, isstaple = part.getModStrandIdx(key) strand, idx = part.getModStrandIdx(key) try: strand.addMods(document, mid, idx, use_undostack=False) except Exception: print(strand, idx) raise
def decode(document, obj, emit_signals=True): """ Parses a dictionary (obj) created from reading a json file and uses it to populate the given document with model data. """ num_bases = len(obj['vstrands'][0]['fwd_ss']) lattice_type = LatticeType.HONEYCOMB part = None # DETERMINE MAX ROW,COL max_row_json = max_col_json = 0 for helix in obj['vstrands']: max_row_json = max(max_row_json, int(helix['row']) + 1) max_col_json = max(max_col_json, int(helix['col']) + 1) # CREATE PART ACCORDING TO LATTICE TYPE if lattice_type == LatticeType.HONEYCOMB: doLattice = HoneycombDnaPart.latticeCoordToPositionXY isEven = HoneycombDnaPart.isEvenParity elif lattice_type == LatticeType.SQUARE: doLattice = SquareDnaPart.latticeCoordToPositionXY isEven = SquareDnaPart.isEvenParity else: raise TypeError("Lattice type not recognized") part = document.createNucleicAcidPart(use_undostack=False) part.setActive(True) setBatch(True) # POPULATE VIRTUAL HELICES ordered_id_list = [] property_dict = {} vh_num_to_coord = {} min_row, max_row = 10000, -10000 min_col, max_col = 10000, -10000 # find row, column limits for helix in obj['vstrands']: row = helix['row'] if row < min_row: min_row = row if row > max_row: max_row = row col = helix['col'] if col < min_col: min_col = col if col > max_col: max_col = col # end for delta_row = (max_row + min_row) // 2 if delta_row & 1: delta_row += 1 delta_column = (max_col + min_col) // 2 if delta_column & 1: delta_column += 1 # print("Found cadnano version 2.5 file") # print("\trows(%d, %d): avg: %d" % (min_row, max_row, delta_row)) # print("\tcolumns(%d, %d): avg: %d" % (min_col, max_col, delta_column)) encoded_keys = [ 'eulerZ', 'repeats', 'bases_per_repeat', 'turns_per_repeat', 'z' ] model_keys = [ 'eulerZ', 'repeat_hint', 'bases_per_repeat', 'turns_per_repeat', 'z' ] for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] # align row and columns to the center 0, 0 coord = (row - delta_row, col - delta_column) vh_num_to_coord[vh_num] = coord ordered_id_list.append(vh_num) property_dict[vh_num] = [helix[key] for key in encoded_keys] # end for radius = DEFAULT_RADIUS for vh_num in sorted(vh_num_to_coord.keys()): row, col = vh_num_to_coord[vh_num] x, y = doLattice(radius, row, col) props = property_dict[vh_num] z = convertToModelZ(props[-1]) props[-1] = z # print("%d:" % vh_num, x, y, z) # print({k:v for k, v in zip(encoded_keys, props)}) part.createVirtualHelix(x, y, z, num_bases, id_num=vh_num, properties=(model_keys, props), use_undostack=False) if not getReopen(): setBatch(False) part.setImportedVHelixOrder(ordered_id_list) # zoom to fit if emit_signals: part.partZDimensionsChangedSignal.emit(part, *part.zBoundsIds(), True) setReopen(False) setBatch(False) # INSTALL STRANDS AND COLLECT XOVER LOCATIONS fwd_ss_seg = defaultdict(list) fwd_ss_xo = defaultdict(list) rev_ss_seg = defaultdict(list) rev_ss_xo = defaultdict(list) try: for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] insertions = helix['insertions'] deletions = helix['deletions'] if isEven(row, col): # parity matters still despite the fwd and rev naming of # this format fwd_strandset, rev_strandset = part.getStrandSets(vh_num) fwd_ss = helix['fwd_ss'] rev_ss = helix['rev_ss'] else: rev_strandset, fwd_strandset = part.getStrandSets(vh_num) rev_ss = helix['fwd_ss'] fwd_ss = helix['rev_ss'] # validate file serialization of lists assert (len(fwd_ss) == len(rev_ss) and len(fwd_ss) == len(insertions) and len(insertions) == len(deletions)) # read fwd_strandset segments and xovers for i in range(len(fwd_ss)): five_vh, five_strand, five_idx, three_vh, three_strand, three_idx = fwd_ss[ i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandType.SCAFFOLD, vh_num, i, five_vh, five_idx, three_vh, three_idx): fwd_ss_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case fwd_ss_seg[vh_num].append( i) # end segment on a double crossover if is3primeXover(StrandType.SCAFFOLD, vh_num, i, three_vh, three_idx): fwd_ss_xo[vh_num].append( (i, three_vh, three_strand, three_idx)) assert (len(fwd_ss_seg[vh_num]) % 2 == 0) # install fwd_strandset segments for i in range(0, len(fwd_ss_seg[vh_num]), 2): low_idx = fwd_ss_seg[vh_num][i] high_idx = fwd_ss_seg[vh_num][i + 1] fwd_strandset.createStrand(low_idx, high_idx, use_undostack=False) # read rev_strandset segments and xovers for i in range(len(rev_ss)): five_vh, five_strand, five_idx, three_vh, three_strand, three_idx = rev_ss[ i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandType.STAPLE, vh_num, i, five_vh, five_idx, three_vh, three_idx): rev_ss_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case rev_ss_seg[vh_num].append( i) # end segment on a double crossover if is3primeXover(StrandType.STAPLE, vh_num, i, three_vh, three_idx): rev_ss_xo[vh_num].append( (i, three_vh, three_strand, three_idx)) assert (len(rev_ss_seg[vh_num]) % 2 == 0) # install rev_strandset segments for i in range(0, len(rev_ss_seg[vh_num]), 2): low_idx = rev_ss_seg[vh_num][i] high_idx = rev_ss_seg[vh_num][i + 1] rev_strandset.createStrand(low_idx, high_idx, use_undostack=False) part.refreshSegments(vh_num) # end for except AssertionError: print("Unrecognized file format.") raise """ INSTALL XOVERS parity matters for the from idx but is already encoded in the `to_strand3p` parameter of the tuple in `fwd_ss_xo` and `rev_ss_xo` """ for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] if isEven(row, col): fwd_strandset, rev_strandset = part.getStrandSets(vh_num) else: rev_strandset, fwd_strandset = part.getStrandSets(vh_num) # install fwd_strandset xovers for (idx5p, to_vh_num, to_strand3p, idx3p) in fwd_ss_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p strand5p = fwd_strandset.getStrand(idx5p) to_fwd_strandset, to_rev_strandset = part.getStrandSets(to_vh_num) if to_strand3p == 0: strand3p = to_fwd_strandset.getStrand(idx3p) else: strand3p = to_rev_strandset.getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, allow_reordering=True, update_oligo=False, use_undostack=False) # install rev_strandset xovers for (idx5p, to_vh_num, to_strand3p, idx3p) in rev_ss_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p strand5p = rev_strandset.getStrand(idx5p) to_fwd_strandset, to_rev_strandset = part.getStrandSets(to_vh_num) coord = vh_num_to_coord[to_vh_num] if to_strand3p == 0: strand3p = to_fwd_strandset.getStrand(idx3p) else: strand3p = to_rev_strandset.getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, allow_reordering=True, update_oligo=False, use_undostack=False) # need to heal all oligo connections into a continuous # oligo for the next steps RefreshOligosCommand(part).redo() # COLORS, INSERTIONS, deletions for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] fwd_ss = helix['fwd_ss'] rev_ss = helix['rev_ss'] insertions = helix['insertions'] deletions = helix['deletions'] fwd_strandset, rev_strandset = part.getStrandSets(vh_num) # install insertions and deletions for base_idx in range(len(rev_ss)): sum_of_insert_deletion = insertions[base_idx] + deletions[base_idx] if sum_of_insert_deletion != 0: strand = fwd_strandset.getStrand(base_idx) strand.addInsertion(base_idx, sum_of_insert_deletion, use_undostack=False) # end for # populate colors for strand_type, base_idx, color in helix['colors']: strandset = fwd_strandset if strand_type == 0 else rev_strandset strand = strandset.getStrand(base_idx) strand.oligo().applyColor(color, use_undostack=False) if 'oligos' in obj: for oligo in obj['oligos']: vh_num = oligo['vh_num'] idx = oligo['idx'] seq = str(oligo['seq']) if oligo['seq'] is not None else '' if seq != '': coord = vh_num_to_coord[vh_num] fwd_strandset, rev_strandset = part.getStrandSets(vh_num) strand = fwd_strandset.getStrand(idx) strand.oligo().applySequence(seq, use_undostack=False) if 'modifications' in obj: for mod_id, item in obj['modifications'].items(): if mod_id != 'int_instances' and mod_id != 'ext_instances': part.createMod(item, mod_id) for key, mid in obj['modifications']['ext_instances'].items(): # strand, idx, coord, isstaple = part.getModStrandIdx(key) strand, idx = part.getModStrandIdx(key) try: strand.addMods(document, mid, idx, use_undostack=False) except Exception: print(strand, idx) raise for key in obj['modifications']['int_instances'].items(): # strand, idx, coord, isstaple = part.getModStrandIdx(key) strand, idx = part.getModStrandIdx(key) try: strand.addMods(document, mid, idx, use_undostack=False) except Exception: print(strand, idx) raise
def decode(document, obj): """ Parses a dictionary (obj) created from reading a json file and uses it to populate the given document with model data. """ num_bases = len(obj['vstrands'][0]['scaf']) if num_bases % 32 == 0: lattice_type = LatticeType.SQUARE elif num_bases % 21 == 0: lattice_type = LatticeType.HONEYCOMB else: raise IOError("error decoding number of bases") # DETERMINE MAX ROW,COL max_row_json = max_col_json = 0 for helix in obj['vstrands']: max_row_json = max(max_row_json, int(helix['row']) + 1) max_col_json = max(max_col_json, int(helix['col']) + 1) # CREATE PART ACCORDING TO LATTICE TYPE if lattice_type == LatticeType.HONEYCOMB: steps = num_bases // 21 # num_rows = max(30, max_row_json, cadnano.app().prefs.honeycombRows) # num_cols = max(32, max_col_json, cadnano.app().prefs.honeycombCols) num_rows = max(30, max_row_json, prefs.HONEYCOMB_PART_MAXROWS) num_cols = max(32, max_col_json, prefs.HONEYCOMB_PART_MAXCOLS) part = document.addHoneycombPart(num_rows, num_cols, steps) elif lattice_type == LatticeType.SQUARE: is_SQ_100 = True # check for custom SQ100 format for helix in obj['vstrands']: if helix['col'] != 0: is_SQ_100 = False break if is_SQ_100: # num_rows, num_cols = 100, 1 num_rows, num_cols = 40, 30 else: num_rows, num_cols = 40, 30 steps = num_bases // 32 # num_rows = max(30, max_row_json, cadnano.app().prefs.squareRows) # num_cols = max(32, max_col_json, cadnano.app().prefs.squareCols) num_rows = max(30, max_row_json, prefs.SQUARE_PART_MAXROWS) num_cols = max(32, max_col_json, prefs.SQUARE_PART_MAXCOLS) part = document.addSquarePart(num_rows, num_cols, steps) # part = SquarePart(document=document, max_row=num_rows, max_col=num_cols, max_steps=steps) else: raise TypeError("Lattice type not recognized") # document._addPart(part, use_undostack=False) setBatch(True) # POPULATE VIRTUAL HELICES ordered_coord_list = [] vh_num_to_coord = {} for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf = helix['scaf'] coord = (row, col) vh_num_to_coord[vh_num] = coord ordered_coord_list.append(coord) # make sure we retain the original order for vh_num in sorted(vh_num_to_coord.keys()): row, col = vh_num_to_coord[vh_num] part.createVirtualHelix(row, col, use_undostack=False) if not getReopen(): setBatch(False) part.setImportedVHelixOrder(ordered_coord_list) setReopen(False) setBatch(False) # INSTALL STRANDS AND COLLECT XOVER LOCATIONS num_helices = len(obj['vstrands']) - 1 scaf_seg = defaultdict(list) scaf_xo = defaultdict(list) stap_seg = defaultdict(list) stap_xo = defaultdict(list) try: for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] vh = part.virtualHelixAtCoord((row, col)) scaf_strand_set = vh.scaffoldStrandSet() stap_strand_set = vh.stapleStrandSet() assert(len(scaf) == len(stap) and len(stap) == part.maxBaseIdx() + 1 and\ len(scaf) == len(insertions) and len(insertions) == len(skips)) # read scaffold segments and xovers for i in range(len(scaf)): five_vh, five_idx, three_vh, three_idx = scaf[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandType.SCAFFOLD, vh_num, i, five_vh,\ five_idx, three_vh, three_idx): scaf_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case scaf_seg[vh_num].append( i) # end segment on a double crossover if is3primeXover(StrandType.SCAFFOLD, vh_num, i, three_vh, three_idx): scaf_xo[vh_num].append((i, three_vh, three_idx)) assert (len(scaf_seg[vh_num]) % 2 == 0) # install scaffold segments for i in range(0, len(scaf_seg[vh_num]), 2): low_idx = scaf_seg[vh_num][i] high_idx = scaf_seg[vh_num][i + 1] scaf_strand_set.createStrand(low_idx, high_idx, use_undostack=False) # read staple segments and xovers for i in range(len(stap)): five_vh, five_idx, three_vh, three_idx = stap[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandType.STAPLE, vh_num, i, five_vh,\ five_idx, three_vh, three_idx): stap_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case stap_seg[vh_num].append( i) # end segment on a double crossover if is3primeXover(StrandType.STAPLE, vh_num, i, three_vh, three_idx): stap_xo[vh_num].append((i, three_vh, three_idx)) assert (len(stap_seg[vh_num]) % 2 == 0) # install staple segments for i in range(0, len(stap_seg[vh_num]), 2): low_idx = stap_seg[vh_num][i] high_idx = stap_seg[vh_num][i + 1] stap_strand_set.createStrand(low_idx, high_idx, use_undostack=False) except AssertionError: print("Unrecognized file format.") raise # INSTALL XOVERS for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] from_vh = part.virtualHelixAtCoord((row, col)) scaf_strand_set = from_vh.scaffoldStrandSet() stap_strand_set = from_vh.stapleStrandSet() # install scaffold xovers for (idx5p, to_vh_num, idx3p) in scaf_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p strand5p = scaf_strand_set.getStrand(idx5p) to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num]) strand3p = to_vh.scaffoldStrandSet().getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, update_oligo=False, use_undostack=False) # install staple xovers for (idx5p, to_vh_num, idx3p) in stap_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p strand5p = stap_strand_set.getStrand(idx5p) to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num]) strand3p = to_vh.stapleStrandSet().getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, update_oligo=False, use_undostack=False) # need to heal all oligo connections into a continuous # oligo for the next steps RefreshOligosCommand(part, include_scaffold=True, colors=(prefs.DEFAULT_SCAF_COLOR, prefs.DEFAULT_STAP_COLOR)).redo() # SET DEFAULT COLOR # for oligo in part.oligos(): # if oligo.isStaple(): # default_color = prefs.DEFAULT_STAP_COLOR # else: # default_color = prefs.DEFAULT_SCAF_COLOR # oligo.applyColor(default_color, use_undostack=False) # COLORS, INSERTIONS, SKIPS for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] vh = part.virtualHelixAtCoord((row, col)) scaf_strand_set = vh.scaffoldStrandSet() stap_strand_set = vh.stapleStrandSet() # install insertions and skips for base_idx in range(len(stap)): sum_of_insert_skip = insertions[base_idx] + skips[base_idx] if sum_of_insert_skip != 0: strand = scaf_strand_set.getStrand(base_idx) strand.addInsertion(base_idx, sum_of_insert_skip, use_undostack=False) # end for # populate colors for base_idx, color_number in helix['stap_colors']: color = Color((color_number >> 16) & 0xFF, (color_number >> 8) & 0xFF, color_number & 0xFF).name() strand = stap_strand_set.getStrand(base_idx) strand.oligo().applyColor(color, use_undostack=False) if 'oligos' in obj: for oligo in obj['oligos']: vh_num = oligo['vh_num'] idx = oligo['idx'] seq = str(oligo['seq']) if oligo['seq'] is not None else '' if seq != '': coord = vh_num_to_coord[vhNum] vh = part.virtualHelixAtCoord(coord) scaf_ss = vh.scaffoldStrandSet() # stapStrandSet = vh.stapleStrandSet() strand = scaf_ss.getStrand(idx) # print "sequence", seq, vh, idx, strand.oligo()._strand5p strand.oligo().applySequence(seq, use_undostack=False) if 'modifications' in obj: # print("AD", cadnano.app().activeDocument) # win = cadnano.app().activeDocument.win # modstool = win.pathToolManager.modsTool # modstool.connectSignals(part) for mod_id, item in obj['modifications'].items(): if mod_id != 'int_instances' and mod_id != 'ext_instances': part.createMod(item, mod_id) for key, mid in obj['modifications']['ext_instances'].items(): strand, idx = part.getModStrandIdx(key) try: strand.addMods(mid, idx, use_undostack=False) except: print(strand, idx) raise for key in obj['modifications']['int_instances'].items(): strand, idx = part.getModStrandIdx(key) try: strand.addMods(mid, idx, use_undostack=False) except: print(strand, idx) raise
def decode(document,obj): num_bases = len(obj['vstrands'][0]['scaf']) if num_bases % 32 == 0: lattice_type = LatticeType.SQUARE elif num_bases % 21 == 0: lattice_type = LatticeType.HONEYCOMB else: raise IOError("error decoding number of bases") # DETERMINE MAX ROW,COL max_row_json = max_col_json = 0 for helix in obj['vstrands']: max_row_json = max(max_row_json, int(helix['row'])+1) max_col_json = max(max_col_json, int(helix['col'])+1) # CREATE PART ACCORDING TO LATTICE TYPE if lattice_type == LatticeType.HONEYCOMB: steps = num_bases // 21 # num_rows = max(30, max_row_json, cadnano.app().prefs.honeycombRows) # num_cols = max(32, max_col_json, cadnano.app().prefs.honeycombCols) num_rows = max(30, max_row_json, prefs.HONEYCOMB_PART_MAXROWS) num_cols = max(32, max_col_json, prefs.HONEYCOMB_PART_MAXCOLS) part = document.addHoneycombPart(num_rows, num_cols, steps) elif lattice_type == LatticeType.SQUARE: is_SQ_100 = True # check for custom SQ100 format for helix in obj['vstrands']: if helix['col'] != 0: is_SQ_100 = False break if is_SQ_100: # num_rows, num_cols = 100, 1 num_rows, num_cols = 40, 30 else: num_rows, num_cols = 40, 30 steps = num_bases // 32 # num_rows = max(30, max_row_json, cadnano.app().prefs.squareRows) # num_cols = max(32, max_col_json, cadnano.app().prefs.squareCols) num_rows = max(30, max_row_json, prefs.SQUARE_PART_MAXROWS) num_cols = max(32, max_col_json, prefs.SQUARE_PART_MAXCOLS) part = document.addSquarePart(num_rows, num_cols, steps) # part = SquarePart(document=document, max_row=num_rows, max_col=num_cols, max_steps=steps) else: raise TypeError("Lattice type not recognized") # document._addPart(part, use_undostack=False) setBatch(True) # POPULATE VIRTUAL HELICES ordered_coord_list = [] vh_num_to_coord = {} for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] coord = (row, col) vh_num_to_coord[vh_num] = coord ordered_coord_list.append(coord) # make sure we retain the original order for vh_num in sorted(vh_num_to_coord.keys()): row, col = vh_num_to_coord[vh_num] part.createVirtualHelix(row, col, use_undostack=False) if not getReopen(): setBatch(False) part.setImportedVHelixOrder(ordered_coord_list) setReopen(False) setBatch(False) num_helices = len(obj['vstrands']) - 1 scaf_seg = defaultdict(list) scaf_xo = defaultdict(list) stap_seg = defaultdict(list) stap_xo = defaultdict(list) for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] vh = part.virtualHelixAtCoord((row, col)) scaf_strand_set = vh._scaf_strand_set stap_strand_set = vh._stap_strand_set # read scaf segments and xovers for i in range(len(scaf)): five_vh, five_idx, three_vh, three_idx = scaf[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandType.SCAFFOLD, vh_num, i, five_vh,\ five_idx, three_vh, three_idx): scaf_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case scaf_seg[vh_num].append(i) # end segment on a double crossover if is3primeXover(StrandType.SCAFFOLD, vh_num, i, three_vh, three_idx): scaf_xo[vh_num].append((i, three_vh, three_idx)) # read staple segments and xovers; update stap .json #TODO: Reem, you need to look here because this is where Amy added in the 5th element #TODO: Reem, you might need to modify this to render existing toeholds in otracad .json files for i in range(len(stap)): five_vh, five_idx, three_vh, three_idx = stap[i] ''' append a fifth element to coordinate list; if staple is hybridized to a scaffold domain at the location, get the index of the scaffold domain on scaffold strandset and append it; if not hybridized to any scaffold domain, append -1. ''' if five_vh == -1 and three_vh == -1: stap[i].append(-1) # null base, cannot hybridize to any domain continue # null base elif hybridized(scaf[i]): # true if hybridized to scaffold at the position scafNum = getStrandIdx(i,scaf_seg,vh_num=vh_num) # get hybridized scaffold domain index stap[i].append(scafNum) if isSegmentStartOrEnd(StrandType.STAPLE, vh_num, i, five_vh,\ five_idx, three_vh, three_idx): stap_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case stap_seg[vh_num].append(i) # end segment on a double crossover if is3primeXover(StrandType.STAPLE, vh_num, i, three_vh, three_idx): stap_xo[vh_num].append((i, three_vh, three_idx)) assert (len(stap_seg[vh_num]) % 2 == 0) # update scaf .json, append hybridized strand index to coordinate for i in range(len(scaf)): five_vh, five_idx, three_vh, three_idx = scaf[i] if five_vh == -1 and three_vh == -1: scaf[i].append(-1) continue # null base elif hybridized(stap[i]): stapNum = getStrandIdx(i,stap_seg,vh_num=vh_num) scaf[i].append(stapNum) new_scaf_seg = [] # install scaffold segments #TODO: Reem, whenever you see install, this is adding/rendering objects for i in range(0, len(scaf_seg[vh_num]), 2): low_idx = scaf_seg[vh_num][i] high_idx = scaf_seg[vh_num][i + 1] # split scaffold domain based on staple domain hybridized to installStrandSet(low_idx,high_idx,scaf,scaf_strand_set,new_scaf_seg) new_stap_seg = [] #install staple segments for i in range(0, len(stap_seg[vh_num]), 2): low_idx = stap_seg[vh_num][i] high_idx = stap_seg[vh_num][i + 1] # split staple domain based on scaffold domain hybridized to installStrandSet(low_idx,high_idx,stap,stap_strand_set,new_stap_seg) # rename staple based on complement domain on scaffold for domain in vh._stap_strand_set: stap_idx = domain._low_idx scaf_domain_idx = getStrandIdx(stap_idx,new_scaf_seg) if scaf_domain_idx is not None: domain.setName(scaf_domain_idx) else: domain.setName(-1) #Inner-staple single-stranded region # INSTALL XOVERS for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] from_vh = part.virtualHelixAtCoord((row, col)) scaf_strand_set = from_vh.scaffoldStrandSet() stap_strand_set = from_vh.stapleStrandSet() # install scaffold xovers for (idx5p, to_vh_num, idx3p) in scaf_xo[vh_num]: strand5p = scaf_strand_set.getStrand(idx5p) to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num]) strand3p = to_vh.scaffoldStrandSet().getStrand(idx3p) assert strand5p._vhNum == from_vh._number and strand3p._vhNum == to_vh_num part.createXover(strand5p, idx5p, strand3p, idx3p, update_oligo=False, use_undostack=False) for (idx5p, to_vh_num, idx3p) in stap_xo[vh_num]: strand5p = stap_strand_set.getStrand(idx5p) to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num]) strand3p = to_vh.stapleStrandSet().getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, update_oligo=False, use_undostack=False) # connect domains into oligos; domains linked by xovers will be on the same oligo RefreshOligosCommand(part, include_scaffold=True, colors=(prefs.DEFAULT_SCAF_COLOR, prefs.DEFAULT_STAP_COLOR)).redo() #SET DEFAULT COLOR for oligo in part.oligos(): if oligo.isStaple(): default_color = prefs.DEFAULT_STAP_COLOR else: default_color = prefs.DEFAULT_SCAF_COLOR oligo.applyColor(default_color, use_undostack=False) # COLORS, INSERTIONS, SKIPS for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] vh = part.virtualHelixAtCoord((row, col)) scaf_strand_set = vh.scaffoldStrandSet() stap_strand_set = vh.stapleStrandSet() # install insertions and skips # for base_idx in range(len(stap)): # sum_of_insert_skip = insertions[base_idx] + skips[base_idx] # if sum_of_insert_skip != 0: # strand = scaf_strand_set.getStrand(base_idx) # strand.addInsertion(base_idx, sum_of_insert_skip, use_undostack=False) # # end for # # populate colors for base_idx, color_number in helix['stap_colors']: color = Color( (color_number >> 16) & 0xFF, (color_number >> 8) & 0xFF, color_number & 0xFF).name() strand = stap_strand_set.getStrand(base_idx) strand.oligo().applyColor(color, use_undostack=False) # # if 'oligos' in obj: # for oligo in obj['oligos']: # vh_num = oligo['vh_num'] # idx = oligo['idx'] # seq = str(oligo['seq']) if oligo['seq'] is not None else '' # if seq != '': # coord = vh_num_to_coord[vh_num] # vh = part.virtualHelixAtCoord(coord) # scaf_ss = vh.scaffoldStrandSet() # # stapStrandSet = vh.stapleStrandSet() # strand = scaf_ss.getStrand(idx) # # print "sequence", seq, vh, idx, strand.oligo()._strand5p # strand.oligo().applySequence(seq, use_undostack=False) # if 'modifications' in obj: # # print("AD", cadnano.app().activeDocument) # # win = cadnano.app().activeDocument.win # # modstool = win.pathToolManager.modsTool # # modstool.connectSignals(part) # for mod_id, item in obj['modifications'].items(): # if mod_id != 'int_instances' and mod_id != 'ext_instances': # part.createMod(item, mod_id) # for key, mid in obj['modifications']['ext_instances'].items(): # strand, idx = part.getModStrandIdx(key) # try: # strand.addMods(mid, idx, use_undostack=False) # except: # print(strand, idx) # raise # for key in obj['modifications']['int_instances'].items(): # strand, idx = part.getModStrandIdx(key) # try: # strand.addMods(mid, idx, use_undostack=False) # except: # print(strand, idx) # raise #modstool.disconnectSignals(part) #end def # # user not allowed to modify file after import document.undoStack().clear()
def decode(document, obj, emit_signals=False): """Parses a dictionary (obj) created from reading a json file and uses it to populate the given document with model data. """ num_bases = len(obj['vstrands'][0]['scaf']) if num_bases % 32 == 0: lattice_type = LatticeType.SQUARE elif num_bases % 21 == 0: lattice_type = LatticeType.HONEYCOMB else: raise IOError("error decoding number of bases") part = None # DETERMINE MAX ROW,COL max_row_json = max_col_json = 0 for helix in obj['vstrands']: max_row_json = max(max_row_json, int(helix['row'])+1) max_col_json = max(max_col_json, int(helix['col'])+1) # CREATE PART ACCORDING TO LATTICE TYPE if lattice_type == LatticeType.HONEYCOMB: doLattice = HoneycombDnaPart.legacyLatticeCoordToPositionXY isEven = HoneycombDnaPart.isEvenParity elif lattice_type == LatticeType.SQUARE: doLattice = SquareDnaPart.legacyLatticeCoordToPositionXY isEven = SquareDnaPart.isEvenParity else: raise TypeError("Lattice type not recognized") part = document.createNucleicAcidPart(use_undostack=False) part.setActive(True) setBatch(True) delta = num_bases - 42 # POPULATE VIRTUAL HELICES ordered_id_list = [] vh_num_to_coord = {} min_row, max_row = 10000, -10000 min_col, max_col = 10000, -10000 # find row, column limits for helix in obj['vstrands']: row = helix['row'] if row < min_row: min_row = row if row > max_row: max_row = row col = helix['col'] if col < min_col: min_col = col if col > max_col: max_col = col # end for delta_row = (max_row + min_row) // 2 # 2 LINES COMMENTED OUT BY NC, doesn't appear to be necessary for honeycomb # if delta_row & 1: # delta_row += 1 delta_column = (max_col + min_col) // 2 if delta_column & 1: delta_column += 1 # print("Found cadnano version 2 file") # print("\trows(%d, %d): avg: %d" % (min_row, max_row, delta_row)) # print("\tcolumns(%d, %d): avg: %d" % (min_col, max_col, delta_column)) for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf= helix['scaf'] # align row and columns to the center 0, 0 coord = (row - delta_row, col - delta_column) vh_num_to_coord[vh_num] = coord ordered_id_list.append(vh_num) # end for # make sure we retain the original order radius = DEFAULT_RADIUS for vh_num in sorted(vh_num_to_coord.keys()): row, col = vh_num_to_coord[vh_num] x, y = doLattice(radius, row, col) # print("%d:" % vh_num, x, y) part.createVirtualHelix(x, y, 0., num_bases, id_num=vh_num, use_undostack=False) # zoom to fit if emit_signals: part.partZDimensionsChangedSignal.emit(part, *part.zBoundsIds(), True) if not getReopen(): setBatch(False) part.setImportedVHelixOrder(ordered_id_list) setReopen(False) setBatch(False) # INSTALL STRANDS AND COLLECT XOVER LOCATIONS scaf_seg = defaultdict(list) scaf_xo = defaultdict(list) stap_seg = defaultdict(list) stap_xo = defaultdict(list) try: for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] if isEven(row, col): scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num) else: stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num) # validate file serialization of lists assert( len(scaf) == len(stap) and len(scaf) == len(insertions) and len(insertions) == len(skips) ) # read scaffold segments and xovers for i in range(len(scaf)): five_vh, five_idx, three_vh, three_idx = scaf[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandType.SCAFFOLD, vh_num, i, five_vh,\ five_idx, three_vh, three_idx): scaf_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case scaf_seg[vh_num].append(i) # end segment on a double crossover if is3primeXover(StrandType.SCAFFOLD, vh_num, i, three_vh, three_idx): scaf_xo[vh_num].append((i, three_vh, three_idx)) assert (len(scaf_seg[vh_num]) % 2 == 0) # install scaffold segments for i in range(0, len(scaf_seg[vh_num]), 2): low_idx = scaf_seg[vh_num][i] high_idx = scaf_seg[vh_num][i + 1] scaf_strand_set.createStrand(low_idx, high_idx, use_undostack=False) # read staple segments and xovers for i in range(len(stap)): five_vh, five_idx, three_vh, three_idx = stap[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandType.STAPLE, vh_num, i, five_vh,\ five_idx, three_vh, three_idx): stap_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case stap_seg[vh_num].append(i) # end segment on a double crossover if is3primeXover(StrandType.STAPLE, vh_num, i, three_vh, three_idx): stap_xo[vh_num].append((i, three_vh, three_idx)) assert (len(stap_seg[vh_num]) % 2 == 0) # install staple segments for i in range(0, len(stap_seg[vh_num]), 2): low_idx = stap_seg[vh_num][i] high_idx = stap_seg[vh_num][i + 1] stap_strand_set.createStrand(low_idx, high_idx, use_undostack=False) part.refreshSegments(vh_num) # end for except AssertionError: print("Unrecognized file format.") raise # INSTALL XOVERS for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] if isEven(row, col): scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num) else: stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num) # install scaffold xovers for (idx5p, to_vh_num, idx3p) in scaf_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p try: strand5p = scaf_strand_set.getStrand(idx5p) except: print(vh_num, idx5p) print(scaf_strand_set.strand_heap) print(stap_strand_set.strand_heap) raise coord = vh_num_to_coord[to_vh_num] if isEven(*coord): to_scaf_strand_set, to_stap_strand_set = part.getStrandSets(to_vh_num) else: to_stap_strand_set, to_scaf_strand_set = part.getStrandSets(to_vh_num) strand3p = to_scaf_strand_set.getStrand(idx3p) part.createXover( strand5p, idx5p, strand3p, idx3p, allow_reordering=True, update_oligo=False, use_undostack=False) # install staple xovers for (idx5p, to_vh_num, idx3p) in stap_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p strand5p = stap_strand_set.getStrand(idx5p) coord = vh_num_to_coord[to_vh_num] if isEven(*coord): to_scaf_strand_set, to_stap_strand_set = part.getStrandSets(to_vh_num) else: to_stap_strand_set, to_scaf_strand_set = part.getStrandSets(to_vh_num) strand3p = to_stap_strand_set.getStrand(idx3p) part.createXover( strand5p, idx5p, strand3p, idx3p, allow_reordering=True, update_oligo=False, use_undostack=False) # need to heal all oligo connections into a continuous # oligo for the next steps RefreshOligosCommand(part).redo() # COLORS, INSERTIONS, SKIPS for helix in obj['vstrands']: vh_num = helix['num'] row, col = vh_num_to_coord[vh_num] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] if isEven(row, col): scaf_strand_set, stap_strand_set = part.getStrandSets(vh_num) else: stap_strand_set, scaf_strand_set = part.getStrandSets(vh_num) # install insertions and skips for base_idx in range(len(stap)): sum_of_insert_skip = insertions[base_idx] + skips[base_idx] if sum_of_insert_skip != 0: strand = scaf_strand_set.getStrand(base_idx) strand.addInsertion(base_idx, sum_of_insert_skip, use_undostack=False) # end for # populate colors for base_idx, color_number in helix['stap_colors']: color = intToColorHex(color_number) strand = stap_strand_set.getStrand(base_idx) strand.oligo().applyColor(color, use_undostack=False) if 'oligos' in obj: for oligo in obj['oligos']: vh_num = oligo['vh_num'] idx = oligo['idx'] seq = str(oligo['seq']) if oligo['seq'] is not None else '' if seq != '': coord = vh_num_to_coord[vh_num] if isEven(*coord): scaf_ss, stap_ss = part.getStrandSets(vh_num) else: stap_ss, scaf_ss = part.getStrandSets(vh_num) strand = scaf_ss.getStrand(idx) # print "sequence", seq, vh, idx, strand.oligo()._strand5p strand.oligo().applySequence(seq, use_undostack=False) if 'modifications' in obj: for mod_id, item in obj['modifications'].items(): if mod_id != 'int_instances' and mod_id != 'ext_instances': part.createMod(item, mod_id) for key, mid in obj['modifications']['ext_instances'].items(): # strand, idx, coord, isstaple = part.getModStrandIdx(key) strand, idx = part.getModStrandIdx(key) try: strand.addMods(document, mid, idx, use_undostack=False) except: print(strand, idx) raise for key, mid in obj['modifications']['int_instances'].items(): # strand, idx, coord, isstaple = part.getModStrandIdx(key) strand, idx = part.getModStrandIdx(key) try: strand.addMods(document, mid, idx, use_undostack=False) except: print(strand, idx) raise
def decode(document, obj): """ Parses a dictionary (obj) created from reading a json file and uses it to populate the given document with model data. """ num_bases = len(obj['vstrands'][0]['scaf']) if num_bases % 32 == 0: lattice_type = LatticeType.SQUARE elif num_bases % 21 == 0: lattice_type = LatticeType.HONEYCOMB else: raise IOError("error decoding number of bases") # DETERMINE MAX ROW,COL max_row_json = max_col_json = 0 for helix in obj['vstrands']: max_row_json = max(max_row_json, int(helix['row'])+1) max_col_json = max(max_col_json, int(helix['col'])+1) # CREATE PART ACCORDING TO LATTICE TYPE if lattice_type == LatticeType.HONEYCOMB: steps = num_bases // 21 # num_rows = max(30, max_row_json, cadnano.app().prefs.honeycombRows) # num_cols = max(32, max_col_json, cadnano.app().prefs.honeycombCols) num_rows = max(30, max_row_json, prefs.HONEYCOMB_PART_MAXROWS) num_cols = max(32, max_col_json, prefs.HONEYCOMB_PART_MAXCOLS) part = document.addHoneycombPart(num_rows, num_cols, steps) elif lattice_type == LatticeType.SQUARE: is_SQ_100 = True # check for custom SQ100 format for helix in obj['vstrands']: if helix['col'] != 0: is_SQ_100 = False break if is_SQ_100: # num_rows, num_cols = 100, 1 num_rows, num_cols = 40, 30 else: num_rows, num_cols = 40, 30 steps = num_bases // 32 # num_rows = max(30, max_row_json, cadnano.app().prefs.squareRows) # num_cols = max(32, max_col_json, cadnano.app().prefs.squareCols) num_rows = max(30, max_row_json, prefs.SQUARE_PART_MAXROWS) num_cols = max(32, max_col_json, prefs.SQUARE_PART_MAXCOLS) part = document.addSquarePart(num_rows, num_cols, steps) # part = SquarePart(document=document, max_row=num_rows, max_col=num_cols, max_steps=steps) else: raise TypeError("Lattice type not recognized") # document._addPart(part, use_undostack=False) setBatch(True) # POPULATE VIRTUAL HELICES ordered_coord_list = [] vh_num_to_coord = {} for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf= helix['scaf'] coord = (row, col) vh_num_to_coord[vh_num] = coord ordered_coord_list.append(coord) # make sure we retain the original order for vh_num in sorted(vh_num_to_coord.keys()): row, col = vh_num_to_coord[vh_num] part.createVirtualHelix(row, col, use_undostack=False) if not getReopen(): setBatch(False) part.setImportedVHelixOrder(ordered_coord_list) setReopen(False) setBatch(False) # INSTALL STRANDS AND COLLECT XOVER LOCATIONS num_helices = len(obj['vstrands']) - 1 scaf_seg = defaultdict(list) scaf_xo = defaultdict(list) stap_seg = defaultdict(list) stap_xo = defaultdict(list) try: for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] vh = part.virtualHelixAtCoord((row, col)) scaf_strand_set = vh.scaffoldStrandSet() stap_strand_set = vh.stapleStrandSet() assert(len(scaf) == len(stap) and len(stap) == part.maxBaseIdx() + 1 and\ len(scaf) == len(insertions) and len(insertions) == len(skips)) # read scaffold segments and xovers for i in range(len(scaf)): five_vh, five_idx, three_vh, three_idx = scaf[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandType.SCAFFOLD, vh_num, i, five_vh,\ five_idx, three_vh, three_idx): scaf_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case scaf_seg[vh_num].append(i) # end segment on a double crossover if is3primeXover(StrandType.SCAFFOLD, vh_num, i, three_vh, three_idx): scaf_xo[vh_num].append((i, three_vh, three_idx)) assert (len(scaf_seg[vh_num]) % 2 == 0) # install scaffold segments for i in range(0, len(scaf_seg[vh_num]), 2): low_idx = scaf_seg[vh_num][i] high_idx = scaf_seg[vh_num][i + 1] scaf_strand_set.createStrand(low_idx, high_idx, use_undostack=False) # read staple segments and xovers for i in range(len(stap)): five_vh, five_idx, three_vh, three_idx = stap[i] if five_vh == -1 and three_vh == -1: continue # null base if isSegmentStartOrEnd(StrandType.STAPLE, vh_num, i, five_vh,\ five_idx, three_vh, three_idx): stap_seg[vh_num].append(i) if five_vh != vh_num and three_vh != vh_num: # special case stap_seg[vh_num].append(i) # end segment on a double crossover if is3primeXover(StrandType.STAPLE, vh_num, i, three_vh, three_idx): stap_xo[vh_num].append((i, three_vh, three_idx)) assert (len(stap_seg[vh_num]) % 2 == 0) # install staple segments for i in range(0, len(stap_seg[vh_num]), 2): low_idx = stap_seg[vh_num][i] high_idx = stap_seg[vh_num][i + 1] stap_strand_set.createStrand(low_idx, high_idx, use_undostack=False) except AssertionError: print("Unrecognized file format.") raise # INSTALL XOVERS for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] from_vh = part.virtualHelixAtCoord((row, col)) scaf_strand_set = from_vh.scaffoldStrandSet() stap_strand_set = from_vh.stapleStrandSet() # install scaffold xovers for (idx5p, to_vh_num, idx3p) in scaf_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p strand5p = scaf_strand_set.getStrand(idx5p) to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num]) strand3p = to_vh.scaffoldStrandSet().getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, update_oligo=False, use_undostack=False) # install staple xovers for (idx5p, to_vh_num, idx3p) in stap_xo[vh_num]: # idx3p is 3' end of strand5p, idx5p is 5' end of strand3p strand5p = stap_strand_set.getStrand(idx5p) to_vh = part.virtualHelixAtCoord(vh_num_to_coord[to_vh_num]) strand3p = to_vh.stapleStrandSet().getStrand(idx3p) part.createXover(strand5p, idx5p, strand3p, idx3p, update_oligo=False, use_undostack=False) # need to heal all oligo connections into a continuous # oligo for the next steps RefreshOligosCommand(part, include_scaffold=True, colors=(prefs.DEFAULT_SCAF_COLOR, prefs.DEFAULT_STAP_COLOR)).redo() # SET DEFAULT COLOR # for oligo in part.oligos(): # if oligo.isStaple(): # default_color = prefs.DEFAULT_STAP_COLOR # else: # default_color = prefs.DEFAULT_SCAF_COLOR # oligo.applyColor(default_color, use_undostack=False) # COLORS, INSERTIONS, SKIPS for helix in obj['vstrands']: vh_num = helix['num'] row = helix['row'] col = helix['col'] scaf = helix['scaf'] stap = helix['stap'] insertions = helix['loop'] skips = helix['skip'] vh = part.virtualHelixAtCoord((row, col)) scaf_strand_set = vh.scaffoldStrandSet() stap_strand_set = vh.stapleStrandSet() # install insertions and skips for base_idx in range(len(stap)): sum_of_insert_skip = insertions[base_idx] + skips[base_idx] if sum_of_insert_skip != 0: strand = scaf_strand_set.getStrand(base_idx) strand.addInsertion(base_idx, sum_of_insert_skip, use_undostack=False) # end for # populate colors for base_idx, color_number in helix['stap_colors']: color = Color( (color_number >> 16) & 0xFF, (color_number >> 8) & 0xFF, color_number & 0xFF).name() strand = stap_strand_set.getStrand(base_idx) strand.oligo().applyColor(color, use_undostack=False) if 'oligos' in obj: for oligo in obj['oligos']: vh_num = oligo['vh_num'] idx = oligo['idx'] seq = str(oligo['seq']) if oligo['seq'] is not None else '' if seq != '': coord = vh_num_to_coord[vhNum] vh = part.virtualHelixAtCoord(coord) scaf_ss = vh.scaffoldStrandSet() # stapStrandSet = vh.stapleStrandSet() strand = scaf_ss.getStrand(idx) # print "sequence", seq, vh, idx, strand.oligo()._strand5p strand.oligo().applySequence(seq, use_undostack=False) if 'modifications' in obj: # print("AD", cadnano.app().activeDocument) # win = cadnano.app().activeDocument.win # modstool = win.pathToolManager.modsTool # modstool.connectSignals(part) for mod_id, item in obj['modifications'].items(): if mod_id != 'int_instances' and mod_id != 'ext_instances': part.createMod(item, mod_id) for key, mid in obj['modifications']['ext_instances'].items(): strand, idx = part.getModStrandIdx(key) try: strand.addMods(mid, idx, use_undostack=False) except: print(strand, idx) raise for key in obj['modifications']['int_instances'].items(): strand, idx = part.getModStrandIdx(key) try: strand.addMods(mid, idx, use_undostack=False) except: print(strand, idx) raise