首页 文章

Python 3 Tkinter按钮命令不起作用(非常具体的场景)

提问于
浏览
0

我正在使用this post中找到的这些日历模块作为我的程序,对导入稍作修改,使其适用于最新的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 的"ok"按钮(应该销毁窗口)停止工作:

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

日历有一个"ok"按钮,用于确认选择日期并关闭日历窗口 . 我试图做的是,如果他/她没有选择日期,用户不能单击"ok"关闭窗口 . 为此,我使用了函数 validate ,它是我的 CalendarDialog 继承的类 tkSimpleDialog.Dialog 中预定义的 . 我覆盖了我的 CalendarDialog 类中的函数来调用 PopUpAlert ,然后将 False 返回到父函数 ok (在日历窗口上按下"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()

(整个事情可以在 tkSimpleDialog 文件中找到,该文件链接在我上面链接的另一个SO页面中 . )

在逐行注释掉行后,我发现 PopUpAlert 上的"ok"按钮仅在日历上没有调用 self.root.destroy() 时才起作用 . 为什么?我该如何解决?

我已经尝试将我的_19104改为 Toplevel 窗口,这也没有用 .

1 回答

  • 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 在某些情况下很棒,但很少需要它 .

相关问题