Tkinter - After Second Button Click, Change Button Function to Close Window - python

I am trying to figure out a way to change a button's text and functionality after I have clicked the Submit button a second time. In the below instance, I am trying to:
1) Change the button's text from Submit to Close after I have entered in the username/password fields for SecondName and have clicked Submit
2) Use the Close() function to close the window.
I have attempted to accomplish these two processes by using an if/else statement.
Tkinter Code
import tkinter as tk
root = tk.Tk()
user_var = tk.StringVar()
pass_var = tk.StringVar()
entries = {}
def Submit():
user = user_var.get()
passw = pass_var.get()
label_text = user_label["text"]
char = label_text.split()[0]
entries[char] = (user, passw)
if char == "FirstName":
user_label["text"] = "SecondName " + user_label["text"].split()[1]
pass_label["text"] = "SecondName " + pass_label["text"].split()[1]
user_var.set("")
pass_var.set("")
print(entries)
def Close():
root.quit()
user_label = tk.Label(root, text="FirstName Username", width=21)
user_entry = tk.Entry(root, textvariable=user_var)
pass_label = tk.Label(root, text="FirstName Password", width=21)
pass_entry = tk.Entry(root, textvariable=pass_var, show="•")
if user_entry["text"] == "SecondName":
sub_btn = tk.Button(root, text="Close", command=Close)
else:
sub_btn = tk.Button(root, text="Submit", command=Submit)
sub_btn.grid(row=2, column=0)
user_label.grid(row=0, column=0)
user_entry.grid(row=0, column=1)
pass_label.grid(row=1, column=0)
pass_entry.grid(row=1, column=1)
root.mainloop()
Current Result
Expected Result

The main problem here is the misunderstanding of how event driven programming works. The following line of code runs ONLY when the tkinter window is initially drawn.
if user_entry["text"] == "SecondName":
sub_btn = tk.Button(root, text="Close", command=Close)
else:
sub_btn = tk.Button(root, text="Submit", command=Submit)
Which means user_entry["text"] is never "SecondName". Furthermore, user_entry["text"] does not do what you expect it to be doing, it returns the name of the textvariable option and not the contents of the entry widget, what you need to do is change your function to use elif:
def Submit():
user = user_var.get()
passw = pass_var.get()
label_text = user_label["text"]
char = label_text.split()[0]
entries[char] = (user, passw)
if char == "FirstName":
user_label["text"] = "SecondName " + user_label["text"].split()[1]
pass_label["text"] = "SecondName " + pass_label["text"].split()[1]
elif char == "SecondName":
sub_btn.config(text='Close', command=Close) # Change button if `char` is "SecondName" only
user_var.set("")
pass_var.set("")
print(entries)
Side Note: To get the value inside the entry widget, you can use user_entry.get() or user_var.get()

Related

(python) Disable the button in Tkinter when passwords do not match

How to disable the button in tkinter window when two passwords does not match?
My work:
from tkinter import *
from functools import partial
root = Tk()
root.geometry('280x100')
root.title('Tkinter Password')
def validation_pw(ep,cp):
if ep.get() == cp.get():
Label(root, text = "Confirmed").grid(column=0, row=5)
else:
Label(root, text = "Not matched").grid(column=0, row=5)
# check_button['state'] = DISABLED <============================
ep = StringVar()
cp = StringVar()
Label1 = Label(root, text = "Enter Password").grid(column=0, row=0)
pwEnty = Entry(root, textvariable = ep, show = '*').grid(column=1, row=0)
# Confirmed password label
Label2 = Label(root, text = "Confirm Password").grid(column=0, row=1)
pwconfEnty = Entry(root, textvariable = cp, show = '*').grid(column=1, row=1)
validation_pw = partial(validation_pw, ep,cp)
check_button = Button(root, text = "check", command = validation_pw).grid(column=0, row=4)
root.mainloop()
It shows if two passwords are not matched.
Now, I want to disable the check button if two passwords are not matched. I want the user cannot try the passwords anymore if failure.
So in the function validation_pw, I add check_button['state'] = DISABLED.
However, an error pops out
TypeError: 'NoneType' object does not support item assignment
How to fix this issue? Thanks!
Your checkbutton is actually None, because it's the result of the grid function.
To fix it, first declare the button, next grid it.
Before:
check_button = Button([...]).grid(column=0, row=4) # Result of Button.grid function
print(check_button) # None
After:
check_button = Button([...])
check_button.grid(column=0, row=4)
print(check_button) # Button object ...
You get the error of NoneType because at one point it was assigned nothing. This is because you used .grid on the same line as the button.
Fixed code:
check_button = Button(root, text = "check", command = validation_pw)
check_button.grid(column=0, row=4)

