【问题标题】:Python 3 Tkinter button command not working (very specific scenario)Python 3 Tkinter 按钮命令不起作用(非常具体的场景)
【发布时间】:2019-05-10 23:56:37
【问题描述】:

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

我将只显示我认为对这个问题很重要的代码的 sn-ps。

所以我制作了这个用于警报的弹出窗口:

#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 窗口,但也没有用。

【问题讨论】:

  • 看起来您在代码中不止一次调用Tk()。那永远不会结局好...
  • @jasonharper 我说我已经尝试过使用 toplevel 代替

标签: python-3.x tkinter


【解决方案1】:

您最好提供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 在某些情况下很棒,但很少需要。

【讨论】:

  • 抱歉,我没有提供 MCVE,因为我什至不知道如何为这个问题制作一个,这就是我在这里问的原因。至于lambda 是否添加*args 取代了对它的需求?另外,我在哪里可以找到一些解释,为什么子类化 Toplevel 和其他 tkinter 小部件比我所做的更好? (以及为什么我的方法“奇怪”)
  • 这是一个简洁的解释,为什么不鼓励使用多个 Tk 实例 - Why are multiple instances of Tk discouraged?
  • @NathanTew 需要 *args 才能使绑定工作,无论有无 lambda。如果您添加一个,它们还允许跟踪工作。子类化只会更好,因为它更整洁。您可以像使用本机对象一样使用自定义对象,而不必制作和使用各种钩子。一旦你使用 OOP 一段时间,它就会变得很明显。
猜你喜欢
  • 1970-01-01
  • 2019-07-14
  • 1970-01-01
  • 2015-08-21
  • 2018-01-24
  • 1970-01-01
  • 2023-02-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多