在每个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变量的所有值。任何有关如何操作的提示都会有所帮助!
答案 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 < strong>]:作为“.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选项卡中的每一个都是他们自己的对象,因此可以自己完成工作:因此,不要让程序向每个选项卡添加选项并将所有值随附到选项中,可以将该工作降级到选项卡本身,然后告诉它如何添加或选择哪些选项,并在需要时询问它的选项和值(而不是在主程序中完成所有工作)。