在每个笔记本选项卡上获取checkbutton变量值Tkinter Python

时间:2016-06-18 06:43:15

标签: python arrays list checkbox tkinter

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

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

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

以下是一些片段:

  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变量的所有值。任何有关如何操作的提示都会有所帮助!

1 个答案:

答案 0 :(得分:2)

好吧,所以这里有一些(...好吧,可能稍微多一点......)比我想要的更多,但我会假设你只是把它从你需要的东西中取出或者发现价值。

简短的回答是,当你的Checkbutton调用cb_check时,它会传递这样的参数:

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

我认为很明显为什么你在我们写出这样的时候会得到一个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 可以是 varis ; cb 可以是 cbuttons ; ix 会更好(在我看来) ind 或只是简单的索引; f (在 ntkb_render 中)应该是

进口 -

看起来你正在为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) v cb opt 都是功能正常工作所必需的;如果使用空列表引用,那么它将失败,除非您创建了0个Checkbuttons(因为在“for循环”中没有任何东西可以迭代;无论如何,这似乎不应该发生) 。这意味着他们最好只是作为位置参数(没有默认值)。如果你用这种方式编写它们,该函数会给你一个错误,指出你没有提供足够的信息来处理它而不是半任意的“IndexError”。

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

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

4) opt 列表在技术上是多余的:您已经引用了所有IntVars( v )的列表,这些列表会实时更新/维护你什么都不做;它基本上同样容易执行 v [ ix ]。get()就像执行选择 [ ix ]:作为“.get()”调用的交换,当你最终需要这个值时,你必须包含一个额外的函数并反复运行它以确保你的 opt 列表是最新的。更复杂的是,有一个论点认为 v 也是多余的,但我们稍后会讨论。

作为额外的注释:我不确定你为什么要包装你的IntVar的整数值( v [ ix ]。带括号的get());它们似乎无关紧要,但我不知道你是否试图以与C / Java /等相同的方式来抛出它。

ntbk_render -

再次注意,这个函数几乎给出了它需要执行的所有东西,因此感觉不像是一个方法而不是一个独立的函数(此时;再次,我们将在最后得到这个)。它的设置方式也意味着它需要所有这些信息,所以它们最好像上面的位置参数一样。

cb参考 -

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

MSG -

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

迭代 - 我不确定使用索引引用( x )是否只是从更严格的编程语言(如C和Java)中获取的习惯,但迭代可能是我最喜欢的优势之一(主观,我知道) )Python有这些类型的语言。您可以直接浏览 msg 中的每个选项,而不是使用 x 来选择 msg 。我们遇到难以逾越的问题的唯一地方是当我们使用 self.rowTracker 时(关于这个主题,代码中没有更新;我们现在会解决这个问题,但和以前一样,我们以后会处理这个问题。我们可以做些什么来修改它是利用Python内置的枚举函数;这将创建一个包含当前索引的元组,后跟迭代索引处的值。 此外,因为您将所有内容保存在列表中,所以必须继续返回列表的索引以获取引用。相反,只需创建对您正在创建的事物(数据类型/对象)的引用,然后将引用添加到列表中。

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

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

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

进一步细化

我要触及的最后一件事 - 没有太多细节,因为我无法确定这对你来说有多少新信息 - 是我之前关于你如何传递参考资料的抱怨。现在,我们已经通过将重要部分减少到只有Checkbuttons列表( cbuttons )来消除了很多复杂性,但是仍然有一些我们可能不需要传递的引用。而不是深入了解更多解释,考虑这些Notebook选项卡中的每一个都是他们自己的对象,因此可以自己完成工作:因此,不要让程序向每个选项卡添加选项并将所有值随附到选项中,可以将该工作降级到选项卡本身,然后告诉它如何添加或选择哪些选项,并在需要时询问它的选项和值(而不是在主程序中完成所有工作)。