Statistiques
| Révision :

root / ase / gui / view.py @ 13

Historique | Voir | Annoter | Télécharger (22,5 ko)

1
#!/usr/bin/env python
2

    
3
# Emacs: treat this as -*- python -*-
4

    
5
import os
6
import gtk
7
import tempfile
8
from math import cos, sin, sqrt
9

    
10
import numpy as np
11

    
12
from ase.data.colors import jmol_colors
13
from ase.gui.repeat import Repeat
14
from ase.gui.rotate import Rotate
15
from ase.utils import rotate
16

    
17

    
18
class View:
19
    def __init__(self, vbox, rotations):
20
        self.colors = [None] * (len(jmol_colors) + 1)
21
        self.nselected = 0
22
        self.light_green_markings = 0
23
        self.axes = rotate(rotations)
24
        # this is a hack, in order to be able to toggle menu actions off/on
25
        # without getting into an infinte loop
26
        self.menu_change = 0
27
    
28
        self.atoms_to_rotate = None
29
        
30
        self.drawing_area = gtk.DrawingArea()
31
        self.drawing_area.set_size_request(450, 450)
32
        self.drawing_area.connect('button_press_event', self.press)
33
        self.drawing_area.connect('button_release_event', self.release)
34
        self.drawing_area.connect('motion-notify-event', self.move)
35
        # Signals used to handle backing pixmap:
36
        self.drawing_area.connect('expose_event', self.expose_event)
37
        self.drawing_area.connect('configure_event', self.configure_event)
38
        self.drawing_area.set_events(gtk.gdk.BUTTON_PRESS_MASK |
39
                                     gtk.gdk.BUTTON_RELEASE_MASK |
40
                                     gtk.gdk.BUTTON_MOTION_MASK |
41
                                     gtk.gdk.POINTER_MOTION_HINT_MASK)
42
        vbox.pack_start(self.drawing_area)
43
        self.drawing_area.show()
44
        self.configured = False
45
        self.frame = None
46
        
47
    def set_coordinates(self, frame=None, focus=None):
48
        if frame is None:
49
            frame = self.frame
50
        self.make_box()
51
        self.bind(frame)
52
        n = self.images.natoms
53
        self.X = np.empty((n + len(self.B1) + len(self.bonds), 3))
54
        #self.X[n:] = np.dot(self.B1, self.images.A[frame])
55
        #self.B = np.dot(self.B2, self.images.A[frame])
56
        self.set_frame(frame, focus=focus, init=True)
57

    
58
    def set_frame(self, frame=None, focus=False, init=False):
59
        if frame is None:
60
            frame = self.frame
61

    
62
        n = self.images.natoms
63

    
64
        if init or frame != self.frame:
65
            A = self.images.A
66
            nc = len(self.B1)
67
            nb = len(self.bonds)
68
            
69
            if init or (A[frame] != A[self.frame]).any():
70
                self.X[n:n + nc] = np.dot(self.B1, A[frame])
71
                self.B = np.empty((nc + nb, 3))
72
                self.B[:nc] = np.dot(self.B2, A[frame])
73

    
74
            if nb > 0:
75
                P = self.images.P[frame]
76
                Af = self.images.repeat[:, np.newaxis] * A[frame]
77
                a = P[self.bonds[:, 0]]
78
                b = P[self.bonds[:, 1]] + np.dot(self.bonds[:, 2:], Af) - a
79
                d = (b**2).sum(1)**0.5
80
                r = 0.65 * self.images.r
81
                x0 = (r[self.bonds[:, 0]] / d).reshape((-1, 1))
82
                x1 = (r[self.bonds[:, 1]] / d).reshape((-1, 1))
83
                self.X[n + nc:] = a + b * x0
84
                b *= 1.0 - x0 - x1
85
                b[self.bonds[:, 2:].any(1)] *= 0.5
86
                self.B[nc:] = self.X[n + nc:] + b
87

    
88
            filenames = self.images.filenames
89
            filename = filenames[frame]
90
            if self.frame is None or filename != filenames[self.frame]:
91
                if filename is None:
92
                    filename = 'ase.gui'
93
                self.window.set_title(filename)
94
                
95
        self.frame = frame
96

    
97
        self.X[:n] = self.images.P[frame]
98
        self.R = self.X[:n]
99
        if focus:
100
            self.focus()
101
        else:
102
            self.draw()
