【问题标题】:Getting checkbutton variables values on every notebook tabs Tkinter Python在每个笔记本标签 Tkinter Python 上获取检查按钮变量值
【发布时间】:2016-10-20 00:44:57
【问题描述】:

在每个 Tkinter 笔记本选项卡上,都有一个检查按钮列表,变量被保存到它们对应的 v[] 中(即 cb.append(Checkbuttons(.., variables = v[x],..))。

目前,我遇到了这个错误:

File "/home/pass/OptionsInterface.py", line 27, in __init__
self.ntbk_render(f = self.f1, ntbkLabel="Options",cb = optsCb, msg = optMsg)
File "/home/pass/OptionsInterface.py", line 59, in ntbk_render
text = msg[x][1], command = self.cb_check(v, opt)))
File "/home/pass/OptionsInterface.py", line 46, in cb_check
opt[ix]=(v[ix].get())
IndexError: list assignment index out of range

我认为错误即将到来。我不知道如何访问 checkbutton 变量的值。

def cb_check(self, v = [], cb = [], opt = []):
    for ix in range(len(cb)):
      opt[ix]=(v[ix].get())
    print opt

这里有一些sn-ps:

  def cb_check(self, v = [], cb = [], opt = []):
    for ix in range(len(cb)):
      opt[ix]=(v[ix].get())
    print opt

  def ntbk_render(self, f=None, ntbkLabel="", cb = [], msg = []):
    v = []
    opt = []
    msg = get_thug_args(word = ntbkLabel, argList = msg) #Allows to get the equivalent list (2d array) 
      #to serve as texts for their corresponding checkboxes

    for x in range(len(msg)):
      v.append(IntVar())
      off_value = 0
      on_value = 1
      cb.append(Checkbutton(f, variable = v[x], onvalue = on_value, offvalue = off_value,
        text = msg[x][1], command = self.cb_check(v, opt)))
      cb[x].grid(row=self.rowTracker + x, column=0, sticky='w')
      opt.append(off_value)
      cb[-1].deselect()

解决错误后,我想在按下底部的按钮 Ok 后获取每个选项卡的 checkbutton 变量的所有值。任何有关如何操作的提示都会有所帮助!

【问题讨论】:

  • 每个标签视图部分都需要进行会话(如果更改则保存)。如果 x 存在,则 x.get() 不起作用。检查按钮可能在其他选项卡上被禁用。创建一个事件,存储每一步的所有状态。
  • 我不知道是不是整个问题,但这绝对是问题的一部分:command = self.cb_check(v, opt)。您正在调用self.cb_check立即,并将结果提供给command 参数。请参阅stackoverflow.com/q/5767228/7432 了解更多信息。
  • @BryanOakley 是的,不久前,这些就是它显示的结果。我会回来更新这个。谢谢提醒!
  • @BryanOakley 嗨,我听从了您关于放置 lambda 的建议,但仍然出现该错误。您是否认为我访问检查按钮的值的方式有问题。我真的需要帮助,我现在被困在这里两天了。任何提示都可以

标签: python arrays list checkbox tkinter


【解决方案1】:

好的,所以这里比我预期的要多一点(……好吧,也许多一点……),但我会假设你会简单地从中拿走你需要的东西或找到价值。

简短的回答是,当您的 Checkbutton 调用 cb_check 时,它传递的参数如下:

cb_check(self = self, v = v, cb = opt, opt = [])

我认为当我们这样写出 IndexError 时,很明显为什么您会收到 IndexError:您正在使用 opt 列表的长度作为索引,以便在未提供 opt 时函数使用的空列表上使用;换句话说,如果您有 5 个选项,它将尝试访问空列表 [] 上的索引 [0…4](显然,一旦访问索引 0 失败,它就会停止)。你的函数不知道你传递给它的东西叫做 v 和 opt:它只是接受你给它的一些随机引用,并将它们按位置参数的顺序放置,然后按顺序填充关键字参数,然后然后使用您告诉它使用的任何默认值填写其余的关键字参数。

半快速旁白:

在尝试修复错误时,如果我不知道出了什么问题,我会先在它与断行中涉及的所有引用中断之前插入一个 print 语句,这通常会告诉您哪些引用不包含您认为它们具有的值。如果这看起来不错,那么我将进一步介入,检查任何查找/函数返回是否有错误。例如:

def cb_check(self, v = [], cb = [], opt = []):
    for ix in range(len(cb)):
        print(ix, opt, v)  ## First check, for sanity’s sake

        print(v[ix])       ## Second Check if I still can’t figure it out, but
                           ## this is a lookup, not an assignment, so it
                           ## shouldn’t be the problem

        print(v[ix].get()) ## Third Check, again, not an assignment

        print(opt[ix])     ## “opt[ix]={something}” is an assignment, so this is
                           ## (logically) where it’s breaking. Here we’re only
                           ## doing a lookup, so we’ll get a normal IndexError
                           ## instead (it won’t say “assignment”)

        opt[ix]=(v[ix].get()) ##point in code where IndexError was raised

