2

I have a Python 3.5 app using tkinter and create/remove widgets in a canvas on the fly. I noticed that when a widget is being destroyed using w.destroy() during the callback of a window resize, the callback finished correctly, but the window is being reset to its original size.

I've tried to delay the w.destroy() using self.after(1000, self.resize) and that kind off works but when dragging/holding the cursor down while the callback is being fired, the resize jumps back anyway.

Anyone having a suggestion to remove widgets on the fly and release memory?

UPDATE: As a workaround I mark widgets to be deleted with w.delete=True and in an 'after' callback delete the widgets which are marked for deletion.

Here is a stripped down sample to illustrate the issue:

UPDATE: Simplified code to bare minimum to reproduce issue.

from tkinter import ALL, N, E, W, S, Canvas, Tk, ttk


class Resize(Tk):
    def __init__(self):
        super().__init__()
        self.init = True
        self.canvas = None
        self.position_window()
        self.create_canvas()

    def resize(self):
        if self.init:
            self.init = False
            return
        # delete(ALL) will remove the widget from the display, but widget 
        # remains in memory as child of canvas. Resizing works ok.
        self.canvas.delete(ALL)
        # widget.destroy() will release memory of widget, but blocks/resets resizing.
        # Window gets resized by 1-2 pixels and jumps back to its original size.
        [child.destroy() for child in self.canvas.winfo_children()]

    def create_canvas(self):
        self.columnconfigure(0, weight=1)
        self.rowconfigure(0, weight=1)

        self.canvas = Canvas(self)
        self.canvas.grid(row=0, column=0, sticky=(N, S, W, E))
        self.canvas.bind('<Configure>', lambda *args: self.resize())

        entry = ttk.Label(self.canvas, text='Some dummy text')
        self.canvas.create_window(20, 20, window=entry, anchor='w')

    def position_window(self):
        self.resizable(1, 1)
        sceenwidth = self.winfo_screenwidth()
        screenheight = self.winfo_screenheight()
        self.update_idletasks()
        width = 600
        height = 300
        left = sceenwidth / 2 - width / 2
        top = (screenheight / 2 - height / 2) / 3
        self.geometry("%dx%d%+d%+d" % (width, height, left, top))


if __name__ == '__main__':
    Resize().mainloop()
passerby
  • 1,807
  • 19
  • 26
  • why are you deleting them and them immediately recreating them? What is the real problem you're trying to solve? – Bryan Oakley Jun 13 '16 at 03:06
  • 1
    @BryanOakley The above code is just a sample to reproduce the issue that scrolling gets aborted when a widget is destroyed during the callback. I agree the code might not be very logical... Based on some fingerpointing from you somewhere else I'm creating a Table widget that handles thousands of rows by only displaying a handful of rows in a canvas. When resizing the canvas I delete superfluous rows and their widgets. At that moment, resizing is being reset without any error. Btw. I've simplified the code. – passerby Jun 13 '16 at 15:15
  • 1
    Did you find a solution ? I have similar problem with my project – D V Jul 10 '20 at 22:52
  • @DV, Missed your comment... No didn't find a solution. Unless you would call using Electron/Angular a solution :-) – passerby Jan 07 '21 at 18:37

2 Answers2

2

I did not try your code, but I had the same problem with my own project.

The window wasn't just resizing/resetting on destroy(); the <Configure> event was seemingly interrupted. The window always reverted back to the very first <Configure> call back no matter how much or how little I resized. Because of that, or along with that, Button-1 was automatically releasing, preventing the window resize grip from following the mouse.

In other words, the window edge was "slipping out" from under the mouse, much like a scrollbar in Windows will reset mid-scroll when the mouse moves too far from it.

Anyways, a solution is to use ttk.Sizegrip and prohibit resizing the top level window with the window borders.

Joao Vitorino
  • 2,976
  • 3
  • 26
  • 55
pez
  • 21
  • 2
  • How do you prohibit resizing the top level window ? I tried root.resizable(False, False). And sizegrip doesn't work at all. – D V Jul 10 '20 at 19:02
0

https://stackoverflow.com/a/65605907/7281120

I answered this somewhere else. I ended up polling

win32api.GetKeyState(0x01) >= 0

and storing a list of things to destroy when the mouse was released and it was safe to do so.

Drew Macrae
  • 101
  • 4