103
        
104
    def set_colors(self):
105
        new = self.drawing_area.window.new_gc
106
        alloc = self.colormap.alloc_color
107
        for z in self.images.Z:
108
            if self.colors[z] is None:
109
                c, p, k = jmol_colors[z]
110
                self.colors[z] = new(alloc(int(65535 * c),
111
                                           int(65535 * p),
112
                                           int(65535 * k)))
113
            
114
    def plot_cell(self):
115
        V = self.images.A[0]
116
        R1 = []
117
        R2 = []
118
        for c in range(3):
119
            v = V[c]
120
            d = sqrt(np.dot(v, v))
121
            n = max(2, int(d / 0.3))
122
            h = v / (2 * n - 1)
123
            R = np.arange(n)[:, None] * (2 * h)
124
            for i, j in [(0, 0), (0, 1), (1, 0), (1, 1)]:
125
                R1.append(R + i * V[(c + 1) % 3] + j * V[(c + 2) % 3])
126
                R2.append(R1[-1] + h)
127
        return np.concatenate(R1), np.concatenate(R2)
128

    
129
    def make_box(self):
130
        if not self.ui.get_widget('/MenuBar/ViewMenu/ShowUnitCell'
131
                                  ).get_active():
132
            self.B1 = self.B2 = np.zeros((0, 3))
133
            return
134
        
135
        V = self.images.A[0]
136
        nn = []
137
        for c in range(3):
138
            v = V[c]
139
            d = sqrt(np.dot(v, v))
140
            n = max(2, int(d / 0.3))
141
            nn.append(n)
142
        self.B1 = np.zeros((2, 2, sum(nn), 3))
143
        self.B2 = np.zeros((2, 2, sum(nn), 3))
144
        n1 = 0
145
        for c, n in enumerate(nn):
146
            n2 = n1 + n
147
            h = 1.0 / (2 * n - 1)
148
            R = np.arange(n) * (2 * h)
149

    
150
            for i, j in [(0, 0), (0, 1), (1, 0), (1, 1)]:
151
                self.B1[i, j, n1:n2, c] = R
152
                self.B1[i, j, n1:n2, (c + 1) % 3] = i
153
                self.B1[i, j, n1:n2, (c + 2) % 3] = j
154
            self.B2[:, :, n1:n2] = self.B1[:, :, n1:n2]
155
            self.B2[:, :, n1:n2, c] += h
156
            n1 = n2
157
        self.B1.shape = (-1, 3)
158
        self.B2.shape = (-1, 3)
159

    
160
    def bind(self, frame):
161
        if not self.ui.get_widget('/MenuBar/ViewMenu/ShowBonds'
162
                                  ).get_active():
163
            self.bonds = np.empty((0, 5), int)
164
            return
165
        
166
        from ase.atoms import Atoms
167
        from ase.calculators.neighborlist import NeighborList
168
        nl = NeighborList(self.images.r * 1.5, skin=0, self_interaction=False)
169
        nl.update(Atoms(positions=self.images.P[frame],
170
                        cell=(self.images.repeat[:, np.newaxis] *
171
                              self.images.A[frame]),
172
                        pbc=self.images.pbc))
173
        nb = nl.nneighbors + nl.npbcneighbors
174
        self.bonds = np.empty((nb, 5), int)
175
        if nb == 0:
176
            return
177
        
178
        n1 = 0
179
        for a in range(self.images.natoms):
180
            indices, offsets = nl.get_neighbors(a)
181
            n2 = n1 + len(indices)
182
            self.bonds[n1:n2, 0] = a
183
            self.bonds[n1:n2, 1] = indices
184
            self.bonds[n1:n2, 2:] = offsets
185
            n1 = n2
186

    
187
        i = self.bonds[:n2, 2:].any(1)
188
        self.bonds[n2:, 0] = self.bonds[i, 1]
189
        self.bonds[n2:, 1] = self.bonds[i, 0]
190
        self.bonds[n2:, 2:] = -self.bonds[i, 2:]
191

    
192
    def toggle_show_unit_cell(self, action):
193
        self.set_coordinates()
194
        
195
    def reset_tools_modes(self):
196
        dummy = self.menu_change
197
        self.menu_change = 1
198
        self.atoms_to_rotate = None
199
        for c_mode in ['Rotate', 'Orient', 'Move']:
