I would like to create a pyton script that takes an excel file (provided by the user), run a script, and save this excel file (with a file name provided by the user).
I would like to implement some buttons using Tkinter to enable non-programmers to easily use my algorithm.
I tried to create a ''load data'' and ''display data'' button (I took a code online) that both work.
However I didn't manage to run the script on the database imported (as I don't know how to access it)
I didnt manage to create a button that enables the user to choose the name of the file they want to save it to.
import tkinter as tk
from tkinter import ttk
from tkinter.filedialog import askopenfilename
from tkinter.filedialog import asksaveasfilename
from tkinter.messagebox import showerror
import pandas as pd
import csv
# --- classes ---
class MyWindow:
def __init__(self, parent):
self.parent = parent
self.filename = None
self.df = None
self.text = tk.Text(self.parent)
self.text.pack()
self.button = tk.Button(self.parent, text='LOAD DATA', command=self.load)
self.button.pack()
self.button = tk.Button(self.parent, text='DISPLAY DATA', command=self.display)
self.button.pack()
self.button = tk.Button(self.parent, text='DISPLAY DATA', command=self.display)
self.button.pack()
def load(self):
name = askopenfilename(filetypes=[('CSV', '*.csv',), ('Excel', ('*.xls', '*.xslm', '*.xlsx'))])
if name:
if name.endswith('.csv'):
self.df = pd.read_csv(name)
else:
self.df = pd.read_excel(name)
self.filename = name
display directly
self.text.insert('end', str(self.df.head()) + '\n')
def display(self):
# ask for file if not loaded yet
if self.df is None:
self.load_file()
# display if loaded
if self.df is not None:
self.text.insert('end', self.filename + '\n')
self.text.insert('end', str(self.df.head()) + '\n')
#def script_python(self):
# self.df=self.df.iloc[:, :-1]
#this is not my original script
#def file_save(self):
# savefile = asksaveasfilename(filetypes=(("Excel files", "*.xlsx"),
# ("All files", "*.*") ))
# --- main ---
if __name__ == '__main__':
root = tk.Tk()
top = MyWindow(root)
root.mainloop()
My python script that do analysis on the excel file works, so I just put a very simple python script to make your life easier.
Thank you
I'm new at Tkinter and not used to classes and dictionaries. thanks for your help
I've modified your code, adding a button to run your script and a button to save the data (I've tried to keep the rest as close to your original code as possible):
import tkinter as tk
from tkinter import ttk
from tkinter.filedialog import askopenfilename
from tkinter.filedialog import asksaveasfilename
from tkinter.messagebox import showerror
import pandas as pd
import csv
# --- classes ---
class MyWindow:
def __init__(self, parent):
self.parent = parent
self.filename = None
self.df = None
self.text = tk.Text(self.parent)
self.text.pack()
# load data button
self.load_button = tk.Button(self.parent, text='LOAD DATA', command=self.load)
self.load_button.pack()
# display data button
self.display_button = tk.Button(self.parent, text='DISPLAY DATA', command=self.display)
self.display_button.pack()
# run script button
self.script_button = tk.Button(self.parent, text='RUN SCRIPT', command=self.script_python)
self.script_button.pack()
# save data button
self.save_button = tk.Button(self.parent, text='SAVE DATA', command=self.file_save)
self.save_button.pack()
def load(self):
name = askopenfilename(filetypes=[('CSV', '*.csv',), ('Excel', ('*.xls', '*.xslm', '*.xlsx'))])
if name:
if name.endswith('.csv'):
self.df = pd.read_csv(name)
else:
self.df = pd.read_excel(name)
self.filename = name
# display directly
self.text.insert('end', str(self.df.head()) + '\n')
def display(self):
# ask for file if not loaded yet
if self.df is None:
self.load_file()
# display if loaded
if self.df is not None:
self.text.insert('end', self.filename + '\n')
self.text.insert('end', str(self.df.head()) + '\n')
def script_python(self):
# replace this with the real thing
self.df = self.df.iloc[:, :-1]
def file_save(self):
fname = asksaveasfilename(filetypes=(("Excel files", "*.xlsx"),
("All files", "*.*")))
# note: this will fail unless user ends the fname with ".xlsx"
self.df.to_excel(fname)
# --- main ---
if __name__ == '__main__':
root = tk.Tk()
top = MyWindow(root)
root.mainloop()
Related
I'm new to Python and with help from here I have drafted a code that gets input from a user and pass it on to other programs.
is there a way to close the pop up window without actually terminating or closing the program. I tried using Destroy() but it either just clears the content of the pop up message or messes the whole code.
Could somebody help me out here please. Below is the code I drafted.
import openpyxl
import tkinter as tk
class App(tk.Frame):
def __init__(self,master=None,**kw):
self.answers = {}
tk.Frame.__init__(self,master=master,**kw)
tk.Label(self,text="Give Input Sheet Path").grid(row=0,column=0)
self.Input_From_User1 = tk.Entry(self)
self.Input_From_User1.grid(row=0,column=1)
tk.Label(self,text="Give Output Sheet Path").grid(row=1,column=0)
self.Input_From_User2 = tk.Entry(self)
self.Input_From_User2.grid(row=1,column=1)
tk.Button(self,text="Feed into Program",command = self.collectAnswers).grid(row=2,column=1)
def collectAnswers(self):
self.answers['Input_Path'] = self.Input_From_User1.get()
self.answers['Output_Path'] = self.Input_From_User2.get()
print("Given Input Path ", self.answers['Input_Path'])
print("Given Output Path ", self.answers['Output_Path'])
global Input_Path
global Output_Path
Input_Path = self.answers['Input_Path']
Output_Path = self.answers['Output_Path']
def main():
root = tk.Tk()
root.geometry("300x100")
App(root).grid()
root.mainloop()
wb = openpyxl.load_workbook(Input_Path)
return wb["Sheet1"]
if __name__ == '__main__':
main()
If all you want is for the TK window to "go away" without actually closing, you could use the iconify() method instead of destroy(); this will minimize the App() window to the taskbar without actually closing it.
I added self.iconify() to the end of your collectAnswers() method.
I also took the liberty of making some changes to your code's style to better align with standard Python style practices - if you aren't already using a linter like Flake8 or a formatter like Black, I would recommend doing so! Better to get used to having a style guide enforced before you develop bad habits!
import openpyxl
import tkinter as tk
class App(tk.Tk): # root application instance
def __init__(self,**kw) -> None:
super().__init__()
self.geometry("300x100")
self.title = ("Enter Sheet Paths")
self.resizable(width=False, height=False)
self.grid()
input_label = tk.Label(
self,
text="Give Input Sheet Path"
)
input_label.grid(row=0, column=0)
self.Input_From_User1 = tk.Entry(self)
self.Input_From_User1.grid(row=0, column=1)
output_label = tk.Label(
self,
text="Give Output Sheet Path"
)
output_label.grid(row=1, column=0)
self.Input_From_User2 = tk.Entry(self)
self.Input_From_User2.grid(row=1, column=1)
submit = tk.Button(
self,
text="Feed into Program",
command=self.collect_answers
)
submit.grid(row=2, column=1)
self.answers = {}
def collect_answers(self) -> None:
self.answers['Input_Path'] = self.Input_From_User1.get()
self.answers['Output_Path'] = self.Input_From_User2.get()
print("Given Input Path ", self.answers['Input_Path'])
print("Given Output Path ", self.answers['Output_Path'])
input_path = self.answers['Input_Path']
output_path = self.answers['Output_Path']
self.iconify() # MINIMIZE THE WINDOW
def main():
app = App()
app.mainloop()
wb = openpyxl.load_workbook(Input_Path)
return wb["Sheet1"]
if __name__ == '__main__':
main()
I'm trying to build a tool for data analysis. I expect that this kind of application gets big, so I'm following the MVC pattern to organize it as best as possible.
My results are saved in .csv files, so the goal should be to "import" them into the GUI. I want to open these files using "CTRL + O" as keybinding and of course using the corresponding option in the menubar.
Now to the actual problem:
The function which is called when hitting "CTRL + O" is in the Controller class and works as expected, I can open a bunch of files and the list saves each name. But when using the menubar I'm stucked how to implement the "command=" option.
This is my code:
import tkinter as tk
from tkinter.filedialog import askopenfilename
from tkinter.constants import ANCHOR, TRUE
from tkinter import Label, filedialog
from tkinter import ttk
class Model():
# more to come
pass
class View:
def __init__(self, view):
self.view = view
self.view.title("analyzer")
self.view.geometry("640x480")
self.view.resizable(False, False)
# menubar
self.menubar = tk.Menu(self.view)
self.view.config(menu=self.menubar)
self.filemenu = tk.Menu(self.menubar, tearoff=False)
self.menubar.add_cascade(label="File", menu=self.filemenu)
self.filemenu.add_command(label="Open", accelerator="Ctrl+O", command=Controller.get_open_filename())
self.filemenu.add_separator()
self.filemenu.add_command(label="Remove", accelerator="Ctrl+R")
self.filemenu.add_command(label="Information", accelerator="Ctrl+I")
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", accelerator="Ctrl+E", command=self.view.quit)
class Controller:
def __init__(self):
self.root = tk.Tk()
self.view = View(self.root)
# keybindings / shortcuts
self.root.bind_all("<Control-e>", lambda event: self.root.quit())
self.root.bind_all("<Control-o>", lambda event: self.get_open_filename())
self.list_of_files = []
def run(self):
self.root.mainloop()
def get_open_filename(self):
self.filename = askopenfilename(title="Select data file", filetypes=(("csv files", "*.csv"), ("all files", "*.*")))
self.list_of_files.append(self.filename)
print(self.list_of_files)
if __name__ == "__main__":
c = Controller()
c.run()
I would really appreciate If some could give me a hint, want I'm doing wrong.
Thanks!
You are trying to call a Controller function from a View Object, but View doesn't know that Controller has that function. It doesn't know that Controller exist.
There might be a better way than this, but you can pass a function as a parameter to View's constructor.
By passing the get_open_filename() function as a parameter to View's constructor you can use that as the command.
NOTE: I called the parameter func so you can see what I'm doing. I'd recommend giving it a better name though.
import tkinter as tk
from tkinter.filedialog import askopenfilename
from tkinter.constants import ANCHOR, TRUE
from tkinter import Label, filedialog
from tkinter import ttk
class Model():
# more to come
pass
class View:
def __init__(self, view, func):
self.view = view
self.view.title("analyzer")
self.view.geometry("640x480")
self.view.resizable(False, False)
# menubar
self.menubar = tk.Menu(self.view)
self.view.config(menu=self.menubar)
self.filemenu = tk.Menu(self.menubar, tearoff=False)
self.menubar.add_cascade(label="File", menu=self.filemenu)
self.filemenu.add_command(label="Open", accelerator="Ctrl+O", command=func)
self.filemenu.add_separator()
self.filemenu.add_command(label="Remove", accelerator="Ctrl+R")
self.filemenu.add_command(label="Information", accelerator="Ctrl+I")
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", accelerator="Ctrl+E", command=self.view.quit)
class Controller:
def __init__(self):
self.root = tk.Tk()
self.view = View(self.root, lambda: self.get_open_filename())
# keybindings / shortcuts
self.root.bind_all("<Control-e>", lambda event: self.root.quit())
self.root.bind_all("<Control-o>", lambda event: self.get_open_filename())
self.list_of_files = []
def run(self):
self.root.mainloop()
def get_open_filename(self):
self.filename = askopenfilename(title="Select data file", filetypes=(("csv files", "*.csv"), ("all files", "*.*")))
self.list_of_files.append(self.filename)
print(self.list_of_files)
if __name__ == "__main__":
c = Controller()
c.run()
Here is the code it is supposed to scan and enumerate a directory and make hashes of whatever files it finds and append them to a dict the code all works separately but I cant get the hashing part to trigger within the threaded code there is no error
import threading
import tkinter as tk
import tkinter.ttk as ttk
import hashlib
import glob
class global_variables:
def __init__(self):
pass
DirSelected = ''
Manifest = []
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("Progress bar example")
self.button = tk.Button(self, text="Start", command=self.start_action)
self.button.pack(padx=10, pady=10)
def start_action(self):
self.button.config(state=tk.DISABLED)
t = threading.Thread(target=self.contar)
t.start()
self.windows_bar = WinProgressBar(self)
def contar(self):
directories_scanned = [str, 'File,Md5!'] # , separator value ! delineator adds new line
target_dir = '/Users/kyle/PycharmProjects/' # this wont run
files = glob.glob(target_dir + '/**/*.*', recursive=True) # Line adds search criteria aka find all all to the
# directory for GLOB
for file in files:
with open(file, "rb") as f:
file_hash = hashlib.md5()
while chunk := f.read(8192):
file_hash.update(chunk)
directories_scanned.append(
file + ',' + str(file_hash.hexdigest() + '!')) # Appends the file path and Md5
global_variables.Manifest = directories_scanned
for x in range(len(global_variables.DirSelected)): # testing to view files TODO REMOVE this
print(global_variables.DirSelected[x])
for i in range(10): #to here
print("Is continuing", i)
print('stop')
self.windows_bar.progressbar.stop()
self.windows_bar.destroy()
self.button.config(state=tk.NORMAL)
class WinProgressBar(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title("Progress")
self.geometry("300x200")
self.progressbar = ttk.Progressbar(self, mode="indeterminate")
self.progressbar.place(x=30, y=60, width=200)
self.progressbar.start(20)
if __name__ == "__main__":
global_variables()
app = App()
app.mainloop()
print("Code after loop")
for x in range(len(global_variables.DirSelected)): # testing to view files TODO REMOVE this
print(global_variables.DirSelected[x])
No errors can be shown this time the code will run
It works fine for me. I think you just were checking the wrong variable DirSelected which was in fact an empty string. (see my code)
Also, I moved your WinProgressBar so it is available when the thread is executing. (was getting an exception on this).
import threading
import tkinter as tk
import tkinter.ttk as ttk
import hashlib
import glob
class global_variables:
def __init__(self):
pass
DirSelected = ''
Manifest = []
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("Progress bar example")
self.button = tk.Button(self, text="Start", command=self.start_action)
self.button.pack(padx=10, pady=10)
def start_action(self):
self.button.config(state=tk.DISABLED)
self.windows_bar = WinProgressBar(self)
t = threading.Thread(target=self.contar)
t.start()
def contar(self):
directories_scanned = [str, 'File,Md5!'] # , separator value ! delineator adds new line
target_dir = '/home/jsantos/workspace/blog.santos.cloud/' # this wont run
files = glob.glob(target_dir + '/**/*.*', recursive=True) # Line adds search criteria aka find all all to the
# directory for GLOB
for file in files:
with open(file, "rb") as f:
file_hash = hashlib.md5()
while chunk := f.read(8192):
file_hash.update(chunk)
directories_scanned.append(
file + ',' + str(file_hash.hexdigest() + '!')) # Appends the file path and Md5
global_variables.Manifest = directories_scanned
for x in range(len(global_variables.Manifest)): # testing to view files TODO REMOVE this
print(global_variables.Manifest[x])
for i in range(10): #to here
print("Is continuing", i)
print('stop')
self.windows_bar.progressbar.stop()
self.windows_bar.destroy()
self.button.config(state=tk.NORMAL)
class WinProgressBar(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title("Progress")
self.geometry("300x200")
self.progressbar = ttk.Progressbar(self, mode="indeterminate")
self.progressbar.place(x=30, y=60, width=200)
self.progressbar.start(20)
if __name__ == "__main__":
global_variables()
app = App()
app.mainloop()
print("Code after loop")
for x in range(len(global_variables.Manifest)): # testing to view files TODO REMOVE this
print(global_variables.Manifest[x])
Checked:
python pass variable tkinter
Tkinter - How to pass instance variable to another class?
and other web resources but didn't find a solution.
I have a Tkinter button to open file:
import tkinter as tk
from tkinter import filedialog as fd
from myClass import my_function
class select_file:
def __init__(self):
self.root = tk.Tk()
self.filename = ""
tk.Button(self.root, text="Browse", command=self.open).pack()
self.root.mainloop()
def open(self):
filetypes = (('LOG files', '*.LOG'), ('All files', '*.*'))
self.filename = fd.askopenfilename(title='open LOG file', filetypes=filetypes)
self.root.destroy()
print(self.filename)
return self.filename
and that's work fine since I can get the file path in right way.
Output:
C:/Users/xxxx/xxxxx/Dataset/SC180918.LOG
My question is how I can pass the selected filename as instance variable to other function within same project but in another file (myClass.py):
class my_function:
log_frame = pd.DataFrame()
def __init__(self, file):
self.file = file
def create_df(self):
myClass.log_frame = pd.read_table(self.file, sep=';', index_col='TimeStamp', parse_dates=True)
return myClass.log_frame
My task is to pass the file to create_df.
I tried several code I will not post to avoid confusion and not working code.
As the file is important, I generated two python files as below:
myClass.py:
class my_function:
def __init__(self, file):
self.file = file
def create_df(self):
print("filename in my_function is ",self.file)
testMe.py:
import tkinter as tk
from tkinter import filedialog as fd
from myClass import my_function
class select_file:
def __init__(self):
self.root = tk.Tk()
self.filename = ""
tk.Button(self.root, text="Browse", command=self.open).pack()
self.root.mainloop()
def open(self):
filetypes = (('LOG files', '*.LOG'), ('All files', '*.*'))
self.filename = fd.askopenfilename(title='open LOG file', filetypes=filetypes)
self.root.destroy()
print("filename in select_file is ",self.filename)
return self.filename
sfClass = select_file() # select filename by Browse
filename = sfClass.filename # get filename from the class
mfClass = my_function(filename) # pass it to the class
mfClass.create_df()
here is the output if I select testMe.py file:
filename in select_file is C:/Users/XXX/XXX/SOquestions/testMe.py
filename in my_function is C:/Users/XXX/XXX/SOquestions/testMe.py
as a result, filename has been successfully passed to the my_function.
I tested as below as well:
print(mfClass.file)
it prints: C:/Users/XXX/XXX/SOquestions/testMe.py
I would do as following;
sfClass = select_file() # select filename by Browse
filename = sfClass.filename # get filename from the class
mfClass = my_function(filename) # pass it to the class
I have this code in python 2.7 using tkinter that creates a Button on a Frame to open a file. There's a Label under it. I'm trying to make it so once the file is opened, the label prints the path, "file1.name" or whatever, on the Label, and if you open a new file it will change that Label again.
Also, I'm betting there's a better way to move data between the functions than I'm using here with global, but that's not worrying me right now.
I have to move the data from the opened files between functions so that I can mix the data and save to a new file. The code is:
from Tkinter import *
import Tkinter
import tkFileDialog
import tkMessageBox
root = Tkinter.Tk()
global rfile1
global rfile2
rfile1 = ""
rfile2 = ""
class Application(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.grid()
self.createWidgets1()
self.createLabels1()
self.createWidgets2()
self.createLabels2()
def createWidgets1(self):
self.oButton = Button(self, text = "open1", command = self.openfile1)
self.oButton.grid()
def createLabels1(self):
self.oLabel = Label(self, text = "whoops")
self.oLabel.grid()
def createWidgets2(self):
self.oButton = Button(self, text = "open2", command= self.openfile2)
self.oButton.grid()
def createLabels2(self):
self.oLabel2 = Label(self, text = "whoops2")
self.oLabel2.grid()
def openfile1(self):
file1 = tkFileDialog.askopenfile(title = "choose a file, *****", parent=root, mode = 'rb')
rfile1 = file1.read()
tkMessageBox.showinfo("oy", rfile1, parent=root)
def openfile2(self):
file2 = tkFileDialog.askopenfile(parent=root, mode='rb')
rfile2 = file2.read()
tkMessageBox.showinfo("hola", rfile2, parent=root)
app = Application()
app.master.title("whiggy whompus")
app.mainloop()
If I understand correctly, you want something like (untested):
def openfile1(self):
file1 = tkFileDialog.askopenfile(title = "choose a file, *****", parent=root, mode = 'rb')
self.oLabel.configure(text=file1.name)
rfile1 = file1.read()
tkMessageBox.showinfo("oy", rfile1, parent=root)
#mgilson has solved your first question. Your second question, about how to pass parameters between functions without using globals :
you might want to look at storing the variables as attributes on your application class :
The syntax self. is an attribute on the current instance (an instance being a particular example of a class - just like your car is a specific example of a class "car").
You can use instance attributes in this example as if they are globals.
from Tkinter import *
import Tkinter
import tkFileDialog
import tkMessageBox
root = Tkinter.Tk()
class Application(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.grid()
self.createWidgets1()
self.createLabels1()
self.createWidgets2()
self.createLabels2()
self.rfile1 = ""
self.rfile2 = ""
def createWidgets1(self):
self.oButton = Button(self, text = "open1", command = self.openfile1)
self.oButton.grid()
def createLabels1(self):
self.oLabel = Label(self, text = "whoops")
self.oLabel.grid()
def createWidgets2(self):
self.oButton = Button(self, text = "open2", command= self.openfile2)
self.oButton.grid()
def createLabels2(self):
self.oLabel2 = Label(self, text = "whoops2")
self.oLabel2.grid()
def openfile1(self):
file1 = tkFileDialog.askopenfile(title = "choose a file, *****", parent=root, mode = 'rb')
self.rfile1 = file1.read()
tkMessageBox.showinfo("oy", self.rfile1, parent=root)
def openfile2(self):
file2 = tkFileDialog.askopenfile(parent=root, mode='rb')
self.rfile2 = file2.read()
tkMessageBox.showinfo("hola", self.rfile2, parent=root)
app = Application()
app.master.title("whiggy whompus")
app.mainloop()