""" python/ui/Animation.py: CCP4MG Molecular Graphics Program Copyright (C) 2001-2008 University of York, CCLRC Copyright (C) 2009-2010 University of York This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, modified in accordance with the provisions of the license to address the requirements of UK law. You should have received a copy of the modified GNU Lesser General Public License along with this library. If not, copies may be downloaded from http://www.ccp4.ac.uk/ccp4license.php This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. """ import mmut import mmdb2 as mmdb from global_definitions import * import utils import os class Animation: ParamsManager = None #------------------------------------------------------------------- def __init__(self,parent='',params={},load=1,**kw): #------------------------------------------------------------------- params.update(kw) self.parent = parent # current_frame = [current_frame, increment to next frame] self.current_frame = [-999,1] self.frame_range = [1,9999] self.legend_obj=None self.nowRunning=0 setattr(self,'filename',params.get('filename',[])) setattr(self,'file_format',params.get('file_format','multiple_PDBs')) if load and self.filename: rv = self.loaddata() if rv: self.delete() AnimationParamsManager() self.update_data_status(params=params) #------------------------------------------------------------------------ def update_data_status(self,params={}): #------------------------------------------------------------------------ rv = 0 if params.has_key('current_frame') and \ params['current_frame'] != self.current_frame and \ params['current_frame'][0] != -999: rv = 1 self.set_frame(frame= params['current_frame'][0], \ direction=params['current_frame'][1]) #print "Animation update_data_status",params['legend_name'],self.legend_obj #if self.legend_obj: print "legend_obj.name",self.legend_obj.name if params.has_key('legend_name'): if params['legend_name'] and not self.legend_obj: rv = 1 self.add_legend() elif (not params['legend_name']) and self.legend_obj: rv = 1 self.legend_obj.delete() self.legend_obj = None return rv def delete(self): if self.legend_obj: self.legend_obj.delete() if hasattr('self','NMR_models_molHnd'): del self.NMR_models_molHnd if hasattr(self.parent,'animation'): del self.parent.animation def params(self): pars = {} for item in ['filename','file_format','current_frame','frame_range']: pars[item] = getattr(self,item) if self.legend_obj: pars['legend_name'] = self.legend_obj.name else: pars['legend_name'] = '' return pars #------------------------------------------------------------------- def loaddata(self,pdbfile='',DIR_pdbfile='',last_frame='',**keywords): #------------------------------------------------------------------- if not pdbfile: pdbfile = self.filename[1] if not DIR_pdbfile:DIR_pdbfile = self.filename[0] # Try to figure out what the file selection means! if pdbfile and self.file_format=='multiple_PDBs': return self.set_filebase(pdbfile=pdbfile,DIR_pdbfile=DIR_pdbfile,last_frame=last_frame) elif pdbfile and self.file_format=='NMR_models': path = utils.get_filename(DIR_pdbfile,pdbfile) if not os.path.exists(path): return 1 molHnd = mmdb.Manager() #rv = molHnd.ReadCoorFile(path) rv = utils.ReadCoorFile(molHnd,path) if rv: del molHnd MGGUI().WarningMessage("Error attempting to read 'NMR' model file") return 1 self.frame_range=[1,molHnd.GetNumberOfModels()] self.filename=[DIR_pdbfile,pdbfile,path] self.NMR_models_molHnd = molHnd if self.parent.nmr_models>1: MGGUI().WarningMessage("Animation successfully loaded. The first model in the structure will be updated with trajectory coordinates when you run the animation") return 0 #-------------------------------------------------------------------------- def set_filebase(self,pdbfile='',DIR_pdbfile='FULLPATH',last_frame=''): #-------------------------------------------------------------------------- import GUI digits = ['0','1','2','3','4','5','6','7''8','9'] path = utils.get_filename(DIR_pdbfile,pdbfile) #print "set_filebase path",path if not os.path.exists(path): return 1 split = os.path.splitext(path) #We expect the filename of the form blahN.pdb #and the first N is probably 1 for nnum in range(-1,-5,-1): if not digits.count(split[0][nnum]): break #print "nnum",nnum if nnum==-1: MGGUI().WarningMessage('Animation file name does not have expected format') return 1 else: nnum=nnum+1 self.filebase = [ split[0][0:nnum], split[1] ] self.filename=[DIR_pdbfile,pdbfile,path] #Find the last frame iframe = int(split[0][nnum:]) continu = 1 while continu: if os.path.exists(self.filebase[0]+str(iframe+1)+ self.filebase[1]): iframe = iframe+1 else: continu = 0 if last_frame and utils.safeInt(last_frame,9999)0 or (mode<0 and not self.nowRunning): self.nowRunning = 1 MAINWINDOW().addAnimationHandler('Animation of '+self.parent.name_label,self.next_frame) #-------------------------------------------------------------------------- def set_frame(self,frame=0,direction='',**keywords): #-------------------------------------------------------------------------- if frame: #print "set_frame frame",frame if frame != self.current_frame[0]: rv = self.load_frame(iframe=frame) if rv[0]: MGGUI().WarningMessage(rv[1]) if direction: self.current_frame[1] = int(direction) def getFrame(self): return self.current_frame[0] def setIncrement(self,inc): self.current_frame[1] = inc def getIncrement(self): return self.current_frame[1] #---------------------------------------------------------------------- def next_frame(self): #---------------------------------------------------------------------- import copy,rebuild iframe = self.current_frame[0] + self.current_frame[1] if iframe < self.frame_range[0]: iframe = copy.deepcopy(self.frame_range[0]) self.current_frame = [self.frame_range[0],1] elif iframe>self.frame_range[1]: iframe = copy.deepcopy(self.frame_range[1]) self.current_frame = [self.frame_range[1],-1] else: self.current_frame[0]=iframe rv = self.load_frame(iframe=iframe) #print "Animation.next_frame next",iframe,self.current_frame,rv return rv #---------------------------------------------------------------------- def load_frame(self,iframe=''): #---------------------------------------------------------------------- import mmdb2 as mmdb #print "Animation.load_frame",iframe, self.file_format if iframe == -1: #Restore the original coordinates molHnd = mmdb.Manager() path = self.parent.filename[2] #rv = molHnd.ReadCoorFile(path) rv = utils.ReadCoorFile(molHnd,path) if rv: del molHnd return[1,'Error loading PDB file '+path] rv = self.parent.molHnd.CopyCoordinates(molHnd) del molHnd if rv: return [1,"Error setting coordinates from "+path] #print "load_frame iframe",iframe,self.current_frame,self.frame_range elif self.file_format == 'multiple_PDBs': path = self.filebase[0]+str(iframe)+ self.filebase[1] #print "loading",path if not os.path.exists(path): return[1,'Error PDB file '+path+' does not exist for frame '+str(iframe)] molHnd = mmdb.Manager() #rv = molHnd.ReadCoorFile(path) rv = utils.ReadCoorFile(molHnd,path) if rv: del molHnd return[1,'Error loading PDB file '+path] rv = self.parent.molHnd.CopyCoordinates(molHnd) #Apply any transformation currently set for this model self.parent.molHnd.ReApplyTransform() del molHnd if rv: return [1,"Error setting coordinates from "+path] elif self.file_format == 'NMR_models': #print "NMR_models_molHnd",self.NMR_models_molHnd rv = self.parent.molHnd.CopyCoordinates(self.NMR_models_molHnd,iframe) if rv: return [1,"Error setting coordinates for frame "+str(iframe)] #Update the GUIs and possible legend self.current_frame[0] = iframe if self.current_frame[1] == '1': dirn = 'forwards' else: dirn = 'backwards' if self.legend_obj: self.legend_obj.set_selection(text=str(self.current_frame[0]+1)) if AnimationParamsManager().get('update_properties'): self.parent.update_dependents('reconfigure') else: self.parent.update_dependents('transform') return [0] #------------------------------------------------------------------- def get_number_of_frames(self): #------------------------------------------------------------------- return self.frame_range[1]-self.frame_range[0]+1 #-------------------------------------------------------------------- def add_legend(self,legend='',master='',attribute=''): #-------------------------------------------------------------------- #print "Animation add_legend",attribute if attribute == 'delete': self.legend_obj = None else: if self.legend_obj: return import Legend self.legend_obj = Legend.Legend(parent=GL2DMANAGER().name, \ name='animation_legend'+self.parent.name, master=self.parent.name) self.legend_obj.set_selection(text=str(self.current_frame[0]+1)) self.parent.add_dependency(self.legend_obj,['delete'],[self.add_legend]) #----------------------------------------------------------------- def copy_data_files(self,project='',dir='',overwrite=1): #----------------------------------------------------------------- ''' Copy the data file(s) to another directory ''' import shutil,glob copied = [] errors = [] if not self.filename: return [len(errors),errors,copied] if self.file_format == 'multiple_PDBs': self.set_filebase(pdbfile=self.filename[1],DIR_pdbfile=self.filename[2]) if not self.filebase: return [1,['No animation files defined'],[]] filnlist = glob.glob(self.filebase[0]+'*'+ self.filebase[1]) else: filnlist = [self.filename] #print "animation copy_data_files filnlist",filnlist for filn in filnlist: ret=utils.copy_data_file(filename=filn,dir=dir,overwrite=overwrite) errors.append(ret[1]) copied.append(ret[2]) if project: self.filename=[project,self.filename[1],ret[1]] return [len(errors),errors,copied] #-------------------------------------------------------------------- #-------------------------------------------------------------------- def AnimationParamsManager(): import services if not Animation.ParamsManager: Animation.ParamsManager = services.ParamsManager( name='Animation_param', \ title='Animation and flashing', picture_definition = 1, gui=[ 'update_properties','delay','flash_delay'], default = {'update_properties': 1, 'delay' : 0.1, 'flash_delay' : 0.5 }, definition={ 'update_properties': dict(type='bool',label='Update properties (e.g. secondary str.) each step'), 'delay' : dict(type=float, label='Minimum time delay between updates', style='spinbox',max=10.0,min=0.1,step=0.1 ), 'flash_delay' : dict(type=float, label='Minimum delay in flashing', style='spinbox',max=10.0,min=0.1,step=0.1 ) }, help = 'animation') return Animation.ParamsManager