Coverage for ase / gui / movie.py: 61.54%
65 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-04 10:20 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-04 10:20 +0000
1# fmt: off
3import numpy as np
5import ase.gui.ui as ui
6from ase.gui.i18n import _
9class Movie:
10 def __init__(self, gui):
11 self.win = win = ui.Window(_('Movie'), close=self.close)
12 win.add(_('Image number:'))
13 self.frame_number = ui.Scale(gui.frame, 0,
14 len(gui.images) - 1,
15 callback=self.new_frame)
16 win.add(self.frame_number)
18 win.add([ui.Button(_('First'), self.click, -1, True),
19 ui.Button(_('Back'), self.click, -1),
20 ui.Button(_('Forward'), self.click, 1),
21 ui.Button(_('Last'), self.click, 1, True)])
23 play = ui.Button(_('Play'), self.play)
24 stop = ui.Button(_('Stop'), self.stop)
26 # TRANSLATORS: This function plays an animation forwards and backwards
27 # alternatingly, e.g. for displaying vibrational movement
28 self.rock = ui.CheckButton(_('Rock'), value=gui.config['movie_rock'])
30 win.add([play, stop, self.rock])
32 if len(gui.images) > 150:
33 skipdefault = len(gui.images) // 150
34 tdefault = min(max(len(gui.images) / (skipdefault * 5.0),
35 1.0), 30)
36 else:
37 skipdefault = 0
38 tdefault = min(max(len(gui.images) / 5.0, 1.0), 30)
39 if gui.config['movie_rate'] != 'auto':
40 tdefault = gui.config['movie_rate']
41 if gui.config['movie_skip'] != 'auto':
42 skipdefault = gui.config['movie_skip']
43 self.time = ui.SpinBox(tdefault, 1.0, 99, 0.1)
44 self.skip = ui.SpinBox(skipdefault, 0, 99, 1)
45 win.add([_(' Frame rate: '), self.time, _(' Skip frames: '),
46 self.skip])
48 self.gui = gui
49 self.direction = 1
50 self.timer = None
51 gui.obs.new_atoms.register(self.close)
53 def close(self):
54 self.stop()
55 self.win.close()
57 def click(self, step, firstlast=False):
58 if firstlast and step < 0:
59 i = 0
60 elif firstlast:
61 i = len(self.gui.images)
62 else:
63 i = max(0, min(len(self.gui.images) - 1, self.gui.frame + step))
65 self.frame_number.value = i
66 if firstlast:
67 self.direction = np.sign(-step)
68 else:
69 self.direction = np.sign(step)
71 def new_frame(self, value):
72 self.gui.set_frame(value)
74 def play(self):
75 self.stop()
76 t = 1 / self.time.value
77 self.timer = self.gui.window.after(t, self.step)
79 def stop(self):
80 if self.timer is not None:
81 self.timer.cancel()
83 def step(self):
84 i = self.gui.frame
85 nimages = len(self.gui.images)
86 delta = int(self.skip.value) + 1
88 if self.rock.value:
89 if i <= self.skip.value:
90 self.direction = 1
91 elif i >= nimages - delta:
92 self.direction = -1
93 i += self.direction * delta
94 else:
95 i = (i + self.direction * delta + nimages) % nimages
97 self.frame_number.value = i
98 self.play()