问题
When I run this and enter the testuser credentials, it all works perfectly and produces this window with the green coloured text on the login status widget.
However, when I then remove the credentials and type in a random password so that I get the error message, it is not red and produces this error stack (same thing happens if i try to reenter the correct credentials)
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Program Files\Python38\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File (my path to file), line 194, in get_info
login_status_widget.config(fg="red")
File "C:\Program Files\Python38\lib\tkinter\__init__.py", line 1637, in configure
return self._configure('configure', cnf, kw)
File "C:\Program Files\Python38\lib\tkinter\__init__.py", line 1627, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!label4"
I don't know why I get this error, especially since it only happens when I change the credentials - sidenote, using a bad login produces the same result, i.e. working red text then anything after doesn't produce colour.
This is my full code, I can't provide relevant section considering I have no clue where the error is coming from although its probably the way I've implemented this in a class or something.
# Stuff to implement: other widgets in root class, actual GUI for program (ideas: restaurant manager, GUI creator)
# Import tkinter as the alias tk, import mainloop from tkinter
import tkinter as tk
from tkinter import mainloop
# Key Aliases for widget.bind("<Key>",function): "<Return>" = Enter key aka return key
class Root(): # Define Root class for GUI windows
"""
Root is the main window class for the GUI which returns an instance with a Tk window, which can then be used to run class functions to return a widget on the window.
Parameters:
grid_allowed (True/False) e.g. variable = Root(True) which would mean grid_allowed would be set to True
Functions:
add_button(text, command={function name}, grid_coord=[{row number},{column number}], name="{any string}"):
add_label(text, grid_coord=[{row},{column}], name="{any string}"):
add_entry(bind={alias of key e.g. "<Return>"}, function={function to be called when key is pressed}, show={character}, grid_coord=[{row number},{column number}], name="{any string}"):
return_lists(): Returns a list containing label_list, button_list and entry_list
delete_widget(name): If a widget with the matching name is found, destroy it
return_widget(name): If a widget with the matching name is found, return it, else return None
check_widget(name): If a widget with the matching name is found, return True, else return False
Notes:
remember to run {Root instance}.window.mainloop() at the end of the program
"""
def __init__(self,grid_allowed,title): # Initialise the class
self.title = title
self.window = tk.Tk()
self.window.title(self.title)
self.button_list = []
self.label_list = []
self.entry_list = []
self.grid_allowed = grid_allowed
def add_button(self,text,command=None,grid_coord=None,name=None): # Function to add a button onto the window of the Root class instance
self.command = command
self.text = text
self.grid_coord = grid_coord
self.name = name
try: # Tries to create a button with a command and if an error is caught, creates a button without a command
self.button = tk.Button(self.window,text=self.text,command=self.command)
except Exception:
self.button = tk.Button(self.window,text=self.text)
if self.grid_allowed: # Checks if the class attribute self.grid_allowed is True, if so uses grid manager, else uses pack manager
self.button.grid(row=self.grid_coord[0],column=self.grid_coord[1])
else:
self.button.pack()
if self.name == None: # Checks if the button has not been given a name and if it has adds a name into the list with the button object
self.button_list.append(self.button)
else:
self.button_list.append([self.name,self.button])
def add_label(self,text,grid_coord=None,name=None): # Function to add a label onto the window of the Root class instance
self.text = text
self.grid_coord = grid_coord
self.label = tk.Label(self.window,text=self.text)#
self.name = name
if self.grid_allowed: # Checks if the class attribute self.grid_allowed is True, if so uses grid manager, else uses pack manager
self.label.grid(row=self.grid_coord[0],column=self.grid_coord[1])
else:
self.label.pack()
if self.name == None: # Checks if the label has not been given a name and if it has adds a name into the list with the label object
self.label_list.append(self.label)
else:
self.label_list.append([self.name,self.label])
def add_entry(self,bind=None,function=None,grid_coord=None,show=None,name=None): # Function to add a entry onto the window of the Root class instance
self.bind = bind
self.function = function
self.grid_coord = grid_coord
self.show = show
self.name = name
if not self.show == None: # Checks if the parameter show has been passed, and if it has then force the entry to only show those characters
self.entry = tk.Entry(self.window,show=self.show)
else:
self.entry = tk.Entry(self.window)
if not self.bind == None: # Checks if the parameter bind has been passed, and if it has then bind the specified key to the specified function
self.entry.bind(self.bind,self.function)
if self.grid_allowed: # Checks if the class attribute self.grid_allowed is True, if so uses grid manager, else uses pack manager
self.entry.grid(row=self.grid_coord[0],column=self.grid_coord[1])
else:
self.entry.pack()
if self.name == None: # Checks if the entry has not been given a name and if it has adds a name into the list with the entry object
self.entry_list.append(self.entry)
else:
self.entry_list.append([self.name,self.entry])
def return_lists(self): # Function to return a list of the lists containing the widgets created on the Root class instance
return [self.label_list, self.button_list, self.entry_list]
def delete_widget(self,name): # Function to iterate over all the widgets created on the Root class instance and delete the one with the same name that is passed as a parameter if found
self.name = name
for widget_list in [self.button_list,self.entry_list,self.label_list]: # Iterates over list, of lists of types of widgets
for widget in widget_list: # Iterates over indiviual widget type list
try:
if widget[0] == self.name: # If the widget has a name and it is the same as the passed parameter, destroy the widget
widget[1].destroy()
except Exception: # If the widget is not found on the current iteration then ignore that widget
pass
def return_widget(self,name): # Function to iterate over all the widgets created on the Root class instance and if one is found with the same name that is passed as a parameter return it
self.name = name
for widget_list in [self.button_list,self.entry_list,self.label_list]: # Iterates over list, of lists of types of widgets
for widget in widget_list: # Iterates over indiviual widget type list
try:
if widget[0] == self.name: # If the widget has a name and it is the same as the passed parameter, return the widget object
return widget[1]
except Exception:
return_bln = None # If the correct widget is not found return None
return return_bln
def check_widget(self,name): # Function to iterate over all the widgets created on the Root class instance and if one is found with the same name that is passed as a parameter return True
self.name = name
for widget_list in [self.button_list,self.entry_list,self.label_list]: # Iterates over list, of lists of types of widgets
for widget in widget_list: # Iterates over indiviual widget type list
try:
if widget[0] == self.name: # If the widget has a name and it is the same as the passed parameter, returns True
return True
except Exception:
return_bln = False # If the correct widget is not found return None
return return_bln
# Actual GUI creation below
def main():
main_window = Root(True,"Login") # Create an instance of Root class and assign main_window to it
main_window.window.resizable(0,0) # Prevent user from resizing the window
main_window.window.geometry("300x90") # Setting the original size of the window to '300x90'
user_dict = {
"testuser":"test",
}
def check_login(username,password): # Function to take a username and password as parameters and return True if they are a valid user, else return False
try:
if user_dict[username] == password: # If the username exists check if the password is correct and return True if it is, False if it isn't
return True
else:
return False
except Exception:
return False # If the user doesn't exist return False
def get_info(): # Function to handle login event on button click
password = main_window.return_widget("password").get() # Get the current value of the password widget from main_window
username = main_window.return_widget("username").get() # Get the current value of the username widget from main_window
email = main_window.return_widget("email").get() # Get the current value of the email widget from main_window
login_status = check_login(username,password) # Calls function with the username and password obtained from the widgets and assigns login_status to result
if main_window.check_widget("login status"): # If the error message/success message already exist, delete the widget
main_window.delete_widget("login status")
if login_status: # If the username and password were correct, add a label widget to the window saying 'Login successful', else add a label widget to the window saying 'Your username or password is incorrect'
main_window.add_label("Login successful",grid_coord=[3,1],name="login status")
login_status_widget = main_window.return_widget("login status")
login_status_widget.config(fg="green")
else:
main_window.add_label("Your username or password is incorrect",grid_coord=[3,1],name="login status")
login_status_widget = main_window.return_widget("login status")
login_status_widget.config(fg="red")
main_window.add_label("Username",grid_coord=[0,0]) # Create the basic layout of the window
main_window.add_entry(grid_coord=[0,1],name="username")
main_window.add_label("Password",grid_coord=[1,0])
main_window.add_entry(show="*",grid_coord=[1,1],name="password")
main_window.add_label("Email",grid_coord=[2,0])
main_window.add_entry(grid_coord=[2,1],name="email")
main_window.add_button(text="Login",command=get_info,grid_coord=[3,0])
list_array = main_window.return_lists() # Get the lists of all the widgets from main_window
main_window.window.mainloop() # Start the mainloop of the GUI to show the window
if __name__ == "__main__": # If the python script is being run in cmd, call main function
main()
回答1:
When I was deleting the widget I didn't use the delete_widget function which I specifically created, but rather returned the widget then called widget.destroy().
I have now fixed this and it is working fine.
Thanks to Bryan Oakley for helping me to realise the stem of the error.
来源:https://stackoverflow.com/questions/59435804/tkinter-tclerror-invalid-command-name-label4