Coverage for /builds/ase/ase/ase/gui/movie.py: 62.30%

61 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-08-02 00:12 +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( 

12 _('Movie'), close=self.close, wmtype='utility') 

13 win.add(_('Image number:')) 

14 self.frame_number = ui.Scale(gui.frame, 0, 

15 len(gui.images) - 1, 

16 callback=self.new_frame) 

17 win.add(self.frame_number) 

18 

19 win.add([ui.Button(_('First'), self.click, -1, True), 

20 ui.Button(_('Back'), self.click, -1), 

21 ui.Button(_('Forward'), self.click, 1), 

22 ui.Button(_('Last'), self.click, 1, True)]) 

23 

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

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

26 

27 # TRANSLATORS: This function plays an animation forwards and backwards 

28 # alternatingly, e.g. for displaying vibrational movement 

29 self.rock = ui.CheckButton(_('Rock')) 

30 

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

32 

33 if len(gui.images) > 150: 

34 skipdefault = len(gui.images) // 150 

35 tdefault = min(max(len(gui.images) / (skipdefault * 5.0), 

36 1.0), 30) 

37 else: 

38 skipdefault = 0 

39 tdefault = min(max(len(gui.images) / 5.0, 1.0), 30) 

40 self.time = ui.SpinBox(tdefault, 1.0, 99, 0.1) 

41 self.skip = ui.SpinBox(skipdefault, 0, 99, 1) 

42 win.add([_(' Frame rate: '), self.time, _(' Skip frames: '), 

43 self.skip]) 

44 

45 self.gui = gui 

46 self.direction = 1 

47 self.timer = None 

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

49 

50 def close(self): 

51 self.stop() 

52 self.win.close() 

53 

54 def click(self, step, firstlast=False): 

55 if firstlast and step < 0: 

56 i = 0 

57 elif firstlast: 

58 i = len(self.gui.images) 

59 else: 

60 i = max(0, min(len(self.gui.images) - 1, self.gui.frame + step)) 

61 

62 self.frame_number.value = i 

63 if firstlast: 

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

65 else: 

66 self.direction = np.sign(step) 

67 

68 def new_frame(self, value): 

69 self.gui.set_frame(value) 

70 

71 def play(self): 

72 self.stop() 

73 t = 1 / self.time.value 

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

75 

76 def stop(self): 

77 if self.timer is not None: 

78 self.timer.cancel() 

79 

80 def step(self): 

81 i = self.gui.frame 

82 nimages = len(self.gui.images) 

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

84 

85 if self.rock.value: 

86 if i <= self.skip.value: 

87 self.direction = 1 

88 elif i >= nimages - delta: 

89 self.direction = -1 

90 i += self.direction * delta 

91 else: 

92 i = (i + self.direction * delta + nimages) % nimages 

93 

94 self.frame_number.value = i 

95 self.play()