Python 添加和删除tkinter帧-跟踪实例

Python 添加和删除tkinter帧-跟踪实例,python,class,tkinter,instance,Python,Class,Tkinter,Instance,我试图在Tkinter GUI中创建一组子帧,每个子帧都包含一些用户输入变量,稍后我需要通过每个子帧的唯一ID获取这些变量。我需要能够随意添加和删除子帧,并以合理的顺序对它们进行标识(此时整数从1开始计数) 确实有三个问题: 1) 具体来说,为什么显示每个帧标题的标签小部件不显示?(这可能有点傻……) 2) 具体来说,为什么有时需要按两次“添加”或“删除”按钮才能添加或删除帧 3) 一般来说:有没有更好的方法?在跟踪帧实例的意义上。他们现在在名单上。。。我早些时候开始写的是一篇dict,但它更难

我试图在Tkinter GUI中创建一组子帧,每个子帧都包含一些用户输入变量,稍后我需要通过每个子帧的唯一ID获取这些变量。我需要能够随意添加和删除子帧,并以合理的顺序对它们进行标识(此时整数从1开始计数)

确实有三个问题:

1) 具体来说,为什么显示每个帧标题的标签小部件不显示?(这可能有点傻……)

2) 具体来说,为什么有时需要按两次“添加”或“删除”按钮才能添加或删除帧

3) 一般来说:有没有更好的方法?在跟踪帧实例的意义上。他们现在在名单上。。。我早些时候开始写的是一篇dict,但它更难用。。。有更好的建议吗

from tkinter import *
from tkinter.ttk import *

class AllInstances(Tk):
    def __init__(self):
        Tk.__init__(self)
        self.master_frame = Frame(self)
        self.master_frame.grid()
        self.all_instances = []

root = AllInstances()

class OneInstance():
    def __init__(self):
        self.number = len(root.all_instances) + 1

        var1 = StringVar()
        var2 = StringVar()
        var3 = StringVar()

        self.sub_frame = Frame(root.master_frame)
        self.sub_frame.grid(column = self.number, row = 0)

        titletext = StringVar()
        titletext.set('%s %s' % ('Frame', self.number))
        print(titletext.get())
        title = Label(self.sub_frame, textvariable = titletext)
        title.grid() #work out why this label does not display!

        uservar1 = Entry(self.sub_frame, textvariable = var1)
        uservar1.grid()
        uservar2 = Entry(self.sub_frame, textvariable = var2)
        uservar2.grid()
        uservar3 = Entry(self.sub_frame, textvariable = var3)
        uservar3.grid()
        #etc etc

        add_button = Button(self.sub_frame, text = 'Add', command = lambda: Create())
        add_button.grid()

        def RemoveInstance(self):
            if len(root.all_instances) > 1:
                root.all_instances.remove(self)
                self.sub_frame.destroy()

                for instance in root.all_instances:
                    instance.number = (root.all_instances.index(instance) + 1)
            else:
                pass

        remove_button = Button(self.sub_frame, text = 'Remove', command = lambda: RemoveInstance(self))
        remove_button.grid()

        root.all_instances.append(self)

def Create():
    OneInstance()

Create()
root.mainloop()

谢谢……:)