200
              self.ui.get_widget('/MenuBar/ToolsMenu/%sAtoms' % c_mode).set_active(False)
201
        self.light_green_markings = 0
202
        self.menu_change = 0        
203
        self.draw()
204
        
205
                      
206
    def toggle_mode(self, mode):
207
        self.menu_change = 1
208
        i_sum = 0
209
        for c_mode in ['Rotate', 'Orient', 'Move']:
210
            i_sum += self.ui.get_widget('/MenuBar/ToolsMenu/%sAtoms' % c_mode).get_active() 
211
        if i_sum == 0 or (i_sum == 1 and sum(self.images.selected) == 0):
212
            self.reset_tools_modes()
213
            return()
214
            
215
        if i_sum == 2:
216
            try:
217
                self.images.selected = self.atoms_to_rotate_0.copy()
218
            except:
219
                self.atoms_to_rotate_0 = self.images.selected.copy()
220
        if i_sum == 1:
221
            self.atoms_to_rotate_0 = self.images.selected.copy()
222

    
223
        for c_mode in ['Rotate', 'Orient', 'Move']:
224
            if c_mode != mode:
225
                  self.ui.get_widget('/MenuBar/ToolsMenu/%sAtoms' % c_mode).set_active(False) 
226
        
227
        if self.ui.get_widget('/MenuBar/ToolsMenu/%sAtoms' % mode).get_active():
228
            self.atoms_to_rotate_0 = self.images.selected.copy()
229
            for i in range(len(self.images.selected)):
230
               self.images.selected[i] = False
231
            self.light_green_markings = 1
232
        else:
233
            try: 
234
                atr = self.atoms_to_rotate_0
235
                for i in range(len(self.images.selected)):
236
                    self.images.selected[i] = atr[i]
237
            except:
238
                pass                
239
                
240
        self.menu_change = 0
241
        self.draw()
242
                      
243
    def toggle_move_mode(self, action):
244
        """
245
        Toggles the move mode, where the selected atoms can be moved with the arrow
246
        keys and pg up/dn. If the shift key is pressed, the movement will be reduced.
247
        
248
        The movement will be relative to the current rotation of the coordinate system.
249
        
250
        The implementation of the move mode is found in the gui.scroll
251
        """
252
        if not (self.menu_change):
253
            self.toggle_mode('Move')
254

    
255
    def toggle_rotate_mode(self, action):
256
        """
257
        Toggles the rotate mode, where the selected atoms can be rotated with the arrow keys
258
        and pg up/dn. If the shift key is pressed, the rotation angle will be reduced.
259
        
260
        The atoms to be rotated will be marked with light green - and the COM of the selected
261
        atoms will be used as the COM of the rotation. This can be changed while rotating the
262
        selected atoms.
263
        
264
        If only two atoms are seleceted, and the number of atoms to be rotated is different from
265
        two, the selected atoms will define the axis of rotation.
266
        
267
        The implementation of the rotate mode is found in the gui.scroll
268
        """
269
        if not (self.menu_change):
270
            self.toggle_mode('Rotate')
271
                
272
    def toggle_orient_mode(self, action):
273
        """
274
        Toggle the orientation mode - the orientation of the atoms will be changed
275
        according to the arrow keys selected.
276
        
277
        If nothing is selected, standard directions are x, y and z
278
        if two atoms are selected, the standard directions are along their displacement vector
279
        if three atoms are selected, the orientation is changed according to the normal of these
280
        three vectors.
281
        """
282
        if not (self.menu_change):
283
            self.toggle_mode('Orient')
284
        self.orient_normal = np.array([1.0, 0.0, 0.0])
285
        sel_pos = []
286
        for i, j in enumerate(self.atoms_to_rotate_0):
287
            if j: 
288
                sel_pos.append(self.R[i])
289
        if len(sel_pos) == 2:
290
            self.orient_normal = sel_pos[0] - sel_pos[1]
291
        if len(sel_pos) == 3:
292
            v1 = sel_pos[1] - sel_pos[0]
293
            v2 = sel_pos[1] - sel_pos[2]
294
            self.orient_normal = np.cross(v1, v2)
295
        self.orient_normal /= sum(self.orient_normal ** 2) ** 0.5
296
            
297
    def toggle_show_axes(self, action):
298
        self.draw()
299

    
300
    def toggle_show_bonds(self, action):
301
        self.set_coordinates()
302

    
303
    def repeat_window(self, menuitem):
