python-3.x - 使用 tkinter python3 构建应用程序/重新导入模块
问题描述
我正在重组这个问题:我觉得最初的问题很啰嗦,对其他人没有特别的帮助。
我有一个直接运行的主应用程序(运行为__main__)。我有一个只希望用作导入的模块(simple_module.py)。我意识到如果需要(通过 if__name__)我可以独立运行它,并将其包含在模块代码中,仅用于演示。
当用户按下 main.py 的“开始”按钮时,它应该打开一个新的顶层窗口,其中包含来自 simple_module 的所有类和小部件,它们都在一个名为 Page 的类中。(同时主应用程序窗口保持打开状态。)
我希望每次按下按钮时都重新导入(或等效)模块。当按下模块的“关闭”按钮或 X 时,我希望它关闭该窗口。main.py 的窗口在整个过程中保持打开状态,按下按钮需要无限地重新打开该模块窗口,就像一个循环一样。
我添加了 if name == ' main ' 只是为了强调我理解它的作用(它通常在我的所有 main.py 应用程序中)以及为什么我仍然无法获得我想要的结果。据我所见,它并没有改变任何东西,我现在只是导入类,但仍然无法识别“新”。这与前面的示例中的问题相同。
我有 main.py
import tkinter as tk
# audio module works as expected
import audio_module as am
# I want this window to open and close on command
import simple_module as sm
class GUI(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
#self.new = tk.Toplevel(self) # auto loads a second, unwanted window
self.session_counter = 0
self.start_btn = tk.Button(root, text="start", command=self.start)
self.start_btn.grid(row=4,column=0,sticky="nsew",pady=30, padx=30, ipady=18)
def start(self):
am.spell() # these audio imports work like a charm, every btn press - single functions call OK
self.session_counter += 1
print(self.session_counter)
#import simple_module - if used here, my usual 'illegal' import style (works great, once only,
# unless in same script as __main__ in which case all re-imports work fine)
# Import attempts
#import simple_module as sm
#page = Page(new) # Page not defined
#sm.Page() #missing parent arg (new)
# error: 'new' not defined
#sm.Page(new)
if __name__ == '__main__':
print('running as __main__')
root = tk.Tk()
#sm.Page = tk.Toplevel(new) # a desperate attempt NO
#page = sm.Page(tk.TopLevel) NO
# qualify and USE module here! sm is not required if you use 'from simple_module import Page' !!
page = sm.Page(root)
#page.pack(fill='both', expand=True)
page.grid(row=0,column=0,sticky='nsew')
main = GUI(root)
root.mainloop()
最后,我们有 simple_module.py:
import tkinter as tk
import audio_module as am
# this module works exactly as expected IF run directly...
class Page(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
# super().__init__(*args, **kwargs)
self.back_btn = tk.Button(parent, text="close", command=self.back)
self.back_btn.grid(row=4,column=0,sticky="nsew",pady=30, padx=30, ipady=18)
def back(self):
am.click()
# close this page BUT have it ready to re-open IF user re-presses button.
new.destroy()
if __name__ == "__main__":
print('running as __main__ directly')
new = tk.Tk()
#new = tk.Toplevel() # this loads an unwanted additional blank window. IF run directly.
page = Page(new)
# the missing line to self contain module!
#page.pack(fill='both', expand=True)
page.grid(row=0,column=0,sticky='nsew')
new.mainloop()
else:
print('running as import with __name__ ==',__name__)
感谢您的耐心和回复。我重新研究了如果您提供了链接的主要指南,它再次确认了我已经相信我知道的内容。当我只想打开一个框架并在它们之间切换时,那里有一个有用的示例,但在这种情况下,我希望主窗口在调用模块窗口时保持打开状态。
解决方案
您面临的问题是您的Page
课程不是为了可重用而编写的。它依赖于全局变量和关于调用它的代码的知识。为了可重用,Page
该类需要是自包含的。
简而言之,这意味着由创建的每个小部件都Page
必须在Page
类中,而不是在根窗口中。此外,创建 的实例的代码Page
需要负责在实例上调用pack
、place
或grid
。
因此,第一步是进行修改Page
,使其可以重用。关键是要求调用者传入父窗口小部件。您正在这样做,但您没有使用传入的值。
Page
应该类似于以下代码。请注意,它明确声明parent
为第一个位置参数,并将其传递给tk.Frame.__init__
:
class Page(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.back_btn = tk.Button(parent, text="close", command=self.back)
...
请注意后退按钮如何成为parent
某个全局变量的子变量而不是某个全局变量的子变量。这是您缺少的重要步骤,这就是允许Page
自包含而不与创建它的代码紧密耦合的原因。
一旦Page
适当地自包含,创建实例的代码就有责任在实例上Page
调用pack
、place
或grid
。
例如,simple_module.py
最后可能有这个块:
if __name__ == "__main__":
new = tk.Tk()
page = Page(new)
page.pack(fill="both", expand=True)
new.mainloop()
在 main.py 中,由于您是simple_module
作为一个整体导入的,因此您需要完全限定以下内容的使用Page
:
import simple_module as sm
...
root = tk.Tk()
page = sm.Page(root)
page.pack(fill="both", expand=True)
root.mainloop()
或者,您可以只导入Page
并省略sm.
:
from simple_module import Page
...
page = Page(root)
...
注意一个文件可以使用root
和一个可以使用的方式new
,但是您的代码在任何一种情况下都可以工作,因为它不依赖于全局变量。parent
在页面内部,无论调用者如何称呼它,它都将始终是。
顺便说一句,你不必在simple_module
里面导入start
——你只需要在程序开始时导入一次。
推荐阅读
- validation - 什么是允许在文本框中使用英语和阿拉伯语以及空格的验证表达式
- highcharts - 折叠和展开父元素后,子进度指示器不起作用
- visual-studio-code - 如何分别在vs代码中编辑大文件中的特定代码部分?
- shopify - 如何从我的网上商店中删除 Shopify 徽标图标?
- php - 解析CSV时PHP相当于excel INDEX MATCH
- c - 为什么我不能使用#if 比较宏和枚举?
- python - 如何通过python中的管道访问程序的输出
- docker-compose - 如何将用户名和密码传递给私有 Docker 注册表以进行“htpasswd”身份验证
- android - 在设备上使用 nativescript angular 6 将数据从我的应用程序发布到本地 api 时出现问题
- java - 使用反射从隐藏的 AOSP 类中获取私有字段