我对tkinter(和面向对象编程)相对较新,正在尝试创建带有第二个窗口的GUI以进行偏好设置。如果以相反的顺序关闭它们,则没有问题,但我正试图使它们能够按任意顺序关闭(这样关闭主窗口就会关闭子窗口)。
我尝试绑定一个简单的函数,在父窗口被销毁时关闭子窗口(如果存在),虽然它似乎不一致。有时它会关闭子窗口,有时它会冻结,我必须关闭内核。我不确定冻结的原因,因为它似乎发生在关闭子窗口之后。请注意,在完整的代码中,我使用tkmacosx在鼠标悬停在按钮上时更改其背景。
这是我代码的一个子集,用于示例,还可能存在其他问题。还有一些来自我的测试的其他内容(例如将销毁子窗口绑定到返回键和在函数内打印)。
import tkinter as tk
from tkinter import ttk
import tkinter.filedialog as fd
import os
from tkmacosx import Button
from tkmacosx import Marquee
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.mainframe = tk.Frame(parent)
self.mainframe.grid(column=0, row=0, sticky=(tk.N, tk.W, tk.E, tk.S))
self.mainframe.columnconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=1)
self.create_widgets()
self.parent.update()
self.window = None
self.parent.geometry(str(self.parent.winfo_width())+"x"+str(self.parent.winfo_height())+\
"+"+str((self.parent.winfo_screenwidth()-self.parent.winfo_width())//2)\
+"+"+str(int((self.parent.winfo_screenheight()-self.parent.winfo_height())*.4)))
self.parent.bind("<Destroy>", self.Destroy_subwindow)
self.parent.bind('<Return>', self.Destroy_subwindow)
def Destroy_subwindow(self, event):
if self.window is not None:
print("closing subwindow")
self.window.destroy()
def create_widgets(self):
self.filepath = ttk.Entry(self.mainframe, width=10)
self.filepath.grid(column = 1, row = 0)
ttk.Label(self.mainframe, text="Input Directory:").grid(column=0, row=0, sticky=tk.W)
# Preferences
self.advOptions = Button(self.mainframe, text = "Preferences", command = self.Preferences,\
borderless = 1,highlightthickness = 0, width = 90, \
overbackground = '#227afe',activebackground = ('#227afe','#0b60ff'),\
activeforeground = "white", overforeground = 'white')
self.advOptions.bind('<Return>', self.Preferences)
self.advOptions.grid(column = 3, row = 1, sticky = tk.E)
def Preferences(self, event = None):
self.window = tk.Toplevel(root)
self.window.title("Preferences")
self.style = ttk.Style(self.window)
self.style.theme_settings( "aqua", settings={
"TNotebook.Tab": {
"configure": {"padding": [5, 1], "background": "#e7e7e9","foreground": "black" },
"map": {"background": [("selected", "#cfcfd1")], "foreground":[("selected", "black")],\
"expand": [("selected", [1, 1, 1, 0])] } } } )
self.tabcontrol = ttk.Notebook(self.window)
self.tab1 = ttk.Frame(self.window)
self.tabcontrol.add(self.tab1, text = "Plot")
root = tk.Tk()
root.title("Kinetic Plotting")
MainApplication(root)
root.mainloop()
如果需要的话,这里是全部代码:
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.mainframe = tk.Frame(parent)
self.mainframe.grid(column=0, row=0, sticky=(tk.N, tk.W, tk.E, tk.S))
self.mainframe.columnconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=1)
self.create_widgets()
self.parent.update()
self.window = None
self.parent.geometry(str(self.parent.winfo_width())+"x"+str(self.parent.winfo_height())+\
"+"+str((self.parent.winfo_screenwidth()-self.parent.winfo_width())//2)\
+"+"+str(int((self.parent.winfo_screenheight()-self.parent.winfo_height())*.4)))
self.parent.bind("<Destroy>", self.Destroy_subwindow)
self.parent.bind('<Return>', self.Destroy_subwindow)
def Destroy_subwindow(self, event):
if self.window is not None:
print("closing subwindow")
self.window.destroy()
def create_widgets(self):
global tempdir, fittingEquation, saveFileType
# Directory input
self.filepath = ttk.Entry(self.mainframe, width=10)
self.filepath.grid(column = 1, row = 0)
ttk.Label(self.mainframe, text="Input Directory:").grid(column=0, row=0, sticky=tk.W)
self.browseButton = Button(self.mainframe, text = "Browse", command = self.Browse_files,\
borderless = 1,highlightthickness = 0, width = 80, \
overbackground = '#227afe',activebackground = ('#227afe','#0b60ff'),\
activeforeground = "white", overforeground = 'white')
self.browseButton.bind('<Return>', self.Browse_files)
self.browseButton.grid(column = 3, row = 0, sticky = tk.E)
# Preferences
self.advOptions = Button(self.mainframe, text = "Preferences", command = self.Preferences,\
borderless = 1,highlightthickness = 0, width = 90, \
overbackground = '#227afe',activebackground = ('#227afe','#0b60ff'),\
activeforeground = "white", overforeground = 'white')
self.advOptions.bind('<Return>', self.Preferences)
self.advOptions.grid(column = 3, row = 1, sticky = tk.E)
# Image type dropdown
self.imageOptions = [".jpg", ".png", ".tiff"]
self.var2 = tk.StringVar(self.mainframe)
self.var2.set(self.imageOptions[0]) # initial value
saveFileType = self.imageOptions[0]
self.imageOption = tk.OptionMenu(self.mainframe, self.var2, *self.imageOptions, command = self.get_savetype)
self.imageOption.grid(columnspan = 2,row = 1, sticky = tk.W)
# Fitting Equation dropdown
self.Options = ["Single Exponential", "Double Exponential", "Control (Flat)"]
self.var = tk.StringVar(self.mainframe)
self.var.set(self.Options[0]) # initial value
fittingEquation = self.Options[0]
self.option = tk.OptionMenu(self.mainframe, self.var, *self.Options, command = self.get_variable)
self.option.grid(columnspan = 2,row = 2, sticky = tk.W)
def set_text(self, text):
self.filepath.delete(0,tk.END)
self.filepath.insert(0,text)
def Browse_files(self):
global tempdir
self.currdir = os.getcwd()
self.tempdir = fd.askdirectory(parent=root, initialdir=self.currdir, title='Please select a directory')
self.set_text(self.tempdir)
tempdir = self.tempdir
def get_variable(self, var):
global fittingEquation
fittingEquation = self.var.get()
def get_savetype(self, var2):
global saveFileType
saveFileType = self.var2.get()
def PlotOptionsWidgets(self):
self.tab1.grid(column=0, row=0, sticky=(tk.N, tk.W, tk.E, tk.S))
self.tab1.columnconfigure(0, weight=1)
self.tab1.rowconfigure(0, weight=1)
# Errorbar checkbox
self.errorvar = tk.IntVar(value = 1)
self.checkerror = tk.Checkbutton(self.tab1, text = "Errorbars (only plotting)", variable = self.errorvar, \
onvalue = 1, offvalue = 0,bg = "#e4e4e4")
self.checkerror.grid(column = 0, row = 0, columnspan = 5)
# Column Labels
ttk.Label(self.tab1, text = "Linear Plots (kobs vs conc)").grid(column = 3, row=1, columnspan = 2)
ttk.Label(self.tab1, text = "Kinetic Traces (F vs time)").grid(column = 0, row=1, columnspan = 2)
# Figure Sizes
self.linearFigureSize = tk.StringVar(value = "2.45, 1")
self.linearFigureSizeEntry = ttk.Entry(self.tab1,textvariable =self.linearFigureSize, width=10)
self.linearFigureSizeEntry.grid(column = 4, row = 2, sticky = tk.W)
ttk.Label(self.tab1, text="Figure Size (in, in)").grid(column=3, row=2, sticky=tk.E)
self.KineticFigureSize = tk.StringVar(value = "2.49, 2.32")
self.KineticFigureSizeEntry = ttk.Entry(self.tab1,textvariable =self.KineticFigureSize, width=10)
self.KineticFigureSizeEntry.grid(column = 1, row = 2, sticky = tk.W)
ttk.Label(self.tab1, text="Figure Size (in, in)").grid(column=0, row=2, sticky=tk.E)
# Axes limits
self.leftAxisLimit = tk.StringVar(value = "Auto")
self.leftAxisLimitentry = ttk.Entry(self.tab1,textvariable =self.leftAxisLimit, width=10)
self.leftAxisLimitentry.grid(column = 1, row = 5, sticky = tk.W)
ttk.Label(self.tab1, text="Left x limit (time)").grid(column=0, row=5, sticky=tk.E)
self.rightAxisLimit = tk.StringVar(value = "Auto")
self.rightAxisLimitentry = ttk.Entry(self.tab1,textvariable =self.rightAxisLimit, width=10)
self.rightAxisLimitentry.grid(column = 1, row = 6, sticky = tk.W)
ttk.Label(self.tab1, text="Right x limit (time)").grid(column=0, row=6, sticky=tk.E)
self.leftAxisLimitConc = tk.StringVar(value = "Auto")
self.leftAxisLimitConcentry = ttk.Entry(self.tab1,textvariable =self.leftAxisLimitConc, width=10)
self.leftAxisLimitConcentry.grid(column = 4, row = 5, sticky = tk.W)
ttk.Label(self.tab1, text="Left x limit (conc.)").grid(column=3, row=5, sticky=tk.E)
self.rightAxisLimitConc = tk.StringVar(value = "Auto")
self.rightAxisLimitConcentry = ttk.Entry(self.tab1,textvariable =self.rightAxisLimitConc, width=10)
self.rightAxisLimitConcentry.grid(column = 4, row = 6, sticky = tk.W)
ttk.Label(self.tab1, text="Right x limit (conc.)").grid(column=3, row=6, sticky=tk.E)
# Axes titles
self.xAxisTitle = tk.StringVar(value = "Time (s)")
self.xAxisTitleEntry = ttk.Entry(self.tab1,textvariable =self.xAxisTitle, width=10)
self.xAxisTitleEntry.grid(column = 1, row = 3, sticky = tk.W)
ttk.Label(self.tab1, text="x-Axis Label").grid(column=0, row=3, sticky=tk.E)
self.yAxisTitle = tk.StringVar(value = "Rel. Emission Intensity")
self.yAxisTitleEntry = ttk.Entry(self.tab1,textvariable =self.yAxisTitle, width=10)
self.yAxisTitleEntry.grid(column = 1, row = 4, sticky = tk.W)
ttk.Label(self.tab1, text="y-Axis Label").grid(column=0, row=4, sticky=tk.E)
self.xAxisTitleLinear = tk.StringVar(value = "[Ion] (Units)")
self.xAxisTitleLinearEntry = ttk.Entry(self.tab1,textvariable =self.xAxisTitleLinear, width=10)
self.xAxisTitleLinearEntry.grid(column = 4, row = 3, sticky = tk.W)
ttk.Label(self.tab1, text="x-Axis Label").grid(column=3, row=3, sticky=tk.E)
self.yAxisTitleLinear = tk.StringVar(value = "kobs (Units)")
self.yAxisTitleLinearEntry = ttk.Entry(self.tab1,textvariable =self.yAxisTitleLinear, width=10)
self.yAxisTitleLinearEntry.grid(column = 4, row = 4, sticky = tk.W)
ttk.Label(self.tab1, text="y-Axis Label").grid(column=3, row=4, sticky=tk.E)
# Font options
self.LinearFont = tk.StringVar(value = "Arial")
self.LinearFontEntry = ttk.Entry(self.tab1, textvariable = self.LinearFont, width = 10)
self.LinearFontEntry.grid(column = 4, row = 7, sticky=tk.W)
ttk.Label(self.tab1, text = "Font Style").grid(column = 3, row = 7, sticky=tk.E)
self.LinearFontSize = tk.DoubleVar(value = 10)
self.LinearFontSizeEntry = ttk.Entry(self.tab1, textvariable = self.LinearFontSize, width = 10)
self.LinearFontSizeEntry.grid(column = 4, row = 8, sticky=tk.W)
ttk.Label(self.tab1, text = "Font Size").grid(column = 3, row = 8, sticky=tk.E)
self.ExponentialFont = tk.StringVar(value = "Arial")
self.ExponentialFontEntry = ttk.Entry(self.tab1, textvariable = self.ExponentialFont, width = 10)
self.ExponentialFontEntry.grid(column = 1, row = 7, sticky=tk.W)
ttk.Label(self.tab1, text = "Font Style").grid(column = 0, row = 7, sticky=tk.E)
self.ExponentialFontSize = tk.DoubleVar(value = 10)
self.ExponentialFontSizeEntry = ttk.Entry(self.tab1, textvariable = self.ExponentialFontSize, width = 10)
self.ExponentialFontSizeEntry.grid(column = 1, row = 8, sticky=tk.W)
ttk.Label(self.tab1, text = "Font Size").grid(column = 0, row = 8, sticky=tk.E)
def RegressionOptionsWidgets(self):
self.tab2.grid(column=0, row=0, sticky=(tk.N, tk.W, tk.E, tk.S))
self.tab2.columnconfigure(0, weight=1)
self.tab2.rowconfigure(0, weight=1)
# Dead time
self.deadTime= tk.DoubleVar(value = 0.01)
self.deadTimeEntry = ttk.Entry(self.tab2, textvariable = self.deadTime, width = 10)
self.deadTimeEntry.grid(column = 3, row = 4, sticky = tk.W)
ttk.Label(self.tab2, text = "Dead Time (s)").grid(column = 0,row = 4, sticky = tk.E, columnspan = 3)
# Weighting
self.weightExp = tk.IntVar(value = 1)
self.checkWeightExp = tk.Checkbutton(self.tab2, text = "Weight Exponential Fit", variable = self.weightExp, \
onvalue = 1, offvalue = 0,bg = "#e4e4e4")
self.checkWeightExp.grid(column = 0, row = 0, columnspan = 5, sticky = tk.W)
self.weightLinear = tk.IntVar(value = 1)
self.checkWeightLinear = tk.Checkbutton(self.tab2, text = "Weight Linear Fit", variable = self.weightLinear, \
onvalue = 1, offvalue = 0,bg = "#e4e4e4")
self.checkWeightLinear.grid(column = 0, row = 1, columnspan = 5, sticky = tk.W)
# Biasing
self.outliers = tk.IntVar(value = 0)
self.checkOutliers = tk.Checkbutton(self.tab2, text = "Check for Outliers", variable = self.outliers, \
onvalue = 1, offvalue = 0,bg = "#e4e4e4")
self.checkOutliers.grid(column = 0, row = 2, columnspan = 5, sticky = tk.W)
self.intialParam = tk.IntVar(value = 1)
self.checkInitialParam = tk.Checkbutton(self.tab2, text = "Calculate Initial Parameters", variable = self.intialParam, \
onvalue = 1, offvalue = 0,bg = "#e4e4e4")
self.checkInitialParam.grid(column = 0, row = 3, columnspan = 5, sticky = tk.W)
def OutputOptionsWidgets(self):
self.tab3.grid(column=0, row=0, sticky=(tk.N, tk.W, tk.E, tk.S))
self.tab3.columnconfigure(0, weight=1)
self.tab3.rowconfigure(0, weight=1)
# DPI output
self.outDpi = tk.StringVar(value = "600")
self.outDpiEntry = ttk.Entry(self.tab3,textvariable =self.outDpi, width=10)
self.outDpiEntry.grid(column = 4, row = 0, sticky = tk.W)
ttk.Label(self.tab3, text="DPI output").grid(column=2, row=0, columnspan = 2)
def resizingfunction(self, event = None):
global tabcontrolw, tabcontrolh
self.active_tab = self.tabcontrol.index(self.tabcontrol.select())
if self.active_tab != 1:
self.window.geometry(str(tabcontrolw)+"x"+str(tabcontrolh))
elif self.active_tab == 1:
self.window.geometry(str(int(tabcontrolw*.7))+"x"+str(int(tabcontrolh*.6)))
def Preferences(self, event = None):
self.window = tk.Toplevel(root)
self.window.title("Preferences")
self.style = ttk.Style(self.window)
self.style.theme_settings( "aqua", settings={
"TNotebook.Tab": {
"configure": {"padding": [5, 1], "background": "#e7e7e9","foreground": "black" },
"map": {"background": [("selected", "#cfcfd1")], "foreground":[("selected", "black")],\
"expand": [("selected", [1, 1, 1, 0])] } } } )
self.tabcontrol = ttk.Notebook(self.window)
self.tab1 = ttk.Frame(self.window)
self.PlotOptionsWidgets()
self.tab2 = ttk.Frame(self.window)
self.RegressionOptionsWidgets()
self.tab3 = ttk.Frame(self.window)
self.OutputOptionsWidgets()
self.tabcontrol.add(self.tab1, text = "Plot")
self.tabcontrol.add(self.tab2, text = "Regression")
self.tabcontrol.add(self.tab3, text = "Output")
self.tabcontrol.pack(expand = 1, fill = "both")
self.window.update()
global tabcontrolw, tabcontrolh
tabcontrolw = self.tabcontrol.winfo_width()
tabcontrolh = self.tabcontrol.winfo_height()
self.tabcontrol.bind("<<NotebookTabChanged>>", self.resizingfunction)
root = tk.Tk()
root.title("Kinetic Plotting")
MainApplication(root)
root.mainloop()
Tk()
时,它应该自动销毁子窗口Toplevel()
。也许这只是在 Mac 上的问题。 - furas