OpenStructure
Loading...
Searching...
No Matches
superpositiondialog.py
Go to the documentation of this file.
1#------------------------------------------------------------------------------
2# This file is part of the OpenStructure project <www.openstructure.org>
3#
4# Copyright (C) 2008-2020 by the OpenStructure authors
5#
6# This library is free software; you can redistribute it and/or modify it under
7# the terms of the GNU Lesser General Public License as published by the Free
8# Software Foundation; either version 3.0 of the License, or (at your option)
9# any later version.
10# This library is distributed in the hope that it will be useful, but WITHOUT
11# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13# details.
14#
15# You should have received a copy of the GNU Lesser General Public License
16# along with this library; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18#------------------------------------------------------------------------------
19#
20# Authors: Stefan Bienert
21#
22from PyQt5 import QtCore, QtGui, QtWidgets
23from ost.mol.alg import Superpose
24from ost import mol
25
26class ChainComboBox(QtWidgets.QComboBox):
27 def __init__(self, ent, gfx, parent=None):
28 # class variables
29 self.all_chains = 'All'
30 QtWidgets.QComboBox.__init__(self, parent)
31 self.entity = ent
32 self.addItem(self.all_chains)
33 for chain in self.entity.chains:
34 self.addItem(chain.name)
35 if self.count()>0:
36 self.setCurrentIndex(0)
37 if gfx:
38 self.gfx = gfx
39 self.highlighted.connect(self._HighlightChain_HighlightChain)
40 else:
41 self.gfx = None
42
43 def focusOutEvent (self, event):
44 if self.gfx:
45 self.gfx.selection = None
46
47 def SetItems(self, ent, gfx):
48 self.clear()
49 self.entity = ent
50 self.addItem(self.all_chains)
51 for chain in self.entity.chains:
52 self.addItem(chain.name)
53 if self.count()>0:
54 self.setCurrentIndex(0)
55 if gfx:
56 self.gfx = gfx
57
58 def _HighlightChain(self, chain_idx):
59 chain = self.itemText(chain_idx)
60 if chain != 'All':
61 self.gfx.SetSelection(self.entity.Select('cname="%s"' % str(chain)))
62 else:
63 self.gfx.SetSelection(self.entity.Select(''))
64
66 if self.currentIndex() == -1:
67 return mol.EntityHandle()
68 elif self.currentText() == self.all_chains:
69 return self.entity
70 return self.entity.Select('cname="%s"' % str(self.currentText()))
71
72 def _SetSelectedChain(self, chain):
73 if hasattr(chain, 'name'):
74 name = chain.name
75 else:
76 name = str(chain)
77 for i in range(self.count()):
78 if self.itemText(i) == name:
79 self.setCurrentIndex(i)
80 break
81 selected_chain = property(_GetSelectedChain, _SetSelectedChain)
82
83class SuperpositionDialog(QtWidgets.QDialog):
84 """
85 Provides a graphical user interface to structurally superpose two entities.
86 Uses function :func:`~ost.mol.alg.Superpose`. The RMSD of two superposed
87 molecules will be stored in attribute ``rmsd``. An index for the selected
88 reference molecule will be stored in attribute ``reference``.
89
90 :param ent_one: The first entity
91 :type ent_one: :class:`~ost.mol.EntityView`, :class:`~ost.mol.EntityHandle`
92 or :class:`~ost.gfx.Entity`
93 :param ent_two: The second entity
94 :type ent_two: :class:`~ost.mol.EntityView`, :class:`~ost.mol.EntityHandle`
95 or :class:`~ost.gfx.Entity`
96
97 **Example Usage:**
98
99 .. code-block:: python
100
101 e1=io.LoadPDB('examples/code_fragments/entity/pdb1ake.ent')
102 e2=io.LoadPDB('examples/code_fragments/entity/pdb4ake.ent')
103
104 sd = ost.gui.dng.superpositiondialog.SuperpositionDialog(e1, e2)
105
106 g1=gfx.Entity('G1', e1)
107 g2=gfx.Entity('G2', e2)
108 scene.Add(g1)
109 scene.Add(g2)
110
111 if sd.reference == 0:
112 scene.CenterOn(g1)
113 else:
114 scene.CenterOn(g2)
115
116 if sd.rmsd != None:
117 LogScript('RMSD: %.3f'%sd.rmsd)
118 """
119
120 def __init__(self, ent_one, ent_two, parent=None):
121 # class variables
123 self.rmsd = None
126 self._mmethod_dict = {'number': 'number',
127 'index': 'index',
128 'local alignment': 'local-aln',
129 'global alignment': 'global-aln'}
130 self.gfx_one = None
131 self.gfx_two = None
132 self.gfx_select_one = None
133 self.gfx_select_two = None
134 QtWidgets.QDialog.__init__(self, parent)
135 self.setWindowTitle('Superpose Structures')
136 if not isinstance(ent_one, mol.EntityHandle) and \
137 not isinstance(ent_one, mol.EntityView):
138 n_one = ent_one.GetName()
139 self.gfx_one = ent_one
140 self.gfx_select_one = self.gfx_one.GetSelection()
141 self.ent_one = ent_one.GetView()
142 else:
143 if isinstance(ent_one, mol.EntityHandle):
144 n_one = ent_one.GetName()
145 elif isinstance(ent_one, mol.EntityView):
146 n_one = ent_one.GetHandle().GetName()
147 self.ent_one = ent_one
148 if len(n_one) == 0:
149 n_one = '1'
150 if not isinstance(ent_two, mol.EntityHandle) and \
151 not isinstance(ent_two, mol.EntityView):
152 n_two = ent_two.GetName()
153 self.gfx_two = ent_two
154 self.gfx_select_two = self.gfx_two.GetSelection()
155 self.ent_two = ent_two.GetView()
156 else:
157 if isinstance(ent_two, mol.EntityHandle):
158 n_two = ent_two.GetName()
159 elif isinstance(ent_two, mol.EntityView):
160 n_two = ent_two.GetHandle().GetName()
161 self.ent_two = ent_two
162 if len(n_two) == 0:
163 n_two = '2'
164 if n_one == n_two:
165 n_one = n_one + ' 1'
166 n_two = n_two + ' 2'
167 layout = QtWidgets.QGridLayout(self)
168 # select reference molecule
169 self.reference = 0;
170 self._reference = self._ReferenceSelection(n_one, n_two)
171 grow = 0
172 layout.addWidget(QtWidgets.QLabel("reference"), grow, 0)
173 layout.addWidget(self._reference, grow, 1)
174 grow += 1
175 # chains
176 self._chain_one = ChainComboBox(self.ent_one, self.gfx_one, self)
177 self._chain_two = ChainComboBox(self.ent_two, self.gfx_two, self)
178 layout.addWidget(QtWidgets.QLabel("reference chain"), grow, 0)
179 layout.addWidget(self._chain_one, grow, 1)
180 grow += 1
181 layout.addWidget(QtWidgets.QLabel("chain"), grow, 0)
182 layout.addWidget(self._chain_two, grow, 1)
183 grow += 1
184 # link chain and reference selection
185 self._reference.currentIndexChanged.connect(self._ChangeChainSelection_ChangeChainSelection)
186 # match methods
188 layout.addWidget(QtWidgets.QLabel('match residues by'), grow, 0)
189 grow += 1
190 layout.addWidget(self._methods)
191
192 # iterative
193 self._iterative=None
194 self._it_box, self._it_in, self._dist_in = self._ItBox()
195 layout.addWidget(self._it_box, grow, 0)
196 # atoms
197 self._atoms = self._FetchAtoms(self._methods.size(),
198 self.ent_one,
199 self.ent_two)
201 layout.addWidget(self._atmselectbx, grow, 1)
202 grow += 1
203 # buttons
204 ok_button = QtWidgets.QPushButton("Superpose")
205 ok_button.clicked.connect(self.accept)
206 cancel_button = QtWidgets.QPushButton("Cancel")
207 hbox_layout = QtWidgets.QHBoxLayout()
208 hbox_layout.addStretch(1)
209 layout.addLayout(hbox_layout, grow, 0, 1, 2)
210 grow += 1
211 cancel_button.clicked.connect(self.reject)
212 self.accepted.connect(self._Superpose_Superpose)
213 hbox_layout.addWidget(cancel_button, 0)
214 hbox_layout.addWidget(ok_button, 0)
215 ok_button.setDefault(True)
216 self.exec_()
217 # restore old selections
218 if self.gfx_one:
219 self.gfx_one.SetSelection(self.gfx_select_one)
220 if self.gfx_two:
221 self.gfx_two.SetSelection(self.gfx_select_two)
222
223 def _Superpose(self):
224 view_one = self._chain_one.selected_chain
225 view_two = self._chain_two.selected_chain
226 atoms = self._GetAtomSelection()
227 try:
228 sp = Superpose(view_two, view_one,
229 self._mmethod_dict[str(self._methods.currentText())],
230 atoms, iterative=self._iterative,
231 max_iterations=self._it_in.value(),
232 distance_threshold=self._dist_in.value())
233 except Exception as e:
234 # mark as failed by setting superposition_error and let caller handle it
235 self.superposition_error = str(e)
236 return
237 self.rmsd = sp.rmsd
238 if self._iterative:
239 self.rmsd_superposed_atoms = sp.rmsd_superposed_atoms
240 self.fraction_superposed = sp.fraction_superposed
241
242 def _toggle_atoms(self, checked):
243 if checked:
244 self._atoms.setEnabled(True)
245 else:
246 self._atoms.setEnabled(False)
247
249 bt1 = QtWidgets.QRadioButton('All')
250 bt2 = QtWidgets.QRadioButton('Backbone')
251 bt3 = QtWidgets.QRadioButton('CA')
252 self.cstmbtntxt = 'Custom'
253 custom_rbutton = QtWidgets.QRadioButton(self.cstmbtntxt)
254 group = QtWidgets.QButtonGroup()
255 group.addButton(bt1)
256 group.addButton(bt2)
257 group.addButton(bt3)
258 group.addButton(custom_rbutton)
259 bt1.setChecked(True)
260 vbox_layout = QtWidgets.QVBoxLayout()
261 vbox_layout.addWidget(bt1)
262 vbox_layout.addWidget(bt2)
263 vbox_layout.addWidget(bt3)
264 vbox_layout.addWidget(custom_rbutton)
265 vbox_layout.addWidget(self._atoms)
266 custom_rbutton.toggled.connect(self._toggle_atoms_toggle_atoms)
267 box = QtWidgets.QGroupBox("atom selection")
268 box.setLayout(vbox_layout)
269 return box, group
270
272 checkedbtn = self._atmselectgrp.checkedButton()
273 if str(checkedbtn.text()) != self.cstmbtntxt:
274 return str(checkedbtn.text())
275 slctn_model = self._atoms.selectionModel()
276 dt_model = slctn_model.model()
277 atms = list()
278 for idx in slctn_model.selectedRows():
279 slctn = dt_model.data(idx, Qt.DisplayRole).toString()
280 atms.append(str(slctn))
281 return atms
282
283 def _FetchAtoms(self, dim, ent_a, ent_b):
284 # fetch list of atoms: only those which are in both entities are considered
285 atm_dict = {}
286 for atm in ent_a.GetAtomList():
287 atm_dict[atm.name] = 0
288 for atm in ent_b.GetAtomList():
289 if atm.name in atm_dict:
290 atm_dict[atm.name] = 1
291 atmlst = list()
292 for atm in sorted(atm_dict.keys()):
293 if atm_dict[atm]:
294 atmlst.append(atm)
295 elems = QtCore.QStringListModel(atmlst)
296 atoms = QtWidgets.QListView(self)
297 dim.setHeight(3*dim.height())
298 atoms.setFixedSize(dim)
299 atoms.setModel(elems)
300 atoms.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
301 atoms.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
302 atoms.setEnabled(False)
303 return atoms
304
305 def _ReferenceSelection(self, name_a, name_b):
306 cbox = QtWidgets.QComboBox()
307 cbox.addItem(name_a)
308 cbox.addItem(name_b)
309 if cbox.count() > 0:
310 cbox.setCurrentIndex(0)
311 return cbox
312
313 def _toggle_iterative(self, checked):
314 if checked:
315 self._it_in.setEnabled(True)
316 self._dist_in.setEnabled(True)
317 self._iterative=True
318 else:
319 self._it_in.setEnabled(False)
320 self._dist_in.setEnabled(False)
321 self._iterative=False
322
323 def _ItBox(self):
324 bt1 = QtWidgets.QRadioButton("On")
325 iteration_label=QtWidgets.QLabel("Max Iterations: ")
326 distance_label=QtWidgets.QLabel("Dist Thresh: ")
327 iteration_in=QtWidgets.QSpinBox()
328 iteration_in.setRange(1,30)
329 iteration_in.setValue(8)
330 distance_in=QtWidgets.QDoubleSpinBox()
331 distance_in.setRange(1.0,10.0)
332 distance_in.setValue(3.0)
333 distance_in.setDecimals(1)
334 distance_in.setSingleStep(0.5)
335 iteration_in.setEnabled(False)
336 distance_in.setEnabled(False)
337 bt1.setChecked(False)
338 self._iterative=False
339 vbox_layout = QtWidgets.QVBoxLayout()
340 vbox_layout.addWidget(bt1)
341 vbox_layout.addWidget(iteration_label)
342 vbox_layout.addWidget(iteration_in)
343 vbox_layout.addWidget(distance_label)
344 vbox_layout.addWidget(distance_in)
345 vbox_layout.addSpacing(50)
346 bt1.toggled.connect(self._toggle_iterative_toggle_iterative)
347 box = QtWidgets.QGroupBox("Iterative")
348 box.setLayout(vbox_layout)
349 return box,iteration_in, distance_in
350
351 def _ChangeChainSelection(self, index):
352 if index == 0:
353 self._chain_one.SetItems(self.ent_one, self.gfx_one)
354 self._chain_two.SetItems(self.ent_two, self.gfx_two)
355 self.reference = 0;
356 elif index == 1:
357 self._chain_one.SetItems(self.ent_two, self.gfx_two)
358 self._chain_two.SetItems(self.ent_one, self.gfx_one)
359 self.reference = 1;
360
361 def _MatchMethods(self):
362 methods=QtWidgets.QComboBox(self)
363 for method in sorted(self._mmethod_dict):
364 methods.addItem(method)
365 return methods
Protein or molecule.
definition of EntityView