输入 Entry 小部件后如何更新 IntVar?
How to update IntVar after typing in Entry widget?
我想制作一个余额输入小部件,它只接受整数,并且值必须是 20 - 5000。我只制定了 int 标准,但我不知道如何处理 20 - 5000 的值限制.
我尝试制作一个 "Start game button" 来执行 if 语句,但它不会更新我在输入框中输入的值,它在开始时默认为 0,即使它的命令函数是一个按钮。
如果有人知道解决方案请给我提示!
@EDIT:下面是我的全部代码!
# Import modules
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.master = master
self.window_geom = WindowsProperties(self.master)
self.master.withdraw()
# Opening first window as a Toplevel
def new_window(self):
self.first_window = tk.Toplevel(self.master)
self.app = FirstWindow(self.first_window)
class FirstWindow(tk.Frame):
def __init__(self, master):
super().__init__(master)
# Create all root objects:
self.window_geom = WindowsProperties(self.master)
self.root_buttons = FirstWindowButtons(self.master)
self.root_entry = FirstWindowEntry(self.master)
self.root_labels = FirstWindowLabels(self.master)
class FirstWindowEntry(tk.Frame):
# Class contain every button on a first screen, with its functions.
def __init__(self, master):
super().__init__(master)
self.player_name = tk.StringVar()
self.vcmd1 = self.register(self.callback_name)
self.player_name_entry = tk.Entry(
master,
textvariable=self.player_name,
font=("Verdana", 12),
width=15,
validate="all",
validatecommand=(self.vcmd1, "%P"),
)
self.player_name_entry.place(x=700, y=230)
self.player_name_entry.focus()
self.player_balance = tk.IntVar()
self.vcmd2 = self.register(self.callback_balance)
self.player_balance_entry = tk.Entry(
master,
textvariable=self.player_balance,
font=("Verdana", 12),
width=15,
validate="all",
validatecommand=(self.vcmd2, "%P"),
)
self.player_balance_entry.place(x=700, y=270)
# Method which validate if entered str is digit in Balance Entry
def callback_balance(self, P):
if len(P) <= 5 and (str.isdigit(P) or P == ""):
return True
else:
return False
# Method which validate if entered str is not longer than 15 characters in Player name Entry
def callback_name(self, P):
if len(P) <= 15:
return True
else:
return False
class FirstWindowButtons(FirstWindowEntry):
# Class contain every button on a first screen, with its functions.
def __init__(self, master):
super().__init__(master)
self.start_button = tk.Button(master, command=self.start_button_func)
self.start_button_img = tk.PhotoImage(
file="C:/Users/rjg5by/Scripts/venv/blackjack/start_button_img.png"
)
self.start_button.config(
image=self.start_button_img,
borderwidth=-10,
bg="black",
activebackground="black",
)
self.start_button.place(x=630, y=500)
self.quit_button = tk.Button(master, command=self.quit_button_func)
self.quit_button_img = tk.PhotoImage(
file="C:/Users/rjg5by/Scripts/venv/blackjack/quit_button_img.png"
)
self.quit_button.config(
image=self.quit_button_img,
borderwidth=-10,
bg="black",
activebackground="black",
)
self.quit_button.place(x=800, y=500)
def start_button_func(self):
print(self.player_balance.get())
if self.player_balance.get() >= 20:
root.deiconify() # Main game play windows appears
self.master.destroy() # Exit of first window
else:
print("Not enough ")
def quit_button_func(self):
root.destroy()
class FirstWindowLabels(tk.Frame):
# Class contain every button on a root screen, with its functions.
def __init__(self, master):
super().__init__(master)
self.player_name_label = tk.Label(
master, text="Player name:", font=("Verdana", 14)
)
self.player_name_label.config(fg="white", bg="black")
self.player_name_label.place(x=540, y=230)
self.player_balance_label = tk.Label(
master, text="Balance:", font=("Verdana", 14)
)
self.player_balance_label.config(fg="white", bg="black")
self.player_balance_label.place(x=540, y=270)
class WindowsProperties(tk.Frame):
# Class define windows main properties.
def __init__(self, master):
super().__init__(master)
self.width_of_window = 980
self.height_of_window = 604
self.screen_width = self.master.winfo_screenwidth()
self.screen_height = self.master.winfo_screenheight()
self.x_coordinate = int((self.screen_width / 2) - (self.width_of_window / 2))
self.y_coordinate = int(
(self.screen_height / 2) - (self.height_of_window / 2) - 30
)
self.master.geometry(
f"{self.width_of_window}x{self.height_of_window}+{self.x_coordinate}+{self.y_coordinate}"
)
self.master.resizable(width=False, height=False) # Resizable of a window
self.master.title("Blackjack") # Title of an application
if __name__ == "__main__":
root = tk.Tk()
app = MainApplication(root)
MainApplication.new_window(root)
root.mainloop()
第二次编辑:
@Mike - SMT 非常感谢您的帮助!我按照你的指示修改了我的代码,我注意到我需要把 self.图像变量以使其工作。在所有更正代码如下所示之后,我有一个关于从 MainApplication Class 获得 window 属性的问题,我把方法 'window_properties()' 和 运行 也放在 TopLevel window 但它没有获得它的属性,你是否知道我如何在不将相同代码放在顶层风中的情况下获得它? (没有方法它也没有得到合适的大小)
此外,请告诉我现在如何在 MainApp Class 中获取 'self.player_name' 和 'self.player_balance' 值?
解决方法:
我已将此行 FirstWindow() 更改为 self.first_window = FirstWindow()
现在我可以通过 self.first_window.player_balance
获得这个值,现在它可以工作了。
编辑:我减少了不必要的代码。
import tkinter as tk
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
self.window_properties()
self.withdraw()
self.new_window()
def new_window(self):
FirstWindow()
def window_properties(self):
width_of_window = 980
height_of_window = 604
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x_coordinate = int((screen_width / 2) - (width_of_window / 2))
y_coordinate = int((screen_height / 2) - (height_of_window / 2) - 30)
self.geometry(
f"{width_of_window}x{height_of_window}+{x_coordinate}+{y_coordinate}"
)
class FirstWindow(tk.Toplevel):
def __init__(self):
super().__init__()
# Defining variables
self.player_name = tk.StringVar()
self.player_balance = tk.IntVar()
self.master.window_properties()
self.vcmd1 = self.register(self.callback_name)
player_name_entry = tk.Entry(
self,
textvariable=self.player_name,
font=("Verdana", 12),
width=15,
validate="all",
validatecommand=(self.vcmd1, "%P"),
)
player_name_entry.place(x=700, y=230)
player_name_entry.focus()
self.vcmd2 = self.register(self.callback_balance)
player_balance_entry = tk.Entry(
self,
textvariable=self.player_balance,
font=("Verdana", 12),
width=15,
validate="all",
validatecommand=(self.vcmd2, "%P"),
)
player_balance_entry.place(x=700, y=270)
# Player balance Label widget
player_balance_label = tk.Label(self, text="Balance:", font=("Verdana", 14))
player_balance_label.config(fg="white", bg="black")
player_balance_label.place(x=540, y=270)
# Information about min/max balance Label widget
self.min_max_label = tk.Label(
self,
text="The balance should be between 20 and 5000 $.",
font=("Verdana", 10),
)
self.min_max_label.config(fg="white", bg="black")
self.min_max_label.place(x=540, y=310)
start_button = tk.Button(self, command=self.start_button_func)
start_button.config(
borderwidth=-10,
bg="black",
activebackground="black",
)
start_button.place(x=630, y=500)
quit_button = tk.Button(self, command=self.quit_button_func)
quit_button.config(borderwidth=-10, bg="black", activebackground="black"
)
quit_button.place(x=800, y=500)
# Method which validate if entered str is digit in Balance Entry
def callback_balance(self, P):
if len(P) <= 5 and (str.isdigit(P) or P == ""):
return True
else:
return False
# Method which validate if entered str is not longer than 15 characters in Player name Entry
def callback_name(self, P):
if len(P) <= 15:
return True
else:
return False
# This method checks if player typed a name, than checks if balance is 20-5000. If all statement are ok, move to main play game window
def start_button_func(self):
if self.player_name.get() == "":
self.min_max_label.config(
fg="red", bg="black", text="Please type your name."
)
elif self.player_balance.get() < 20:
self.min_max_label.config(
fg="red",
bg="black",
text="Please correct your balance value. It should be at least 20 $.",
)
elif self.player_balance.get() > 5000:
self.min_max_label.config(
fg="red",
bg="black",
text="Please correct your balance value. Maximum is 5000 $.",
)
else:
self.master.deiconify() # Main game play windows appears
self.destroy() # Exit of first window
# Quiting whole game button
def quit_button_func(self):
self.master.destroy()
if __name__ == "__main__":
MainApplication().mainloop()
我找到了解决问题的方法。问题是我以错误的顺序创建对象。我制作了一个按钮对象(它定义了平衡是否正常),而不是一个入口对象,这是不正确的!
正确的代码应该是这样的:
self.window_geom = WindowsProperties(self.master)
self.root_entry = FirstWindowEntry(self.master) # <-- Create before buttons!
self.root_buttons = FirstWindowButtons(self.master)
self.root_labels = FirstWindowLabels(self.master)
你在这里做的太过分了。通过将所有内容分离到不同的 classes 中,您将更难获取不同 classes 中的变量。尽管可能,我会把你的顶级 window 合而为一 class 而不是很多。
也就是说,我还将构建您的主应用程序以继承自 Tk() 而不是 Frame()。
我已经删除了您的图像以测试以下示例的功能。
考虑以下重新编写的代码,如果您有任何问题,请告诉我:
import tkinter as tk
class MainApplication(tk.Tk):
"""Main class of Blackjack Game.
Importing game play classes and window properties
"""
def __init__(self):
super().__init__()
self.title("Blackjack")
width_of_window = 980
height_of_window = 604
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x_coordinate = int((screen_width / 2) - (width_of_window / 2))
y_coordinate = int((screen_height / 2) - (height_of_window / 2) - 30)
self.geometry(f"{width_of_window}x{height_of_window}+{x_coordinate}+{y_coordinate}")
self.resizable(width=False, height=False)
tk.Label(self, text='bg image').place(x=0, y=0)
self.withdraw()
self.new_window()
def new_window(self):
FirstWindow()
class FirstWindow(tk.Toplevel):
"""First window class.
Contains player name and balance entry, label. Start/Quit buttons
"""
def __init__(self):
super().__init__()
self.player_name = tk.StringVar()
self.player_balance = tk.IntVar()
tk.Label(self, text='bg image').place(x=0, y=0)
self.vcmd1 = self.register(self.callback_name)
player_name_entry = tk.Entry(self, textvariable=self.player_name, font=("Verdana", 12),
width=15, validate="all", validatecommand=(self.vcmd1, "%P"))
player_name_entry.place(x=700, y=230)
player_name_entry.focus()
self.vcmd2 = self.register(self.callback_balance)
tk.Entry(self, textvariable=self.player_balance, font=("Verdana", 12), width=15, validate="all",
validatecommand=(self.vcmd2, "%P")).place(x=700, y=270)
tk.Button(self, command=self.start_button_func, text='start btn').place(x=630, y=500)
tk.Button(self, command=self.quit_button_func, text='quit btn').place(x=800, y=500)
tk.Label(self, text="Player name:", font=("Verdana", 14)).place(x=540, y=230)
tk.Label(self, text="Balance:", font=("Verdana", 14)).place(x=540, y=270)
def callback_balance(self, p):
if len(p) <= 5 and (str.isdigit(p) or p == ""):
return True
else:
return False
def callback_name(self, p):
if len(p) <= 15:
return True
else:
return False
def start_button_func(self):
print(self.player_balance.get())
if self.player_balance.get() >= 20:
self.master.deiconify()
self.destroy()
else:
print("Not enough ")
def quit_button_func(self):
self.destroy()
if __name__ == "__main__":
MainApplication().mainloop()
我想制作一个余额输入小部件,它只接受整数,并且值必须是 20 - 5000。我只制定了 int 标准,但我不知道如何处理 20 - 5000 的值限制.
我尝试制作一个 "Start game button" 来执行 if 语句,但它不会更新我在输入框中输入的值,它在开始时默认为 0,即使它的命令函数是一个按钮。
如果有人知道解决方案请给我提示!
@EDIT:下面是我的全部代码!
# Import modules
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.master = master
self.window_geom = WindowsProperties(self.master)
self.master.withdraw()
# Opening first window as a Toplevel
def new_window(self):
self.first_window = tk.Toplevel(self.master)
self.app = FirstWindow(self.first_window)
class FirstWindow(tk.Frame):
def __init__(self, master):
super().__init__(master)
# Create all root objects:
self.window_geom = WindowsProperties(self.master)
self.root_buttons = FirstWindowButtons(self.master)
self.root_entry = FirstWindowEntry(self.master)
self.root_labels = FirstWindowLabels(self.master)
class FirstWindowEntry(tk.Frame):
# Class contain every button on a first screen, with its functions.
def __init__(self, master):
super().__init__(master)
self.player_name = tk.StringVar()
self.vcmd1 = self.register(self.callback_name)
self.player_name_entry = tk.Entry(
master,
textvariable=self.player_name,
font=("Verdana", 12),
width=15,
validate="all",
validatecommand=(self.vcmd1, "%P"),
)
self.player_name_entry.place(x=700, y=230)
self.player_name_entry.focus()
self.player_balance = tk.IntVar()
self.vcmd2 = self.register(self.callback_balance)
self.player_balance_entry = tk.Entry(
master,
textvariable=self.player_balance,
font=("Verdana", 12),
width=15,
validate="all",
validatecommand=(self.vcmd2, "%P"),
)
self.player_balance_entry.place(x=700, y=270)
# Method which validate if entered str is digit in Balance Entry
def callback_balance(self, P):
if len(P) <= 5 and (str.isdigit(P) or P == ""):
return True
else:
return False
# Method which validate if entered str is not longer than 15 characters in Player name Entry
def callback_name(self, P):
if len(P) <= 15:
return True
else:
return False
class FirstWindowButtons(FirstWindowEntry):
# Class contain every button on a first screen, with its functions.
def __init__(self, master):
super().__init__(master)
self.start_button = tk.Button(master, command=self.start_button_func)
self.start_button_img = tk.PhotoImage(
file="C:/Users/rjg5by/Scripts/venv/blackjack/start_button_img.png"
)
self.start_button.config(
image=self.start_button_img,
borderwidth=-10,
bg="black",
activebackground="black",
)
self.start_button.place(x=630, y=500)
self.quit_button = tk.Button(master, command=self.quit_button_func)
self.quit_button_img = tk.PhotoImage(
file="C:/Users/rjg5by/Scripts/venv/blackjack/quit_button_img.png"
)
self.quit_button.config(
image=self.quit_button_img,
borderwidth=-10,
bg="black",
activebackground="black",
)
self.quit_button.place(x=800, y=500)
def start_button_func(self):
print(self.player_balance.get())
if self.player_balance.get() >= 20:
root.deiconify() # Main game play windows appears
self.master.destroy() # Exit of first window
else:
print("Not enough ")
def quit_button_func(self):
root.destroy()
class FirstWindowLabels(tk.Frame):
# Class contain every button on a root screen, with its functions.
def __init__(self, master):
super().__init__(master)
self.player_name_label = tk.Label(
master, text="Player name:", font=("Verdana", 14)
)
self.player_name_label.config(fg="white", bg="black")
self.player_name_label.place(x=540, y=230)
self.player_balance_label = tk.Label(
master, text="Balance:", font=("Verdana", 14)
)
self.player_balance_label.config(fg="white", bg="black")
self.player_balance_label.place(x=540, y=270)
class WindowsProperties(tk.Frame):
# Class define windows main properties.
def __init__(self, master):
super().__init__(master)
self.width_of_window = 980
self.height_of_window = 604
self.screen_width = self.master.winfo_screenwidth()
self.screen_height = self.master.winfo_screenheight()
self.x_coordinate = int((self.screen_width / 2) - (self.width_of_window / 2))
self.y_coordinate = int(
(self.screen_height / 2) - (self.height_of_window / 2) - 30
)
self.master.geometry(
f"{self.width_of_window}x{self.height_of_window}+{self.x_coordinate}+{self.y_coordinate}"
)
self.master.resizable(width=False, height=False) # Resizable of a window
self.master.title("Blackjack") # Title of an application
if __name__ == "__main__":
root = tk.Tk()
app = MainApplication(root)
MainApplication.new_window(root)
root.mainloop()
第二次编辑:
@Mike - SMT 非常感谢您的帮助!我按照你的指示修改了我的代码,我注意到我需要把 self.图像变量以使其工作。在所有更正代码如下所示之后,我有一个关于从 MainApplication Class 获得 window 属性的问题,我把方法 'window_properties()' 和 运行 也放在 TopLevel window 但它没有获得它的属性,你是否知道我如何在不将相同代码放在顶层风中的情况下获得它? (没有方法它也没有得到合适的大小)
此外,请告诉我现在如何在 MainApp Class 中获取 'self.player_name' 和 'self.player_balance' 值?
解决方法: 我已将此行 FirstWindow() 更改为 self.first_window = FirstWindow()
现在我可以通过 self.first_window.player_balance
获得这个值,现在它可以工作了。
编辑:我减少了不必要的代码。
import tkinter as tk
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
self.window_properties()
self.withdraw()
self.new_window()
def new_window(self):
FirstWindow()
def window_properties(self):
width_of_window = 980
height_of_window = 604
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x_coordinate = int((screen_width / 2) - (width_of_window / 2))
y_coordinate = int((screen_height / 2) - (height_of_window / 2) - 30)
self.geometry(
f"{width_of_window}x{height_of_window}+{x_coordinate}+{y_coordinate}"
)
class FirstWindow(tk.Toplevel):
def __init__(self):
super().__init__()
# Defining variables
self.player_name = tk.StringVar()
self.player_balance = tk.IntVar()
self.master.window_properties()
self.vcmd1 = self.register(self.callback_name)
player_name_entry = tk.Entry(
self,
textvariable=self.player_name,
font=("Verdana", 12),
width=15,
validate="all",
validatecommand=(self.vcmd1, "%P"),
)
player_name_entry.place(x=700, y=230)
player_name_entry.focus()
self.vcmd2 = self.register(self.callback_balance)
player_balance_entry = tk.Entry(
self,
textvariable=self.player_balance,
font=("Verdana", 12),
width=15,
validate="all",
validatecommand=(self.vcmd2, "%P"),
)
player_balance_entry.place(x=700, y=270)
# Player balance Label widget
player_balance_label = tk.Label(self, text="Balance:", font=("Verdana", 14))
player_balance_label.config(fg="white", bg="black")
player_balance_label.place(x=540, y=270)
# Information about min/max balance Label widget
self.min_max_label = tk.Label(
self,
text="The balance should be between 20 and 5000 $.",
font=("Verdana", 10),
)
self.min_max_label.config(fg="white", bg="black")
self.min_max_label.place(x=540, y=310)
start_button = tk.Button(self, command=self.start_button_func)
start_button.config(
borderwidth=-10,
bg="black",
activebackground="black",
)
start_button.place(x=630, y=500)
quit_button = tk.Button(self, command=self.quit_button_func)
quit_button.config(borderwidth=-10, bg="black", activebackground="black"
)
quit_button.place(x=800, y=500)
# Method which validate if entered str is digit in Balance Entry
def callback_balance(self, P):
if len(P) <= 5 and (str.isdigit(P) or P == ""):
return True
else:
return False
# Method which validate if entered str is not longer than 15 characters in Player name Entry
def callback_name(self, P):
if len(P) <= 15:
return True
else:
return False
# This method checks if player typed a name, than checks if balance is 20-5000. If all statement are ok, move to main play game window
def start_button_func(self):
if self.player_name.get() == "":
self.min_max_label.config(
fg="red", bg="black", text="Please type your name."
)
elif self.player_balance.get() < 20:
self.min_max_label.config(
fg="red",
bg="black",
text="Please correct your balance value. It should be at least 20 $.",
)
elif self.player_balance.get() > 5000:
self.min_max_label.config(
fg="red",
bg="black",
text="Please correct your balance value. Maximum is 5000 $.",
)
else:
self.master.deiconify() # Main game play windows appears
self.destroy() # Exit of first window
# Quiting whole game button
def quit_button_func(self):
self.master.destroy()
if __name__ == "__main__":
MainApplication().mainloop()
我找到了解决问题的方法。问题是我以错误的顺序创建对象。我制作了一个按钮对象(它定义了平衡是否正常),而不是一个入口对象,这是不正确的!
正确的代码应该是这样的:
self.window_geom = WindowsProperties(self.master)
self.root_entry = FirstWindowEntry(self.master) # <-- Create before buttons!
self.root_buttons = FirstWindowButtons(self.master)
self.root_labels = FirstWindowLabels(self.master)
你在这里做的太过分了。通过将所有内容分离到不同的 classes 中,您将更难获取不同 classes 中的变量。尽管可能,我会把你的顶级 window 合而为一 class 而不是很多。
也就是说,我还将构建您的主应用程序以继承自 Tk() 而不是 Frame()。
我已经删除了您的图像以测试以下示例的功能。
考虑以下重新编写的代码,如果您有任何问题,请告诉我:
import tkinter as tk
class MainApplication(tk.Tk):
"""Main class of Blackjack Game.
Importing game play classes and window properties
"""
def __init__(self):
super().__init__()
self.title("Blackjack")
width_of_window = 980
height_of_window = 604
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x_coordinate = int((screen_width / 2) - (width_of_window / 2))
y_coordinate = int((screen_height / 2) - (height_of_window / 2) - 30)
self.geometry(f"{width_of_window}x{height_of_window}+{x_coordinate}+{y_coordinate}")
self.resizable(width=False, height=False)
tk.Label(self, text='bg image').place(x=0, y=0)
self.withdraw()
self.new_window()
def new_window(self):
FirstWindow()
class FirstWindow(tk.Toplevel):
"""First window class.
Contains player name and balance entry, label. Start/Quit buttons
"""
def __init__(self):
super().__init__()
self.player_name = tk.StringVar()
self.player_balance = tk.IntVar()
tk.Label(self, text='bg image').place(x=0, y=0)
self.vcmd1 = self.register(self.callback_name)
player_name_entry = tk.Entry(self, textvariable=self.player_name, font=("Verdana", 12),
width=15, validate="all", validatecommand=(self.vcmd1, "%P"))
player_name_entry.place(x=700, y=230)
player_name_entry.focus()
self.vcmd2 = self.register(self.callback_balance)
tk.Entry(self, textvariable=self.player_balance, font=("Verdana", 12), width=15, validate="all",
validatecommand=(self.vcmd2, "%P")).place(x=700, y=270)
tk.Button(self, command=self.start_button_func, text='start btn').place(x=630, y=500)
tk.Button(self, command=self.quit_button_func, text='quit btn').place(x=800, y=500)
tk.Label(self, text="Player name:", font=("Verdana", 14)).place(x=540, y=230)
tk.Label(self, text="Balance:", font=("Verdana", 14)).place(x=540, y=270)
def callback_balance(self, p):
if len(p) <= 5 and (str.isdigit(p) or p == ""):
return True
else:
return False
def callback_name(self, p):
if len(p) <= 15:
return True
else:
return False
def start_button_func(self):
print(self.player_balance.get())
if self.player_balance.get() >= 20:
self.master.deiconify()
self.destroy()
else:
print("Not enough ")
def quit_button_func(self):
self.destroy()
if __name__ == "__main__":
MainApplication().mainloop()