3

I'm using the code below (Python 2.7 and Python 3.2) to show an Open Files dialog that supports multiple-selection. On Linux filenames is a python list, but on Windows filenames is returned as {C:/Documents and Settings/IE User/My Documents/VPC_EULA.txt} {C:/Documents and Settings/IE User/My Documents/VPC_ReadMe.txt}, i.e. a raw TCL list.

Is this a python bug, and does anyone here know a good way to convert the raw TCL list into a python list?

if sys.hexversion >= 0x030000F0:
    import tkinter.filedialog as filedialog
else:
    import tkFileDialog as filedialog

options = {}
options['filetypes'] = [('vnote files', '.vnt') ,('all files', '.*')]
options['multiple'] = 1
filenames = filedialog.askopenfilename(**options)
jcollado
  • 39,419
  • 8
  • 102
  • 133
eug
  • 1,118
  • 1
  • 16
  • 25

4 Answers4

3

The problem is an “interesting” interaction between Tcl, Tk and Python, each of which is doing something sensible on its own but where the combination isn't behaving correctly. The deep issue is that Tcl and Python have very different ideas about what types mean, and this is manifesting itself as a value that Tcl sees as a list but Python sees as a string (with the code in Tk assuming that it doesn't need to be careful to be clean for Python). Arguably the Python interface should use the fact that it can know that a Tcl list will be coming back from a multiple selection and hide this, but it doesn't so you're stuck.

I can (and should!) fix this in Tk, but I don't know how long it would take for the fix to find its way back to you that way.


[EDIT]: This is now fixed (with this patch) in the Tk 8.5 maintenance branch and on the main development branch. I can't predict when you'll be able to get a fixed version unless you grab the source out of our fossil repository and build it yourself.

Community
  • 1
  • 1
Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • The problem was an evil type shimmer caused by some unwise code in the Win-specific part of Tk that was checking to see if we were producing a non-empty result in an odd way. Which is silly, since it was pretty easy to get that info anyway without the inefficient data conversion. You can get the patch (for Tk!) from the link in my post. – Donal Fellows Feb 10 '12 at 16:00
  • Also, the only workaround is to parse that Tcl list yourself. While it's not exactly impossible to do that, I really recommend hanging in there while we get the fix deployed. It'll be in ActiveTcl 8.5.12.0 when that comes out. – Donal Fellows Feb 10 '12 at 16:05
  • Sounds wonderful, thanks...except I get an "core.tcl.tk uses an invalid security certificate" error when I click on the link to the patch you provided in your edit -- no a big deal, though. – martineau Feb 10 '12 at 17:06
  • Thanks for looking into this Donal! I'll need a Python-based workaround for current versions - so perhaps a good way would be to evaluate the string into a list using Tcl, as suggested by Cameron Laird [on the python mailing list](http://bytes.com/topic/python/answers/536853-tcl-list-python-list)? I don't know tkinter well so am unsure that that conversion is fully correct.. If you include a workaround in your answer it should be useful to others. – eug Feb 11 '12 at 06:45
  • @martineau: Yeah, it's a self-signed cert. :-( Luckily, it's about to expire in a few weeks so we'll have to fix things. Hopefully I'll be able to talk the guy who's administering the machine into using a certificate with an external CA… – Donal Fellows Feb 11 '12 at 09:07
  • @DonalFellows could you take a look at [this](https://stackoverflow.com/questions/74360620/tkinter-bug-in-filename-window?noredirect=1#comment131276426_74360620) question please ? – Thingamabobs Nov 08 '22 at 13:32
1

For some reason the tk_eval based fix doesn't work for me. The filenames in the string returned by tkFileDialog are only wrapped in {} brackets if they contain whitespace, whereas the tcl docs seem to imply that all list items should be delimited by those brackets.

Anyway, here's a fix that seems to work for me (python 2.7.3 on windows 7):

def fixlist(filenames):
    #do nothing if already a python list
    if isinstance(filenames,list): return filenames

    #http://docs.python.org/library/re.html
    #the re should match: {text and white space in brackets} AND anynonwhitespacetokens
    #*? is a non-greedy match for any character sequence
    #\S is non white space

    #split filenames string up into a proper python list
    result = re.findall("{.*?}|\S+",filenames)

    #remove any {} characters from the start and end of the file names
    result = [ re.sub("^{|}$","",i) for i in result ]

    return result
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
0

A quick way that I've used:

filenames = filenames.strip('{}').split('} {')
user6811
  • 417
  • 1
  • 6
  • 13
0

This fix works for me:

if sys.hexversion >= 0x030000F0:
    import tkinter.filedialog as filedialog
    string_type = str
else:
    import tkFileDialog as filedialog
    string_type = basestring

options = {}
options['filetypes'] = [('vnote files', '.vnt') ,('all files', '.*')]
options['multiple'] = 1
filenames = filedialog.askopenfilename(**options)
if isinstance(filenames, string_type):
    # tkinter is not converting the TCL list into a python list...
    # see http://stackoverflow.com/questions/9227859/
    #
    # based on suggestion by Cameron Laird in http://bytes.com/topic/python/answers/536853-tcl-list-python-list
    if sys.hexversion >= 0x030000F0:
        import tkinter
    else:
        import Tkinter as tkinter
    tk_eval = tkinter.Tk().tk.eval
    tcl_list_length = int(tk_eval("set tcl_list {%s}; llength $tcl_list" % filenames))
    filenames = [] # change to a list
    for i in range(tcl_list_length):
        filenames.append(tk_eval("lindex $tcl_list %d" % i))
return filenames
eug
  • 1,118
  • 1
  • 16
  • 25