TtkBootstrap error "bgerror failed to handle background error" when destroy root window

57 views Asked by At

I have a simple application that needs to open a window to perform user login (in the example, it's 'root'). Once the credentials are verified, the login window should close, and the main window (in the example, it's 'MainWindows') should open.

The issue is that when I destroy the login window and create the main one, the following error always occurs, and the program freezes:

bgerror failed to handle background error.
    Original error: can't invoke "event" command: application has been destroyed
    Error in bgerror: can't invoke "tk" command: application has been destroyed

The code is as follows:

import tkinter as tk
from tkinter import filedialog
import ttkbootstrap as tb
from ttkbootstrap.dialogs import Messagebox

# login check
def login_check(username, password):
    if username == "a" and password == "p":
        return True
    else:
        return False

# login function
def on_login_click():
    username = ent_codice_utente.get()
    password = ent_password.get()

    if login_check(username, password):
        root.destroy()
        MainWindow()
                        
    else:
        err_credential = Messagebox.show_error('Invalid credential', parent=root)

def MainWindow():
    MainWindow = tb.Window(title='GESCON', themename='superhero')
    MainWindow.geometry('1366x768')

    MainWindow.columnconfigure(0, weight=1)
    MainWindow.rowconfigure(0, weight=1) 
    MainWindow.rowconfigure(1, weight=4)

    frm_filtri = tb.LabelFrame(MainWindow, text="Filter", borderwidth=2)
    frm_filtri.columnconfigure(0, weight=10)
    frm_filtri.columnconfigure(1, weight=10)
    frm_filtri.columnconfigure(2, weight=10)
    frm_filtri.rowconfigure(0, weight=10)
    frm_filtri.rowconfigure(1, weight=10)
    frm_filtri.rowconfigure(2, weight=10)
    frm_griglia = tb.LabelFrame(MainWindow, text='Result', borderwidth=2)

    frm_filtri.grid(column=0, row=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5)
    frm_griglia.grid(column=0, row=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5)
    
    btn_elabora = tb.Button(frm_filtri, text='Search')
    btn_elabora.grid(column=3, row=2, padx=50, pady=5)
    
    menubar = tb.Menu(MainWindow)
    MainWindow.config(menu=menubar)
    menu_file = tb.Menu(menubar, tearoff=0) 
    menu_help = tb.Menu(menubar, tearoff=0)

    menubar.add_cascade(label='File', menu=menu_file)
    menubar.add_cascade(label='Help', menu=menu_help)

    menu_anagrafiche = tb.Menu(menu_file, tearoff=0)
    menu_file.add_cascade(label='Anagrafiche', menu=menu_anagrafiche)
    menu_file.add_separator()
    menu_file.add_command(label='Import file')
    menu_file.add_separator()
    menu_file.add_command(label='Exit')

    menu_help.add_command(label='Info')
    
#Login window

root = tb.Window(title='Login', themename='superhero')
root.geometry('500x300')

lbl_codice_utente = tb.Label(root, text="User:")
ent_codice_utente = tb.Entry(root)

lbl_password = tb.Label(root, text="Password:")
ent_password = tb.Entry(root, show="*")

btn_login = tb.Button(root, text="Login", bootstyle='success, outline', command=on_login_click)
btn_exit = tb.Button(root, text="Exit", bootstyle='danger, outline', command = lambda:root.destroy())

lbl_codice_utente.grid(padx=10, pady=10, column=0, row=0, sticky='e')
ent_codice_utente.grid(pady=5, column=1, row=0, sticky='w')
lbl_password.grid(padx=10, pady=10, column=0, row=1, sticky='e')
ent_password.grid(pady=5, column=1, row=1, sticky='w')
btn_login.grid(pady=20, column=0, row=3)
btn_exit.grid(pady=20, column=1, row=3)

root.mainloop()

Any suggestions on how to eliminate the error or achieve the desired behavior in a different way?

Thank in advance.

1

There are 1 answers

0
NULL On

So first, the error is saying you destroyed the root/application. That's not good and probably why it would freeze.

I did find a workaround. Due to the 'ttkbootstrap api' it doesn't allow to override the window root with a 'copy'. Anyways, the solution:

#Login window
root = tb.Window(themename='superhero', size=[0,0])
root.overrideredirect(True)
loginWindow = tb.Toplevel(title="Login")

the root creates a window then sets the size to 0x0 and hides the UI ( the workaround ) then the loginWindow is the toplevel object aka the window you see.

Replace the all the objects with a root parent of loginWindow for example:

lbl_codice_utente = tb.Label(loginWindow, text="User:")

now the ticky part of removing the window. You will have to pass the window object to the function inside the button like so:

btn_login = tb.Button(loginWindow, text="Login", bootstyle='success, outline', command=lambda window = loginWindow : on_login_click(window))

and now you can destroy the window:

# login function
def on_login_click(loginWindow):
    username = ent_codice_utente.get()
    password = ent_password.get()

    if login_check(username, password):
        loginWindow.destroy()
        MainWindow()
                        
    else:
        err_credential = Messagebox.show_error('Invalid credential', parent=root)

feel free to comment any questions you have, I know it's a lot. Hope it helps.