Coverage for ase / gui / render.py: 86.79%

106 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-04 10:20 +0000

1# fmt: off 

2 

3from os import unlink 

4 

5import numpy as np 

6 

7import ase.gui.ui as ui 

8from ase.gui.i18n import _ 

9from ase.io.pov import get_bondpairs, write_pov 

10 

11pack = error = Help = 42 

12 

13 

14class Render: 

15 texture_list = ['ase2', 'ase3', 'glass', 'simple', 'pale', 

16 'intermediate', 'vmd', 'jmol'] 

17 cameras = ['orthographic', 'perspective', 'ultra_wide_angle'] 

18 

19 def __init__(self, gui): 

20 self.gui = gui 

21 self.win = win = ui.Window(_('Render current view in povray ... ')) 

22 win.add(ui.Label(_("Rendering %d atoms.") % len(self.gui.atoms))) 

23 

24 guiwidth, guiheight = self.get_guisize() 

25 self.width_widget = ui.SpinBox(guiwidth, start=1, end=9999, step=1) 

26 self.height_widget = ui.SpinBox(guiheight, start=1, end=9999, step=1) 

27 win.add([ui.Label(_('Size')), self.width_widget, 

28 ui.Label('⨯'), self.height_widget]) 

29 

30 self.linewidth_widget = ui.SpinBox(0.07, start=0.01, end=9.99, 

31 step=0.01) 

32 win.add([ui.Label(_('Line width')), self.linewidth_widget, 

33 ui.Label(_('Ångström'))]) 

34 

35 self.constraints_widget = ui.CheckButton(_("Render constraints")) 

36 self.cell_widget = ui.CheckButton(_("Render unit cell"), value=True) 

37 win.add([self.cell_widget, self.constraints_widget]) 

38 

39 formula = gui.atoms.get_chemical_formula(mode='hill') 

40 self.basename_widget = ui.Entry(width=30, value=formula, 

41 callback=self.update_outputname) 

42 win.add([ui.Label(_('Output basename: ')), self.basename_widget]) 

43 self.povray_executable = ui.Entry(width=30, value='povray') 

44 win.add([ui.Label(_('POVRAY executable')), self.povray_executable]) 

45 self.outputname_widget = ui.Label() 

46 win.add([ui.Label(_('Output filename: ')), self.outputname_widget]) 

47 self.update_outputname() 

48 

49 self.texture_widget = ui.ComboBox(labels=self.texture_list, 

50 values=self.texture_list) 

51 win.add([ui.Label(_('Atomic texture set:')), 

52 self.texture_widget]) 

53 # complicated texture stuff 

54 

55 self.camera_widget = ui.ComboBox(labels=self.cameras, 

56 values=self.cameras) 

57 self.camera_distance_widget = ui.SpinBox(50.0, -99.0, 99.0, 1.0) 

58 win.add([ui.Label(_('Camera type: ')), self.camera_widget]) 

59 win.add([ui.Label(_('Camera distance')), self.camera_distance_widget]) 

60 

61 # render current frame/all frames 

62 self.frames_widget = ui.RadioButtons([_('Render current frame'), 

63 _('Render all frames')]) 

64 win.add(self.frames_widget) 

65 if len(gui.images) == 1: 

66 self.frames_widget.buttons[1].widget.configure(state='disabled') 

67 

68 self.run_povray_widget = ui.CheckButton(_('Run povray'), True) 

69 self.keep_files_widget = ui.CheckButton(_('Keep povray files'), False) 

70 self.show_output_widget = ui.CheckButton(_('Show output window'), True) 

71 self.transparent = ui.CheckButton(_("Transparent background"), True) 

72 win.add(self.transparent) 

73 win.add([self.run_povray_widget, self.keep_files_widget, 

74 self.show_output_widget]) 

75 win.add(ui.Button(_('Render'), self.ok)) 

76 

77 def get_guisize(self): 

78 win = self.gui.window.win 

79 return win.winfo_width(), win.winfo_height() 

80 

81 def ok(self, *args): 

82 print("Rendering with povray:") 

83 _guiwidth, guiheight = self.get_guisize() 

