0

Why if i create two widgets, it stops working? My main frame loop:

import tkinter as tk

class LobbyGUI(tk.Frame):
    def __init__(self):
        self.main_window = tk.Tk()

        self.x = 200
        self.y = 200
        self.height = 500
        self.width = 900

        self.main_window.geometry(f"{self.width}x{self.height}+{self.x}+{self.y}")

        super(LobbyGUI, self).__init__(self.main_window)

        self.pack()
        self.main_window.update_idletasks()

        self.widgets = dict()
        self.widgets['search'] = SearchWindow(self.main_window)
        self.widgets['search2'] = SearchWindow(self.main_window)

        self.main_window.bind("<Configure>", self.on_drag)

    def mainloop(self, n=0):
        while True:
            self.update_idletasks()
            self.update()

    def on_drag(self, event):
        for widget in self.widgets.values():
            widget.place()

And there is a class supposed to dynamically follow the root window:

class SearchWindow:
    def __init__(self, root):
        self.root = root
        self.top = tk.Toplevel(root)
        self.width = 100
        self.height = 20
        self.top.geometry(f"{self.width}x{self.height}+0+0")

        self.place()

        self.top.overrideredirect(1)  # remove title border
        self.entry = tk.Entry(master=self.top, width=self.width, borderwidth=0, highlightthickness=0)
        self.entry.pack()

    def place(self):
        x = self.root.winfo_rootx()
        y = self.root.winfo_rooty()
        width = self.root.winfo_width()
        self.top.geometry(f"+{x + width - self.width - 50}+{y + 20}")
        self.top.lift()

It works fine with one SearchWindow child, but doesnt with two. Where could be an issue with this approach?

F1res
  • 118
  • 2
  • 9
  • Why did you create your own `mainloop`? It's going to be considerably less efficient than the real mainloop. – Bryan Oakley May 13 '21 at 19:00
  • @BryanOakley Why would it be? Following [this answer](https://stackoverflow.com/questions/29158220/tkinter-understanding-mainloop) this is equal – F1res May 13 '21 at 19:03
  • They are _conceptually_ equal, but they are not equal. The real `mainloop` does a bit more work than just calling `update`. The answer you point to actually does a good job of explaining why calling `update` and `update_idletasks` in a loop bad. – Bryan Oakley May 13 '21 at 19:09
  • @BryanOakley You may be right. I will check source code to completely understand the difference. – F1res May 13 '21 at 19:12
  • @BryanOakley I've managed to find out that the problem is in two `.lift()` calls. Do you have an idea how to solve this? – F1res May 13 '21 at 20:33
  • Okay, for this moment, i think that configure event call itself every time it tries to change the order of frames. – F1res May 13 '21 at 21:57

1 Answers1

0

Okay, after couple of hours i've succeed. All we need to do is to filter real change of main frame position from other events mapped to <Configure>. This may not be an effictive way, but atleast it's working:

Solution 1:

class LobbyGUI:
    def __init__(self):
        self.main_window = tk.Tk()

        self.x = 200
        self.y = 200
        self.height = 500
        self.width = 900

        self.geometry = f"{self.width}x{self.height}+{self.x}+{self.y}"
        self.main_window.geometry(self.geometry)

        self.widgets = dict()
        self.widgets['search'] = SearchWindow(self.main_window)
        self.widgets['search2'] = SearchWindow(self.main_window)

        self.main_window.bind("<Configure>", self.on_drag)

    def on_drag(self, event):
        for widget in self.widgets.values():
            widget.place()
        if self.geometry != self.main_window.geometry():
            for widget in self.widgets.values():
                widget.lift(aboveThis=self.main_window)
            self.geometry = self.main_window.geometry()

    def start(self):
        self.main_window.mainloop()

class SearchWindow(tk.Toplevel):
    def __init__(self, root):
        super(SearchWindow, self).__init__(root)
        self.root = root
        self.width = 100
        self.height = 20
        self.geometry(f"{self.width}x{self.height}+0+0")

        # self.top.overrideredirect(1)  # remove title border
        self.entry = tk.Entry(master=self, width=self.width, borderwidth=0, highlightthickness=0)
        self.entry.pack()

        self.place()

    def place(self):
        x = self.root.winfo_rootx()
        y = self.root.winfo_rooty()
        width = self.root.winfo_width()
        self.geometry(f"+{max(x + width - self.width - 50, 0)}+{y + 20}")

And i came up with second idea right now. Very bad, but not to mention:

Solution 2:

def mainloop(self):
    while 1:
        self.main_window.update()
        for widget in self.widgets.values():
            widget.lift(aboveThis=self.main_window)
        self.main_window.update_idletasks()
F1res
  • 118
  • 2
  • 9