【问题标题】:Python 3.4 tkinter checkbutton variable handling not working / respondingPython 3.4 tkinter checkbutton 变量处理不起作用/响应
【发布时间】:2016-02-16 03:21:55
【问题描述】:

EDIT 现在已移至Python 3.4.3 tkinter - Program freezes on declaration of IntVar or any other tkinter data type,因为这是问题的根源,现已解决。基本上,永远不要使用 tkinter 在 Python 3.x 中命名任何“大师”,它会导致无限循环:(。我的完整代码现在已被删除,因为它是我的课程作业,不希望人们对它进行刻痕:D 编辑

我对 tkinter 比较陌生,看不出哪里出错了。我尝试使用 StringVar() 和常规字符串来获取多个输入字段以在启用 Checkbutton 时禁用。这是问题所在的框架:

class CActivity(tk.Frame):

def Clear(self):
    pass # To be completed

def Today(self):
    if self.todayVar == "ON":
        self.day.configure(state="disabled")
        self.month.configure(state="disabled")
        self.year.configure(state="disabled")
    else:
        self.day.configure(state="normal")
        self.month.configure(state="normal")
        self.year.configure(state="normal")

def createWidgets(self):

    self.title = tk.Label(self)
    self.title["text"] = "Add an Activity"
    self.title["font"] = ("Times New Roman",30)
    self.title["fg"] = "purple"
    self.title.grid(row=0,column=0,sticky="W",padx=5,pady=5)

    self.todayVar = ""

    tk.Label(self,text="Activity Name:",font=("Times New Roman",15)).grid(row=1,column=0,sticky="W",padx=5,pady=5)
    name = tk.Entry(self).grid(row=1,column=1,columnspan=3,sticky="E",padx=5,pady=5)
    tk.Label(self,text="Priority:",font=("Times New Roman",15)).grid(row=2,column=0,sticky="W",padx=5,pady=5)
    priority = tk.Checkbutton(self).grid(row=2,column=1,sticky="W",padx=0,pady=5)
    tk.Label(self,text="Today?",font=("Times New Roman",15)).grid(row=3,column=0,sticky="W",padx=5,pady=5)
    today = tk.Checkbutton(self,onvalue="ON",offvalue="OFF",variable=self.todayVar,command=self.Today).grid(row=3,column=1,sticky="W",padx=0,pady=5) #problem possibly on this line
    tk.Label(self,text="Date (DD/MM/YYYY):",font=("Times New Roman",15)).grid(row=4,column=0,sticky="W",padx=5,pady=5)

    day = tk.Entry(self,width=2).grid(row=4,column=1,sticky="W",padx=2,pady=5)
    month = tk.Entry(self,width=2).grid(row=4,column=2,sticky="W",padx=2,pady=5)
    year = tk.Entry(self,width=4).grid(row=4,column=3,sticky="W",padx=2,pady=5)

    self.clear = tk.Button(self, command=self.Clear)
    self.clear["text"] = "Clear"
    self.clear["font"] = ("Times New Roman",15)
    self.clear["fg"] = "red"
    self.clear.grid(row=7,column=4,sticky="WE",padx=5,pady=5)

    self.back = tk.Button(self)
    self.back["text"] = "Back"
    self.back["font"] = ("Times New Roman",15)
    self.back["fg"] = "red"
    self.back["command"] = self.parent.Menu
    self.back.grid(row=8,column=4,sticky="WE",padx=5,pady=5)

def __init__(self, parent):

    tk.Frame.__init__(self, parent)
    self.pack()
    self.parent = parent
    self.createWidgets()

这里是使用 StringVar() 而不是标准的 Python 字符串:

class CActivity(tk.Frame):

def Clear(self):
    pass # To be completed

def Today(self):
    if self.todayVar.get() == "ON":
        self.day.configure(state="disabled")
        self.month.configure(state="disabled")
        self.year.configure(state="disabled")
    else:
        self.day.configure(state="normal")
        self.month.configure(state="normal")
        self.year.configure(state="normal")

def createWidgets(self):

    self.title = tk.Label(self)
    self.title["text"] = "Add an Activity"
    self.title["font"] = ("Times New Roman",30)
    self.title["fg"] = "purple"
    self.title.grid(row=0,column=0,sticky="W",padx=5,pady=5)

    self.todayVar = tk.StringVar()

    tk.Label(self,text="Activity Name:",font=("Times New Roman",15)).grid(row=1,column=0,sticky="W",padx=5,pady=5)
    name = tk.Entry(self).grid(row=1,column=1,columnspan=3,sticky="E",padx=5,pady=5)
    tk.Label(self,text="Priority:",font=("Times New Roman",15)).grid(row=2,column=0,sticky="W",padx=5,pady=5)
    priority = tk.Checkbutton(self).grid(row=2,column=1,sticky="W",padx=0,pady=5)
    tk.Label(self,text="Today?",font=("Times New Roman",15)).grid(row=3,column=0,sticky="W",padx=5,pady=5)
    today = tk.Checkbutton(self,onvalue="ON",offvalue="OFF",variable=self.todayVar,command=self.Today).grid(row=3,column=1,sticky="W",padx=0,pady=5)
    tk.Label(self,text="Date (DD/MM/YYYY):",font=("Times New Roman",15)).grid(row=4,column=0,sticky="W",padx=5,pady=5)

    day = tk.Entry(self,width=2).grid(row=4,column=1,sticky="W",padx=2,pady=5)
    month = tk.Entry(self,width=2).grid(row=4,column=2,sticky="W",padx=2,pady=5)
    year = tk.Entry(self,width=4).grid(row=4,column=3,sticky="W",padx=2,pady=5)

    self.clear = tk.Button(self, command=self.Clear)
    self.clear["text"] = "Clear"
    self.clear["font"] = ("Times New Roman",15)
    self.clear["fg"] = "red"
    self.clear.grid(row=7,column=4,sticky="WE",padx=5,pady=5)

    self.back = tk.Button(self)
    self.back["text"] = "Back"
    self.back["font"] = ("Times New Roman",15)
    self.back["fg"] = "red"
    self.back["command"] = self.parent.Menu
    self.back.grid(row=8,column=4,sticky="WE",padx=5,pady=5)

def __init__(self, parent):

    tk.Frame.__init__(self, parent)
    self.pack()
    self.parent = parent
    self.createWidgets()

在使用标准字符串的情况下,程序运行良好,直到您单击 Checkbutton,此时 Checkbutton 变为灰色,然后程序停止响应。在使用 StringVar() 的情况下,tk 窗口根本不会加载,因为该框架在窗口初始化期间被初始化。感谢您的帮助,如果您想要完整的代码来帮助找到问题,请告诉我。

【问题讨论】:

  • self.todayVar = tk.StringVar() 是示例中错误所在的行,因为程序在到达该行时会冻结(使用调试打印)。我认为这可能与在初始化 tk 窗口期间引用 StringVar() 时有关?
  • @PM2Ring 太好了,谢谢。

标签: python python-3.x tkinter


【解决方案1】:

这是您的代码的精简版本,可以正常运行:

#!/usr/bin/env python

''' Toggle disable / normal of Tkinter widgets

    Written by PM 2Ring & R. Murray 2015.11.15
    See http://stackoverflow.com/q/33711472/4014959
'''

#Python 3 / Python 2 Tkinter import
try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk


class CActivity(tk.Frame):
    def today_cb(self):
        if self.todayVar.get():
            state = "disabled"
        else:
            state = "normal"
        #print(state)

        self.day.configure(state=state)
        self.month.configure(state=state)
        self.year.configure(state=state)


    def create_widgets(self):
        title_text = ("Click the 'Today' Checkbutton to\n"
            "disable / enable date Entry widgets")
        title = tk.Label(self, text=title_text)
        title.grid(row=0, column=0, sticky="W", padx=5, pady=5)

        self.todayVar = tk.IntVar()

        tk.Label(self,text="Today").grid(row=1, column=1, 
            sticky="W", padx=0, pady=5)
        today = tk.Checkbutton(self, variable=self.todayVar, 
            command=self.today_cb)
        today.grid(row=2, column=1, sticky="W", padx=0, pady=5)

        #Date Entry widgets
        tk.Label(self,text="Day").grid(row=3, column=1,
            sticky="W", padx=2, pady=5)
        self.day = tk.Entry(self, width=2)
        self.day.grid(row=4, column=1, sticky="W", padx=2, pady=5)

        tk.Label(self,text="Month").grid(row=3, column=2,
            sticky="W", padx=2,pady=5)
        self.month = tk.Entry(self, width=2)
        self.month.grid(row=4, column=2, sticky="W", padx=2, pady=5)

        tk.Label(self,text="Year").grid(row=3, column=3,
            sticky="W", padx=2, pady=5)
        self.year = tk.Entry(self, width=4)
        self.year.grid(row=4, column=3, sticky="W", padx=2, pady=5)


    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.pack()
        self.parent = parent
        self.create_widgets()


if __name__ == '__main__':
    master = tk.Tk()
    master.title("Disable widgets demo")
    frame = CActivity(master)
    master.mainloop()

您的代码存在一些问题。

您的createWidgets 方法将日期Entry 小部件保存到本地 变量中,例如day,但它们需要在实例属性中,例如self.day,以便可以访问它们通过Checkbutton的回调方法。

另一个问题是您在与小部件构造函数相同的行上调用.grid 方法。该方法返回None,这样做

day = tk.Entry(self,width=2).grid(row=4,column=1)

day 设置为无。

如果您不需要保存对小部件的引用,则可以在构造函数的同一行使用.grid(或.pack),但是当您这样做时它将不起作用 需要那个参考。 :)

