如何解决 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,即 BooleanVarStringVarDoubleVarIntVar。所以你创建的所有变量都不适用于 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)
#.......