Coverage for /builds/ase/ase/ase/gui/celleditor.py: 84.31%
102 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
3'''celleditor.py - Window for editing the cell of an atoms object
4'''
5import numpy as np
7import ase.gui.ui as ui
8from ase.cell import Cell
9from ase.gui.i18n import _
12class CellEditor:
13 '''Window for editing the cell of an atoms object.'''
15 def __init__(self, gui):
16 self.gui = gui
17 self.gui.obs.set_atoms.register(self.notify_atoms_changed)
19 # Create grid control for cells
20 # xx xy xz ||x|| pbc
21 # yx yy yz ||y|| pbc
22 # zx zy zz ||z|| pbc
23 self.cell_grid = []
24 self.pbc = []
25 self.angles = []
27 atoms = self.gui.atoms
29 cell = atoms.cell
30 mags = cell.lengths()
31 angles = cell.angles()
32 pbc = atoms.pbc
34 for i in [0, 1, 2]: # x_ y_ z_
35 row = []
36 for j in [0, 1, 2]: # _x _y _z
37 row.append(ui.SpinBox(cell[i][j], -30, 30, 0.1,
38 self.apply_vectors, rounding=7, width=9))
39 row.append(ui.SpinBox(mags[i], -30, 30, 0.1, self.apply_magnitudes,
40 rounding=7, width=9))
41 self.cell_grid.append(row)
42 self.pbc.append(ui.CheckButton('', bool(pbc[i]), self.apply_pbc))
43 self.angles.append(ui.SpinBox(angles[i], -360, 360, 15,
44 self.apply_angles,
45 rounding=7, width=9))
47 self.scale_atoms = ui.CheckButton('', False)
48 self.vacuum = ui.SpinBox(5, 0, 15, 0.1, self.apply_vacuum)
50 # TRANSLATORS: This is a title of a window.
51 win = self.win = ui.Window(_('Cell Editor'), wmtype='utility')
53 x, y, z = self.cell_grid
55 win.add([_('A:'), x[0], x[1], x[2], _('||A||:'), x[3],
56 _('periodic:'), self.pbc[0]])
57 win.add([_('B:'), y[0], y[1], y[2], _('||B||:'), y[3],
58 _('periodic:'), self.pbc[1]])
59 win.add([_('C:'), z[0], z[1], z[2], _('||C||:'), z[3],
60 _('periodic:'), self.pbc[2]])
61 win.add([_('∠BC:'), self.angles[0], _('∠AC:'), self.angles[1],
62 _('∠AB:'), self.angles[2]])
63 win.add([_('Scale atoms with cell:'), self.scale_atoms])
64 win.add([ui.Button(_('Apply Vectors'), self.apply_vectors),
65 ui.Button(_('Apply Magnitudes'), self.apply_magnitudes),
66 ui.Button(_('Apply Angles'), self.apply_angles)])
67 win.add([_('Pressing 〈Enter〉 as you enter values will '
68 'automatically apply correctly')])
69 # TRANSLATORS: verb
70 win.add([ui.Button(_('Center'), self.apply_center),
71 ui.Button(_('Wrap'), self.apply_wrap),
72 _('Vacuum:'), self.vacuum,
73 ui.Button(_('Apply Vacuum'), self.apply_vacuum)])
75 def apply_center(self, *args):
76 atoms = self.gui.atoms.copy()
77 atoms.center()
78 self.gui.new_atoms(atoms)
80 def apply_wrap(self, *args):
81 atoms = self.gui.atoms.copy()
82 atoms.wrap()
83 self.gui.new_atoms(atoms)
85 def apply_vacuum(self, *args):
86 atoms = self.gui.atoms.copy()
88 axis = []
89 for index, pbc in enumerate(atoms.pbc):
90 if not pbc:
91 axis.append(index)
93 atoms.center(vacuum=self.vacuum.value, axis=axis)
94 self.gui.new_atoms(atoms)
96 def apply_vectors(self, *args):
97 atoms = self.gui.atoms.copy()
99 atoms.set_cell(self.get_vectors(),
100 scale_atoms=self.scale_atoms.var.get())
101 self.gui.new_atoms(atoms)
103 def get_vectors(self):
104 x, y, z = self.cell_grid
105 cell = np.array(
106 [[x[0].value, x[1].value, x[2].value],
107 [y[0].value, y[1].value, y[2].value],
108 [z[0].value, z[1].value, z[2].value]]
109 )
110 return Cell(cell)
112 def get_magnitudes(self):
113 x, y, z = self.cell_grid
114 return np.array([x[3].value, y[3].value, z[3].value])
116 def apply_magnitudes(self, *args):
117 atoms = self.gui.atoms.copy()
119 old_mags = atoms.cell.lengths()
120 new_mags = self.get_magnitudes()
122 newcell = atoms.cell.copy()
123 for i in range(3):
124 newcell[i] *= new_mags[i] / old_mags[i]
126 atoms.set_cell(newcell,
127 scale_atoms=self.scale_atoms.var.get())
129 self.gui.new_atoms(atoms)
131 def apply_angles(self, *args):
132 atoms = self.gui.atoms.copy()
134 cell_data = atoms.cell.cellpar()
135 cell_data[3:7] = [self.angles[0].value, self.angles[1].value,
136 self.angles[2].value]
138 atoms.set_cell(cell_data, scale_atoms=self.scale_atoms.var.get())
140 self.gui.new_atoms(atoms)
142 def apply_pbc(self, *args):
143 atoms = self.gui.atoms.copy()
145 pbc = [pbc.var.get() for pbc in self.pbc]
146 atoms.set_pbc(pbc)
148 self.gui.new_atoms(atoms)
150 def notify_atoms_changed(self):
151 atoms = self.gui.atoms
152 self.update(atoms.cell, atoms.pbc)
154 def update(self, cell, pbc):
155 cell = Cell(cell)
156 mags = cell.lengths()
157 angles = cell.angles()
159 for i in range(3):
160 for j in range(3):
161 if np.isnan(cell[i][j]):
162 cell[i][j] = 0
163 self.cell_grid[i][j].value = cell[i][j]
165 if np.isnan(mags[i]):
166 mags[i] = 0
167 self.cell_grid[i][3].value = mags[i]
169 if np.isnan(angles[i]):
170 angles[i] = 0
171 self.angles[i].value = angles[i]
173 self.pbc[i].var.set(bool(pbc[i]))