Python Directory Reader Tkinter GUI not working properly - python

Hi Everyone I am working on my final project and I am creating a directory snooper
So here is the bottomline of this script:
GUI with Tkinter that asks user for directory and prompts user to
save a cvs file to the space of their choosing. CSV will contain
filename,file extension and file size taken from the directory that
the user inputed.
My GUI has a text scroll box that should print the process of the
script and print when it is done,
My problems:
When saving CSV. It does not produce a CSV and I get this error
"ValueError: I/O operation on closed file".asksaveasfilename() is
not working
GUI text scroll box is not working and printing out the information
i need
How do I add headers to my CSV so that my CSV will have
filename,extension,size, and comment
Attached below is my script. Can anyone help me with this?
from Tkinter import Tk
from tkFileDialog import askdirectory
from array import *
import os
version = '1.0'
import os
import csv
from Tkinter import BOTH, LEFT, TOP, END, StringVar
from ttk import Frame, Entry, Button, Label
from ScrolledText import ScrolledText
from tkFileDialog import askdirectory
from tkFileDialog import asksaveasfilename
class FileWalkerWindow(Frame):
def __init__(self):
Frame.__init__(self)
self.pack(expand=True, fill=BOTH)
self.master.title("Directory Snooper v" + version)
self.master.iconname("Directory Snooper")
self.dir = StringVar() # tkinter does not work with standard python variables
self.dir.set(os.getcwd()) # set to current working directory
description = "This program walks through a directories " \
+ "print out name of directory file path. " \
+ "prints out mumber of files in your in your directory. " \
+ "It list files and tests for corrupted zipfiles and " \
+ "creates a CSV file of the findings"
row1 = Frame(self)
Label(row1, text="Choose Directory:").pack(side=LEFT, pady=10)
self.dir_ent = Entry(row1, width=80, textvariable=self.dir)
self.dir_ent.pack(side=LEFT)
Button(row1, text="Browse", width=10, command=self.browse).pack(side=LEFT, padx=5)
row1.pack(side=TOP, ipadx=15)
row2 = Frame(self)
btn = Button(row2, text="Snoop", command=self.savefile, width=15)
btn.pack(side=LEFT, padx=5, pady=10)
row2.pack(side=TOP)
self.output = ScrolledText(self, height=15, state="normal",
padx=10, pady=10,
wrap='word')
self.output.insert(END, description)
self.output.pack(side=LEFT, fill=BOTH, expand=True, padx=5, pady=5)
self.bind('<Key-Return>', self.savefile) # bind enter press to walk
def browse(self):
dirpath = askdirectory(parent=self, title="Select Directory")
self.dir.set(dirpath)
def savefile (self):
self.output.delete(1.0, END)
name=asksaveasfilename()
with open(name,'w') as csvfile:
dirList = os.listdir(self.dir.get())
data = ((fname, str(os.path.getsize(self.dir.get() + "/" + fname)), str(os.path.splitext(fname)[1]),) for
fname in
dirList)
for entry in data:
create=csv.writer(csvfile)
create.writerow(','.join(entry) + '\n')
csvfile.close()
if __name__ == '__main__':
FileWalkerWindow().mainloop()

When saving CSV. It does not produce a CSV and I get this error
"ValueError: I/O operation on closed file"
You do close the file in your for loop:
for entry in data:
create=csv.writer(csvfile)
create.writerow(','.join(entry) + '\n')
csvfile.close()
Unindent it to close the file after the for loop. You should move the csv.writer() outside of the for loop as well (as mentioned in comments):
create=csv.writer(csvfile)
for entry in data:
create.writerow(','.join(entry) + '\n')
csvfile.close()
GUI text scroll box is not working and printing out the information i need
The question is when you want to display the information!
You can add a button to print the content of the chosen directory or print it automatically when the user selects a folder to browse.
To do the latter, you can edit you browse method, which should look like:
def browse(self):
dirpath = askdirectory(parent=self, title="Select Directory")
self.dir.set(dirpath)
self.output.insert(END, "\n" + "\n".join(os.listdir(dirpath)))
How do I add headers to my CSV so that my CSV will have
filename,extension,size, and comment
Just write a Python string containing your headers before writing actual values:
with open(name,'w') as csvfile:
csvfile.write("filename,extension,size,comment")
dirList = os.listdir(self.dir.get())
# some other stuff after...
Please note also that, while it is strongly discouraged by Python guidelines to use from module import * in general, the standard way to import Tkinter is from Tkinter import * :)

