program flow hanging inside inner function - python-3.x

Really hoping to get an explanation /fix for this issue. Using Python3 and tkinter.
I have a program window that calls a chain of functions for log in to a database. One of them calls a toplevel() log in dialog that should return an array / list of values. The toplevel dialog creates a set of entries that are read by a nested function and values stashed in a global list and then destroys toplevel window. The list is returned by the outer function to an original calling function.
Works great except if this function call originates from a higher level TK dialog. As is, I have to close out the entire program to get a return from that function. How do I fix this? Function listed here
Thanks!
# GUI constructor for log in
def login():
# Create logwin window
logwin = Toplevel()
logwin.title("Log In")
# Called by login() GUI/bsubmit - returns credentials from log in GUI
def userinfo():
global carray
sname=serventry.get()
lname=lnentry.get()
pname=passentry.get()
carray = [sname, lname, pname]
logwin.destroy()
print("userinfo() = ", carray)
# Create GUI
servlabel = Label(logwin, text="Server Address: ")
servlabel.grid(row=0, column=0)
serventry = Entry(logwin)
serventry.grid(row=0, column=1)
serventry.focus()
lnlable = Label(logwin, text="User Name: ")
lnlable.grid(row=1, column=0, padx=5)
lnentry = Entry(logwin)
lnentry.grid(row=1, column=1, padx=4)
passlabel = Label(logwin, text="Enter Password: ")
passlabel.grid(row=2, column=0)
passentry = Entry(logwin, show="*")
passentry.grid(row=2, column=1)
bsubmit = Button(logwin, text="Submit", command=userinfo, default='active')
bsubmit.grid(row=4, column=1, columnspan=2)
logwin.mainloop()
print("login() end:", carray)
return(carray)

I found the answer to this one:
I was using logwin.destroy in the inner function userinfo() and for whatever reason this did not allow the function to continue while the main Tk() construct was running. I then tried placing logwin.quit() on the inner loop and this allowed the function to progress as intended but did not destroy the logwin construct. Placing logwin.destroy() after the logwin.mainloop() then takes care of this. Data is passed back to my program and life is good.
def login():
# Create logwin window
logwin = Toplevel(root)
logwin.title("Log In")
# Called by login() GUI/bsubmit - returns credentials from log in GUI
def userinfo():
global carray
sname=serventry.get()
lname=lnentry.get()
pname=passentry.get()
carray = [sname, lname, pname]
logwin.quit()
print("userinfo() = ", carray)
return carray
# Create GUI
servlabel = Label(logwin, text="Server Address: ")
servlabel.grid(row=0, column=0)
serventry = Entry(logwin)
serventry.grid(row=0, column=1)
serventry.focus()
lnlable = Label(logwin, text="User Name: ")
lnlable.grid(row=1, column=0, padx=5)
lnentry = Entry(logwin)
lnentry.grid(row=1, column=1, padx=4)
passlabel = Label(logwin, text="Enter Password: ")
passlabel.grid(row=2, column=0)
passentry = Entry(logwin, show="*")
passentry.grid(row=2, column=1)
bsubmit = Button(logwin, text="Submit", command=userinfo, default='active')
bsubmit.grid(row=4, column=1, columnspan=2)
logwin.bind('<Return>', userinfoalt) # binds alternate credentials function to the return key.
logwin.mainloop()
logwin.destroy()
print("login() end:", carray)
return(carray)

Related

name 'main_window' is not defined

from tkinter import *
def create_main_window():
global main_window
main_window = Toplevel()
main_window.update()
entrance_window = Tk()
first_text_label = Label(entrance_window, text="you are in:").grid(row=0, column=0)
place_entry = Entry(entrance_window).grid(row=0, column=1)
submit_button = Button(entrance_window, text="Submit", command=create_main_window).grid(row=1, column=0, columnspan=2)
Label(main_window, text=f"{place_entry}").pack()
entrance_window.mainloop()
the program should open a new window with the text from the entry box from the first window but it either shows None if I write
Label(main_window, text=f"{place_entry}").pack()
in the create_main_window or it gives me an error saying that main_window is not defined if I write it after the button code.
Can someone help with this?
Try this:
from tkinter import *
def create_main_window():
global main_window
main_window = Toplevel(main_window)
label = Label(main_window, text=f"{place_entry.get()}")
label.pack()
# main_window.update() # This is useless
entrance_window = Tk()
first_text_label = Label(entrance_window, text="You are in:")
first_text_label.grid(row=0, column=0)
place_entry = Entry(entrance_window)
place_entry.grid(row=0, column=1)
submit_button = Button(entrance_window, text="Submit", command=create_main_window)
submit_button.grid(row=1, column=0, columnspan=2)
entrance_window.mainloop()
I moved the label creation inside create_main_window. Also please note that using var = a().b(), saves what ever b() returns inside var. That is why when you use var = Entry(...).pack(...), var is always None.
This is because you are trying to add a Label to an object that doesn't exist. Move the Label function to the create_main_window() function, like below:
from tkinter import *
def create_main_window():
global main_window, entrance_window
main_window = Toplevel()
place_entry = Entry(entrance_window).grid(row=0, column=1)
Label(main_window, text=f"{place_entry}").pack()
main_window.update()
entrance_window = Tk()
first_text_label = Label(entrance_window, text="you are in:").grid(row=0, column=0)
submit_button = Button(entrance_window, text="Submit", command=create_main_window).grid(row=1, column=0, columnspan=2)
entrance_window.mainloop()

