tkinter框架

时间:2018-01-05 20:41:14

标签: python python-3.x tkinter iteration

我想使用tkinter GUI迭代字典(例如)并允许用户对其值进行操作。

例如,我的老板可能想要遍历部门并选择要解雇的员工。以下代码(主要)适用于第一个部门,但我不了解如何前往下一个部门(self.advance以下)。

question是相关的,但只是更新现有小部件的值。每个部门的员工人数各不相同,所以我不能只更新名称,而且还必须允许垂直滚动。

迭代发生在一个框架(innerFrame)内,其余部分基本上是静态的。我应该摧毁并重新创建innerFrame,还是仅仅内部的所有小部件?无论哪种方式,我怎样才能进入下一次迭代?

# Example data
emp = {'Sales':['Alice','Bryan','Cathy','Dave'],
       'Product':['Elizabeth','Frank','Gordon','Heather',
                  'Irene','John','Kristof','Lauren'],
       'Marketing':['Marvin'],
       'Accounting':['Nancy','Oscar','Peter','Quentin',
                     'Rebecca','Sally','Trevor','Umberto',
                     'Victoria','Wally','Xavier','Yolanda',
                     'Zeus']}

import tkinter as tk
from tkinter import messagebox

class bossWidget(tk.Frame):
    def __init__(self, root):
        """
        Scrollbar code credit to Bryan Oakley:
        https://stackoverflow.com/a/3092341/2573061
        """
        super().__init__()     
        self.canvas = tk.Canvas(root, borderwidth=0)
        self.frame  = tk.Frame(self.canvas)
        self.scroll = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.scroll.set)
        self.scroll.pack(side="right", fill="y")
        self.canvas.pack(side="left", fill="both", expand=True)
        self.canvas.create_window((4,4), window=self.frame, anchor="nw", 
                                  tags="self.frame")
        self.frame.bind("<Configure>", self.onFrameConfigure)
        self.initUI()        

    def initUI(self):
        """
        Creates the static UI content and the innerFrame that will hold the
        dynamic UI content (i.e., the Checkbuttons for the copies)
        """
        self.master.title("Boss Interface")
        self.instructLabel = tk.Label( self.frame, justify='left',
                                      text = "Select the employees you wish to FIRE")
        self.skipButton   = tk.Button( self.frame, text="Skip Department", 
                                      command = self.advance)
        self.deleteButton = tk.Button( self.frame, text="Fire employees", fg = 'red',
                                       command = self.executeSelection )
        self.quitButton   = tk.Button( self.frame, text="Exit", command=self.frame.quit)
        self.innerFrame   = tk.Frame( self.frame)
        self.instructLabel.pack(anchor = 'nw', padx=5,pady=5)
        self.innerFrame.pack(anchor='nw', padx=5, pady=20, expand=True)
        self.deleteButton.pack(side='left', padx=5,pady=5)
        self.skipButton.pack(side='left', padx=5,pady=5)
        self.quitButton.pack(side='left', padx=5,pady=5)

    def populateUI(self, title, labelList):
        """
        Creates and packs a list of Checkbuttons (cbList) into the innerFrame
        By default, the first Checkbutton will be unchecked, all others checked.
        You should help the boss out by passing the best employee at the head of the list
        """
        self.instructLabel.config(text = title + ' department:\nSelect the employees you wish to FIRE')
        self.cbList = [None] * len(labelList)
        self.cbValues = [tk.BooleanVar() for i in range(len(labelList))]
        for i in range(len(labelList)):
            self.cbList[i] = tk.Checkbutton( self.innerFrame, 
                                        text=labelList[i], 
                                        variable = self.cbValues[i])
            if i: self.cbList[i].select() # Check subsequent buttons by default
            self.cbList[i].pack(anchor = 'w', padx=5,pady=5) 

    def advance(self):
        # -------------> this is what I don't understand how to do <-------------
        self.innerFrame.destroy()  # this destroys everything! 
        # how to advance to next iteration?

    def querySelection(self):
        return [x.get() for x in self.cbValues]

    def executeSelection(self):
        fired = self.querySelection()

        if ( not all(x for x in fired) or 
             messagebox.askokcancel(message='Fire ALL the employees in the department?') 
           ):       
            for i in range(len(self.cbList)):
                empName = self.cbList[i].cget('text') 
                if fired[i]:
                    print('Sorry, '+ empName + ', but we have to let you go.', flush=True)
                else:    
                    print('See you Monday, '+ empName, flush=True)    
            self.advance()   

    def onFrameConfigure(self, event):
        """Reset the scroll region to encompass the inner frame"""
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

