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

1# fmt: off 

2 

3import numpy as np 

4 

5import ase.gui.ui as ui 

6from ase.gui.i18n import _ 

7 

8 

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) 

17 

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)]) 

22 

23 play = ui.Button(_('Play'), self.play) 

24 stop = ui.Button(_('Stop'), self.stop) 

25 

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']) 

29 

30 win.add([play, stop, self.rock]) 

31 

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]) 

47 

48 self.gui = gui 

49 self.direction = 1 

50 self.timer = None 

51 gui.obs.new_atoms.register(self.close) 

52 

53 def close(self): 

54 self.stop() 

55 self.win.close() 

56 

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)) 

64 

65 self.frame_number.value = i 

66 if firstlast: 

67 self.direction = np.sign(-step) 

68 else: 

69 self.direction = np.sign(step) 

70 

71 def new_frame(self, value): 

72 self.gui.set_frame(value) 

73 

74 def play(self): 

75 self.stop() 

76 t = 1 / self.time.value 

77 self.timer = self.gui.window.after(t, self.step) 

78 

79 def stop(self): 

80 if self.timer is not None: 

81 self.timer.cancel() 

82 

83 def step(self): 

84 i = self.gui.frame 

85 nimages = len(self.gui.images) 

86 delta = int(self.skip.value) + 1 

87 

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 

96 

97 self.frame_number.value = i 

98 self.play()