Python 3 Tkinter按钮命令不起作用(非常特定的情况)

时间:2018-12-10 04:46:56

标签: python-3.x tkinter

我正在this帖子中为我的程序使用这些日历模块,并对导入进行了一些小的修改以使其适用于最新的python版本。

我只显示我认为对这个问题有影响的代码片段。

因此,我有一个用于警报的弹出窗口:

#class for pop-up windows for alerts, errors etc.
class PopUpAlert():
    def __init__(self, alert='Alert!'):
        self.root = tk.Tk()

        tk.Label(self.root,
            text=alert,
            font="Verdana 15",
            fg='red',
            padx=10,
            pady=5).pack(side=tk.TOP)

        self.root.bind('<Return>', (lambda event: self.ok()))
        tk.Button(self.root,
            text='ok',
            pady=10,
            command=self.ok).pack(side=tk.TOP)

    def ok(self):
        print('ok clicked')
        self.root.destroy()

函数ok的创建只是为了我测试该函数是否被调用。此窗口在我的代码中完全可以正常工作,除非我尝试使用日历实现,否则PopUpAlert的“确定”按钮(应该会破坏该窗口)停止工作:

class CalendarDialog(tkSimpleDialog.Dialog):
    """Dialog box that displays a calendar and returns the selected date"""
    def body(self, master):
        self.calendar = ttkcalendar.Calendar(master)
        self.calendar.pack()

    def apply(self):
        self.result = self.calendar.selection

    def validate(self):
        if self.calendar.selection == None:
            PopUpAlert(alert='Please select a date or click cancel!')
            return False
        return True

日历上有一个“确定”按钮,用于确认日期的选择并关闭日历窗口。我要尝试做的是,如果用户未选择日期,则无法单击“确定”以关闭窗口。为此,我使用了validate函数,该函数在我的tkSimpleDialog.Dialog继承的类CalendarDialog中预定义。我改写了CalendarDialog类中的函数以调用PopUpAlert,然后将False返回给父函数ok(在按下“确定”按钮时会调用日历窗口):

    def ok(self, event=None):

        if not self.validate():
            self.initial_focus.focus_set() # put focus back
            return

        self.withdraw()
        self.update_idletasks()

        self.apply()

        self.cancel()

    def cancel(self, event=None):

        # put focus back to the parent window
        self.parent.focus_set()
        self.destroy()

(整个内容可以在我上面链接的另一个SO页面中链接的tkSimpleDialog文件中找到。)

在逐行注释之后,我发现PopUpAlert上的“确定”按钮仅在日历上未调用self.root.destroy()时才起作用。为什么?我该如何解决?

我已经尝试将自己的PopUpAlert更改为Toplevel窗口,这也无法正常工作。

1 个答案:

答案 0 :(得分:0)

提供mcve而不是要求我们这样做会更好。

问题是默认情况下,对话框会禁用对其他窗口(包括其产生的窗口)的点击。要解决此问题,您需要使用Toplevel而不是Tk(如上所述) AND ,并将此行代码添加到PopUpAlert.__init__的末尾:

self.root.grab_set() 

如果您将Toplevel子类化,而不是使用那个奇怪的包装器,那将更加整洁。这是mcve:

try:
    import Tkinter as tk
    import tkSimpleDialog as sd
except:
    import tkinter as tk
    from tkinter import simpledialog as sd

#class for pop-up windows for alerts, errors etc.
class PopUpAlert(tk.Toplevel):
    def __init__(self, master, alert='Alert!', **kwargs):
        tk.Toplevel.__init__(self, master, **kwargs)

        tk.Label(self,
            text=alert,
            font="Verdana 15",
            fg='red',
            padx=10,
            pady=5).pack(side=tk.TOP)

        self.bind('<Return>', self.ok)
        tk.Button(self,
            text='ok',
            pady=10,
            command=self.ok).pack(side=tk.TOP)

        self.grab_set() # this window only gets commands

    def ok(self, *args):
        print('ok clicked')
        self.destroy()

class CalendarDialog(sd.Dialog):
    """Dialog box that displays a calendar and returns the selected date"""
    def body(self, master):
        self.calendar = tk.Label(master, text="Whatever you do, don't click 'OK'!")
        self.calendar.pack()

    def validate(self):
        PopUpAlert(self, alert='Please select a date or click cancel!')

def display():
    CalendarDialog(root)

root = tk.Tk()
tk.Button(root, text='data data data', command=display).pack()
root.mainloop()

请注意,我还摆脱了无用的lambda,它恰好是我的宠儿。 lambda在某些情况下很好,但是很少需要它。