From 74feb333987ec3f1a2fd9424740b52d5e1e9b1ba Mon Sep 17 00:00:00 2001 From: Stefan Bienert <stefan.bienert@unibas.ch> Date: Thu, 16 Jun 2011 14:17:39 +0200 Subject: [PATCH] Added dialog superpositiondialog to scene menu (menu.py) of dng --- modules/gui/doc/dialogs.rst | 25 ++ modules/gui/doc/gui.rst | 2 +- modules/gui/doc/images/100208_Dialogs.png | Bin 0 -> 3016 bytes .../doc/images/100624_superpose_dialog.png | Bin 0 -> 15322 bytes modules/gui/pymod/CMakeLists.txt | 1 + modules/gui/pymod/__init__.py | 22 +- modules/gui/pymod/dng/init.py | 1 + modules/gui/pymod/dng/menu.py | 33 +- modules/gui/pymod/dng/superpositiondialog.py | 282 ++++++++++++++++++ 9 files changed, 362 insertions(+), 4 deletions(-) create mode 100644 modules/gui/doc/dialogs.rst create mode 100644 modules/gui/doc/images/100208_Dialogs.png create mode 100644 modules/gui/doc/images/100624_superpose_dialog.png create mode 100644 modules/gui/pymod/dng/superpositiondialog.py diff --git a/modules/gui/doc/dialogs.rst b/modules/gui/doc/dialogs.rst new file mode 100644 index 000000000..a2bc2a7f2 --- /dev/null +++ b/modules/gui/doc/dialogs.rst @@ -0,0 +1,25 @@ +Dialogs +================================================================================ +.. currentmodule:: ost.gui + +OpenStructure provides several :class:`Dialogs` which can be called from its +many menus in the top bar. + + +.. image:: images/100208_Dialogs.png + +Scene Menu +-------------------------------------------------------------------------------- + + +Superpose +^^^^^^^^^ + + Structurally superpose two entities. + + .. image:: images/100624_superpose_dialog.png + + The ``Superpose`` entry in the ``Scene`` menu is only accessible if two + instances of the graphical :class:`~ost.gfx.Entity` are selected. + + .. autoclass:: ost.gui.dng.superpositiondialog.SuperpositionDialog diff --git a/modules/gui/doc/gui.rst b/modules/gui/doc/gui.rst index 675bf6b13..19a407ac0 100644 --- a/modules/gui/doc/gui.rst +++ b/modules/gui/doc/gui.rst @@ -19,4 +19,4 @@ C++ Qt widgets as well as with PyQt widgets. Learn more about :doc:`python_cpp`. tools python_shell sequence_viewer - + dialogs diff --git a/modules/gui/doc/images/100208_Dialogs.png b/modules/gui/doc/images/100208_Dialogs.png new file mode 100644 index 0000000000000000000000000000000000000000..f42f8809a3848f01390db42a71dbcc96bba53a01 GIT binary patch literal 3016 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 Rn1O+T!PC{xWt~$(698uT;O77U literal 0 HcmV?d00001 diff --git a/modules/gui/doc/images/100624_superpose_dialog.png b/modules/gui/doc/images/100624_superpose_dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..c01e657c23b0d60f6d08af94d632e95b0b74f6d3 GIT binary patch literal 15322 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 b|CvR9iU|A;WG`i4U|{fc^>bP0l+XkKWWG0x literal 0 HcmV?d00001 diff --git a/modules/gui/pymod/CMakeLists.txt b/modules/gui/pymod/CMakeLists.txt index dc6329131..9a623a9a1 100644 --- a/modules/gui/pymod/CMakeLists.txt +++ b/modules/gui/pymod/CMakeLists.txt @@ -90,6 +90,7 @@ set(OST_GUI_PYMOD_MODULES set(OST_GUI_PYMOD_DNG_MODULES __init__.py termuse.py + superpositiondialog.py init.py menu.py ) diff --git a/modules/gui/pymod/__init__.py b/modules/gui/pymod/__init__.py index c54365524..a63b91820 100644 --- a/modules/gui/pymod/__init__.py +++ b/modules/gui/pymod/__init__.py @@ -184,7 +184,27 @@ class OneOf: if isinstance(node, cl): return True return False - + +class TwoOf: + def __init__(self, *classes): + self.classes=classes + def __call__(self): + sel=SceneSelection.Instance() + act_count=sel.GetActiveNodeCount() + if act_count<2: + return False + found=0 + for i in range(0, act_count): + node=sel.GetActiveNode(i) + for cl in self.classes: + if isinstance(node, cl): + found += 1 + if found > 2: + return False + if found == 2: + return True + return False + class ManyOf: def __init__(self, *classes): self.classes=classes diff --git a/modules/gui/pymod/dng/init.py b/modules/gui/pymod/dng/init.py index 661ddadc4..d46af73d8 100644 --- a/modules/gui/pymod/dng/init.py +++ b/modules/gui/pymod/dng/init.py @@ -21,6 +21,7 @@ from ost.gui.init_spacenav import _InitSpaceNav from ost.gui.init_context_menu import _InitContextMenu from ost.gui.init_splash import _InitSplash from ost.gui.dng import termuse +from ost.gui.dng import superpositiondialog import ost.gui.dng.menu from PyQt4.QtGui import * def _my_exit(code): diff --git a/modules/gui/pymod/dng/menu.py b/modules/gui/pymod/dng/menu.py index c2b672c2b..de943d322 100644 --- a/modules/gui/pymod/dng/menu.py +++ b/modules/gui/pymod/dng/menu.py @@ -1,9 +1,12 @@ from PyQt4.QtCore import * from PyQt4.QtGui import * -from ost import gui, gfx, io +from ost import * +from ost import gui from ost.gui.scene.loader_manager_widget import LoaderManagerWidget from ost.gui.init_splash import _InitSplash from ost.gui.dng import termuse +from ost.gui.dng import superpositiondialog + import sys class FileMenu(QMenu): def __init__(self, parent=None): @@ -134,6 +137,8 @@ class SceneMenu(QMenu): enabled=gui.ManyOf(gfx.GfxObj)) gui.AddMenuAction(self, 'Fit To Screen', self._FitToScreen, enabled=gui.OneOf(gfx.Entity)) + gui.AddMenuAction(self, 'Superpose', self._SuperposeDialog, + enabled=gui.TwoOf(gfx.Entity)) gui.AddMenuAction(self, 'Save Snapshot', self._ExportScene) gui.AddMenuAction(self, 'Scene Clipping', self._ClipScene, shortcut='Ctrl+Shift+C') @@ -169,7 +174,31 @@ class SceneMenu(QMenu): def _FitToScreen(self): sel=gui.SceneSelection.Instance() gfx.FitToScreen(sel.GetActiveNode(0)) - + + def _SuperposeDialog(self): + sel=gui.SceneSelection.Instance() + act_count=sel.GetActiveNodeCount() + # we now that there have to be 2 gfx.Entities, because of using TwoOf to + # enable menu entry! + i = 0; + gfx_ent_1 = sel.GetActiveNode(i) + while not isinstance(gfx_ent_1, gfx.Entity): + i += 1 + gfx_ent_1 = sel.GetActiveNode(i) + i += 1 + gfx_ent_2 = sel.GetActiveNode(i) + while not isinstance(gfx_ent_2, gfx.Entity): + i += 1 + gfx_ent_2 = sel.GetActiveNode(i) + sd = superpositiondialog.SuperpositionDialog(gfx_ent_1, gfx_ent_2) + if sd.reference == 0: + gfx_ent_1.UpdatePositions() + gfx.Scene().CenterOn(gfx_ent_1) + else: + gfx_ent_2.UpdatePositions() + gfx.Scene().CenterOn(gfx_ent_2) + LogScript('RMSD: %.3f'%sd.rmsd) + class WindowMenu(QMenu): def __init__(self, parent=None): QMenu.__init__(self, parent) diff --git a/modules/gui/pymod/dng/superpositiondialog.py b/modules/gui/pymod/dng/superpositiondialog.py new file mode 100644 index 000000000..f426bc330 --- /dev/null +++ b/modules/gui/pymod/dng/superpositiondialog.py @@ -0,0 +1,282 @@ +#------------------------------------------------------------------------------ +# This file is part of the OpenStructure project <www.openstructure.org> +# +# Copyright (C) 2008-2011 by the OpenStructure authors +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 3.0 of the License, or (at your option) +# any later version. +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +#------------------------------------------------------------------------------ +# +# Authors: Stefan Bienert +# +from PyQt4.QtCore import * +from PyQt4.QtGui import * +from ost.mol.alg import Superpose +from ost import mol + +class ChainComboBox(QComboBox): + def __init__(self, ent, parent=None): + # class variables + self.all_chains = 'All' + QComboBox.__init__(self, parent) + self.entity = ent + self.addItem(self.all_chains) + for chain in self.entity.chains: + self.addItem(chain.name) + if self.count()>0: + self.setCurrentIndex(0) + + def SetItems(self, ent): + self.clear() + self.entity = ent + self.addItem(self.all_chains) + for chain in self.entity.chains: + self.addItem(chain.name) + if self.count()>0: + self.setCurrentIndex(0) + return + + def _GetSelectedChain(self): + if self.currentIndex() == -1: + return mol.EntityHandle() + elif self.currentText() == self.all_chains: + return self.entity + return self.entity.Select('cname=' + str(self.currentText())) + + def _SetSelectedChain(self, chain): + if hasattr(chain, 'name'): + name = chain.name + else: + name = str(chain) + for i in range(self.count()): + if self.itemText(i) == name: + self.setCurrentIndex(i) + break + selected_chain = property(_GetSelectedChain, _SetSelectedChain) + +class SuperpositionDialog(QDialog): + """ + Provides a graphical user interface to structurally superpose two entities. + Uses function :func:`~ost.mol.alg.Superpose`. The RMSD of two superposed + molecules will be stored in attribute ``rmsd``. An index for the selected + reference molecule will be stored in attribute ``reference``. + + :param ent_one: The first entity + :type ent_one: :class:`~ost.mol.EntityView`, :class:`~ost.mol.EntityHandle` + or :class:`~ost.gfx.Entity` + :param ent_two: The second entity + :type ent_two: :class:`~ost.mol.EntityView`, :class:`~ost.mol.EntityHandle` + or :class:`~ost.gfx.Entity` + + **Example Usage:** + + .. code-block:: python + + e1=io.LoadPDB('examples/code_fragments/entity/pdb1ake.ent') + e2=io.LoadPDB('examples/code_fragments/entity/pdb4ake.ent') + + sd = superpositiondialog.SuperpositionDialog(e1, e2) + + g1=gfx.Entity('G1', e1) + g2=gfx.Entity('G2', e2) + scene.Add(g1) + scene.Add(g2) + + if sd.reference == 0: + scene.CenterOn(g1) + else: + scene.CenterOn(g2) + + LogScript('RMSD: %.3f'%sd.rmsd) + """ + + def __init__(self, ent_one, ent_two, parent=None): + # class variables + self.rmsd = 0.0 + self._mmethod_dict = {'number': 'number', + 'index': 'index', + 'local alignment': 'local-aln', + 'global alignment': 'global-aln'} + QDialog.__init__(self, parent) + self.setWindowTitle('Superpose structures') + if not isinstance(ent_one, mol.EntityHandle) and \ + not isinstance(ent_one, mol.EntityView): + n_one = ent_one.GetName() + self.ent_one = ent_one.GetView() + else: + if isinstance(ent_one, mol.EntityHandle): + n_one = ent_one.GetName() + elif isinstance(ent_one, mol.EntityView): + n_one = ent_one.GetHandle().GetName() + self.ent_one = ent_one + if len(n_one) == 0: + n_one = '1' + if not isinstance(ent_two, mol.EntityHandle) and \ + not isinstance(ent_two, mol.EntityView): + n_two = ent_two.GetName() + self.ent_two = ent_two.GetView() + else: + if isinstance(ent_two, mol.EntityHandle): + n_two = ent_two.GetName() + elif isinstance(ent_two, mol.EntityView): + n_two = ent_two.GetHandle().GetName() + self.ent_two = ent_two + if len(n_two) == 0: + n_two = '2' + if n_one == n_two: + n_one = n_one + ' 1' + n_two = n_two + ' 2' + layout = QGridLayout(self) + # select reference molecule + self.reference = 0; + self._reference = self._ReferenceSelection(n_one, n_two) + grow = 0 + layout.addWidget(QLabel("reference"), grow, 0) + layout.addWidget(self._reference, grow, 1) + grow += 1 + # chains + self._chain_one = ChainComboBox(self.ent_one, self) + self._chain_two = ChainComboBox(self.ent_two, self) + layout.addWidget(QLabel("reference chain"), grow, 0) + layout.addWidget(self._chain_one, grow, 1) + grow += 1 + layout.addWidget(QLabel("chain"), grow, 0) + layout.addWidget(self._chain_two, grow, 1) + grow += 1 + # link chain and reference selection + QObject.connect(self._reference, + SIGNAL('currentIndexChanged(int)'), + self._ChangeChainSelection) + # match methods + self._methods = self._MatchMethods() + layout.addWidget(QLabel('match residues by'), grow, 0) + grow += 1 + layout.addWidget(self._methods) + # atoms + self._atoms = self._FetchAtoms(self._methods.size(), + self.ent_one, + self.ent_two) + self._atmselectbx, self._atmselectgrp = self._AtomSelectionBox() + layout.addWidget(self._atmselectbx, grow, 1) + grow += 1 + # buttons + ok_button = QPushButton("Superpose") + QObject.connect(ok_button, SIGNAL('clicked()'), self.accept) + cancel_button = QPushButton("Cancel") + hbox_layout = QHBoxLayout() + hbox_layout.addStretch(1) + layout.addLayout(hbox_layout, grow, 0, 1, 2) + grow += 1 + QObject.connect(cancel_button, SIGNAL('clicked()'), self.reject) + QObject.connect(self, SIGNAL('accepted()'), self._Superpose) + hbox_layout.addWidget(cancel_button, 0) + hbox_layout.addWidget(ok_button, 0) + ok_button.setDefault(True) + self.exec_() + + def _Superpose(self): + view_one = self._chain_one.selected_chain + view_two = self._chain_two.selected_chain + atoms = self._GetAtomSelection() + sp = Superpose(view_one, view_two, + self._mmethod_dict[str(self._methods.currentText())], + atoms) + self.rmsd = sp.rmsd + + def _toggle_atoms(self, checked): + if checked: + self._atoms.setEnabled(True) + else: + self._atoms.setEnabled(False) + + def _AtomSelectionBox(self): + bt1 = QRadioButton('All') + bt2 = QRadioButton('Backbone') + bt3 = QRadioButton('CA') + self.cstmbtntxt = 'Custom' + custom_rbutton = QRadioButton(self.cstmbtntxt) + group = QButtonGroup() + group.addButton(bt1) + group.addButton(bt2) + group.addButton(bt3) + group.addButton(custom_rbutton) + bt1.setChecked(True) + vbox_layout = QVBoxLayout() + vbox_layout.addWidget(bt1) + vbox_layout.addWidget(bt2) + vbox_layout.addWidget(bt3) + vbox_layout.addWidget(custom_rbutton) + vbox_layout.addWidget(self._atoms) + QObject.connect(custom_rbutton, SIGNAL('toggled(bool)'), self._toggle_atoms) + box = QGroupBox("atom selection") + box.setLayout(vbox_layout) + return box, group + + def _GetAtomSelection(self): + checkedbtn = self._atmselectgrp.checkedButton() + if str(checkedbtn.text()) != self.cstmbtntxt: + return str(checkedbtn.text()) + slctn_model = self._atoms.selectionModel() + dt_model = slctn_model.model() + atms = list() + for idx in slctn_model.selectedRows(): + slctn = dt_model.data(idx, Qt.DisplayRole).toString() + atms.append(str(slctn)) + return atms + + def _FetchAtoms(self, dim, ent_a, ent_b): + # fetch list of atoms: only those which are in both entities are considered + atm_dict = {} + for atm in ent_a.GetAtomList(): + atm_dict[atm.name] = 0 + for atm in ent_b.GetAtomList(): + if atm.name in atm_dict: + atm_dict[atm.name] = 1 + atmlst = QStringList() + for atm in sorted(atm_dict.keys()): + if atm_dict[atm]: + atmlst.append(atm) + elems = QStringListModel(atmlst) + atoms = QListView(self) + dim.setHeight(3*dim.height()) + atoms.setFixedSize(dim) + atoms.setModel(elems) + atoms.setSelectionMode(QAbstractItemView.MultiSelection) + atoms.setEditTriggers(QAbstractItemView.NoEditTriggers) + atoms.setEnabled(False) + return atoms + + def _ReferenceSelection(self, name_a, name_b): + cbox = QComboBox() + cbox.addItem(name_a) + cbox.addItem(name_b) + if cbox.count() > 0: + cbox.setCurrentIndex(0) + return cbox + + def _ChangeChainSelection(self, index): + if index == 0: + self._chain_one.SetItems(self.ent_one) + self._chain_two.SetItems(self.ent_two) + self.reference = 0; + elif index == 1: + self._chain_one.SetItems(self.ent_two) + self._chain_two.SetItems(self.ent_one) + self.reference = 1; + return + + def _MatchMethods(self): + methods=QComboBox(self) + for method in sorted(self._mmethod_dict): + methods.addItem(method) + return methods -- GitLab