Related

How to get a user-selected file and feed it to a python program in Tkinter

I am currently messing about with Tkinter creating an interface for a project I created. The program takes in a bunch of file paths as inputs for it to run. I'm trying to create a tkinter interface where I can upload the 4 files I need or at least somehow get the filepaths and the. feed those to the program. Here is what I have:
import sys
import os
import comparatorclass
from tkinter import *
from tkinter.ttk import *
from tkinter.filedialog import askopenfile
root=Tk()
root.geometry('1000x1000')
def open_file():
file = askopenfile(mode ='r', filetypes =[('Python Files', '*.py')])
if file is not None:
content = file.read()
print(content)
def run_comparator():
comparatorclass.main()
button2 = Button(root, text ='Open', command = lambda:open_file())
button2.pack(side = TOP, pady = 10)
button1 = Button(root,text="hello",command= run_comparator)
button1.pack()
root.mainloop()
as you can see, I have two buttons. The issue I'm having is how to connect my openfile function to my run_comparator function such that the 4 files I need to open are passed on to the run_comparator

Use variable outside of function or in other function?

Ive rewritten this for more context, in a logical order this is what i want the program to do
1 by pressing open file it needs to open a specified file and put it into the text widget (done)
2 by pressing the extract file it needs to extract something from a specified subtree(in an xml file)
3 export the extracted data to a text file
but lets go back to point nr 2 as i have not yet written the extractor(easy part) first i need to refference a file i want to edit, and that is where i run into my problem. inside extract it cant acess vars in openfile and i dont want to reopen the file again.
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
import tkinter as tk
interface = tk.Tk()
interface.geometry("500x500")
interface.title("Text display")
def openfile():
filename = filedialog.askopenfilename()
print(filename)
file = open(filename)
txt = file.read()
print(txt)
T = tk.Text(interface, height=10, width=50)
T.insert(tk.END, txt)
T.grid(column=1, row=2)
return txt
def extract():
print(txt)
button = ttk.Button(interface, text="Open text File", command=openfile) # <------
button.grid(column=1, row=1)
buttonex = ttk.Button(interface, text="Extract subtitles", command=extract) # <------
buttonex.grid(column=2, row=1)
interface.mainloop()
NameError: name 'txt' is not defined (when i press extract)
As the edit of the initial questions shows that a GUI is planned I would suggest to move your TK widgets into an interface class as in the tkinter documentation. Further, if you plan to do more complex manipulations you should make an own class to hold your data resp. manipulate it.
import tkinter as tk
class App(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.create_widgets()
def create_widgets(self):
# Create your buttons and connect them to the methods
self.button_load = tk.Button(self)
self.button_load["command"] = self.loadTextFromFile()
self.button_load["text"] = "Load Data"
self.button_load.gird(column=1, row=1)
self.buttonex = tk.Button(self)
self.buttonex["text"] = "Extract subtitles"
self.buttonex["command"] = self.extract()
self.buttonex.grid(column=2, row=1)
# Create the text widget
self.text_widget = tk.Text(self, height=10, width=50)
self.text_widget.grid(column=1, row=2)
def loadTextFromFile(self):
filename = filedialog.askopenfilename()
print(filename)
try:
file = open(filename)
txt = file.read()
file.close()
self.text_widget.insert(tk.END, txt)
except Exception:
# If anything went wrong, close the file before reraising
file.close()
raise
def extract(self):
# Now you have access to self.text_widget and it holds your read in text
do_something(self.text_widget)
# Maybe at the following functions to make your file importable without directly executing it. Could come in handy later on.
def run():
# create the application
myapp = App()
#
# here are method calls to the window manager class
#
myapp.master.title("My Do-Nothing Application")
myapp.master.maxsize(1000, 400)
# start the program
myapp.mainloop()
if __name__ == '__main__':
run()
Have a look at the tkinter documentation for further examples: https://docs.python.org/3/library/tkinter.html
The additional if clause checks if your module is the main module and executes the run function. This defines an entry point if you directly run your module and prevents functions from execution at import time if you intend to import the module into another module. A more detailed explanation can be found here:
What does if __name__ == "__main__": do?
---------- Old Answer --------------
As pointed out in the comment you need to somehow return your local variables back to the calling function in order to be able to use them somewhere else. This could be achieved by a simple return statement:
def openfile():
""" It is the responsibility of the caller to close the return value """
filename = filedialog.askopenfilename()
print(filename)
try:
file = open(filename)
txt = file.read()
T = tk.Text(interface, height=10, width=50)
T.insert(tk.END, txt)
T.grid(column=1, row=2)
return file
except Exception:
# If anything went wrong, close the file before reraising
file.close()
raise
would for example return the open fileid for further manipulation. E.g.:
def extract():
with openfile() as file:
txtex = file.read()
print (txtex)
If your goal is howeer, to manipulate the file content and read it later you would need to save your manipulations back into the file otherwise your second read would not see the changes. Dont forget to close your file again.
I changed three lines where I believe to have come across typos:
From: self.button_load["command"] = self.loadTextFromFile() to: self.button_load["command"] = self.loadTextFromFile
From: self.button_load.gird(column=1, row=1) to: self.button_load.grid(column=1, row=1)
From: self.buttonex["command"] = self.extract() to: self.buttonex["command"] = self.extract
Thank you for this example!

Update a label with the file's name, AFTER the file has been selected in the dile directory

Before a file is selected, the GUI will have a blank space where the file's name should be. After the file is selected, the GUI should update, and the name of the file selected should be displayed. I have made many different attempts at this, and the program does execute properly, however the name of the file is not displayed. I will show, as best I can, what the GUI should looke like before and after the file is selected
I have tried setting a StringVar() and having the label it is associated with update appropriately, however it has not worked so far. If I had to guess what was wrong, I would guess that the window ins't updating properly, but I am not sure.
import tkinter as tk
from tkinter import StringVar
from tkinter import ttk
from tkinter import filedialog
#Wraps two functions inside an object which allows both functions to use filename#
class PDFSelector:
#Allows user to select PDF to use in program#
def select_PDF(self):
#Opens file directory to select a file, and shows both folders and PDF files only#
#This should be what changes lbl1a below to the name of the file selected)
self.filename = filedialog.askopenfilename(initialdir = "/", title = "Select file", filetypes = (("pdf files", "*.pdf"), ("all files", "*.*")))
file_name.set(self.filename)
window.update_idletasks()
window.update()
#----Main----#
#Creates an instance of the wrapped functions to use the GUI#
selector = PDFSelector()
#Creats the GUI that will be used to select inputs#
window = tk.Tk()
window.geometry("600x130")
window.title("Word Frequency Program")
window.resizable(0, 0)
#Just a simple label on the GUI#
#The name of the file should appear next to "File Selected" AFTER the file has been selected by the user
lbl1 = tk.Label(window, text = "File Selected: ")
lbl1.grid(row = 1, column = 1)
file_name = StringVar()
lbl1a = tk.Label(window, textvariable = file_name)
lbl1a.grid(row = 1, column = 2)
#Calls the select_PDF method to choose a PDF for the program to read#
button1 = ttk.Button(window, text = "Select File", command = selector.select_PDF)
button1.grid(row = 1, column = 4)
window.mainloop()
window.destroy()
GUI should display the name of the file
Apparently the code does work, however there is something on my end that does not display the updated name of the PDF selected. This is for anyone who finds this question in the future, that the code I wrote works.

Python - tkinter and glob.glob working togheter

Im new to tkinter and trying to open the explorer (on windows) so that i can chose what folder i want to use in my program. I found a template for tkinter and altered it to work with my function and how i need the filepath to be. Before i tried using tkinter to "select my folder", i had manually writen the directory in the glob.glob function like this glob.glob(r'C:\Users\Desktop\Spyder\*.log') (and it worked). So my new ide was to replace the pathname input from r'C:\Users\Desktop\Spyder\*.log' to a variabel that stored the same pathname but now it used tkinters askdirectory() to finde the directory inteed.
import glob
import os
from itertools import zip_longest
import tkinter as tk
from tkinter import filedialog
#-------------Connect to Access2013------------------
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.pack()
self.create_widgets()
def create_widgets(self):
self.select_folder = tk.Button(self)
self.select_folder["text"] = "Open WindowsExplorer"
self.select_folder["command"] = self.ask_directory_to_folder
self.select_folder.pack(side="top")
self.quit = tk.Button(self, text="QUIT", fg="red",
command=root.destroy)
self.quit.pack(side="bottom")
def ask_directory_to_folder(self):
clerdatabase() # a funktion that resets the autonumber and deleats all data from every table
print("Open!")
filepath = filedialog.askdirectory()
log_filepath = "r'"+ str(filepath +"/*.log'")
right_log_filepath = log_filepath.replace('/','\ ').replace(' ','')
find_filenames(right_log_filepath)
root = tk.Tk()
app = Application(master=root)
app.mainloop()
#--------------Scan selected folder for .log files and starts to scan files---------
def find_filenames(right_log_filepath): #finds every file in the chosen filepath
print(right_log_filepath) # r'C:\Users\Desktop\Spyder\*.log'
print("ok")
filenames = [] # list for all the found filenames
for filepath_search in glob.glob(str(right_log_filepath), recursive=True): #A for loop that opens every .log file in the chosen directory folder
print('run')
My problem ist that i don´t get the for loop filepath_search to work (it prints "ok"). But the word run inside the for loop dose not print, i guess it´s because it gets stuck somewhere before that? Someone who has more experience with tkinter that can help me? Thanks
I guess issue caused by what is passed to glob.glob since it doesn't find anything. It seems that it is mostly related to the fact that you add ' characters at the beggining and end of your right_log_filepath.
In ask_directory_to_folder function replace:
log_filepath = "r'"+ str(filepath +"/*.log'")
right_log_filepath = log_filepath.replace('/','\ ').replace(' ','')
find_filenames(right_log_filepath)
With:
from os import path # should be at the top of your file
log_filepath = path.join(filepath, "*.log")
find_filenames(log_filepath)

I want to use extensions list at the time of "save as" option in File Dialog for file write in Python 3

I want to use extensions list example : [".txt",".html",".css"] for save as option in file dialog popup window. when I use this method
file_opt = options = {}
options['defaultextension'] = '.txt',I can able to write any file with .txt as default extension without choose in save as option but I want to choose extensions for save as in file dialog popup window from using my extensions list.
I'm using Python 3.5 based Anaconda IDE
If you look at the documentation here you can see that you can pass in the filetypes keyword which specifies a list of tuples that have the name, and file extension respectively for the different types of filetypes you want to be able to save as.. So you can do something along the lines of:
import tkinter as tk
from tkinter import filedialog as fd
def save_file():
filename = fd.asksaveasfilename(defaultextension='.txt',
filetypes= [('Text','.txt'), ('HTML', '.html'), ('CSS', '.css')])
if filename:
print("User saved the filename with extension:", filename.split(".")[-1])
root = tk.Tk()
button = tk.Button(root, text='Save File', command=save_file)
button.pack()
root.mainloop()
Or if you really want to use a dictionary for this:
import tkinter as tk
from tkinter import filedialog as fd
SAVE_OPTS = {'defaultextension':'.txt',
'filetypes': [('Text','.txt'), ('HTML', '.html'), ('CSS', '.css')]}
def save_file():
filename = fd.asksaveasfilename(**SAVE_OPTS)
if filename:
print("User saved the filename with extension:", filename.split(".")[-1])
root = tk.Tk()
button = tk.Button(root, text='Save File', command=save_file)
button.pack()
root.mainloop()
SaveFileDialog sd = new SaveFileDialog();
sd.Filter = "Text File (*.txt)|*.txt|PNG File (*.txt)|*.png"|...;
if(sd.ShowDialog() == DialogResult.OK) {
richTextBox1.SaveFile(sd.FileName, RichTextBoxStreamType.PlainText);
}

Categories

Resources