我稍微简化了Checkbutton 的回调方法,以减少代码重复。我在 Checkbutton 的构造函数中去掉了 onvalue="ON",offvalue="OFF",因为默认的 0 和 1 足以完成这项任务,恕我直言,(但如果你愿意,可以随意改回来 :)),因为我们是使用数字状态,我使用的是 IntVar 而不是字符串或 StringVar。

我还更改了方法的名称以符合 PEP 8 约定。


您仍然对代码中的局部变量与实例变量有些混淆。 dayself.day 不同,因此您需要修复它,例如,

day = tk.Entry(self,width=2)
self.day.grid(row=4,column=1,sticky="W",padx=2,pady=5)

需要

self.day = tk.Entry(self,width=2)
self.day.grid(row=4,column=1,sticky="W",padx=2,pady=5)

另外,请记住 Bryan Oakley 所说的关于需要引用 Tkinter StringVarIntVar 等实例的小部件 variabletextvariable 属性。

【讨论】:

  • 这正是我想要的,我现在就去在我的代码中实现它。谢谢!
  • 我仍然有同样的问题,所以我认为我的错误在其他地方,尽管我可能已经犯了一个错误,很高兴你帮我解决了这个问题。我会将我的完整代码放入问题中,如果您能看到问题所在,那就太好了。
  • @R.Murray:好的,但是尝试将您的问题浓缩为minimal reproducible example。这不仅使诊断和解决问题变得更容易,而且使您的问题对 SO 更有价值,因为它更有可能帮助未来的读者。
  • @R.Murray:我的 猜测 是您可能还有一堆其他错误,类似于 day = tk.Entry(self,width=2).grid(row=4,column=1) 导致整个代码破坏的事情,并使用 somevar你需要self.somevar。所以希望,一旦你清理了这些东西,你的代码就会更接近正常工作。
【解决方案2】:

variabletextvariable 属性需要引用 Tkinter 的实例 StringVarIntVarDoubleVarBooleanVar。不能使用普通变量。

其次,您必须使用这些变量的get 方法,以便在if 语句中比较它们之前检索这些值。

self.todayVar = StringVar()
...
today = tk.Checkbutton(..., variable=self.todayVar, ...)
...
if self.todayVar.get() == "ON":

【讨论】:

  • 很高兴已经清理了,但是如果您阅读上面的答案线程,您会发现问题现在已经有所发展!非常感谢您对此提出的任何建议。
  • 布莱恩,你能用新代码重现 OP 的问题吗?正如我上面所说,它对我有用,而且我看不到任何潜在的麻烦点。
猜你喜欢
  • 1970-01-01
  • 2018-10-20
  • 2018-10-26
  • 2021-06-29
  • 2020-12-05
  • 1970-01-01
  • 1970-01-01
  • 2020-05-04
  • 2020-11-30
相关资源
最近更新 更多