I added a label onto the end of the program to make sure that the variable found was correct and was being displayed, however when I run the program, it is saying that 'loggedIn is not defined' even though I have stated the variable before the subroutine. This is not allowing the test label to be shown. Does anybody know a reason for this?
Here is my code:
loggedIn = ""
def done():
checkID = txtCID.get()
checkPassword = txtCPassword.get()
with open("employees.txt", "r") as empList:
for i in empList:
fields = i.split(", ")
if len(fields) < 2:
continue
chID = fields[0]
if chID == checkID.upper():
exPassword = fields[12]
chPassword = str(exPassword)
if chPassword == checkPassword:
global loggedIn
loggedIn = i
show_frame(entries_frame1)
else:
txtCID.delete(0,END)
txtCPassword.delete(0,END)
btnDone=Button(login_frame2,command=done,text="Login", width=15, font=("Calibri", 16, "bold"), fg="white", bg="black")
btnDone.grid(row=3, column=2)
btnCancel=Button(login_frame2,command=lambda:show_frame(login_frame),text="Cancel", width=15, font= ("Calibri", 16, "bold"), fg="white", bg="black")
btnCancel.grid(row=3, column=4)
loggedInlbl=Label(entries_frame1,text=loggedIn,font=("Calibri", 16), bg="lightgrey")
loggedInlbl.grid(row=2, column=10, padx=10, pady=10)
You have to use a StringVar object, not a regular str, to link the text of the label to changes made by done when it is called.
from tkinter import StringVar, Label
loggedIn = StringVar("")
# Since you aren't *assigning* to the name loggedIn, you don't need
# a global statement at all.
def done():
...
loggedIn.set(str(i))
...
...
# textvariable, not text, so that the Label will monitor changes
# to the StringVar object.
loggedInlbl = Label(entries_frame1, textvariable=loggedIn, ...)
Initially, the label will contain the empty string, but once you click your done button, you should see the label change (assuming loggedIn.set gets called by done).
The error about loggedIn not being defined appears to be a static analysis tool not understanding the unorthodox location of your original global statement, not anything to do with Python actually defining the variable.
Related
I am writing a files manager in python. One of the functions is to rename a file. Instead of entering the filename in the terminal with the 'input()' function, I want an input box to pop up so the user can enter the filename in there.
This is the function that renames the file.
import os
import tkinter
from tkinter import filedialog
from tkinter import messagebox
def rename_file():
messagebox.showinfo(title="Rename File", message="Select the file you want to rename!")
renameFileSource = filedialog.askopenfilename()
if renameFileSource == "":
messagebox.showinfo(title="File Select Canceled", message="You have canceled the file selection!")
else:
try:
namePath = os.path.dirname(renameFileSource)
renameFileDestination = os.path.splitext(namePath)[1]
messagebox.showinfo(title="Rename File", message="Enter the new file name!")
entry_box()
if renameFileInput == "":
messagebox.showinfo(title="No input", message="You have not entered anything!")
else:
newPath = os.path.join(namePath, renameFileInput + renameFileDestination)
os.rename(renameFileSource, newPath)
messagebox.showinfo(title="File Renamed", message="The file has succesfully been renamed!")
except OSError as renameFileError:
messagebox.showerror(title="File Error", message="The following error has occured:\n\n"+ str(renameFileError) + "\n\nPlease try again!")
This is the function that has the input box I made with tkinter. I want this function to pass the input to the variable 'renameFileInput' in the 'rename_file()' function
def entry_box():
entryBox = tkinter.Tk()
def rename_input():
renameFileInput = entryInput.get()
entryBox.destroy()
tkinter.Label(entryBox, text="Enter data here: ").pack()
entryInput = tkinter.Entry(entryBox)
entryInput.pack()
button = tkinter.Button(entryBox, text="GO!", width=3, height=1, command=rename_input)
button.pack()
entryBox.mainloop()
This is the function that has my main GUI.
def gui_window():
guiWindow = tkinter.Tk()
tkinter.Label(guiWindow, text="Lucas' Files Manager\n", fg="green", font=("", 40, "normal", "underline")).pack()
tkinter.Button(guiWindow, text="Rename File", width=25, height=1, bg="blue", fg="red", font=("", "20", "bold"), command=rename_file).pack()
tkinter.Button(guiWindow, text="Exit Program", width=25, height=1, bg="blue", fg="red", font=("", "20", "bold"), command=guiWindow.destroy).pack()
guiWindow.mainloop()
gui_window()
When I run the code as shown above, I get the following error: NameError: name 'renameFileInput' is not defined.
I have uploaded the code to my entire files manager to github: https://github.com/GierLucas/File-Manager
What is happening is that your program is trying to acces the variable renameFileInput before it creates it, so a NameError exception is raised. From the code you linked (here), you define that variable in the function rename_input (which is inside of entry_box) and try to use it in the function rename_file.
The problem is that the variable created in rename_input is destroyed when the function ends, that is when its scope ends. To get the rest of the code know that value you have a few options. The recommended is to return that value, so you can transfer it to the scope where that function was called. The other option is to make that variable a global variable. This means that variable can be accessed in every part of your code.
To globalize a variable in a scope, type global <variable> at the start of the function, in your case:
def entry_box():
entryBox = tkinter.Tk()
def callback():
global renameFileInput
renameFileInput = entryInput.get()
print(renameFileInput)
entryBox.destroy()
tkinter.Label(entryBox, text="Enter data here: ").pack()
entryInput = tkinter.Entry(entryBox)
entryInput.pack()
btn = tkinter.Button(entryBox, text="GO!", width=3, height=1, command=callback)
btn.pack()
entryBox.mainloop()
This should solve the problem you state, although your code has more errors, starting from that mainloop you have in that method. It will not let you keep the application as you would expect.
Further information
To create popup windows better use TopLevel class. See here a related question.
About scopes
About return in functions, see here
Im having difficulties with deleting content by a function when the entry was made by another function.
def recipe_add():
recepty_add_window = Toplevel()
recepty_add_window.geometry("400x400")
name_entry_label=Label(recepty_add_window, text="Name:").grid(row=1,column=0)
name_entry = Entry(recepty_add_window, width = 50, textvariable=name_var).grid(row=1, column=1,padx=5)
b_add = Button(recepty_add_window, text="Add recipe", width=50, command=add_recipe).grid(row=6, columnspan=2, pady=5, padx = 5)
The add_recipe has name_entry.delete function inside.
def add_recipe():
name_entry.delete(0,END)
I receive: NameError: name 'name_entry' is not defined.
I tried to make name_entry global or changing the Toplevel to "single window app" by forgeting everythin that is a grid and putting it there again, nothing seems to help. Thank you all for any help.
You need to declare a global variable where you define it, not where you use it. Also, .grid() returns None, so you first want to store the Entry in the variable and then call grid():
from tkinter import *
def recipe_add():
global name_entry
name_var = StringVar()
name_entry = Entry(recepty_add_window, width = 50, textvariable=name_var)
name_entry.grid(row=1, column=1,padx=5)
def add_recipe():
name_entry.delete(0,END)
I'm very new. Haven't gotten to "Classes" yet. Just trying to understand functions before I move forward. Trying to make a basic login widget that when the register button is clicked, it destroys the root window, launches the register window, then once the submit button is clicked, it gets the entry info and for right now, just prints it. I have one file that works but only has one Tk() window. Once you add another like the register function, the code no longer works. I figure it must be the order of operation that I am not understanding the concept of so I wanted some help. I have even tried to put the variables, "username_var, password_var into the register function since they are originally outside of the function but that didn't work either. Also tried calling global variables inside the register function but no luck there either. I worked on this for two days, so please don't think I didn't try all I could on my own. If you know of any documentation that I could read to better understand this, please let me know. I couldn't find anything on this topic.
from tkinter import *
# The first widget/container, not resizable
root = Tk()
root.resizable(False, False)
# variables to store the usernames and passwords
username_var = StringVar()
password_var = StringVar()
password2_var = StringVar()
''' Submit logic, if submit button is clicked or enter is pressed and username does not exist in variable list user_name and both password entries match, save username and password in variable list. '''
def submit():
print('Your username is ' + username_var.get())
if password_var == password2_var:
print('Your password is ' + password_var.get())
else:
print('Passwords do not match')
''' register button logic(if register button is clicked, destroy root window, load register window. Register window will have one username and two password labels and entries, entries have textvariables, rw = register window '''
def register_user():
root.destroy()
register_window = Tk()
rw_user_label = Label(register_window, text='Username')
rw_user_label.grid(row=0, column=0)
rw_pass_label = Label(register_window, text='Password')
rw_pass_label.grid(row=1, column=0)
rw_pass_label = Label(register_window, text='Password')
rw_pass_label.grid(row=2, column=0)
rw_user_entry = Entry(register_window, textvariable=username_var)
rw_user_entry.grid(row=0, column=1)
rw_pass_entry = Entry(register_window, textvariable=password_var, show='*')
rw_pass_entry.grid(row=1, column=1)
rw_pass_entry2 = Entry(register_window, textvariable=password2_var, show='*')
rw_pass_entry2.grid(row=2, column=1)
submit_button = Button(register_window, text='Submit', command=submit)
submit_button.grid(row=3, column=1, sticky='ew')
# username and password labels with grid locations
user_label = Label(root, text='Username')
user_label.grid(row=0, column=0)
pass_label = Label(root, text='Password')
pass_label.grid(row=1, column=0)
# username and password entries with grid locations
user_entry = Entry(root)
user_entry.grid(row=0, column=1)
pass_entry = Entry(root, show='*')
pass_entry.grid(row=1, column=1)
# Login and Register buttons with grid locations, both call functions
login_button = Button(root, text='Login')
login_button.grid(row=2, column=1, sticky='ew')
register_button = Button(root, text='Register', command=register_user)
register_button.grid(row=3, column=1, sticky='ew')
# creates an infinite loop for main root widget until destroy function is called or the widget is exited out of
root.mainloop()
In the register function the entry textvariables are used as initial values to be placed as default in this code. Any changes to the textvariables in the entry don't happen unless they have been declared global inside register.
def register():
global username_var, password_var, password2_var
so you are on the right track in terms of thinking about scope - a global variable has to be specified as global if it is changed inside a function scope. Otherwise it is simply referenced.
When I run this code is says "UserName is not defined even" though its defined in the function right below it. Do the functions have to be a certain order and if so is there a way to fix that.
from tkinter import *
def MasterLogin():
Name = UserName.get()
Email = RegisterEmail.get()
Password = RegisterPassword.get()
MasterLogin = Tk()
MasterLogin.title('Login')
MasterLogin.geometry('260x100')
LoginEmail = Entry(MasterLogin, width=30).grid(row=0, column=1)
LoginEmailText = Label(MasterLogin, text=Email).grid(row=0, column=0)
def MasterRegister():
MasterRegister = Tk()
MasterRegister.title('Register')
MasterRegister.geometry('260x100')
UserName = Entry(MasterRegister, width=30).grid(row=0, column=1)
UserNameText = Label(MasterRegister, text='Name ').grid(row=0, column=0)
RegisterEmail = Entry(MasterRegister, width=30).grid(row=1, column=1)
RegisterEmailText = Label(MasterRegister, text='Email ').grid(row=1, column=0)
RegisterPassword = Entry(MasterRegister, width=30).grid(row=2, column=1)
RegisterPasswordText = Label(MasterRegister, text='Password ').grid(row=2, column=0)
RegisterCont = Button(MasterRegister, text='Continue', width=25, bg='blue', fg='white',
command=MasterLogin).grid(row=3, column=1)
When looking at this code I would suggest the following. Create a class that handles windows. In this, you can easily have functions that do what you need to do, in this code it seems you are using functions as the end all be all for your code, this would be inefficient for your code and not very future proof.
Without a class you can still achieve what you want for an Entry box, the issues here with your example is that your variables are declared along a private scope making them inaccessible to the rest of the program, this can be fixed with declaring global within a certain area of your code but this can become messy and render functions almost useless for private functionality (this can lead to many errors)
Heres my simple example for an entry box with a button that gets the data in it
from tkinter import *
def ButtonPress(entry):
entry = entry
print(entry.get())
return entry.get()
F = Tk()
F.geometry("300x100")
F.config(bg="black")
myEntry = Entry()
myEntry.pack()
myButton = Button(text="Enter",command=lambda : ButtonPress(myEntry))
myButton.pack()
F.mainloop()
I keep getting this issue in my code when I try to use the retrieve command that I had made, I want the button named 'retrieve' to get the information in the entry box. This will then trigger the strGame command.
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1699, in __call__
return self.func(*args)
TypeError: retrieve() missing 1 required positional argument: 'entry'
My code:
from tkinter import *
verif = False
true=True
items=['pistol','knife','sword']
command=False
root=Tk()
root.iconbitmap('GameLogo.ico')
strvar=StringVar()
root.title('RPG Game')
root.geometry('900x600')
root.resizable(False,False)
frame1 = Frame(root, relief=RAISED)
frame1.pack(side=LEFT, fill=Y, padx=10, pady=10)
entry = Entry(frame1).pack(fill=X)
def retrieve(entry):
result=entry.get()
return result
retreive = Button(frame1, command=retrieve, text="Click to input").pack(fill=X)
def srtGame():
try:
if retrieve(e1)==str('drop'):
print("HELLO")
command = True
print ("Your inventory:", items)
dropItem = input("Which item do you want to drop? ")
for i in range(len(items)):
if items[i] == dropItem:
items.remove(dropItem)
print("Your", dropItem,"has been dropped. ")
print("Your inventory", items)
verif = True
if verif == False and command == True:
print("You don't have that item. Please enter another command.")
except:
IndexError
StartGame = Button(frame1, text="Start game.", relief=RAISED, command=srtGame).pack(fill=X)
GameOutput = Label(frame1, textvariable=strvar, relief=RAISED).pack(fill=X)
root.mainloop()
There are numerous errors in this program, which I will clarify here.
Useless variable assignments
The lines that goes like:
widgetVar = Widget(args).geometry(args)
Such as:
entry = Entry(frame1).pack(fill=X)
retreive = Button(frame1, command=retrieve, text="Click to input").pack(fill=X)
Is surely not doing what you intended. By chaining the construction of the widget along with geometry call, what really happens is: Widget(...) returns an instance, on which pack() or other geometry manager is called, that in turn returns None.
Hence all these variables are None, and if you need to store a reference you should break it down to two different lines of code.
Unnecessary "middlemen"
As I understand, you want your game to start (probably load another window/screen) on clicking Start game.
You can just add code to check the Entry contents when the user presses Start game directly, instead of having a button to Input that really does nothing useful as such.
Understand Tkinter vars
The point of using Tkinter vars is to avoid explicitly accessing the Widget to check data that it contains. For example, if you bind StringVar to the Entry, you no longer need to access the Entry object directly.
Mixing command-line and GUI
I would recommend using the Dialog in Tkinter, that can be used to create a pop-up with a text field, to ask the user for which item to drop.
Working Code
from Tkinter import *
items = ['pistol', 'knife', 'sword']
root = Tk()
strvar = StringVar()
root.title('RPG Game')
root.geometry('900x600')
root.resizable(False, False)
frame1 = Frame(root, relief=RAISED)
frame1.pack(side=LEFT, fill=Y, padx=10, pady=10)
entry = Entry(frame1, textvariable=strvar)
entry.pack(fill=X)
def srtGame():
if strvar.get() == str('drop'):
print("HELLO")
print("Your inventory:", items)
# TODO:
# Use Dialog window to ask for item to drop
# Let that be stored in `dropItem`
dropItem = None # Temporary
# Better than using for loop for this
if dropItem in items:
items.remove(dropItem)
print("Your", dropItem, "has been dropped. ")
print("Your inventory", items)
else:
print("You don't have that item. Please enter another command.")
startButton = Button(
frame1, text="Start game.", relief=RAISED, command=srtGame)
startButton.pack(fill=X)
GameOutput = Label(frame1, textvariable=strvar, relief=RAISED)
GameOutput.pack(fill=X)
root.mainloop()