84 width = self.width_widget.value 

85 height = self.height_widget.value 

86 # (Do width/height become inconsistent upon gui resize? Not critical) 

87 scale = self.gui.scale * height / guiheight 

88 bbox = np.empty(4) 

89 size = np.array([width, height]) / scale 

90 bbox[0:2] = np.dot(self.gui.center, self.gui.axes[:, :2]) - size / 2 

91 bbox[2:] = bbox[:2] + size 

92 

93 plotting_var_settings = { 

94 'bbox': bbox, 

95 'rotation': self.gui.axes, 

96 'show_unit_cell': self.cell_widget.value 

97 } 

98 

99 povray_settings = { 

100 'display': self.show_output_widget.value, 

101 'transparent': self.transparent.value, 

102 'camera_type': self.camera_widget.value, 

103 'camera_dist': self.camera_distance_widget.value, 

104 'canvas_width': width, 

105 'celllinewidth': self.linewidth_widget.value, 

106 'exportconstraints': self.constraints_widget.value, 

107 } 

108 

109 multiframe = bool(self.frames_widget.value) 

110 if multiframe: 

111 assert len(self.gui.images) > 1 

112 

113 if multiframe: 

114 frames = range(len(self.gui.images)) 

115 else: 

116 frames = [self.gui.frame] 

117 

118 initial_frame = self.gui.frame 

119 for frame in frames: 

120 self.gui.set_frame(frame) 

121 povray_settings['textures'] = self.get_textures() 

122 povray_settings['colors'] = self.gui.get_colors(rgb=True) 

123 atoms = self.gui.images.get_atoms(frame) 

124 radii_scale = 1 # atom size multiplier 

125 # self.gui.config['show_bonds'] is always False 

126 if self.gui.window['toggle-show-bonds']: 

127 print(" | Building bonds") 

128 povray_settings['bondatoms'] = get_bondpairs(atoms) 

129 radii_scale = 0.65 # value from draw method of View class 

130 filename = self.update_outputname() 

131 print(" | Writing files for image", filename, "...") 

132 plotting_var_settings['radii'] = radii_scale * \ 

133 self.gui.get_covalent_radii() 

134 renderer = write_pov( 

135 filename, atoms, 

136 povray_settings=povray_settings, 

137 **plotting_var_settings) 

138 if self.run_povray_widget.value: 

139 renderer.render( 

140 povray_executable=self.povray_executable.value, 

141 clean_up=False) 

142 if not self.keep_files_widget.value: 

143 print(" | Deleting temporary file ", filename) 

144 unlink(filename) 

145 filename = filename[:-4] + '.ini' 

146 print(" | Deleting temporary file ", filename) 

147 unlink(filename) 

148 self.gui.set_frame(initial_frame) 

149 self.update_outputname() 

150 

151 def update_outputname(self): 

152 tokens = [self.basename_widget.value] 

153 movielen = len(self.gui.images) 

154 if movielen > 1: 

155 ndigits = len(str(movielen)) 

156 token = ('{:0' + str(ndigits) + 'd}').format(self.gui.frame) 

157 tokens.append(token) 

158 tokens.append('pov') 

159 fname = '.'.join(tokens) 

160 self.outputname_widget.text = fname 

161 return fname 

162 # if self.movie.get_active(): 

163 # while len(movie_index) + len(str(self.iframe)) < len( 

164 # str(self.nimages)): 

165 # movie_index += '0' 

166 # movie_index = '.' + movie_index + str(self.iframe) 

167 # name = self.basename.get_text() + movie_index + '.pov' 

168 # self.outputname.set_text(name) 

169 

170 def get_textures(self): 

171 return [self.texture_widget.value] * len(self.gui.atoms) 

172 # natoms = len(self.gui.atoms) 

173 # textures = natoms * [ 

174 # self.texture_list[0] #self.default_texture.get_active()] 

175 # ] 

176 # for mat in self.materials: 

177 # sel = mat[1] 

178 # t = self.finish_list[mat[2].get_active()] 

179 # if mat[0]: 

180 # for n, val in enumerate(sel): 

181 # if val: 

182 # textures[n] = t 

183 # return textures