简单的解决方法是将 Checkbutton 命令更改为“lambda: self.cb_check(v,cb,opt)”或更明确(这样我们就可以进行健全性检查)“lambda: self.cb_check(v = v , cb = cb, opt = opt)。” (我会进一步提到,您可以将“lambda:”更改为“lambda v = v, cb = cb, opt = opt:”,以进一步确保您将永远引用相同的列表,但这应该无关紧要,尤其是因为我将在下面建议的更改)

[剩下的内容是:第一部分——明确解释你的代码在做什么并对其进行批评;第二部分-您如何布局的另一种方法。如前所述,以上解决了你的问题,所以剩下的只是一个改进练习]

关于您的参考名称-

有一句古老的格言“代码的阅读次数比编写的次数多得多”,Zen of Python 的一部分说:“显式优于隐式。[...] 可读性很重要。”所以不要害怕输入更多内容,以便更容易看到发生了什么(在上面的解决方案中,同样的逻辑适用于显式地将变量传递给 cb_check)。 v 可以是 variscb 可以是cbuttonsix 会更好(在我看来)作为 ind 或只是简单的 index; f(在 ntkb_render 中)应该是 parentma​​ster

进口-

看起来您正在为 tkinter 执行星号 (*) 导入,或者显式导入其中的一部分。出于两个原因,我不鼓励你做这些事情中的任何一个。第一个原因与上述相同:如果只需多敲几下按键就可以更轻松地查看所有内容的来源,那么从长远来看,这是值得的。如果您需要稍后通过代码查找每个 tkinter Widget/Var/etc,那么简单地搜索“tk”比搜索“Frame”然后“Checkbutton”然后 IntVar 等等要容易得多。其次,导入偶尔会发生冲突:所以如果-例如-你这样做了

import time ## or from time import time, even
from datetime import time

生活对你来说可能有点毛茸茸。因此,“将 tkinter 作为 tk 导入”(例如)比您目前的做法更好。

cb_check-

关于这个函数,我要指出几点:

1) vcbopt都是函数正常工作所必需的;如果改为使用空列表引用,那么它将失败,除非您创建了 0 个 Checkbuttons(因为在“for 循环”中不会有任何东西可以迭代;无论如何,这似乎不应该发生) .这意味着它们最好只是作为位置参数(没有默认值)。如果你以这种方式编写它们,该函数会给你一个错误,说明你没有给它足够的信息来处理它,而不是一个半任意的“IndexError”。

2) 因为您为函数提供了它需要的所有信息,所以没有实际理由(根据提供的代码,无论如何)为什么函数需要成为某个对象的方法。

3) 每次选择 Checkbutton 时都会调用此函数,但会重新更新所有 Checkbuttons(而不仅仅是选中的那个)的记录值(在 opt 中)。

4) opt 列表在技术上是多余的:您已经拥有对所有 IntVar (v) 列表的引用,这些列表会实时更新/维护无需您做任何事情;执行 v[ix].get() 和执行 opt[ix 基本上一样容易strong>]:作为“.get()”调用的交换,当您最终需要该值时,您必须包含一个完整的额外函数并重复运行它以确保您的 opt 列表是最新的.更复杂的是,还有一个观点认为 v 也是多余的,但我们稍后会讨论。

另外说明一下:我不知道你为什么包装了 IntVar (v[ix] 的整数值。 get()) 带括号;它们似乎无关紧要,但我不知道您是否尝试以与 C/Java/etc 相同的方式转换值。

ntbk_render-

再次,请注意,该函数几乎提供了它需要执行的所有内容,因此感觉不像是一个方法,而是一个独立的函数(此时;同样,我们将在最后谈到这一点)。它的设置方式也意味着它需要所有这些信息,因此最好将它们作为上述位置参数。

cb 参考-

vopt 不同,cb 引用可以提供给函数。如果我们沿着 cb 的路径通过代码,我们会发现它的长度必须始终等于 vopt。假设,我们可能希望将 cb 传递给此方法而不是 vopt 的原因是因为我们只关心对 的引用>cb 在我们的其余代码中。但是,请注意 cb 始终是带有 append 方法的空迭代(似乎可以安全地假设它始终是空列表)。所以要么我们应该在开始对它做任何事情之前测试它以确保它是空的(因为如果它不是,它会破坏我们的代码),或者我们应该在创建它的同时创建它 v选择。不知道您的代码是如何设置的,我个人认为将它与其他两个一起初始化然后简单地在方法末尾返回它是最简单的(将“return cb”放在这个函数的结尾和“cb=[whatever].ntbk_render(f = someframe, ntbklabel = “somethug”, msg = argList)”)。回到 optv 的冗余(cb_check 中的第 4 点),因为我们将所有 Checkbuttons 保留在 cb,我们可以在需要时使用它们来访问它们的 IntVar。

消息-

你将 msg 传递给函数,然后将其用于 get_thug_args 中“argList”的值,并将其替换为结果。我认为将传递 ntbk_render 的关键字称为“argList”会更有意义,因为这就是它的用途,然后简单地让 msg 成为get_thug_args 的返回值。 (同样的思路也适用于关键字“ntbkLabel”,记录一下)