def main(): 
    root = tk.Tk()   
    root.geometry("400x250+250+100") # width x height + xOffset + yOffset 
    app = bossWidget(root)
    while emp:    
        department, employees = emp.popitem()
        app.pack(side='top',fill='both',expand=True)
        app.populateUI(title = department, labelList = employees)
        root.mainloop()
        try:
            root.destroy()
        except tk.TclError:
            pass # if run in my IDE, the root already is destroyed

if __name__ == '__main__':
    main()   

3 个答案:

答案 0 :(得分:1)

基本模式是每个帧都有一个类或一个函数。这些类或函数中的每一个都创建一个Frame,并将其所有小部件放在该框架中。

然后,您需要做的就是删除当前帧,并调用函数或对象来创建新帧。就这么简单。

本网站上的一些例子:

答案 1 :(得分:1)

以下是对代码的简短修改,以处理更新解雇员工和切换框架的复选框,以显示部门中的新员工。如果所有员工都被解雇,我没有处理。还有一个小错误,但我会留给你弄清楚。

这可能是 lot 清洁工。我只是不想重写你的所有代码......

   # Example data
    emp = [['Sales', ['Alice','Bryan','Cathy','Dave']],
           ['Product', ['Elizabeth','Frank','Gordon','Heather',
                      'Irene','John','Kristof','Lauren']],
           ['Marketing', ['Marvin']],
           ['Accounting', ['Nancy','Oscar','Peter','Quentin',
                         'Rebecca','Sally','Trevor','Umberto',
                         'Victoria','Wally','Xavier','Yolanda',
                         'Zeus']]]

    import tkinter as tk
    from tkinter import messagebox

    class bossWidget(tk.Frame):
        def __init__(self, root):
            """
            Scrollbar code credit to Bryan Oakley:
            https://stackoverflow.com/a/3092341/2573061
            """
            super().__init__()    

            self.cursor = 0

            self.canvas = tk.Canvas(root, borderwidth=0)
            self.frame  = tk.Frame(self.canvas)
            self.scroll = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
            self.canvas.configure(yscrollcommand=self.scroll.set)
            self.scroll.pack(side="right", fill="y")
            self.canvas.pack(side="left", fill="both", expand=True)
            self.canvas.create_window((4,4), window=self.frame, anchor="nw", 
                                      tags="self.frame")
            self.frame.bind("<Configure>", self.onFrameConfigure)
            self.initUI()        

        def initUI(self):
            """
            Creates the static UI content and the innerFrame that will hold the
            dynamic UI content (i.e., the Checkbuttons for the copies)
            """
            self.master.title("Boss Interface")
            self.instructLabel = tk.Label( self.frame, justify='left',
                                          text = "Select the employees you wish to FIRE")
            self.skipButton   = tk.Button( self.frame, text="Skip Department", 
                                          command = self.advance)
            self.deleteButton = tk.Button( self.frame, text="Fire employees", fg = 'red',
                                           command = self.executeSelection )
            self.quitButton   = tk.Button( self.frame, text="Exit", command=self.frame.quit)
            self.innerFrame = tk.Frame(self.frame)
            self.instructLabel.pack(anchor = 'nw', padx=5,pady=5)
            self.innerFrame.pack(anchor = 'nw', padx=5,pady=5)
            self.deleteButton.pack(side='left', padx=5,pady=5)
            self.skipButton.pack(side='left', padx=5,pady=5)
            self.quitButton.pack(side='left', padx=5,pady=5)

            self.populateUI(*self.get_populate_items())

        def get_populate_items(self):

            return (emp[self.cursor][0], emp[self.cursor][1])

        def populateUI(self, title, labelList):
            """
            Creates and packs a list of Checkbuttons (cbList) into the innerFrame
            By default, the first Checkbutton will be unchecked, all others checked.
            You should help the boss out by passing the best employee at the head of the list
            """
            for child in self.innerFrame.winfo_children():
                child.destroy()
            self.instructLabel.config(text = title + ' department:\nSelect the employees you wish to FIRE')
            self.cbList = [None] * len(labelList)
            self.cbValues = [tk.BooleanVar() for i in range(len(labelList))]
            for i in range(len(labelList)):
                self.cbList[i] = tk.Checkbutton( self.innerFrame, 
                                            text=labelList[i], 
                                            variable = self.cbValues[i])
                if i: self.cbList[i].select() # Check subsequent buttons by default
                self.cbList[i].pack(anchor = 'w', padx=5,pady=5) 

        def advance(self):

            if (self.cursor < len(emp) - 1):
                self.cursor += 1
            else:
                self.cursor  = 0
            self.populateUI(*self.get_populate_items())

        def querySelection(self):
            return [x.get() for x in self.cbValues]

        def executeSelection(self):
            fired = self.querySelection()

            if ( not all(x for x in fired) or 
                 messagebox.askokcancel(message='Fire ALL the employees in the department?') 
               ):       
                for i in range(len(self.cbList)):
                    empName = self.cbList[i].cget('text') 
                    if fired[i]:
                        emp[self.cursor][1].remove(empName)
                        print('Sorry, '+ empName + ', but we have to let you go.', flush=True)
                    else:    
                        print('See you Monday, '+ empName, flush=True) 
                self.populateUI(*self.get_populate_items())
                # self.advance()   

        def onFrameConfigure(self, event):
            """Reset the scroll region to encompass the inner frame"""
            self.canvas.configure(scrollregion=self.canvas.bbox("all"))

    def main(): 
        root = tk.Tk()   
        root.geometry("400x250+250+100") # width x height + xOffset + yOffset 
        app = bossWidget(root)
        root.mainloop()
        # while emp:    
        #     department, employees = emp.popitem()
        #     app.pack(side='top',fill='both',expand=True)
        #     app.populateUI(title = department, labelList = employees)
        #     root.mainloop()
        #     try:
        #         root.destroy()
        #     except tk.TclError:
        #         pass # if run in my IDE, the root already is destroyed

    if __name__ == '__main__':
        main()   

