# __version__ = '$Id: PmwFileDialog.py,v 1.2 2002/08/23 15:03:35 gregm Exp $' # # Filename dialogs using Pmw # # (C) Rob W.W. Hooft, Nonius BV, 1998 # # Modifications: # # J. Willem M. Nissink, Cambridge Crystallographic Data Centre, 8/2002 # Added optional information pane at top of dialog; if option # 'info' is specified, the text given will be shown (in blue). # Modified example to show both file and directory-type dialog # # No Guarantees. Distribute Freely. # Please send bug-fixes/patches/features to # ################################################################################ import os,fnmatch,time import Tkinter,Pmw #Pmw.setversion("0.8.5") def _errorpop(master,text): d=Pmw.MessageDialog(master, title="Error", message_text=text, buttons=("OK",)) d.component('message').pack(ipadx=15,ipady=15) d.activate() d.destroy() class PmwFileDialog(Pmw.Dialog): """File Dialog using Pmw""" def __init__(self, parent = None, **kw): # Define the megawidget options. optiondefs = ( ('filter', '*', self.newfilter), ('directory', os.getcwd(), self.newdir), ('filename', '', self.newfilename), ('historylen',10, None), ('command', None, None), ('info', None, None), ) self.defineoptions(kw, optiondefs) # Initialise base class (after defining options). Pmw.Dialog.__init__(self, parent) self.withdraw() # Create the components. interior = self.interior() if self['info'] is not None: rowoffset=1 dn = self.infotxt() dn.grid(row=0,column=0,columnspan=2,padx=3,pady=3) else: rowoffset=0 dn = self.mkdn() dn.grid(row=0+rowoffset,column=0,columnspan=2,padx=3,pady=3) del dn # Create the directory list component. dnb = self.mkdnb() dnb.grid(row=1+rowoffset,column=0,sticky='news',padx=3,pady=3) del dnb # Create the filename list component. fnb = self.mkfnb() fnb.grid(row=1+rowoffset,column=1,sticky='news',padx=3,pady=3) del fnb # Create the filter entry ft = self.mkft() ft.grid(row=2+rowoffset,column=0,columnspan=2,padx=3,pady=3) del ft # Create the filename entry fn = self.mkfn() fn.grid(row=3+rowoffset,column=0,columnspan=2,padx=3,pady=3) fn.bind('',self.okbutton) del fn # Buttonbox already exists bb=self.component('buttonbox') bb.add('OK',command=self.okbutton) bb.add('Cancel',command=self.cancelbutton) del bb Pmw.alignlabels([self.component('filename'), self.component('filter'), self.component('dirname')]) def infotxt(self): """ Make information block component at the top """ return self.createcomponent( 'infobox', (), None, Tkinter.Label, (self.interior(),), width=51, relief='groove', foreground='darkblue', justify='left', text=self['info'] ) def mkdn(self): """Make directory name component""" return self.createcomponent( 'dirname', (), None, Pmw.ComboBox, (self.interior(),), entryfield_value=self['directory'], entryfield_entry_width=40, entryfield_validate=self.dirvalidate, selectioncommand=self.setdir, labelpos='w', label_text='Directory:') def mkdnb(self): """Make directory name box""" return self.createcomponent( 'dirnamebox', (), None, Pmw.ScrolledListBox, (self.interior(),), label_text='directories', labelpos='n', hscrollmode='none', dblclickcommand=self.selectdir) def mkft(self): """Make filter""" return self.createcomponent( 'filter', (), None, Pmw.ComboBox, (self.interior(),), entryfield_value=self['filter'], entryfield_entry_width=40, selectioncommand=self.setfilter, labelpos='w', label_text='Filter:') def mkfnb(self): """Make filename list box""" return self.createcomponent( 'filenamebox', (), None, Pmw.ScrolledListBox, (self.interior(),), label_text='files', labelpos='n', hscrollmode='none', selectioncommand=self.singleselectfile, dblclickcommand=self.selectfile) def mkfn(self): """Make file name entry""" return self.createcomponent( 'filename', (), None, Pmw.ComboBox, (self.interior(),), entryfield_value=self['filename'], entryfield_entry_width=40, entryfield_validate=self.filevalidate, selectioncommand=self.setfilename, labelpos='w', label_text='Filename:') def dirvalidate(self,string): if os.path.isdir(string): return Pmw.OK else: return Pmw.PARTIAL def filevalidate(self,string): if string=='': return Pmw.PARTIAL elif os.path.isfile(string): return Pmw.OK elif os.path.exists(string): return Pmw.PARTIAL else: return Pmw.OK def okbutton(self): """OK action: user thinks he has input valid data and wants to proceed. This is also called by in the filename entry""" fn=self.component('filename').get() self.setfilename(fn) if self.validate(fn): self.canceled=0 self.deactivate() def cancelbutton(self): """Cancel the operation""" self.canceled=1 self.deactivate() def tidy(self,w,v): """Insert text v into the entry and at the top of the list of the combobox w, remove duplicates""" if not v: return entry=w.component('entry') entry.delete(0,'end') entry.insert(0,v) list=w.component('scrolledlist') list.insert(0,v) index=1 while indexself['historylen']: list.delete(index) else: index=index+1 w.checkentry() def setfilename(self,value): if not value: return value=os.path.join(self['directory'],value) dir,fil=os.path.split(value) self.configure(directory=dir,filename=value) c=self['command'] if callable(c): c() def newfilename(self): """Make sure a newly set filename makes it into the combobox list""" self.tidy(self.component('filename'),self['filename']) def setfilter(self,value): self.configure(filter=value) def newfilter(self): """Make sure a newly set filter makes it into the combobox list""" self.tidy(self.component('filter'),self['filter']) self.fillit() def setdir(self,value): self.configure(directory=value) def newdir(self): """Make sure a newly set dirname makes it into the combobox list""" self.tidy(self.component('dirname'),self['directory']) self.fillit() def singleselectfile(self): """Single click in file listbox. Move file to "filename" combobox""" cs=self.component('filenamebox').curselection() if cs!=(): value=self.component('filenamebox').get(cs) self.setfilename(value) def selectfile(self): """Take the selected file from the filename, normalize it, and OK""" self.singleselectfile() value=self.component('filename').get() self.setfilename(value) if value: self.okbutton() def selectdir(self): """Take selected directory from the dirnamebox into the dirname""" cs=self.component('dirnamebox').curselection() if cs!=(): value=self.component('dirnamebox').get(cs) dir=self['directory'] if not dir: dir=os.getcwd() if value: if value=='..': dir=os.path.split(dir)[0] else: dir=os.path.join(dir,value) self.configure(directory=dir) self.fillit() def askfilename(self,directory=None,filter=None): """The actual client function. Activates the dialog, and returns only after a valid filename has been entered (return value is that filename) or when canceled (return value is None)""" if directory!=None: self.configure(directory=directory) if filter!=None: self.configure(filter=filter) self.fillit() self.canceled=1 # Needed for when user kills dialog window self.activate() if self.canceled: return None else: return self.component('filename').get() lastdir="" lastfilter=None lasttime=0 def fillit(self): """Get the directory list and show it in the two listboxes""" # Do not run unnecesarily if self.lastdir==self['directory'] and self.lastfilter==self['filter'] and self.lasttime>os.stat(self.lastdir)[8]: return self.lastdir=self['directory'] self.lastfilter=self['filter'] self.lasttime=time.time() dir=self['directory'] if not dir: dir=os.getcwd() dirs=['..'] files=[] try: fl=os.listdir(dir) fl.sort() except os.error,arg: if arg[0] in (2,20): return raise for f in fl: if os.path.isdir(os.path.join(dir,f)): dirs.append(f) else: filter=self['filter'] if not filter: filter='*' if fnmatch.fnmatch(f,filter): files.append(f) self.component('filenamebox').setlist(files) self.component('dirnamebox').setlist(dirs) def validate(self,filename): """Validation function. Should return 1 if the filename is valid, 0 if invalid. May pop up dialogs to tell user why. Especially suited to subclasses: i.e. only return 1 if the file does/doesn't exist""" return 1 class PmwDirDialog(PmwFileDialog): """Directory Dialog using Pmw""" def __init__(self, parent = None, **kw): # Define the megawidget options. optiondefs = ( ('directory', os.getcwd(), self.newdir), ('historylen',10, None), ('command', None, None), ('info', None, None), ) self.defineoptions(kw, optiondefs) # Initialise base class (after defining options). Pmw.Dialog.__init__(self, parent) self.withdraw() # Create the components. interior = self.interior() if self['info'] is not None: rowoffset=1 dn = self.infotxt() dn.grid(row=0,column=0,columnspan=2,padx=3,pady=3) else: rowoffset=0 dn = self.mkdn() dn.grid(row=1+rowoffset,column=0,columnspan=2,padx=3,pady=3) dn.bind('',self.okbutton) del dn # Create the directory list component. dnb = self.mkdnb() dnb.grid(row=0+rowoffset,column=0,columnspan=2,sticky='news',padx=3,pady=3) del dnb # Buttonbox already exists bb=self.component('buttonbox') bb.add('OK',command=self.okbutton) bb.add('Cancel',command=self.cancelbutton) del bb lastdir="" def fillit(self): """Get the directory list and show it in the two listboxes""" # Do not run unnecesarily if self.lastdir==self['directory']: return self.lastdir=self['directory'] dir=self['directory'] if not dir: dir=os.getcwd() dirs=['..'] try: fl=os.listdir(dir) fl.sort() except os.error,arg: if arg[0] in (2,20): return raise for f in fl: if os.path.isdir(os.path.join(dir,f)): dirs.append(f) self.component('dirnamebox').setlist(dirs) def okbutton(self): """OK action: user thinks he has input valid data and wants to proceed. This is also called by in the dirname entry""" fn=self.component('dirname').get() self.configure(directory=fn) if self.validate(fn): self.canceled=0 self.deactivate() def askfilename(self,directory=None): """The actual client function. Activates the dialog, and returns only after a valid filename has been entered (return value is that filename) or when canceled (return value is None)""" if directory!=None: self.configure(directory=directory) self.fillit() self.activate() if self.canceled: return None else: return self.component('dirname').get() def dirvalidate(self,string): if os.path.isdir(string): return Pmw.OK elif os.path.exists(string): return Pmw.PARTIAL else: return Pmw.OK def validate(self,filename): """Validation function. Should return 1 if the filename is valid, 0 if invalid. May pop up dialogs to tell user why. Especially suited to subclasses: i.e. only return 1 if the file does/doesn't exist""" if filename=='': _errorpop(self.interior(),"Empty filename") return 0 if os.path.isdir(filename) or not os.path.exists(filename): return 1 else: _errorpop(self.interior(),"This is not a directory") return 0 class PmwExistingFileDialog(PmwFileDialog): def filevalidate(self,string): if os.path.isfile(string): return Pmw.OK else: return Pmw.PARTIAL def validate(self,filename): if os.path.isfile(filename): return 1 elif os.path.exists(filename): _errorpop(self.interior(),"This is not a plain file") return 0 else: _errorpop(self.interior(),"Please select an existing file") return 0 class PmwExistingDirDialog(PmwDirDialog): def dirvalidate(self,string): if os.path.isdir(string): return Pmw.OK else: return Pmw.PARTIAL def validate(self,filename): if os.path.isdir(filename): return 1 elif os.path.exists(filename): _errorpop(self.interior(),"This is not a directory") return 0 else: _errorpop(self.interior(),"Please select an existing directory") if __name__=="__main__": root=Tkinter.Tk() root.withdraw() Pmw.initialise() f0=PmwFileDialog(root) f0.title('File name dialog') n=f0.askfilename() print '\nFilename : ',repr(n),'\n' f1=PmwDirDialog(root,info='This is a directory dialog') f1.title('Directory name dialog') while 1: n=f1.askfilename() if n is None: break print "Dirname : ",repr(n)