提问人:Arolan 提问时间:6/9/2023 最后编辑:Trenton McKinneyArolan 更新时间:6/9/2023 访问量:73
Tkinter 应用类从不调用其析构函数
Tkinter app class never calls its destructor
问:
我在 Python 中制作 Tkinter 类时遇到问题。 我有以下类,由于我添加了图形(initStatistics 方法),我的 Tkinter 应用程序不会停止。
问题是我需要它停止才能“持久化”数据库并保存数据帧。
import tkinter as tk
from tkinter import ttk
import pandas as pd
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib import pyplot as plt
import seaborn as sns
from pandastable import Table
import database as db
sns.set_theme()
class GUI():
def __init__(self): # Constructor
print("TRACE : Constructor")
self.categories = { # true = entrant
'Choisir' : None,
'Bourses' : True,
'Remboursements' : True,
'Stage' : True,
'Virement parents' : True,
'Blablacar' : True,
'Cours particuliers' : True,
'Transports' : False,
'Loisirs' : False,
'Loyer' : False,
'Cadeaux' : False,
'Sorties' : False,
'Abonnements' : False,
'Autre' : False,
'Courses' : False,
}
self.root = tk.Tk()
self.root.title("Tableur de l'espace")
self.root.state('zoomed') # Pour maximiser à l'ouverture
# Tabs
self.tabControl = ttk.Notebook(self.root)
self.tabHome = ttk.Frame(self.tabControl)
self.tabControl.add(self.tabHome, text="Home")
self.tabTable = ttk.Frame(self.tabControl)
self.tabControl.add(self.tabTable, text="Historique")
self.tabTransaction = ttk.Frame(self.tabControl)
self.tabControl.add(self.tabTransaction, text="Nouvelle transaction")
self.tabControl.pack(expand=1, fill="both")
# Init Home
self.th_lab_balance = tk.Label(self.tabHome, text="Solde actuel : ")
self.th_lab_balance.grid(row=0, column=1, sticky="ew")
self.th_lab_balance.config(font = ("Courier", 14))
# Init table of transactions
self.dfTransactions = db.load().sort_values('date', ascending=False)
self.table = Table(self.tabTable, dataframe=self.dfTransactions, showtoolbar=True, showstatusbar=True)
self.table.show()
self.initStatistics()
# Launch
print("TRACE : mainloop")
self.root.mainloop()
def initStatistics(self):
categoriesEntrant = {k:v for (k,v) in self.categories.items() if v}
categoriesSortant = {k:v for (k,v) in self.categories.items() if not v}
dfEntrant = self.dfTransactions.loc[(self.dfTransactions["categorie"].isin(categoriesEntrant) | self.dfTransactions["categorie"].isin(['SoldeInitial']))==True]
dfSortant = self.dfTransactions.loc[self.dfTransactions["categorie"].isin(categoriesSortant)==True]
# Current balance
balance = str(round(dfEntrant['entrant'].sum() - dfSortant['sortant'].sum(), 2)) + "€"
self.th_lab_balance_nb = tk.Label(self.tabHome, text=balance)
self.th_lab_balance_nb.grid(row=0, column=2, sticky="ew")
self.th_lab_balance_nb.config(font = ("Courier", 14))
# Plots
dfEntrant = dfEntrant.loc[dfEntrant["categorie"].isin(['SoldeInitial'])==False] # remove soldeInitial
dfEntrantSommeParCategorie = dfEntrant.groupby('categorie')['entrant'].sum()
dfSortantSommeParCategorie = dfSortant.groupby('categorie')['sortant'].sum()
lf = ttk.Labelframe(self.tabHome, text='Plot Area')
lf.grid(row=0, column=0, sticky="nsew")
fig, axes = plt.subplots(nrows=2, ncols=1)
fig1 = dfEntrantSommeParCategorie.plot.pie(y="Entrant", ax=axes[0])
fig2 = dfSortantSommeParCategorie.plot.pie(y="Sortant", ax=axes[1])
fig1.axis("off")
fig2.axis("off")
fig1.set_title("Recettes par catégories")
fig2.set_title("Dépenses par catégories")
plt.tight_layout()
self.plot = FigureCanvasTkAgg(fig, master=lf)
self.plot.get_tk_widget().pack()
def __del__(self) : # Destructor
print("TRACE : Destructor")
db.save(self.dfTransactions)
display = GUI()
del display
我试图从我的代码中删除上述方法,然后调用析构函数。不过,我无法弄清楚问题出在哪里。
答:
1赞
LiiVion
6/9/2023
#1
您可以使用以下代码示例。正如@Thingamabobs已经建议的那样,您可以在杀死 gui 之前使用 run aWM_DELETE_WINDOW
function
from tkinter import *
def exit():
root.destroy()
# sys.exit() <-- if you want to stop your whole script, not just the gui
root = Tk()
root.geometry('800x600')
root.protocol('WM_DELETE_WINDOW', exit) # calling the exit function on closing
root.mainloop()
我建议使用 tkinter 类,因为它使处理变得容易得多。下面是一个示例,如何将 Tkinter 与类一起使用。inherited
from tkinter import *
import sys
class GUI(Tk): # inherited from the Tk Class > used as root window
def __init__(self):
super().__init__() # Init from Tk Class
self.geometry('800x600')
self.destroy_button = Button(self, text='Click to Destroy root', command=self.destroy_button_event)
self.destroy_button.pack(pady=50)
self.custom_frame = CustomFrame(self, width=400, height=200, background='black')
self.custom_frame.pack(expand=True, fill=BOTH, pady=200)
def destroy_button_event(self):
self.destroy()
# sys.exit() <-- if you want to stop your whole script, not just the gui
class CustomFrame(Frame):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.destroy_frame_button = Button(self, text='Click to Destroy frame', command=self.destroy_frame_button_event)
self.destroy_frame_button.pack()
def destroy_frame_button_event(self):
self.destroy()
if __name__ == '__main__':
gui = GUI()
gui.mainloop()
评论
0赞
Arolan
6/10/2023
多谢。这似乎奏效了。我会听从你关于继承:)的建议
评论
self.root.protocol('WM_DELETE_WINDOW', self.destructor_method)
mainloop()
__init__()