user5849798
user5849798

Reputation:

Tkinter, asksaveasfile, and unicode

Long story short, I'm making a program that writes a whole bunch of Unicode to a file. It does this by using the Tkinter asksaveasfile dialog to let the user choose where they want to save it. Here's the offending snippet (By the way, I imported tkinter.filedialog as fudder just for fun.):

name = fudder.askopenfilename(defaultextension =("Python Files","*.py"),title = "Choose a file to encrypt.")
with open(name,'r') as Usefile:
    filecont = Usefile.read()
    if filecont is None:
        return
    else:
        result = crypt(filecont)
        with fudder.asksaveasfile(mode = 'w', defaultextension = '.txt', title = 'Save the decrypted file.') as newf:
            if result is None:
                return
            else:
                newf.write(result[0].encode('utf-8'))
                newf.write('\n\n\nKey:\n\n\n')
                newf.write(result[1].encode('utf-8'))
                newf.close()

I looked at several SO questions like this one, but none of them addressed this in particular. The asksaveasfile dialog works just like the open function, but I can't specify the encoding. I tried to encode it to bytes, but you can only write strings to a file, and you can't convert the bytes to a string and then convert it back to bytes.

Upvotes: 2

Views: 3603

Answers (2)

Terry Jan Reedy
Terry Jan Reedy

Reputation: 19174

I checked the code for asksaveasfile and indeed it only passes the filename and mode to open. It is strictly a convenience function combining a call to asksaveasfilename with a call to open(filename, mode), with all other args defaulted. So don't use it.

Instead, use asksaveasfilename and explicitly open the file yourself, just as you did for for the source file. You can then write the open call you want, with encoding='utf-8' and any other non-default options.

The first with should end after you have read Usefile and have no further need of it. The if statement should therefore be dedented.

Upvotes: 6

mhawke
mhawke

Reputation: 87134

Assuming that you are using Python 3 (tkinter.filedialog strongly suggests that you are), the file will be opened with the locale encoding as returned by locale.getpreferredencoding(). If that is already UTF-8 then you should be able to write directly to the file without explicitly encoding the strings.

However, because you are encrypting the file, crypt() probably returns a bytes string, not a str (unicode) string. If this is the case then you should open the input and output files in binary mode. Then no encoding is required when writing to the output file.

name = fudder.askopenfilename(defaultextension =("Python Files","*.py"),title = "Choose a file to encrypt.")
with open(name, 'rb') as Usefile:
    filecont = Usefile.read()
    if filecont is None:
        return
    else:
        result = crypt(filecont)
        if result is None:
            return
        with fudder.asksaveasfile(mode = 'wb', defaultextension = '.txt', title = 'Save the decrypted file.') as newf:
            newf.write(result[0])
            newf.write(b'\n\n\nKey:\n\n\n')
            newf.write(result[1])

The key differences are:

  • Files are opened in binary mode
  • No encoding
  • Save dialog is not displayed if crypt() returns None

Upvotes: 2

Related Questions