python - 在 Tkinter 中动态初始化文件夹中的框架

标签 python tkinter

我正在尝试将文件夹中的所有框架作为包导入,然后初始化所有这些框架,这样我就可以 raise按下按钮即可。

这是我的文件夹结构:

+-projectfolder
|--bargraphtutor.py
|-- __init__.py (empty)
|--pages
   |--startpage.py
   |-- .
   |-- .
   |--aboutpage.py
   |--__init__.py

__init__.pypages文件夹中有如下代码,用于打包所有.py该文件夹中的文件到 pages 。这是取自 this question .

from os.path import dirname, basename, isfile, join
import glob

pages = glob.glob(join(dirname(__file__), "*.py"))
__all__ = [ basename(f)[:-3] for f in pages if isfile(f) and not f.endswith('__init__.py')]
from . import *

然后我开始做 import pages as p 。问题是,每个帧都是文件中的一个类,要初始化每个帧,我必须知道每个文件的名称和类名:

示例:bargraphtutor.py 的一部分

self.frames = {}
for F in (p.aboutpage.AboutPage, p.startpage.StartPage): # This is where I'd like to make as 
    page_name = F.__name__                               # automated as possible. 
    frame = F(parent=container, controller=self)
    self.frames[page_name] = frame

    # put all of the pages in the same location;
    # the one on the top of the stacking order
    # will be the one that is visible.
    frame.grid(row=0, column=0, sticky="nsew")

示例:startpage.py:

import tkinter as tk   # python 3
from tkinter import ttk
from tkinter.ttk import Label, Button
from tkinter import font


class StartPage(tk.Frame):   # Each Frame is defined via a class

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        titleLabel= ttk.Label(self, text='This is the Startpage')
        titleLabel.pack()

那么如何导入包,然后迭代 bargraphtutor.py 中的所有框架,但不知道所有类名呢?我尽可能使用 p.__all__它返回包中所有文件的名称,但我不知道如何从那里继续。

编辑:如果我将文件命名为与类相同的名称,我会遇到命名空间问题吗?

最佳答案

概述

我解决这个问题的方法是让我的所有页面继承基类,以便我可以使用 __subclasses__函数在导入后获取所有子类的列表。要导入它们,我将使用 python 的 glob模块查找文件和 importlib模块按路径导入文件。

文件结构示例

例如,让我们从这个简单的文件夹结构开始:

.
├── main.py
└── pages
    ├── PageOne.py
    ├── PageTwo.py
    ├── __init__.py
    └── basepage.py

__init__.py 是空的,但让我们将 pages 视为一个模块。

basepage.py 定义了 BasePage 类,所有其他页面都从该类继承。它不需要太多,因为每个页面都会负责页面内的所有内容。它可能看起来像这样:

import tkinter as tk

class BasePage(tk.Frame):
    def __init__(self, master, controller):
        self.master = master
        self.controller = controller
        super().__init__(master)

PageOne.pyPageTwo.py 包含页面。它们具有相似的结构。例如,PageOne.py 可能如下所示:

import tkinter as tk
from .basepage import BasePage

class PageOne(BasePage):
    def __init__(self, parent, controller):
        super().__init__(parent, controller)
        label = tk.Label(self, text="This is page one")
        label.pack(padx=20, pady=20)

第二页是相同的,只是在明显的地方写的是“二”而不是“一”。请注意此页面是如何从 BasePage 继承的。这很重要,稍后您就会看到。

获取要导入的文件列表

您可以使用glob模块获取“pages”子目录中所有文件的列表。它看起来像这样:

import glob
for filename in glob.glob("./pages/*.py"):
    ...

如果您的实际文件夹包含页面文件和非页面文件,您可以使用命名约定,以便仅导入页面文件。例如,您可以将模式更改为 ".pages/Page*.py"

按文件名导入文件

Python 的 importlib模块具有允许我们按文件名导入文件的功能。

例如,给定 filename 中的文件名,我们可以像这样导入该文件:

import importlib.util
from pathlib import Path

path = Path(filename)
module_name = f"pages.{path.stem}"
spec = importlib.util.spec_from_file_location(module_name, path)
spec.loader.exec_module(module)

如果我们传入 ./pages/PageOne.py 之类的内容,它将加载名为 pages.PageOne 的模块。

获取页面类

导入一个或多个页面后,我们就可以使用 BasePage 类。还记得每个页面是如何继承这个类的吗?我们可以使用__subclasses__子类的方法为我们提供所有子类的列表。

将所有这些放在一起,这是一个函数,它导入“pages”子文件夹中与“Page*.py”匹配的所有文件,然后返回类:

def get_pages(self):
    for filename in glob.glob("./pages/*.py"):
        path = Path(filename)
        module_name = f"pages.{path.stem}"
        print(f"module name: {module_name}")
        spec = importlib.util.spec_from_file_location(module_name, path)
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)

    return BasePage.__subclasses__()

创建页面实例

此时,创建页面实例非常简单。你的主程序可以做这样的事情:

    self.frames = {}
    for page_class in self.get_pages():
        page = page_class(parent=page_container, controller=self)
        page_name = page_class.__name__
        self.frames[page_name] = page
        page.grid(row=0, column=0, sticky="nsew")

关于python - 在 Tkinter 中动态初始化文件夹中的框架,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60780275/

相关文章:

python - Django:没有名为 context_processors 的模块,基本 URL

Python:计算两个矩阵的相似元素

python - 为什么在 Mac 上键入括号时 Python 的 IDLE 会崩溃?

python - Tkinter : Moving more than one object at once

python - 你如何返回到元素的先前版本或在 pycharm 中创建备份保存?

python - 自定义字体的 Kivy 标签标记

python - Pandas read_csv 将字符串转换为日期时间并保存到数据库 Python

python - tkinter 在标签框架中添加小部件打破标签框架大小

python - Tkinter:尝试点击时窗口闪烁

python - Tkinter 复选框命令