Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python tkinter选项菜单与跟踪抛出组合;TCL错误:无法分配更多菜单;_Python_Tkinter_Ttk_Optionmenu - Fatal编程技术网

Python tkinter选项菜单与跟踪抛出组合;TCL错误:无法分配更多菜单;

Python tkinter选项菜单与跟踪抛出组合;TCL错误:无法分配更多菜单;,python,tkinter,ttk,optionmenu,Python,Tkinter,Ttk,Optionmenu,我想用python编写一个代码生成器,让用户通过ttk.OptionMenu选择变量的类型。在该选项菜单的正后方,用户必须定义ttk.Checkbutton中的值(如果是布尔类型)或条目(如果是整数或字符串),然后定义一个值。GUI如下所示: 如果我想将类型更改为int或string,则后面的ttk.Checkbutton应更改为ttk.Entry,但程序确实会崩溃,因为它无法分配更多菜单[\u tkinter.TclError:无法分配更多菜单。]。我不知道该怎么办,也没有找到解决办法。我通

我想用python编写一个代码生成器,让用户通过ttk.OptionMenu选择变量的类型。在该选项菜单的正后方,用户必须定义ttk.Checkbutton中的值(如果是布尔类型)或条目(如果是整数或字符串),然后定义一个值。GUI如下所示:

如果我想将类型更改为int或string,则后面的ttk.Checkbutton应更改为ttk.Entry,但程序确实会崩溃,因为它无法分配更多菜单[\u tkinter.TclError:无法分配更多菜单。]。我不知道该怎么办,也没有找到解决办法。我通过跟踪将checkbutton与一个函数连接起来,该函数在optionmenu更改时一直被调用

下面是我的简单编码示例:

import tkinter as tk
import tkinter.ttk as ttk
from functools import partial


class MyWindow(ttk.Frame):
    def __init__(self, parent):
        ttk.Frame.__init__(self, parent)
        line = [tk.StringVar(value="bool"), tk.BooleanVar(value=True)]
        self.vars = [line]
        self.show()

    def show(self):
        for widget in self.children.values():
            widget.grid_forget()
        x = 0
        for var in self.vars:
            ttk.OptionMenu(self, var[0], var[0].get(), *["bool", "int", "string"]).grid(row=x, column=0)
            var[0].trace("w", partial(self.menu_changed, line=var))
            if var[0].get() == "bool":
                ttk.Checkbutton(self, variable=var[1].get()).grid(row=x, column=1)
            else:
                ttk.Entry(self, textvariable=var[1].get()).grid(row=x, column=1)
            x += 1
        ttk.Button(self, text="+", command=self.add, width=3).grid(row=x, column=0)
        self.place(x=10, y=10, anchor=tk.NW)

    def add(self):
        line = [tk.StringVar(value="bool"), tk.BooleanVar(value=True)]
        self.vars.append(line)
        self.show()

    def menu_changed(self, *_, line):
        if line[0].get() == "bool":
            line[1] = tk.BooleanVar(value=True)
        elif line[0].get() == "int":
            line[1] = tk.IntVar(value=0)
        elif line[0].get() == "string":
            line[1] = tk.StringVar()
        self.show()


root = tk.Tk()
window = MyWindow(root)
root.mainloop()

我希望你知道怎么了。提前感谢。

您的代码中有两个主要问题。第一个是每次调用
self.show()
self.menu\u changed()
时都会不断创建新的小部件,第二个是在
self.show()
函数中设置变量的跟踪,因此会执行多次

我重新整理了代码,只创建了新的小部件,并在
self.add()
函数中设置了跟踪。这样,您就不会创建过多的小部件。唯一的“问题”是,我对Checkbutton/条目使用
StringVar
,即使选择了布尔值或整数,所有值都转换为字符串。我还添加了一个打印按钮来打印所有当前值

import tkinter as tk
import tkinter.ttk as ttk
from functools import partial


