如何将 "show details" 按钮添加到 tkinter 消息框?

How can I add a "show details" button to a tkinter messagebox?

我有一个 Python 脚本,它使用 tkinter.messagebox 在发生意外异常时显示带有回溯详细信息的错误消息。

import tkinter.messagebox as tm
import traceback

try:
    1/0
except Exception as error:
    tm.showerror(title="Error",
                 message="An error has occurred: '" + str(error) + "'.",
                 detail=traceback.format_exc())

以这种方式显示回溯有一些缺点。

我想添加一个 "show details" 按钮来在只读文本字段中显示更多信息,而不是默认显示错误详细信息。

如何将 "show details" 按钮添加到 tkinter 消息框?

我会使用 Toplevel() window 来构建我自己的客户错误框。

我认为在这里使用 ttk 按钮是个好主意,结合框架和权重,我们可以使 window 看起来足够体面。

为了防止 window 被用户调整大小,我还必须设置一种方法来切换详细信息文本框。使用跟踪变量和使用易于设置的 if/else 语句。

最后,我们可以使用 .config(state="disabled")

禁用文本框
import tkinter as tk
import tkinter.ttk as ttk
import traceback


class MyApp(tk.Tk):
    def __init__(self):
        super().__init__()
        tk.Button(self, text='test error', command=self.run_bad_math).pack()

    @staticmethod
    def run_bad_math():
        try:
            1/0
        except Exception as error:
            title = 'Traceback Error'
            message = "An error has occurred: '{}'.".format(error)
            detail = traceback.format_exc()
            TopErrorWindow(title, message, detail)


class TopErrorWindow(tk.Toplevel):
    def __init__(self, title, message, detail):
        tk.Toplevel.__init__(self)
        self.details_expanded = False
        self.title(title)
        self.geometry('350x75')
        self.minsize(350, 75)
        self.maxsize(425, 250)
        self.rowconfigure(0, weight=0)
        self.rowconfigure(1, weight=1)
        self.columnconfigure(0, weight=1)

        button_frame = tk.Frame(self)
        button_frame.grid(row=0, column=0, sticky='nsew')
        button_frame.columnconfigure(0, weight=1)
        button_frame.columnconfigure(1, weight=1)

        text_frame = tk.Frame(self)
        text_frame.grid(row=1, column=0, padx=(7, 7), pady=(7, 7), sticky='nsew')
        text_frame.rowconfigure(0, weight=1)
        text_frame.columnconfigure(0, weight=1)

        ttk.Label(button_frame, text=message).grid(row=0, column=0, columnspan=2, pady=(7, 7))
        ttk.Button(button_frame, text='OK', command=self.destroy).grid(row=1, column=0, sticky='e')
        ttk.Button(button_frame, text='Details', command=self.toggle_details).grid(row=1, column=1, sticky='w')

        self.textbox = tk.Text(text_frame, height=6)
        self.textbox.insert('1.0', detail)
        self.textbox.config(state='disabled')
        self.scrollb = tk.Scrollbar(text_frame, command=self.textbox.yview)
        self.textbox.config(yscrollcommand=self.scrollb.set)

    def toggle_details(self):
        if self.details_expanded:
            self.textbox.grid_forget()
            self.scrollb.grid_forget()
            self.geometry('350x75')
            self.details_expanded = False
        else:
            self.textbox.grid(row=0, column=0, sticky='nsew')
            self.scrollb.grid(row=0, column=1, sticky='nsew')
            self.geometry('350x160')
            self.details_expanded = True


if __name__ == '__main__':
    App = MyApp().mainloop()

结果:

现在可以调整大小:D

更新:

回应您的以下声明:

The error window will not display if a Tk instance hasn't been initialized first.

如果我们将 class 设置为它自己的 Tk() 实例,它可以用作独立的错误弹出窗口。我还添加了一些对齐更改和一些调整大小的控件,以使此 class 更符合您在评论中提到的标准错误消息。

见下面的代码。

import tkinter as tk
import tkinter.ttk as ttk


class TopErrorWindow(tk.Tk):
    def __init__(self, title, message, detail):
        super().__init__()
        self.details_expanded = False
        self.title(title)
        self.geometry('350x75')
        self.minsize(350, 75)
        self.maxsize(425, 250)
        self.resizable(False, False)
        self.rowconfigure(0, weight=0)
        self.rowconfigure(1, weight=1)
        self.columnconfigure(0, weight=1)

        button_frame = tk.Frame(self)
        button_frame.grid(row=0, column=0, sticky='nsew')
        button_frame.columnconfigure(0, weight=1)
        button_frame.columnconfigure(1, weight=1)

        text_frame = tk.Frame(self)
        text_frame.grid(row=1, column=0, padx=(7, 7), pady=(7, 7), sticky='nsew')
        text_frame.rowconfigure(0, weight=1)
        text_frame.columnconfigure(0, weight=1)

        ttk.Label(button_frame, text=message).grid(row=0, column=0, columnspan=3, pady=(7, 7), padx=(7, 7), sticky='w')
        ttk.Button(button_frame, text='OK', command=self.destroy).grid(row=1, column=1, sticky='e')
        ttk.Button(button_frame, text='Details',
                   command=self.toggle_details).grid(row=1, column=2, padx=(7, 7), sticky='e')

        self.textbox = tk.Text(text_frame, height=6)
        self.textbox.insert('1.0', detail)
        self.textbox.config(state='disabled')
        self.scrollb = tk.Scrollbar(text_frame, command=self.textbox.yview)
        self.textbox.config(yscrollcommand=self.scrollb.set)
        self.mainloop()

    def toggle_details(self):
        if self.details_expanded:
            self.textbox.grid_forget()
            self.scrollb.grid_forget()
            self.resizable(False, False)
            self.geometry('350x75')
            self.details_expanded = False
        else:
            self.textbox.grid(row=0, column=0, sticky='nsew')
            self.scrollb.grid(row=0, column=1, sticky='nsew')
            self.resizable(True, True)
            self.geometry('350x160')
            self.details_expanded = True

结果:

您也可以使用 canvas 添加您想要的错误图像类型的图像。