Commit 976dedb9 authored by eric pellegrini's avatar eric pellegrini
Browse files

The userdefinition viewer, the registry viewer and the periodictable

viewer are no longer plugins.
Improved significantly the ergonomy of the periodictable viewer
The user can now performs several atom selection and transmutation.
Improved the atom list widget
Fixed several bugs
Added new icons
parent a8429c86
......@@ -36,7 +36,7 @@ import operator
import numpy
from MDANSE.Framework.UserDefinitionsStore import UD_STORE
from MDANSE.Framework.Configurators.IConfigurator import IConfigurator, ConfiguratorError
from MDANSE.Framework.Configurators.IConfigurator import IConfigurator
from MDANSE.Framework.AtomSelectionParser import AtomSelectionParser
# The granularities at which the selection will be performed
......@@ -76,20 +76,26 @@ class AtomSelectionConfigurator(IConfigurator):
trajConfig = configuration[self._dependencies['trajectory']]
if value is None:
value = 'all'
elif not isinstance(value,basestring):
raise ConfiguratorError("invalid type for atom selection. Must be a string", self)
value = ['all']
if isinstance(value,basestring):
value = [value]
self["value"] = value
if UD_STORE.has_definition(trajConfig["basename"],"atom_selection",value):
ud = UD_STORE.get_definition(trajConfig["basename"],"atom_selection",value)
self.update(ud)
else:
parser = AtomSelectionParser(trajConfig["instance"].universe)
self["indexes"] = parser.parse(value)
self["expression"] = value
self["indexes"] = []
for v in value:
if UD_STORE.has_definition(trajConfig["basename"],"atom_selection",v):
ud = UD_STORE.get_definition(trajConfig["basename"],"atom_selection",v)
self["indexes"].append(ud["indexes"])
else:
parser = AtomSelectionParser(trajConfig["instance"].universe)
self["indexes"].append(parser.parse(v))
self["indexes"].sort()
self["n_selected_atoms"] = len(self["indexes"])
atoms = sorted(trajConfig["universe"].atomList(), key = operator.attrgetter('index'))
selectedAtoms = [atoms[idx] for idx in self["indexes"]]
......
......@@ -84,25 +84,19 @@ class AtomTransmutationConfigurator(IConfigurator):
trajConfig = configuration[self._dependencies['trajectory']]
parser = AtomSelectionParser(trajConfig["instance"].universe)
# If the input value is a dictionary, it must have a selection string as key and the element
# to be transmutated to as value
if isinstance(value,dict):
for expression,element in value.items():
indexes = parser.parse(element,expression)
self.transmutate(configuration, indexes, element)
# Otherwise, it must be a string that will be found as a user-definition keys
elif isinstance(value,basestring):
if UD_STORE.has_definition(trajConfig["basename"],"atom_transmutation",value):
ud = UD_STORE.get_definition(trajConfig["basename"],"atom_transmutation",value)
self.transmutate(configuration, ud["indexes"], ud["element"])
for expression,element in value:
# Otherwise, it must be a string that will be found as a user-definition keys
if not isinstance(expression,basestring):
raise ConfiguratorError("Wrong format for atom transmutation configurator.",self)
if UD_STORE.has_definition(trajConfig["basename"],"atom_transmutation",expression):
ud = UD_STORE.get_definition(trajConfig["basename"],"atom_selection",expression)
self.transmutate(configuration, ud["indexes"], element)
else:
raise ConfiguratorError("wrong parameters type: must be either a dictionary whose keys are an atom selection string and values are the target element \
or a list of string that match an user definition",self)
else:
raise ConfiguratorError("wrong parameters type: must be either a dictionary whose keys are an atom selection string and values are the target element \
or a list of string that match an user definition",self)
indexes = parser.parse(expression)
self.transmutate(configuration, indexes, element)
def transmutate(self, configuration, selection, element):
'''
......
#MDANSE : Molecular Dynamics Analysis for Neutron Scattering Experiments
#------------------------------------------------------------------------------------------
#Copyright (C)
#2015- Eric C. Pellegrini Institut Laue-Langevin
#BP 156
#6, rue Jules Horowitz
#38042 Grenoble Cedex 9
#France
#pellegrini[at]ill.fr
#goret[at]ill.fr
#aoun[at]ill.fr
#
#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 2.1 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
'''
Created on Mar 30, 2015
:author: Eric C. Pellegrini
'''
import collections
import os
import wx
import wx.aui as wxaui
from MDANSE import LOGGER, REGISTRY
from MDANSE.Externals.pubsub import pub
from MDANSE.Framework.AtomSelectionParser import AtomSelectionParser, AtomSelectionParserError
from MDANSE.Framework.Plugins.DataPlugin import get_data_plugin
from MDANSE.Framework.Widgets.UserDefinitionWidget import UserDefinitionsPlugin
from MDANSE.MolecularDynamics.Trajectory import sorted_atoms
class Query(object):
def __init__(self):
self._list = []
self._parser = None
def get_expression(self):
s = []
for v in self._list:
if isinstance(v, list):
keyword,arguments = v
arguments = ",".join([str(val) for val in arguments])
s.append("%s %s" % (keyword,arguments))
else:
s.append(v)
return "".join(s)
@property
def list(self):
return self._list
@property
def parser(self):
return self._parser
def add_operator(self, operator):
if operator == "(":
if self._list:
if self._list[-1] == ")" or isinstance(self._list[-1], list):
return
self._list.append("(")
elif operator == ")":
if self._list:
if self._list[-1] == ")" or isinstance(self._list[-1], list):
if self._list.count(")") < self._list.count("("):
self._list.append(")")
elif operator == "not":
if self._list:
if self._list[-1] == ")":
return
self._list.append(" not ")
elif operator in ["and","or"]:
if self._list:
if self._list[-1] == ")" or isinstance(self._list[-1], list):
self._list.append(" %s " % operator)
def add_query(self, query):
if not self._list:
if query is not None:
self._list.append(query)
else:
keyword, values = query
if isinstance(self._list[-1], list):
if self._list[-1][0] == keyword:
if values:
self._list[-1] = query
else:
del self._list[-1]
else:
self._list.extend([' or ', query])
elif isinstance(self._list[-1], basestring):
if self._list[-1] in [' and ',' or ','(',' not ']:
self._list.append(query)
def clear(self):
self._list = []
def is_empty(self):
return len(self._list) == 0
def parse(self):
if self._parser is None:
return []
try:
expression = self.get_expression()
selection = self._parser.parse(expression)
except AtomSelectionParserError:
return ("",[])
else:
return (expression,selection)
def pop(self):
return self._list.pop()
def set_parser(self, parser):
self._parser = parser
class AtomSelectionPlugin(UserDefinitionsPlugin):
type = 'atom_selection'
label = "Atom selection"
ancestor = ["molecular_viewer"]
def __init__(self, parent, *args, **kwargs):
self._query = Query()
self._selectors = {}
self._selection = []
pub.subscribe(self.msg_select_atoms_from_viewer, 'msg_select_atoms_from_viewer')
UserDefinitionsPlugin.__init__(self,parent,size=(800,500))
def build_panel(self):
self._mainPanel = wx.ScrolledWindow(self, wx.ID_ANY, size=self.GetSize())
self._mainPanel.SetScrollbars(20,20,50,50)
sizer = wx.BoxSizer(wx.VERTICAL)
settingsPanel = wx.Panel(self._mainPanel,wx.ID_ANY)
# Build the widgets used to build a selection from selection strings and operators
self._queryPanel = wx.Panel(settingsPanel)
self.filterTree = wx.TreeCtrl(self._queryPanel, 1, wx.DefaultPosition, style=wx.TR_HIDE_ROOT|wx.TR_HAS_BUTTONS)
root = self.filterTree.AddRoot("filters")
filters = self.filterTree.AppendItem(root, "Filter by")
selectors = REGISTRY["selector"].values()
self.__filters = collections.OrderedDict()
for selector in selectors:
if selector.section is not None:
if self.__filters.has_key(selector.section):
self.__filters[selector.section].append(selector.type)
else:
self.__filters[selector.section] = [selector.type]
for section, subsections in sorted(self.__filters.items()):
section_node = self.filterTree.AppendItem(filters, section)
for subsection in sorted(subsections):
self.filterTree.AppendItem(section_node,subsection)
self.values = wx.ListBox(self._queryPanel, wx.ID_ANY, style = wx.LB_MULTIPLE|wx.LB_NEEDED_SB)
leftBraceLinker = wx.Button(self._queryPanel, wx.ID_ANY, label = "(")
rightBraceLinker = wx.Button(self._queryPanel, wx.ID_ANY, label = ")")
andLinker = wx.Button(self._queryPanel, wx.ID_ANY, label = "and")
orLinker = wx.Button(self._queryPanel, wx.ID_ANY, label = "or")
notLinker = wx.Button(self._queryPanel, wx.ID_ANY, label = "not")
selectionStringSizer = wx.BoxSizer(wx.VERTICAL)
keywordsValuesSizer = wx.BoxSizer(wx.HORIZONTAL)
keywordsValuesSizer.Add(self.filterTree, 1, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT|wx.EXPAND, 2)
keywordsValuesSizer.Add(self.values, 3, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT|wx.EXPAND, 2)
linkersSizer = wx.BoxSizer(wx.HORIZONTAL)
linkersSizer.Add(leftBraceLinker, 1, flag=wx.ALL|wx.EXPAND)
linkersSizer.Add(rightBraceLinker, 1, flag=wx.ALL|wx.EXPAND)
linkersSizer.Add(andLinker, 1, flag=wx.ALL|wx.EXPAND)
linkersSizer.Add(orLinker, 1, flag=wx.ALL|wx.EXPAND)
linkersSizer.Add(notLinker, 1, flag=wx.ALL|wx.EXPAND)
selectionStringSizer.Add(keywordsValuesSizer,1,wx.ALL|wx.EXPAND,5)
selectionStringSizer.Add(linkersSizer,0,wx.ALL|wx.EXPAND,5)
self._queryPanel.SetSizer(selectionStringSizer)
selectionSizer = wx.BoxSizer(wx.VERTICAL)
selectionSizer.Add(self._queryPanel,1,wx.ALL|wx.EXPAND,5)
settingsPanel.SetSizer(selectionSizer)
# The widgets related to the selection being performed
selectionExpressionStaticBox = wx.StaticBox(self._mainPanel, wx.ID_ANY, label = "Selection")
selectionExpressionStaticBoxSizer = wx.StaticBoxSizer(selectionExpressionStaticBox, wx.HORIZONTAL)
self.selectionTextCtrl = wx.TextCtrl(self._mainPanel, wx.ID_ANY, style = wx.TE_READONLY)
clearButton = wx.Button(self._mainPanel, wx.ID_ANY, label="Clear")
self._selectionExpressionSizer = wx.GridBagSizer(5,5)
self._selectionExpressionSizer.AddGrowableCol(0)
self._selectionExpressionSizer.Add(self.selectionTextCtrl, pos=(0,0), span=(1,2),flag=wx.ALL|wx.EXPAND)
self._selectionExpressionSizer.Add(clearButton, pos=(0,2), flag=wx.ALL)
selectionExpressionStaticBoxSizer.Add(self._selectionExpressionSizer,1,wx.ALL|wx.EXPAND,5)
self._selectionSummary = wx.TextCtrl(self._mainPanel,wx.ID_ANY,style=wx.TE_LINEWRAP|wx.TE_MULTILINE|wx.TE_READONLY)
sizer.Add(settingsPanel, 2, wx.ALL|wx.EXPAND, 5)
sizer.Add(selectionExpressionStaticBoxSizer, 0, wx.ALL|wx.EXPAND, 5)
sizer.Add(self._selectionSummary, 1, wx.ALL|wx.EXPAND, 5)
self._mainPanel.SetSizer(sizer)
self._mainSizer = wx.BoxSizer(wx.VERTICAL)
self._mainSizer.Add(self._mainPanel, 1, wx.EXPAND|wx.ALL, 5)
self.SetSizer(self._mainSizer)
self._mgr.AddPane(self._mainPanel, wxaui.AuiPaneInfo().DestroyOnClose().Center().Dock().CaptionVisible(False).CloseButton(False).BestSize(self.GetSize()))
self._mgr.Update()
self.Bind(wx.EVT_TREE_SEL_CHANGED, self.on_display_keyword_values, self.filterTree)
self.Bind(wx.EVT_LISTBOX, self.on_insert_keyword_values, self.values)
self.Bind(wx.EVT_BUTTON, self.on_add_operator, leftBraceLinker)
self.Bind(wx.EVT_BUTTON, self.on_add_operator, rightBraceLinker)
self.Bind(wx.EVT_BUTTON, self.on_add_operator, orLinker)
self.Bind(wx.EVT_BUTTON, self.on_add_operator, andLinker)
self.Bind(wx.EVT_BUTTON, self.on_add_operator, notLinker)
self.Bind(wx.EVT_BUTTON, self.on_clear, clearButton)
def plug(self):
self.parent.mgr.GetPane(self).Float().Dockable(False).CloseButton(True).BestSize((600,600))
self.parent.mgr.Update()
self.set_trajectory(self.dataproxy.data)
def set_trajectory(self,trajectory):
self._trajectory = trajectory
self._query.set_parser(AtomSelectionParser(self._trajectory.universe))
self._atoms = sorted_atoms(self._trajectory.universe)
self._target = os.path.basename(self._trajectory.filename)
def display_selection_summary(self):
self._selectionSummary.Clear()
nSelectedAtoms = len(self._selection)
self._selectionSummary.AppendText("Number of selected atoms: %d\n\n" % nSelectedAtoms)
if nSelectedAtoms == 0:
return
self._selectionSummary.AppendText("List of selected atoms:\n")
for idx in self._selection:
self._selectionSummary.AppendText("\t%s (MMTK index: %d)\n" % (self._atoms[idx].fullName(),self._atoms[idx].index))
def insert_keyword_values(self, keyword, values):
self._query.add_query([keyword,values])
self.set_selection()
self.selectionTextCtrl.SetValue(self._query.get_expression())
self.display_selection_summary()
def close(self):
pub.sendMessage('msg_clear_selection',plugin=self)
def on_add_operator(self, event=None):
operator = event.GetEventObject().GetLabel()
self._query.add_operator(operator)
self.selectionTextCtrl.SetValue(self._query.get_expression())
self.values.DeselectAll()
def on_browse_python_script(self, event):
dlg = wx.FileDialog(self, "Browse python selection script", defaultDir=os.getcwd(), wildcard="Python files (*.py)|*.py",style=wx.FD_OPEN|wx.FD_MULTIPLE)
if dlg.ShowModal() == wx.ID_OK:
self.insert_keyword_values("pythonscript", dlg.GetPaths())
def on_clear(self, event=None):
self._query.clear()
self.selectionTextCtrl.Clear()
self.values.DeselectAll()
self._selectionSummary.Clear()
pub.sendMessage('msg_clear_selection',plugin=self)
def on_display_keyword_values(self, event=None):
if self._trajectory is None:
return
item = event.GetItem()
selectionFilter = self.filterTree.GetItemText(item)
if not REGISTRY["selector"].has_key(selectionFilter):
return
if not self._selectors.has_key(selectionFilter):
self._selectors[selectionFilter] = [str(v) for v in REGISTRY["selector"][selectionFilter](self._trajectory.universe).choices]
self.values.Set(self._selectors[selectionFilter])
def on_insert_keyword_values(self, event):
item = self.filterTree.GetSelection()
keyword = str(self.filterTree.GetItemText(item))
values = [str(self.values.GetString(v)) for v in self.values.GetSelections()]
self.insert_keyword_values(keyword, values)
def on_mode_selection(self, event=None):
self.selectedMode = str(event.GetEventObject().GetLabelText()).lower()
self.keywords.DeselectAll()
self.values.DeselectAll()
def msg_select_atoms_from_viewer(self, message):
dataPlugin,selection = message
if dataPlugin != get_data_plugin(self):
return
self._query.clear()
self.insert_keyword_values("atom_picked", selection)
def set_selection(self):
_, self._selection = self._query.parse()
pub.sendMessage("msg_set_selection", plugin=self)
@property
def selection(self):
return self._selection
def validate(self):
if not self._selection:
LOGGER("The current selection is empty", "error", ["dialog"])
return None
return {'indexes':self._selection}
\ No newline at end of file
#MDANSE : Molecular Dynamics Analysis for Neutron Scattering Experiments
#------------------------------------------------------------------------------------------
#Copyright (C)
#2015- Eric C. Pellegrini Institut Laue-Langevin
#BP 156
#6, rue Jules Horowitz
#38042 Grenoble Cedex 9
#France
#pellegrini[at]ill.fr
#goret[at]ill.fr
#aoun[at]ill.fr
#
#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 2.1 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
'''
Created on Mar 30, 2015
:author: Eric C. Pellegrini
'''
from MDANSE.Framework.Plugins.AtomSelectionPlugin import AtomSelectionPlugin
class AtomTransmutationPlugin(AtomSelectionPlugin):
type = 'atom_transmutation'
label = "Atom transmutation"
ancestor = ["molecular_viewer"]
udType = "atom_selection"
\ No newline at end of file
#MDANSE : Molecular Dynamics Analysis for Neutron Scattering Experiments
#------------------------------------------------------------------------------------------
#Copyright (C)
#2015- Eric C. Pellegrini Institut Laue-Langevin
#BP 156
#6, rue Jules Horowitz
#38042 Grenoble Cedex 9
#France
#pellegrini[at]ill.fr
#goret[at]ill.fr
#aoun[at]ill.fr
#
#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 2.1 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
'''
Created on Jun 30, 2015
:author: Eric C. Pellegrini
'''
import os
import wx
import wx.aui as wxaui
from MDANSE import LOGGER
from MDANSE.Framework.Widgets.UserDefinitionWidget import UserDefinitionsPlugin
from MDANSE.MolecularDynamics.Trajectory import find_atoms_in_molecule, get_chemical_objects_dict
class AtomNameDropTarget(wx.TextDropTarget):
def __init__(self, molTree,atList):