下面是我要做的:

  • 使
    AllInstances
    继承自
    Frame
    (并重命名为类似“FrameGroup”的名称)。然后它变成了一个“megawidget”,子框架是它可以直接控制的子框架
  • 使您的子帧成为
    Frame
    的子类(并称之为“subframe”)。这也是一个“megawidget”,它需要与其父级紧密耦合(即:它不应该知道
    root.all_实例
  • 让“添加”和“删除”按钮调用FrameGroup对象上的方法
  • 我还建议不要执行全局导入。因为您同时使用ttk和tkinter,根据您对导入语句的顺序,其中一个将覆盖另一个

    下面是一个带有这些修改的工作示例:

    import tkinter as tk
    
    class FrameGroup(tk.Frame):
        def __init__(self, parent):
            tk.Frame.__init__(self, parent)
            self.all_instances = []
            self.counter = 0
    
        def Add(self):
            self.counter += 1
            name = "Frame %s" % self.counter 
            subframe = Subframe(self, name=name)
            subframe.pack(side="left", fill="y")
            self.all_instances.append(subframe)
    
        def Remove(self, instance):
            # don't allow the user to destroy the last item
            if len(self.all_instances) > 1:
                index = self.all_instances.index(instance)
                subframe = self.all_instances.pop(index)
                subframe.destroy()
    
        def HowMany(self):
            return len(self.all_instances)
    
        def ShowMe(self):
            for instance in self.all_instances:
                print(instance.get())
    
    class Subframe(tk.Frame):
        def __init__(self, parent, name):
            tk.Frame.__init__(self, parent)
            self.parent = parent
            self.e1 = tk.Entry(self)
            self.e2 = tk.Entry(self)
            self.e3 = tk.Entry(self)
            label = tk.Label(self, text=name, anchor="center")
            add_button = tk.Button(self, text="Add", command=self.parent.Add)
            remove_button = tk.Button(self, text="Remove", command=lambda: self.parent.Remove(self))
    
            label.pack(side="top", fill="x")
            self.e1.pack(side="top", fill="x")
            self.e2.pack(side="top", fill="x")
            self.e3.pack(side="top", fill="x")
            add_button.pack(side="top")
            remove_button.pack(side="top")
    
        def get(self):
            return (self.e1.get(), self.e2.get(), self.e3.get())
    
    class GUI(tk.Tk):
        def __init__(self):
            tk.Tk.__init__(self)
            self.master_frame = tk.Frame(self)
            self.master_frame.grid()
            self.all_instances = FrameGroup(self.master_frame)
            self.all_instances.grid()
    
            # create the first frame
            self.all_instances.Add()
    
    root = GUI()
    root.mainloop()
    

    下面是我要做的:

  • 使
    AllInstances
    继承自
    Frame
    (并重命名为类似于“FrameGroup”的名称)。然后它变成一个“megawidget”,子框架是它可以直接控制的子框架
  • 使您的子帧成为
    Frame
    (并称之为“subframe”)的子类。这也是一个“megawidget”,它需要与其父级(即:它不应该知道
    根目录。所有\u实例
  • 让“添加”和“删除”按钮调用FrameGroup对象上的方法
  • 我还建议不要执行全局导入。因为您同时使用ttk和tkinter,根据您对导入语句的顺序,其中一个将覆盖另一个

    下面是一个带有这些修改的工作示例:

    import tkinter as tk
    
    class FrameGroup(tk.Frame):
        def __init__(self, parent):
            tk.Frame.__init__(self, parent)
            self.all_instances = []
            self.counter = 0
    
        def Add(self):
            self.counter += 1
            name = "Frame %s" % self.counter 
            subframe = Subframe(self, name=name)
            subframe.pack(side="left", fill="y")
            self.all_instances.append(subframe)
    
        def Remove(self, instance):
            # don't allow the user to destroy the last item
            if len(self.all_instances) > 1:
                index = self.all_instances.index(instance)
                subframe = self.all_instances.pop(index)
                subframe.destroy()
    
        def HowMany(self):
            return len(self.all_instances)
    
        def ShowMe(self):
            for instance in self.all_instances:
                print(instance.get())
    
    class Subframe(tk.Frame):
        def __init__(self, parent, name):
            tk.Frame.__init__(self, parent)
            self.parent = parent
            self.e1 = tk.Entry(self)
            self.e2 = tk.Entry(self)
            self.e3 = tk.Entry(self)
            label = tk.Label(self, text=name, anchor="center")
            add_button = tk.Button(self, text="Add", command=self.parent.Add)
            remove_button = tk.Button(self, text="Remove", command=lambda: self.parent.Remove(self))
    
            label.pack(side="top", fill="x")
            self.e1.pack(side="top", fill="x")
            self.e2.pack(side="top", fill="x")
            self.e3.pack(side="top", fill="x")
            add_button.pack(side="top")
            remove_button.pack(side="top")
    
        def get(self):
            return (self.e1.get(), self.e2.get(), self.e3.get())
    
    class GUI(tk.Tk):
        def __init__(self):
            tk.Tk.__init__(self)
            self.master_frame = tk.Frame(self)
            self.master_frame.grid()
            self.all_instances = FrameGroup(self.master_frame)
            self.all_instances.grid()
    
            # create the first frame
            self.all_instances.Add()
    
    root = GUI()
    root.mainloop()
    

    谢谢Bryan。我想我可以看到您重组它的逻辑。您更简单的.pack()方法避免了获取正确列的问题,但连续列和帧名称对我来说是必要的,因此我必须使用.grid()再次查看这一点.Re:导入ttk并覆盖tkinter-我对此很满意,不需要tk.widget或ttk.widget的清晰性(基于我实际使用的所有小部件都被覆盖,然后我可以使用ttk样式而无需额外键入)但是我还应该注意到其他缺点吗?@Tom:pack和grid与框架名称无关。至于其他缺点,我只是认为不进行全局导入是一种最佳做法。另外,当您进行全局导入时,它会使代码更难理解。例如,我假设您使用的是tkinter按钮和标签s、 直到我完善了我的答案,我才意识到你依赖ttk导入而不是tk导入。如果你使用
    ttk.Button
    tk.Button
    ,你的代码非常清楚你在使用哪些小部件,在编写软件时,清晰性应该是一个高度优先的目标。@Tom:如果你想连续使用tk.Button即使在用户删除中间的一个列时,一个简单的解决办法是在<代码>子帧< /代码>类中添加一个<代码>重命名< /代码>方法,然后让您的<代码>删除>代码>函数遍历所有的实例,用每个列所需的新名称调用重命名。@ BryanOakley,我几乎有类似的问题。使用一个不使用类的函数创建了我的框架。在我的例子中,我如何编辑删除函数?谢谢。谢谢Bryan。我想我可以看到你如何重新构造它的逻辑。你的simpler.pack()这种方法避免了获取正确列的问题,但连续的列和帧名称对我来说是必要的,所以我必须使用.grid()再次查看。Re:导入ttk并覆盖tkinter-我对此很满意,不需要tk.widget或ttk.widget的清晰性(基于我实际使用的所有小部件都被覆盖,然后我可以使用ttk样式而无需额外键入)但是我还应该注意到其他缺点吗?@Tom:pack和grid与框架名称无关。至于其他缺点,我只是认为不进行全局导入是一种最佳做法。另外,当您进行全局导入时,它会使代码更难理解。例如,我假设您使用的是tkinter按钮和标签s、 直到我完善了我的答案,我才意识到你依赖ttk导入而不是tk导入。如果你使用
    ttk.Button
    tk.Button
    ,你的代码非常清楚你在使用哪些小部件,在编写软件时,清晰性应该是一个高度优先的目标。@Tom:如果你想连续使用tk.Button即使在用户删除中间的一个列时,一个简单的解决办法是在<代码>子帧< /代码>类中添加一个<代码>重命名< /代码>方法,然后让您的<代码>删除>代码>函数遍历所有的实例,用每个列所需的新名称调用重命名。@ BryanOakley,我几乎有类似的问题。使用不使用类的函数创建了我的框架。在我的情况下,如何编辑删除函数?谢谢。