Tkinter的嵌套类工厂 [英] Nested Class factory with tkinter

查看:94
本文介绍了Tkinter的嵌套类工厂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to build a script for import in my future projects. That Script should create some tk.Frames in a tk.Frame and let me edit the created ones in a main.

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

I think, the best way to get there is to create a Holder_frame class and put some nested classes in. so I could call them in my main with Holder_frame.F1. I tried a lot of code and I ended up here making me an account. Anyway here is where Im at:

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()

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

Everything works fine, till I try to call a Frame. (AttributeError 'Holder_frame' object has no attribute 'F1') I think my problem is the structure but need some help to solve it.

有什么建议吗?

推荐答案

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

If I'm getting it right I think you mean to have some sort of a Base class that has some configuration which a set of frames have in common like for example you want to have 10 frames of 300x400 geometry and of a brown background in common and later having another set of frames with a different configuration, which can be accessed in an organised way. Then I would say you have an interesting way but I would rather use a list or a dictionary anyway.

以下是实现此目标的一些方法.

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

In this approach, I've created a function that returns a dictionary with all the frames created and contained in it like in format ({..., '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().只需根据需要修改此类.


Approach 2

Here I've used class and objects. Where I made this class Frames though you can name it anything you want. I also added some important method like cget() and configure(), through these methods once get a value to an option and configure options for all the frames respectively. There are more useful methods like bind() and bind_all() if you need those just modify this class as per your need.

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

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


Approach 3

If you want to have differently configured frames contained in one class, where all those frames have some method in common or some attribute in common.

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试图实现的方法.


Approach 4

This is the approach that OP is trying to achieve.

import tkinter as tk


class BaseClass(tk.Frame):
    def __init__(self, master, cnf={}, **kw):
        kw = tk._cnfmerge((cnf, kw))
        cnf = [(i, kw.pop(i, None))
               for i in ('pack', 'grid', 'place') if i in kw]
        tk.Frame.__init__(self, master, **kw)
        self.master = master

        if cnf:
            self.__getattribute__(cnf[-1][0])(cnf=cnf[-1][1])


class Container(tk.Frame):
    """Container class which can contain tkinter widgets. 
    Geometry (pack, grid, place) configuration of widgets 
    can also be passed as an argument.

    For Example:-

    >>> Container(root, widget=tk.Button,
              B5=dict(width=30, height=40, bg='black',
                      fg='white', pack=(), text='Button1'),
              B6=dict(width=50, height=10, bg='green', text='Button2',
                      place=dict(relx=0.5, rely=1, anchor='s')))
    """
    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():
            geo = [(i, cnf.pop(i, None))
                   for i in ('pack', 'grid', 'place') if i in cnf]
            setattr(Container, name, wid(master, cnf))
            if geo:
                manager, cnf2 = geo[-1]
                widget = getattr(Container, name)
                getattr(widget, manager)(cnf=cnf2)


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

    Container(root, widget=Container.BaseClass,
              F1=dict(width=30, height=40, bg='black', relief='sunken',
                      pack=dict(ipadx=10, ipady=10, fill='both'), bd=5),
              F2=dict(width=50, height=10, bg='green',
                      pack=dict(ipadx=10, ipady=10, fill='both')),
              )

    Container(root, widget=tk.Button,
              B5=dict(width=30, height=40, bg='black',
                      fg='white', pack={}, text='Button1'),
              B6=dict(width=50, height=10, bg='green', text='Button2',
                      place=dict(relx=0.5, rely=1, anchor='s')),
              )

    print(Container.__dict__)
    root.mainloop()


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


A lot can be done and can be modified according to one's needs, these are just some approaches that I think will work very well to automate and keep a set of frames in shape and together.

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

这篇关于Tkinter的嵌套类工厂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