带有tkinter的嵌套类工厂

问题描述 投票:7回答:2

我正在尝试为将来的项目构建一个导入脚本。该脚本应该在tk.Frame中创建一些tk.Frame,然后让我在main

中编辑创建的tk.Frame。

我认为,到达那里的最好方法是创建Holder_frame类,并放入一些嵌套类。因此我可以在Holder_frame.F1中的main中调用它们。我尝试了很多代码,最终在这里给了我一个帐户。无论如何,我在这里:

import tkinter as tk
from tkinter import Frame,Button

class BaseClass(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master
        self.pack()


class Holder_frame(tk.Frame):
    Names = []
    def __init__(self, master, frames=2):
        tk.Frame.__init__(self, master)
        self.master = master
        frame_names = Holder_frame.Names
        for i in range(0,frames):
            frame_names.append("F"+str(i+1))
        print(frame_names)
        Holder_frame.factory()
    def factory():
        print(Holder_frame.Names)
        print(type(BaseClass))
        for idex,i in enumerate (Holder_frame.Names):
            print(i)
            class NestedClass(BaseClass):
                pass

            NestedClass.__name__ = i
            NestedClass.__qualname__ = i

if __name__ == "__main__":
    root = tk.Tk()
    def raise1():
        Holder_frame.F1.tkraise()
    def raise2():
        Holder_frame.F2.tkraise()

    holder=Holder_frame(root,frames=2)
    holder.grid(row=1,column=0)
    b1 = tk.Button(root, text='1', command=raise1)
    b1.grid(row=0,column=0)
    b2 = tk.Button(root, text='2', command=raise2)
    b2.grid(row=0,column=1)


    root.mainloop()

一切正常,直到我尝试调用框架为止。(AttributeError'Holder_frame'对象没有属性'F1')我认为我的问题是结构,但需要一些帮助来解决。

有什么建议吗?enter image description here

为了弄清楚我要寻找的内容,我在图片中编写了这样的程序。许多框架可以构建和变形,这就是我要自动化的东西。

python tkinter factory inner-classes
2个回答
1
投票

如果我做对了,我想您的意思是拥有某种具有某种配置的Base class,这些配置具有一组帧的共同点,例如,您想拥有10帧300x400 几何图形,并且具有棕色背景,并且以后又具有另一组具有不同配置的帧,可以通过有组织的方式进行访问。然后我会说您有一种有趣的方式,但是无论如何我还是希望使用列表或字典。

这里有一些方法可以实现这一目标。

方法1

用这种方法,我创建了一个函数,该函数返回一个字典,其中包含所有创建并包含在其中的帧,如格式({..., 'F20': tkinter.frame, ...}

import tkinter as tk

def get_base_frames(num, master, cnf={}, **kw):
    """
    Create list of frames with common configuration options.

    Args:
        num (int): Number of frames to be created.
        master (tk.Misc): Takes tkinter widget or window as a parent for the frames.
        cnf (dict): configuration options for all the frames.
        kw: configuration options for all the frames.

    Return:
        Dictionary of frames ({..., 'F20': tkinter.frame, ...}).
    """
    return {f'F{n+1}': tk.Frame(master, cnf=cnf, **kw) for n in range(num)}

if __name__ == "__main__":
    root = tk.Tk()
    frame_holder = get_base_frames(10, root, width=50, height=50, bg='brown')

    # Frames can be accessed through their names like so.
    print(frame_holder.get('F1'))

方法2

这里我使用了类和对象。我在哪里开设了此类Frames,但您可以为它命名。我还添加了一些重要的方法,例如cget()configure(),通过这些方法,一旦获得一个选项的值并分别为所有帧配置选项即可。 如果需要,还有一些更有用的方法,例如bind()bind_all(),只需根据需要修改此类。

import tkinter as tk

class Frames(object):
    def __init__(self, master=None, cnf={}, **kw):
        super().__init__()
        num = cnf.pop('num', kw.pop('num', 0))
        for n in range(num):
            self.__setattr__(f'F{n+1}', tk.Frame(master, cnf=cnf, **kw))

    def configure(self, cnf={}, **kw):
        """Configure resources of a widget.

        The values for resources are specified as keyword
        arguments. To get an overview about
        the allowed keyword arguments call the method keys.
        """
        for frame in self.__dict__:
            frame = self.__getattribute__(frame)
            if isinstance(frame, tk.Frame):
                if not cnf and not kw:
                    return frame.configure()
                frame.configure(cnf=cnf, **kw)
    config = configure

    def cget(self, key):
        """Return the resource value for a KEY given as string."""
        for frame in self.__dict__:
            frame = self.__getattribute__(frame)
            if isinstance(frame, tk.Frame):
                return frame.cget(key)
    __getitem__ = cget


if __name__ == "__main__":
    root = tk.Tk()
    frame_holder = Frames(root, num=10, width=10, 
                          bd=2, relief='sunken', bg='yellow')

    # Frames can be accessed through their naems like so.
    print(frame_holder.F4) 
    print(frame_holder['bg'])
    frame_holder.config(bg='blue')
    print(frame_holder['bg'])

方法3

如果要具有在一个类中包含不同配置的框架,,其中所有这些框架都具有某种共同的方法或某种属性。

import tkinter as tk

class BaseFrame(tk.Frame):
    def __init__(self, master=None, cnf={}, **kw):
        super().__init__(master=master, cnf={}, **kw)

    def common_function(self):
        """This function will be common in every 
        frame created through this class."""
        # Do something...

class FrameHolder(object):
    def __init__(self, master=None, cnf={}, **kw):
        kw = tk._cnfmerge((cnf, kw))
        num = kw.pop('num', len(kw))

        for n in range(num):
            name = f'F{n+1}'
            cnf = kw.get(name)
            self.__setattr__(name, BaseFrame(master, cnf))

if __name__ == "__main__":
    root = tk.Tk()

    holder = FrameHolder(root, 
                    F1=dict(width=30, height=40, bg='black'),
                    F2=dict(width=50, height=10, bg='green'),
                    F3=dict(width=300, height=350, bg='blue'),
                    F4=dict(width=100, height=100, bg='yellow'),
                    )
    print(holder.F1)
    print(holder.__dict__)

方法4

这是OP试图实现的方法。

import tkinter as tk

class BaseClass(tk.Frame):
    def __init__(self, master, cnf={}, **kw):
        tk.Frame.__init__(self, master, cnf=cnf, **kw)
        self.master = master
        self.pack(ipadx=10, ipady=10, fill='both')


class Container(tk.Frame):
    BaseClass = BaseClass

    def __init__(self, master=None, cnf={}, **kw):
        kw = tk._cnfmerge((cnf, kw))
        wid = kw.pop('widget', tk.Frame)
        for name, cnf in kw.items():
            setattr(Container, name, wid(master, cnf))


if __name__ == "__main__":
    root = tk.Tk()

    Container(root, widget=Container.BaseClass,
            F1=dict(bg='black', relief='sunken', bd=2),
            F2=dict(bg='green', relief='sunken', bd=2),
            )

    Container(root, widget=tk.Button,
            B5=dict(text='Button 1', width=10, height=5, fg='blue'),
            B6=dict(text='Button 2', width=10, height=5, fg='green'),
            )

    Container.B5.pack()
    Container.B6.pack()

    root.mainloop()

[可以做很多事情,也可以根据自己的需要进行修改,这些只是我认为可以很好地实现自动化并使一组框架保持形状和在一起的一些方法。

有多种方法可以做到这一点,或者有比这些更好,更高效的方法,可以随时提出建议并分享新知识。


1
投票

我想这个问题的一种解决方案,因为我不完全理解您的问题,但这是我的解决方案:

import tkinter as tk
from tkinter import Frame,Button

class BaseClass(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master
        self.pack()


class Holder_frame(tk.Frame):
    def __init__(self, master, frames=2):
        tk.Frame.__init__(self, master)
        self.master = master
        self.frame_names = []
        for i in range(frames):
            Holder_frame.create_frames("F"+str(i+1), self)

    @classmethod
    def create_frames(cls, name, master):
        setattr(cls, name, tk.Frame(master))

if __name__ == "__main__":
    root = tk.Tk()
    def raise1():
        print(type(Holder_frame.F1))
    def raise2():
        print(type(Holder_frame.F2))

    holder=Holder_frame(root,frames=2)
    holder.grid(row=1,column=0)
    b1 = tk.Button(root, text='1', command=raise1)
    b1.grid(row=0,column=0)
    b2 = tk.Button(root, text='2', command=raise2)
    b2.grid(row=0,column=1)
    print(Holder_frame.__dict__.items())

    root.mainloop()

setattr的使用允许将变量添加到类中,就像您在代码中键入一个函数一样。这使您可以从类外部访问框架,就像访问“全局变量”]

我也使用文件来测试它是否也可以作为导入模块在外部工作:

# main.py
from nested_class import Holder_frame
import tkinter as tk

root = tk.Tk()
holder=Holder_frame(root,frames=1000)
holder.grid(row=1,column=0)
print(Holder_frame.__dict__.items())

root.mainloop()

我希望这能回答您的问题,

詹姆斯

编辑:

[在思考之后,我认为是要为您想要的内容提供一个更清洁的系统。使用this post中的代码,您可以看到您的书面系统可以替换为ttk.Notebook,并且通过使用style.layout('TNotebook.Tab', [])删除顶部栏,您可以看到您将获得一个具有框架的框架小部件其中的小部件:

import tkinter as tk
import tkinter.ttk as ttk

class multiframe_example:
    def __init__(self, master):
        self.master = master

        style = ttk.Style()
        style.layout('TNotebook.Tab', [])   
        notebook = ttk.Notebook(self.master)
        notebook.grid(row=0, column=0)

        self.master.grid_rowconfigure(0, weight=1)
        self.master.grid_columnconfigure(0, weight=1)

        tab1 = tk.Frame(self.master,  width=500, height=500, background="green")
        tab2 = tk.Frame(self.master,  width=500, height=500)
        tab3 = tk.Frame(self.master,  width=500, height=500)


        notebook.add(tab1)
        notebook.add(tab2)
        notebook.add(tab3)

        notebook.select(0) # select tab 1
        notebook.select(1) # select tab 2
        notebook.select(2) # select tab 3

def main():
    root = tk.Tk()
    root.geometry("500x500")
    multiframe_example(root)
    root.mainloop()

if __name__ == '__main__':
    main()

希望此代码可以为您提供支持,并且可以满足您的要求!

© www.soinside.com 2019 - 2024. All rights reserved.