"Fossies" - the Fresh Open Source Software Archive

Member "relax-5.0.0/gui/spin_viewer/tree.py" (2 Dec 2019, 42517 Bytes) of package /linux/privat/relax-5.0.0.src.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "tree.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.1.3_vs_5.0.0.

    1 ###############################################################################
    2 #                                                                             #
    3 # Copyright (C) 2010-2015,2019 Edward d'Auvergne                              #
    4 #                                                                             #
    5 # This file is part of the program relax (http://www.nmr-relax.com).          #
    6 #                                                                             #
    7 # This program is free software: you can redistribute it and/or modify        #
    8 # it under the terms of the GNU General Public License as published by        #
    9 # the Free Software Foundation, either version 3 of the License, or           #
   10 # (at your option) any later version.                                         #
   11 #                                                                             #
   12 # This program is distributed in the hope that it will be useful,             #
   13 # but WITHOUT ANY WARRANTY; without even the implied warranty of              #
   14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
   15 # GNU General Public License for more details.                                #
   16 #                                                                             #
   17 # You should have received a copy of the GNU General Public License           #
   18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
   19 #                                                                             #
   20 ###############################################################################
   21 
   22 # Module docstring.
   23 """The molecule, residue, and spin tree view GUI elements."""
   24 
   25 
   26 # Python module imports.
   27 import wx
   28 
   29 # relax module imports.
   30 import dep_check
   31 from graphics import fetch_icon
   32 from gui.components.menu import build_menu_item
   33 from gui.message import Question
   34 from gui.string_conv import gui_to_str
   35 from gui.uf_objects import Uf_storage; uf_store = Uf_storage()
   36 from pipe_control.mol_res_spin import get_molecule_ids, molecule_loop, residue_loop, return_molecule, return_residue, return_spin, spin_loop
   37 from pipe_control.pipes import cdp_name, get_pipe
   38 from pipe_control.selection import is_mol_selected, is_res_selected, is_spin_selected
   39 from status import Status; status = Status()
   40 
   41 
   42 # Some IDs for the menu entries.
   43 MENU_MOLECULE_MOLECULE_COPY = wx.NewId()
   44 MENU_MOLECULE_MOLECULE_DELETE = wx.NewId()
   45 MENU_MOLECULE_MOLECULE_DESELECT = wx.NewId()
   46 MENU_MOLECULE_MOLECULE_NAME = wx.NewId()
   47 MENU_MOLECULE_MOLECULE_SELECT = wx.NewId()
   48 MENU_MOLECULE_MOLECULE_TYPE = wx.NewId()
   49 MENU_MOLECULE_RESIDUE_CREATE = wx.NewId()
   50 MENU_RESIDUE_RESIDUE_COPY = wx.NewId()
   51 MENU_RESIDUE_RESIDUE_DELETE = wx.NewId()
   52 MENU_RESIDUE_RESIDUE_DESELECT = wx.NewId()
   53 MENU_RESIDUE_RESIDUE_NAME = wx.NewId()
   54 MENU_RESIDUE_RESIDUE_NUMBER = wx.NewId()
   55 MENU_RESIDUE_RESIDUE_SELECT = wx.NewId()
   56 MENU_RESIDUE_SPIN_ADD = wx.NewId()
   57 MENU_RESIDUE_SPIN_CREATE_PSEUDO = wx.NewId()
   58 MENU_ROOT_MOLECULE_CREATE = wx.NewId()
   59 MENU_ROOT_LOAD_SPINS = wx.NewId()
   60 MENU_SPIN_SPIN_COPY = wx.NewId()
   61 MENU_SPIN_SPIN_DELETE = wx.NewId()
   62 MENU_SPIN_SPIN_DESELECT = wx.NewId()
   63 MENU_SPIN_SPIN_ELEMENT = wx.NewId()
   64 MENU_SPIN_SPIN_NAME = wx.NewId()
   65 MENU_SPIN_SPIN_NUMBER = wx.NewId()
   66 MENU_SPIN_SPIN_SELECT = wx.NewId()
   67 
   68 
   69 class Mol_res_spin_tree(wx.Window):
   70     """The tree view class."""
   71 
   72     def __init__(self, gui, parent=None, id=None):
   73         """Set up the tree GUI element.
   74 
   75         @param gui:         The gui object.
   76         @type gui:          wx object
   77         @keyword parent:    The parent GUI element that this is to be attached to.
   78         @type parent:       wx object
   79         @keyword id:        The ID number.
   80         @type id:           int
   81         """
   82 
   83         # Store the args.
   84         self.gui = gui
   85         self.parent = parent
   86 
   87         # Execute the base class method.
   88         wx.Window.__init__(self, parent, id, style=wx.WANTS_CHARS)
   89 
   90         # Some default values.
   91         self.icon_size = 22
   92 
   93         # The tree.
   94         self.tree = wx.TreeCtrl(parent=self, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TR_DEFAULT_STYLE)
   95 
   96         # A tracking structure for the tree IDs.
   97         self.tree_ids = {}
   98 
   99         # Resize the tree element.
  100         self.Bind(wx.EVT_SIZE, self._resize)
  101 
  102         # The tree roots.
  103         self.root = self.tree.AddRoot("Spin system information")
  104         if dep_check.wx_classic:
  105             self.tree.SetPyData(self.root, "root")
  106         else:
  107             self.tree.SetItemData(self.root, "root")
  108 
  109         # Build the icon list.
  110         icon_list = wx.ImageList(self.icon_size, self.icon_size)
  111 
  112         # The normal icons.
  113         self.icon_mol_index = icon_list.Add(wx.Bitmap(fetch_icon("relax.molecule", "22x22"), wx.BITMAP_TYPE_ANY))
  114         self.icon_mol_unfold_index = icon_list.Add(wx.Bitmap(fetch_icon("relax.molecule_unfolded", "22x22"), wx.BITMAP_TYPE_ANY))
  115         self.icon_res_index = icon_list.Add(wx.Bitmap(fetch_icon("relax.residue", "22x22"), wx.BITMAP_TYPE_ANY))
  116         self.icon_spin_index = icon_list.Add(wx.Bitmap(fetch_icon("relax.spin", "22x22"), wx.BITMAP_TYPE_ANY))
  117 
  118         # The deselected icons.
  119         self.icon_mol_index_desel = icon_list.Add(wx.Bitmap(fetch_icon("relax.molecule_grey", "22x22"), wx.BITMAP_TYPE_ANY))
  120         self.icon_mol_unfold_index_desel = icon_list.Add(wx.Bitmap(fetch_icon("relax.molecule_unfolded_grey", "22x22"), wx.BITMAP_TYPE_ANY))
  121         self.icon_res_index_desel = icon_list.Add(wx.Bitmap(fetch_icon("relax.residue_grey", "22x22"), wx.BITMAP_TYPE_ANY))
  122         self.icon_spin_index_desel = icon_list.Add(wx.Bitmap(fetch_icon("relax.spin_grey", "22x22"), wx.BITMAP_TYPE_ANY))
  123 
  124         # Set the icon list.
  125         self.tree.SetImageList(icon_list)
  126 
  127         # Some weird black magic (this is essential)!!
  128         self.icon_list = icon_list
  129 
  130         # Populate the tree.
  131         self.update()
  132 
  133         # Catch mouse events.
  134         self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self._selection)
  135         self.tree.Bind(wx.EVT_RIGHT_DOWN, self._right_click)
  136 
  137 
  138     def _resize(self, event):
  139         """Resize the tree element.
  140 
  141         @param event:   The wx event.
  142         @type event:    wx event
  143         """
  144 
  145         # The panel dimensions.
  146         width, height = self.GetClientSize()
  147 
  148         # Set the tree dimensions.
  149         if dep_check.wx_classic:
  150             self.tree.SetDimensions(0, 0, width, height)
  151         else:
  152             self.tree.SetSize(0, 0, width, height)
  153 
  154 
  155     def _right_click(self, event):
  156         """Handle right clicks in the tree.
  157 
  158         @param event:   The wx event.
  159         @type event:    wx event
  160         """
  161 
  162         # Obtain the position.
  163         pos = event.GetPosition()
  164 
  165         # Find the item clicked on.
  166         item, flags = self.tree.HitTest(pos)
  167 
  168         # The python data (with catch for wxPython 2.9 behaviour).
  169         if not item.IsOk():
  170             self.info = None
  171         elif dep_check.wx_classic:
  172             self.info = self.tree.GetItemPyData(item)
  173         else:
  174             self.info = self.tree.GetItemData(item)
  175 
  176         # Bring up the default menu.
  177         if self.info == None:
  178             self.menu_default()
  179 
  180         # Bring up the root menu.
  181         elif self.info == 'root':
  182             self.menu_root()
  183 
  184         # Bring up the molecule menu.
  185         elif self.info['type'] == 'mol':
  186             self.menu_molecule()
  187 
  188         # Bring up the residue menu.
  189         elif self.info['type'] == 'res':
  190             self.menu_residue()
  191 
  192         # Bring up the spin menu.
  193         elif self.info['type'] == 'spin':
  194             self.menu_spin()
  195 
  196 
  197     def _selection(self, event):
  198         """Handle changes in selection in the tree.
  199 
  200         @param event:   The wx event.
  201         @type event:    wx event
  202         """
  203 
  204         # Find the item clicked on.
  205         item = event.GetItem()
  206 
  207         # The python data.
  208         if dep_check.wx_classic:
  209             info = self.tree.GetItemPyData(item)
  210         else:
  211             info = self.tree.GetItemData(item)
  212 
  213         # Display the container.
  214         self.gui.spin_viewer.container.display(info)
  215 
  216 
  217     def action_molecule_molecule_copy(self, event):
  218         """Wrapper method.
  219 
  220         @param event:   The wx event.
  221         @type event:    wx event
  222         """
  223 
  224         # Launch the user function wizard.
  225         uf_store['molecule.copy'](wx_parent=self.gui.spin_viewer, pipe_from=cdp_name(), mol_from=self.info['id'], pipe_to=cdp_name())
  226 
  227 
  228     def action_molecule_molecule_delete(self, event):
  229         """Wrapper method.
  230 
  231         @param event:   The wx event.
  232         @type event:    wx event
  233         """
  234 
  235         # Ask if this should be done.
  236         msg = "Are you sure you would like to delete this molecule?  This only affects the spin data, all structural data will remain.  This operation cannot be undone."
  237         if status.show_gui and Question(msg, parent=self.gui.spin_viewer, default=False, size=(400, 170)).ShowModal() == wx.ID_NO:
  238             return
  239 
  240         # Delete the molecule.
  241         self.gui.interpreter.queue('molecule.delete', gui_to_str(self.info['id']))
  242 
  243 
  244     def action_molecule_molecule_deselect(self, event):
  245         """Wrapper method.
  246 
  247         @param event:   The wx event.
  248         @type event:    wx event
  249         """
  250 
  251         # Ask if this should be done.
  252         msg = "Are you sure you would like to deselect all spins of this molecule?"
  253         if status.show_gui and Question(msg, parent=self.gui.spin_viewer, default=False).ShowModal() == wx.ID_NO:
  254             return
  255 
  256         # Deselect the molecule.
  257         self.gui.interpreter.queue('deselect.spin', spin_id=gui_to_str(self.info['id']), change_all=False)
  258 
  259 
  260     def action_molecule_molecule_name(self, event):
  261         """Wrapper method.
  262 
  263         @param event:   The wx event.
  264         @type event:    wx event
  265         """
  266 
  267         # Launch the user function wizard.
  268         uf_store['molecule.name'](wx_parent=self.gui.spin_viewer, mol_id=self.info['id'])
  269 
  270 
  271     def action_molecule_molecule_type(self, event):
  272         """Wrapper method.
  273 
  274         @param event:   The wx event.
  275         @type event:    wx event
  276         """
  277 
  278         # Get the current molecule type
  279         mol = return_molecule(self.info['id'])
  280         type = None
  281         if hasattr(mol, 'type'):
  282             type = mol.type
  283 
  284         # Launch the user function wizard.
  285         if type == None:
  286             uf_store['molecule.type'](wx_parent=self.gui.spin_viewer, mol_id=self.info['id'])
  287         else:
  288             uf_store['molecule.type'](wx_parent=self.gui.spin_viewer, mol_id=self.info['id'], type=type)
  289 
  290 
  291     def action_molecule_molecule_select(self, event):
  292         """Wrapper method.
  293 
  294         @param event:   The wx event.
  295         @type event:    wx event
  296         """
  297 
  298         # Ask if this should be done.
  299         msg = "Are you sure you would like to select all spins of this molecule?"
  300         if status.show_gui and Question(msg, parent=self.gui.spin_viewer, default=False).ShowModal() == wx.ID_NO:
  301             return
  302 
  303         # Select the molecule.
  304         self.gui.interpreter.queue('select.spin', spin_id=gui_to_str(self.info['id']), change_all=False)
  305 
  306 
  307     def action_molecule_residue_create(self, event):
  308         """Wrapper method.
  309 
  310         @param event:   The wx event.
  311         @type event:    wx event
  312         """
  313 
  314         # Launch the user function wizard.
  315         uf_store['residue.create'](wx_parent=self.gui.spin_viewer, mol_name=self.info['mol_name'])
  316 
  317 
  318     def action_residue_residue_copy(self, event):
  319         """Wrapper method.
  320 
  321         @param event:   The wx event.
  322         @type event:    wx event
  323         """
  324 
  325         # Launch the user function wizard.
  326         uf_store['residue.copy'](wx_parent=self.gui.spin_viewer, pipe_from=cdp_name(), res_from=self.info['id'], pipe_to=cdp_name())
  327 
  328 
  329     def action_residue_residue_delete(self, event):
  330         """Wrapper method.
  331 
  332         @param event:   The wx event.
  333         @type event:    wx event
  334         """
  335 
  336         # Ask if this should be done.
  337         msg = "Are you sure you would like to delete this residue?  This only affects the spin data, all structural data will remain.  This operation cannot be undone."
  338         if status.show_gui and Question(msg, parent=self.gui.spin_viewer, default=False, size=(400, 170)).ShowModal() == wx.ID_NO:
  339             return
  340 
  341         # Delete the residue.
  342         self.gui.interpreter.queue('residue.delete', gui_to_str(self.info['id']))
  343 
  344 
  345     def action_residue_residue_deselect(self, event):
  346         """Wrapper method.
  347 
  348         @param event:   The wx event.
  349         @type event:    wx event
  350         """
  351 
  352         # Ask if this should be done.
  353         msg = "Are you sure you would like to deselect all spins of this residue?"
  354         if status.show_gui and Question(msg, parent=self.gui.spin_viewer, default=False).ShowModal() == wx.ID_NO:
  355             return
  356 
  357         # Deselect the residue.
  358         self.gui.interpreter.queue('deselect.spin', spin_id=gui_to_str(self.info['id']), change_all=False)
  359 
  360 
  361     def action_residue_residue_name(self, event):
  362         """Wrapper method.
  363 
  364         @param event:   The wx event.
  365         @type event:    wx event
  366         """
  367 
  368         # Launch the user function wizard.
  369         uf_store['residue.name'](wx_parent=self.gui.spin_viewer, res_id=self.info['id'])
  370 
  371 
  372     def action_residue_residue_number(self, event):
  373         """Wrapper method.
  374 
  375         @param event:   The wx event.
  376         @type event:    wx event
  377         """
  378 
  379         # Launch the user function wizard.
  380         uf_store['residue.number'](wx_parent=self.gui.spin_viewer, res_id=self.info['id'])
  381 
  382 
  383     def action_residue_residue_select(self, event):
  384         """Wrapper method.
  385 
  386         @param event:   The wx event.
  387         @type event:    wx event
  388         """
  389 
  390         # Ask if this should be done.
  391         msg = "Are you sure you would like to select all spins of this residue?"
  392         if status.show_gui and Question(msg, parent=self.gui.spin_viewer, default=False).ShowModal() == wx.ID_NO:
  393             return
  394 
  395         # Select the residue.
  396         self.gui.interpreter.queue('select.spin', spin_id=gui_to_str(self.info['id']), change_all=False)
  397 
  398 
  399     def action_residue_spin_add(self, event):
  400         """Wrapper method.
  401 
  402         @param event:   The wx event.
  403         @type event:    wx event
  404         """
  405 
  406         # Launch the user function wizard.
  407         uf_store['spin.create'](wx_parent=self.gui.spin_viewer, mol_name=self.info['mol_name'], res_num=self.info['res_num'], res_name=self.info['res_name'])
  408 
  409 
  410     def action_residue_spin_create_pseudo(self, event):
  411         """Wrapper method.
  412 
  413         @param event:   The wx event.
  414         @type event:    wx event
  415         """
  416 
  417         # Launch the user function wizard.
  418         uf_store['spin.create_pseudo'](wx_parent=self.gui.spin_viewer, res_id=self.info['id'])
  419 
  420 
  421     def action_root_molecule_create(self, event):
  422         """Wrapper method.
  423 
  424         @param event:   The wx event.
  425         @type event:    wx event
  426         """
  427 
  428         # Launch the user function wizard.
  429         uf_store['molecule.create'](wx_parent=self.gui.spin_viewer)
  430 
  431 
  432     def action_spin_spin_copy(self, event):
  433         """Wrapper method.
  434 
  435         @param event:   The wx event.
  436         @type event:    wx event
  437         """
  438 
  439         # Launch the user function wizard.
  440         uf_store['spin.copy'](wx_parent=self.gui.spin_viewer, pipe_from=cdp_name(), spin_from=self.info['id'], pipe_to=cdp_name())
  441 
  442 
  443     def action_spin_spin_delete(self, event):
  444         """Wrapper method.
  445 
  446         @param event:   The wx event.
  447         @type event:    wx event
  448         """
  449 
  450         # Ask if this should be done.
  451         msg = "Are you sure you would like to delete this spin?  This only affects the spin data, all structural data will remain.  This operation cannot be undone."
  452         if status.show_gui and Question(msg, parent=self.gui.spin_viewer, default=False, size=(400, 170)).ShowModal() == wx.ID_NO:
  453             return
  454 
  455         # Delete the spin.
  456         self.gui.interpreter.queue('spin.delete', gui_to_str(self.info['id']))
  457 
  458 
  459     def action_spin_spin_deselect(self, event):
  460         """Wrapper method.
  461 
  462         @param event:   The wx event.
  463         @type event:    wx event
  464         """
  465 
  466         # Deselect the spin.
  467         self.gui.interpreter.queue('deselect.spin', spin_id=gui_to_str(self.info['id']), change_all=False)
  468 
  469 
  470     def action_spin_spin_element(self, event):
  471         """Wrapper method.
  472 
  473         @param event:   The wx event.
  474         @type event:    wx event
  475         """
  476 
  477         # Get the current spin element.
  478         spin = return_spin(spin_id=self.info['id'])
  479         element = None
  480         if hasattr(spin, 'element'):
  481             element = spin.element
  482 
  483         # Launch the user function wizard.
  484         uf_store['spin.element'](wx_parent=self.gui.spin_viewer, spin_id=self.info['id'], element=element)
  485 
  486 
  487     def action_spin_spin_name(self, event):
  488         """Wrapper method.
  489 
  490         @param event:   The wx event.
  491         @type event:    wx event
  492         """
  493 
  494         # Launch the user function wizard.
  495         uf_store['spin.name'](wx_parent=self.gui.spin_viewer, spin_id=self.info['id'])
  496 
  497 
  498     def action_spin_spin_number(self, event):
  499         """Wrapper method.
  500 
  501         @param event:   The wx event.
  502         @type event:    wx event
  503         """
  504 
  505         # Launch the user function wizard.
  506         uf_store['spin.number'](wx_parent=self.gui.spin_viewer, spin_id=self.info['id'])
  507 
  508 
  509     def action_spin_spin_select(self, event):
  510         """Wrapper method.
  511 
  512         @param event:   The wx event.
  513         @type event:    wx event
  514         """
  515 
  516         # Select the spin.
  517         self.gui.interpreter.queue('select.spin', spin_id=gui_to_str(self.info['id']), change_all=False)
  518 
  519 
  520     def get_info(self):
  521         """Get the python data structure associated with the current item.
  522 
  523         @return:    The dictionary of data.
  524         @rtype:     dict
  525         """
  526 
  527         # The current item.
  528         item = self.tree.GetSelection()
  529 
  530         # No data.
  531         if not item.IsOk():
  532             return
  533 
  534         # Return the associated python data.
  535         if dep_check.wx_classic:
  536             return self.tree.GetItemPyData(item)
  537         else:
  538             return self.tree.GetItemData(item)
  539 
  540 
  541     def menu_default(self):
  542         """The right click root menu."""
  543 
  544         # The menu.
  545         menu = wx.Menu()
  546 
  547         # The load spins entry.
  548         item = build_menu_item(menu, id=MENU_ROOT_LOAD_SPINS, text="Load spins", icon=fetch_icon("relax.spin", "16x16"))
  549         if status.exec_lock.locked():
  550             item.Enable(False)
  551 
  552         # The menu actions.
  553         self.Bind(wx.EVT_MENU, self.gui.spin_viewer.load_spins_wizard, id=MENU_ROOT_LOAD_SPINS)
  554 
  555         # Show the menu.
  556         if status.show_gui:
  557             self.PopupMenu(menu)
  558 
  559         # Cleanup.
  560         menu.Destroy()
  561 
  562 
  563     def menu_molecule(self):
  564         """The right click molecule menu."""
  565 
  566         # Init the item list.
  567         items = []
  568 
  569         # The menu.
  570         menu = wx.Menu()
  571 
  572         # Add some menu items for the spin user functions.
  573         items.append(build_menu_item(menu, id=MENU_MOLECULE_MOLECULE_COPY, text="&Copy the molecule", icon=fetch_icon("oxygen.actions.list-add")))
  574         items.append(build_menu_item(menu, id=MENU_MOLECULE_MOLECULE_DELETE, text="De&lete the molecule", icon=fetch_icon("oxygen.actions.list-remove")))
  575         items.append(build_menu_item(menu, id=MENU_MOLECULE_MOLECULE_NAME, text="&Name the molecule", icon=fetch_icon("oxygen.actions.edit-rename")))
  576         items.append(build_menu_item(menu, id=MENU_MOLECULE_MOLECULE_TYPE, text="Set the molecule &type", icon=fetch_icon("oxygen.actions.edit-rename")))
  577         items.append(build_menu_item(menu, id=MENU_MOLECULE_RESIDUE_CREATE, text="Add a &residue", icon=fetch_icon("oxygen.actions.list-add-relax-blue")))
  578 
  579         # Add the items and activate them.
  580         for item in items:
  581             if status.exec_lock.locked():
  582                 item.Enable(False)
  583 
  584         # Add a separator.
  585         menu.AppendSeparator()
  586 
  587         # Selection or deselection.
  588         if self.info['select']:
  589             item = build_menu_item(menu, id=MENU_MOLECULE_MOLECULE_DESELECT, text="&Deselect", icon=fetch_icon("relax.molecule_grey"))
  590         else:
  591             item = build_menu_item(menu, id=MENU_MOLECULE_MOLECULE_SELECT, text="&Select", icon=fetch_icon("relax.molecule"))
  592         if status.exec_lock.locked():
  593             item.Enable(False)
  594 
  595         # The menu actions.
  596         self.Bind(wx.EVT_MENU, self.action_molecule_molecule_copy, id=MENU_MOLECULE_MOLECULE_COPY)
  597         self.Bind(wx.EVT_MENU, self.action_molecule_molecule_delete, id=MENU_MOLECULE_MOLECULE_DELETE)
  598         self.Bind(wx.EVT_MENU, self.action_molecule_molecule_name, id=MENU_MOLECULE_MOLECULE_NAME)
  599         self.Bind(wx.EVT_MENU, self.action_molecule_molecule_type, id=MENU_MOLECULE_MOLECULE_TYPE)
  600         self.Bind(wx.EVT_MENU, self.action_molecule_residue_create, id=MENU_MOLECULE_RESIDUE_CREATE)
  601         if self.info['select']:
  602             self.Bind(wx.EVT_MENU, self.action_molecule_molecule_deselect, id=MENU_MOLECULE_MOLECULE_DESELECT)
  603         else:
  604             self.Bind(wx.EVT_MENU, self.action_molecule_molecule_select, id=MENU_MOLECULE_MOLECULE_SELECT)
  605 
  606         # Show the menu.
  607         if status.show_gui:
  608             self.PopupMenu(menu)
  609 
  610         # Cleanup.
  611         menu.Destroy()
  612 
  613 
  614     def menu_residue(self):
  615         """The right click molecule menu."""
  616 
  617         # Init the item list.
  618         items = []
  619 
  620         # The menu.
  621         menu = wx.Menu()
  622 
  623         # Add some menu items for the spin user functions.
  624         items.append(build_menu_item(menu, id=MENU_RESIDUE_RESIDUE_COPY, text="&Copy the residue", icon=fetch_icon("oxygen.actions.list-add")))
  625         items.append(build_menu_item(menu, id=MENU_RESIDUE_RESIDUE_DELETE, text="De&lete the residue", icon=fetch_icon("oxygen.actions.list-remove")))
  626         items.append(build_menu_item(menu, id=MENU_RESIDUE_RESIDUE_NAME, text="&Name the residue", icon=fetch_icon("oxygen.actions.edit-rename")))
  627         items.append(build_menu_item(menu, id=MENU_RESIDUE_RESIDUE_NUMBER, text="N&umber the residue", icon=fetch_icon("oxygen.actions.edit-rename")))
  628         items.append(build_menu_item(menu, id=MENU_RESIDUE_SPIN_ADD, text="&Add a spin", icon=fetch_icon("oxygen.actions.list-add-relax-blue")))
  629         items.append(build_menu_item(menu, id=MENU_RESIDUE_SPIN_CREATE_PSEUDO, text="Create a &pseudo-atom", icon=fetch_icon("oxygen.actions.list-add-relax-blue")))
  630 
  631         # Add the items and activate them.
  632         for item in items:
  633             if status.exec_lock.locked():
  634                 item.Enable(False)
  635 
  636         # Add a separator.
  637         menu.AppendSeparator()
  638 
  639         # Selection or deselection.
  640         if self.info['select']:
  641             item = build_menu_item(menu, id=MENU_RESIDUE_RESIDUE_DESELECT, text="&Deselect", icon=fetch_icon("relax.residue_grey"))
  642         else:
  643             item = build_menu_item(menu, id=MENU_RESIDUE_RESIDUE_SELECT, text="&Select", icon=fetch_icon("relax.residue"))
  644         if status.exec_lock.locked():
  645             item.Enable(False)
  646 
  647         # The menu actions.
  648         self.Bind(wx.EVT_MENU, self.action_residue_residue_copy, id=MENU_RESIDUE_RESIDUE_COPY)
  649         self.Bind(wx.EVT_MENU, self.action_residue_residue_delete, id=MENU_RESIDUE_RESIDUE_DELETE)
  650         self.Bind(wx.EVT_MENU, self.action_residue_residue_name, id=MENU_RESIDUE_RESIDUE_NAME)
  651         self.Bind(wx.EVT_MENU, self.action_residue_residue_number, id=MENU_RESIDUE_RESIDUE_NUMBER)
  652         self.Bind(wx.EVT_MENU, self.action_residue_spin_add, id=MENU_RESIDUE_SPIN_ADD)
  653         self.Bind(wx.EVT_MENU, self.action_residue_spin_create_pseudo, id=MENU_RESIDUE_SPIN_CREATE_PSEUDO)
  654         if self.info['select']:
  655             self.Bind(wx.EVT_MENU, self.action_residue_residue_deselect, id=MENU_RESIDUE_RESIDUE_DESELECT)
  656         else:
  657             self.Bind(wx.EVT_MENU, self.action_residue_residue_select, id=MENU_RESIDUE_RESIDUE_SELECT)
  658 
  659         # Show the menu.
  660         if status.show_gui:
  661             self.PopupMenu(menu)
  662 
  663         # Cleanup.
  664         menu.Destroy()
  665 
  666 
  667     def menu_root(self):
  668         """The right click root menu."""
  669 
  670         # Init the item list.
  671         items = []
  672 
  673         # The menu.
  674         menu = wx.Menu()
  675 
  676         # Add some menu items for the spin user functions.
  677         items.append(build_menu_item(menu, id=MENU_ROOT_MOLECULE_CREATE, text="&Add a molecule", icon=fetch_icon("oxygen.actions.list-add-relax-blue")))
  678         items.append(build_menu_item(menu, id=MENU_ROOT_LOAD_SPINS, text="&Load spins", icon=fetch_icon("relax.spin", "16x16")))
  679 
  680         # Add the items and activate them.
  681         for item in items:
  682             if status.exec_lock.locked():
  683                 item.Enable(False)
  684 
  685         # The menu actions.
  686         self.Bind(wx.EVT_MENU, self.action_root_molecule_create, id=MENU_ROOT_MOLECULE_CREATE)
  687         self.Bind(wx.EVT_MENU, self.gui.spin_viewer.load_spins_wizard, id=MENU_ROOT_LOAD_SPINS)
  688 
  689         # Show the menu.
  690         if status.show_gui:
  691             self.PopupMenu(menu)
  692 
  693         # Cleanup.
  694         menu.Destroy()
  695 
  696 
  697     def menu_spin(self):
  698         """The right click spin menu."""
  699 
  700         # Init the item list.
  701         items = []
  702 
  703         # The menu.
  704         menu = wx.Menu()
  705 
  706         # Add some menu items for the spin user functions.
  707         items.append(build_menu_item(menu, id=MENU_SPIN_SPIN_COPY, text="&Copy the spin", icon=fetch_icon("oxygen.actions.list-add")))
  708         items.append(build_menu_item(menu, id=MENU_SPIN_SPIN_DELETE, text="De&lete the spin", icon=fetch_icon("oxygen.actions.list-remove")))
  709         items.append(build_menu_item(menu, id=MENU_SPIN_SPIN_ELEMENT, text="Set the element &type of the spin", icon=fetch_icon("oxygen.actions.edit-rename")))
  710         items.append(build_menu_item(menu, id=MENU_SPIN_SPIN_NAME, text="&Name the spin", icon=fetch_icon("oxygen.actions.edit-rename")))
  711         items.append(build_menu_item(menu, id=MENU_SPIN_SPIN_NUMBER, text="N&umber the spin", icon=fetch_icon("oxygen.actions.edit-rename")))
  712 
  713         # Add the items and activate them.
  714         for item in items:
  715             if status.exec_lock.locked():
  716                 item.Enable(False)
  717 
  718         # Add a separator.
  719         menu.AppendSeparator()
  720 
  721         # Selection or deselection.
  722         if self.info['select']:
  723             item = build_menu_item(menu, id=MENU_SPIN_SPIN_DESELECT, text="&Deselect", icon=fetch_icon("relax.spin_grey"))
  724         else:
  725             item = build_menu_item(menu, id=MENU_SPIN_SPIN_SELECT, text="&Select", icon=fetch_icon("relax.spin"))
  726         if status.exec_lock.locked():
  727             item.Enable(False)
  728 
  729         # The menu actions.
  730         self.Bind(wx.EVT_MENU, self.action_spin_spin_copy, id=MENU_SPIN_SPIN_COPY)
  731         self.Bind(wx.EVT_MENU, self.action_spin_spin_delete, id=MENU_SPIN_SPIN_DELETE)
  732         self.Bind(wx.EVT_MENU, self.action_spin_spin_element, id=MENU_SPIN_SPIN_ELEMENT)
  733         self.Bind(wx.EVT_MENU, self.action_spin_spin_name, id=MENU_SPIN_SPIN_NAME)
  734         self.Bind(wx.EVT_MENU, self.action_spin_spin_number, id=MENU_SPIN_SPIN_NUMBER)
  735         if self.info['select']:
  736             self.Bind(wx.EVT_MENU, self.action_spin_spin_deselect, id=MENU_SPIN_SPIN_DESELECT)
  737         else:
  738             self.Bind(wx.EVT_MENU, self.action_spin_spin_select, id=MENU_SPIN_SPIN_SELECT)
  739 
  740         # Show the menu.
  741         if status.show_gui:
  742             self.PopupMenu(menu)
  743 
  744         # Cleanup.
  745         menu.Destroy()
  746 
  747 
  748     def prune_mol(self):
  749         """Remove any molecules which have been deleted."""
  750 
  751         # Get a list of molecule IDs from the relax data store.
  752         mol_ids = get_molecule_ids()
  753 
  754         # Find if the molecule has been removed.
  755         prune_list = []
  756         for key in self.tree_ids:
  757             # Get the python data.
  758             if dep_check.wx_classic:
  759                 info = self.tree.GetItemPyData(key)
  760             else:
  761                 info = self.tree.GetItemData(key)
  762 
  763             # No info.
  764             if info == None or 'id' not in info:
  765                 continue
  766 
  767             # Add to the prune list if it has been removed.
  768             if info['id'] not in mol_ids:
  769                 prune_list.append(key)
  770 
  771         # Delete the data.
  772         for key in prune_list:
  773             self.tree.Delete(key)
  774             self.tree_ids.pop(key)
  775 
  776 
  777     def prune_res(self, mol_branch_id, mol_id):
  778         """Remove any molecules which have been deleted.
  779 
  780         @param mol_branch_id:   The molecule branch ID of the wx.TreeCtrl object.
  781         @type mol_branch_id:    TreeItemId
  782         @param mol_id:          The molecule identification string.
  783         @type mol_id:           str
  784         """
  785 
  786         # Find if the molecule has been removed.
  787         prune_list = []
  788         for key in self.tree_ids[mol_branch_id]:
  789             # Get the python data.
  790             if dep_check.wx_classic:
  791                 info = self.tree.GetItemPyData(key)
  792             else:
  793                 info = self.tree.GetItemData(key)
  794 
  795             # No info.
  796             if info == None or 'id' not in info:
  797                 continue
  798 
  799             # Get the residue.
  800             res = return_residue(info['id'])
  801 
  802             # Add to the prune list if it has been removed or renamed/renumbered.
  803             if res == None or res.name != info['res_name'] or res.num != info['res_num']:
  804                 prune_list.append(key)
  805 
  806         # Delete the data.
  807         for key in prune_list:
  808             self.tree.Delete(key)
  809             self.tree_ids[mol_branch_id].pop(key)
  810 
  811 
  812     def prune_spin(self, mol_branch_id, res_branch_id, res_id):
  813         """Remove any spins which have been deleted.
  814 
  815         @param mol_branch_id:   The molecule branch ID of the wx.TreeCtrl object.
  816         @type mol_branch_id:    TreeItemId
  817         @param res_branch_id:   The residue branch ID of the wx.TreeCtrl object.
  818         @type res_branch_id:    TreeItemId
  819         @param res_id:          The residue identification string.
  820         @type res_id:           str
  821         """
  822 
  823         # Find if the molecule has been removed.
  824         prune_list = []
  825         for key in self.tree_ids[mol_branch_id][res_branch_id]:
  826             # Get the python data.
  827             if dep_check.wx_classic:
  828                 info = self.tree.GetItemPyData(key)
  829             else:
  830                 info = self.tree.GetItemData(key)
  831 
  832             # No info.
  833             if info == None or 'id' not in info:
  834                 continue
  835 
  836             # Get the spin.
  837             spin = return_spin(spin_id=info['id'])
  838 
  839             # Add to the prune list if it has been removed or renamed/renumbered.
  840             if spin == None or spin.name != info['spin_name'] or spin.num != info['spin_num']:
  841                 prune_list.append(key)
  842 
  843         # Delete the data.
  844         for key in prune_list:
  845             self.tree.Delete(key)
  846             self.tree_ids[mol_branch_id][res_branch_id].pop(key)
  847 
  848 
  849     def set_bitmap_mol(self, mol_branch_id, select=True):
  850         """Set the molecule bitmaps.
  851 
  852         @param mol_branch_id:   The molecule branch ID of the wx.TreeCtrl object.
  853         @type mol_branch_id:    TreeItemId
  854         @keyword select:        The selection flag.
  855         @type select:           bool
  856         """
  857 
  858         # The bitmaps for the selected state.
  859         if select:
  860             bmp = self.icon_mol_index
  861             bmp_unfold = self.icon_mol_unfold_index
  862 
  863         # The bitmaps for the deselected state.
  864         else:
  865             bmp = self.icon_mol_index_desel
  866             bmp_unfold = self.icon_mol_unfold_index_desel
  867 
  868         # Set the image.
  869         self.tree.SetItemImage(mol_branch_id, bmp, wx.TreeItemIcon_Normal)
  870         self.tree.SetItemImage(mol_branch_id, bmp_unfold, wx.TreeItemIcon_Expanded)
  871 
  872 
  873     def set_bitmap_res(self, res_branch_id, select=True):
  874         """Set the residue bitmaps.
  875 
  876         @param res_branch_id:   The residue branch ID of the wx.TreeCtrl object.
  877         @type res_branch_id:    TreeItemId
  878         @keyword select:        The selection flag.
  879         @type select:           bool
  880         """
  881 
  882         # The bitmaps for the selected state.
  883         if select:
  884             bmp = self.icon_res_index
  885 
  886         # The bitmaps for the deselected state.
  887         else:
  888             bmp = self.icon_res_index_desel
  889 
  890         # Set the image.
  891         self.tree.SetItemImage(res_branch_id, bmp, wx.TreeItemIcon_Normal & wx.TreeItemIcon_Expanded)
  892 
  893 
  894     def set_bitmap_spin(self, spin_branch_id, select=True):
  895         """Set the spin bitmaps.
  896 
  897         @param spin_branch_id:  The spin branch ID of the wx.TreeCtrl object.
  898         @type spin_branch_id:   TreeItemId
  899         @keyword select:        The selection flag.
  900         @type select:           bool
  901         """
  902 
  903         # The bitmaps for the selected state.
  904         if select:
  905             bmp = self.icon_spin_index
  906 
  907         # The bitmaps for the deselected state.
  908         else:
  909             bmp = self.icon_spin_index_desel
  910 
  911         # Set the image.
  912         self.tree.SetItemImage(spin_branch_id, bmp, wx.TreeItemIcon_Normal & wx.TreeItemIcon_Expanded)
  913 
  914 
  915     def update(self, pipe_name=None):
  916         """Update the tree view using the given data pipe."""
  917 
  918         # Acquire the pipe and spin locks.
  919         status.pipe_lock.acquire('spin viewer window')
  920         status.spin_lock.acquire('spin viewer window')
  921         try:
  922             # The data pipe.
  923             if not pipe_name:
  924                 pipe = cdp
  925             else:
  926                 pipe = get_pipe(pipe_name)
  927 
  928             # No data pipe, so delete everything and return.
  929             if not pipe:
  930                 self.tree.DeleteChildren(self.root)
  931                 return
  932 
  933             # Update the molecules.
  934             for mol, mol_id in molecule_loop(return_id=True):
  935                 self.update_mol(mol, mol_id)
  936 
  937             # Remove any deleted molecules.
  938             self.prune_mol()
  939 
  940         # Release the locks.
  941         finally:
  942             status.pipe_lock.release('spin viewer window')
  943             status.spin_lock.release('spin viewer window')
  944 
  945 
  946     def update_mol(self, mol, mol_id):
  947         """Update the given molecule in the tree.
  948 
  949         @param mol:     The molecule container.
  950         @type mol:      MoleculeContainer instance
  951         @param mol_id:  The molecule identification string.
  952         @type mol_id:   str
  953         """
  954 
  955         # Find the molecule, if it already exists.
  956         new_mol = True
  957         for key in self.tree_ids:
  958             # Get the python data.
  959             if dep_check.wx_classic:
  960                 data = self.tree.GetItemPyData(key)
  961             else:
  962                 data = self.tree.GetItemData(key)
  963 
  964             # No info.
  965             if data == None or 'id' not in data:
  966                 continue
  967 
  968             # Check the mol_id for a match and, if so, terminate to speed things up.
  969             if mol_id == data['id']:
  970                 new_mol = False
  971                 mol_branch_id = key
  972                 break
  973 
  974         # A new molecule.
  975         if new_mol:
  976             # Append a molecule with name to the tree.
  977             mol_branch_id = self.tree.AppendItem(self.root, "Molecule: %s" % mol.name)
  978 
  979             # The data to store.
  980             data = {
  981                 'type': 'mol',
  982                 'mol_name': mol.name,
  983                 'id': mol_id,
  984                 'select': is_mol_selected(mol_id)
  985             }
  986             if dep_check.wx_classic:
  987                 self.tree.SetPyData(mol_branch_id, data)
  988             else:
  989                 self.tree.SetItemData(mol_branch_id, data)
  990 
  991             # Add the id to the tracking structure.
  992             self.tree_ids[mol_branch_id] = {}
  993 
  994             # Set the bitmap.
  995             self.set_bitmap_mol(mol_branch_id, select=data['select'])
  996 
  997         # An old molecule.
  998         else:
  999             # Check the selection state.
 1000             select = is_mol_selected(data['id'])
 1001 
 1002             # Change of state.
 1003             if select != data['select']:
 1004                 # Store the new state.
 1005                 data['select'] = select
 1006 
 1007                 # Set the bitmap.
 1008                 self.set_bitmap_mol(mol_branch_id, select=data['select'])
 1009 
 1010         # Update the residues of this molecule.
 1011         for res, res_id in residue_loop(mol_id, return_id=True):
 1012             self.update_res(mol_branch_id, mol, res, res_id)
 1013 
 1014         # Start new molecules expanded.
 1015         if new_mol and data['select']:
 1016             self.tree.Expand(mol_branch_id)
 1017 
 1018         # Remove any deleted residues.
 1019         self.prune_res(mol_branch_id, mol_id)
 1020 
 1021         # Expand the root.
 1022         self.tree.Expand(self.root)
 1023 
 1024 
 1025     def update_res(self, mol_branch_id, mol, res, res_id):
 1026         """Update the given residue in the tree.
 1027 
 1028         @param mol_branch_id:   The molecule branch ID of the wx.TreeCtrl object.
 1029         @type mol_branch_id:    TreeItemId
 1030         @param mol:             The molecule container.
 1031         @type mol:              MoleculeContainer instance
 1032         @param res:             The residue container.
 1033         @type res:              ResidueContainer instance
 1034         @param res_id:          The residue identification string.
 1035         @type res_id:           str
 1036         """
 1037 
 1038         # Find the residue, if it already exists.
 1039         new_res = True
 1040         for key in self.tree_ids[mol_branch_id]:
 1041             # Get the python data.
 1042             if dep_check.wx_classic:
 1043                 data = self.tree.GetItemPyData(key)
 1044             else:
 1045                 data = self.tree.GetItemData(key)
 1046 
 1047             # No info.
 1048             if data == None or 'id' not in data:
 1049                 continue
 1050 
 1051             # Check the res_id, res name, and res number for a match and, if so, terminate to speed things up.
 1052             if res_id == data['id'] and res.name == data['res_name'] and res.num == data['res_num']:
 1053                 new_res = False
 1054                 res_branch_id = key
 1055                 break
 1056 
 1057         # A new residue.
 1058         if new_res:
 1059             # Append a residue with name and number to the tree.
 1060             res_branch_id = self.tree.AppendItem(mol_branch_id, "Residue: %s %s" % (res.num, res.name))
 1061 
 1062             # The data to store.
 1063             data = {
 1064                 'type': 'res',
 1065                 'mol_name': mol.name,
 1066                 'res_name': res.name,
 1067                 'res_num': res.num,
 1068                 'id': res_id,
 1069                 'select': is_res_selected(res_id)
 1070             }
 1071             if dep_check.wx_classic:
 1072                 self.tree.SetPyData(res_branch_id, data)
 1073             else:
 1074                 self.tree.SetItemData(res_branch_id, data)
 1075 
 1076             # Add the id to the tracking structure.
 1077             self.tree_ids[mol_branch_id][res_branch_id] = {}
 1078 
 1079             # Set the bitmap.
 1080             self.set_bitmap_res(res_branch_id, select=data['select'])
 1081 
 1082         # An old residue.
 1083         else:
 1084             # Check the selection state.
 1085             select = is_res_selected(data['id'])
 1086 
 1087             # Change of state.
 1088             if select != data['select']:
 1089                 # Store the new state.
 1090                 data['select'] = select
 1091 
 1092                 # Set the bitmap.
 1093                 self.set_bitmap_res(res_branch_id, select=data['select'])
 1094 
 1095         # Update the spins of this residue.
 1096         for spin, spin_id in spin_loop(res_id, return_id=True):
 1097             self.update_spin(mol_branch_id, res_branch_id, mol, res, spin, spin_id)
 1098 
 1099         # Start new residues expanded.
 1100         if new_res and data['select']:
 1101             self.tree.Expand(res_branch_id)
 1102 
 1103         # Remove any deleted spins.
 1104         self.prune_spin(mol_branch_id, res_branch_id, res_id)
 1105 
 1106 
 1107     def update_spin(self, mol_branch_id, res_branch_id, mol, res, spin, spin_id):
 1108         """Update the given spin in the tree.
 1109 
 1110         @param mol_branch_id:   The molecule branch ID of the wx.TreeCtrl object.
 1111         @type mol_branch_id:    TreeItemId
 1112         @param res_branch_id:   The residue branch ID of the wx.TreeCtrl object.
 1113         @type res_branch_id:    TreeItemId
 1114         @param mol:             The molecule container.
 1115         @type mol:              MoleculeContainer instance
 1116         @param res:             The residue container.
 1117         @type res:              ResidueContainer instance
 1118         @param spin:            The spin container.
 1119         @type spin:             SpinContainer instance
 1120         @param spin_id:         The spin identification string.
 1121         @type spin_id:          str
 1122         """
 1123 
 1124         # Find the spin, if it already exists.
 1125         new_spin = True
 1126         for key in self.tree_ids[mol_branch_id][res_branch_id]:
 1127             # Get the python data.
 1128             if dep_check.wx_classic:
 1129                 data = self.tree.GetItemPyData(key)
 1130             else:
 1131                 data = self.tree.GetItemData(key)
 1132 
 1133             # No info.
 1134             if data == None or 'id' not in data:
 1135                 continue
 1136 
 1137             # Check the spin_id, spin name and spin number for a match and, if so, terminate to speed things up.
 1138             if spin_id == data['id'] and spin.name == data['spin_name'] and spin.num == data['spin_num']:
 1139                 new_spin = False
 1140                 spin_branch_id = key
 1141                 break
 1142 
 1143         # A new spin.
 1144         if new_spin:
 1145             # Append a spin with name and number to the tree.
 1146             spin_branch_id = self.tree.AppendItem(res_branch_id, "Spin: %s %s" % (spin.num, spin.name))
 1147 
 1148             # The data to store.
 1149             data = {
 1150                 'type': 'spin',
 1151                 'mol_name': mol.name,
 1152                 'res_name': res.name,
 1153                 'res_num': res.num,
 1154                 'spin_name': spin.name,
 1155                 'spin_num': spin.num,
 1156                 'id': spin_id,
 1157                 'select': is_spin_selected(spin_id)
 1158             }
 1159             if dep_check.wx_classic:
 1160                 self.tree.SetPyData(spin_branch_id, data)
 1161             else:
 1162                 self.tree.SetItemData(spin_branch_id, data)
 1163 
 1164             # Add the id to the tracking structure.
 1165             self.tree_ids[mol_branch_id][res_branch_id][spin_branch_id] = True
 1166 
 1167             # Set the bitmap.
 1168             self.set_bitmap_spin(spin_branch_id, select=data['select'])
 1169 
 1170         # An old spin.
 1171         else:
 1172             # Check the selection state.
 1173             select = is_spin_selected(data['id'])
 1174 
 1175             # Change of state.
 1176             if select != data['select']:
 1177                 # Store the new state.
 1178                 data['select'] = select
 1179 
 1180                 # Set the bitmap.
 1181                 self.set_bitmap_spin(spin_branch_id, select=data['select'])
 1182 
 1183         # Start new spins expanded.
 1184         if new_spin and data['select']:
 1185             self.tree.Expand(spin_branch_id)