我正在try 编写一个与askyesno()相同的自定义消息框(为更大的项目做准备),而Grab_Set()在用户 Select 按钮之前不会维护对程序的控制.如何让Grab_Set()在用户单击消息框中的按钮之前保持对程序流的控制,以及将用户的 Select 返回给调用函数的最佳方式是什么?

import tkinter as tk
from tkinter import Menu, ttk
from tkinter.messagebox import askyesno

class Test(tk.Tk):
    def builtin(self, wtitle):
        print('Button One')
        answer = askyesno(title=wtitle, message='Click something', icon='info')
        print('User pressed: %s' % answer)


    def custom(self, wtitle):
        print('Button Two')
        answer = popup_msg(self, wtitle, 'Click to close')
        print('User pressed: %s' % answer)
        
        
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.wm_title('Test')
        self.geometry('300x100')

        frame   = ttk.Frame(self)
        
        button_one   = ttk.Button(self, text='Built-in',
                                  command=lambda: self.builtin('Built-In'))
        button_two   = ttk.Button(self, text='Custom',
                                  command=lambda: self.custom('Custom'))

        button_one.pack(padx=5, pady=5)
        button_two.pack(padx=5, pady=5)
        frame.pack()


def popup_msg(root, wtitle, msg):
    ret = False
    
    def test_func(val):
        popup.destroy()
        return val
        
    popup = tk.Toplevel()
    popup.wm_title(wtitle)
    popup.geometry("300x100")
   
    label = ttk.Label(popup, text=msg, anchor=tk.CENTER)
    label.pack(side="top", fill="x", pady=10)
    button1 = ttk.Button(popup, text="Yes", command = lambda: test_func(True))
    button2 = ttk.Button(popup, text="No", command = lambda: test_func(False))
    button1.pack()
    button2.pack()

    popup.grab_set()
    return ret


app = Test()
app.mainloop()

上面的代码创建了两个按钮-内置调用askyesno(),而Custom调用一个用于创建MessageBox的函数.内置函数打印出"Button One",然后显示消息框.当用户 Select 是或否时,该框消失,该功能打印用户的 Select .使用Custom时,该功能会打印"Button Two"和"User Press False",并打开自定义窗口.单击是或否关闭窗口,但对打印输出的内容没有影响.

推荐答案

IIUC,您可以派生simpledialog.Dialog的子类来创建自定义对话框并覆盖所需的方法.

下面是一个例子:

import tkinter as tk
from tkinter import Menu, simpledialog, ttk
from tkinter.messagebox import askyesno


class Test(tk.Tk):
    def builtin(self, wtitle):
        print("Button One")
        answer = askyesno(title=wtitle, message="Click something", icon="info")
        print("User pressed: %s" % answer)

    def custom(self, wtitle):
        print("Button Two")
        answer = popup_msg(
            self, wtitle="My Custom MessageBox", msg="This is custom MessageBox"
        )
        print("User pressed: %s" % answer)

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.wm_title("Test")
        self.geometry("300x100")

        frame = ttk.Frame(self)

        button_one = ttk.Button(
            self, text="Built-in", command=lambda: self.builtin("Built-In")
        )
        button_two = ttk.Button(
            self, text="Custom", command=lambda: self.custom("Custom")
        )

        button_one.pack(padx=5, pady=5)
        button_two.pack(padx=5, pady=5)
        frame.pack()


class MessageBox(simpledialog.Dialog):
    def __init__(self, master, *args, **kwargs):
        self.sample_var = tk.StringVar()
        self.sample_var.set("No")

        self.label_var = tk.StringVar()
        self.label_var.set(kwargs.pop("msg", "default msg"))

        simpledialog.Dialog.__init__(
            self, master, title=kwargs.pop("wtitle", "default title"), *args, **kwargs
        )

    def body(self, master):
        """create dialog body.

        return widget that should have initial focus.
        This method should be overridden, and is called
        by the __init__ method.
        """

        tk.Label(master, textvariable=self.label_var).grid(row=0, columnspan=2)

        self.btn_yes = tk.Button(
            master, text="Yes", command=lambda: self._button_yes(master)
        ).grid(row=1, column=0)

        self.btn_no = tk.Button(
            master, text="No", command=lambda: self._button_no(master)
        ).grid(row=1, column=1)

    def _button_yes(self, master):
        self.sample_var.set("Yes")
        self.ok()

    def _button_no(self, master):
        self.sample_var.set("No")
        self.ok()

    def validate(self):
        """validate the data

        This method is called automatically to validate the data before the
        dialog is destroyed. By default, it always validates OK.
        """

        return 1  # override

    def apply(self):
        """process the data

        This method is called automatically to process the data, *after*
        the dialog is destroyed. By default, it does nothing.
        """
        self.result = self.sample_var.get()

    def buttonbox(self):
        """Override default simpledialog.Dialog buttons"""
        pass


def popup_msg(root, wtitle, msg):
    res = MessageBox(app, wtitle=wtitle, msg=msg)
    return res.result


app = Test()
app.mainloop()

当您点击Custom按钮时,您将看到:

enter image description here

然后,当您单击Yes/No时,对话框关闭,程序打印User pressed: Yes/No

Python相关问答推荐

aiohTTP与pytest的奇怪行为

有没有办法清除气流中的僵尸

Flask:如何在完整路由代码执行之前返回验证

在Python中,什么表达相当于0x1.0p-53?

为什么我的(工作)代码(生成交互式情节)在将其放入函数中时不再工作?

Pandas滚动分钟,来自其他列的相应值

Docker-compose:为不同项目创建相同的容器

使用Python Cerberus初始化一个循环数据 struct (例如树)(v1.3.5)

提取两行之间的标题的常规表达

Django mysql图标不适用于小 case

Pandas 有条件轮班操作

将pandas Dataframe转换为3D numpy矩阵

无法定位元素错误404

如何在Python脚本中附加一个Google tab(已经打开)

在np数组上实现无重叠的二维滑动窗口

如何从数据库上传数据到html?

什么是最好的方法来切割一个相框到一个面具的第一个实例?

lityter不让我输入左边的方括号,'

(Python/Pandas)基于列中非缺失值的子集DataFrame

人口全部乱序 - Python—Matplotlib—映射