304
        self.reset_tools_modes()
305
        Repeat(self)
306

    
307
    def rotate_window(self, menuitem):
308
        Rotate(self)
309
        
310
    def focus(self, x=None):
311
        if (self.images.natoms == 0 and not
312
            self.ui.get_widget('/MenuBar/ViewMenu/ShowUnitCell').get_active()):
313
            self.scale = 1.0
314
            self.center = np.zeros(3)
315
            self.draw()
316
            return
317
        
318
        P = np.dot(self.X, self.axes)
319
        n = self.images.natoms
320
        P[:n] -= self.images.r[:, None]
321
        P1 = P.min(0) 
322
        P[:n] += 2 * self.images.r[:, None]
323
        P2 = P.max(0)
324
        self.center = np.dot(self.axes, (P1 + P2) / 2)
325
        S = 1.3 * (P2 - P1)
326
        if S[0] * self.height < S[1] * self.width:
327
            self.scale = self.height / S[1]
328
        else:
329
            self.scale = self.width / S[0]
330
        self.draw()
331

    
332
    def draw(self, status=True):
333
        self.pixmap.draw_rectangle(self.white_gc, True, 0, 0,
334
                                   self.width, self.height)
335
        axes = self.scale * self.axes * (1, -1, 1)
336
        offset = (np.dot(self.center, axes) -
337
                  (0.5 * self.width, 0.5 * self.height, 0))
338
        X = np.dot(self.X, axes) - offset
339
        n = self.images.natoms
340
        self.indices = X[:, 2].argsort()
341
        P = self.P = X[:n, :2]
342
        X1 = X[n:, :2].round().astype(int)
343
        X2 = (np.dot(self.B, axes) - offset).round().astype(int)
344

    
345
        if self.ui.get_widget('/MenuBar/ViewMenu/ShowBonds').get_active():
346
            r = self.images.r * (0.65 * self.scale)
347
        else:
348
            r = self.images.r * self.scale
349
        A = (P - r[:, None]).round().astype(int)
350
        d = (2 * r).round().astype(int)
351
        selected_gc = self.selected_gc
352

    
353
        colors = self.colors
354
        Z = self.images.Z
355
        arc = self.pixmap.draw_arc
356
        line = self.pixmap.draw_line
357
        black_gc = self.black_gc
358
        dynamic = self.images.dynamic
359
        selected = self.images.selected
360
        visible = self.images.visible
361
        for a in self.indices:
362
            if a < n:
363
                ra = d[a]
364
                if visible[a]:
365
                    arc(colors[Z[a]], True, A[a, 0], A[a, 1], ra, ra, 0, 23040)
366
                if  self.light_green_markings and self.atoms_to_rotate_0[a]:
367
                    arc(self.green, False, A[a, 0] + 2, A[a, 1] + 2,
368
                        ra - 4, ra - 4, 0, 23040)
369

    
370
                if not dynamic[a]:
371
                    R1 = int(0.14644 * ra)
372
                    R2 = int(0.85355 * ra)
373
                    line(black_gc,
374
                         A[a, 0] + R1, A[a, 1] + R1,
375
                         A[a, 0] + R2, A[a, 1] + R2)
376
                    line(black_gc,
377
                         A[a, 0] + R2, A[a, 1] + R1,
378
                         A[a, 0] + R1, A[a, 1] + R2)
379
                if selected[a]:
380
                    arc(selected_gc, False, A[a, 0], A[a, 1], ra, ra, 0, 23040)
381
                elif visible[a]:
382
                    arc(black_gc, False, A[a, 0], A[a, 1], ra, ra, 0, 23040)
383
            else:
384
                a -= n
385
                line(black_gc, X1[a, 0], X1[a, 1], X2[a, 0], X2[a, 1])
386

    
387
        if self.ui.get_widget('/MenuBar/ViewMenu/ShowAxes').get_active():
388
            self.draw_axes()
389

    
390
        if self.images.nimages > 1:
391
            self.draw_frame_number()
392
            
393
        self.drawing_area.window.draw_drawable(self.white_gc, self.pixmap,
394
                                               0, 0, 0, 0,
395
                                               self.width, self.height)
396

    
397
        if status:
398
            self.status()
399

    
400
    def draw_axes(self):
401
        L = np.zeros((10, 2, 3))
402
        L[:3, 1] = self.axes * 15
