简体   繁体   English

创建 Tkinter 个按钮的列表或字典以在不同的框架中使用

[英]Create a list or dictionary of Tkinter buttons to use in different frames

So I have my Tkinter application that consist of multiple frame所以我的 Tkinter 应用程序包含多个框架

All these multiple frames contain the same basic structure of many buttons;所有这些多个框架都包含许多按钮的相同基本结构; the only difference is that the buttons have a different bg on each page.唯一的区别是bg在每一页上都有不同的背景。

In my actual project, these buttons contain so many options, and so having to write the same basic code each time for all pages makes my code look unnecessarily long.在我的实际项目中,这些按钮包含如此多的选项,因此每次都必须为所有页面编写相同的基本代码使我的代码看起来不必要地长。

So I'm thinking: Is there a way to put all these buttons into a dictionary or list, and pack them onto each separate frame?所以我在想:有没有办法将所有这些按钮放入字典或列表中,并将它们打包到每个单独的框架中? (Bear in mind the button will need to inherit the bg variable from the specific frame.) (请记住,按钮需要从特定框架继承bg变量。)

I've created a minimal example to illustrate what I mean:我创建了一个最小示例来说明我的意思:

import tkinter as tk 
from tkinter import *

listt = []
self = None
bg_colour_for_this_frame = None
button1 = Button(self,text="Button 1",bg=bg_colour_for_this_frame,fg='white')
button2 = Button(self,text="Button 2",bg=bg_colour_for_this_frame,fg='blue')
button3 = Button(self,text="Button 3",bg=bg_colour_for_this_frame,fg='orange')
listt.append(button1)
listt.append(button2)
listt.append(button3)

class Tkinter(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)
        self.frames = {}
        for F in (StartPage, SecondPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame(StartPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()
        frame.winfo_toplevel().geometry("860x864")
        frame.configure(bg='#000000')

class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        Button(self,text='SecondPage',command=lambda:controller.show_frame(SecondPage)).pack()
        for s in listt:
            s.pack()

class SecondPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        Button(self,text='StartPage',command=lambda:controller.show_frame(StartPage)).pack()
        for s in listt:
            s.pack()

app = Tkinter()
app.mainloop()

Or maybe, instead of having a list, use a dictionary:或者,也许,而不是有一个列表,使用字典:

listt = {'button1':'Button[root,text="Button 1",bg=bg_colour_for_this_frame,fg="white"]',
        'button2':'Button[root,text="Button 2",bg=bg_colour_for_this_frame,fg="red"]',
        'button3':'Button[root,text="Button 3",bg=bg_colour_for_this_frame,fg="blue"]',
       }

I get the error:我收到错误:

s.pack() s.pack()
AttributeError: 'str' object has no attribute 'pack' AttributeError: 'str' object 没有属性 'pack'

Since you can't create the Button s before the page they're on exists, It would be simpler to make a function and call it during the initialization of each of the page classes — like the make_buttons() shown below:由于您无法在Button所在的页面存在之前创建它们,因此创建一个 function 并在每个页面类的初始化期间调用它会更简单——如下所示的make_buttons()

import tkinter as tk
from tkinter import *

# Button options for all pages.
BTN_OPTS = [dict(text="Button 1", fg='white'),
            dict(text="Button 2", fg='blue'),
            dict(text="Button 3", fg='orange')]


def make_buttons(parent, bg_colour):
    return [Button(parent, bg=bg_colour, **opts) for opts in BTN_OPTS]


class Tkinter(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F in (StartPage, SecondPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame(StartPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()
        frame.winfo_toplevel().geometry("860x864")
        frame.configure(bg='#000000')


class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        Button(self, text='SecondPage',
               command=lambda: controller.show_frame(SecondPage)).pack()
        for btn in make_buttons(self, 'Pink'):
            btn.pack()


class SecondPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        Button(self, text='StartPage',
               command=lambda: controller.show_frame(StartPage)).pack()
        for btn in make_buttons(self, 'green'):
            btn.pack()

app = Tkinter()
app.mainloop()

A more sophisticated and object-oriented approach would be to define a base class for all page classes that had a method in it something like the function above, and then derive the concrete subclasses from that allowing them just inherit the method.一种更复杂和面向对象的方法是为所有页面类定义一个基类 class,其中包含一个类似于上面的 function 的方法,然后从中派生具体的子类,允许它们继承该方法。 It also gets rid of the global data because the button options are now in a (base) class attribute.它还摆脱了全局数据,因为按钮选项现在位于(基本)class 属性中。

Here's a runnable example of how it could be done that way.这是一个可运行的示例,说明如何以这种方式完成。 Note: it requires Python 3.6+ because it uses object.__init_subclass__() which was added in that version:注意:它需要 Python 3.6+,因为它使用了在该版本中添加的object.__init_subclass__()

import tkinter as tk


class Tkinter(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F in (StartPage, SecondPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame(StartPage)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()
        frame.winfo_toplevel().geometry("860x864")
        frame.configure(bg='#000000')


class BasePage(tk.Frame):
    # Button options common to all pages.
    BTN_OPTS = [dict(text="Button 1", fg='white'),
                dict(text="Button 2", fg='blue'),
                dict(text="Button 3", fg='orange')]

    @classmethod
    def __init_subclass__(cls, /, bg_color, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.bg_color = bg_color

    def __init__(self, parent, controller, text, command):
        super().__init__(parent)
        tk.Button(self, text=text, command=command).pack()  # Next page button.
        for btn in (tk.Button(self, bg=self.bg_color, **opts) for opts in self.BTN_OPTS):
            btn.pack()


class StartPage(BasePage, bg_color='pink'):
    def __init__(self, parent, controller):
        super().__init__(parent, controller, text='SecondPage',
                         command=lambda: controller.show_frame(SecondPage))


class SecondPage(BasePage, bg_color='green'):
    def __init__(self, parent, controller):
        super().__init__(parent, controller, text='StartPage',
                         command=lambda: controller.show_frame(StartPage))


app = Tkinter()
app.mainloop()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM