MatthewG
MatthewG

Reputation: 815

Center TopLevel window, keeping dynamic sizing of grid/pack geometry?

I want to center a TopLevel window, to the middle of it's parent window (Tk or TopLevel window). The problem is I want to keep the dynamic sizing of the window that .pack() and .grid(), geometry managers of tkinter, give (the window size scales depending on how much size is needed).

I want to do this without having to use the update(), which I know would solve my problem, because I don't want the window to flash up and then quickly flash to the location it is meant to be.

This is my best attempt so far, the problem is that it removes the dynamic sizing.

from tkinter import *


def center_to_win(window, master):
    x = master.winfo_x()
    y = master.winfo_y()
    w = window.winfo_reqwidth()
    h = window.winfo_reqheight()
    total_x = x + (master.winfo_width() // 2) - (w // 2)
    total_y = y + (master.winfo_height() // 2) - (h // 2)
    window.geometry("%dx%d+%d+%d" % (int(w), int(h), int(total_x), int(total_y)))


class MainWin(Tk):
    def __init__(self):
        super(MainWin, self).__init__()
        self.update()
        pu = PopUp(self)


class PopUp(Toplevel):
    def __init__(self, master):
        super(PopUp, self).__init__(master)
        for i in range(20):
            label = Label(self, text="label i")
            label.grid(column=0, row=i)
        center_to_win(self, master)


if __name__ == '__main__':
    win = MainWin()
    win.mainloop()

If anyone knows how to do this, without having the window flash on screen, that would be a great help. :)

Upvotes: 1

Views: 218

Answers (1)

Saad
Saad

Reputation: 3430

As far as I know, it is kinda not possible for a window to update without update() before mainloop() executes. However it can be done without flashing the window by updating the window when it's hidden. We can use window.wm_withdraw() to hide and window.wm_deiconify() to show, the delay is not noticeable at least on my system. window.iconify() can also be used in place of window.wm_withdraw() but it cannot be used together with transient().

Chances to make function center_to_win() to centralise one window on another.

def center_to_win(window, master):
    window.wm_withdraw()
    window.update()
    x = master.winfo_x()
    y = master.winfo_y()
    w = window.winfo_reqwidth()
    h = window.winfo_reqheight()
    total_x = x + (master.winfo_width() // 2) - (w // 2)
    total_y = y + (master.winfo_height() // 2) - (h // 2)
    window.geometry("%dx%d+%d+%d" % (int(w), int(h), int(total_x), int(total_y)))
    window.wm_deiconify()

Calling the function just by center_to_win(self, master) works completely fine but I noticed an issue when you set geometery of main window in the starting it can be fixed by calling the function like this self.after(1, center_to_win, self, master). Though it was very random and it might not happen to you but in case if it does then use after(..).


For dynamic sizing.

Though this fixes the dynamic sizing issue but you should use self.grid_rowconfigure(index, weight=1) and self.grid_columnconfigure(index, weight=1) for grid layout.

...

for i in range(20):
    self.grid_rowconfigure(i, weight=1)
    label = Label(self, text="label i")
    label.grid(column=0, row=i)
...

Upvotes: 2

Related Questions