403
        L[3:5] = self.axes[0] * 20
404
        L[5:7] = self.axes[1] * 20
405
        L[7:] = self.axes[2] * 20
406
        L[3:, :, :2] += (((-4, -5), (4,  5)), ((-4,  5), ( 4, -5)), 
407
                         ((-4,  5), (0,  0)), ((-4, -5), ( 4,  5)), 
408
                         ((-4,  5), (4,  5)), (( 4,  5), (-4, -5)), 
409
                         ((-4, -5), (4, -5)))
410
        L = L.round().astype(int)
411
        L[:, :, 0] += 20
412
        L[:, :, 1] = self.height - 20 - L[:, :, 1]
413
        line = self.pixmap.draw_line
414
        colors = ([self.black_gc] * 3 +
415
                  [self.red] * 2 + [self.green] * 2 + [self.blue] * 3)
416
        for i in L[:, 1, 2].argsort():
417
            (a, b), (c, d) = L[i, :, :2]
418
            line(colors[i], a, b, c, d)
419

    
420
    digits = np.array(((1, 1, 1, 1, 1, 1, 0),
421
                       (0, 1, 1, 0, 0, 0, 0),
422
                       (1, 0, 1, 1, 0, 1, 1),
423
                       (1, 1, 1, 1, 0, 0, 1),
424
                       (0, 1, 1, 0, 1, 0, 1),
425
                       (1, 1, 0, 1, 1, 0, 1),
426
                       (1, 1, 0, 1, 1, 1, 1),
427
                       (0, 1, 1, 1, 0, 0, 0),
428
                       (1, 1, 1, 1, 1, 1, 1),
429
                       (0, 1, 1, 1, 1, 0, 1)), bool)
430

    
431
    bars = np.array(((0, 2, 1, 2),
432
                     (1, 2, 1, 1),
433
                     (1, 1, 1, 0),
434
                     (1, 0, 0, 0),
435
                     (0, 0, 0, 1),
436
                     (0, 1, 0, 2),
437
                     (0, 1, 1, 1))) * 5
438
    
439
    def draw_frame_number(self):
440
        n = str(self.frame)
441
        x = self.width - 3 - 8 * len(n)
442
        y = self.height - 27
443
        color = self.black_gc
444
        line = self.pixmap.draw_line
445
        for c in n:
446
            bars = View.bars[View.digits[int(c)]]
447
            for a, b, c, d in bars:
448
                line(color, a + x, b + y, c + x, d + y)
449
            x += 8
450
        
451
    def release(self, drawing_area, event):
452
        if event.button != 1:
453
            return
454

    
455
        selected = self.images.selected
456
        selected_ordered = self.images.selected_ordered
457

    
458
        if event.time < self.t0 + 200:  # 200 ms
459
            d = self.P - self.xy
460
            hit = np.less((d**2).sum(1), (self.scale * self.images.r)**2)
461
            for a in self.indices[::-1]:
462
                if a < self.images.natoms and hit[a]:
463
                    if event.state & gtk.gdk.CONTROL_MASK:
464
                        selected[a] = not selected[a]
465
                        if selected[a]: 
466
                            selected_ordered += [a]
467
                        elif len(selected_ordered) > 0:
468
                            if selected_ordered[-1] == a:
469
                                selected_ordered = selected_ordered[:-1]
470
                            else:
471
                                selected_ordered = []
472
                    else:
473
                        selected[:] = False
474
                        selected[a] = True
475
                        selected_ordered = [a]
476
                    break
477
            else:
478
                selected[:] = False
479
                selected_ordered = []
480
            self.draw()
481
        else:
482
            A = (event.x, event.y)
483
            C1 = np.minimum(A, self.xy)
484
            C2 = np.maximum(A, self.xy)
485
            hit = np.logical_and(self.P > C1, self.P < C2)
486
            indices = np.compress(hit.prod(1), np.arange(len(hit)))
487
            if not (event.state & gtk.gdk.CONTROL_MASK):
488
                selected[:] = False
489
            selected[indices] = True
490
            if len(indices) == 1 and indices[0] not in self.images.selected_ordered: 
491
                selected_ordered += [indices[0]]
492
            elif len(indices) > 1:
493
                selected_ordered = []
494
            self.draw()
495

    
496
        indices = np.arange(self.images.natoms)[self.images.selected]
497
        if len(indices) != len(selected_ordered):