迭代- 我不确定使用索引引用 (x) 是否只是从 C 和 Java 等更严格的编程语言中养成的习惯,但迭代可能是我最喜欢的优势之一(主观的,我知道) Python 在这些类型的语言上的优势。除了使用 x,要从 msg 中取出您的选项,您可以简单地逐步检查 msg 中的每个单独选项。我们遇到无法克服的问题的唯一地方是当我们使用 self.rowTracker 时(在这个问题上,您的代码中没有更新;我们现在会解决这个问题,但和以前一样,我们稍后会处理)。我们可以做的修改是利用 Python 内置的 enumerate 函数;这将创建一个包含当前索引的元组,后跟迭代索引处的值。 此外,由于您将所有内容都保存在列表中,因此您必须继续返回列表的索引以获取参考。相反,只需创建对您正在创建的事物(数据类型/对象)的引用,然后将引用添加到列表中。

以下是迄今为止基于我上面提到的大部分内容对代码的调整

import tkinter as tk ## tk now refers to the instance of the tkinter module that we imported
def ntbk_render(self, parent, word, argList):
    cbuttons=list() ## The use of “list()” here is purely personal preference; feel free to
                    ## continue with brackets
    msg = get_thug_args(word = word, argList=argList) ## returns a 2d array [ [{some value},
                                                      ## checkbutton text,…], …]

    for x,option in enumerate(msg):
        ## Each iteration automatically does x=current index, option=msg[current_index]
        variable = tk.IntVar()
        ## off and on values for Checkbuttons are 0 and 1 respectively by default, so it’s
        ## redundant at the moment to assign them
        chbutton=tk.Checkbutton(parent, variable=variable, text=option[1])
        chbutton.variable = variable ## rather than carrying the variable references around,
                                     ## I’m just going to tack them onto the checkbutton they
                                     ## belong to
        chbutton.grid(row = self.rowTracker + x, column=0, sticky=’w’) 
        chbutton.deselect()
        cbuttons.append(chbutton)
    self.rowTracker += len(msg) ## Updating the rowTracker
    return cbuttons

def get_options(self, cbuttons):
    ## I’m going to keep this new function fairly simple for clarity’s sake
    values=[]
    for chbutton in cbuttons:
        value=chbutton.variable.get() ## It is for this purpose that we made
                                      ## chbutton.variable=variable above
        values.append(value)
    return values

是的,这部分内容有点冗长,但代码中的任何错误都将更容易发现,因为一切都是明确的。

进一步细化

我要谈的最后一件事 - 不涉及太多细节,因为我无法确定其中有多少是新信息 - 是我之前对你如何传递参考资料的抱怨。现在,我们已经通过将重要部分减少到仅检查按钮列表 (cbuttons) 消除了很多复杂性,但是仍然传递了一些我们可能不需要的引用。与其深入解释,不如考虑每个笔记本选项卡都是它们自己的对象,因此可以做自己的工作:因此,不要让您的程序向每个选项卡添加选项并将所有值传递给选项,而是可以将该工作委托给选项卡本身,然后告诉它如何或添加哪些选项,并在需要时询问它的选项和值(而不是在主程序中完成所有这些工作)。

【讨论】:

  • 感谢您对如何使我的代码更具可读性以及原因的回答和建议。积分表示赞赏:)。我不得不承认,我编写代码时不关心未来的阅读问题,编写代码只是为了完成工作。 ntbk_render 的目的是真正展示每个选项卡提供的选项,只需要使其可读并剥离我不需要的东西。感谢您的提示,也许从现在开始,我会努力成为一名优秀的程序员。 :D
  • 现在,我真的明白了。在使用“ntbk_render”并获取 cbuttons 后,我可以通过“get_options”获取变量值的更新。非常感谢您的清晰解释和帮助!
  • 我没有得到rowTracker更新中的逻辑。 len(msg)不是有固定长度吗?
  • 从提供的代码看来,self.rowTracker 会跟踪网格上的最后一行,以便稍后附加项目。因此,如果我们从行索引 0 开始并添加 Checkbuttons A,B,C 那么最后一行索引(C 的索引) 是 2。因为 Enumerate 默认从 0 开始,所以当我们添加 Checkbuttons D,E,F 他们会有索引+0,+1,+2。因此,如果我们用第一组的长度(长度 3)更新 self.rowTrackerD 将有一个行索引 3+0,E->3+1 和 F->3+2。我们添加的下一组 Checkbuttons 将从 6 开始:3 + len( [D,E,F] )
  • 再次运行代码,我注意到 ntbk_render 的最后两行的缩进在我复制时变得很糟糕:self.rowTracker 刚刚更新在我们添加完所有 Checkbuttons 之后一次——显然——返回应该只发生在我们完成迭代之后。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-15
  • 1970-01-01
  • 2013-03-12
  • 2018-10-04
  • 2023-03-18
相关资源
最近更新 更多