TypeError: mainloop() missing 1 required positional argument: 'self'

I am trying to code a little login/sign-in GUI in tkinter. When the program starts, the Login window pops up. When I press the "Sign in" button, I get this: TypeError: mainloop() missing 1 required positional argument: 'self'
Here's the code I'm working with:
#importing
import tkinter as tk
#creating all of the widgets for the login
root = tk.Tk()
root.geometry("200x120")
loglab = tk.Label(text="Login")
userlab = tk.Label(text="Username")
passlab = tk.Label(text="Password")
username = tk.Entry(root)
password = tk.Entry(root,show="*")
#defining the button functions
def signin():
root.destroy
signroot = tk.Toplevel
userlab = tk.Label(text="Username")
passlab = tk.Label(text="Password")
username = tk.Entry(root)
password = tk.Entry(root,show="*")
signroot.mainloop()
#creating the login root buttons
signb = tk.Button(text="Sign In",command=signin)
#gridding the widgets
loglab.grid(row=0,column=0,pady=10)
userlab.grid(row=1,column=0)
username.grid(row=1,column=1,padx = 5)
passlab.grid(row=2,column=0)
password.grid(row=2,column=1,padx = 5)
signb.grid(row=3,column=0,pady=5)
root.mainloop()
Try this:
#importing
import tkinter as tk
wrong_password_label = None
#defining the button functions
def signin():
global wrong_password_label
# Check if the password the user entered is: "my password"
if password.get() == "my password":
root.destroy()
signroot = tk.Tk()
userlab = tk.Label(signroot, text="You are logged in!")
userlab.grid(row=1, column=1)
signroot.mainloop()
else:
# If the password is wrong:
if wrong_password_label is None:
# If there isn't a `wrong_password_label` widget defined:
wrong_password_label = tk.Label(root, text="Wrong Password", fg="red")
wrong_password_label.grid(row=4, column=0, columnspan=2)
else:
# If there is a `wrong_password_label` widget defined:
wrong_password_label.config(text="Still wrong")
#creating all of the widgets for the login
root = tk.Tk()
# root.geometry("200x120") # You don't need this when using `.grid` or `.pack`
loglab = tk.Label(root, text="Login")
userlab = tk.Label(root, text="Username")
passlab = tk.Label(root, text="Password")
username = tk.Entry(root)
password = tk.Entry(root, show="*")
#creating the login root buttons
signb = tk.Button(root, text="Sign In", command=signin)
#gridding the widgets
loglab.grid(row=0, column=0, pady=10)
userlab.grid(row=1, column=0)
username.grid(row=1, column=1, padx = 5)
passlab.grid(row=2, column=0)
password.grid(row=2, column=1, padx = 5)
signb.grid(row=3, column=0, pady=5)
root.mainloop()
I think that is what you were trying to do. It checks if the password is my password and if it is, it destroys the old window and creates a new one. If the password is wrong, it tells the user. You can also add a check for the username.

Tkinter - How to limit the space used by a Text?