498
            selected_ordered = []
499
        self.images.selected_ordered = selected_ordered
500

    
501
    def press(self, drawing_area, event):
502
        self.button = event.button
503
        self.xy = (event.x, event.y)
504
        self.t0 = event.time
505
        self.axes0 = self.axes
506
        self.center0 = self.center
507
        
508
    def move(self, drawing_area, event):
509
             
510
        x, y, state = event.window.get_pointer()
511
        x0, y0 = self.xy
512
        if self.button == 1:
513
            window = self.drawing_area.window
514
            window.draw_drawable(self.white_gc, self.pixmap,
515
                                 0, 0, 0, 0,
516
                                 self.width, self.height)
517
            x0 = int(round(x0))
518
            y0 = int(round(y0))
519
            window.draw_rectangle(self.selected_gc, False,
520
                                  min(x, x0), min(y, y0),
521
                                  abs(x - x0), abs(y - y0))
522
            return
523
        if self.button == 2:
524
            return
525
        if state & gtk.gdk.SHIFT_MASK:
526
            self.center = (self.center0 -
527
                           np.dot(self.axes, (x - x0, y0 - y, 0)) / self.scale)
528
        else:
529
            # Snap mode: the a-b angle and t should multipla of 15 degrees ???
530
            a = x - x0
531
            b = y0 - y
532
            t = sqrt(a * a + b * b)
533
            if t > 0:
534
                a /= t
535
                b /= t
536
            else:
537
                a = 1.0
538
                b = 0.0
539
            c = cos(0.01 * t)
540
            s = -sin(0.01 * t)
541
            rotation = np.array([(c * a * a + b * b, (c - 1) * b * a, s * a),
542
                                 ((c - 1) * a * b, c * b * b + a * a, s * b),
543
                                 (-s * a, -s * b, c)])
544
            self.axes = np.dot(self.axes0, rotation)
545
            if self.images.natoms > 0:
546
                com = self.X[:self.images.natoms].mean(0) 
547
            else:
548
                com = self.images.A[self.frame].mean(0)
549
            self.center = com - np.dot(com - self.center0,
550
                                       np.dot(self.axes0, self.axes.T))
551
        self.draw(status=False)
552
        
553
    # Create a new backing pixmap of the appropriate size
554
    def configure_event(self, drawing_area, event):
555
        if self.configured:
556
            w = self.width
557
            h = self.height
558
        else:
559
            self.colormap = self.drawing_area.get_colormap()
560
            self.black_gc = self.drawing_area.get_style().black_gc
561
            self.white_gc = self.drawing_area.get_style().white_gc
562
            self.red = self.drawing_area.window.new_gc(
563
                self.colormap.alloc_color(62345, 0, 0), line_width=2)
564
            self.green = self.drawing_area.window.new_gc(
565
                self.colormap.alloc_color(0, 54456, 0), line_width=2)
566
            self.blue = self.drawing_area.window.new_gc(
567
                self.colormap.alloc_color(0, 0, 54456), line_width=2)
568
            self.selected_gc = self.drawing_area.window.new_gc(
569
                self.colormap.alloc_color(0, 16456, 0),
570
                line_width=3)
571
            
572
        x, y, self.width, self.height = drawing_area.get_allocation()
573
        self.pixmap = gtk.gdk.Pixmap(drawing_area.window,
574
                                     self.width, self.height)
575
        if self.configured:
576
            self.scale *= sqrt(1.0 * self.width * self.height / (w * h))
577
            self.draw()
578
        self.configured = True
579
        
580
    # Redraw the screen from the backing pixmap
581
    def expose_event(self, drawing_area, event):
582
        x , y, width, height = event.area
583
        gc = self.white_gc
584
        drawing_area.window.draw_drawable(gc, self.pixmap,
585
                                          x, y, x, y, width, height)
586

    
587
    def external_viewer(self, action):
588
        name = action.get_name()
589
        command = {'Avogadro' : 'avogadro',
590
                   'XMakeMol': 'xmakemol -f',
591
                   'RasMol':'rasmol -xyz',
592
                   'VMD': 'vmd'}[name]
593
        fd, filename = tempfile.mkstemp('.xyz', 'ase.gui-')
594
        os.close(fd)
595
        self.images.write(filename)
596
        os.system('(%s %s &); (sleep 60; rm %s) &' %
597
                  (command, filename, filename))