Advertisement
Guest User

filedialogue.py

a guest
Sep 26th, 2015
125
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.63 KB | None | 0 0
  1. """File selection dialog classes.
  2.  
  3. Classes:
  4.  
  5. - FileDialog
  6. - LoadFileDialog
  7. - SaveFileDialog
  8.  
  9. This module also presents tk common file dialogues, it provides interfaces
  10. to the native file dialogues available in Tk 4.2 and newer, and the
  11. directory dialogue available in Tk 8.3 and newer.
  12. These interfaces were written by Fredrik Lundh, May 1997.
  13. """
  14.  
  15. from tkinter import *
  16. from tkinter.dialog import Dialog
  17. from tkinter import commondialog
  18.  
  19. import os
  20. import fnmatch
  21.  
  22.  
  23. dialogstates = {}
  24.  
  25.  
  26. class FileDialog:
  27.  
  28.     """Standard file selection dialog -- no checks on selected file.
  29.  
  30.    Usage:
  31.  
  32.        d = FileDialog(master)
  33.        fname = d.go(dir_or_file, pattern, default, key)
  34.        if fname is None: ...canceled...
  35.        else: ...open file...
  36.  
  37.    All arguments to go() are optional.
  38.  
  39.    The 'key' argument specifies a key in the global dictionary
  40.    'dialogstates', which keeps track of the values for the directory
  41.    and pattern arguments, overriding the values passed in (it does
  42.    not keep track of the default argument!).  If no key is specified,
  43.    the dialog keeps no memory of previous state.  Note that memory is
  44.    kept even when the dialog is canceled.  (All this emulates the
  45.    behavior of the Macintosh file selection dialogs.)
  46.  
  47.    """
  48.  
  49.     title = "File Selection Dialog"
  50.  
  51.     def __init__(self, master, title=None):
  52.         if title is None: title = self.title
  53.         self.master = master
  54.         self.directory = None
  55.  
  56.         self.top = Toplevel(master)
  57.         self.top.title(title)
  58.         self.top.iconname(title)
  59.  
  60.         self.botframe = Frame(self.top)
  61.         self.botframe.pack(side=BOTTOM, fill=X)
  62.  
  63.         self.selection = Entry(self.top)
  64.         self.selection.pack(side=BOTTOM, fill=X)
  65.         self.selection.bind('<Return>', self.ok_event)
  66.  
  67.         self.filter = Entry(self.top)
  68.         self.filter.pack(side=TOP, fill=X)
  69.         self.filter.bind('<Return>', self.filter_command)
  70.  
  71.         self.midframe = Frame(self.top)
  72.         self.midframe.pack(expand=YES, fill=BOTH)
  73.  
  74.         self.filesbar = Scrollbar(self.midframe)
  75.         self.filesbar.pack(side=RIGHT, fill=Y)
  76.         self.files = Listbox(self.midframe, exportselection=0,
  77.                              yscrollcommand=(self.filesbar, 'set'))
  78.         self.files.pack(side=RIGHT, expand=YES, fill=BOTH)
  79.         btags = self.files.bindtags()
  80.         self.files.bindtags(btags[1:] + btags[:1])
  81.         self.files.bind('<ButtonRelease-1>', self.files_select_event)
  82.         self.files.bind('<Double-ButtonRelease-1>', self.files_double_event)
  83.         self.filesbar.config(command=(self.files, 'yview'))
  84.  
  85.         self.dirsbar = Scrollbar(self.midframe)
  86.         self.dirsbar.pack(side=LEFT, fill=Y)
  87.         self.dirs = Listbox(self.midframe, exportselection=0,
  88.                             yscrollcommand=(self.dirsbar, 'set'))
  89.         self.dirs.pack(side=LEFT, expand=YES, fill=BOTH)
  90.         self.dirsbar.config(command=(self.dirs, 'yview'))
  91.         btags = self.dirs.bindtags()
  92.         self.dirs.bindtags(btags[1:] + btags[:1])
  93.         self.dirs.bind('<ButtonRelease-1>', self.dirs_select_event)
  94.         self.dirs.bind('<Double-ButtonRelease-1>', self.dirs_double_event)
  95.  
  96.         self.ok_button = Button(self.botframe,
  97.                                  text="OK",
  98.                                  command=self.ok_command)
  99.         self.ok_button.pack(side=LEFT)
  100.         self.filter_button = Button(self.botframe,
  101.                                     text="Filter",
  102.                                     command=self.filter_command)
  103.         self.filter_button.pack(side=LEFT, expand=YES)
  104.         self.cancel_button = Button(self.botframe,
  105.                                     text="Cancel",
  106.                                     command=self.cancel_command)
  107.         self.cancel_button.pack(side=RIGHT)
  108.  
  109.         self.top.protocol('WM_DELETE_WINDOW', self.cancel_command)
  110.         # XXX Are the following okay for a general audience?
  111.         self.top.bind('<Alt-w>', self.cancel_command)
  112.         self.top.bind('<Alt-W>', self.cancel_command)
  113.  
  114.     def go(self, dir_or_file=os.curdir, pattern="*", default="", key=None):
  115.         if key and key in dialogstates:
  116.             self.directory, pattern = dialogstates[key]
  117.         else:
  118.             dir_or_file = os.path.expanduser(dir_or_file)
  119.             if os.path.isdir(dir_or_file):
  120.                 self.directory = dir_or_file
  121.             else:
  122.                 self.directory, default = os.path.split(dir_or_file)
  123.         self.set_filter(self.directory, pattern)
  124.         self.set_selection(default)
  125.         self.filter_command()
  126.         self.selection.focus_set()
  127.         self.top.wait_visibility() # window needs to be visible for the grab
  128.         self.top.grab_set()
  129.         self.how = None
  130.         self.master.mainloop()          # Exited by self.quit(how)
  131.         if key:
  132.             directory, pattern = self.get_filter()
  133.             if self.how:
  134.                 directory = os.path.dirname(self.how)
  135.             dialogstates[key] = directory, pattern
  136.         self.top.destroy()
  137.         return self.how
  138.  
  139.     def quit(self, how=None):
  140.         self.how = how
  141.         self.master.quit()              # Exit mainloop()
  142.  
  143.     def dirs_double_event(self, event):
  144.         self.filter_command()
  145.  
  146.     def dirs_select_event(self, event):
  147.         dir, pat = self.get_filter()
  148.         subdir = self.dirs.get('active')
  149.         dir = os.path.normpath(os.path.join(self.directory, subdir))
  150.         self.set_filter(dir, pat)
  151.  
  152.     def files_double_event(self, event):
  153.         self.ok_command()
  154.  
  155.     def files_select_event(self, event):
  156.         file = self.files.get('active')
  157.         self.set_selection(file)
  158.  
  159.     def ok_event(self, event):
  160.         self.ok_command()
  161.  
  162.     def ok_command(self):
  163.         self.quit(self.get_selection())
  164.  
  165.     def filter_command(self, event=None):
  166.         dir, pat = self.get_filter()
  167.         try:
  168.             names = os.listdir(dir)
  169.         except OSError:
  170.             self.master.bell()
  171.             return
  172.         self.directory = dir
  173.         self.set_filter(dir, pat)
  174.         names.sort()
  175.         subdirs = [os.pardir]
  176.         matchingfiles = []
  177.         for name in names:
  178.             fullname = os.path.join(dir, name)
  179.             if os.path.isdir(fullname):
  180.                 subdirs.append(name)
  181.             elif fnmatch.fnmatch(name, pat):
  182.                 matchingfiles.append(name)
  183.         self.dirs.delete(0, END)
  184.         for name in subdirs:
  185.             self.dirs.insert(END, name)
  186.         self.files.delete(0, END)
  187.         for name in matchingfiles:
  188.             self.files.insert(END, name)
  189.         head, tail = os.path.split(self.get_selection())
  190.         if tail == os.curdir: tail = ''
  191.         self.set_selection(tail)
  192.  
  193.     def get_filter(self):
  194.         filter = self.filter.get()
  195.         filter = os.path.expanduser(filter)
  196.         if filter[-1:] == os.sep or os.path.isdir(filter):
  197.             filter = os.path.join(filter, "*")
  198.         return os.path.split(filter)
  199.  
  200.     def get_selection(self):
  201.         file = self.selection.get()
  202.         file = os.path.expanduser(file)
  203.         return file
  204.  
  205.     def cancel_command(self, event=None):
  206.         self.quit()
  207.  
  208.     def set_filter(self, dir, pat):
  209.         if not os.path.isabs(dir):
  210.             try:
  211.                 pwd = os.getcwd()
  212.             except OSError:
  213.                 pwd = None
  214.             if pwd:
  215.                 dir = os.path.join(pwd, dir)
  216.                 dir = os.path.normpath(dir)
  217.         self.filter.delete(0, END)
  218.         self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*"))
  219.  
  220.     def set_selection(self, file):
  221.         self.selection.delete(0, END)
  222.         self.selection.insert(END, os.path.join(self.directory, file))
  223.  
  224.  
  225. class LoadFileDialog(FileDialog):
  226.  
  227.     """File selection dialog which checks that the file exists."""
  228.  
  229.     title = "Load File Selection Dialog"
  230.  
  231.     def ok_command(self):
  232.         file = self.get_selection()
  233.         if not os.path.isfile(file):
  234.             self.master.bell()
  235.         else:
  236.             self.quit(file)
  237.  
  238.  
  239. class SaveFileDialog(FileDialog):
  240.  
  241.     """File selection dialog which checks that the file may be created."""
  242.  
  243.     title = "Save File Selection Dialog"
  244.  
  245.     def ok_command(self):
  246.         file = self.get_selection()
  247.         if os.path.exists(file):
  248.             if os.path.isdir(file):
  249.                 self.master.bell()
  250.                 return
  251.             d = Dialog(self.top,
  252.                        title="Overwrite Existing File Question",
  253.                        text="Overwrite existing file %r?" % (file,),
  254.                        bitmap='questhead',
  255.                        default=1,
  256.                        strings=("Yes", "Cancel"))
  257.             if d.num != 0:
  258.                 return
  259.         else:
  260.             head, tail = os.path.split(file)
  261.             if not os.path.isdir(head):
  262.                 self.master.bell()
  263.                 return
  264.         self.quit(file)
  265.  
  266.  
  267.  
  268. # For the following classes and modules:
  269. #
  270. # options (all have default values):
  271. #
  272. # - defaultextension: added to filename if not explicitly given
  273. #
  274. # - filetypes: sequence of (label, pattern) tuples.  the same pattern
  275. #   may occur with several patterns.  use "*" as pattern to indicate
  276. #   all files.
  277. #
  278. # - initialdir: initial directory.  preserved by dialog instance.
  279. #
  280. # - initialfile: initial file (ignored by the open dialog).  preserved
  281. #   by dialog instance.
  282. #
  283. # - parent: which window to place the dialog on top of
  284. #
  285. # - title: dialog title
  286. #
  287. # - multiple: if true user may select more than one file
  288. #
  289. # options for the directory chooser:
  290. #
  291. # - initialdir, parent, title: see above
  292. #
  293. # - mustexist: if true, user must pick an existing directory
  294. #
  295.  
  296.  
  297. class _Dialog(commondialog.Dialog):
  298.  
  299.     def _fixoptions(self):
  300.         try:
  301.             # make sure "filetypes" is a tuple
  302.             self.options["filetypes"] = tuple(self.options["filetypes"])
  303.         except KeyError:
  304.             pass
  305.  
  306.     def _fixresult(self, widget, result):
  307.         if result:
  308.             # keep directory and filename until next time
  309.             # convert Tcl path objects to strings
  310.             try:
  311.                 result = result.string
  312.             except AttributeError:
  313.                 # it already is a string
  314.                 pass
  315.             path, file = os.path.split(result)
  316.             self.options["initialdir"] = path
  317.             self.options["initialfile"] = file
  318.         self.filename = result # compatibility
  319.         return result
  320.  
  321.  
  322. #
  323. # file dialogs
  324.  
  325. class Open(_Dialog):
  326.     "Ask for a filename to open"
  327.  
  328.     command = "tk_getOpenFile"
  329.  
  330.     def _fixresult(self, widget, result):
  331.         if isinstance(result, tuple):
  332.             # multiple results:
  333.             result = tuple([getattr(r, "string", r) for r in result])
  334.             if result:
  335.                 path, file = os.path.split(result[0])
  336.                 self.options["initialdir"] = path
  337.                 # don't set initialfile or filename, as we have multiple of these
  338.             return result
  339.         if not widget.tk.wantobjects() and "multiple" in self.options:
  340.             # Need to split result explicitly
  341.             return self._fixresult(widget, widget.tk.splitlist(result))
  342.         return _Dialog._fixresult(self, widget, result)
  343.  
  344. class SaveAs(_Dialog):
  345.     "Ask for a filename to save as"
  346.  
  347.     command = "tk_getSaveFile"
  348.  
  349.  
  350. # the directory dialog has its own _fix routines.
  351. class Directory(commondialog.Dialog):
  352.     "Ask for a directory"
  353.  
  354.     command = "tk_chooseDirectory"
  355.  
  356.     def _fixresult(self, widget, result):
  357.         if result:
  358.             # convert Tcl path objects to strings
  359.             try:
  360.                 result = result.string
  361.             except AttributeError:
  362.                 # it already is a string
  363.                 pass
  364.             # keep directory until next time
  365.             self.options["initialdir"] = result
  366.         self.directory = result # compatibility
  367.         return result
  368.  
  369. #
  370. # convenience stuff
  371.  
  372. def askopenfilename(**options):
  373.     "Ask for a filename to open"
  374.  
  375.     return Open(**options).show()
  376.  
  377. def asksaveasfilename(**options):
  378.     "Ask for a filename to save as"
  379.  
  380.     return SaveAs(**options).show()
  381.  
  382. def askopenfilenames(**options):
  383.     """Ask for multiple filenames to open
  384.  
  385.    Returns a list of filenames or empty list if
  386.    cancel button selected
  387.    """
  388.     options["multiple"]=1
  389.     return Open(**options).show()
  390.  
  391. # FIXME: are the following  perhaps a bit too convenient?
  392.  
  393. def askopenfile(mode = "r", **options):
  394.     "Ask for a filename to open, and returned the opened file"
  395.  
  396.     filename = Open(**options).show()
  397.     if filename:
  398.         return open(filename, mode)
  399.     return None
  400.  
  401. def askopenfiles(mode = "r", **options):
  402.     """Ask for multiple filenames and return the open file
  403.    objects
  404.  
  405.    returns a list of open file objects or an empty list if
  406.    cancel selected
  407.    """
  408.  
  409.     files = askopenfilenames(**options)
  410.     if files:
  411.         ofiles=[]
  412.         for filename in files:
  413.             ofiles.append(open(filename, mode))
  414.         files=ofiles
  415.     return files
  416.  
  417.  
  418. def asksaveasfile(mode = "w", **options):
  419.     "Ask for a filename to save as, and returned the opened file"
  420.  
  421.     filename = SaveAs(**options).show()
  422.     if filename:
  423.         return open(filename, mode)
  424.     return None
  425.  
  426. def askdirectory (**options):
  427.     "Ask for a directory, and return the file name"
  428.     return Directory(**options).show()
  429.  
  430.  
  431.  
  432. # --------------------------------------------------------------------
  433. # test stuff
  434.  
  435. def test():
  436.     """Simple test program."""
  437.     root = Tk()
  438.     root.withdraw()
  439.     fd = LoadFileDialog(root)
  440.     loadfile = fd.go(key="test")
  441.     fd = SaveFileDialog(root)
  442.     savefile = fd.go(key="test")
  443.     print(loadfile, savefile)
  444.  
  445.     # Since the file name may contain non-ASCII characters, we need
  446.     # to find an encoding that likely supports the file name, and
  447.     # displays correctly on the terminal.
  448.  
  449.     # Start off with UTF-8
  450.     enc = "utf-8"
  451.     import sys
  452.  
  453.     # See whether CODESET is defined
  454.     try:
  455.         import locale
  456.         locale.setlocale(locale.LC_ALL,'')
  457.         enc = locale.nl_langinfo(locale.CODESET)
  458.     except (ImportError, AttributeError):
  459.         pass
  460.  
  461.     # dialog for openening files
  462.  
  463.     openfilename=askopenfilename(filetypes=[("all files", "*")])
  464.     try:
  465.         fp=open(openfilename,"r")
  466.         fp.close()
  467.     except:
  468.         print("Could not open File: ")
  469.         print(sys.exc_info()[1])
  470.  
  471.     print("open", openfilename.encode(enc))
  472.  
  473.     # dialog for saving files
  474.  
  475.     saveasfilename=asksaveasfilename()
  476.     print("saveas", saveasfilename.encode(enc))
  477.  
  478. if __name__ == '__main__':
  479.     test()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement