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
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-04 10:20 +0000
1# fmt: off
3from os import unlink
5import numpy as np
7import ase.gui.ui as ui
8from ase.gui.i18n import _
9from ase.io.pov import get_bondpairs, write_pov
11pack = error = Help = 42
14class Render:
15 texture_list = ['ase2', 'ase3', 'glass', 'simple', 'pale',
16 'intermediate', 'vmd', 'jmol']
17 cameras = ['orthographic', 'perspective', 'ultra_wide_angle']
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)))
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])
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'))])
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])
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()
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
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])
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')
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))
77 def get_guisize(self):
78 win = self.gui.window.win
79 return win.winfo_width(), win.winfo_height()
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
93 plotting_var_settings = {
94 'bbox': bbox,
95 'rotation': self.gui.axes,
96 'show_unit_cell': self.cell_widget.value
97 }
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 }
109 multiframe = bool(self.frames_widget.value)
110 if multiframe:
111 assert len(self.gui.images) > 1
113 if multiframe:
114 frames = range(len(self.gui.images))
115 else:
116 frames = [self.gui.frame]
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()
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)
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