警告用户未保存的更改将丢失
Warning users that unsaved changes will be lost
我的应用由一个大框架 (cp2) 和两个较小的框架组成:顶部框架包含允许用户在应用的不同屏幕之间切换的按钮,底部框架包含屏幕本身 (还有框架)。我一开始就初始化了所有的屏幕,然后使用它们的提升方法在它们之间切换。
事实是,如果用户单击一个按钮转到另一个屏幕,则在当前屏幕所做的未保存更改将会丢失。我想显示带有确认消息的警告,但我只看到两种触发警告的方法,我似乎无法弄清楚如何实现。
选项 1 是向 exibirTela 方法添加警告:如果用户单击调用此方法的按钮,该方法将在提升另一帧之前将屏幕上的值与数据库进行比较。问题是,该方法知道哪个框架即将被提升 (t),但它不知道现在哪个框架在顶部(用户即将离开的屏幕)。
选项 2 是在单独的方法上发出警告,由框架失去其顶部位置的事件触发。这个选项的问题是我似乎不存在这样的事件来绑定方法。
任何一个都适合我。当然,除非我有理由不使用它们,在这种情况下,请随时告诉我。当然,也欢迎任何其他建议。
代码如下。对不起,葡萄牙语的名字。我添加了一些英文评论:希望这有帮助。
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...
我想我为您的第一个选项找到了部分解决方案。有一个方法叫做 winfo_children
, and according to the effbot.org
文档:
Returns a list containing widget instances for all children of this widget. The windows are returned in stacking order from bottom to top.
现在,我创建了一个示例来说明此机制的工作原理。该示例基于 "famous" Bryan Oakley's program on how to switch between frames. I have added a Text
widget which displays the stacking order of self.container
. To do this I have used the winfo_children
method mentioned above, which returns the widgets of self.container
in stacking order. The top of the stack (the visible widget) is the last element of this list (as mentioned above), but I have used reversed
函数将其显示为第一个元素。
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()
当您更改帧时,您肯定会注意到 Text
小部件的输出的堆栈顺序发生了变化。 (请注意 Text
小部件永远不会位于顶部)。所以,现在你知道哪个框架在容器的顶部,你可以继续你第一个选项的想法。
如果您想了解更多关于 "lifts" 和 "lowers" 在 tkinter 中如何工作的信息,请查看 Tk 的官方文档,其中包含专门针对此主题的部分:
代码不在 Python 中,因为 Tk 是一个 Tcl 库,但这些概念也适用于 tkinter。
我的应用由一个大框架 (cp2) 和两个较小的框架组成:顶部框架包含允许用户在应用的不同屏幕之间切换的按钮,底部框架包含屏幕本身 (还有框架)。我一开始就初始化了所有的屏幕,然后使用它们的提升方法在它们之间切换。
事实是,如果用户单击一个按钮转到另一个屏幕,则在当前屏幕所做的未保存更改将会丢失。我想显示带有确认消息的警告,但我只看到两种触发警告的方法,我似乎无法弄清楚如何实现。
选项 1 是向 exibirTela 方法添加警告:如果用户单击调用此方法的按钮,该方法将在提升另一帧之前将屏幕上的值与数据库进行比较。问题是,该方法知道哪个框架即将被提升 (t),但它不知道现在哪个框架在顶部(用户即将离开的屏幕)。
选项 2 是在单独的方法上发出警告,由框架失去其顶部位置的事件触发。这个选项的问题是我似乎不存在这样的事件来绑定方法。
任何一个都适合我。当然,除非我有理由不使用它们,在这种情况下,请随时告诉我。当然,也欢迎任何其他建议。
代码如下。对不起,葡萄牙语的名字。我添加了一些英文评论:希望这有帮助。
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...
我想我为您的第一个选项找到了部分解决方案。有一个方法叫做 winfo_children
, and according to the effbot.org
文档:
Returns a list containing widget instances for all children of this widget. The windows are returned in stacking order from bottom to top.
现在,我创建了一个示例来说明此机制的工作原理。该示例基于 "famous" Bryan Oakley's program on how to switch between frames. I have added a Text
widget which displays the stacking order of self.container
. To do this I have used the winfo_children
method mentioned above, which returns the widgets of self.container
in stacking order. The top of the stack (the visible widget) is the last element of this list (as mentioned above), but I have used reversed
函数将其显示为第一个元素。
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()
当您更改帧时,您肯定会注意到 Text
小部件的输出的堆栈顺序发生了变化。 (请注意 Text
小部件永远不会位于顶部)。所以,现在你知道哪个框架在容器的顶部,你可以继续你第一个选项的想法。
如果您想了解更多关于 "lifts" 和 "lowers" 在 tkinter 中如何工作的信息,请查看 Tk 的官方文档,其中包含专门针对此主题的部分:
代码不在 Python 中,因为 Tk 是一个 Tcl 库,但这些概念也适用于 tkinter。