Transferring variables through functions - python

Another late night project. I was trying to make a simple login screen (the credentials will be used later).
Now I want to store the username and password as the variables USERNAME and PASSWORD in the "login screen".
For some reason it doesnt work. I tried so many things, like 'global', 'return' etc.
Is there a way to store the inputs in those variables without changing the code dramasticly? I will be modifying the code later on quite a bit and need to understand and explain this too numerous people.
EDIT:
in the dropdown menu there is a option called "-------". I never put it there, but it keeps popping up. Is there a reason why it always pops up? And how can I delete it?
import os
import smtplib
from tkinter import *
import tkinter.messagebox
#USERNAME
#PASSWORD
root = Tk()
root.geometry("500x300")
root.title("E-mail-Sending program EBF")
# *** FUNCTIONS ***
def setLoginCredentials():
USERNAME = entryLoginUsername.get()
PASSWORD = entryLoginPassword.get()
print(USERNAME)
print(PASSWORD)
def loginCredentials(event):
#Create another screen
loginScreen = Toplevel(root)
loginScreen.title("login-screen")
loginScreen.geometry("300x300")
#LABELS LOGIN SCREEN
labelLoginUsername = Label(loginScreen, text="E-mail:")
labelLoginUsername.grid(row=0,column=0, sticky=E)
labelLoginPassword = Label(loginScreen, text="Password:")
labelLoginPassword.grid(row=1,column=0, sticky=E)
#ENTRIES LOGIN SCREEN
entryLoginUsername = Entry(loginScreen)
entryLoginUsername.grid(row=0,column=1)
entryLoginPassword = Entry(loginScreen)
entryLoginPassword.grid(row=1,column=1)
#LOGIN BUTTON
loginButton1 = Button(loginScreen,text="Login",command=setLoginCredentials)
# loginButton1.bind("<Button-1>", setLoginCredentials)
loginButton1.grid(row=2,column=1, sticky=W)
def previewEmail():
tkinter.messagebox.showinfo('Email preview','Dear professor <NAME>\n\n\nThis email is on behalf of the Academy Committee of EBF Groningen, which is responsible for the booksale of the Economics and Business Faculty of the University of Groningen.\n\nSince you are the coordinator of the course <NAME>, we were wondering if any alterations were made regarding the compulsory literature that has not been listed on the latest version of Ocasys yet.\n\nWe would like the confirmation if the course literature on Ocasys is up to date or if any alterations are needed. This way we are able to contact the suppliers of these books and ensure that inconveniences, due to providing the wrong books, can be avoided.\n\n\nMet vriendelijke groet,\nKind Regard,\n\n<SENDER> - <FUNCTION>\nAcademy Committee\nEBF Groningen\n')
# *** LABELS HOMESCREEN ***
labelSender = Label(root, text="Sender:")
labelSender.grid(row=0,column=0, sticky=E)
labelFunction = Label(root, text="Function:")
labelFunction.grid(row=1,column=0, sticky=E)
labelEmail = Label(root, text="Email:")
labelEmail.grid(row=2,column=0, sticky=E)
labelProfessor = Label(root, text="Professor:")
labelProfessor.grid(row=3,column=0, sticky=E)
labelCourse = Label(root, text="Course:")
labelCourse.grid(row=4,column=0, sticky=E)
# *** ENTRIES MAINSCREEN***
entrySender = Entry(root)
entrySender.grid(row=0,column=2, columnspan=2)
entryFunction = Entry(root)
entryFunction.grid(row=1,column=2, columnspan=2)
entryEmail = Entry(root)
entryEmail.grid(row=2,column=2, columnspan=2)
entryProfessor = Entry(root)
entryProfessor.grid(row=3,column=2, columnspan=2)
entryCourse = Entry(root)
entryCourse.grid(row=4,column=2, columnspan=2)
# *** ENTRIES LOGINSCREEN ***
# *** BUTTONS ***
loginButton = Button(root, text="Login")
loginButton.bind("<Button-1>", loginCredentials)
loginButton.grid(row=6,column=0, sticky=E)
# *** MAIN MENU ***
menu= Menu(root)
root.config(menu=menu)
subMenu = Menu(root)
menu.add_cascade(label="Menu", menu=subMenu)
subMenu.add_command(label="Preview", command=previewEmail)
root.mainloop()

writing to a global variable inside a function works like this:
a = None
def foo():
global a
a = 42
a = 3
foo()
print(a)
Output:
42

The root of the problem is that you're using local variables everywhere but expecting them to be global.
If you want a variable to be accessible outside of the scope it was created in, you must define it as global1
def setLoginCredentials():
global USERNAME
global PASSWORD
global entryLoginUsername
global entryLoginPassword
...
def loginCredentials(event):
global entryLoginUsername
global entryLoginPassword
...
1 Strictly speaking this isn't true - global variables can be read without being declared global, but they can't be modified. However, since your intent is to use global variables, declaring them as global even when you don't need to will clarity to your code.
For more information see What are the rules for local and global variables in Python? in the official python documentation.

Related

Why isn't this variable defined?

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.

Python tkinter functions with multiply Tk()

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.

Python password validation without reading file

