我有一个程序,我正在无休止地编辑,每次我做一个更改,我必须退出并重新启动。我希望能够重新加载并重新启动,而不必退出并再次返回。
这是一个名为reload.py的文件中的一个简单程序,如果有可用的reexec,它会做我想要的:
from tkinter import *
class Application(Frame):
def do_load(self):
print("time to reload")
reexec("reload.py")
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.my_reload = Button(self)
self.my_reload["text"] = "Reload",
self.my_reload["command"] = self.do_load
self.my_reload.pack()
if __name__ == '__main__':
root = Tk()
app = Application(master=root)
app.mainloop()
我尝试使用以下方法重新加载Application类:
def do_load(self):
print("time to reload")
reload(Application)
我收到错误:
File "python3.4/importlib/__init__.py", line 122, in reload
raise TypeError("reload() argument must be module")
TypeError: reload() argument must be module
这是一个RTFM案例,我需要了解更多有关模块的内容吗?
任何帮助或建议都会很乐意接受。
答案 0 :(得分:3)
为了在这里使用<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Private Area</title>
</head>
<body>
<h1>Home</h1>
<h2>Welcome <?php echo $username; ?>!</h2>
<a href="home/logout">Logout</a>
<div>
<button type="button" class="btn">Load Book</button>
</div>
</body>
</html>
,你必须重新加载主模块,并杀死TK实例,这样python就可以用编辑过的源代码重新创建另一个模块。
<强> main.py 强>
importlib
<强> executor.py 强>
from tkinter import *
class Application(Frame):
def __init__(self, master=None, on_reload=None):
Frame.__init__(self, master)
self.pack()
self.my_reload = Button(self)
self.my_reload["text"] = "Reload",
self.my_reload["command"] = on_reload
self.my_reload.pack()
答案 1 :(得分:0)
我刚刚完成了我正在创建的工作项目的确切问题。 Gal Ben David提供的答案确实有效,但仅适用于具有单个窗口的应用程序。这是因为tkinter.Tk
根是在应用程序初始化时创建的,而不是在脚本底部的条件语句下创建的。因此,user1683793支持将其放置在正确的位置。只要root
是全局变量,您就可以轻松地使用它生成具有相同根对象的其他窗口。
以下面的项目根目录为例。
root/ ├── __main__.py └── myapp/ ├── __init__.py ├── core.py ├── datatypes.py └── gui.py
忽略__init__.py
软件包的myapp
文件,您有两个模块
您的应用程序的用户界面:保存主窗口的myapp.core
模块和保存核心容器使用的所有组件的myapp.gui
模块。看到更改类意味着更改模块本身,重新加载整个模块更有意义,因此始终在运行时使用最新版本。
要在不重新启动程序的情况下重新加载应用程序GUI,使用的过程必须:
importlib.reload
)root / myapp / core.py
首先,您需要一个与其他GUI组件完全隔离的容器类。最好开始并能够返回到空的默认状态,这是在编辑root/myapp/gui.py
后尝试重新加载其组件时将保留的状态。此默认状态将仅保留菜单栏,所有重新加载命令都存储在运行时菜单中。
from tkinter import *
#Import commented out until live editing has been completed
#from myapp import gui, datatypes
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master=master)
self.build_vars() #Builds all Tk variables used by this frame
self.build_menus() #Builds Application Menus
self.container = Frame(self) #Container for all Application Components
self.build_components() #Builds GUI Components
...
def build_menus(self):
"""
Builds the application menus bar.
"""
self.menubar = Menu(self) #Menu Bar
filemenu = Menu(self.menubar, tearoff=0) #File Menu
self.menubar.add_cascade(label="File", menu=filemenu) #Adds File Menu to Menu Bar
runtimemenu = Menu(self.menubar, tearoff=0) #Runtime Menu - Location of All
runtimemenu.add_command(label="Reload Window", command=self.reload_window)
runtimemenu.add_command(label="Reload Components", command=self.reload_components)
runtimemenu.add_separator()
runtimemenu.add_command(label="Reload Datatypes", command=self.reload_datatypes)
self.menubar.add_cascade(label="Runtime", menu=runtimemenu)
self.master.config(menu=self.menubar)
def build_components(self):
"""
Builds the application components.
"""
#Keep all imports to reloadable dependencies in
#the local scope to use the most recent version
from myapp.gui import AppFrame
self.pane = AppFrame(self.container)
self.pane.pack()
...
self.container.pack()
def build_vars(self):
"""
Build all Tk variables (BooleanVar, DoubleVar, IntVar, StringVar) used by the application.
"""
...
def reload_window(self):
"""
Reloads the entire Application frame.
Use when making minor changes to this class to update the Application instance at runtime.
"""
from myapp import core
from importlib import reload
core = reload(core) #Reload the current module to reflect any changes
cls = getattr(app, type(self).__name__) #Get Application from reloaded module
frame = cls(self.master)
self.destroy() #Destroy instance of the old application before packing the new instance
frame.pack()
def reload_components(self):
"""
Reloads all components used in the Application frame.
Use when making changes to the classes of GUI components to
update the application GUI at runtime.
"""
from myapp import gui
from importlib import reload
reload(gui) #Reload the current module to reflect any changes
#Destroy the old GUI Components
for widget in self.container.winfo_children():
widget.destroy()
self.build_components() #Rebuild the GUI components using the reloaded module
def reload_datatypes(self):
"""
Reloads a user module named `datatypes` .
This command is useful for reflecting major and
minor changes to the target module at runtime.
"""
from myapp import datatypes
from importlib import reload
reload(datatypes)
reload_window
和reload_components
方法分别重新加载应用程序的核心模块和组件模块。
reload_window
,然后在重新加载的myapp.core
中搜索与当前应用程序类同名的类,从新类中创建一个新对象,并销毁旧的应用程序窗口。
reload_components
从基础容器中删除所有组件,然后重新运行build_components
。由于import语句是在build_components
中声明的,因此它将始终使用最新版本的重载模块。
root / myapp / gui.py
此模块将包含myapp.core
模块使用的所有GUI组件。
from tkinter import *
class AppFrame(Frame):
def __init__(self, master=None):
Frame.__init__(self, master=master)
...
root / __ main __。py
最后,在目录顶部使用__main__.py
,您可以运行应用程序并重新加载应用程序的特定部分,而不必完全重新启动程序。明确引用的唯一用户代码是启动时的myapp.core.Application
。
from tkinter import Tk
#Module Setup Code
...
def create_application(master):
#Keep all imports to reloadable dependencies in
#the local scope to use the most recent version
from myapp.core import Application
return Application(master=root)
if __name__ == '__main__':
root = Tk()
app = create_application(root)
app.mainloop()
注意:为使此操作尽可能有效,应将所有导入保留到重新加载的模块中,这些模块本地化为使用它们的功能,直到完成编辑这些模块为止。取决于您使用的加载程序,以这种方式重新加载模块多次(如果是全局导入的话)可能会使同一模块的多个版本在整个程序中徘徊。在本地导入时,除sys.modules
中的模块之外,没有保留对模块的有效引用。重新加载期间替换的所有未引用模块都可以用于垃圾回收。
如果您无法做到这一点,一种解决方法是包括一个函数force_update(self, module)
,该函数搜索旧模块版本中对内容的引用,并使用映射到最新模块的引用创建一个新对象模块的版本。为此,甚至不必寻找旧模块。检查功能是否最新可以很简单。
def force_update(self, module):
func = self.attr_func
same = func.__module__ == module.__name__
newfunc = getattr(module, func.__name__)
if same and func != newfunc:
self.attr_func = newfunc
return self