My code currently checks the username and password entered my the user and then returns to the label with the corresponding text.
As shown below:
from tkinter import *
def Login():
global AnameEL
global ApwordEL # More globals :D
global ArootA
global f1
global f2
ArootA = Tk() # This now makes a new window.
ArootA.geometry('1280x720')
ArootA.title('Admin login') # This makes the window title 'login'
f1 = Frame(width=200, height=200, background="#D3D3D3")
f2 = Frame(ArootA, width=400, height=200)
f1.pack(fill="both", expand=True, padx=0, pady=0)
f2.place(in_=f1, anchor="c", relx=.5, rely=.5)
AnameL = Label(f2, text='Username: ') # More labels
ApwordL = Label(f2, text='Password: ') # ^
AnameL.grid(row=1, sticky=W)
ApwordL.grid(row=2, sticky=W)
AnameEL = Entry(f2) # The entry input
ApwordEL = Entry(f2, show='*')
AnameEL.grid(row=1, column=1)
ApwordEL.grid(row=2, column=1)
AloginB = Button(f2, text='Login', command=CheckLogin) # This makes the login button, which will go to the CheckLogin def.
AloginB.grid(columnspan=2, sticky=W)
ArootA.mainloop()
def CheckLogin():
checkP = Label(f2, text='')
checkP.grid(row=3, column=1)
if AnameEL.get() == "test" and ApwordEL.get() == "123": # Checks to see if you entered the correct data.
checkP.config(text='sucess')
else:
checkP.config(text='fail')
Login()
I would like to add another feature where after 2 seconds new lines of code are ran depending on the login failed/success.
For example when the user enters a wrong login I would like the text "fail" to disappear after 2 seconds and if the user enters the correct password I would like a new function to be ran after 2 seconds of the "success" being displayed.
So I tried this:
(also importing time at the top of my code)
if AnameEL.get() == "test" and ApwordEL.get() == "123": # Checks to see if you entered the correct data.
checkP.config(text='sucess')
time.sleep(2)
nextpage()
else:
checkP.config(text='fail')
time.sleep(2)
checkP.config(text='')
def nextpage():
f1.destroy()
However, this wasn't successful. After the login button was pressed it waited 2 seconds and then ran nextpage() instead of displaying "success" for 2 seconds and then running nextpage() and for incorrect logins it goes straight to checkP.config(text='') after 2 seconds of the button press.
How can I resolve this?
All help is appreciated,
Thanks.
You need to update root before using time.sleep(). Additionally, since you are dealing with a GUI, you should prefer using timers over pausing execution. In this case, Tkinter's own after() function should be preferred over time.sleep(), because it simply places the event on the event queue as opposed to pausing execution.
after(delay_ms, callback=None, *args)
Registers an alarm callback that is called after a given time.
So, per your example:
if AnameEL.get() == "test" and ApwordEL.get() == "123":
checkP.config(text='sucess')
ArootA.update()
time.sleep(2)
nextpage()
else:
checkP.config(text='fail')
ArootA.update()
time.sleep(2)
nextpage()
With after():
if AnameEL.get() == "test" and ApwordEL.get() == "123":
checkP.config(text='sucess')
ArootA.after(2000, nextpage)
else:
checkP.config(text='fail')
ArootA.after(2000, lambda : checkP.config(text=''))
You may also want to take a look at alternative ways to update the values of labels to avoid having to update root while you are in the mainloop (e.g. Making python/tkinter label widget update?).
Related
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.
import Tkinter as tk
import os, time, smtplib
top = tk.Tk()
top.withdraw()
def pause(n):
time.sleep(n)
def clear():
os.system('cls')
def valueGET(val1, val2):
top.withdraw()
user = val1 + "#gmail.com"
pswd = val2
try:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(user, pswd)
except smtplib.SMTPAuthenticationError:
print "Sorry, wrong password and/or username"
pause(1)
clear()
login()
print "sucess"
def login():
top.deiconify()
L1 = tk.Label(top, text="User Name")
L1.grid(row=0, column=0)
E1 = tk.Entry(top, bd = 5)
E1.grid(row=0, column=1)
L1 = tk.Label(top, text="Password")
L1.grid(row=1, column=0)
E2 = tk.Entry(top, bd = 5, show="*")
E2.grid(row=1, column=1)
submit = tk.Button(top, text="Submit", width=15, command=lambda: valueGET(E1.get(), E2.get()))
submit.grid(row=3, column=1)
top.mainloop()
def main():
print "Do you wish to proceed?"
cont = raw_input("> ").lower()
if cont[0] == "y":
clear()
login()
print "pass"
main()
pardon me for not describing the question accurately enough but I honestly cant think of another way of putting it. When I run the code it goes all the way through to valueGET() and if the user and pass is correct it should (i think break out of the try block - which it does because i put a basic print statement to test that. But then it goes straight to the login again and stops at the submit button (i ran it through visual studio to see where it was getting stuck and it looks like its there). SO anyways, if anyone could help clarify this it would be great... much thanks in advance
There are some problems. If I understand you right, you get into the except block and from there you call login() again. However you did not clear your top window. Should the window contain the login form twice? And you call the top.mainloop() in login() which causes the mainloop to be run a second time when you call login() the second time.
Try moving top.mainloop() outside login() (maybe into the main() function and do not recreate the contents of your window.
I am new to codding in general. I have found a code for a function which restricts users of entering anything but digits in entry field but I can't assign the function to entry object.
I have tree problems.
I can't assign function to entry.
I want error print message to show in label "A".
Pressing "Enter" multiple times execute "def doit(FFF)" multiple times on top of each other, so I want to restrict pressing it more than one time.
I have been trying for the past 3 days but I keep failing.
from tkinter import *
def doit(FFF):
...
def val():
API = IN.get()
while True:
try:
API = int(input())
except ValueError:
print('Not an integer')
continue
else:
return API
break
root = Tk()
root.geometry("300x200")
IN = Entry(root)
IN.bind("<Return>", val, lambda _:doit(FFF))
IN.pack(side=LEFT, fill="both", expand=True)
A = Label(root, fg="red")
A.pack(side=LEFT)
B = Button(root, text="START", fg="black", command=lambda:doit(FFF))
B.pack(side=RIGHT)
root.mainloop()
Example
is_integer() checks only if text is integer number and returns True/False so it can be used in many places.
<KeyRelease> executes validate() after every keypress (or rather key release) and validate() uses is_integer() to check text in Entry (event.widget gives access to widget to which was bind() event) and changes text in Label.
Button executes function run_it() which uses is_integer() to check if Entry has correct integer (to make sure) or you have to check if Entry is not empty. If Entry has no correct integer then it changes text in Label and ends function.
import tkinter as tk
# --- functions ---
def run_it():
# you can check it again to make sure
text = e.get()
if not is_integer(text):
l['text'] = "You can't run it"
return
# do what you want
l['text'] = 'Running ...'
def validate(event):
#text = e.get()
text = event.widget.get()
if is_integer(text):
l['text'] = ''
else:
l['text'] = 'Not an integer'
def is_integer(text):
try:
value = int(text)
return True
except ValueError:
return False
# --- main ---
root = tk.Tk()
e = tk.Entry(root)
e.pack()
#e.bind("<Return>", validate)
e.bind("<KeyRelease>", validate)
l = tk.Label(root)
l.pack()
b = tk.Button(root, text="START", command=run_it)
b.pack()
root.mainloop()
I am new to python. I am attempting to get serial input from an Arduino board and receive it in Python 3 on a Raspberry Pi 3. In the code below, I get the code just fine from the Arduino and I can display it using Tkinter. The thing is, depending on the code I get from the Arduino, I want to display different screens. For this, I have added a Tkinter button. This button should just call the NextButton subroutine and increment the DisplayScreen value. It should then recall the ShowDisplay routine and give me the next screen. The button does display on the screen, but clicking it does nothing.
Any help would be greatly appreciated. Thanks
import serial
from tkinter import *
v=" "
DisplayScreen =1
# Make Serial Connection
ser = serial.Serial('/dev/ttyACM0', 9600)
#Subroutine to increment display value
def NextButton():
DisplayScreen = DisplayScreen +1
Print ("Got Here")
if DisplayScreen == 3:
DisplayScreen = 1
# Update Display
ShowDisplay()
#Subroutine to show display
def ShowDisplay():
# Make values available from other parts of the program.
global v
if DisplayScreen == 1:
# Get rid of existing display entities
for widget in frame.winfo_children():
widget.destroy()
#Add Label
Label(frame, text="Display Screen 1").grid(row=0, column=0)
Label(frame, text=v).grid(row=1, column=0)
# Add button to go to next screen
Button(frame, text='Next', command=NextButton).grid(row=3, column=1)
else:
# Get rid of existing display entities
for widget in frame.winfo_children():
widget.destroy()
#Add Label
Label(frame, text="Display Screen 2").grid(row=0, column=0)
Label(frame, text=v).grid(row=1, column=0)
# Add button to go to next screen
Button(frame, text='Next', command=NextButton).grid(row=3, column=1)
def update_label():
global v
# get arduino info
PinballData= ser.readline()
# convert data to text
v = str(PinballData, 'utf-8')
# show display
ShowDisplay()
root.after(10, update_label)
#calls update_label function again after 1 second. (1000 milliseconds.)
root = Tk()
frame = Frame(root)
frame.grid()
update_label()
root.mainloop()
There are several things that need fixing:
You should declare 'DisplayScreen' globally inside NextButton function.
Print statement is wrong. It must be print()
Thus, the function NextButton has to look like:
def NextButton():
global DisplayScreen
DisplayScreen = DisplayScreen +1
print("Got Here")
if DisplayScreen == 3:
DisplayScreen = 1
# Update Display
ShowDisplay()
By doing these changes, I have been able to display "Got Here" by clicking on the button. However, it is really complicated to click on it since you are updating the entire graphical interface each 10 ms.
I would strongly recommend to update only the label instead of the entire root. Even better, you can change the text of a widget at any time by associating a StringVar():
v = StringVar()
Label(master, textvariable=v).pack()
In this way, you would have a much more stable graphical interface.
I would also recommend you to make use of classes since you have several variables shared between functions. Then, you could easily use self.
I am making a python program to track various user merits and ranks. It needs to have a graphical user interface. However, when I add a while loop, it hangs! The while loop is needed to hold up the program until input is given. Here is the code:
def __init__(self):
global master, mainCanvas;
tree.write('./oldUsrData.xml')
god = self
#Create Base Window
master=Tk()
master.title("Briar Woods Falcon Robotics Merit Tracker 2.0")
master.maxsize(500,500)
#Create the Credit Label
creditLabel = Label(master, text="Developed by Falcon Robotics. Powered by Python.")
creditLabel.grid(row = 1, column= 1)
creditLabel.pack()
#Make the Main Canvas
mainCanvas = Canvas(master, width = 500, height=500, fill = None)
#Password Entry
inputPass = StringVar()
passwordEntry = Entry(master, textvariable=inputPass, show="$")
passwordEntry.grid(row=2, column=1)
#Define a few Action Functions
def startSetUp():
god.setUp()
def checkPassword(self):
if inputPass.get() == encryptionKey:
passwordEntry.destroy()
mainCanvas.create_text(250,250,text="CORRECT PASSWORD", tags="correctPassword")
continueButton = Button(master, text="Continue", command=startSetUp)
mainCanvas.create_window(270,270, window=continueButton, tags="correctPassword")
else:
exit()
passwordEntry.bind('<Key-Return>', checkPassword)
passwordEntry.pack()
mainCanvas.pack()
master.mainloop()
#define the merit ranks
global meritDict;
meritDict = { -4: 'Untouchable',
-3: 'Scum',
-2: 'Criminal',
-1: 'Mindless Grunt',
0: 'Citizen',
1: 'Vigilante',
2: 'Generic Hero',
3: 'Sharkboy/ Lavagirl',
4: 'Wonderwomen/Matter-eating lad',
5: 'Member of the Justice League',
6: 'X-men',
7: 'Avenger'}
def setUp(self):
#Verify Merit Dictionary
mainCanvas.delete("correctPassword")
mainCanvas.create_text(30,30,text="This is the Merit Ranking System. Change Program Source Code to edit",anchor="nw", tags="merit")
for x in range(-4,8,1):
mainCanvas.create_text(200,(x+4)*20+50, text= str(x) + ": " + str(meritDict[x]), anchor='w')
#create Quitter function
quitted = False
def quitter():
quitted = True
exit()
quit()
quitterButton = Button(master, text="Quit", command=quitter)
mainCanvas.create_window(50, 330, window=quitterButton, tag="quitter")
#Create User Name Entry
userEntryFinished = False;
def getUserEntry():
userVar = StringVar()
user = ""
def userEnter(self):
user = userVar.get()
mainCanvas.create_text(250, 350, text="User Inputted: " + user, tags="userEnter");
userEntryFinished=True;
userEntry = Entry(master, textvariable=userVar)
mainCanvas.create_window(250, 330, window=userEntry, tags="userEnter")
userEntry.bind('<Key-Return>', userEnter)
getUserEntry();
while not userEntryFinished:
pass
... #<--Further, irrelevant code
The code continues, but through trial and error, I determined that the while loop was the source of error. Also, I will need to take input until the quit button is pressed, so how can I go about that? Also, why do all while loops cause this strange problem?
I am using tkinter with python 2.6.
Note: Everything is already defined, just not included in this snippet of code. tree and root are global.
Clarification: Code Hangs when the "Continue" Button is pressed
Also: Is there a way to just wait for user input? That would help a lot.
Your code already has a "while loop" -- that is the loop created when you call mainloop. In GUI programming, you shouldn't be creating your own loops in code to wait for user input. Instead, you create widgets and then respond to events that occur in/on those widgets.
The specific reason your program hangs is because your while loop prevents the event loop from doing what is supposed to do, which is to respond to events. Not just user events, but requests from the system to redraw itself.
The solution is simply to remove your while not userEntryFinished loop, and instead redesign your code to respond to events. Put all the code that is after that loop into a function. Then, in getUserEntry, instead of / in addition to setting the flag, you can call this function.