如何解决 ttk.Checkbuttons 通过不同变量链接的问题?
How can I fix this issue with ttk.Checkbuttons being linked through different variables?
我已经与 Python 一起工作了大约两周。我遇到了一种情况,我一生都无法找到解决方案。我在链接所有复选框时遇到问题。
我到处搜索,我看到的常见线索是在声明 Checkbutton 和分配变量时不小心没有声明不同的变量。但是,正如您在下面发布的测试代码中看到的那样,我为每个复选框指定了唯一命名的变量。正如您将看到的,所有初始化为 FALSE 的变量都已链接,并且所有初始化为 TRUE 的变量都已链接。
当我在做的时候,有人能指出为什么这些框架如此糟糕吗?我很难理解这个网格系统。不是像使用 row/columns 来定位东西的常规 table 吗?根据我的经验,它现在是这样工作的。
import tkinter
from tkinter import *
from tkinter import ttk, Checkbutton, Tk
checkbox0 = FALSE
checkbox1 = FALSE
checkbox2 = TRUE
checkbox3 = FALSE
checkbox4 = FALSE
checkbox5 = TRUE
checkbox6 = FALSE
checkbox7 = FALSE
checkbox8 = TRUE
checkbox9 = FALSE
mainWindow = Tk()
maincontent = ttk.Frame(mainWindow, borderwidth=5, relief='sunken', width=600, height=400).grid(row=0, column=0, padx=5, pady=5, sticky=NSEW)
content0 = ttk.Frame(maincontent, borderwidth=5, relief='sunken').grid(row=0, column=0, sticky=NSEW)
checkbox_0 = ttk.Checkbutton(content0, text="Checkbox 0", variable=checkbox0).grid(row=0, column=0, padx=5, pady=5)
checkbox_1 = ttk.Checkbutton(content0, text="Checkbox 1", variable=checkbox1).grid(row=1, column=0, padx=5, pady=5)
checkbox_2 = ttk.Checkbutton(content0, text="Checkbox 2", variable=checkbox2).grid(row=2, column=0, padx=5, pady=5)
checkbox_3 = ttk.Checkbutton(content0, text="Checkbox 3", variable=checkbox3).grid(row=3, column=0, padx=5, pady=5)
checkbox_4 = ttk.Checkbutton(content0, text="Checkbox 4", variable=checkbox4).grid(row=4, column=0, padx=5, pady=5)
content1 = ttk.Frame(maincontent, borderwidth=5, relief='sunken').grid(row=0, column=1, sticky=NSEW)
checkbox_5 = ttk.Checkbutton(content1, text="Checkbox 5", variable=checkbox5).grid(row=0, column=0, padx=5, pady=5)
checkbox_6 = ttk.Checkbutton(content1, text="Checkbox 6", variable=checkbox6).grid(row=1, column=0, padx=5, pady=5)
checkbox_7 = ttk.Checkbutton(content1, text="Checkbox 7", variable=checkbox7).grid(row=2, column=0, padx=5, pady=5)
checkbox_8 = ttk.Checkbutton(content1, text="Checkbox 8", variable=checkbox8).grid(row=3, column=0, padx=5, pady=5)
checkbox_9 = ttk.Checkbutton(content1, text="Checkbox 9", variable=checkbox9).grid(row=4, column=0, padx=5, pady=5)
mainWindow.mainloop()
非常感谢有人解释这里发生了什么以及如何使它正常工作。在过去的两周里,我几乎不停地处理我的项目,这让我超负荷了。
感谢任何人可以提供的帮助。
您的代码有两个主要问题。首先,Checkbutton
期望 Tkinter variable,即 BooleanVar
、StringVar
、DoubleVar
或 IntVar
。所以你创建的所有变量都不适用于 Checkbutton
.
接下来,您重复了 maincontent=ttk.Frame(....).grid(...)
之类的操作。这个 returns None
然后它被分配给你的变量 maincontent
。要正确保存对每个小部件的引用,您需要将其分成两行:
maincontent=ttk.Frame(...)
maincontent=grid(...)
在这两个问题都已修复的情况下,以下是您的代码如何使用 for
循环来减少重复代码:
import tkinter as tk
from tkinter import ttk
mainWindow = tk.Tk()
all_var = [tk.BooleanVar() for _ in range(10)]
maincontent = ttk.Frame(mainWindow, borderwidth=5, relief='sunken', width=600, height=400)
maincontent.grid(row=0, column=0, padx=5, pady=5, sticky="nsew")
content0 = ttk.Frame(maincontent, borderwidth=5, relief='sunken')
content0.grid(row=0, column=0, sticky="nsew")
content1 = ttk.Frame(maincontent, borderwidth=5, relief='sunken')
content1.grid(row=0, column=1, sticky="nsew")
for i in range(5):
ttk.Checkbutton(content0, text=f"Checkbox {i}",variable=all_var[i],
command=lambda x=i: print(f"Checkbox {x}, {all_var[x].get()}")
).grid(row=i, column=0, padx=5, pady=5)
ttk.Checkbutton(content1, text=f"Checkbox {i+5}", variable=all_var[i+5],
command=lambda x=i+5: print(f"Checkbox {x}, {all_var[x].get()}")
).grid(row=i, column=0, padx=5, pady=5)
mainWindow.mainloop()
首先 * 表示您导入所有内容:
from tkinter import *
播种这个没用:
import tkinter
from tkinter import ttk, Checkbutton, Tk
其次,你用这个复选按钮写了很多次同样的东西。尽量让您的工作更轻松喜欢让 python 为您插入它们。
我不知道为什么,但是 tkinter 的 Checkbutton 在自动排列上有一些降压。
尝试使用 tkinter.ttk 中的 Checkbutton。
并尝试更多地使用 pack() 而不是 (grid),这样更容易排列它们。
这是我编写所有代码的方式,希望对您有用:)
# From tkinter we will use just Tk for the root and an IntVar because tkinter.ttk have not them :P
from tkinter import Tk, IntVar, Button
# From tkinter.ttik will import same checkbuttons becase thei will look more natural
# ttk will be much closer to default OS look them tkinter
# But the probblem here is you can not use bg, relief and such like this on ttk elements
# So if you want to add decorations on element try to import them from old/default tkinter :(((((
from tkinter.ttk import Frame, Checkbutton
root = Tk()
# Define two check buttons list
checkButtons_1 = []
checkButtons_2 = []
# We will set a number here what will be used to set the names on check buttons later
# Because will be used on more then one place I like to change it just one time next time
maxButtons = 10
# Try to append some names for them
# In programin the count start from 0 so we will increse the number with one to start from 1
for i in range(maxButtons):
# First of all we will add first half of the range
# Because some time we can get a float we try to avoid that by converting the division in a intiger
# But we will count with les 1 because: 10/2=5 so half is 5 but we will make i+1 so 5+1=6 is bigger the 5
if i <= int((maxButtons / 2) - 1):
checkButtons_1.append(f"Checkbox {i+1}")
# How we will add the rest of them
else:
checkButtons_2.append(f"Checkbox {i+1}")
class CheckBtn(Frame):
def __init__(self, parent=None, selected=[], side="top", expand=True, fill="x"):
Frame.__init__(self, parent)
self.vars = []
# For every itme new created
for item in selected:
# Because variabbles on checkbuttom are intiger (just 1 and 0) make an IntVar
var = IntVar()
# Make a little frame for every check box
frame = Frame(self)
frame.pack(side=side)
# Make the check bok
chk = Checkbutton(self, text=item, variable=var)
chk.pack(expand=expand, fill=fill, side=side)
# Save every variable state in a array list
self.vars.append(var)
# This stahe will be called later to see witch box are checked and witch not
def state(self):
return map((lambda var: var.get()), self.vars)
class gui:
def __init__(self, master):
self.master = master
# For the main screen we make two frame. One on top for the check buttons and one on bottom for the buttons
frameTop = Frame(master)
frameBottom = Frame(master)
# Now we make two frame for the checkbuttons. One for left and one for the right side
frameLeft = Frame(frameTop)
frameRight = Frame(frameTop)
# Here we will pack all frames. Here is important the order you pack, above you can make them on what order you want
frameTop.pack(expand=True, fill="x", side="top")
frameBottom.pack(expand=True, fill="x", side="bottom")
frameLeft.pack(expand=True, fill="x", side="left")
frameRight.pack(expand=True, fill="x", side="left")
# Here we create tne check button
# We define ch1 and ch2 because we will need to refer them to print the status later
ch1 = CheckBtn(frameLeft, checkButtons_1)
ch2 = CheckBtn(frameRight, checkButtons_2)
# Here we pack the buttons on screen
ch1.pack()
ch2.pack()
# We call lambda status here because our function will be after the button is pack
# So wil llook on all file for that function.
# With out that lambda function you need to put "seeStatus()" above the button.
# We not need to make btn = Button(frame,...) because we will not check the buttons anywhere for now
Button(frameBottom, text="Check status", command=lambda:seeStatus(),
bg="cyan", activebackground="cyan", relief="groove", bd=4
).pack(expand=True, fill="x", side="left")
# And for funn we add and this quit button :))
Button(frameBottom, text="Quit", command=root.quit,
bg="tomato", activebackground="tomato", relief="groove", bd=4
).pack(expand=True, fill="x", side="left")
def seeStatus():
print(list(ch1.state()), list(ch2.state()))
# If the this file is the main file then make this
# This basiclly check if you run the code from here or not.
# If not then will not execute this, you can import them to another file and will run from that file.
# Google it about. Is easy to undersnatnd.
if __name__ == "__main__":
gui(root)
root.mainloop()
但我不会拒绝你的问题,所以我只是给你一个完整的硬代码,我想...很抱歉。
问题是如果你制作一个新框架,你必须以两种不同的方式来做。
# Like that
Frame(root).pack()
# Or you you refer them back to this frame you need to "give him a name"
frame = Frame(root)
frame.grid()
# THIS NEVER WILL WORK SO GOOD
frame = Frame(root).grid()
所以你的主要问题是你如何写 pack/grid 需要分开在不同的两行...
maincontent = Frame(mainWindow, borderwidth=5, relief='sunken', width=600, height=400)
maincontent.grid(row=0, column=0, padx=5, pady=5, sticky=NSEW)
content0 = Frame(maincontent, borderwidth=5, relief='sunken')
content0.grid(row=0, column=0, sticky=NS)
checkbox_0 = Checkbutton(content0, text="Checkbox 0", variable=checkbox0)
checkbox_0.grid(row=0, column=0, padx=5, pady=5)
#.......
content1 = Frame(maincontent, borderwidth=5, relief='sunken')
content1.grid(row=0, column=1, sticky=NS)
checkbox_5 = Checkbutton(content1, text="Checkbox 5", variable=checkbox5)
checkbox_5.grid(row=0, column=0, padx=5, pady=5)
#.......
我已经与 Python 一起工作了大约两周。我遇到了一种情况,我一生都无法找到解决方案。我在链接所有复选框时遇到问题。
我到处搜索,我看到的常见线索是在声明 Checkbutton 和分配变量时不小心没有声明不同的变量。但是,正如您在下面发布的测试代码中看到的那样,我为每个复选框指定了唯一命名的变量。正如您将看到的,所有初始化为 FALSE 的变量都已链接,并且所有初始化为 TRUE 的变量都已链接。
当我在做的时候,有人能指出为什么这些框架如此糟糕吗?我很难理解这个网格系统。不是像使用 row/columns 来定位东西的常规 table 吗?根据我的经验,它现在是这样工作的。
import tkinter
from tkinter import *
from tkinter import ttk, Checkbutton, Tk
checkbox0 = FALSE
checkbox1 = FALSE
checkbox2 = TRUE
checkbox3 = FALSE
checkbox4 = FALSE
checkbox5 = TRUE
checkbox6 = FALSE
checkbox7 = FALSE
checkbox8 = TRUE
checkbox9 = FALSE
mainWindow = Tk()
maincontent = ttk.Frame(mainWindow, borderwidth=5, relief='sunken', width=600, height=400).grid(row=0, column=0, padx=5, pady=5, sticky=NSEW)
content0 = ttk.Frame(maincontent, borderwidth=5, relief='sunken').grid(row=0, column=0, sticky=NSEW)
checkbox_0 = ttk.Checkbutton(content0, text="Checkbox 0", variable=checkbox0).grid(row=0, column=0, padx=5, pady=5)
checkbox_1 = ttk.Checkbutton(content0, text="Checkbox 1", variable=checkbox1).grid(row=1, column=0, padx=5, pady=5)
checkbox_2 = ttk.Checkbutton(content0, text="Checkbox 2", variable=checkbox2).grid(row=2, column=0, padx=5, pady=5)
checkbox_3 = ttk.Checkbutton(content0, text="Checkbox 3", variable=checkbox3).grid(row=3, column=0, padx=5, pady=5)
checkbox_4 = ttk.Checkbutton(content0, text="Checkbox 4", variable=checkbox4).grid(row=4, column=0, padx=5, pady=5)
content1 = ttk.Frame(maincontent, borderwidth=5, relief='sunken').grid(row=0, column=1, sticky=NSEW)
checkbox_5 = ttk.Checkbutton(content1, text="Checkbox 5", variable=checkbox5).grid(row=0, column=0, padx=5, pady=5)
checkbox_6 = ttk.Checkbutton(content1, text="Checkbox 6", variable=checkbox6).grid(row=1, column=0, padx=5, pady=5)
checkbox_7 = ttk.Checkbutton(content1, text="Checkbox 7", variable=checkbox7).grid(row=2, column=0, padx=5, pady=5)
checkbox_8 = ttk.Checkbutton(content1, text="Checkbox 8", variable=checkbox8).grid(row=3, column=0, padx=5, pady=5)
checkbox_9 = ttk.Checkbutton(content1, text="Checkbox 9", variable=checkbox9).grid(row=4, column=0, padx=5, pady=5)
mainWindow.mainloop()
非常感谢有人解释这里发生了什么以及如何使它正常工作。在过去的两周里,我几乎不停地处理我的项目,这让我超负荷了。
感谢任何人可以提供的帮助。
您的代码有两个主要问题。首先,Checkbutton
期望 Tkinter variable,即 BooleanVar
、StringVar
、DoubleVar
或 IntVar
。所以你创建的所有变量都不适用于 Checkbutton
.
接下来,您重复了 maincontent=ttk.Frame(....).grid(...)
之类的操作。这个 returns None
然后它被分配给你的变量 maincontent
。要正确保存对每个小部件的引用,您需要将其分成两行:
maincontent=ttk.Frame(...)
maincontent=grid(...)
在这两个问题都已修复的情况下,以下是您的代码如何使用 for
循环来减少重复代码:
import tkinter as tk
from tkinter import ttk
mainWindow = tk.Tk()
all_var = [tk.BooleanVar() for _ in range(10)]
maincontent = ttk.Frame(mainWindow, borderwidth=5, relief='sunken', width=600, height=400)
maincontent.grid(row=0, column=0, padx=5, pady=5, sticky="nsew")
content0 = ttk.Frame(maincontent, borderwidth=5, relief='sunken')
content0.grid(row=0, column=0, sticky="nsew")
content1 = ttk.Frame(maincontent, borderwidth=5, relief='sunken')
content1.grid(row=0, column=1, sticky="nsew")
for i in range(5):
ttk.Checkbutton(content0, text=f"Checkbox {i}",variable=all_var[i],
command=lambda x=i: print(f"Checkbox {x}, {all_var[x].get()}")
).grid(row=i, column=0, padx=5, pady=5)
ttk.Checkbutton(content1, text=f"Checkbox {i+5}", variable=all_var[i+5],
command=lambda x=i+5: print(f"Checkbox {x}, {all_var[x].get()}")
).grid(row=i, column=0, padx=5, pady=5)
mainWindow.mainloop()
首先 * 表示您导入所有内容:
from tkinter import *
播种这个没用:
import tkinter
from tkinter import ttk, Checkbutton, Tk
其次,你用这个复选按钮写了很多次同样的东西。尽量让您的工作更轻松喜欢让 python 为您插入它们。
我不知道为什么,但是 tkinter 的 Checkbutton 在自动排列上有一些降压。
尝试使用 tkinter.ttk 中的 Checkbutton。
并尝试更多地使用 pack() 而不是 (grid),这样更容易排列它们。
这是我编写所有代码的方式,希望对您有用:)
# From tkinter we will use just Tk for the root and an IntVar because tkinter.ttk have not them :P
from tkinter import Tk, IntVar, Button
# From tkinter.ttik will import same checkbuttons becase thei will look more natural
# ttk will be much closer to default OS look them tkinter
# But the probblem here is you can not use bg, relief and such like this on ttk elements
# So if you want to add decorations on element try to import them from old/default tkinter :(((((
from tkinter.ttk import Frame, Checkbutton
root = Tk()
# Define two check buttons list
checkButtons_1 = []
checkButtons_2 = []
# We will set a number here what will be used to set the names on check buttons later
# Because will be used on more then one place I like to change it just one time next time
maxButtons = 10
# Try to append some names for them
# In programin the count start from 0 so we will increse the number with one to start from 1
for i in range(maxButtons):
# First of all we will add first half of the range
# Because some time we can get a float we try to avoid that by converting the division in a intiger
# But we will count with les 1 because: 10/2=5 so half is 5 but we will make i+1 so 5+1=6 is bigger the 5
if i <= int((maxButtons / 2) - 1):
checkButtons_1.append(f"Checkbox {i+1}")
# How we will add the rest of them
else:
checkButtons_2.append(f"Checkbox {i+1}")
class CheckBtn(Frame):
def __init__(self, parent=None, selected=[], side="top", expand=True, fill="x"):
Frame.__init__(self, parent)
self.vars = []
# For every itme new created
for item in selected:
# Because variabbles on checkbuttom are intiger (just 1 and 0) make an IntVar
var = IntVar()
# Make a little frame for every check box
frame = Frame(self)
frame.pack(side=side)
# Make the check bok
chk = Checkbutton(self, text=item, variable=var)
chk.pack(expand=expand, fill=fill, side=side)
# Save every variable state in a array list
self.vars.append(var)
# This stahe will be called later to see witch box are checked and witch not
def state(self):
return map((lambda var: var.get()), self.vars)
class gui:
def __init__(self, master):
self.master = master
# For the main screen we make two frame. One on top for the check buttons and one on bottom for the buttons
frameTop = Frame(master)
frameBottom = Frame(master)
# Now we make two frame for the checkbuttons. One for left and one for the right side
frameLeft = Frame(frameTop)
frameRight = Frame(frameTop)
# Here we will pack all frames. Here is important the order you pack, above you can make them on what order you want
frameTop.pack(expand=True, fill="x", side="top")
frameBottom.pack(expand=True, fill="x", side="bottom")
frameLeft.pack(expand=True, fill="x", side="left")
frameRight.pack(expand=True, fill="x", side="left")
# Here we create tne check button
# We define ch1 and ch2 because we will need to refer them to print the status later
ch1 = CheckBtn(frameLeft, checkButtons_1)
ch2 = CheckBtn(frameRight, checkButtons_2)
# Here we pack the buttons on screen
ch1.pack()
ch2.pack()
# We call lambda status here because our function will be after the button is pack
# So wil llook on all file for that function.
# With out that lambda function you need to put "seeStatus()" above the button.
# We not need to make btn = Button(frame,...) because we will not check the buttons anywhere for now
Button(frameBottom, text="Check status", command=lambda:seeStatus(),
bg="cyan", activebackground="cyan", relief="groove", bd=4
).pack(expand=True, fill="x", side="left")
# And for funn we add and this quit button :))
Button(frameBottom, text="Quit", command=root.quit,
bg="tomato", activebackground="tomato", relief="groove", bd=4
).pack(expand=True, fill="x", side="left")
def seeStatus():
print(list(ch1.state()), list(ch2.state()))
# If the this file is the main file then make this
# This basiclly check if you run the code from here or not.
# If not then will not execute this, you can import them to another file and will run from that file.
# Google it about. Is easy to undersnatnd.
if __name__ == "__main__":
gui(root)
root.mainloop()
但我不会拒绝你的问题,所以我只是给你一个完整的硬代码,我想...很抱歉。
问题是如果你制作一个新框架,你必须以两种不同的方式来做。
# Like that
Frame(root).pack()
# Or you you refer them back to this frame you need to "give him a name"
frame = Frame(root)
frame.grid()
# THIS NEVER WILL WORK SO GOOD
frame = Frame(root).grid()
所以你的主要问题是你如何写 pack/grid 需要分开在不同的两行...
maincontent = Frame(mainWindow, borderwidth=5, relief='sunken', width=600, height=400)
maincontent.grid(row=0, column=0, padx=5, pady=5, sticky=NSEW)
content0 = Frame(maincontent, borderwidth=5, relief='sunken')
content0.grid(row=0, column=0, sticky=NS)
checkbox_0 = Checkbutton(content0, text="Checkbox 0", variable=checkbox0)
checkbox_0.grid(row=0, column=0, padx=5, pady=5)
#.......
content1 = Frame(maincontent, borderwidth=5, relief='sunken')
content1.grid(row=0, column=1, sticky=NS)
checkbox_5 = Checkbutton(content1, text="Checkbox 5", variable=checkbox5)
checkbox_5.grid(row=0, column=0, padx=5, pady=5)
#.......