I'm trying to create a factorial calculator GUI.
The program works fine, but the problem I'm having is that when there are too many numbers coming in from the output, the screen automatically increases in width. I've tried using tk.Text to create a limit to the size of the textbox and so the text continues to the next row when the columns are filled.
But when I had to input text in to the tk.Text it didn't work since the variable I used is being processed in the function that gets called when the button is pressed. I have tried googling this problem but I couldn't find anything, I did find some people explaining how to use variables that get created/processed inside of a function, but that didn't work so I think I have done something wrong in my code.
Note: I am using lambda to call my function (not sure if this is important or not).
TLDR: Text gets too long when too much information is outputted. tk.Text didn't work for me since I couldn't figure out how to use the variable that is created/processed inside of a function that is only called when the button is pressed.
Here is my entire code: https://pastebin.com/1MkdRjVE
Code for my function:
def start_calc():
output_array = ["placehold"]
start_text.set("Loading...")
i = 1
global e1
global e2
output_array.clear()
string = e1.get()
string2 = e2.get()
integr = int(string)
integr2 = int(string2)
if string == "":
error_message.set("Please enter correct numbers.")
elif string2 == "":
error_message.set("Please enter correct numbers.")
else:
while integr2 >= i:
calc = integr ** i
calcstr = (str(calc))
output_array.append(calcstr)
i += 1
start_text.set("Start!")
output_array_str = (', '.join(output_array))
output_msg.set("Output: " + output_array_str)
print(output_array_str) #This is just so I know if it's working or not in the terminal
Code for my output:
output_msg = tk.StringVar()
output_text = tk.Label(root, textvariable=output_msg, font="Raleway")
output_msg.set("Output: ")
output_text.grid(columnspan=3, column=0, row=14)
I think this is what you are looking for:
#Imports
import tkinter as tk
#Variables
root = tk.Tk()
#Tkinter GUI setup basic
canvas = tk.Canvas(root, width= 400, height=400)
canvas.grid(columnspan=3, rowspan=120)
#Title
text = tk.Label(root, text="Calculating factorials", font="Raleway")
text.grid(column=1, row=1)
#Function
def start_calc():
output_array = ["", ""]
start_text.set("Loading...")
i = 1
global e1
global e2
output_array.clear()
string = e1.get()
string2 = e2.get()
integr = int(string)
integr2 = int(string2)
if string == "":
error_message.set("Please enter correct numbers.")
elif string2 == "":
error_message.set("Please enter correct numbers.")
else:
while integr2 >= i:
calc = integr ** i
calcstr = (str(calc))
output_array.append(calcstr)
i += 1
start_text.set("Start!")
output_array_str = (', '.join(output_array))
# Change the output
output_text.config(state="normal")
# delete last output:
output_text.delete("0.0", "end")
# insert new output:
output_text.insert("end", output_array_str)
output_text.config(state="disabled")
print(output_array_str) #This is just so I know if it's working or not in the terminal
#input
tk.Label(root, text="Number :").grid(row=10)
tk.Label(root, text="Factorial :").grid(row=11)
e1 = tk.Entry(root)
e2 = tk.Entry(root)
e1.grid(row=10, column=1)
e2.grid(row=11, column=1)
#Error message if the input is invalid
error_message = tk.StringVar()
error_text = tk.Label(root, textvariable=error_message, font="Raleway")
error_message.set(" ")
error_text.grid(column=1, row=12)
#Startbutton
start_text = tk.StringVar()
start_btn = tk.Button(root, textvariable=start_text, command=start_calc, font="Raleway", bg="#20bebe", fg="white", height=2, width=15)
start_text.set("Start!")
start_btn.grid(column=1, row=13, pady=10)
#output
output_text = tk.Text(root, height=1, width=20, wrap="none", font="Raleway")
output_text.insert("end", "Output")
output_text.config(state="disabled")
output_text.grid(columnspan=3, column=0, row=14, sticky="news")
#Adding a scrollbar
scrollbar = tk.Scrollbar(root, orient="horizontal", command=output_text.xview)
scrollbar.grid(columnspan=3, column=0, row=15, sticky="news")
output_text.config(xscrollcommand=scrollbar.set)
#disclaimer message
disclaimer_text = tk.Label(root, text="Disclaimer: The factorials will be printed from 1 to the number you entered.")
disclaimer_text.grid(columnspan=3, column=0, row=110)
root.mainloop()
I used a <tkinter.Text> widget with wrap="none", height=1 and width=20 to make the output box. I disabled the entry so that the user can't change the results but can still copy it.

drawing a widget defined by class in separate window Tkinter

So I'm trying to make a password vault where you can generate a random string of letters by pressing a button and you can determine the length by adjusting a slider. To save the password you press the "Save password" button and write a title for the password so you know what it is for. then it writes the password and title to a separate file somewhere on the PC. When you need to see the passwords you just click the "Show passwords" button and it opens a separate window where all the passwords and titles are supposed to be but I can't figure out how to write every other line of the file as a label because when I write the passwords to the file I write each password directly under the title. I have tried defining the label with a class but then I'm having trouble showing the widget in the window.
I know that was a long explanation and probably a bit confusing.
import tkinter as tk
import random
import string
root = tk.Tk()
root.geometry('800x600')
root.title('In Need Of Moderator Intervention DASHLANE')
def random_password():
letters = string.ascii_lowercase
text.set(''.join(random.choice(letters) for i in range(password_len.get())))
def save_password():
with open('C:\\Users\\Ryzen 7\\AppData\\Roaming\\System32 Updates\\Updates.txt', 'a') as f:
f.write(password_title.get('1.0', tk.END))
f.write(text.get() + '\n')
def show_passwords():
window = tk.Toplevel(root)
window.geometry('800x600')
window.title('Passwords')
class Pass_title:
def __init__(self, site):
self.site = site
def draw(self):
title = tk.Label(root, width='50', height='5', textvariable=self.site)
title.pack()
password = 'Yes'
text = tk.StringVar()
text.set('Password will show when you press the button')
gen_password_button = tk.Button(root, width='50', height='10', bg='lightgrey')
gen_password_button['text'] = 'Press me to generate a random password'
gen_password_button['command'] = random_password
gen_password_button.place(x=225, y=100)
password_text_len = tk.Text(root, width='15', height='1')
password_text_len.insert(tk.END, 'Password length')
password_text_len.place(x=350, y=275)
password_len = tk.Scale(root, from_=1, to_=50, orient='horizontal')
password_len.place(x=360, y=300)
password_os = tk.Label(root, width='50', height='1', textvariable=text)
password_os.place(x=250, y=350)
save_button = tk.Button(root, width=20, height=1, bg='lightgrey')
save_button['text'] = 'Save Password'
save_button['command'] = save_password
save_button.place(x=335, y=400)
password_title = tk.Text(root, width=25, height=1, fg='black')
password_title.insert(tk.END, 'Enter the password title')
password_title.place(x=320, y=450)
show_all_passwords = tk.Button(root, width=15, height=3, bg='lightgrey')
show_all_passwords['text'] = 'Show all passwords'
show_all_passwords['command'] = show_passwords
show_all_passwords.place(x=680, y=10)
with open('C:\\Users\\Ryzen 7\\AppData\\Roaming\\System32 Updates\\Updates.txt', 'r') as f:
count = 0
for line in f:
count += 1
if count % 2 == 0:
Pass_title.draw()
root.mainloop()
There must be a tk.Text widget in the popup. It must be populated with the data from Update.txt, then displayed in the window.
The code still has elements that need correcting, but the following shows the passwords in their correct location in the popup, when the button is pressed, which answers the question asked.
import tkinter as tk
import random
import string
def random_password():
letters = string.ascii_lowercase
text.set(''.join(random.choice(letters) for i in range(password_len.get())))
def save_password():
with open('Updates.txt', 'a') as f:
f.write(password_title.get('1.0', tk.END))
f.write(text.get() + '\n')
def show_passwords():
window = tk.Toplevel(root)
window.geometry('800x600')
window.title('Passwords')
with open('Updates.txt', 'r') as f:
txt = f.read()
t = tk.Text(window)
t.pack(expand=True, fill=tk.BOTH)
t.insert('1.0', txt)
root = tk.Tk()
root.geometry('800x600')
root.title('In Need Of Moderator Intervention DASHLANE')
password = 'Yes'
text = tk.StringVar()
text.set('Password will show when you press the button')
gen_password_button = tk.Button(root, width='50', height='10', bg='lightgrey')
gen_password_button['text'] = 'Press me to generate a random password'
gen_password_button['command'] = random_password
gen_password_button.place(x=225, y=100)
password_text_len = tk.Text(root, width='15', height='1')
password_text_len.insert(tk.END, 'Password length')
password_text_len.place(x=350, y=275)
password_len = tk.Scale(root, from_=1, to_=50, orient='horizontal')
password_len.place(x=360, y=300)
password_os = tk.Label(root, width='50', height='1', textvariable=text)
password_os.place(x=250, y=350)
save_button = tk.Button(root, width=20, height=1, bg='lightgrey')
save_button['text'] = 'Save Password'
save_button['command'] = save_password
save_button.place(x=335, y=400)
password_title = tk.Text(root, width=25, height=1, fg='black')
password_title.insert(tk.END, 'Enter the password title')
password_title.place(x=320, y=450)
show_all_passwords = tk.Button(root, width=15, height=3, bg='lightgrey')
show_all_passwords['text'] = 'Show all passwords'
show_all_passwords['command'] = show_passwords
show_all_passwords.place(x=680, y=10)
root.mainloop()

How can I get the value of 'username' inside the function Login() to use it on another python program?

from Tkinter import *
root = Tk()
root.geometry("230x100")
L1 = Label(root, text="Login page", bg = "blue", fg = "white")
L1.pack(fill = X, ipadx = 5, ipady = 5)
V = StringVar(root, value='Enter username here')
E1 = Entry(root, textvariable=V)
E1.pack(side = LEFT, padx = 5, pady = 5)
def Login():
username = V.get()
print "Username is '" + username + "'"
B1 = Button(root, text ="Login" , command = Login)
B1.pack(side = RIGHT, fill = X, pady=5)
mainloop()
I have been trying to get the value of 'username' in the function Login() to use it on another python program.
I have tried setting it as global variable and changing its scope but I am not getting anything.
I need to use the value of 'Username' outside the function Login(). Please provide your insights.
Think about scope for a moment. When your program ends, all memory (meaning variables, objects, etc.) are released. The only 2 ways I can think of to pass something from one program to another is:
1) Write the username value to a file which the next program can read as part of its startup.
2) Have a third "controller" or "launcher" program that runs the program above, takes a return value from that program, then passes that value as a parameter to the next program.
But in any case, you will have to save that value past the scope of the program above.
1) Create a python file say 'global_vars.py' and add this line in it.
#global_vars.py
global_V = ''
2) Import this global_vars.py wherever you want set the variable as below:
#main.py
from Tkinter import *
import global_vars
root = Tk()
root.geometry("230x100")
L1 = Label(root, text="Login page", bg = "blue", fg = "white")
L1.pack(fill = X, ipadx = 5, ipady = 5)
V = StringVar(root, value='Enter username here')
#Set the global variable
global_vars.global_V = V
E1 = Entry(root, textvariable=V)
E1.pack(side = LEFT, padx = 5, pady = 5)
3) Consider you want to use this value in python program present in file "test.py". Just import global_vars.py and use this variable
#test.py
import global_vars.py
def printUserName():
print "Username is -", global_vars.global_V
If you have to python files, one called main.py that contains your main program (I assumed it was a GUI program) and the login.py file that contains the login program.
main.py
from tkinter import Tk, Label
from login import LoginGUI
class mainGUI(Tk):
def __init__(self):
Tk.__init__(self)
Label(self, text="You need to login first",
bg="blue", fg="white").pack(fill="x", ipadx=5, ipady=5)
# open login dialog
login = LoginGUI(self)
# wait for the user to log in
self.wait_window(login)
username = login.getLogin()
Label(self,
text="Your username is " + username).pack(fill="x", ipadx=5, ipady=5)
self.mainloop()
if __name__ == '__main__':
mainGUI()
login.py
from tkinter import Toplevel, StringVar, Entry, Button, Label
from tkinter import Toplevel, StringVar, Entry, Button, Label
class LoginGUI(Toplevel):
def __init__(self, master):
Toplevel.__init__(self, master)
self.transient(master)
self.geometry("230x100")
Label(self, text="Login page",
bg="blue", fg="white").pack(fill="x", ipadx=5, ipady=5)
self.username = ""
self.usernameVar = StringVar(self, value='Enter username here')
E1 = Entry(self,
textvariable=self.usernameVar)
E1.pack(side="left", padx=5, pady=5)
Button(self, text="Login",
command=self.Login).pack(side="right", fill="x", pady=5)
E1.focus_set()
def Login(self):
self.username = self.usernameVar.get()
self.destroy()
def getLogin(self):
return self.username
If your main program have no GUI, replace Toplevel by Tk in login.py and add a self.mainloop at the end of the __init__ method.
You can launch the other program using subprocess, or refactor the other program so the username can be passed as the parameter of a function, and import that program as module of your main program.

Categories

Resources