OpenStructure
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 #
22 from PyQt5 import QtCore, QtGui, QtWidgets
23 from ost.mol.alg import Superpose
24 from ost import mol
25 
26 class ChainComboBox(QtWidgets.QComboBox):
27  def __init__(self, ent, gfx, parent=None):
28  # class variables
29  self.all_chainsall_chains = 'All'
30  QtWidgets.QComboBox.__init__(self, parent)
31  self.entityentity = ent
32  self.addItem(self.all_chainsall_chains)
33  for chain in self.entityentity.chains:
34  self.addItem(chain.name)
35  if self.count()>0:
36  self.setCurrentIndex(0)
37  if gfx:
38  self.gfxgfx = gfx
39  self.highlighted.connect(self._HighlightChain_HighlightChain)
40  else:
41  self.gfxgfx = None
42 
43  def focusOutEvent (self, event):
44  if self.gfxgfx:
45  self.gfxgfx.selection = None
46 
47  def SetItems(self, ent, gfx):
48  self.clear()
49  self.entityentity = ent
50  self.addItem(self.all_chainsall_chains)
51  for chain in self.entityentity.chains:
52  self.addItem(chain.name)
53  if self.count()>0:
54  self.setCurrentIndex(0)
55  if gfx:
56  self.gfxgfx = gfx
57 
58  def _HighlightChain(self, chain_idx):
59  chain = self.itemText(chain_idx)
60  if chain != 'All':
61  self.gfxgfx.SetSelection(self.entityentity.Select('cname="%s"' % str(chain)))
62  else:
63  self.gfxgfx.SetSelection(self.entityentity.Select(''))
64 
65  def _GetSelectedChain(self):
66  if self.currentIndex() == -1:
67  return mol.EntityHandle()
68  elif self.currentText() == self.all_chainsall_chains:
69  return self.entityentity
70  return self.entityentity.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 
83 class 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
122  self.rmsd_superposed_atomsrmsd_superposed_atoms = None
123  self.rmsdrmsd = None
124  self.fraction_superposedfraction_superposed = None
125  self.superposition_errorsuperposition_error = None
126  self._mmethod_dict_mmethod_dict = {'number': 'number',
127  'index': 'index',
128  'local alignment': 'local-aln',
129  'global alignment': 'global-aln'}
130  self.gfx_onegfx_one = None
131  self.gfx_twogfx_two = None
132  self.gfx_select_onegfx_select_one = None
133  self.gfx_select_twogfx_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_onegfx_one = ent_one
140  self.gfx_select_onegfx_select_one = self.gfx_onegfx_one.GetSelection()
141  self.ent_oneent_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_oneent_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_twogfx_two = ent_two
154  self.gfx_select_twogfx_select_two = self.gfx_twogfx_two.GetSelection()
155  self.ent_twoent_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_twoent_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.referencereference = 0;
170  self._reference_reference = self._ReferenceSelection_ReferenceSelection(n_one, n_two)
171  grow = 0
172  layout.addWidget(QtWidgets.QLabel("reference"), grow, 0)
173  layout.addWidget(self._reference_reference, grow, 1)
174  grow += 1
175  # chains
176  self._chain_one_chain_one = ChainComboBox(self.ent_oneent_one, self.gfx_onegfx_one, self)
177  self._chain_two_chain_two = ChainComboBox(self.ent_twoent_two, self.gfx_twogfx_two, self)
178  layout.addWidget(QtWidgets.QLabel("reference chain"), grow, 0)
179  layout.addWidget(self._chain_one_chain_one, grow, 1)
180  grow += 1
181  layout.addWidget(QtWidgets.QLabel("chain"), grow, 0)
182  layout.addWidget(self._chain_two_chain_two, grow, 1)
183  grow += 1
184  # link chain and reference selection
185  self._reference_reference.currentIndexChanged.connect(self._ChangeChainSelection_ChangeChainSelection)
186  # match methods
187  self._methods_methods = self._MatchMethods_MatchMethods()
188  layout.addWidget(QtWidgets.QLabel('match residues by'), grow, 0)
189  grow += 1
190  layout.addWidget(self._methods_methods)
191 
192  # iterative
193  self._iterative_iterative=None
194  self._it_box, self._it_in, self._dist_in_dist_in = self._ItBox_ItBox()
195  layout.addWidget(self._it_box, grow, 0)
196  # atoms
197  self._atoms_atoms = self._FetchAtoms_FetchAtoms(self._methods_methods.size(),
198  self.ent_oneent_one,
199  self.ent_twoent_two)
200  self._atmselectbx, self._atmselectgrp_atmselectgrp = self._AtomSelectionBox_AtomSelectionBox()
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_onegfx_one:
219  self.gfx_onegfx_one.SetSelection(self.gfx_select_onegfx_select_one)
220  if self.gfx_twogfx_two:
221  self.gfx_twogfx_two.SetSelection(self.gfx_select_twogfx_select_two)
222 
223  def _Superpose(self):
224  view_one = self._chain_one_chain_one.selected_chain
225  view_two = self._chain_two_chain_two.selected_chain
226  atoms = self._GetAtomSelection_GetAtomSelection()
227  try:
228  sp = Superpose(view_two, view_one,
229  self._mmethod_dict_mmethod_dict[str(self._methods_methods.currentText())],
230  atoms, iterative=self._iterative_iterative,
231  max_iterations=self._it_in.value(),
232  distance_threshold=self._dist_in_dist_in.value())
233  except Exception as e:
234  # mark as failed by setting superposition_error and let caller handle it
235  self.superposition_errorsuperposition_error = str(e)
236  return
237  self.rmsdrmsd = sp.rmsd
238  if self._iterative_iterative:
239  self.rmsd_superposed_atomsrmsd_superposed_atoms = sp.rmsd_superposed_atoms
240  self.fraction_superposedfraction_superposed = sp.fraction_superposed
241 
242  def _toggle_atoms(self, checked):
243  if checked:
244  self._atoms_atoms.setEnabled(True)
245  else:
246  self._atoms_atoms.setEnabled(False)
247 
248  def _AtomSelectionBox(self):
249  bt1 = QtWidgets.QRadioButton('All')
250  bt2 = QtWidgets.QRadioButton('Backbone')
251  bt3 = QtWidgets.QRadioButton('CA')
252  self.cstmbtntxtcstmbtntxt = 'Custom'
253  custom_rbutton = QtWidgets.QRadioButton(self.cstmbtntxtcstmbtntxt)
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_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 
271  def _GetAtomSelection(self):
272  checkedbtn = self._atmselectgrp_atmselectgrp.checkedButton()
273  if str(checkedbtn.text()) != self.cstmbtntxtcstmbtntxt:
274  return str(checkedbtn.text())
275  slctn_model = self._atoms_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_dist_in.setEnabled(True)
317  self._iterative_iterative=True
318  else:
319  self._it_in.setEnabled(False)
320  self._dist_in_dist_in.setEnabled(False)
321  self._iterative_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_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_chain_one.SetItems(self.ent_oneent_one, self.gfx_onegfx_one)
354  self._chain_two_chain_two.SetItems(self.ent_twoent_two, self.gfx_twogfx_two)
355  self.referencereference = 0;
356  elif index == 1:
357  self._chain_one_chain_one.SetItems(self.ent_twoent_two, self.gfx_twogfx_two)
358  self._chain_two_chain_two.SetItems(self.ent_oneent_one, self.gfx_onegfx_one)
359  self.referencereference = 1;
360 
361  def _MatchMethods(self):
362  methods=QtWidgets.QComboBox(self)
363  for method in sorted(self._mmethod_dict_mmethod_dict):
364  methods.addItem(method)
365  return methods
def __init__(self, ent_one, ent_two, parent=None)
Protein or molecule.
definition of EntityView
Definition: entity_view.hh:86
def Superpose(ent_a, ent_b, match='number', atoms='all', iterative=False, max_iterations=5, distance_threshold=3.0)
Definition: superpose.py:280