Python 警告用户未保存的更改将丢失
我的应用程序由一个大框架(cp2)组成,其中包含两个较小的框架:顶部框架包含允许用户在应用程序的不同屏幕之间切换的按钮,底部框架包含屏幕本身(也包括框架)。我在开始时初始化所有屏幕,然后使用提升方法在它们之间切换 问题是,如果用户单击一个按钮转到另一个屏幕,在当前屏幕上所做的未保存更改将丢失。我想显示一个带有确认消息的警告,但我只看到触发警告的两种方式,我似乎不知道如何实现这两种方式 选项1将向exibirTela方法添加警告:如果用户单击调用此方法的按钮,该方法将在提升另一帧之前将屏幕上的值与数据库进行比较。问题是,该方法知道哪一帧将被提升(t),但它不知道哪一帧现在位于顶部(用户将要离开的屏幕) 选项2是通过一个单独的方法发出警告,由框架失去顶部位置的事件触发。这个选项的问题是,似乎不存在这样的事件来将方法绑定到它 任何一个都适合我。当然,除非我有理由不使用它们,在这种情况下,请随时告诉我。当然,我们也欢迎任何其他建议 代码如下。对不起,葡萄牙语的名字。我用英语补充了一些评论:希望这有帮助Python 警告用户未保存的更改将丢失,python,tkinter,frame,Python,Tkinter,Frame,我的应用程序由一个大框架(cp2)组成,其中包含两个较小的框架:顶部框架包含允许用户在应用程序的不同屏幕之间切换的按钮,底部框架包含屏幕本身(也包括框架)。我在开始时初始化所有屏幕,然后使用提升方法在它们之间切换 问题是,如果用户单击一个按钮转到另一个屏幕,在当前屏幕上所做的未保存更改将丢失。我想显示一个带有确认消息的警告,但我只看到触发警告的两种方式,我似乎不知道如何实现这两种方式 选项1将向exibirTela方法添加警告:如果用户单击调用此方法的按钮,该方法将在提升另一帧之前将屏幕上的值与
from tkinter import *
from tkinter.messagebox import *
import sqlite3
class cp2(Frame):
def __init__(self, *args, **kwargs):
Frame.__init__(self, *args, **kwargs)
root.title(string="Controle de Produtos e Pedidos")
self.columnconfigure(0, weight=1)
self.rowconfigure(1, weight=1)
# Here I create two frames: one for the buttons, one for the screens
barraOpcoes = Frame(self, bd=10, bg="yellow")
barraOpcoes.grid(row=0, column=0, sticky=EW)
barraOpcoes.columnconfigure(2, weight=1)
areaPrincipal = Frame(self, bd=10)
areaPrincipal.grid(row=1, column=0, sticky=NSEW)
areaPrincipal.columnconfigure(0, weight=1)
# Here I create a set (telasAplicacao) to which I'll add all the screens (frames)
self.telasAplicacao = {}
for tela in (telaInicial,
telaCrudFabricante, telaCrudRevista,
telaInclusaoFabricante, telaInclusaoRevista,
telaAlteracaoFabricante, telaAlteracaoRevista):
novaTela = tela(areaPrincipal, self)
self.telasAplicacao[tela] = novaTela
novaTela.grid(row=0, column=0, sticky=NSEW)
# Here I add the buttons that switch between frames
btInicio = Button(barraOpcoes, text="Início", command=self.exibirTelaInicial)
btInicio.grid(row=0, column=0, sticky=W)
btFabricantes = Button(barraOpcoes, text="Fabricantes", command=self.exibirTelaCrudFabricante)
btFabricantes.grid(row=0, column=1, sticky=W)
btRevistas = Button(barraOpcoes, text="Revistas", command=self.exibirTelaCrudRevista)
btRevistas.grid(row=0, column=2, sticky=W)
btSair = Button(barraOpcoes, text="SAIR", command=lambda: sairCP2(self))
btSair.grid(row=0, column=3, sticky=E)
# Always start with telaInicial on top: this is the welcome screen
self.exibirTelaInicial()
def exibirTela(self, t):
minhaTela = self.telasAplicacao[t]
minhaTela.exibirDados(codigo=kwargs["codigo"])
minhaTela.lift()
def exibirTelaInicial(self):
self.exibirTela(telaInicial)
def exibirTelaCrudFabricante(self):
self.exibirTela(telaCrudFabricante)
def exibirTelaCrudRevista(self):
self.exibirTela(telaCrudRevista)
class telaAlteracaoFabricante(Frame):
def __init__(self, parent, controller):
# lots of widgets on this frame
# also, all the other methods of this class
# and so on to all the other frames in telasAplicacao...
我想我为你的选择一找到了部分解决方案。有一个名为的方法,根据文档,它是: 返回包含此小部件所有子部件的小部件实例的列表。窗口按从下到上的堆叠顺序返回 现在,我创建了一个示例,展示了这种机制是如何工作的。该示例基于。我添加了一个
Text
小部件,它显示self.container
的堆叠顺序。为此,我使用了上面提到的方法,它以堆叠顺序返回self.container
的小部件。堆栈顶部(可见小部件)是这个列表的最后一个元素(如上所述),但我已经使用函数将其显示为第一个元素
import tkinter as tk
from tkinter import ttk
LARGE_FONT= ("Verdana", 12)
class MainWindowController(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.container = tk.Frame(self)
self.container.grid_rowconfigure(0, weight=1)
self.container.grid_columnconfigure(0, weight=1)
self.container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
frame = F(self.container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.so = tk.Text(self.container, state="disabled", font=("Arial", 14),
width=70, height=12, bd=1, relief="groove")
self.so.grid(row=1, column=0, sticky="nsew", pady=(60, 0))
self.update_stack_order()
self.show_frame(StartPage)
def update_stack_order(self):
self.so.config(state="normal")
self.so.delete("1.0", "end")
self.so.insert("end", "Stack order of the widgets of self.container (0 means top)\n\n\n")
for i, child in enumerate(reversed(self.container.winfo_children())):
self.so.insert("end", str(i) + "\t" + repr(child) + "\n\n")
self.so.config(state="disabled")
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
self.update_stack_order()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="START PAGE", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button = ttk.Button(self, text="Visit Page 1",
command=lambda: controller.show_frame(PageOne))
button.pack()
button2 = ttk.Button(self, text="Visit Page 2",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="PAGE ONE", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = ttk.Button(self, text="Page Two",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="PAGE TWO", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = ttk.Button(self, text="Page One",
command=lambda: controller.show_frame(PageOne))
button2.pack()
app = MainWindowController()
app.mainloop()
在更改帧时,您肯定会注意到文本
小部件的输出改变了堆栈顺序。(请注意,文本
小部件从不在顶部)。现在,您知道哪个框架位于容器顶部,您可以继续第一个选项的想法
如果您想了解更多关于tkinter中“提升”和“降低”的工作原理,请查阅Tk的官方文档,其中包含一节专门介绍此主题:
代码不是用Python编写的,因为Tk是一个Tcl库,但这些概念也应用于tkinter。通过询问用户的所有退出条件!这里的方法:谢谢你的回复,但我的问题不是“X”按钮:我已经用WM_DELETE_窗口协议覆盖了这个按钮,正如你提供的链接所建议的那样。我需要跟踪的事件是帧的提升/降低:当用户通过exibirTela方法从一个帧切换到另一个帧时,我需要在提升下一个帧之前检查当前帧上是否进行了更改。为什么你说切换页面时页面上的所有数据都将丢失?如果不销毁小部件,数据应该仍然存在。我在你的代码中没有看到任何会破坏数据的东西。是的,从技术上讲,数据仍然存在,但是如果用户试图返回到该页面单击“保存”按钮,exibirDados方法将用数据库中当前的值替换它,因此当框架被提升时,任何未保存的更改都将丢失。谢谢!这就是我所需要的:现在我知道哪个帧将要失去“当前屏幕”状态,我可以检查是否有未保存的更改并警告用户。