Prompt opens Infinitely times in Tkinter

In my previous Question grab_set() function not working in tkinter, Flavio Moraes suggested me a code.
When I tried to implement it in my main code, I was getting an error which I have recorded here: https://youtu.be/qQyeTmbdqT0
Here is a piece of that code which produces exactly the same error:
from tkinter import *
def login():
#ALL GIFs etc.
top.resizable(0, 0)
top.title("IDLE for MySQL- Login")
top.geometry("1080x720")
submit_button = Button(top,
text="Login", fg="#FFFFFF", bd=4, bg="#000000", width=8, font=("Helvetica", 18), command=check).place(
x=410,
y=400)
quit_button = Button(top,
text="Quit", fg="#FFFFFF", bd=4, bg="#000000", width=8, font=("Helvetica", 18), command=exit).place(
x=550,
y=400)
def check():
global nlabel, top
#user = _id.get() [_id = Entry]
#passw = _p.get() [_p = Entry]
global mydb, c
while True:
try:
"""
mydb = mysql.connector.connect(
host="localhost",
user=user,
password=passw
)
c = mydb.cursor(buffered=True)"""
raise TypeError #Any error just to produce a "Wrong Password" Effect
except:
inc = Toplevel()
inc.wait_visibility()
inc.grab_set_global()
inc.focus_set()
inc.geometry("300x100")
msg = Label(inc,
text="Incorrect User ID/ Password", font=("Helvetica", 12)).place(
x=10,
y=15)
button1 = Button(inc,
text="Ok", bg="#FFFFFF", bd=3, fg="#000000", font=("Helvetica", 18), command=closepop).place(
x=190,
y=55)
button2 = Button(inc,
text="Exit", bg="#FFFFFF", bd=3, fg="#000000", font=("Helvetica", 18), command=exit).place(
x=240,
y=55)
else:
break
def closepop():
global inc
inc.grab_release()
inc.destroy()
top = Tk()
login()
top.mainloop()
From what I understand, the problem is that every time the code is checking for the password regardless of any button pressed by the used or not. I even tried butting it in a loop, but that also didn't work.
The while True loop is an infinite loop and inside the loop your raising an error, which will lead to execution of the except block, and since your not properly breaking out of the loop, it keeps on happening. Its not clear what your trying to do with the code, but this is the problem. One potential solution is to use after() which will repeat the function a given amount time, in ms. Like root.after(5000,check), this will execute check() after 5000ms(5 sec).

Fix _tkinter.TclError: no events specified in binding

Got this code from somewhere online. I looked at the other SO answer but it didn't work for me. What should I fix this error
Question: How to fix "- _tkinter.TclError: no events specified in binding"
import tkinter as tk
fields = ['Email', 'Password', 'School']
def fetch(entries):
for entry in entries:
field = entry[0]
text = entry[1].get()
print('%s: "%s"' % (field, text))
def makeform(root, fields):
entries = []
for field in fields:
row = tk.Frame(root)
lab = tk.Label(row, width=15, text=field, anchor='w')
ent = tk.Entry(row)
row.pack(side=tk.TOP, fill=tk.X, padx=5, pady=5)
lab.pack(side=tk.LEFT)
ent.pack(side=tk.RIGHT, expand=tk.YES, fill=tk.X)
entries.append((field, ent))
return entries
def getCreds():
root = tk.Tk()
ents = makeform(root, fields)
root.bind('', (lambda events=ents: fetch(e)))
b1 = tk.Button(root, text='Show',
command=(lambda events=ents: fetch(e)))
b1.pack(side=tk.LEFT, padx=5, pady=5)
b2 = tk.Button(root, text='Quit', command=root.quit)
b2.pack(side=tk.LEFT, padx=5, pady=5)
root.mainloop()
getCreds()
_tkinter.TclError: no events specified in binding
This should be relatively obvious. If you look at any of the many many examples of bind() on SO or google you will find that the first argument always has something specific in it and never an empty string.
Take some time to read up on tkinter-events-and-bindings.
There are 2 problems with you root.bind() 1st any time you click anywhere on the screen it will call the function. This is likely not what you want.
The 2nd problem both with your bind and the button command is your lambda. events=ents: fetch(e) you define your list of entries as events but then pass e to the function. So you have to correct that.
Personally I would create the list in the same place you define the root as well as define the root in the global name space. This will allow us to avoid the lambda as well.
import tkinter as tk
fields = ['Email', 'Password', 'School']
def fetch(_=None):
for ndex, entry in enumerate(entries):
print('{}: {}'.format(fields[ndex], entry.get()))
root = tk.Tk()
root.config(background='gray')
entries = []
for ndex, field in enumerate(fields):
tk.Label(root, width=15, text=field, anchor='w').grid(row=ndex, column=0, sticky='ew')
entries.append(tk.Entry(root))
entries[-1].grid(row=ndex, column=1, sticky='ew')
# The problem with root.bind is that it will constantly be calling the function anywhere you click on root.
root.bind('<Button-1>', fetch)
tk.Button(root, text='Show', command=fetch).grid(row=len(fields)+1, column=1, sticky='ew')
tk.Button(root, text='Quit', command=root.quit).grid(row=len(fields)+2, column=1, sticky='ew')
root.mainloop()

