简体   繁体   中英

Create a python tkinter window with no X (close) button

I'm writing a 'wizard' type Python Tkinter GUI that collects information from the user and then performs several actions based on the user's entries: file copying, DB updates, etc. The processing normally takes 30-60 seconds and during that time, I want to:

  1. Provide the user with text updates on the activity and progress
  2. Prevent the user from closing the app until it's finished what it's doing

I started on the route of having the text updates appear in a child window that's configured to be trainsient and using wait_window to pause the main loop until the activities are done. This worked fine for other custom dialog boxes I created which have OK/cancel buttons that call the window's destroy method. The basic approach is:

def myCustomDialog(parent,*args):
    winCDLG = _cdlgWin(parent,*args)
    winCDLG.showWin()
    winCDLG.dlgWin.focus_set()
    winCDLG.dlgWin.grab_set()
    winCDLG.dlgWin.transient(parent)
    winCDLG.dlgWin.wait_window(winCDLG.dlgWin)
    return winCDLG.userResponse

class _cdlgWin():
    def __init__(self,parent,*args):
        self.parent = parent
        self.dlgWin = tk.Toplevel()
        self.userResponse = ''

    def showWin(self):
        #Tkinter widgets and geometry defined here

    def _btnOKClick(self):
        #self.userResponse assigned from user entry/entries on dialog
        self.dlgWin.destroy()

    def _btnCancelClick(self):
        self.dlgWin.destroy()

However this approach isn't working for the new monitor-and-update dialog I want to create.

First, because there's no user-initiated action to trigger the copy/update activities and then the destroy , I have to put them either in showWin, or in another method. I've tried both ways but I'm stuck between a race condition (the code completes the copy/update stuff but then tries to destroy the window before it's there), and never executing the copy/update stuff in the first place because it hits the wait_window before I can activate the other method.

If I could figure out a way past that, then the secondary problem (preventing the user from closing the child window before the work's done) is covered by the answers below.

So... is there any kind of bandaid I could apply to make this approach work the way I want? Or do I need to just scrap this because it can't work? (And if it's the latter, is there any way I can accomplish the original goal?)

self.dlgWin.overrideredirect(1) will remove all of the buttons (make a borderless window). Is that what you're looking for?

As far as I know, window control buttons are implemented by the window manager, so I think it is not possible to just remove one of them with Tkinter (I am not 100% sure though). The common solution for this problem is to set a callback to the protocol WM_DELETE_WINDOW and use it to control the behaviour of the window:

class _cdlgWin():
    def __init__(self,parent,*args):
        self.parent = parent
        self.dlgWin = tk.Toplevel()
        self.dlgWin.protocol('WM_DELETE_WINDOW', self.close)
        self.userResponse = ''

    def close(self):
        tkMessageBox.showwarning('Warning!',
                                 'The pending action has not finished yet')
    # ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM