R4PH43L
R4PH43L

Reputation: 2200

Tkinter - resize parent after grid_remove()

Either I did misread some of the documentation or I still do not quite understand some concepts of Tkinter.

If I add widgets (e.g. Labels) to a frame using grid() the frame gets resized to fit them. (so far as expected)

If I call grid_remove on all the children (using frame.winfo_children() to adress all of them) the frame does not get resized again (what I would like to have).

Is there any specific reason why after these actions

  1. add 20 elements
  2. grid_remove() all of them
  3. add 10 again

these results occur?

  1. The frame resizes to fit 20.
  2. Does not resize on removal.
  3. Shrinks after adding 10 to only fit them?

Edit

I am using an extension of this code (which is more than the MCVE) to collapse or expand a ttk.LabelFrame triggered by click onto the Label. (Thus hiding the children with the intention of keeping subsequent .grid() calls without parameters available).

Possibly I am running on an inefficient approach here - remarks on that are welcome as well.

@TheLizzards approach of rescaling the frame to 1 and then to 0 does not really work in this case (The Label of the LabelFrame disappears) but I do quite like the approach.

@JRiggles approach works with the given restraints but is quite ugly when elements are added to the collapsed Labelframe.

At the current stage I did play around using both approaches, already tried @BryanOakleys answer that I did see in a different post but it seems me binding '<Expose>' resulted in an infinityloop.


Replication Code:


import tkinter as tk
from tkinter import ttk

root = tk.Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)

app = ttk.Frame(root)
app.grid(row=0, column=0, columnspan=2, sticky=tk.NW+tk.SE)

def add(start=None):
    start = start or len(app.winfo_children())
    for i in range(start,start+10):
        ttk.Label(app, text=f"{i}").grid(row=i, column=0, sticky=tk.NW+tk.SE)

def rem():
    for _f in app.grid_slaves():
        _f.grid_remove()

ttk.Button(root, text='+', command=add).grid(row=1, column=0)
ttk.Button(root, text='-', command=rem).grid(row=1, column=1)

root.mainloop()

Upvotes: -1

Views: 61

Answers (4)

Bryan Oakley
Bryan Oakley

Reputation: 386315

The issue is that grid (and also pack) is responsible for resizing the window when the window has child widgets. When you remove the last widget, grid is no longer responsible for resizing the window so it doesn't cause the window to resize.

A simple solution is to create a 1x1 pixel widget (for example, a frame) and add it with grid and the window will shrink around it.

Upvotes: 0

acw1668
acw1668

Reputation: 47085

You can call app.grid_remove() at the end of rem() to make frame app invisible. But you need to call app.grid() at the end of add() to show it back.

Note that it is better to call _f.destroy() instead of _f.grid_remove() if you will not resume those labels later.

def add(start=None):
    start = start or len(app.winfo_children())
    for i in range(start,start+10):
        ttk.Label(app, text=f"{i}").grid(row=i, column=0, sticky=tk.NW+tk.SE)
    app.grid()

def rem():
    for _f in app.grid_slaves():
        _f.destroy()
    app.grid_remove()

Upvotes: 1

TheLizzard
TheLizzard

Reputation: 7710

This is a known issue in tcl. The best workaround is to either destroy the frame and recreate it or use:

app.config(width=1, height=1)
app.config(width=0, height=0)

This sets the width and height to 1 pixel and then resets it so that it can auto-resize when more widgets are added. For a full solution, we will have to wait for someone to fix this behaviour in tcl.

Upvotes: 3

JRiggles
JRiggles

Reputation: 6810

I was able to get the Frame to collapse on item removal by adding a call to app.update_idletasks() in the rem function, but the behavior is a bit janky because it's being called on every iteration of the for loop, and it does seem to leave "space" for an empty row in the Frame

(and no, moving it outside the for loop doesn't work, alas)

import tkinter as tk
from tkinter import ttk

root = tk.Tk()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)

app = ttk.Frame(root)
app.grid(row=0, column=0, columnspan=2, sticky='nesw')

def add(start=None):
    start = start or len(app.winfo_children())
    for i in range(start,start+10):
        ttk.Label(app, text=f"{i}").grid(row=i, column=0, sticky='nesw')

def rem():
    for _f in app.grid_slaves():
        _f.grid_remove()
        app.update_idletasks()

ttk.Button(root, text='+', command=add).grid(row=1, column=0)
ttk.Button(root, text='-', command=rem).grid(row=1, column=1)

root.mainloop()

I can't say I recommend this approach, but it "works"

Upvotes: 0

Related Questions