Python tkinter disable "submit" button until all fields are populated

I am still relatively new at Python, but I am making a GUI app that has 2 entry fields and two filedialog buttons for the user to select the file to import and the directory to save the output of the program. I am trying to do some validation on the entry fields to make sure that the user cannot click on the submit button until the entry fields are filled in and they have selected a file to import and a directory to save the output.
I got some of the way, but I'm stuck and I'm afraid I don't know enough about classes and methods to determine why I cannot change the status of my submit_button.config?
I have read various examples of how to do validation to entry fields including using validatecommand and building a validate method within my class. I abandoned that because I could not figure out how to validate multiple fields within the submit_button command.
Here is my code as it sits right now. I am struggling with the validate method within the Application class.
import pandas as pd
import numpy as np
from tkinter import *
from tkinter import ttk
from tkinter import filedialog as fd
from tkinter import messagebox
import os
class FileLogic:
def __init__(self, path, save_location, request_id, exeuction_id):
self.path = path
self.save_location = save_location
self.request_id = request_id
self.execution_id = execution_id
def fileopen(self=None):
global fileName
global path
path = fd.askopenfilename(title = "Select File", filetypes=( ("Excel files", "*.xlsx"),("All files", "*.*") ) )
fileName = os.path.split(path)[1]
if not fileName:
messagebox.showerror("ERROR - File Not Selected", "A file was not selected to process. Please select a file by double-clicking or select file and press Open button")
else:
file_select_label = Label(root, text=("File Selected: " + fileName), width=75, bg="light blue")
file_select_label.grid(row=7, columnspan=2)
return path
def filesave(self=None):
global save_location
save_location = fd.askdirectory(title = "Select Directory")
if not save_location:
messagebox.showerror("ERROR - Directory Not Selected", "This upload process will build an output file. Please select a folder where the output file can be saved")
else:
file_select_label = Label(root, text=("Output file will be saved: " + save_location), width=75, bg="light blue")
file_select_label.grid(row=8, columnspan=2)
return save_location
def submit(self, path, save_location, request_id, execution_id):
print("FileLogic path: " + self.path)
print("FileLogic save: " + self.save_location)
print("FileLogic request: " + self.request_id)
print("FileLogic execution: " + self.execution_id)
# FileParsing.__init__(request_id)
class FileParsing:
def __init__(self, request_id):
self.request_id = request_id
# self.execution_id_entry = execution_id_entry
print("request id2: " + request_id)
class Application(Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
global submit_button
##### Define the Labels ###############
self.request_id_label = Label(root, text="Enter Rebate Request Id:", bg="light blue", bd=2, width=25).grid(row=0, column=0)
self.execution_id_label = Label(root, text="Enter Rebate Execution Id:", bg="light blue", bd=2, width=25).grid(row=1, column=0)
self.blank_label = Label(root, bg="light blue")
####### Define the Entry fields ##################
self.request_id_entry = Entry(root,bg="light gray", bd=2, width=25, textvariable=request_id_entry).grid(row=0, column=1)
self.execution_id_entry = Entry(root, bg="light gray", bd=2, width=25, textvariable=execution_id_entry).grid(row=1, column=1)
###### Define the Buttons ###############
self.submit_button = Button(root, text="Submit", bg="gray", width=17, command= lambda: self.submit_click(path, save_location, request_id, execution_id))
self.submit_button.config(state='disabled')
self.open_file_button = Button(root, text="Select file to process", width = 30, command=FileLogic.fileopen).grid(row=3, column=0)
self.save_location_button = Button(root, text="Select location to save output", width=30, command=FileLogic.filesave).grid(row=4, column=0)
##### Build the Grid ##################
self.blank_label.grid(row=2, column=0)
self.blank_label.grid(row=5, columnspan=2)
self.submit_button.grid(row=6, column=1)
def validate(self, *args):
print("validate")
button_status = self.create_widgets(submit_button)
if request_id_entry.get():
print("normal")
print(button_status)
# self.submit_button.config(state='normal')
else:
print("diabled")
print(submit_button.config)
# self.submit_button.config(state='disabled')
def num_check(self,var):
var = self.var.get()
print(var)
if var.isnumeric():
return True
else:
tkinter.messagebox.showinfo("Error", "Enter Numeric Value")
def submit_click(self, path, save_location, request_id, execution_id):
self.request_id = request_id_entry.get()
self.execution_id = execution_id_entry.get()
a = FileLogic(path, save_location, request_id, execution_id)
FileLogic.submit(a, path, save_location, request_id, execution_id)
root=Tk()
root.title("Rebate Bid Data Upload")
root.geometry("500x200")
root.configure(background="light blue")
request_id_entry = StringVar()
execution_id_entry = StringVar()
request_id_entry.trace("w", Application.validate)
app = Application(root)
root.mainloop()
I am trying to get where the submit button is disabled until all the entry elements and filedialog attributes are complete. Then for the entry fields I am checking to make sure they are numeric and I will want to make sure they are integers.
You are not using textvariable correctly. Also note that you are not keeping a reference of your entry widgets by defining them and calling the grid method on the same line.
def create_widgets(self):
#global submit_button #you don't have to declare global here: submit_button is already an attribute
...
self.request_var = StringVar() #create StringVars for request
self.execution_var = StringVar() #ditto for execution
self.request_id_entry = Entry(root,bg="light gray", bd=2, width=25,textvariable=self.request_var).grid(row=0, column=1) #set the textvariable to the StringVar
self.execution_id_entry = Entry(root, bg="light gray", bd=2, width=25,textvariable=self.execution_var).grid(row=1, column=1)
self.request_var.trace("w",self.validate) #trace changes on StringVar
self.execution_var.trace("w",self.validate)
...
def validate(self, *args):
if self.request_var.get() and self.execution_var.get(): #if both StringVars has content
print("normal")
self.submit_button.config(state='normal')
else:
print("disabled")
self.submit_button.config(state='disabled')

How can i edit this code i've made so far so that when i click the button 'signup' it closes that gui and opens the next one

i am using tkinter to make a gui and have made various different buttons and now i have made all this i am unsure how to correctly make the first gui box close as the second one opens (sign_in function)
from tkinter import *
class login:
def __init__(self, master):
frame = Frame(master)
frame.grid()
self.button1 = Button(frame, text="signup", fg="green",command=self.sign_in)
self.button2 = Button(frame, text="sign in", fg="black",)
self.button3 = Button(frame, text="quit", fg="red", command=frame.master.destroy)
self.button1.grid(stick=W)
self.button2.grid(stick=W)
self.button3.grid(stick=W)
def sign_in(self):
frame = Frame()
frame.grid()
name = Label(root, text="Name: ")
password = Label(root, text="password: ")
entry1 = Entry(root)
entry2 = Entry(root)
name.grid(row=0, sticky=E)
password.grid(row=1, sticky=E)
entry1.grid(row=0, column=1)
entry2.grid(row=1, column=1)
c = Checkbutton(root, text="keep me logged in")
c.grid(columnspan=2, sticky="w")
root = Tk()
account=login(root)
root.mainloop()
Your code contains some indentation errors so I'll just go by your question.
when i click the button 'signup' it closes that gui and opens the next one
You can do so by first withdrawing your root window like this: root.withdraw() which will hide your original window. Then create a Toplevel window like this: newWindow = tk.Toplevel(root) to create a new window. You will just need to place these lines in the button command call.
Here's what you can change in the sign_in note that I changed all the masters to frame and not root:
def sign_in(self):
root.withdraw()
frame = Toplevel(root)
name = Label(frame, text="Name: ")
password = Label(frame, text="password: ")
entry1 = Entry(frame)
entry2 = Entry(frame)
name.grid(row=0, sticky=E)
password.grid(row=1, sticky=E)
entry1.grid(row=0, column=1)
entry2.grid(row=1, column=1)
c = Checkbutton(frame, text="keep me logged in")
c.grid(columnspan=2, sticky="w")

Resources