I am new to Tkinter and GUI design but I'm definitely excited to learn. I'm trying to create a user interface that allows a user to load an excel spreadsheet .xls. If the spreadsheet is successfully opened then a button should pop up allowing the user to import information into a database.
My problem is that whenever I run this code the file dialog pops up before the main gui with the actual browse button pops up. I did some tests and this behavior is due to calling fname.show(). The thing is, and please correct me if I'm wrong, I need to use fname.show() to get the filepath so that I can pass the filepath into the xlrd.open_workbook method to read data from the spreadsheet. Is there another way of getting the filename? I feel like there should be an easy solution but there isn't much in terms of documentation on the specifics of tkFileDialog.
Thanks.
Below is my code:
import config
from importdb import importTLAtoDB
import Tkinter as tk
import tkFileDialog as tkfd
import tkMessageBox as tkmb
import xlrd
def openFile():
#returns an opened file
fname = tkfd.Open(filetypes = [("xls files","*.xls")])
fpath = fname.show()
if fname:
try:
TLA_sheet = xlrd.open_workbook(fpath).\
sheet_by_name('Q3 TLA - TOP SKUs')
tk.Button(root, text = "Import TLAs", command = importTLAtoDB(TLA_sheet)).pack()
tkmb.showinfo("Success!", "Spreadsheet successfully loaded. \n\
Click Import TLAs to load TLA info into RCKHYVEDB database.")
except:
tkmb.showerror("Error", "Failed to read file\n '%s'\n\
Make sure file is a type .xls" % fpath)
#GUI setup
root = tk.Tk()
root.title("TLA Database Tool")
tk.Button(root, text = "Browse", command = openFile(), width = 10).pack()
root.mainloop()
When you do, command = importTLAtoDB(TLA_sheet) you call the function and assign functions' value to command.
If you want to call a function with an argument, you need to use lambda or partials(there might be other options but these two are most preferred ones as far as I know).
So your Button line should be like this:
tk.Button(root, text="Import TLAs", command=lambda: importTLAtoDB(TLA_sheet)).pack()
Also, you may want to check this question. How to pass arguments to a Button command in Tkinter?
tk.Button(root, text = "Browse", command = openFile, width = 10).pack()
you dont want to actually call it when you setup the button
Related
I'm a super beginner with Python, so please be kind.
I am creating an app that should take in user input from text boxes, and then when the user presses the submit button this is saved in a text file.
I think the issue is that I'm not quite sure how to create the right function for the pushbutton command.
I would really appreciate if someone can code a simple app showing how to do this.
This is the code I have so far, but I get an error "TypeError: write() argument must be str, not TextBox".
from guizero import *
import os
cwd = os.getcwd()
# function for writing files
def save_file():
with open(cwd+'/Desktop/File handling/newfile.txt','a') as f:
f.write(userInput)
app = App("testing")
userInput = TextBox(app)
submit_button = PushButton(app, command=save_file, text="submit")
app.display()
`
I figured it out. Thanks :)
from guizero import *
import os
cwd = os.getcwd()
# function for writing files
def save_file():
with open(cwd+'/Desktop/File handling/newfile.txt','a') as f:
f.write("First name:"+" "+first_name.value+"\n")
#the app
app = App("testing")
#text box
first_name = TextBox(app, width=30, grid=[2,4])
#submit button
box = Box(app)
submitbutton = PushButton(app, command=(save_file), text="Submit")
app.display()
I'm writing a tkinter app that involves a lot of user input fields. One of these fields is a full file path, which is just begging for user error. In order to mitigate this, I'd like to program that particular Entry widget to call filedialog.askopenfilename() when clicked on, so that the user can just pick the file rather than entering the full path manually. As I understand it, the following code should do just that:
# Minimalized version of my code that reproduces the problem
import tkinter as tk
from tkinter import ttk, filedialog
parent = tk.Tk()
val = tk.StringVar()
def get_file(_):
f = filedialog.askopenfilename()
val.set(f)
parent.focus()
label = ttk.Label(parent, text="Put your file here")
label.pack()
enter = ttk.Entry(parent, textvariable=val)
enter.pack()
enter.bind("<FocusIn>", get_file)
parent.mainloop()
When I run this and click on the Entry widget, get_file() gets called twice in a row. The second call comes immediately after I close the first file dialog and overwrites the first file I chose.
Why is this happening, and how do I stop it?
Thanks!
I have this script where I initialize a tkinter object with some labels, entries, and buttons with commands. when I run the script, the init function executes fine and the instance of the tkinter window gets spawned and the buttons work just fine. The problem is that, the logic after the init function does not get executed and the tkinter window stays looping for ever unless I hit the exit button and then it just finishes the script. I want to be able to use the inputs from the tkinter.Entry() and the tkinter.filedialog.askopenfilename() functions to do more stuff later in the application such as loading an excel sheet based on the path passed on the askopenfilename() sections but I can't get it to continue.
Note: I am new to using classes so I believe my mistake is somewhere in the way I am laying down the building blocks of the instance itself. Also, while putting this scrip together so many other questions arised and it has been very hard to find concise answers online. I will post some of those questions here in case someone wants to answer them (not needed though)
1- Do I need to declare every single new variable that come in new methods of the class in the init function, if so, why? I just did it here because pycharm game me warning every time I did not do it but not sure what is the reason behind it.
2- When calling a variable on a new method within the class, and the variable is already defined in the init fucntion, do I need to include the variable in the arguments section of the method? if so, do I add the variable with the self argument or not?
import pandas as pd
from openpyxl import load_workbook
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter.messagebox import showinfo
from datetime import datetime
import openpyxl
class App(tk.Tk):
def __init__(self):
super().__init__()
# Define parameters
self.current_PR_report = ""
self.new_PR_report = ""
self.path = ""
self.raw_excel = None
self.new_report = None
self.current_report = ""
# Configure the root window
self.title("PR Reader")
self.geometry('250x200')
# Label
self.label = ttk.Label(self, text="Merge Problem Reports!")
self.label.pack()
# Date Label and Entry
self.date_label = tk.Label(self, text="Enter Date (MM-DD-YYYY)")
self.date_label.pack()
self.date_label_entry = tk.Entry(self)
self.date_label_entry.pack()
# Run button
self.button = ttk.Button(self, text="Run", command=self.select_file)
self.button.pack()
# Button for closing
self.exit_button = tk.Button(self, text="Exit", command=self.destroy)
self.exit_button.pack(pady=20)
# Bring Dialog box to obtain Excel files paths
def select_file(self):
self.current_PR_report = filedialog.askopenfilename(
title="Select Current PR Report",
initialdir="some path",
filetypes=[("Excel Files", "*.xlsx")])
self.new_PR_report = filedialog.askopenfilename(
title="Select New PR Report",
initialdir="some path",
filetypes=[("Excel Files", "*.xlsx")])
# Load the spreadsheets
self.new_report = self.read_excel_report(self.new_PR_report)
self.current_report = self.read_excel_report(self.current_PR_report)
# Define function to load Excel data
def read_excel_report(self, path):
try:
# sheet_name=None indicates you want a dictionary of data frames, each item in the dictionary
# representing a different worksheet.
self.raw_excel = pd.read_excel(path, sheet_name=-1, engine='openpyxl')
self.raw_excel = self.raw_excel.fillna('')
print("data extracted")
return self.raw_excel
except Exception as e:
print(e)
if __name__ == "__main__":
app = App()
app.mainloop()
My main issue is that after running the script my other methods were not getting executed. I learned that I not only need to define the methods on my class but also need to call them inside of it. I this case, I had to call them inside the logic of the "select_file" method since it is the method called when the "Run" button gets called.
I just started programming with Python and I'm trying to create a GUI using tkinter where it would ask the user to select a zip file and file destination to send it to after extracting. What I have noticed is that when a user re-enters a destination, it would still store the previous file directory as well. How do I prevent this?
import tkinter as tk
from tkinter import filedialog
# Setting the window size
screenHeight = 450
screenWidth = 350
root = tk.Tk()
# get the location of the zip file
def input_dir():
input_filedir = filedialog.askopenfilename(initialdir='/', title='Select File',
filetypes=(("zip", "*.zip"), ("all files", "*.*")))
my_label.pack()
return input_filedir
# get location of destination for file
def output_dir():
output_filename = filedialog.askdirectory()
# Setting the canvas size to insert our frames
canvas = tk.Canvas(root, height=screenHeight, width=screenWidth)
canvas.pack()
# Setting the frame size to insert all our widgets
frame = tk.Frame(root, bg='#002060')
frame.place(relwidth=1, relheight=1)
# button to get user to chose file directory
openFile = tk.Button(frame, text='Choose the file path you want to extract', command=input_dir)
openFile.pack(side='top')
# button to get destination path
saveFile = tk.Button(frame, text="Chose the location to save the file", command=output_dir)
saveFile.pack(side='bottom')
extractButton = tk.Button(frame, text="Extract Now")
root.mainloop()
I have tried adding this line of code in the def input_dir function but it changed the positioning of the buttons. I'm still working on the code for extracting the zip.
for widget in frame.winfor_children():
if isinstance(widget, tk.Label):
widget.destroy()
The files/directories the user clicks are not saved really. Your variables input_filedir and output_filename are garbage collected when their respective functions are completed. If you are talking about how the dialog boxes go back to the most recent places opened in a dialog, that is not really something you can mess with too much. The easy answer is you can add the keyword 'initialdir' when you make the dialog to just pick where it goes like this:
output_filename = filedialog.askdirectory(initialdir='C:/This/sort/of/thing/')
The long answer is that filedialog.askdirectory actually creates an instance of filedialog.Directory and in that class, it saves that information for later in a method called _fixresult (but that method also does other things that are important.) You could overwrite this by doing something like this:
class MyDirectory(filedialog.Directory):
def _fixresult(self, widget, result):
"""
this is just a copy of filedialog.Directory._fixresult without
the part that saves the directory for next time
"""
if result:
# convert Tcl path objects to strings
try:
result = result.string
except AttributeError:
# it already is a string
pass
self.directory = result # compatibility
return result
def askdirectory (**options):
"Ask for a directory, and return the file name"
return MyDirectory(**options).show()
and then using your own askdirectory function instead of filedialog.askdirectory, but that is really convoluted so I recommend using initialdir instead if you can.
P.S. When a button is clicked, it calls the function you set after "command=" when you made the button; it seems you got that. But these functions are 'void', in that their return is just ignored. That is to say, the "return input_filedir" line does nothing.
So I have a program that is basically supposed to have a button that opens a file dialog in the (username) folder. But when I run the program it opens without even pushing the button. What's more, the button doesn't even show up. So in addition to that problem I have to find a way to turn the selected directory into a string.
import tkinter
import tkinter.filedialog
import getpass
gui = tkinter.Tk()
user = getpass.getuser()
tkinter.Button(gui, command=tkinter.filedialog.askopenfilename(initialdir='C:/Users/%s' % user)).pack()
gui.mainloop()
Regarding your first issue, you need to put the call to tkinter.filedialog.askopenfilename in a function so that it isn't run on startup. I actually just answered a question about this this morning, so you can look here for the answer.
Regarding your second issue, the button isn't showing up because you never placed it on the window. You can use the grid method for this:
button = tkinter.Button(gui, command=lambda: tkinter.filedialog.askopenfilename(initialdir='C:/Users/%s' % user))
button.grid()
All in all, your code should be like this:
import tkinter
import tkinter.filedialog
import getpass
gui = tkinter.Tk()
user = getpass.getuser()
button = tkinter.Button(gui, command=lambda: tkinter.filedialog.askopenfilename(initialdir='C:/Users/%s' % user))
button.grid()
gui.mainloop()
You forgot to use a geometry manager on the button:
button = tkinter.Button(window, command=test)
button.pack()
If you don't do it, the button won't be drawn. You might find this link useful: http://effbot.org/tkinterbook/pack.htm.
Note that to pass the command to handler you have to write only the name of the function (Like it's descibed in the other answer).
This is an old question, but I just wanted to add an alternate method of preventing Tkinter from running methods at start-up. You can use Python's functools.partial (doc):
import tkinter
import tkinter.filedialog
import getpass
import functools
gui = tkinter.Tk()
user = getpass.getuser()
button = tkinter.Button(
gui,
command=functools.partial(
tkinter.filedialog.askopenfilename,
initialdir='C:/Users/%s' % user)
)
button.grid()
gui.mainloop()