【问题标题】:How to update IntVar after typing in Entry widget?输入 Entry 小部件后如何更新 IntVar?
【发布时间】:2019-11-02 12:00:02
【问题描述】:

我想做一个余额输入小部件,它只接受整数,值必须是 20 - 5000。我只制作了 int 标准,但我不知道如何处理 20 - 5000 的值限制。

我尝试制作一个执行 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 类获取窗口属性的问题,我把方法 'window_properties()' 放在那里并在 TopLevel 窗口中运行它,但它没有得到它的属性,你可能有一些想法如何在不将相同代码放置在顶级风中的情况下获得它? (没有方法它也没有得到合适的大小)

另外,请告诉我现在如何在 MainApp 类中获取 '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()

【问题讨论】:

  • 为什么你认为变量没有被更新?当我添加足够的代码来运行您的代码并查看变量时,它似乎正在更新。请提供一个完整的minimal reproducible example
  • start_button_funcEdit 你的问题并展示你如何调用这个函数。
  • print(player_balance.get()) 应为print(self.player_balance.get())
  • 我们不需要整个代码。一个写得很好的问题应该包括minimal reproducible example

标签: python tkinter tkinter-entry


【解决方案1】:

你在这里做的太过分了。通过将所有内容分成不同的类,您很难找到不同类中的变量。虽然可能我会在一个类而不是多个类中构建您的顶部窗口。

也就是说,我还将构建您的主应用程序以从 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()

【讨论】:

    【解决方案2】:

    我找到了解决问题的方法。问题是我以错误的顺序创建对象。我创建了一个按钮对象(定义余额是否正常)和一个不正确的条目对象!

    正确的代码应该如下所示:

    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)
    

    【讨论】:

    • widget的创建顺序与entry如何更新变量无关。
    • 我认为这是一个原因,因为我从入口类继承了值。如果不是,为什么在更改之后它会起作用?也许给我你的想法?
    • 我明白你在做什么。为什么选择从入口类继承而不是像所有其他类一样仅从 Frame 继承?真的,我认为你这样做的方式太过分了,而且会造成不必要的工作。
    • Mike 供您参考,我仍然是初学者。我继承它是因为我需要 self.player_balance.get() 值,它是一个不同的类,因此我认为有必要进行继承。如果我错了,请纠正我。
    • 不,没有必要。您可以通过 self.master 引用该变量。也就是说,我会将你所有的第一个窗口类都写成一个类来避免这种情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-23
    • 2016-12-27
    • 2012-06-02
    • 1970-01-01
    相关资源
    最近更新 更多