答案 2 :(得分:0)

我接受了Pythonista的答案,但最终做了以下事情:

  • UI构造函数将数据作为参数获取(可能比全局数据变量更好)
  • UI填充程序首先删除所有现有标签(请参阅接受的答案)
  • UI populator然后关闭一个记录(如果剩下,否则终止)
  • 执行其他任务后执行按钮调用UI填充程序
  • 跳过按钮只调用UI填充程序(因此可以完全删除高级功能)

这就是我用完的东西。正如Pythonista所说,它很混乱,但我们都必须从某个地方开始。

# Example data
emp = {'Sales':['Alice','Bryan','Cathy','Dave'],
       'Product':['Elizabeth','Frank','Gordon','Heather',
                  'Irene','John','Kristof','Lauren'],
       'Marketing':['Marvin'],
       'Accounting':['Nancy','Oscar','Peter','Quentin',
                     'Rebecca','Sally','Trevor','Umberto',
                     'Victoria','Wally','Xavier','Yolanda',
                     'Zeus']}

import tkinter as tk
from tkinter import messagebox

class bossWidget(tk.Frame):
    def __init__(self, root, data):
        """
        Scrollbar code credit to Bryan Oakley:
        https://stackoverflow.com/a/3092341/2573061
        """
        super().__init__()     
        self.canvas = tk.Canvas(root, borderwidth=0)
        self.frame  = tk.Frame(self.canvas)
        self.scroll = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.scroll.set)
        self.scroll.pack(side="right", fill="y")
        self.canvas.pack(side="left", fill="both", expand=True)
        self.canvas.create_window((4,4), window=self.frame, anchor="nw", 
                                  tags="self.frame")
        self.frame.bind("<Configure>", self.onFrameConfigure)
        self.data = data
        self.initUI()        

    def initUI(self):
        """
        Creates the static UI content and the innerFrame that will hold the
        dynamic UI content (i.e., the Checkbuttons for the copies)
        """
        self.master.title("Boss Interface")
        self.instructLabel = tk.Label( self.frame, justify='left',
                                      text = "Select the employees you wish to FIRE")
        self.skipButton   = tk.Button( self.frame, text="Skip Department", 
                                      command = self.populateUI)
        self.deleteButton = tk.Button( self.frame, text="Fire employees", fg = 'red',
                                       command = self.executeSelection )
        self.quitButton   = tk.Button( self.frame, text="Exit", command=self.frame.quit)
        self.innerFrame   = tk.Frame( self.frame)
        self.instructLabel.pack(anchor = 'nw', padx=5,pady=5)
        self.innerFrame.pack(anchor='nw', padx=5, pady=20, expand=True)
        self.deleteButton.pack(side='left', padx=5,pady=5)
        self.skipButton.pack(side='left', padx=5,pady=5)
        self.quitButton.pack(side='left', padx=5,pady=5)
        self.populateUI()

    def populateUI(self):
        """
        Creates and packs a list of Checkbuttons (cbList) into the innerFrame
        By default, the first Checkbutton will be unchecked, all others checked.
        You should help the boss out by passing the best employee at the head of the list
        """
        for child in self.innerFrame.winfo_children():
            child.destroy()
        try:
            title, labelList = self.data.popitem()
            self.instructLabel.config(text = title + ' department:\nSelect the employees you wish to FIRE')
            self.cbList = [None] * len(labelList)
            self.cbValues = [tk.BooleanVar() for i in range(len(labelList))]
            for i in range(len(labelList)):
                self.cbList[i] = tk.Checkbutton( self.innerFrame, 
                                            text=labelList[i], 
                                            variable = self.cbValues[i])
                if i: self.cbList[i].select() # Check subsequent buttons by default
                self.cbList[i].pack(anchor = 'w', padx=5,pady=5) 
        except KeyError:
            messagebox.showinfo("All done", "You've purged all the departments.  Good job, boss.")
            self.frame.quit()

    def querySelection(self):
        return [x.get() for x in self.cbValues]

    def executeSelection(self):
        fired = self.querySelection()

        if ( not all(x for x in fired) or 
             messagebox.askokcancel(message='Fire ALL the employees in the department?') 
           ):       
            for i in range(len(self.cbList)):
                empName = self.cbList[i].cget('text') 
                if fired[i]:
                    print('Sorry, '+ empName + ', but we have to let you go.', flush=True)
                else:    
                    print('See you Monday, '+ empName, flush=True)    
            self.populateUI()   

    def onFrameConfigure(self, event):
        """Reset the scroll region to encompass the inner frame"""
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

def main(): 
    root = tk.Tk()   
    root.geometry("400x250+250+100") # width x height + xOffset + yOffset 
    app = bossWidget(root, data=emp)
    app.mainloop()
    try:
        root.destroy()
    except tk.TclError:
        pass # if run in my IDE, the root already is destroyed

if __name__ == '__main__':
    main()