class MyWindow(ttk.Frame):
    def __init__(self, parent):
        ttk.Frame.__init__(self, parent)
        self.lines = []
        self.add_button = ttk.Button(self, text="+", command=self.add, width=3)
        self.print_button = ttk.Button(self, text="Print", command=self.print_values)
        self.place(x=10, y=10, anchor=tk.NW)
        self.add()

    def show(self):
        for widget in self.children.values():
            widget.grid_forget()
        x = 0
        for line in self.lines:
            line[2].grid(row=x, column=0)
            if line[0].get() == "bool":
                line[3].grid(row=x, column=1)
            else:
                line[4].grid(row=x, column=1)
            x += 1
        self.add_button.grid(row=x, column=0)
        self.print_button.grid(row=x, column=1)

    def add(self):
        line = [tk.StringVar(value="bool"), tk.StringVar(value="1")]
        line.append(ttk.OptionMenu(self, line[0], line[0].get(), *["bool", "int", "string"]))
        line.append(ttk.Checkbutton(self, variable=line[1]))
        line.append(ttk.Entry(self, textvariable=line[1]))
        line[0].trace("w", partial(self.menu_changed, line=line))
        self.lines.append(line)
        self.show()

    def menu_changed(self, *_, line):
        if line[0].get() == "bool":
            line[1].set("1")
        elif line[0].get() == "int":
            line[1].set("0")
        elif line[0].get() == "string":
            line[1].set("")
        self.show()

    def print_values(self):
        print("Current values:")
        for line in self.lines:
            print(line[1].get())


root = tk.Tk()
window = MyWindow(root)
root.mainloop()

顺便说一下,您实际上不需要跟踪StringVar,您可以向OptionMenu添加一个命令参数,如下所示:

ttk.OptionMenu(self, line[0], line[0].get(), *["bool", "int", "string"], command=partial(self.menu_changed, line=line))

代码中有两个主要问题。第一个是每次调用
self.show()
self.menu\u changed()
时都会不断创建新的小部件,第二个是在
self.show()
函数中设置变量的跟踪,因此会执行多次

我重新整理了代码,只创建了新的小部件,并在
self.add()
函数中设置了跟踪。这样,您就不会创建过多的小部件。唯一的“问题”是,我对Checkbutton/条目使用
StringVar
,即使选择了布尔值或整数,所有值都转换为字符串。我还添加了一个打印按钮来打印所有当前值

import tkinter as tk
import tkinter.ttk as ttk
from functools import partial


class MyWindow(ttk.Frame):
    def __init__(self, parent):
        ttk.Frame.__init__(self, parent)
        self.lines = []
        self.add_button = ttk.Button(self, text="+", command=self.add, width=3)
        self.print_button = ttk.Button(self, text="Print", command=self.print_values)
        self.place(x=10, y=10, anchor=tk.NW)
        self.add()

    def show(self):
        for widget in self.children.values():
            widget.grid_forget()
        x = 0
        for line in self.lines:
            line[2].grid(row=x, column=0)
            if line[0].get() == "bool":
                line[3].grid(row=x, column=1)
            else:
                line[4].grid(row=x, column=1)
            x += 1
        self.add_button.grid(row=x, column=0)
        self.print_button.grid(row=x, column=1)

    def add(self):
        line = [tk.StringVar(value="bool"), tk.StringVar(value="1")]
        line.append(ttk.OptionMenu(self, line[0], line[0].get(), *["bool", "int", "string"]))
        line.append(ttk.Checkbutton(self, variable=line[1]))
        line.append(ttk.Entry(self, textvariable=line[1]))
        line[0].trace("w", partial(self.menu_changed, line=line))
        self.lines.append(line)
        self.show()

    def menu_changed(self, *_, line):
        if line[0].get() == "bool":
            line[1].set("1")
        elif line[0].get() == "int":
            line[1].set("0")
        elif line[0].get() == "string":
            line[1].set("")
        self.show()

    def print_values(self):
        print("Current values:")
        for line in self.lines:
            print(line[1].get())


