I'm trying to make a savefile dialog in tkinter. I need to save the file name to use later. However, I do not want the filedialog to accept selecting an already existing file name.
So far I only have this:
from tkinter import filedialog
my_file = filedialog.asksaveasfilename(defaultextension = ".myfile",
filetypes = [("MY SUPER FILE", ".myfile"),
("All files", ".*")])
One possibility would be to get the file name, check if it exists (using os.path.isfile) and ask again the user for new name if there is already a file with the same name. However, the tkinter filedialog asks the user "file already exists. do you want to overwrite?". So it seems confusing if later I tell the user that I do not accept the filename choice. Is there a way to force the tkinter filedialog to not ask the user about the overwriting?
Edit: Based on the suggestions in the answers, I tried to make my own save file dialog.
I basically only added a warning to the tkinter save dialog:
class MySaveFileDialog(filedialog.FileDialog):
""" File save dialog that does not allow overwriting of existing file"""
def ok_command(self):
file = self.get_selection()
if os.path.exists(file):
if os.path.isdir(file):
self.master.bell()
return
messagebox.showarning("The current file name already exists. Please give another name to save your file.")
else:
head, tail = os.path.split(file)
if not os.path.isdir(head):
self.master.bell()
return
self.quit(file)
So, it looks pretty simple. Then I thought: I need to create my own asksaveasfilename function.
I went to check the source:
def asksaveasfilename(**options):
"Ask for a filename to save as"
return SaveAs(**options).show()
Humm.. I need to see what is SaveAs doing.
class SaveAs(_Dialog):
"Ask for a filename to save as"
command = "tk_getSaveFile"
Aaannddd... i'm lost. I don't understand how this pieces fit together. 'SaveAs' just has the command tk_getSaveFile. How is the SaveFileDialog being used here? And how can I make my own myasksaveasfilename function?
There is no such option. If you want to get rid of the security question, then you'll have to write your own file dialog.
If you look at filedialog.py, you'll see that the dialog is implemented in Python. So all you have to do is to extend the class SaveFileDialog and override the method ok_command() with one that doesn't allow to select an existing file name.
You can use most of the existing code and just change a few texts to achieve your goal.
I haven't tested it but this code should work:
def ok_command(self):
file = self.get_selection()
if os.path.exists(file):
if os.path.isdir(file):
self.master.bell()
return
d = Dialog(self.top,
title="Overwrite Existing File",
text="You can't overwrite an existing file %r. Please select a new name." % (file,),
bitmap='error',
default=0,
strings=("OK",))
return
else:
head, tail = os.path.split(file)
if not os.path.isdir(head):
self.master.bell()
return
self.quit(file)
An answer to a slightly adjacent topic, but this was the closest existing question on SO so assume others may end up here when faced with the same problem, and I got my answer from above information.
If you are looking to select a tk.filedialog.asksaveasfile, but without overwriting an existing file (ie append to it), and wanting to avoid users being faced with the 'Do you want to overwrite?' popup, one can select the append mode, and turn off the overwrite popup.
outfilename = tk.filedialog.asksaveasfile(title = 'Save processing output CSV',
initialdir = output_directory,
defaultextension = '.csv',
mode = 'a',
confirmoverwrite = False,
filetypes = (('csv File', '.csv'),)
)
To complete what Aaron said, here is the current code of ok_command:
def ok_command(self):
file = self.get_selection()
if os.path.exists(file):
if os.path.isdir(file):
self.master.bell()
return
d = Dialog(self.top,
title="Overwrite Existing File Question",
text="Overwrite existing file %r?" % (file,),
bitmap='questhead',
default=1,
strings=("Yes", "Cancel"))
if d.num != 0:
return
else:
head, tail = os.path.split(file)
if not os.path.isdir(head):
self.master.bell()
return
self.quit(file)
You can achieve this using tkFileDialog.asksaveasfilename(confirmoverwrite=False)
Here is a mock up:
import tkFileDialog
import tkMessageBox as mbox
class Example():
proceed = False
while proceed == False:
dlg = tkFileDialog.asksaveasfilename(confirmoverwrite=False)
fname = dlg
if fname != '':
try:
f = open(fname, "r")
f.close()
mbox.showerror("Error","File exists - Choose another file name")
except:
mbox.showinfo("Info","File does not exist - Continue")
proceed = True
else:
break
print("Finished")
Related
I am using python tkinter to make a text editor. I am trying to work out the logic of how to save the file the first time with a dialog box (so you can set the file name), and all the other times without. Here is the code I have so far:
filename = ""
def save():
input_data = textinput.get("1.0", "1000000.0")
try:
with open(filename, "w") as f:
f.write(input_data)
f.close()
except IOError:
file = filedialog.asksaveasfile(initialdir="/documents", title="Save file", defaultextension=".txt", filetypes=[("HTML files", ".html"), ("All file types", ".*")])
global filename
filename = file.name
file.write(input_data)
file.close()
input_data is the text field contents, and the save() function is called when they push the save button.
I am trying to make it so the first time when you click save, the filename variable has not been assigned to the file path and name, so it does the IOError, that opens the box and saves the file. It also sets a global filename as seen here: Link. But this error is given: SyntaxError: name 'filename' is used prior to global declaration. It is, but that is what the link said to do. How can I solve this problem? Thanks for any help in advance! xD
A global statement needs to be placed above the first use of a variable, even if that first use is just reading the existing value, not assigning to it.
In your code, you're trying to use filename in your open call, within the with statement. The global statement occurs later, when you're trying to assign to the global filename variable.
The easy fix is to just move the global statement up to the top of the function:
filename = ""
def save():
global filename # move the global statement up here
input_data = textinput.get("1.0", "1000000.0")
try:
with open(filename, "w") as f: # so it's above this use of filename
f.write(input_data)
f.close()
except IOError:
file = filedialog.asksaveasfile(initialdir="/documents", title="Save file", defaultextension=".txt", filetypes=[("HTML files", ".html"), ("All file types", ".*")])
filename = file.name # not only the assignment down here
file.write(input_data)
file.close()
A better approach is probably to not use a global variable at all, and instead use an instance of a class to hold the filename, but that's a more disruptive change.
from an GUI application designed with tkinter, I wish to save some datas in a file in appending mode. To get the file's name I use asksaveasfilename from filedialog module. Here is the code:
from tkinter.filedialog import asksaveasfilename
def save_file():
file_name = asksaveasfilename()
if file_name:
f = open(file_name, 'a')
contents = tab_chrono.text_area.get(1.0, 'end')
f.write(contents)
f.close()
The problem happens when I select in the dialog an existing file, I got a warning that the file will be overwritten. It is not true since I append in the file.
Is there a way to get rid of this warning ? Or do I have to rewrite a askappendfilename myself ? This is missing in filedialog module.
The asksaveasfilename dialog accepts a confirmoverwrite argument to enable or disable the file existence check.
file_name = asksaveasfilename(confirmoverwrite=False)
This can be found in the Tk manual for tk_getSaveFile but doesn't appear to be documented for tkinter. It was introduced in Tk 8.5.11 so is relatively new in Tk terms (released Nov 2011).
Use the option confirmoverwrite to prevent the message, when selecting an existing file.
import tkFileDialog
import time
class Example():
dlg = tkFileDialog.asksaveasfilename(confirmoverwrite=False)
fname = dlg
if fname != '':
try:
f = open(fname, "rw+")
text = f.read()
print text
except:
f = open(fname, "w")
new_text = time.time()
f.write(str(new_text)+'\n')
f.close()
Edit: Note that I am using f.read() to be able to print the existing text.
You may want to remove the f.read() and subsequent print statement and replace them with a f.seek(0,2) which positions the pointer at the end of the existing file.
The other option is as follows using the append option in the file open, which will create the file if it doesn't already exist:
import tkFileDialog
import time
class Example():
dlg = tkFileDialog.asksaveasfilename(confirmoverwrite=False)
fname = dlg
if fname != '':
f = open(fname, "a")
new_text = time.time()
f.write(str(new_text)+'\n')
f.close()
I am wondering how I can make my script save to the Desktop. Here's my code:
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.platypus import Image
import csv
import os
data_file = "hata.csv"
def import_data(data_file):
inv_data = csv.reader(open(data_file, "r"))
for row in inv_data:
var1 = row[0]
# do more stuff
pdf_file = os.path.abspath("~/Desktop/%s.pdf" % var1)
generate_pdf(variable, pdf_file)
def generate_pdf(variable, file_name):
c = canvas.Canvas(file_name, pagesize=letter)
# do some stuff with my variables
c.setFont("Helvetica", 40, leading=None)
c.drawString(150, 2300, var1)
c.showPage()
c.save()
import_data(data_file)
So this works perfectly, and it saves/creates the PDF I want -- but in the directory of the script. I would instead like to save it to, say, the Desktop.
When I researched and found os.path.abspath, I thought I solved it; but I receive the following error
File "/usr/local/lib/python3.4/site-packages/reportlab/pdfbase/pdfdoc.py", line 218, in SaveToFile
f = open(filename, "wb")
FileNotFoundError: [Errno 2] No such file or directory: '/Users/TARDIS/Desktop/tests/~/Desktop/00001.pdf'
which tells me that it's trying to save starting from my script's home folder. How do I get it to see outside of that?
After much trial and error using different methods that all had drawbacks, I came up with a solution and figured I'd post it here for posterity. I'm rather new to programming so apologies if this is obvious to the more experienced.
First, I give my pdf file a name:
pdf_name = number + ".pdf"
Then, I find the path to the Desktop for current user (given that I don't know what the user name will be, which was the original root of the problem) and create a path to it so that the pdf can be to be saved there.
save_name = os.path.join(os.path.expanduser("~"), "Desktop/", pdf_name)
Finally, that's passed in to my pdf generation function:
...
save_name = ....
generate_pdf(variable, save_name)
def generate_pdf(variable, save_name):
c = canvas.Canvas(save_name, pagesize=letter)
....
And that's it.
I am using tkFileDialog.asksaveasfilename function in a GUI. The following is the code:
def onSaveImage(self):
ftypes = [('PNG', '*.png'), ('JPEG', '*.jpg'), ('PDF', '*.pdf')]
imagefname = tkFileDialog.asksaveasfilename(parent=self, filetypes = ftypes)
The function to save the file is deduced from the filename extension. Is there a more elegant way to get the users choice of file type from the file dialog itself?
Maybe now it's late but
Try to set defaultextension
saveDirectory=asksaveasfilename(defaultextension=".*",filetypes = (("JPEG files","*.jpg"),('all files','*.*')))
you'll get the result is the filename following by the the users choice of file type
Is there a method of creating a text file without opening a text file in "w" or "a" mode? For instance If I wanted to open a file in "r" mode but the file does not exist then when I catch IOError I want a new file to be created
e.g.:
while flag == True:
try:
# opening src in a+ mode will allow me to read and append to file
with open("Class {0} data.txt".format(classNo),"r") as src:
# list containing all data from file, one line is one item in list
data = src.readlines()
for ind,line in enumerate(data):
if surname.lower() and firstName.lower() in line.lower():
# overwrite the relevant item in data with the updated score
data[ind] = "{0} {1}\n".format(line.rstrip(),score)
rewrite = True
else:
with open("Class {0} data.txt".format(classNo),"a") as src:
src.write("{0},{1} : {2}{3} ".format(surname, firstName, score,"\n"))
if rewrite == True:
# reopen src in write mode and overwrite all the records with the items in data
with open("Class {} data.txt".format(classNo),"w") as src:
src.writelines(data)
flag = False
except IOError:
print("New data file created")
# Here I want a new file to be created and assigned to the variable src so when the
# while loop iterates for the second time the file should successfully open
At the beginning just check if the file exists and create it if it doesn't:
filename = "Class {0} data.txt"
if not os.path.isfile(filename):
open(filename, 'w').close()
From this point on you can assume the file exists, this will greatly simplify your code.
No operating system will allow you to create a file without actually writing to it. You can encapsulate this in a library so that the creation is not visible, but it is impossible to avoid writing to the file system if you really want to modify the file system.
Here is a quick and dirty open replacement which does what you propose.
def open_for_reading_create_if_missing(filename):
try:
handle = open(filename, 'r')
except IOError:
with open(filename, 'w') as f:
pass
handle = open(filename, 'r')
return handle
Better would be to create the file if it doesn't exist, e.g. Something like:
import sys, os
def ensure_file_exists(file_name):
""" Make sure that I file with the given name exists """
(the_dir, fname) = os.path.split(file_name)
if not os.path.exists(the_dir):
sys.mkdirs(the_dir) # This may give an exception if the directory cannot be made.
if not os.path.exists(file_name):
open(file_name, 'w').close()
You could even have a safe_open function that did something similar prior to opening for read and returning the file handle.
The sample code provided in the question is not very clear, specially because it invokes multiple variables that are not defined anywhere. But based on it here is my suggestion. You can create a function similar to touch + file open, but which will be platform agnostic.
def touch_open( filename):
try:
connect = open( filename, "r")
except IOError:
connect = open( filename, "a")
connect.close()
connect = open( filename, "r")
return connect
This function will open the file for you if it exists. If the file doesn't exist it will create a blank file with the same name and the open it. An additional bonus functionality with respect to import os; os.system('touch test.txt') is that it does not create a child process in the shell making it faster.
Since it doesn't use the with open(filename) as src syntax you should either remember to close the connection at the end with connection = touch_open( filename); connection.close() or preferably you could open it in a for loop. Example:
file2open = "test.txt"
for i, row in enumerate( touch_open( file2open)):
print i, row, # print the line number and content
This option should be preferred to data = src.readlines() followed by enumerate( data), found in your code, because it avoids looping twice through the file.