1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
import os
import numpy as np
import ase.gui.ui as ui
from ase import Atoms
from ase.data import atomic_numbers, chemical_symbols
from ase.gui.i18n import _
current_selection_string = _('(selection)')
class AddAtoms:
def __init__(self, gui):
self.gui = gui
win = self.win = ui.Window(_('Add atoms'), wmtype='utility')
win.add(_('Specify chemical symbol, formula, or filename.'))
def choose_file():
chooser = ui.ASEFileChooser(self.win.win)
filename = chooser.go()
if filename is None: # No file selected
return
self.combobox.value = filename
# Load the file immediately, so we can warn now in case of error
self.readfile(filename, format=chooser.format)
if self.gui.images.selected.any():
default = current_selection_string
else:
default = 'H2'
self._filename = None
self._atoms_from_file = None
from ase.collections import g2
labels = sorted(name for name in g2.names
if len(g2[name]) > 1)
values = labels
combobox = ui.ComboBox(labels, values)
win.add([_('Add:'), combobox,
ui.Button(_('File ...'), callback=choose_file)])
combobox.widget.bind('<Return>', lambda e: self.add())
combobox.value = default
self.combobox = combobox
spinners = [ui.SpinBox(0.0, -1e3, 1e3, 0.1, rounding=2, width=3)
for __ in range(3)]
win.add([_('Coordinates:')] + spinners)
self.spinners = spinners
win.add(_('Coordinates are relative to the center of the selection, '
'if any, else absolute.'))
self.picky = ui.CheckButton(_('Check positions'), True)
win.add([ui.Button(_('Add'), self.add),
self.picky])
self.focus()
def readfile(self, filename, format=None):
if filename == self._filename:
# We have this file already
return self._atoms_from_file
from ase.io import read
try:
atoms = read(filename)
except Exception as err:
ui.show_io_error(filename, err)
atoms = None
filename = None
# Cache selected Atoms/filename (or None) for future calls
self._atoms_from_file = atoms
self._filename = filename
return atoms
def get_atoms(self):
# Get the text, whether it's a combobox item or not
val = self.combobox.widget.get()
if val == current_selection_string:
selection = self.gui.images.selected.copy()
if selection.any():
atoms = self.gui.atoms.copy()
return atoms[selection[:len(self.gui.atoms)]]
if val in atomic_numbers: # Note: This means val is a symbol!
return Atoms(val)
if val.isdigit() and int(val) < len(chemical_symbols):
return Atoms(numbers=[int(val)])
from ase.collections import g2
if val in g2.names:
return g2[val]
if os.path.exists(val):
return self.readfile(val) # May show UI error
ui.showerror(_('Cannot add atoms'),
_('{} is neither atom, molecule, nor file')
.format(val))
return None
def getcoords(self):
addcoords = np.array([spinner.value for spinner in self.spinners])
pos = self.gui.atoms.positions
if self.gui.images.selected[:len(pos)].any():
pos = pos[self.gui.images.selected[:len(pos)]]
center = pos.mean(0)
addcoords += center
return addcoords
def focus(self):
self.combobox.widget.focus_set()
def add(self):
newatoms = self.get_atoms()
if newatoms is None: # Error dialog was shown
return
newcenter = self.getcoords()
# Not newatoms.center() because we want the same centering method
# used for adding atoms relative to selections (mean).
previous_center = newatoms.positions.mean(0)
newatoms.positions += newcenter - previous_center
atoms = self.gui.atoms
if len(atoms) and self.picky.value:
from ase.geometry import get_distances
_disps, dists = get_distances(atoms.positions,
newatoms.positions)
mindist = dists.min()
if mindist < 0.5:
ui.showerror(_('Bad positions'),
_('Atom would be less than 0.5 Å from '
'an existing atom. To override, '
'uncheck the check positions option.'))
return
self.gui.add_atoms_and_select(newatoms)
|