def fourier_transform(v = None, step = None, subregion = None, model_id = None): if v is None: from VolumeViewer import active_volume v = active_volume() if v is None: return m = v.matrix(step = step, subregion = subregion) from numpy.fft import fftn cftm = fftn(m) # Complex128 result, same array size as input from numpy import absolute, float32 aftm = absolute(cftm).astype(float32) cftm = None # Release memory aftm *= 1.0/aftm.size aftm[0,0,0] = 0 # Constant term often huge making histogram hard to use ftm = fftshift(aftm) aftm = None # Release memory # Place FT centered on data, scaled to keep same volume xyz_min, xyz_max = v.xyz_bounds() xyz_center = map(lambda a,b: 0.5*(a+b), xyz_min, xyz_max) ijk_size = list(ftm.shape) ijk_size.reverse() if step is None: ijk_step = v.region[2] elif isinstance(step, int): ijk_step = (step,step,step) else: ijk_step = step xyz_size = map(lambda a,b: a-b, xyz_max, xyz_min) vol = xyz_size[0]*xyz_size[1]*xyz_size[2] cell_size = map(lambda a,b: a*b, v.data.step, ijk_step) cell_vol = cell_size[0]*cell_size[1]*cell_size[2] scale = pow(vol*cell_vol, 1.0/3) step = map(lambda a: scale/a, xyz_size) origin = map(lambda c,s,z: c-0.5*s*z, xyz_center, step, ijk_size) from VolumeData import Array_Grid_Data ftd = Array_Grid_Data(ftm, origin, step) ftd.name = v.name + ' FT' from VolumeViewer import volume_from_grid_data ftr = volume_from_grid_data(ftd, show_data = False, model_id = model_id) ftr.copy_settings_from(v, copy_thresholds = False, copy_colors = False, copy_region = False) ftr.initialize_thresholds() ftr.set_parameters(show_outline_box = True) ftr.show() v.unshow() # Hide original map return ftr
def export_mask(segmentation, savePath=None, format='mrc', binSize=(1, 1, 1), sequentialIds=True): m = segmentation.mask origin = segmentation.grid_origin() step = segmentation.grid_step() # Include only id numbers of top-level region groups. from numpy import zeros, empty parent = zeros((segmentation.max_region_id + 1, ), dtype=m.dtype) for r in segmentation.all_regions(): parent[r.rid] = r.top_parent().rid if sequentialIds: used_ids = list(set(parent)) used_ids.sort() seq_id = zeros((used_ids[-1] + 1, ), dtype=m.dtype) for i, id in enumerate(used_ids): seq_id[id] = i parent = seq_id[parent] array = parent[m] # Expand array to match unbinned density map size. if tuple(binSize) != (1, 1, 1): step = [float(s) / b for s, b in zip(step, binSize)] shape = [a * b for a, b in zip(array.shape, binSize[::-1])] aub = empty(shape, array.dtype) b2, b1, b0 = binSize for o0 in range(b0): for o1 in range(b1): for o2 in range(b2): aub[o0::b0, o1::b1, o2::b2] = array array = aub from VolumeData import Array_Grid_Data g = Array_Grid_Data(array, origin, step) g.name = segmentation.name + ' region ids' if savePath is None: # Open mask map as a volume. import VolumeViewer v = VolumeViewer.volume_from_grid_data(g) v.openState.xform = segmentation.openState.xform else: # Write map file. from VolumeData import save_grid_data save_grid_data(g, savePath, format) return g
def openVolume(self): try: while True: if self.vol_conn.poll(): msg = self.vol_conn.recv() if msg == 'open_volume': data = self.vol_conn.recv()#objects are serialized by default grid = Array_Grid_Data(data) self.volume = volume_from_grid_data(grid) elif msg == 'voxel_size': self.voxelSize = self.vol_conn.recv() cmd = "volume #0 voxelSize %s"%self.voxelSize runCommand(cmd) elif msg == 'command_list': commandList = self.vol_conn.recv() for command in commandList: runCommand(command) elif msg == 'end':#if you don't break cicle volume is never shown break else: sleep(0.01) except EOFError: print ('Lost connection to client')
def openVolume(self): try: while True: if self.remote_conn.poll(): msg = self.remote_conn.recv() #print msg if msg == 'open_volume': data = self.remote_conn.recv() print data grid = Array_Grid_Data(data) self.volume = volume_from_grid_data(grid) #runCommand("volume #0 step 1") elif msg == 'voxelSize': voxelSize = self.remote_conn.recv() cmd = "volume #0 voxelSize %s" % voxelSize runCommand(cmd) runCommand("focus") elif msg == 'draw_angular_distribution': angulardist = self.remote_conn.recv() for command in angulardist: runCommand(command) elif msg == 'end': break else: sleep(0.01) except EOFError: print 'Lost connection to client'
def create_geometric_model(shape_name, param_list): """ Creates chimera model object from a geometric shape Centers model at center of frame (for future rotations) Model object is numbered #1 :param shpae_name: one of known shapes - cube, sphere :param param_list: list of geomteric shape paramters (cube side, sphere radius..) """ if shape_name == 'cube': matrix = create_cube_matrix(param_list) elif shape_name == 'sphere': matrix = create_sphere_matrix(param_list) elif shape_name == 'L': matrix = create_L_matrix(param_list) else: raise ('unkown shape!') # create model v = volume_from_grid_data(Array_Grid_Data(matrix)) tmp_model = chimera.specifier.evalSpec('#0').models()[0] trans = tuple(np.array(tmp_model.openState.cofr.data()) * -1) tmp_model.openState.globalXform(euler_xform([0, 0, 0], trans)) # change model number to #1 for consitency model = tmp_model.copy() tmp_model.close() return model
def laplacian(v = None, step = None, subregion = None, model_id = None): if v is None: from VolumeViewer import active_volume v = active_volume() if v is None: return None m = v.matrix(step = step, subregion = subregion) from numpy import float32, multiply, add lm = m.astype(float32) # Copy array multiply(lm, -6.0, lm) add(lm[:-1,:,:], m[1:,:,:], lm[:-1,:,:]) add(lm[1:,:,:], m[:-1,:,:], lm[1:,:,:]) add(lm[:,:-1,:], m[:,1:,:], lm[:,:-1,:]) add(lm[:,1:,:], m[:,:-1,:], lm[:,1:,:]) add(lm[:,:,:-1], m[:,:,1:], lm[:,:,:-1]) add(lm[:,:,1:], m[:,:,:-1], lm[:,:,1:]) lm[0,:,:] = 0 lm[-1,:,:] = 0 lm[:,0,:] = 0 lm[:,-1,:] = 0 lm[:,:,0] = 0 lm[:,:,-1] = 0 origin, step = v.data_origin_and_step(subregion = subregion, step = step) d = v.data from VolumeData import Array_Grid_Data ld = Array_Grid_Data(lm, origin, step, d.cell_angles, d.rotation, name = v.name + ' Laplacian') ld.polar_values = True from VolumeViewer import volume_from_grid_data lv = volume_from_grid_data(ld, show_data = False, model_id = model_id) lv.copy_settings_from(v, copy_thresholds = False, copy_colors = False) lv.set_parameters(cap_faces = False) lv.initialize_thresholds() lv.show() v.unshow() # Hide original map return lv
def subregion_grid(self, voxel_size, xform, name): bm = self.box_model if bm is None or bm.box is None or bm.model() is None: return None # Deterime array size. Use half voxel padding on all sides. elength = bm.edge_lengths() from math import ceil size = [ max(1, int(ceil((elength[a] - voxel_size[a]) / voxel_size[a]))) for a in (0, 1, 2) ] # Allocate array. from VolumeData import allocate_array array = allocate_array(size, zero_fill=True) # Determine origin, rotation, and cell angles. b2vxf = bm.xform() b2vxf.premultiply(xform.inverse()) from Matrix import xform_matrix, apply_matrix, apply_matrix_without_translation, cell_angles_and_rotation b2v = xform_matrix(b2vxf) origin = apply_matrix(b2v, bm.origin()) vaxes = [apply_matrix_without_translation(b2v, v) for v in bm.axes()] cell_angles, rotation = cell_angles_and_rotation(vaxes) # Create grid. from VolumeData import Array_Grid_Data g = Array_Grid_Data(array, origin, voxel_size, cell_angles, rotation, name=name) # Add half voxel padding. g.set_origin(g.ijk_to_xyz((0.5, 0.5, 0.5))) return g
def masked_grid_data(grid_data, mask, mask_value = None): d = grid_data matrix = d.full_matrix() from numpy import zeros, putmask masked = zeros(matrix.shape, matrix.dtype) if mask_value is None: putmask(masked, mask, matrix) else: putmask(masked, mask == mask_value, matrix) from VolumeData import Array_Grid_Data masked_grid_data = Array_Grid_Data(masked, d.origin, d.step, d.cell_angles, d.rotation) return masked_grid_data
def make_normalized_map(data): from numpy import mean, std m = data.full_matrix() mn = (m - m.mean()) / m.std() from VolumeData import Array_Grid_Data dn = Array_Grid_Data(mn, data.data.origin, data.data.step, data.data.cell_angles, data.data.rotation, name = data.data.name + ' normalized') from VolumeViewer import volume_from_grid_data vn = volume_from_grid_data(dn, show_data = False) vn.initialize_thresholds() vn.copy_settings_from(data) vn.data.symmetries = data.data.symmetries return vn
def molecule_grid_data(atoms, resolution, step, pad, cutoff_range, sigma_factor): from _multiscale import get_atom_coordinates, bounding_box xyz = get_atom_coordinates(atoms, transformed=True) # Transform coordinates to local coordinates of the molecule containing # the first atom. This handles multiple unaligned molecules. from Matrix import xform_matrix m0 = atoms[0].molecule tf = xform_matrix(m0.openState.xform.inverse()) from _contour import affine_transform_vertices affine_transform_vertices(xyz, tf) xyz_min, xyz_max = bounding_box(xyz) origin = [x - pad for x in xyz_min] ijk = (xyz - origin) / step anum = [a.element.number for a in atoms] sdev = sigma_factor * resolution / step from numpy import zeros, float32 sdevs = zeros((len(atoms), 3), float32) sdevs[:] = sdev from math import pow, pi, ceil normalization = pow(2 * pi, -1.5) * pow(sdev * step, -3) shape = [ int(ceil((xyz_max[a] - xyz_min[a] + 2 * pad) / step)) for a in (2, 1, 0) ] matrix = zeros(shape, float32) from _gaussian import sum_of_gaussians sum_of_gaussians(ijk, anum, sdevs, cutoff_range, matrix) matrix *= normalization molecules = set([a.molecule for a in atoms]) if len(molecules) > 1: name = 'molmap res %.3g' % (resolution, ) else: name = 'molmap %s res %.3g' % (m0.name, resolution) from VolumeData import Array_Grid_Data grid = Array_Grid_Data(matrix, origin, (step, step, step), name=name) return grid, molecules
def gaussian_grid(volume, sdev, step = 1, subregion = None, region = None, task = None): v = volume if region is None: region = v.subregion(step, subregion) origin, step = v.region_origin_and_step(region) ijk_sdev = [sdev / s for s in step] m = v.region_matrix(region) gm = gaussian_convolution(m, ijk_sdev, task = task) from VolumeData import Array_Grid_Data d = v.data if v.name.endswith('gaussian'): name = v.name else: name = '%s gaussian' % v.name gg = Array_Grid_Data(gm, origin, step, d.cell_angles, d.rotation, name = name) return gg
def median_grid(volume, bin_size = 3, iterations = 1, step = 1, subregion = None, region = None): v = volume if region is None: region = v.subregion(step, subregion) origin, step = v.region_origin_and_step(region) vm = v.region_matrix(region) m = vm for i in range(iterations): m = median_array(m, bin_size) from VolumeData import Array_Grid_Data d = v.data if v.name.endswith('median'): name = v.name else: name = '%s median' % v.name mg = Array_Grid_Data(m, origin, step, d.cell_angles, d.rotation, name = name) return mg
def openVolume(self): '''Wait for volume data and open in volume viewer''' try: while True: if self.remote_conn.poll(): msg = self.remote_conn.recv() if type(msg) is numpy.ndarray: from VolumeData import Array_Grid_Data grid = Array_Grid_Data(msg) from VolumeViewer import volume_from_grid_data self.v = volume_from_grid_data(grid) break #else: #print 'msg: ' + msg else: time.sleep(0.01) except EOFError: print 'Lost connection to client' self.listener.close()
def answer(self, msg): #print msg if msg == 'open_volume': data = self.vol_conn.recv()#objects are serialized by default #print data grid = Array_Grid_Data(data) self.volume = volume_from_grid_data(grid) self.centerVolume() elif msg == 'voxel_size': self.voxelSize = self.vol_conn.recv() cmd = "volume #0 voxelSize %s"%self.voxelSize #print cmd runCommand(cmd) runCommand("focus") #end debug elif msg == 'command_list': commandList = self.vol_conn.recv() for command in commandList: runCommand(command)
def computeVolume(self, atoms, startFrame=None, endFrame=None, bound=None, volumeName=None, step=1, spacing=0.5): # load and process frames if startFrame is None: startFrame = self.startFrame if endFrame is None: endFrame = self.endFrame if bound is not None: from _closepoints import find_close_points, BOXES_METHOD from Matrix import xform_matrix if self.holdingSteady: steadyAtoms, steadySel, steadyCS, inverse = \ self.holdingSteady # all the above used later... inverse = xform_matrix(inverse) gridData = {} from math import floor from numpy import array, float32 from _contour import affine_transform_vertices for fn in range(startFrame, endFrame + 1, step): cs = self.findCoordSet(fn) if not cs: self.status("Loading frame %d" % fn) self._LoadFrame(fn, makeCurrent=False) cs = self.findCoordSet(fn) self.status("Processing frame %d" % fn) pts = array([a.coord(cs) for a in atoms], float32) if self.holdingSteady: if bound is not None: steadyPoints = array([a.coord(cs) for a in steadyAtoms], float32) closeIndices = find_close_points( BOXES_METHOD, steadyPoints, #otherPoints, bound)[1] pts, bound)[1] pts = pts[closeIndices] try: xf, inv = self.transforms[fn] except KeyError: xf, inv = self.steadyXform(cs=cs) self.transforms[fn] = (xf, inv) xf = xform_matrix(xf) affine_transform_vertices(pts, xf) affine_transform_vertices(pts, inverse) # add a half-voxel since volume positions are # considered to be at the center of their voxel from numpy import floor, zeros pts = floor(pts / spacing + 0.5).astype(int) for pt in pts: center = tuple(pt) gridData[center] = gridData.get(center, 0) + 1 # generate volume self.status("Generating volume") axisData = zip(*tuple(gridData.keys())) minXyz = [min(ad) for ad in axisData] maxXyz = [max(ad) for ad in axisData] # allow for zero-padding on both ends dims = [maxXyz[axis] - minXyz[axis] + 3 for axis in range(3)] from numpy import zeros, transpose volume = zeros(dims, int) for index, val in gridData.items(): adjIndex = tuple([index[i] - minXyz[i] + 1 for i in range(3)]) volume[adjIndex] = val from VolumeData import Array_Grid_Data gd = Array_Grid_Data( volume.transpose(), # the "cushion of zeros" means d-1... [(d - 1) * spacing for d in minXyz], [spacing] * 3) if volumeName is None: volumeName = self.ensemble.name gd.name = volumeName # show volume self.status("Showing volume") import VolumeViewer dataRegion = VolumeViewer.volume_from_grid_data(gd) vd = VolumeViewer.volumedialog.volume_dialog(create=True) vd.message("Volume can be saved from File menu") self.status("Volume shown")
def masked_volume(volume, surfaces, projection_axis=(0, 0, 1), full_map=False, sandwich=False, invert_mask=False): g = volume.data # Determine transform from vertex coordinates to depth array indices step = min(g.plane_spacings()) fx, fy, fz = orthonormal_frame(projection_axis) from numpy import array, float32, intc, zeros, subtract tf = array(((fx[0], fx[1], fx[2], 0), (fy[0], fy[1], fy[2], 0), (fz[0], fz[1], fz[2], 0)), float32) / step # Transform vertices to depth array coordinates. zsurf = [] tcount = 0 for vertices, triangles in surfaces: varray = vertices.copy() apply_transform(tf, varray) zsurf.append((varray, triangles)) tcount += len(triangles) if tcount == 0: return None vmin, vmax = bounding_box(zsurf) voffset = -(vmin - 0.5) tf[:, 3] += voffset from _contour import shift_vertices for varray, triangles in zsurf: shift_vertices(varray, voffset) from math import ceil, floor dxsize = int(ceil(vmax[0] - vmin[0] + 1)) dysize = int(ceil(vmax[1] - vmin[1] + 1)) # Create depth arrays depth = zeros((dysize, dxsize), float32) tnum = zeros((dysize, dxsize), intc) depth2 = zeros((dysize, dxsize), float32) tnum2 = zeros((dysize, dxsize), intc) # Create minimal size masked volume array and transformation from # masked volume indices to depth array indices. if full_map or invert_mask: from VolumeViewer.volume import full_region ijk_min, ijk_max = full_region(g.size)[:2] else: ijk_min, ijk_max = bounding_box(surfaces, g.xyz_to_ijk_transform) ijk_min = [int(floor(i)) for i in ijk_min] ijk_max = [int(ceil(i)) for i in ijk_max] from VolumeViewer.volume import clamp_region ijk_min, ijk_max = clamp_region((ijk_min, ijk_max, (1, 1, 1)), g.size)[:2] ijk_size = map(lambda a, b: a - b + 1, ijk_max, ijk_min) vol = g.matrix(ijk_min, ijk_size) mvol = zeros(vol.shape, vol.dtype) from Matrix import translation_matrix, multiply_matrices mijk_to_dijk = multiply_matrices(tf, g.ijk_to_xyz_transform, translation_matrix(ijk_min)) # Copy volume to masked volume at masked depth intervals. max_depth = 1e37 if sandwich: dlimit = .5 * max_depth else: dlimit = 2 * max_depth beyond = beyond_tnum = None max_layers = 200 for iter in range(max_layers): depth.fill(max_depth) tnum.fill(-1) any = surfaces_z_depth(zsurf, depth, tnum, beyond, beyond_tnum) if not any: break depth2.fill(max_depth) tnum2.fill(-1) surfaces_z_depth(zsurf, depth2, tnum2, depth, tnum) from _mask import copy_slab copy_slab(depth, depth2, mijk_to_dijk, vol, mvol, dlimit) beyond = depth2 beyond_tnum = tnum2 if invert_mask: subtract(vol, mvol, mvol) # Create masked volume grid object. from VolumeData import Array_Grid_Data from Matrix import apply_matrix morigin = apply_matrix(g.ijk_to_xyz_transform, ijk_min) m = Array_Grid_Data(mvol, morigin, g.step, cell_angles=g.cell_angles, rotation=g.rotation, name=g.name + ' masked') # Create masked volume object. from VolumeViewer import volume_from_grid_data v = volume_from_grid_data(m, show_data=False) v.copy_settings_from(volume, copy_region=False) v.show() volume.show(show=False) return v
def computeVolume(self, atoms, startFrame=None, endFrame=None, bound=None, volumeName=None, step=1, spacing=0.5): # load and process frames if startFrame is None: startFrame = self.startFrame if endFrame is None: endFrame = self.endFrame if bound is not None: from _closepoints import find_close_points, BOXES_METHOD from Matrix import xform_matrix if self.holdingSteady: steadyAtoms, steadySel, steadyCS, inverse = \ self.holdingSteady # all the above used later... inverse = xform_matrix(inverse) gridData = {} from math import floor from numpy import array, float32 from _contour import affine_transform_vertices for fn in range(startFrame, endFrame+1, step): cs = self.findCoordSet(fn) if not cs: self.status("Loading frame %d" % fn) self._LoadFrame(fn, makeCurrent=False) cs = self.findCoordSet(fn) self.status("Processing frame %d" % fn) pts = array([a.coord(cs) for a in atoms], float32) if self.holdingSteady: if bound is not None: steadyPoints = array([a.coord(cs) for a in steadyAtoms], float32) closeIndices = find_close_points( BOXES_METHOD, steadyPoints, #otherPoints, bound)[1] pts, bound)[1] pts = pts[closeIndices] try: xf, inv = self.transforms[fn] except KeyError: xf, inv = self.steadyXform(cs=cs) self.transforms[fn] = (xf, inv) xf = xform_matrix(xf) affine_transform_vertices(pts, xf) affine_transform_vertices(pts, inverse) # add a half-voxel since volume positions are # considered to be at the center of their voxel from numpy import floor, zeros pts = floor(pts/spacing + 0.5).astype(int) for pt in pts: center = tuple(pt) gridData[center] = gridData.get(center, 0) + 1 # generate volume self.status("Generating volume") axisData = zip(*tuple(gridData.keys())) minXyz = [min(ad) for ad in axisData] maxXyz = [max(ad) for ad in axisData] # allow for zero-padding on both ends dims = [maxXyz[axis] - minXyz[axis] + 3 for axis in range(3)] from numpy import zeros, transpose volume = zeros(dims, int) for index, val in gridData.items(): adjIndex = tuple([index[i] - minXyz[i] + 1 for i in range(3)]) volume[adjIndex] = val from VolumeData import Array_Grid_Data gd = Array_Grid_Data(volume.transpose(), # the "cushion of zeros" means d-1... [(d-1) * spacing for d in minXyz], [spacing] * 3) if volumeName is None: volumeName = self.ensemble.name gd.name = volumeName # show volume self.status("Showing volume") import VolumeViewer dataRegion = VolumeViewer.volume_from_grid_data(gd) vd = VolumeViewer.volumedialog.volume_dialog(create=True) vd.message("Volume can be saved from File menu") self.status("Volume shown")
def computeVolume(self, atoms, frame_ids, volumeName=None, spacing=0.5, radiiTreatment="ignored"): #function taken from Movie/gui.py and tweaked to compute volume based on an array of frame_ids from Matrix import xform_matrix gridData = {} from math import floor from numpy import array, float32, concatenate from _contour import affine_transform_vertices insideDeltas = {} include = {} sp2 = spacing * spacing for fn in frame_ids: cs = self.movie.findCoordSet(fn) if not cs: self.movie.status("Loading frame %d" % fn) self.movie._LoadFrame(int(fn), makeCurrent=False) cs = self.movie.findCoordSet(fn) self.movie.status("Processing frame %d" % fn) pts = array([a.coord(cs) for a in atoms], float32) if self.movie.holdingSteady: if bound is not None: steadyPoints = array([a.coord(cs) for a in steadyAtoms], float32) closeIndices = find_close_points( BOXES_METHOD, steadyPoints, #otherPoints, bound)[1] pts, bound)[1] pts = pts[closeIndices] try: xf, inv = self.movie.transforms[fn] except KeyError: xf, inv = self.movie.steadyXform(cs=cs) self.movie.transforms[fn] = (xf, inv) xf = xform_matrix(xf) affine_transform_vertices(pts, xf) affine_transform_vertices(pts, inverse) if radiiTreatment != "ignored": ptArrays = [pts] for pt, radius in zip(pts, [a.radius for a in atoms]): if radius not in insideDeltas: mul = 1 deltas = [] rad2 = radius * radius while mul * spacing <= radius: for dx in range(-mul, mul+1): for dy in range(-mul, mul+1): for dz in range(-mul, mul+1): if radiiTreatment == "uniform" \ and min(dx, dy, dz) > -mul and max(dx, dy, dz) < mul: continue key = tuple(sorted([abs(dx), abs(dy), abs(dz)])) if key not in include.setdefault(radius, {}): include[radius][key] = (dx*dx + dy*dy + dz*dz ) * sp2 <= rad2 if include[radius][key]: deltas.append([d*spacing for d in (dx,dy,dz)]) mul += 1 insideDeltas[radius] = array(deltas) if len(deltas) < 10: print deltas if insideDeltas[radius].size > 0: ptArrays.append(pt + insideDeltas[radius]) pts = concatenate(ptArrays) # add a half-voxel since volume positions are # considered to be at the center of their voxel from numpy import floor, zeros pts = floor(pts/spacing + 0.5).astype(int) for pt in pts: center = tuple(pt) gridData[center] = gridData.get(center, 0) + 1 # generate volume self.movie.status("Generating volume") axisData = zip(*tuple(gridData.keys())) minXyz = [min(ad) for ad in axisData] maxXyz = [max(ad) for ad in axisData] # allow for zero-padding on both ends dims = [maxXyz[axis] - minXyz[axis] + 3 for axis in range(3)] from numpy import zeros, transpose volume = zeros(dims, int) for index, val in gridData.items(): adjIndex = tuple([index[i] - minXyz[i] + 1 for i in range(3)]) volume[adjIndex] = val from VolumeData import Array_Grid_Data gd = Array_Grid_Data(volume.transpose(), # the "cushion of zeros" means d-1... [(d-1) * spacing for d in minXyz], [spacing] * 3) if volumeName is None: volumeName = self.movie.ensemble.name gd.name = volumeName # show volume self.movie.status("Showing volume") import VolumeViewer dataRegion = VolumeViewer.volume_from_grid_data(gd) vd = VolumeViewer.volumedialog.volume_dialog(create=True) vd.message("Volume can be saved from File menu") self.movie.status("Volume shown")