My son is trying to learn python and wondered if someone could help on here?
The issue we are having is that we can not get password validation to work. Nothing fancy,
1 st window - enter name and password
if correct - 2nd window pops up
if incorrect - 3 window pops up
We have the following code, however the third window also pops up.
I suspect it's something to do with def main or variable.
from tkinter import*
window1=Tk()
window1.title("First window")
username="user"
password="password"
def main():
if eone == "user" and etwo == "password":
master=tk()
master.title("second window")
else:
master1=tk()
master1.title("third window")
Label(window1, text="username").grid(row=0)
Label(window1, text="password").grid(row=1)
eone=Entry(window1)
etwo=Entry(window1, show='*')
eone.grid(row=0, column=1)
etwo.grid(row=1, column=1)
b1 = Button(window1, text="login") ,bg='00c714',fg='#ffffff',command=main())
b1.grid(row=3, column=0, sticky=E)
b2=Button(window1, command=window1.destroy, text="exit", bg='#fc0303' ,fg='#ffffff')
b2.grid(row=3, column=1, sticky=E)
mainloop()
He has spent many hours on it yesterday and would appreciate any help
Thanks
First, the code you posted gave some errors so fixing them:
replacing tk() with ~~Tk()~~ Toplevel() (see the comments)
replacing '00c714' with '#00c714'
removing parantheses here "login")
now it becomes "compile" time error-free. As for your question, 2 things need changing:
When we need to give a callback / command to a button, we give the function itself and not call it! IOW, command=main() will lead to calling main right away when program runs (without pressing to button). Instead, we should do command=main (note the lack of parantheses). Actually this is what is done here also: command=window1.destroy - we need to give the function itself and tkinter will call it with parantheses when button is pressed.
eone == "user" This compares the tkinter Entry widget directly with the string "user"! What you meant is through get method of entries: eone.get() == "user". Same goes for etwo too.
Overall, here is the code with these modifications (and some PEP-8 compliant formatting):
from tkinter import*
window1 = Tk()
window1.title("First window")
username = "user"
password = "password"
def main():
# Change here: using `get` to get what is written in entries
if eone.get() == "user" and etwo.get() == "password":
master = Toplevel()
master.title("second window")
else:
master1 = Toplevel()
master1.title("third window")
Label(window1, text="username").grid(row=0)
Label(window1, text="password").grid(row=1)
eone = Entry(window1)
etwo = Entry(window1, show='*')
eone.grid(row=0, column=1)
etwo.grid(row=1, column=1)
# Change here: using function `main` itself instead of calling it
b1 = Button(window1, text="login", bg="#00c714", fg="#ffffff", command=main)
b1.grid(row=3, column=0, sticky=E)
b2 = Button(window1, command=window1.destroy, text="exit", bg="#fc0303", fg='#ffffff')
b2.grid(row=3, column=1, sticky=E)
mainloop()

Python tkinter error:'Variable is not defined" even though it is define

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()

Tkinter in python: Unresolved reference

so im making a simple log in and sign up page and all is going well until i try to get my variables from my definition for my sign up page. Python says it is an unresolved reference that should resolve the typing matches and all but maybe somebody can help. Also i know there is a easier way to add pages by using classes but i i decided to do it this way. Thanks ahead of time. p.s the error is in the sign_up_check() code which the variables are gotten from the sign_up() definition.
import tkinter
# window set up
window = tkinter.Tk()
window.geometry("300x200")
window.title("Game")
window.configure(bg="grey26")
# username label
lbl = tkinter.Label(window, text="username", bg="gold")
enter_username = tkinter.Entry(window, bg="indian red1")
lbl.pack()
enter_username.pack()
# password setup
label_password = tkinter.Label(window, text="password", bg="gold")
enter_password = tkinter.Entry(window, bg="indian red1")
label_password.pack()
enter_password.pack()
# sign in
def log_in():
username = enter_username.get()
password = enter_password.get()
if len(username) > 0 and len(password) > 0:
print("it passed")
else:
print("Log in error: Double check username and password")
def sign_up_check():
# init vars
username = signup_username.get()
password = signup_pasword.get()
age = signup_age.get()
# check if vars greater than one
if len(username) > 0 and len(password) > 0 and age > 0:
print("this all passed")
else:
print("all else failed")
def sign_up():
# create new window
window1 = tkinter.Toplevel()
window1.geometry("300x200")
window1.title("Sign up")
window1.configure(bg="grey26")
# create buttons and labels
# username
label_sign_up = tkinter.Label(window1, text="Enter new username", bg="indian red1")
signup_username = tkinter.Entry(window1, bg="gold")
label_sign_up.pack()
signup_username.pack()
# password
label_sign_up_password = tkinter.Label(window1, text="Enter new password", bg="indian red1")
signup_password = tkinter.Entry(window1, bg="gold")
label_sign_up_password.pack()
signup_password.pack()
# age
label_enter_age = tkinter.Label(window1, text="Enter your age", bg="indian red1")
signup_age = tkinter.Entry(window1, bg="gold")
label_enter_age.pack()
signup_age.pack()
# confirm account
button_account = tkinter.Button(window1, text="Create account", bg="red", command=lambda: sign_up_check())
button_account.pack()
# log in button set up
button_login = tkinter.Button(window, text="Log in", bg="gold", command=lambda: log_in())
button_login.pack()
# sign up button
button_signup = tkinter.Button(window, text="Sign up", bg="gold", command=lambda: sign_up())
button_signup.pack()
window.mainloop()
The main reason for your behavior is that the locally defined variables from your functions are in a local scope.
That means that they are only available in your local function, not outside of it.
Like martineau pointed out, you have a typo in password in function sign_up_check.
Using global Variables:
to make this work every widget that is created inside a function needs to be created globally.
def login():
global enter_username, enter_password
# [... The rest of the code ...]
def sign_up_check():
global signup_username, signup_password, signup_age
# [... The rest of the code ...]
Is there any specific and programmatically relevant reason why you do not want to use classes?
To be honest with you, i do not see any advantage in using global variables and procedural programming in coding Graphical User Interfaces especially with python.
I highly recommend an object oriented programming style here to improve maintenance of code and reduce the amount of redundant coding (global declaration at beginning, global usage in every function, etc...).

Categories