├── .gitignore
├── CATG.py
├── LICENSE
├── Manual
└── CATG User Guide.pdf
├── README.md
├── coll_asm_corr_gui
├── __init__.py
├── corrector
│ ├── __init__.py
│ ├── corrector.py
│ ├── locator.py
│ └── vis.py
├── io
│ ├── __init__.py
│ └── file_reader.py
├── main
│ ├── __init__.py
│ ├── assembly_corrector_main.py
│ └── file_loader_dialog.py
├── resources
│ ├── CATG.icns
│ ├── CATG.ico
│ ├── CATG.png
│ ├── assembly_corrector_main.ui
│ └── file_loader_dialog.ui
└── ui
│ ├── __init__.py
│ ├── custom_control.py
│ ├── ui_assembly_corrector_main.py
│ └── ui_file_loader_dialog.py
└── test
└── test_data.tar.gz
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .vscode
3 | .idea
4 | *.pyc
5 | __pycache__
6 | venv
7 | *.exe
8 | *.build
9 | *.dist
10 | *.onefile-build
11 | *.app
--------------------------------------------------------------------------------
/CATG.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | from os import path
4 | from PySide6.QtWidgets import QApplication
5 | from PySide6.QtGui import QIcon
6 | from PySide6.QtCore import QCoreApplication, Qt
7 | from qt_material import apply_stylesheet
8 | from coll_asm_corr_gui.main.assembly_corrector_main import AssemblyCorrectorMain
9 |
10 |
11 | if __name__ == "__main__":
12 | QCoreApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
13 | app = QApplication([])
14 | icon_path = path.join(path.dirname(path.abspath(__file__)), "coll_asm_corr_gui/resources/CATG.png")
15 | app.setWindowIcon(QIcon(icon_path))
16 | main_window = AssemblyCorrectorMain()
17 | apply_stylesheet(app, theme="dark_teal.xml")
18 | main_window.show()
19 | sys.exit(app.exec())
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2022, Shengcheng Zhang
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/Manual/CATG User Guide.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sc-zhang/CATG/606d7f23fe713f445e2af61d3766958bc2dfa072/Manual/CATG User Guide.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CATG: A Tool for Correcting Genome Assembly with Collinearity
2 |
3 | [](https://doi.org/10.5281/zenodo.13621059)
4 |
5 | # Introduction
6 | CATG (**C**ollinearity-based **A**ssembly correc**T**or **G**UI) is a GUI application base on Qt with PySide6.
7 | It is a tool that can adjust assembly with collinearity and generate tour files for assembly.
8 |
9 | # How to cite
10 | > CATG is now published in G3 Genes|Genomes|Genetics
11 | > Shengcheng Zhang, Hejun Du, Xingtan Zhang, Binzhong Wang, Collinearity-based Assembly Correction Tool GUI: Software for collinearity-based genome assembly correction, G3 Genes|Genomes|Genetics, Volume 15, Issue 2, February 2025, jkae277, [https://doi.org/10.1093/g3journal/jkae277](https://doi.org/10.1093/g3journal/jkae277).
12 |
13 |
14 | # Installation
15 | ## Download pre-build binary files
16 |
17 | User can download executable file with following links.
18 |
19 | 1. Windows user
20 | - https://github.com/sc-zhang/CATG/releases/download/v1.2.2/CATG-v1.2.2.exe
21 | - https://zenodo.org/records/13621059/files/CATG-v1.2.2.exe?download=1
22 |
23 | 2. Mac user (Apple silicon)
24 | - https://github.com/sc-zhang/CATG/releases/download/v1.2.2/CATG-v1.2.2.arm.dmg
25 | - https://zenodo.org/records/13621059/files/CATG-v1.2.2.arm.dmg?download=1
26 |
27 | 3. Mac user (Intel silicon)
28 | - https://github.com/sc-zhang/CATG/releases/download/v1.2.2/CATG-v1.2.2.Intel.dmg
29 | - https://zenodo.org/records/13621059/files/CATG-v1.2.2.Intel.dmg?download=1
30 |
31 | 4. Ubuntu user
32 | - https://github.com/sc-zhang/CATG/releases/download/v1.2.2/CATG-v1.2.2.bin
33 | - https://zenodo.org/records/13621059/files/CATG-v1.2.2.bin?download=1
34 |
35 | # Usage
36 |
37 | The online version of user manual could be found in [CATG Wiki](https://github.com/sc-zhang/CATG/wiki)
38 |
39 | The pdf version of user manual could be found in [CATG User Guide](https://github.com/sc-zhang/CATG/releases/download/v1.2.2/CATG.User.Guide.pdf)
40 |
41 | # Test data
42 |
43 | 1. Test data could be found in following links.
44 | - https://github.com/sc-zhang/CATG/archive/refs/tags/v1.2.2.zip
45 | - https://zenodo.org/records/13621059/files/sc-zhang/CATG-v1.2.2.zip?download=1
46 |
47 | 2. Unzip this compressed file, a file named "test.tar.gz" could be found in test folder, then unzip test.tar.gz, four files: qry.agp, qry.bed, qry.ref.anchors, ref.bed could be found which can be used with this tool.
48 |
49 | 3. test.tar.gz also could be found in following links.
50 | - https://github.com/sc-zhang/CATG/releases/download/v1.2.2/test_data.tar.gz
51 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sc-zhang/CATG/606d7f23fe713f445e2af61d3766958bc2dfa072/coll_asm_corr_gui/__init__.py
--------------------------------------------------------------------------------
/coll_asm_corr_gui/corrector/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sc-zhang/CATG/606d7f23fe713f445e2af61d3766958bc2dfa072/coll_asm_corr_gui/corrector/__init__.py
--------------------------------------------------------------------------------
/coll_asm_corr_gui/corrector/corrector.py:
--------------------------------------------------------------------------------
1 | class Corrector:
2 |
3 | def __init__(self):
4 | pass
5 |
6 | @staticmethod
7 | def get_gene_ctg(chr_list, sp, ep):
8 | infos = []
9 | for info in chr_list:
10 | chsp = info[0]
11 | chep = info[1]
12 | ovlp = min(chep, ep) - max(chsp, sp)
13 | if ovlp >= 0:
14 | infos.append([ovlp, info])
15 | if infos:
16 | return sorted(infos, reverse=True)[0][1]
17 | else:
18 | return []
19 |
20 | @staticmethod
21 | def trans_anno(ori_agp_db, adj_agp_db, ori_bed_db):
22 | adj_bed_db = {}
23 |
24 | cov_adj_agp_db = {}
25 | for chrn in adj_agp_db:
26 | for sp, ep, ctg, ctg_len, direct in adj_agp_db[chrn]:
27 | cov_adj_agp_db[ctg] = [chrn, sp, ep, ctg_len, direct]
28 |
29 | for gid in sorted(ori_bed_db):
30 | chrn, gsp, gep, gdir = ori_bed_db[gid]
31 |
32 | match_ctg = Corrector.get_gene_ctg(ori_agp_db[chrn], gsp, gep)
33 |
34 | if match_ctg:
35 | csp, cep, tig, _, tdir = match_ctg
36 | if tig not in cov_adj_agp_db:
37 | continue
38 | nchrn, nsp, nep, nctg_len, ndir = cov_adj_agp_db[tig]
39 |
40 | if tdir == '+':
41 | gts = gsp - csp + 1
42 | gte = gep - csp + 1
43 | else:
44 | gts = cep - gep + 1
45 | gte = cep - gsp + 1
46 | if gdir == tdir:
47 | gtd = '+'
48 | else:
49 | gtd = '-'
50 | if ndir == '+':
51 | gns = nsp + gts - 1
52 | gne = nsp + gte - 1
53 | else:
54 | gns = nep - gte + 1
55 | gne = nep - gts + 1
56 | if gtd == ndir:
57 | gnd = '+'
58 | else:
59 | gnd = '-'
60 | if gns <= 0 or gne <= 0:
61 | continue
62 | adj_bed_db[gid] = [nchrn, gns, gne, gnd]
63 |
64 | return adj_bed_db
65 |
66 | @staticmethod
67 | def reverse_chr(agp_list):
68 | adj_agp_list = []
69 | base = 1
70 | for _, _, ctg, ctg_len, direct in agp_list[::-1]:
71 | if direct == '+':
72 | direct = '-'
73 | else:
74 | direct = '+'
75 | adj_agp_list.append([base, base+ctg_len-1, ctg, ctg_len, direct])
76 | base += ctg_len+100
77 | return adj_agp_list
78 |
79 | @staticmethod
80 | def reverse_block(agp_list, pos):
81 | start_idx, _, end_idx, _ = pos
82 | adj_agp_list = []
83 | base = 1
84 |
85 | for _ in agp_list[:start_idx]:
86 | adj_agp_list.append(_)
87 | base += _[3]+100
88 |
89 | for i in range(end_idx, start_idx-1, -1):
90 | _, _, ctg, ctg_len, direct = agp_list[i]
91 | if direct == '+':
92 | direct = '-'
93 | else:
94 | direct = '+'
95 | adj_agp_list.append([base, base+ctg_len-1, ctg, ctg_len, direct])
96 | base += ctg_len+100
97 |
98 | for _ in agp_list[end_idx+1:]:
99 | adj_agp_list.append(_)
100 | base += _[3]+100
101 |
102 | return adj_agp_list
103 |
104 | @staticmethod
105 | def split_block(agp_list, pos):
106 | start_idx, _, end_idx, _ = pos
107 | adj_agp_list = []
108 |
109 | base = 1
110 |
111 | for _ in agp_list[:start_idx]:
112 | adj_agp_list.append(_)
113 | base += _[3]+100
114 |
115 | extract_agp_list = agp_list[start_idx: end_idx+1]
116 |
117 | for _ in agp_list[end_idx + 1:]:
118 | _[0] = base
119 | _[1] = base+_[3]-1
120 | adj_agp_list.append(_)
121 | base += _[3]+100
122 |
123 | return adj_agp_list, extract_agp_list
124 |
125 | @staticmethod
126 | def ins_term(agp_list, ins_agp_list, ins_head=True):
127 | adj_agp_list = []
128 | base = 1
129 | if ins_head:
130 | order = [ins_agp_list, agp_list]
131 | else:
132 | order = [agp_list, ins_agp_list]
133 | for cur_list in order:
134 | for _ in cur_list:
135 | _[0] = base
136 | _[1] = base+_[3]-1
137 | adj_agp_list.append(_)
138 | base += _[3]+100
139 | return adj_agp_list
140 |
141 | @staticmethod
142 | def ins_pos(agp_list, ins_agp_list, pos, ins_before=True):
143 | adj_agp_list = []
144 | base = 1
145 | if ins_before:
146 | ins_pos = pos[0]
147 | order = [agp_list[:ins_pos], ins_agp_list, agp_list[ins_pos:]]
148 | else:
149 | ins_pos = pos[2]
150 | order = [agp_list[:ins_pos+1], ins_agp_list, agp_list[ins_pos+1:]]
151 | for cur_list in order:
152 | for _ in cur_list:
153 | _[0] = base
154 | _[1] = base+_[3]-1
155 | adj_agp_list.append(_)
156 | base += _[3]+100
157 | return adj_agp_list
158 |
159 | @staticmethod
160 | def swap_blk_diff_chr(src_list, src_pos, tgt_list, tgt_pos):
161 | src_start_idx, _, src_end_idx, _ = src_pos
162 | tgt_start_idx, _, tgt_end_idx, _ = tgt_pos
163 |
164 | src_base = 1
165 | tgt_base = 1
166 |
167 | src_adj_agp_list = []
168 | tgt_adj_agp_list = []
169 |
170 | for _ in src_list[:src_start_idx]:
171 | src_adj_agp_list.append(_)
172 | src_base += _[3]+100
173 |
174 | for _ in tgt_list[:tgt_start_idx]:
175 | tgt_adj_agp_list.append(_)
176 | tgt_base += _[3]+100
177 |
178 | for _ in src_list[src_start_idx: src_end_idx+1]:
179 | _[0] = tgt_base
180 | _[1] = tgt_base+_[3]-1
181 | tgt_adj_agp_list.append(_)
182 | tgt_base += _[3]+100
183 |
184 | for _ in tgt_list[tgt_start_idx: tgt_end_idx+1]:
185 | _[0] = src_base
186 | _[1] = src_base+_[3]-1
187 | src_adj_agp_list.append(_)
188 | src_base += _[3]+100
189 |
190 | for _ in src_list[src_end_idx+1:]:
191 | _[0] = src_base
192 | _[1] = src_base+_[3]-1
193 | src_adj_agp_list.append(_)
194 | src_base += _[3]+100
195 |
196 | for _ in tgt_list[tgt_end_idx+1:]:
197 | _[0] = tgt_base
198 | _[1] = tgt_base+_[3]-1
199 | tgt_adj_agp_list.append(_)
200 | tgt_base += _[3]+100
201 |
202 | return src_adj_agp_list, tgt_adj_agp_list
203 |
204 | @staticmethod
205 | def swap_blk_single_chr(agp_list, src_pos, tgt_pos):
206 | src_start_idx, _, src_end_idx, _ = src_pos
207 | tgt_start_idx, _, tgt_end_idx, _ = tgt_pos
208 | if src_start_idx > tgt_start_idx:
209 | src_start_idx, tgt_start_idx = tgt_start_idx, src_start_idx
210 | src_end_idx, tgt_end_idx = tgt_end_idx, src_end_idx
211 |
212 | if tgt_start_idx < src_end_idx:
213 | tgt_start_idx = src_end_idx + 1
214 | order = [agp_list[:src_start_idx],
215 | agp_list[tgt_start_idx: tgt_end_idx+1],
216 | agp_list[src_end_idx+1: tgt_start_idx],
217 | agp_list[src_start_idx: src_end_idx+1],
218 | agp_list[tgt_end_idx+1:]]
219 |
220 | adj_agp_list = []
221 | base = 1
222 | for cur_list in order:
223 | for _ in cur_list:
224 | _[0] = base
225 | _[1] = base + _[3] - 1
226 | adj_agp_list.append(_)
227 | base += _[3] + 100
228 | return adj_agp_list
229 |
230 | @staticmethod
231 | def remove_blk(agp_list, pos):
232 | start_idx, _, end_idx, _ = pos
233 | adj_agp_list = []
234 |
235 | base = 1
236 |
237 | for _ in agp_list[:start_idx]:
238 | adj_agp_list.append(_)
239 | base += _[3] + 100
240 |
241 | for _ in agp_list[end_idx + 1:]:
242 | _[0] = base
243 | _[1] = base + _[3] - 1
244 | adj_agp_list.append(_)
245 | base += _[3] + 100
246 |
247 | return adj_agp_list
248 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/corrector/locator.py:
--------------------------------------------------------------------------------
1 | from math import sqrt
2 |
3 |
4 | def euc_dist(a, b):
5 | return sqrt((b[0] - a[0]) ** 2 + (b[1] - a[1]) ** 2)
6 |
7 |
8 | class Locator:
9 |
10 | def __init__(self):
11 | self.links = []
12 | self.block_db = {}
13 |
14 | def convert_anchors(self, qry_db, ref_db, anc_list):
15 | link_db = {}
16 |
17 | for qry_gene, ref_gene in anc_list:
18 | if qry_gene not in qry_db or ref_gene not in ref_db:
19 | continue
20 | qchr, qsp, qep, _ = qry_db[qry_gene]
21 | qp = min(qsp, qep)
22 | schr, ssp, sep, _ = ref_db[ref_gene]
23 | sp = min(ssp, sep)
24 | if qchr not in link_db:
25 | link_db[qchr] = {}
26 | if schr not in link_db[qchr]:
27 | link_db[qchr][schr] = []
28 | link_db[qchr][schr].append([qp, sp])
29 |
30 | for chrx in sorted(link_db):
31 | for chry in sorted(link_db[chrx]):
32 | for x, y in sorted(link_db[chrx][chry]):
33 | self.links.append([chrx, x, chry, y])
34 |
35 | def get_break_blocks(self, resolution):
36 | link_db = {}
37 | chr_len_db = {}
38 | for qchr, qp, schr, sp in self.links:
39 | if qchr not in link_db:
40 | link_db[qchr] = {}
41 | if schr not in link_db[qchr]:
42 | link_db[qchr][schr] = []
43 | link_db[qchr][schr].append([qp, sp])
44 | if qchr not in chr_len_db:
45 | chr_len_db[qchr] = {}
46 | if schr not in chr_len_db[qchr]:
47 | chr_len_db[qchr][schr] = [0, 0]
48 | if qp > chr_len_db[qchr][schr][0]:
49 | chr_len_db[qchr][schr][0] = qp
50 | if sp > chr_len_db[qchr][schr][1]:
51 | chr_len_db[qchr][schr][1] = sp
52 |
53 | for qchr in link_db:
54 | self.block_db[qchr] = {}
55 | for schr in link_db[qchr]:
56 | groups = []
57 | chr_len_merge = euc_dist([0, 0], chr_len_db[qchr][schr])
58 | for x, y in link_db[qchr][schr]:
59 | if len(groups) == 0:
60 | groups.append([[x, y]])
61 | else:
62 | is_add = False
63 | for i in range(0, len(groups)):
64 | tail_x, tail_y = groups[i][-1]
65 | if euc_dist([x, y], [tail_x, tail_y]) * resolution / chr_len_merge < 1:
66 | groups[i].append([x, y])
67 | is_add = True
68 | break
69 | if not is_add:
70 | groups.append([[x, y]])
71 |
72 | self.block_db[qchr][schr] = []
73 | for group in groups:
74 | x = []
75 | y = []
76 | for i in range(0, len(group)):
77 | x.append(group[i][0])
78 | y.append(group[i][1])
79 | min_y = min(y)
80 | max_y = max(y)
81 | min_index = y.index(min_y)
82 | max_index = y.index(max_y)
83 | min_x = x[min_index]
84 | max_x = x[max_index]
85 | sx, sy = group[0]
86 | ex, ey = group[-1]
87 | if min_x > max_x:
88 | tmp = min_x
89 | min_x = max_x
90 | max_x = tmp
91 | tmp = min_y
92 | min_y = max_y
93 | max_y = tmp
94 | tmp_list = []
95 | tmp_list.extend([sx, sy])
96 | if sx < min_x < ex:
97 | tmp_list.extend([min_x, min_y])
98 | if sx < max_x < ex:
99 | tmp_list.extend([max_x, max_y])
100 | tmp_list.extend([ex, ey])
101 | for i in range(0, len(tmp_list) - 2, 2):
102 | self.block_db[qchr][schr].append([tmp_list[i], tmp_list[i + 1], tmp_list[i + 2], tmp_list[i + 3]])
103 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/corrector/vis.py:
--------------------------------------------------------------------------------
1 | import matplotlib
2 | matplotlib.use("qt5agg")
3 | from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
4 | import matplotlib.backends.backend_pdf
5 | import matplotlib.pyplot as plt
6 | from coll_asm_corr_gui.corrector.locator import euc_dist
7 |
8 |
9 | class VisCanvas(FigureCanvas):
10 |
11 | def __init__(self):
12 | fig = plt.figure(figsize=(10, 10), dpi=300, tight_layout=True, facecolor="#FFF2E2")
13 | self.plt = plt
14 | super(VisCanvas, self).__init__(fig)
15 |
16 |
17 | class VisContent:
18 |
19 | def __init__(self):
20 | self.chr_list_x = []
21 | self.chr_list_y = []
22 | self.block_list_db = {}
23 | self.block_detail = []
24 | self.data_db = {}
25 | self.block_regions = []
26 | self.figure_content = VisCanvas()
27 |
28 | def convert_link(self, links):
29 | chr_set_x = set()
30 | chr_set_y = set()
31 | self.data_db = {}
32 | for chr_x, pos_x, chr_y, pos_y in links:
33 | chr_set_x.add(chr_x)
34 | chr_set_y.add(chr_y)
35 | if chr_x not in self.data_db:
36 | self.data_db[chr_x] = {}
37 | if chr_y not in self.data_db[chr_x]:
38 | self.data_db[chr_x][chr_y] = []
39 | self.data_db[chr_x][chr_y].append([pos_x, pos_y])
40 |
41 | self.chr_list_x = sorted(chr_set_x)
42 | self.chr_list_y = sorted(chr_set_y)
43 |
44 | @staticmethod
45 | def __get_ctg_pos(region, pos):
46 | s = 0
47 | e = len(region) - 1
48 | while s <= e:
49 | mid = int((s + e) / 2)
50 | if region[mid][0] > pos:
51 | e = mid - 1
52 | elif region[mid][0] < pos:
53 | s = mid + 1
54 | else:
55 | return mid
56 | if region[e][1] >= pos:
57 | return e
58 | elif e == len(region) - 1:
59 | return e
60 | else:
61 | return -1
62 |
63 | def gen_figure(self, links, block_db, agp_db, resolution, qry_name="", ref_name=""):
64 | self.convert_link(links)
65 | chr_len_db = {}
66 |
67 | for chrx in self.chr_list_x:
68 | if chrx not in chr_len_db:
69 | chr_len_db[chrx] = 0
70 | for chry in self.chr_list_y:
71 | if chry not in chr_len_db:
72 | chr_len_db[chry] = 0
73 | if chry not in self.data_db[chrx]:
74 | continue
75 | for x, y in self.data_db[chrx][chry]:
76 | if x > chr_len_db[chrx]:
77 | chr_len_db[chrx] = x
78 | if y > chr_len_db[chry]:
79 | chr_len_db[chry] = y
80 | base_x = 0
81 | base_y = 0
82 | offset_db = {}
83 | for chrx in self.chr_list_x:
84 | offset_db[chrx] = base_x
85 | base_x += chr_len_db[chrx]
86 | for chry in self.chr_list_y:
87 | offset_db[chry] = base_y
88 | base_y += chr_len_db[chry]
89 |
90 | data_x = []
91 | data_y = []
92 | for chrx in self.chr_list_x:
93 | for chry in self.chr_list_y:
94 | if chry not in self.data_db[chrx]:
95 | continue
96 | for x, y in self.data_db[chrx][chry]:
97 | data_x.append(x + offset_db[chrx])
98 | data_y.append(y + offset_db[chry])
99 |
100 | block_x = []
101 | block_y = []
102 |
103 | idx = 0
104 |
105 | self.block_list_db = {}
106 | self.block_detail = []
107 | self.block_regions = []
108 |
109 | for chrx in self.chr_list_x:
110 | for chry in self.chr_list_y:
111 | if chrx not in block_db or chry not in block_db[chrx]:
112 | continue
113 | for x1, y1, x2, y2 in block_db[chrx][chry]:
114 | if euc_dist([x1, y1], [x2, y2]) * 1. * resolution / \
115 | euc_dist([0, 0], [chr_len_db[chrx], chr_len_db[chry]]) < 1:
116 | continue
117 |
118 | idx += 1
119 | if chrx not in self.block_list_db:
120 | self.block_list_db[chrx] = []
121 | self.block_list_db[chrx].append(str(idx))
122 |
123 | block_x.append([x1 + offset_db[chrx], x2 + offset_db[chrx]])
124 | rstart = self.__get_ctg_pos(agp_db[chrx], x1)
125 | rend = self.__get_ctg_pos(agp_db[chrx], x2)
126 | if rstart == -1 or rend == -1:
127 | continue
128 | self.block_detail.append([])
129 | for _ in range(rstart, rend+1):
130 | self.block_detail[-1].append(agp_db[chrx][_][2]+agp_db[chrx][_][4])
131 | ctg1 = agp_db[chrx][rstart][2]
132 | ctg2 = agp_db[chrx][rend][2]
133 | self.block_regions.append([rstart, ctg1, rend, ctg2])
134 | block_y.append([y1 + offset_db[chry], y2 + offset_db[chry]])
135 |
136 | max_x = 0
137 | max_y = 0
138 | for chrx in self.chr_list_x:
139 | max_x += chr_len_db[chrx]
140 |
141 | for chry in self.chr_list_y:
142 | max_y += chr_len_db[chry]
143 |
144 | x_ticks = []
145 | x_labels = []
146 | base_x = 0
147 | chrx_idx_db = {}
148 | idx = 0
149 | offset_x_list = []
150 |
151 | self.figure_content.plt.clf()
152 |
153 | # draw grid lines
154 | for chrx in self.chr_list_x:
155 | offset_x_list.append(base_x)
156 | chrx_idx_db[chrx] = idx
157 | idx += 1
158 | self.figure_content.plt.plot([chr_len_db[chrx] + base_x, chr_len_db[chrx] + base_x], [0, max_y],
159 | linestyle='-',
160 | color='green',
161 | linewidth=0.5, markersize=0)
162 | x_ticks.append(base_x + int(chr_len_db[chrx] / 2))
163 | x_labels.append(chrx)
164 | base_x += chr_len_db[chrx]
165 | offset_x_list.append(base_x)
166 |
167 | y_ticks = []
168 | y_labels = []
169 | base_y = 0
170 | chry_idx_db = {}
171 | idx = 0
172 | offset_y_list = []
173 |
174 | for chry in self.chr_list_y:
175 | offset_y_list.append(base_y)
176 | chry_idx_db[chry] = idx
177 | idx += 1
178 | self.figure_content.plt.plot([0, max_x], [chr_len_db[chry] + base_y, chr_len_db[chry] + base_y],
179 | linestyle='-',
180 | color='green',
181 | linewidth=0.5, markersize=0)
182 | y_ticks.append(base_y + int(chr_len_db[chry] / 2))
183 | y_labels.append(chry)
184 | base_y += chr_len_db[chry]
185 | offset_y_list.append(base_y)
186 |
187 | # draw dotplot
188 | down_sample_dist = max(int(len(data_x) / 1500), 1)
189 | self.figure_content.plt.plot(data_x[::down_sample_dist], data_y[::down_sample_dist],
190 | linestyle='', color='black', marker='o', markersize=0.5)
191 |
192 | # draw blocks and block ids
193 | for i in range(0, len(block_x)):
194 | block_pos = [(block_x[i][0] + block_x[i][1]) / 2.0, (block_y[i][0] + block_y[i][1]) / 2.0]
195 |
196 | self.figure_content.plt.plot(block_x[i], block_y[i], linestyle='-', color='orange', linewidth=0.5,
197 | markersize=0)
198 | self.figure_content.plt.annotate(i+1,
199 | xy=block_pos,
200 | bbox=dict(
201 | boxstyle="circle,pad=0",
202 | fc="white",
203 | ec="black",
204 | alpha=0.5,
205 | lw=1
206 | ),
207 | fontsize=8, color='blue', ha='right')
208 |
209 | self.figure_content.plt.xlim([0, max_x])
210 | self.figure_content.plt.ylim([0, max_y])
211 | self.figure_content.plt.xticks(x_ticks)
212 | self.figure_content.plt.yticks(y_ticks)
213 | self.figure_content.plt.xlabel(qry_name)
214 | self.figure_content.plt.ylabel(ref_name)
215 | ax = self.figure_content.plt.gca()
216 | ax.set_facecolor("#FFF2E2")
217 | ax.set_xticklabels(x_labels, rotation=45)
218 | ax.set_yticklabels(y_labels, rotation=0)
219 | ax.xaxis.set_ticks_position('top')
220 | ax.yaxis.set_ticks_position('right')
221 | ax.invert_yaxis()
222 | ax.tick_params(top=False, right=False)
223 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/io/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sc-zhang/CATG/606d7f23fe713f445e2af61d3766958bc2dfa072/coll_asm_corr_gui/io/__init__.py
--------------------------------------------------------------------------------
/coll_asm_corr_gui/io/file_reader.py:
--------------------------------------------------------------------------------
1 | class Reader:
2 |
3 | def __init__(self):
4 | pass
5 |
6 | @staticmethod
7 | def read_bed(bed_file):
8 | dict = {}
9 | chr_set = set()
10 | with open(bed_file, 'r') as fin:
11 | for line in fin:
12 | if len(line.strip()) == 0 or line[0] == '#':
13 | continue
14 | data = line.strip().split()
15 | if len(data) < 4:
16 | continue
17 | chrn = data[0]
18 | chr_set.add(chrn)
19 | try:
20 | sp = int(data[1])
21 | ep = int(data[2])
22 | except ValueError:
23 | return None, None
24 | direct = '+'
25 | if sp > ep:
26 | sp, ep = ep, sp
27 | direct = '-'
28 | gene = data[3]
29 | dict[gene] = [chrn, sp, ep, direct]
30 | return sorted(chr_set), dict
31 |
32 | @staticmethod
33 | def read_agp(in_agp):
34 | dict = {}
35 | with open(in_agp, 'r') as fin:
36 | for line in fin:
37 | data = line.strip().split()
38 | if len(line.strip()) == 0 or line[0] == '#' or data[4] == 'U' or len(data) < 8:
39 | continue
40 | chr_x = data[0]
41 | try:
42 | sp = int(float(data[1]))
43 | ep = int(float(data[2]))
44 | ctg_len = int(data[7])
45 | except ValueError:
46 | return None
47 | ctg = data[5]
48 | direct = data[-1]
49 | if chr_x not in dict:
50 | dict[chr_x] = []
51 | dict[chr_x].append([sp, ep, ctg, ctg_len, direct])
52 | return dict
53 |
54 | @staticmethod
55 | def read_anchors(in_anchors):
56 | gene_pairs = []
57 | with open(in_anchors, 'r') as fin:
58 | for line in fin:
59 | if len(line.strip()) == 0 or line[0] == '#':
60 | continue
61 | data = line.strip().split()
62 | if len(data) < 2:
63 | return None
64 | gene_pairs.append([data[0], data[1]])
65 | return gene_pairs
66 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/main/__init__.py:
--------------------------------------------------------------------------------
1 | import coll_asm_corr_gui.main.assembly_corrector_main
2 | import coll_asm_corr_gui.main.file_loader_dialog
3 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/main/assembly_corrector_main.py:
--------------------------------------------------------------------------------
1 | from os import path
2 | import sys
3 | from coll_asm_corr_gui.main import file_loader_dialog
4 | from coll_asm_corr_gui.io import file_reader
5 | from coll_asm_corr_gui.ui import ui_assembly_corrector_main, custom_control
6 | from coll_asm_corr_gui.corrector import locator, vis, corrector
7 | from copy import deepcopy
8 | from traceback import format_exc
9 | from PySide6.QtWidgets import QWidget, QFileDialog, QMessageBox
10 | from PySide6.QtCore import QCoreApplication, QEventLoop
11 |
12 |
13 | class OptArgs:
14 |
15 | def __init__(self):
16 | self.src_chr = ""
17 | self.src_blk = ""
18 | self.tgt_chr = ""
19 | self.tgt_blk = ""
20 | self.is_rev = False
21 |
22 |
23 | class AssemblyCorrectorMain(QWidget):
24 |
25 | def __init__(self):
26 | super(AssemblyCorrectorMain, self).__init__()
27 | self.ui = None
28 | self.main_window = None
29 | self.graph_scene = None
30 |
31 | self.opt_method_db = {
32 | "Insert front": self.__ins_front,
33 | "Insert back": self.__ins_back,
34 | "Insert head": self.__ins_head,
35 | "Insert tail": self.__ins_tail,
36 | "Source chromosome": self.__rev_chr,
37 | "Source block": self.__rev_blk,
38 | "Swap chromosome": self.__swp_chr,
39 | "Swap block": self.__swp_blk,
40 | "Delete block": self.__remove_blk
41 | }
42 | self.opt_method_info = {
43 | "Insert front": "Inserting",
44 | "Insert back": "Inserting",
45 | "Insert head": "Inserting",
46 | "Insert tail": "Inserting",
47 | "Source chromosome": "Reversing",
48 | "Source block": "Reversing",
49 | "Swap chromosome": "Swapping",
50 | "Swap block": "Swapping",
51 | "Delete block": "Removing"
52 | }
53 | # File paths and names
54 | self.qry_bed_file = ""
55 | self.ref_bed_file = ""
56 | self.anchors_file = ""
57 | self.qry_agp_file = ""
58 | self.qry_name = ""
59 | self.ref_name = ""
60 |
61 | # Datas
62 | self.qry_chr_list = None
63 | self.qry_bed_db = None
64 | self.last_bed_db = None
65 | self.ref_bed_db = None
66 | self.gene_pairs = None
67 | self.qry_agp_db = None
68 | self.last_agp_db = None
69 | self.block_regions = None
70 | self.block_list_db = None
71 | self.block_detail = None
72 |
73 | # mpl_vis for generate collinearity figure
74 | self.mpl_vis = vis.VisContent()
75 |
76 | # corrector for all adjust operations
77 | self.corrector = corrector.Corrector()
78 |
79 | # reader for data
80 | self.reader = file_reader.Reader()
81 |
82 | self.__init_ui()
83 |
84 | def __init_ui(self):
85 | self.ui = ui_assembly_corrector_main.Ui_AssemblyCorrectorMain()
86 | self.ui.setupUi(self)
87 |
88 | self.ui.method_cbox.addItems(self.opt_method_db.keys())
89 | self.ui.file_loader_btn.clicked.connect(self.__load_file_loader)
90 | self.ui.file_save_btn.clicked.connect(self.__save_files)
91 | self.ui.refresh_btn.clicked.connect(self.__refresh)
92 | self.ui.undo_btn.clicked.connect(self.__undo_modify)
93 | self.ui.mod_btn.clicked.connect(self.__modify)
94 | self.ui.src_chr_cbox.currentTextChanged.connect(self.__add_src_blks)
95 | self.ui.tgt_chr_cbox.currentTextChanged.connect(self.__add_tgt_blks)
96 | self.ui.src_blk_cbox.currentTextChanged.connect(self.__add_blk_lst)
97 |
98 | # Functions below are used for loading data and generating figure
99 | def __load_file_loader(self):
100 | file_loader = file_loader_dialog.FileLoaderDialog(self)
101 | self.__notify_with_title("Select files")
102 | file_loader.signal_path.connect(self.__get_file_path)
103 | file_loader.setModal(True)
104 | file_loader.show()
105 |
106 | def __get_file_path(self, content):
107 | if content:
108 | self.qry_bed_file, self.ref_bed_file, self.anchors_file, self.qry_agp_file = content
109 | if path.isfile(self.qry_bed_file) and path.isfile(self.ref_bed_file) and \
110 | path.isfile(self.anchors_file) and path.isfile(self.qry_agp_file):
111 | self.__notify_with_title("Loading files")
112 |
113 | if self.__load_file():
114 | self.__add_options()
115 | status = self.__show_pic()
116 | if status == -1:
117 | return
118 | self.__notify_with_title("Files loaded")
119 | else:
120 | QMessageBox.critical(self, 'Error', 'Cannot load files, please check input files!')
121 | self.__notify_with_title("Files load failed")
122 | return
123 | self.__enable_controls()
124 | else:
125 | self.__notify_with_title()
126 | else:
127 | self.__notify_with_title()
128 |
129 | def __load_file(self):
130 | if '/' in self.qry_bed_file:
131 | sep = '/'
132 | else:
133 | sep = '\\'
134 | self.qry_name = self.qry_bed_file.split(sep)[-1].split('.')[0]
135 | self.ref_name = self.ref_bed_file.split(sep)[-1].split('.')[0]
136 | try:
137 | self.qry_chr_list, self.qry_bed_db = self.reader.read_bed(self.qry_bed_file)
138 | if not self.qry_bed_db:
139 | return False
140 |
141 | _, self.ref_bed_db = self.reader.read_bed(self.ref_bed_file)
142 | if not self.ref_bed_db:
143 | return False
144 |
145 | self.gene_pairs = self.reader.read_anchors(self.anchors_file)
146 | if not self.gene_pairs:
147 | return False
148 |
149 | self.qry_agp_db = self.reader.read_agp(self.qry_agp_file)
150 | if not self.qry_agp_db:
151 | return False
152 | except IndexError:
153 | return False
154 | return True
155 |
156 | def __save_files(self):
157 | self.__notify_with_title("Saving files")
158 | folder_path = QFileDialog.getExistingDirectory(self, "Select folder")
159 | if folder_path and path.isdir(folder_path):
160 | self.__notify_with_title("Saving tour files")
161 | for chrn in self.qry_agp_db:
162 | tour_file = path.join(folder_path, '%s.tour' % chrn)
163 | with open(tour_file, 'w') as fout:
164 | tour_list = []
165 | for _, _, ctg, _, direct in self.qry_agp_db[chrn]:
166 | tour_list.append("%s%s" % (ctg, direct))
167 | fout.write("%s" % ' '.join(tour_list))
168 |
169 | self.__notify_with_title("Saving figure")
170 | fig_file = path.join(folder_path, "%s.%s.pdf" % (self.qry_name, self.ref_name))
171 | self.mpl_vis.figure_content.plt.savefig(fig_file, bbox_inches='tight')
172 |
173 | self.__notify_with_title("Saving blocks")
174 | block_file = path.join(folder_path, "contig_blocks.txt")
175 | with open(block_file, 'w') as fout:
176 | for chrn in sorted(self.mpl_vis.block_list_db):
177 | for idx in self.mpl_vis.block_list_db[chrn]:
178 | idx = int(idx) - 1
179 | fout.write(">%s_Block_%d\n%s\n" % (chrn, idx + 1, ' '.join(self.mpl_vis.block_detail[idx])))
180 |
181 | QMessageBox.information(self, "Save files", "Tour files saved.")
182 | self.__notify_with_title("Success")
183 | else:
184 | self.__notify_with_title("Nothing saved")
185 |
186 | def __show_pic(self):
187 | QCoreApplication.processEvents(QEventLoop.AllEvents)
188 | try:
189 | self.__notify_with_title("Drawing")
190 | blk_loc = locator.Locator()
191 | blk_loc.convert_anchors(self.qry_bed_db, self.ref_bed_db, self.gene_pairs)
192 | if self.ui.resolution_text.text():
193 | resolution = int(float(self.ui.resolution_text.text()))
194 | else:
195 | resolution = 20
196 | if resolution == 0 or resolution == 20:
197 | resolution = 20
198 | self.ui.resolution_text.setText("20")
199 | blk_loc.get_break_blocks(resolution)
200 |
201 | self.mpl_vis.gen_figure(blk_loc.links, blk_loc.block_db, self.qry_agp_db, resolution,
202 | self.qry_name, self.ref_name)
203 |
204 | self.block_regions = self.mpl_vis.block_regions
205 | self.block_list_db = self.mpl_vis.block_list_db
206 | self.block_detail = self.mpl_vis.block_detail
207 |
208 | self.ui.src_blk_cbox.clear()
209 | src_chr = self.ui.src_chr_cbox.currentText()
210 | if src_chr in self.block_list_db:
211 | self.ui.src_blk_cbox.addItems(self.block_list_db[src_chr])
212 |
213 | self.ui.blk_lst.clear()
214 | self.ui.blk_lst.addItems(self.block_detail[0])
215 |
216 | self.ui.tgt_blk_cbox.clear()
217 | if self.ui.tgt_chr_cbox.currentText() in self.block_list_db:
218 | self.ui.tgt_blk_cbox.addItems(self.block_list_db[self.ui.tgt_chr_cbox.currentText()])
219 | if not self.graph_scene:
220 | self.graph_scene = custom_control.ControlGraphicsScene()
221 | self.graph_scene.addWidget(self.mpl_vis.figure_content)
222 | self.ui.plot_viewer.setScene(self.graph_scene)
223 | self.ui.plot_viewer.show()
224 | else:
225 | self.mpl_vis.figure_content.draw()
226 | QCoreApplication.processEvents(QEventLoop.AllEvents)
227 | self.__notify_with_title("Success")
228 | except Exception as e:
229 | QMessageBox.critical(self, "Show picture failed", format_exc())
230 | self.__notify_with_title("Draw failed by %s" % repr(e))
231 | return -1
232 | return 0
233 |
234 | # Functions below are used for controlling UI
235 | def __enable_controls(self):
236 | self.ui.file_save_btn.setEnabled(True)
237 | self.ui.blk_lst.setEnabled(True)
238 | self.ui.undo_btn.setEnabled(True)
239 | self.ui.mod_btn.setEnabled(True)
240 | self.ui.refresh_btn.setEnabled(True)
241 | self.ui.src_chr_cbox.setEnabled(True)
242 | self.ui.src_blk_cbox.setEnabled(True)
243 | self.ui.tgt_chr_cbox.setEnabled(True)
244 | self.ui.tgt_blk_cbox.setEnabled(True)
245 | self.ui.method_cbox.setEnabled(True)
246 | self.ui.rev_chk.setEnabled(True)
247 | self.ui.plot_viewer.setEnabled(True)
248 |
249 | def __enable_buttons(self):
250 | self.ui.mod_btn.setEnabled(True)
251 | self.ui.undo_btn.setEnabled(True)
252 | self.ui.refresh_btn.setEnabled(True)
253 | QCoreApplication.processEvents(QEventLoop.AllEvents)
254 |
255 | def __disable_buttons(self):
256 | self.ui.mod_btn.setEnabled(False)
257 | self.ui.undo_btn.setEnabled(False)
258 | self.ui.refresh_btn.setEnabled(False)
259 | QCoreApplication.processEvents(QEventLoop.AllEvents)
260 |
261 | def __add_options(self):
262 | self.ui.src_chr_cbox.addItems(self.qry_chr_list)
263 | self.ui.tgt_chr_cbox.addItems(self.qry_chr_list)
264 |
265 | def __add_src_blks(self, value):
266 | self.ui.src_blk_cbox.clear()
267 | if self.block_list_db and value in self.block_list_db:
268 | self.ui.src_blk_cbox.addItems(self.block_list_db[value])
269 |
270 | def __add_tgt_blks(self, value):
271 | self.ui.tgt_blk_cbox.clear()
272 | if self.block_list_db and value in self.block_list_db:
273 | self.ui.tgt_blk_cbox.addItems(self.block_list_db[value])
274 |
275 | def __add_blk_lst(self, value):
276 | self.ui.blk_lst.clear()
277 | if value:
278 | self.ui.blk_lst.addItems(self.block_detail[int(value) - 1])
279 |
280 | def __notify_with_title(self, info=""):
281 | if info:
282 | self.setWindowTitle("CATG - %s" % info)
283 | else:
284 | self.setWindowTitle("CATG")
285 |
286 | def closeEvent(self, event):
287 | sys.exit(0)
288 |
289 | # Functions below are used for adjusting collinearity blocks
290 | def __modify(self):
291 |
292 | self.ui.mod_btn.setText("Modifying...")
293 | self.__disable_buttons()
294 | args = OptArgs()
295 |
296 | args.src_chr = self.ui.src_chr_cbox.currentText()
297 | args.src_blk = int(self.ui.src_blk_cbox.currentText()) - 1
298 | args.tgt_chr = self.ui.tgt_chr_cbox.currentText()
299 | args.tgt_blk = int(self.ui.tgt_blk_cbox.currentText()) - 1
300 | args.is_rev = self.ui.rev_chk.isChecked()
301 | opt = self.ui.method_cbox.currentText()
302 | try:
303 | if opt in self.opt_method_db:
304 | self.__notify_with_title(self.opt_method_info[opt])
305 |
306 | self.last_agp_db = deepcopy(self.qry_agp_db)
307 | self.last_bed_db = deepcopy(self.qry_bed_db)
308 | self.opt_method_db[opt](args)
309 | self.__show_pic()
310 | self.ui.mod_btn.setText("Modify")
311 | self.__enable_buttons()
312 | self.__notify_with_title("Success")
313 | except Exception as e:
314 | QMessageBox.critical(self, "Modify failed", format_exc())
315 | self.__notify_with_title(self.opt_method_info[opt] + " Failed with: %s" % repr(e))
316 | return
317 |
318 | def __undo_modify(self):
319 | self.ui.undo_btn.setText("Restoring")
320 | self.__disable_buttons()
321 | if self.last_agp_db:
322 | self.__notify_with_title("Restoring last status")
323 | tmp_db = deepcopy(self.qry_agp_db)
324 | self.qry_agp_db = deepcopy(self.last_agp_db)
325 | self.last_agp_db = deepcopy(tmp_db)
326 | tmp_db = deepcopy(self.qry_bed_db)
327 | self.qry_bed_db = deepcopy(self.last_bed_db)
328 | self.last_bed_db = deepcopy(tmp_db)
329 | del tmp_db
330 | self.__show_pic()
331 | self.__enable_buttons()
332 | self.ui.undo_btn.setText("Undo")
333 | self.__notify_with_title("Success")
334 | else:
335 | self.__notify_with_title("Unable restore")
336 |
337 | def __refresh(self):
338 | self.ui.refresh_btn.setText("Refreshing")
339 | self.__disable_buttons()
340 | self.__show_pic()
341 | self.__enable_buttons()
342 | self.ui.refresh_btn.setText("Refresh")
343 |
344 | def __rev_chr(self, args):
345 | if not args.is_rev:
346 | return
347 | tmp_dict = deepcopy(self.qry_agp_db)
348 | tmp_dict[args.src_chr] = self.corrector.reverse_chr(tmp_dict[args.src_chr])
349 | self.qry_bed_db = self.corrector.trans_anno(self.qry_agp_db, tmp_dict, self.qry_bed_db)
350 | self.qry_agp_db = deepcopy(tmp_dict)
351 | del tmp_dict
352 |
353 | def __rev_blk(self, args):
354 | if not args.is_rev:
355 | return
356 | tmp_dict = deepcopy(self.qry_agp_db)
357 | tmp_dict[args.src_chr] = self.corrector.reverse_block(tmp_dict[args.src_chr], self.block_regions[args.src_blk])
358 | self.qry_bed_db = self.corrector.trans_anno(self.qry_agp_db, tmp_dict, self.qry_bed_db)
359 | self.qry_agp_db = deepcopy(tmp_dict)
360 | del tmp_dict
361 |
362 | def __ins_head(self, args):
363 | tmp_dict = deepcopy(self.qry_agp_db)
364 | tmp_dict[args.src_chr], extract_agp_list = self.corrector.split_block(tmp_dict[args.src_chr],
365 | self.block_regions[args.src_blk])
366 | if args.is_rev:
367 | extract_agp_list = self.corrector.reverse_chr(extract_agp_list)
368 |
369 | tmp_dict[args.tgt_chr] = self.corrector.ins_term(tmp_dict[args.tgt_chr], extract_agp_list)
370 | self.qry_bed_db = self.corrector.trans_anno(self.qry_agp_db, tmp_dict, self.qry_bed_db)
371 | self.qry_agp_db = deepcopy(tmp_dict)
372 | del tmp_dict
373 |
374 | def __ins_tail(self, args):
375 | tmp_dict = deepcopy(self.qry_agp_db)
376 | tmp_dict[args.src_chr], extract_agp_list = self.corrector.split_block(tmp_dict[args.src_chr],
377 | self.block_regions[args.src_blk])
378 | if args.is_rev:
379 | extract_agp_list = self.corrector.reverse_chr(extract_agp_list)
380 |
381 | tmp_dict[args.tgt_chr] = self.corrector.ins_term(tmp_dict[args.tgt_chr], extract_agp_list, False)
382 | self.qry_bed_db = self.corrector.trans_anno(self.qry_agp_db, tmp_dict, self.qry_bed_db)
383 | self.qry_agp_db = deepcopy(tmp_dict)
384 | del tmp_dict
385 |
386 | def __ins_front(self, args):
387 | tmp_dict = deepcopy(self.qry_agp_db)
388 | tmp_dict[args.src_chr], extract_agp_list = self.corrector.split_block(tmp_dict[args.src_chr],
389 | self.block_regions[args.src_blk])
390 | if args.is_rev:
391 | extract_agp_list = self.corrector.reverse_chr(extract_agp_list)
392 |
393 | tmp_dict[args.tgt_chr] = self.corrector.ins_pos(tmp_dict[args.tgt_chr], extract_agp_list,
394 | self.block_regions[args.tgt_blk])
395 | self.qry_bed_db = self.corrector.trans_anno(self.qry_agp_db, tmp_dict, self.qry_bed_db)
396 | self.qry_agp_db = deepcopy(tmp_dict)
397 | del tmp_dict
398 |
399 | def __ins_back(self, args):
400 | tmp_dict = deepcopy(self.qry_agp_db)
401 | tmp_dict[args.src_chr], extract_agp_list = self.corrector.split_block(tmp_dict[args.src_chr],
402 | self.block_regions[args.src_blk])
403 | if args.is_rev:
404 | extract_agp_list = self.corrector.reverse_chr(extract_agp_list)
405 |
406 | tmp_dict[args.tgt_chr] = self.corrector.ins_pos(tmp_dict[args.tgt_chr], extract_agp_list,
407 | self.block_regions[args.tgt_blk],
408 | False)
409 | self.qry_bed_db = self.corrector.trans_anno(self.qry_agp_db, tmp_dict, self.qry_bed_db)
410 | self.qry_agp_db = deepcopy(tmp_dict)
411 | del tmp_dict
412 |
413 | def __swp_chr(self, args):
414 | if args.src_chr != args.tgt_chr:
415 | tmp_dict = deepcopy(self.qry_agp_db)
416 | tmp_list = deepcopy(tmp_dict[args.src_chr])
417 | tmp_dict[args.src_chr] = deepcopy(tmp_dict[args.tgt_chr])
418 | tmp_dict[args.tgt_chr] = deepcopy(tmp_list)
419 |
420 | self.qry_bed_db = self.corrector.trans_anno(self.qry_agp_db, tmp_dict, self.qry_bed_db)
421 | self.qry_agp_db = deepcopy(tmp_dict)
422 | del tmp_dict, tmp_list
423 |
424 | def __swp_blk(self, args):
425 | if args.src_blk != args.tgt_blk:
426 | if args.src_chr != args.tgt_chr:
427 | tmp_dict = deepcopy(self.qry_agp_db)
428 | tmp_dict[args.tgt_chr], tmp_dict[args.src_chr] = self.corrector.swap_blk_diff_chr(
429 | tmp_dict[args.src_chr],
430 | self.block_regions[
431 | args.src_blk],
432 | tmp_dict[args.tgt_chr],
433 | self.block_regions[
434 | args.tgt_blk])
435 |
436 | self.qry_bed_db = self.corrector.trans_anno(self.qry_agp_db, tmp_dict, self.qry_bed_db)
437 | self.qry_agp_db = deepcopy(tmp_dict)
438 |
439 | del tmp_dict
440 | else:
441 | tmp_dict = deepcopy(self.qry_agp_db)
442 | tmp_dict[args.src_chr] = self.corrector.swap_blk_single_chr(tmp_dict[args.src_chr],
443 | self.block_regions[args.src_blk],
444 | self.block_regions[args.tgt_blk])
445 | if not tmp_dict[args.src_chr]:
446 | return
447 | self.qry_bed_db = self.corrector.trans_anno(self.qry_agp_db, tmp_dict, self.qry_bed_db)
448 | self.qry_agp_db = deepcopy(tmp_dict)
449 |
450 | del tmp_dict
451 |
452 | def __remove_blk(self, args):
453 | tmp_dict = deepcopy(self.qry_agp_db)
454 | tmp_dict[args.src_chr] = self.corrector.remove_blk(tmp_dict[args.src_chr], self.block_regions[args.src_blk])
455 | self.qry_bed_db = self.corrector.trans_anno(self.qry_agp_db, tmp_dict, self.qry_bed_db)
456 | self.qry_agp_db = deepcopy(tmp_dict)
457 |
458 | del tmp_dict
459 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/main/file_loader_dialog.py:
--------------------------------------------------------------------------------
1 | from PySide6.QtWidgets import QDialog, QFileDialog, QDialogButtonBox
2 | from PySide6.QtCore import Signal
3 | from coll_asm_corr_gui.ui.ui_file_loader_dialog import Ui_FileLoaderDialog
4 |
5 |
6 | class FileLoaderDialog(QDialog):
7 |
8 | signal_path = Signal(list)
9 |
10 | def __init__(self, parent=None):
11 | super(FileLoaderDialog, self).__init__(parent)
12 | self.qry_bed_file = ""
13 | self.ref_bed_file = ""
14 | self.anchors_file = ""
15 | self.qry_agp_file = ""
16 | self.ui = None
17 | self.init_ui()
18 |
19 | def init_ui(self):
20 | self.ui = Ui_FileLoaderDialog()
21 | self.ui.setupUi(self)
22 | self.ui.qry_bed_btn.clicked.connect(self.load_qry_bed)
23 | self.ui.ref_bed_btn.clicked.connect(self.load_ref_bed)
24 | self.ui.anchors_btn.clicked.connect(self.load_anchors)
25 | self.ui.qry_agp_btn.clicked.connect(self.load_qry_agp)
26 | self.ui.check_btn.button(QDialogButtonBox.Ok).clicked.connect(self.send_path)
27 | self.ui.check_btn.button(QDialogButtonBox.Cancel).clicked.connect(self.send_cancel)
28 |
29 | def load_qry_bed(self):
30 | self.qry_bed_file = QFileDialog.getOpenFileName(self, "Select query bed file",
31 | filter="bed files(*.bed);;all files(*.*)")[0]
32 | self.ui.qry_bed_text.setText(self.qry_bed_file)
33 |
34 | def load_ref_bed(self):
35 | self.ref_bed_file = QFileDialog.getOpenFileName(self, "Select reference bed file",
36 | filter="bed files(*.bed);;all files(*.*)")[0]
37 | self.ui.ref_bed_text.setText(self.ref_bed_file)
38 |
39 | def load_anchors(self):
40 | self.anchors_file = QFileDialog.getOpenFileName(self, "Select anchors file",
41 | filter="anchors files(*.anchors);;all files(*.*)")[0]
42 | self.ui.anchors_text.setText(self.anchors_file)
43 |
44 | def load_qry_agp(self):
45 | self.qry_agp_file = QFileDialog.getOpenFileName(self, "Select query agp file",
46 | filter="agp files(*.agp);;all files(*.*)")[0]
47 | self.ui.qry_agp_text.setText(self.qry_agp_file)
48 |
49 | def send_path(self):
50 | self.qry_bed_file = self.ui.qry_bed_text.text()
51 | self.ref_bed_file = self.ui.ref_bed_text.text()
52 | self.anchors_file = self.ui.anchors_text.text()
53 | self.qry_agp_file = self.ui.qry_agp_text.text()
54 | content = [self.qry_bed_file, self.ref_bed_file, self.anchors_file, self.qry_agp_file]
55 | self.signal_path.emit(content)
56 |
57 | def send_cancel(self):
58 | self.signal_path.emit(None)
59 |
60 | def closeEvent(self, event):
61 | self.signal_path.emit(None)
--------------------------------------------------------------------------------
/coll_asm_corr_gui/resources/CATG.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sc-zhang/CATG/606d7f23fe713f445e2af61d3766958bc2dfa072/coll_asm_corr_gui/resources/CATG.icns
--------------------------------------------------------------------------------
/coll_asm_corr_gui/resources/CATG.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sc-zhang/CATG/606d7f23fe713f445e2af61d3766958bc2dfa072/coll_asm_corr_gui/resources/CATG.ico
--------------------------------------------------------------------------------
/coll_asm_corr_gui/resources/CATG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sc-zhang/CATG/606d7f23fe713f445e2af61d3766958bc2dfa072/coll_asm_corr_gui/resources/CATG.png
--------------------------------------------------------------------------------
/coll_asm_corr_gui/resources/assembly_corrector_main.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | AssemblyCorrectorMain
4 |
5 |
6 |
7 | 0
8 | 0
9 | 1936
10 | 1147
11 |
12 |
13 |
14 | CATG
15 |
16 |
17 | -
18 |
19 |
20 | false
21 |
22 |
23 |
24 | -
25 |
26 |
27 |
28 | 0
29 | 0
30 |
31 |
32 |
33 |
-
34 |
35 |
36 |
37 | 12
38 |
39 |
40 |
41 | Target chromosome:
42 |
43 |
44 |
45 | -
46 |
47 |
48 | false
49 |
50 |
51 |
52 | -
53 |
54 |
55 | false
56 |
57 |
58 |
59 | -
60 |
61 |
62 |
63 | 0
64 | 0
65 |
66 |
67 |
68 |
69 | 0
70 | 0
71 |
72 |
73 |
74 | QFrame::Plain
75 |
76 |
77 | 2
78 |
79 |
80 | Qt::Horizontal
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 | 12
89 |
90 |
91 |
92 | Source block id:
93 |
94 |
95 |
96 | -
97 |
98 |
99 | QFrame::Plain
100 |
101 |
102 | 2
103 |
104 |
105 | Qt::Horizontal
106 |
107 |
108 |
109 | -
110 |
111 |
112 | false
113 |
114 |
115 |
116 | -
117 |
118 |
119 | false
120 |
121 |
122 |
123 | -
124 |
125 |
-
126 |
127 |
128 |
129 | 0
130 | 0
131 |
132 |
133 |
134 |
135 | 16
136 | true
137 |
138 |
139 |
140 | Load files
141 |
142 |
143 |
144 | -
145 |
146 |
147 | false
148 |
149 |
150 |
151 | 0
152 | 0
153 |
154 |
155 |
156 |
157 | 16
158 | true
159 |
160 |
161 |
162 | Save files
163 |
164 |
165 |
166 |
167 |
168 | -
169 |
170 |
171 |
172 | 12
173 |
174 |
175 |
176 | Resolution:
177 |
178 |
179 |
180 | -
181 |
182 |
183 | 20
184 |
185 |
186 |
187 | -
188 |
189 |
190 | false
191 |
192 |
193 | Reverse
194 |
195 |
196 |
197 | -
198 |
199 |
200 |
201 | 12
202 | true
203 |
204 |
205 |
206 | Contigs in current block:
207 |
208 |
209 |
210 | -
211 |
212 |
213 |
214 | 12
215 |
216 |
217 |
218 | Source chromosome:
219 |
220 |
221 |
222 | -
223 |
224 |
-
225 |
226 |
227 | false
228 |
229 |
230 |
231 | 0
232 | 0
233 |
234 |
235 |
236 |
237 | 16
238 | true
239 |
240 |
241 |
242 | Undo
243 |
244 |
245 |
246 | -
247 |
248 |
249 | false
250 |
251 |
252 |
253 | 0
254 | 0
255 |
256 |
257 |
258 |
259 | 16
260 | true
261 |
262 |
263 |
264 | Refresh
265 |
266 |
267 |
268 | -
269 |
270 |
271 | false
272 |
273 |
274 |
275 | 0
276 | 0
277 |
278 |
279 |
280 |
281 | 16
282 | true
283 |
284 |
285 |
286 | Modify
287 |
288 |
289 |
290 |
291 |
292 | -
293 |
294 |
295 | QFrame::Plain
296 |
297 |
298 | 2
299 |
300 |
301 | Qt::Horizontal
302 |
303 |
304 |
305 | -
306 |
307 |
308 | false
309 |
310 |
311 |
312 | -
313 |
314 |
315 | false
316 |
317 |
318 |
319 | -
320 |
321 |
322 |
323 | 12
324 |
325 |
326 |
327 | Target block id:
328 |
329 |
330 |
331 | -
332 |
333 |
334 |
335 | 12
336 | true
337 |
338 |
339 |
340 | Operations:
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/resources/file_loader_dialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | FileLoaderDialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 640
10 | 250
11 |
12 |
13 |
14 |
15 | 0
16 | 0
17 |
18 |
19 |
20 |
21 | 640
22 | 250
23 |
24 |
25 |
26 |
27 | 640
28 | 250
29 |
30 |
31 |
32 | File loader
33 |
34 |
35 | -
36 |
37 |
-
38 |
39 |
40 |
41 | 0
42 | 0
43 |
44 |
45 |
46 |
47 | 16777215
48 | 30
49 |
50 |
51 |
52 | Query bed file:
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 | 0
61 | 0
62 |
63 |
64 |
65 | true
66 |
67 |
68 |
69 | -
70 |
71 |
72 |
73 | 0
74 | 0
75 |
76 |
77 |
78 |
79 | 16777215
80 | 30
81 |
82 |
83 |
84 | ...
85 |
86 |
87 |
88 | -
89 |
90 |
91 |
92 | 16777215
93 | 30
94 |
95 |
96 |
97 | Reference bed file:
98 |
99 |
100 |
101 | -
102 |
103 |
104 |
105 | 0
106 | 0
107 |
108 |
109 |
110 | true
111 |
112 |
113 |
114 | -
115 |
116 |
117 |
118 | 0
119 | 0
120 |
121 |
122 |
123 |
124 | 16777215
125 | 30
126 |
127 |
128 |
129 | ...
130 |
131 |
132 |
133 | -
134 |
135 |
136 |
137 | 16777215
138 | 30
139 |
140 |
141 |
142 | Anchors file:
143 |
144 |
145 |
146 | -
147 |
148 |
149 |
150 | 0
151 | 0
152 |
153 |
154 |
155 | true
156 |
157 |
158 |
159 | -
160 |
161 |
162 |
163 | 0
164 | 0
165 |
166 |
167 |
168 |
169 | 16777215
170 | 30
171 |
172 |
173 |
174 | ...
175 |
176 |
177 |
178 | -
179 |
180 |
181 |
182 | 16777215
183 | 30
184 |
185 |
186 |
187 | Query AGP file:
188 |
189 |
190 |
191 | -
192 |
193 |
194 |
195 | 0
196 | 0
197 |
198 |
199 |
200 | true
201 |
202 |
203 |
204 | -
205 |
206 |
207 |
208 | 0
209 | 0
210 |
211 |
212 |
213 |
214 | 16777215
215 | 30
216 |
217 |
218 |
219 | ...
220 |
221 |
222 |
223 |
224 |
225 | -
226 |
227 |
228 | Qt::Horizontal
229 |
230 |
231 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 | check_btn
241 | accepted()
242 | FileLoaderDialog
243 | accept()
244 |
245 |
246 | 248
247 | 254
248 |
249 |
250 | 157
251 | 274
252 |
253 |
254 |
255 |
256 | check_btn
257 | rejected()
258 | FileLoaderDialog
259 | reject()
260 |
261 |
262 | 316
263 | 260
264 |
265 |
266 | 286
267 | 274
268 |
269 |
270 |
271 |
272 |
273 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/ui/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/ui/custom_control.py:
--------------------------------------------------------------------------------
1 | from PySide6.QtWidgets import QGraphicsView, QGraphicsScene, QLineEdit
2 | from PySide6.QtCore import Qt, QPoint
3 |
4 |
5 | class ControlGraphicsView(QGraphicsView):
6 |
7 | def __init__(self, parent=None):
8 | super(ControlGraphicsView, self).__init__()
9 | self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
10 | self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
11 | self.setDragMode(QGraphicsView.ScrollHandDrag)
12 | self.setAcceptDrops(True)
13 | self.__zoom = 5
14 |
15 | def wheelEvent(self, event):
16 | delta = event.angleDelta().y()
17 | if delta == 0:
18 | pass
19 | if delta > 0 and self.__zoom >= 10:
20 | return
21 | elif delta < 0 and self.__zoom <= 0:
22 | return
23 | else:
24 | cur_point = event.position()
25 | scene_pos = self.mapToScene(QPoint(cur_point.x(), cur_point.y()))
26 |
27 | view_width = self.viewport().width()
28 | view_height = self.viewport().height()
29 |
30 | h_scale = cur_point.x() / view_width
31 | v_scale = cur_point.y() / view_height
32 |
33 | if delta > 0:
34 | self.scale(1.25, 1.25)
35 | self.__zoom += 1
36 | elif delta < 0:
37 | self.scale(0.8, 0.8)
38 | self.__zoom -= 1
39 | view_point = self.transform().map(scene_pos)
40 | self.horizontalScrollBar().setValue(int(view_point.x() - view_width * h_scale))
41 | self.verticalScrollBar().setValue(int(view_point.y() - view_height * v_scale))
42 |
43 | self.update()
44 |
45 |
46 | class ControlGraphicsScene(QGraphicsScene):
47 | def __init__(self, parent=None):
48 | super(ControlGraphicsScene, self).__init__()
49 |
50 | self.__left_click = False
51 | self.__point = QPoint(0, 0)
52 | self.__start_pos = None
53 | self.__end_pos = None
54 |
55 | def mouseMoveEvent(self, e):
56 | if self.__left_click:
57 | self.__end_pos = e.pos() - self.__start_pos
58 | self.__point = self.__point + self.__end_pos
59 | self.__start_pos = e.pos()
60 | self.repaint()
61 |
62 | def mousePressEvent(self, e):
63 | if e.button() == Qt.LeftButton:
64 | self.__left_click = True
65 | self.__start_pos = e.pos()
66 |
67 | def mouseReleaseEvent(self, e):
68 | if e.button() == Qt.LeftButton:
69 | self.__left_click = False
70 |
71 |
72 | class DragLineEdit(QLineEdit):
73 |
74 | def __init__(self, parent=None):
75 | super(DragLineEdit, self).__init__(parent)
76 |
77 | def dragEnterEvent(self, event):
78 | if event.mimeData().hasUrls():
79 | event.acceptProposedAction()
80 | else:
81 | event.ignore()
82 |
83 | def dropEvent(self, event):
84 | if event.mimeData().hasUrls():
85 | self.setText(event.mimeData().urls()[0].toLocalFile())
86 | event.accept()
87 | else:
88 | event.ignore()
89 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/ui/ui_assembly_corrector_main.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | ################################################################################
4 | ## Form generated from reading UI file 'assembly_adjuster_mainSfJexb.ui'
5 | ##
6 | ## Created by: Qt User Interface Compiler version 6.4.0
7 | ##
8 | ## WARNING! All changes made in this file will be lost when recompiling UI file!
9 | ################################################################################
10 |
11 | from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
12 | QMetaObject, QObject, QPoint, QRect,
13 | QSize, QTime, QUrl, Qt)
14 | from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
15 | QFont, QFontDatabase, QGradient, QIcon,
16 | QImage, QKeySequence, QLinearGradient, QPainter,
17 | QPalette, QPixmap, QRadialGradient, QTransform)
18 | from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QFrame,
19 | QGraphicsView, QGridLayout, QHBoxLayout, QLabel,
20 | QLineEdit, QListWidget, QListWidgetItem, QPushButton,
21 | QSizePolicy, QWidget)
22 | from coll_asm_corr_gui.ui.custom_control import ControlGraphicsView
23 |
24 | class Ui_AssemblyCorrectorMain(object):
25 | def setupUi(self, AssemblyCorrectorMain):
26 | if not AssemblyCorrectorMain.objectName():
27 | AssemblyCorrectorMain.setObjectName(u"AssemblyAdjusterMain")
28 | AssemblyCorrectorMain.resize(1936, 1147)
29 | self.gridLayout_2 = QGridLayout(AssemblyCorrectorMain)
30 | self.gridLayout_2.setObjectName(u"gridLayout_2")
31 | self.plot_viewer = ControlGraphicsView(AssemblyCorrectorMain)
32 | self.plot_viewer.setObjectName(u"plot_viewer")
33 | self.plot_viewer.setEnabled(False)
34 |
35 | self.gridLayout_2.addWidget(self.plot_viewer, 0, 0, 1, 1)
36 |
37 | self.Frame = QFrame(AssemblyCorrectorMain)
38 | self.Frame.setObjectName(u"Frame")
39 | sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
40 | sizePolicy.setHorizontalStretch(0)
41 | sizePolicy.setVerticalStretch(0)
42 | sizePolicy.setHeightForWidth(self.Frame.sizePolicy().hasHeightForWidth())
43 | self.Frame.setSizePolicy(sizePolicy)
44 | self.gridLayout = QGridLayout(self.Frame)
45 | self.gridLayout.setObjectName(u"gridLayout")
46 | self.label_2 = QLabel(self.Frame)
47 | self.label_2.setObjectName(u"label_2")
48 | font = QFont()
49 | font.setPointSize(12)
50 | self.label_2.setFont(font)
51 |
52 | self.gridLayout.addWidget(self.label_2, 9, 0, 1, 1)
53 |
54 | self.blk_lst = QListWidget(self.Frame)
55 | self.blk_lst.setObjectName(u"blk_lst")
56 | self.blk_lst.setEnabled(False)
57 |
58 | self.gridLayout.addWidget(self.blk_lst, 4, 0, 1, 2)
59 |
60 | self.src_chr_cbox = QComboBox(self.Frame)
61 | self.src_chr_cbox.setObjectName(u"src_chr_cbox")
62 | self.src_chr_cbox.setEnabled(False)
63 |
64 | self.gridLayout.addWidget(self.src_chr_cbox, 7, 1, 1, 1)
65 |
66 | self.line = QFrame(self.Frame)
67 | self.line.setObjectName(u"line")
68 | sizePolicy1 = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
69 | sizePolicy1.setHorizontalStretch(0)
70 | sizePolicy1.setVerticalStretch(0)
71 | sizePolicy1.setHeightForWidth(self.line.sizePolicy().hasHeightForWidth())
72 | self.line.setSizePolicy(sizePolicy1)
73 | self.line.setSizeIncrement(QSize(0, 0))
74 | self.line.setFrameShadow(QFrame.Plain)
75 | self.line.setLineWidth(2)
76 | self.line.setFrameShape(QFrame.HLine)
77 |
78 | self.gridLayout.addWidget(self.line, 2, 0, 1, 2)
79 |
80 | self.label_3 = QLabel(self.Frame)
81 | self.label_3.setObjectName(u"label_3")
82 | self.label_3.setFont(font)
83 |
84 | self.gridLayout.addWidget(self.label_3, 8, 0, 1, 1)
85 |
86 | self.line_2 = QFrame(self.Frame)
87 | self.line_2.setObjectName(u"line_2")
88 | self.line_2.setFrameShadow(QFrame.Plain)
89 | self.line_2.setLineWidth(2)
90 | self.line_2.setFrameShape(QFrame.HLine)
91 |
92 | self.gridLayout.addWidget(self.line_2, 13, 0, 1, 2)
93 |
94 | self.method_cbox = QComboBox(self.Frame)
95 | self.method_cbox.setObjectName(u"method_cbox")
96 | self.method_cbox.setEnabled(False)
97 |
98 | self.gridLayout.addWidget(self.method_cbox, 12, 1, 1, 1)
99 |
100 | self.tgt_chr_cbox = QComboBox(self.Frame)
101 | self.tgt_chr_cbox.setObjectName(u"tgt_chr_cbox")
102 | self.tgt_chr_cbox.setEnabled(False)
103 |
104 | self.gridLayout.addWidget(self.tgt_chr_cbox, 9, 1, 1, 1)
105 |
106 | self.horizontalLayout_2 = QHBoxLayout()
107 | self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
108 | self.file_loader_btn = QPushButton(self.Frame)
109 | self.file_loader_btn.setObjectName(u"file_loader_btn")
110 | sizePolicy2 = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
111 | sizePolicy2.setHorizontalStretch(0)
112 | sizePolicy2.setVerticalStretch(0)
113 | sizePolicy2.setHeightForWidth(self.file_loader_btn.sizePolicy().hasHeightForWidth())
114 | self.file_loader_btn.setSizePolicy(sizePolicy2)
115 | font1 = QFont()
116 | font1.setPointSize(16)
117 | font1.setBold(True)
118 | self.file_loader_btn.setFont(font1)
119 |
120 | self.horizontalLayout_2.addWidget(self.file_loader_btn)
121 |
122 | self.file_save_btn = QPushButton(self.Frame)
123 | self.file_save_btn.setObjectName(u"file_save_btn")
124 | self.file_save_btn.setEnabled(False)
125 | sizePolicy2.setHeightForWidth(self.file_save_btn.sizePolicy().hasHeightForWidth())
126 | self.file_save_btn.setSizePolicy(sizePolicy2)
127 | self.file_save_btn.setFont(font1)
128 |
129 | self.horizontalLayout_2.addWidget(self.file_save_btn)
130 |
131 |
132 | self.gridLayout.addLayout(self.horizontalLayout_2, 1, 0, 1, 2)
133 |
134 | self.label_5 = QLabel(self.Frame)
135 | self.label_5.setObjectName(u"label_5")
136 | self.label_5.setFont(font)
137 |
138 | self.gridLayout.addWidget(self.label_5, 11, 0, 1, 1)
139 |
140 | self.resolution_text = QLineEdit(self.Frame)
141 | self.resolution_text.setObjectName(u"resolution_text")
142 |
143 | self.gridLayout.addWidget(self.resolution_text, 11, 1, 1, 1)
144 |
145 | self.rev_chk = QCheckBox(self.Frame)
146 | self.rev_chk.setObjectName(u"rev_chk")
147 | self.rev_chk.setEnabled(False)
148 |
149 | self.gridLayout.addWidget(self.rev_chk, 12, 0, 1, 1)
150 |
151 | self.label_6 = QLabel(self.Frame)
152 | self.label_6.setObjectName(u"label_6")
153 | font2 = QFont()
154 | font2.setPointSize(12)
155 | font2.setBold(True)
156 | self.label_6.setFont(font2)
157 |
158 | self.gridLayout.addWidget(self.label_6, 3, 0, 1, 1)
159 |
160 | self.label = QLabel(self.Frame)
161 | self.label.setObjectName(u"label")
162 | self.label.setFont(font)
163 |
164 | self.gridLayout.addWidget(self.label, 7, 0, 1, 1)
165 |
166 | self.horizontalLayout = QHBoxLayout()
167 | self.horizontalLayout.setObjectName(u"horizontalLayout")
168 | self.undo_btn = QPushButton(self.Frame)
169 | self.undo_btn.setObjectName(u"undo_btn")
170 | self.undo_btn.setEnabled(False)
171 | sizePolicy2.setHeightForWidth(self.undo_btn.sizePolicy().hasHeightForWidth())
172 | self.undo_btn.setSizePolicy(sizePolicy2)
173 | self.undo_btn.setFont(font1)
174 |
175 | self.horizontalLayout.addWidget(self.undo_btn)
176 |
177 | self.refresh_btn = QPushButton(self.Frame)
178 | self.refresh_btn.setObjectName(u"refresh_btn")
179 | self.refresh_btn.setEnabled(False)
180 | sizePolicy2.setHeightForWidth(self.refresh_btn.sizePolicy().hasHeightForWidth())
181 | self.refresh_btn.setSizePolicy(sizePolicy2)
182 | self.refresh_btn.setFont(font1)
183 |
184 | self.horizontalLayout.addWidget(self.refresh_btn)
185 |
186 | self.mod_btn = QPushButton(self.Frame)
187 | self.mod_btn.setObjectName(u"mod_btn")
188 | self.mod_btn.setEnabled(False)
189 | sizePolicy2.setHeightForWidth(self.mod_btn.sizePolicy().hasHeightForWidth())
190 | self.mod_btn.setSizePolicy(sizePolicy2)
191 | self.mod_btn.setFont(font1)
192 |
193 | self.horizontalLayout.addWidget(self.mod_btn)
194 |
195 |
196 | self.gridLayout.addLayout(self.horizontalLayout, 14, 0, 1, 2)
197 |
198 | self.line_3 = QFrame(self.Frame)
199 | self.line_3.setObjectName(u"line_3")
200 | self.line_3.setFrameShadow(QFrame.Plain)
201 | self.line_3.setLineWidth(2)
202 | self.line_3.setFrameShape(QFrame.HLine)
203 |
204 | self.gridLayout.addWidget(self.line_3, 5, 0, 1, 2)
205 |
206 | self.src_blk_cbox = QComboBox(self.Frame)
207 | self.src_blk_cbox.setObjectName(u"src_blk_cbox")
208 | self.src_blk_cbox.setEnabled(False)
209 |
210 | self.gridLayout.addWidget(self.src_blk_cbox, 8, 1, 1, 1)
211 |
212 | self.tgt_blk_cbox = QComboBox(self.Frame)
213 | self.tgt_blk_cbox.setObjectName(u"tgt_blk_cbox")
214 | self.tgt_blk_cbox.setEnabled(False)
215 |
216 | self.gridLayout.addWidget(self.tgt_blk_cbox, 10, 1, 1, 1)
217 |
218 | self.label_4 = QLabel(self.Frame)
219 | self.label_4.setObjectName(u"label_4")
220 | self.label_4.setFont(font)
221 |
222 | self.gridLayout.addWidget(self.label_4, 10, 0, 1, 1)
223 |
224 | self.label_7 = QLabel(self.Frame)
225 | self.label_7.setObjectName(u"label_7")
226 | self.label_7.setFont(font2)
227 |
228 | self.gridLayout.addWidget(self.label_7, 6, 0, 1, 1)
229 |
230 |
231 | self.gridLayout_2.addWidget(self.Frame, 0, 1, 1, 1)
232 |
233 |
234 | self.retranslateUi(AssemblyCorrectorMain)
235 |
236 | QMetaObject.connectSlotsByName(AssemblyCorrectorMain)
237 | # setupUi
238 |
239 | def retranslateUi(self, AssemblyAdjusterMain):
240 | AssemblyAdjusterMain.setWindowTitle(QCoreApplication.translate("AssemblyAdjusterMain", u"CATG", None))
241 | self.label_2.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Target chromosome:", None))
242 | self.label_3.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Source block id:", None))
243 | self.file_loader_btn.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Load files", None))
244 | self.file_save_btn.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Save files", None))
245 | self.label_5.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Resolution:", None))
246 | self.resolution_text.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"20", None))
247 | self.rev_chk.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Reverse", None))
248 | self.label_6.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Contigs in current block:", None))
249 | self.label.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Source chromosome:", None))
250 | self.undo_btn.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Undo", None))
251 | self.refresh_btn.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Refresh", None))
252 | self.mod_btn.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Modify", None))
253 | self.label_4.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Target block id:", None))
254 | self.label_7.setText(QCoreApplication.translate("AssemblyAdjusterMain", u"Operations:", None))
255 | # retranslateUi
256 |
257 |
--------------------------------------------------------------------------------
/coll_asm_corr_gui/ui/ui_file_loader_dialog.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | ################################################################################
4 | ## Form generated from reading UI file 'file_loader_dialogtGRscx.ui'
5 | ##
6 | ## Created by: Qt User Interface Compiler version 5.15.2
7 | ##
8 | ## WARNING! All changes made in this file will be lost when recompiling UI file!
9 | ################################################################################
10 |
11 | from PySide6.QtCore import *
12 | from PySide6.QtGui import *
13 | from PySide6.QtWidgets import *
14 | from coll_asm_corr_gui.ui.custom_control import DragLineEdit
15 |
16 |
17 | class Ui_FileLoaderDialog(object):
18 | def setupUi(self, FileLoaderDialog):
19 | if not FileLoaderDialog.objectName():
20 | FileLoaderDialog.setObjectName(u"FileLoaderDialog")
21 | FileLoaderDialog.resize(640, 250)
22 | sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
23 | sizePolicy.setHorizontalStretch(0)
24 | sizePolicy.setVerticalStretch(0)
25 | sizePolicy.setHeightForWidth(FileLoaderDialog.sizePolicy().hasHeightForWidth())
26 | FileLoaderDialog.setSizePolicy(sizePolicy)
27 | FileLoaderDialog.setMinimumSize(QSize(640, 250))
28 | FileLoaderDialog.setMaximumSize(QSize(640, 250))
29 | self.verticalLayout = QVBoxLayout(FileLoaderDialog)
30 | self.verticalLayout.setObjectName(u"verticalLayout")
31 | self.gridLayout = QGridLayout()
32 | self.gridLayout.setObjectName(u"gridLayout")
33 | self.label = QLabel(FileLoaderDialog)
34 | self.label.setObjectName(u"label")
35 | sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
36 | sizePolicy1.setHorizontalStretch(0)
37 | sizePolicy1.setVerticalStretch(0)
38 | sizePolicy1.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
39 | self.label.setSizePolicy(sizePolicy1)
40 | self.label.setMaximumSize(QSize(16777215, 30))
41 |
42 | self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
43 |
44 | self.qry_bed_text = DragLineEdit(FileLoaderDialog)
45 | self.qry_bed_text.setObjectName(u"qry_bed_text")
46 | self.qry_bed_text.setMinimumSize(QSize(0, 0))
47 | self.qry_bed_text.setDragEnabled(True)
48 |
49 | self.gridLayout.addWidget(self.qry_bed_text, 0, 1, 1, 1)
50 |
51 | self.qry_bed_btn = QPushButton(FileLoaderDialog)
52 | self.qry_bed_btn.setObjectName(u"qry_bed_btn")
53 | sizePolicy2 = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
54 | sizePolicy2.setHorizontalStretch(0)
55 | sizePolicy2.setVerticalStretch(0)
56 | sizePolicy2.setHeightForWidth(self.qry_bed_btn.sizePolicy().hasHeightForWidth())
57 | self.qry_bed_btn.setSizePolicy(sizePolicy2)
58 | self.qry_bed_btn.setMaximumSize(QSize(16777215, 30))
59 |
60 | self.gridLayout.addWidget(self.qry_bed_btn, 0, 2, 1, 1)
61 |
62 | self.label_2 = QLabel(FileLoaderDialog)
63 | self.label_2.setObjectName(u"label_2")
64 | self.label_2.setMaximumSize(QSize(16777215, 30))
65 |
66 | self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
67 |
68 | self.ref_bed_text = DragLineEdit(FileLoaderDialog)
69 | self.ref_bed_text.setObjectName(u"ref_bed_text")
70 | self.ref_bed_text.setMinimumSize(QSize(0, 0))
71 | self.ref_bed_text.setDragEnabled(True)
72 |
73 | self.gridLayout.addWidget(self.ref_bed_text, 1, 1, 1, 1)
74 |
75 | self.ref_bed_btn = QPushButton(FileLoaderDialog)
76 | self.ref_bed_btn.setObjectName(u"ref_bed_btn")
77 | sizePolicy2.setHeightForWidth(self.ref_bed_btn.sizePolicy().hasHeightForWidth())
78 | self.ref_bed_btn.setSizePolicy(sizePolicy2)
79 | self.ref_bed_btn.setMaximumSize(QSize(16777215, 30))
80 |
81 | self.gridLayout.addWidget(self.ref_bed_btn, 1, 2, 1, 1)
82 |
83 | self.label_3 = QLabel(FileLoaderDialog)
84 | self.label_3.setObjectName(u"label_3")
85 | self.label_3.setMaximumSize(QSize(16777215, 30))
86 |
87 | self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
88 |
89 | self.anchors_text = DragLineEdit(FileLoaderDialog)
90 | self.anchors_text.setObjectName(u"anchors_text")
91 | self.anchors_text.setMinimumSize(QSize(0, 0))
92 | self.anchors_text.setDragEnabled(True)
93 |
94 | self.gridLayout.addWidget(self.anchors_text, 2, 1, 1, 1)
95 |
96 | self.anchors_btn = QPushButton(FileLoaderDialog)
97 | self.anchors_btn.setObjectName(u"anchors_btn")
98 | sizePolicy2.setHeightForWidth(self.anchors_btn.sizePolicy().hasHeightForWidth())
99 | self.anchors_btn.setSizePolicy(sizePolicy2)
100 | self.anchors_btn.setMaximumSize(QSize(16777215, 30))
101 |
102 | self.gridLayout.addWidget(self.anchors_btn, 2, 2, 1, 1)
103 |
104 | self.label_4 = QLabel(FileLoaderDialog)
105 | self.label_4.setObjectName(u"label_4")
106 | self.label_4.setMaximumSize(QSize(16777215, 30))
107 |
108 | self.gridLayout.addWidget(self.label_4, 3, 0, 1, 1)
109 |
110 | self.qry_agp_text = DragLineEdit(FileLoaderDialog)
111 | self.qry_agp_text.setObjectName(u"qry_agp_text")
112 | self.qry_agp_text.setMinimumSize(QSize(0, 0))
113 | self.qry_agp_text.setDragEnabled(True)
114 |
115 | self.gridLayout.addWidget(self.qry_agp_text, 3, 1, 1, 1)
116 |
117 | self.qry_agp_btn = QPushButton(FileLoaderDialog)
118 | self.qry_agp_btn.setObjectName(u"qry_agp_btn")
119 | sizePolicy2.setHeightForWidth(self.qry_agp_btn.sizePolicy().hasHeightForWidth())
120 | self.qry_agp_btn.setSizePolicy(sizePolicy2)
121 | self.qry_agp_btn.setMaximumSize(QSize(16777215, 30))
122 |
123 | self.gridLayout.addWidget(self.qry_agp_btn, 3, 2, 1, 1)
124 |
125 |
126 | self.verticalLayout.addLayout(self.gridLayout)
127 |
128 | self.check_btn = QDialogButtonBox(FileLoaderDialog)
129 | self.check_btn.setObjectName(u"check_btn")
130 | self.check_btn.setOrientation(Qt.Horizontal)
131 | self.check_btn.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
132 |
133 | self.verticalLayout.addWidget(self.check_btn)
134 |
135 |
136 | self.retranslateUi(FileLoaderDialog)
137 | self.check_btn.accepted.connect(FileLoaderDialog.accept)
138 | self.check_btn.rejected.connect(FileLoaderDialog.reject)
139 |
140 | QMetaObject.connectSlotsByName(FileLoaderDialog)
141 | # setupUi
142 |
143 | def retranslateUi(self, FileLoaderDialog):
144 | FileLoaderDialog.setWindowTitle(QCoreApplication.translate("FileLoaderDialog", u"File loader", None))
145 | self.label.setText(QCoreApplication.translate("FileLoaderDialog", u"Query bed file:", None))
146 | self.qry_bed_btn.setText(QCoreApplication.translate("FileLoaderDialog", u"...", None))
147 | self.label_2.setText(QCoreApplication.translate("FileLoaderDialog", u"Reference bed file:", None))
148 | self.ref_bed_btn.setText(QCoreApplication.translate("FileLoaderDialog", u"...", None))
149 | self.label_3.setText(QCoreApplication.translate("FileLoaderDialog", u"Anchors file:", None))
150 | self.anchors_btn.setText(QCoreApplication.translate("FileLoaderDialog", u"...", None))
151 | self.label_4.setText(QCoreApplication.translate("FileLoaderDialog", u"Query AGP file:", None))
152 | self.qry_agp_btn.setText(QCoreApplication.translate("FileLoaderDialog", u"...", None))
153 | # retranslateUi
154 |
155 |
--------------------------------------------------------------------------------
/test/test_data.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sc-zhang/CATG/606d7f23fe713f445e2af61d3766958bc2dfa072/test/test_data.tar.gz
--------------------------------------------------------------------------------