498 lines
14 KiB
Python
498 lines
14 KiB
Python
#
|
|
__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 <r.hooft@euromail.com>
|
|
#
|
|
################################################################################
|
|
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('<Return>',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 <Return> 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 index<list.index('end'):
|
|
k=list.get(index)
|
|
if k==v or index>self['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('<Return>',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 <Return> 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)
|