root = tk.Tk()
window = MyWindow(root)
root.mainloop()

顺便说一下,您实际上不需要跟踪StringVar,您可以向OptionMenu添加一个命令参数,如下所示:

ttk.OptionMenu(self, line[0], line[0].get(), *["bool", "int", "string"], command=partial(self.menu_changed, line=line))

通过@fhdrsdg先前的回答,我发现了错误。我写过

ttk.OptionMenu(self, var[0], var[0].get(), *["bool", "int", "string"]).grid(row=x, column=0)
var[0].trace("w", partial(self.menu_changed, line=var))
。。。在执行跟踪(…)之前执行网格(…)

如果您改为编写以下命令,并将该命令放在optionmenu的声明中,那么它将在grid()之前被跟踪并起作用。以下是代码的工作行:

cmd = partial(self.menu_changed, line=var)
ttk.OptionMenu(self, var[0], var[0].get(), *["bool", "int", "string"], command=cmd).grid(row=x, column=0)

但是非常感谢@fhdrsdg,因为如果没有他的提示,我永远不会发现这个错误

借助@fhdrsdg之前的回答,我发现了错误。我写过

ttk.OptionMenu(self, var[0], var[0].get(), *["bool", "int", "string"]).grid(row=x, column=0)
var[0].trace("w", partial(self.menu_changed, line=var))
。。。在执行跟踪(…)之前执行网格(…)

如果您改为编写以下命令,并将该命令放在optionmenu的声明中,那么它将在grid()之前被跟踪并起作用。以下是代码的工作行:

cmd = partial(self.menu_changed, line=var)
ttk.OptionMenu(self, var[0], var[0].get(), *["bool", "int", "string"], command=cmd).grid(row=x, column=0)

但是非常感谢@fhdrsdg,因为如果没有他的提示,我永远不会发现这个错误

如果这是你从我的回答中得到的唯一东西,你仍然会过得很糟糕。你一直在制造太多的小部件,只是以一个不那么极端的速度。此外,使用
命令
参数与跟踪变量不同,每次变量值更改时都会触发跟踪,每次选择选项菜单中的选项时都会触发命令。只是这两种方法都适用于您的预期用途。问题不在于你在使用跟踪,而在于你在
show()
函数中使用它。@fhdrsdg你是对的你的例子很好,但对我来说没有用,因为我的代码要复杂得多,用你描述的方式更改它并不能满足我的所有要求。这样,即使有10个选项菜单,它也能正常工作。这对我来说已经足够了,我明白。重写代码是否值得,只有你自己能决定。我只希望你能理解我的担忧,并在未来的项目中牢记这一点。祝你好运@fhdrsdg是的,我理解,您的解决方案很好,但对我来说,更改整个代码很复杂。但以后我会提醒你的。非常感谢。如果这是你从我的回答中得到的唯一东西,你仍然会过得很糟糕。你一直在制造太多的小部件,只是以一个不那么极端的速度。此外,使用
命令
参数与跟踪变量不同,每次变量值更改时都会触发跟踪,每次选择选项菜单中的选项时都会触发命令。只是这两种方法都适用于您的预期用途。问题不在于你在使用跟踪,而在于你在
show()
函数中使用它。@fhdrsdg你是对的你的例子很好,但对我来说没有用,因为我的代码要复杂得多,用你描述的方式更改它并不能满足我的所有要求。这样,即使有10个选项菜单,它也能正常工作。这对我来说已经足够了,我明白。重写代码是否值得,只有你自己能决定。我只希望你能理解我的担忧,并在未来的项目中牢记这一点。祝你好运@fhdrsdg是的,我理解,您的解决方案很好,但对我来说,更改整个代码很复杂。但以后我会提醒你的。非常感谢。